Coverage for src/lexigram/admin/settings/panel/models.py: 100%
27 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:18 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:18 +0800
1"""Pydantic models bound to configuration specs."""
3from __future__ import annotations
5from typing import Literal
7from lexigram.domain import DomainModel
8from lexigram.validation import Field
10__all__ = [
11 "DEFAULT_CSP",
12 "BrandingSettings",
13 "CacheSettings",
14 "I18nSettings",
15 "ProfilerSettings",
16 "RbacSettings",
17 "SecuritySettings",
18]
20DEFAULT_CSP = (
21 "default-src 'self'; "
22 "script-src 'self' 'unsafe-inline' https://unpkg.com; "
23 "style-src 'self' 'unsafe-inline' https://unpkg.com; "
24 "img-src 'self' data:; "
25 "font-src 'self'; "
26 "connect-src 'self' https://unpkg.com; "
27 "frame-ancestors 'none';"
28)
31class BrandingSettings(DomainModel):
32 """Site branding and theme settings consumed by the admin renderer."""
34 site_name: str = Field(
35 default="Lexigram Admin",
36 title="Site Name",
37 description="Name shown in the topbar, login page, and document title.",
38 )
39 primary_color: str = Field(
40 default="#6b7280",
41 title="Primary Color",
42 description="Hex color used for the primary UI accent.",
43 )
44 logo_url: str = Field(default="", title="Logo URL")
45 favicon_url: str = Field(default="", title="Favicon URL")
46 dark_mode: Literal["system", "light", "dark"] = Field(
47 default="system",
48 title="Dark Mode",
49 description="Theme preference: follow the system, force light, or force dark.",
50 )
53class CacheSettings(DomainModel):
54 """Response caching settings consumed by AdminCacheMiddleware."""
56 enabled: bool = Field(
57 default=True,
58 title="Enabled",
59 description="Cache successful GET responses.",
60 )
61 default_ttl: int = Field(
62 default=60,
63 ge=0,
64 title="Default TTL (seconds)",
65 description="Default cache lifetime when no Cache-Control header is present.",
66 )
69class SecuritySettings(DomainModel):
70 """HTTP security header settings consumed by AdminSecurityHeaders."""
72 csp: str = Field(
73 default=DEFAULT_CSP,
74 title="Content Security Policy",
75 description="Content-Security-Policy header value.",
76 )
77 hsts_max_age: int = Field(
78 default=63072000,
79 ge=0,
80 title="HSTS Max Age (seconds)",
81 description="Strict-Transport-Security max-age.",
82 )
85class I18nSettings(DomainModel):
86 """Internationalization defaults consumed by the i18n locale resolver."""
88 default_locale: str = Field(
89 default="en",
90 title="Default Locale",
91 description="Fallback BCP 47 locale tag used when a request resolves no locale.",
92 )
93 default_timezone: str = Field(
94 default="UTC",
95 title="Default Timezone",
96 description="Fallback IANA timezone name used when a request resolves no timezone.",
97 )
100class RbacSettings(DomainModel):
101 """RBAC defaults consumed by the permission service."""
103 default_role: str = Field(
104 default="viewer",
105 title="Default Role",
106 description="Role assigned to users with no explicit role mapping.",
107 )
108 allow_anonymous: bool = Field(
109 default=False,
110 title="Allow Anonymous",
111 description="Permit requests without an authenticated identity.",
112 )
115class ProfilerSettings(DomainModel):
116 """Profiler toggles. Rendering/persistence only — no consumer is wired (see plan)."""
118 enabled: bool = Field(
119 default=False,
120 title="Enabled",
121 description="Enable request profiling.",
122 )
123 slow_threshold_ms: int = Field(
124 default=500,
125 ge=1,
126 title="Slow Threshold (ms)",
127 description="Requests slower than this are flagged as slow.",
128 )