241713083b
- 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>
35 lines
1.3 KiB
Python
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}>"
|