Files
copilot-swe-agent[bot] 241713083b Add persistent settings system with database backend and comprehensive UI
- New Setting ORM model (key-value store with category, value_type, audit fields)
- Alembic migration to create the settings table
- Settings API endpoints: GET/PUT /api/v1/settings/{key}, GET /api/v1/settings (list+filter), POST /api/v1/settings/bulk
- Default seeding (17 sensible defaults across general/dmarc/dns/cloudflare/notifications categories)
- Secret redaction for cloudflare.api_token and notifications.smtp_password
- Updated settings.html: General, DMARC Policy Defaults, DNS Resolver, Cloudflare Integration, Email Notifications sections
- All forms wired to the API via Alpine.js with flash feedback
- 12 new tests for the settings model and endpoints

Agent-Logs-Url: https://github.com/christianlouis/dmarq/sessions/19dbc6cd-07cb-406e-b3b6-411f7721f737

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-30 07:58:51 +00:00

35 lines
1.3 KiB
Python

from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text
from app.core.database import Base
class Setting(Base):
"""
Key-value store for system-wide application settings.
Settings are grouped by a ``category`` prefix (e.g. ``general``,
``dmarc``, ``cloudflare``) to make bulk retrieval and UI grouping easy.
The ``value`` is always stored as text; callers are responsible for
serialising/deserialising typed values (int, bool, JSON) via the
``value_type`` hint.
"""
__tablename__ = "settings"
key = Column(String(100), primary_key=True)
value = Column(Text, nullable=True)
# Human-readable description shown in the admin UI
description = Column(String(255), nullable=True)
# Hint for the UI / API on how to interpret the value: string | integer | boolean | json
value_type = Column(String(20), nullable=False, default="string")
# Category / section grouping (e.g. "general", "dmarc", "cloudflare", "dns")
category = Column(String(50), nullable=False, default="general")
# Audit fields
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
updated_by = Column(Integer, ForeignKey("users.id"), nullable=True)
def __repr__(self):
return f"<Setting key={self.key!r} category={self.category!r}>"