feat: production launch — email audit, team invites, security headers, router fixes
Some checks failed
CI / ruff (push) Successful in 9s
CI / pytest (push) Failing after 47m32s
CI / validate (push) Successful in 23s
CI / dependency-scanning (push) Successful in 29s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped

- Fix loyalty & monitoring router bugs (_get_router → named routers)
- Implement team invitation email with send_template + seed templates (en/fr/de)
- Add SecurityHeadersMiddleware (nosniff, HSTS, referrer-policy, permissions-policy)
- Build email audit admin page: service, schemas, API, page route, menu, i18n, HTML, JS
- Clean stale TODO in platform-menu-config.js
- Add 67 tests (unit + integration) covering all new functionality

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 18:24:30 +01:00
parent 4ebd419987
commit ce822af883
25 changed files with 2485 additions and 19 deletions

View File

@@ -245,3 +245,74 @@ class EmailTestResponse(BaseModel):
success: bool
message: str
email_log_id: int | None = None
# =============================================================================
# Email Log (Audit) Schemas
# =============================================================================
class EmailLogListItem(BaseModel):
"""Compact email log item (no body content)."""
id: int
recipient_email: str
recipient_name: str | None = None
subject: str
status: str
template_code: str | None = None
provider: str | None = None
store_id: int | None = None
related_type: str | None = None
related_id: int | None = None
created_at: str | None = None
sent_at: str | None = None
error_message: str | None = None
class EmailLogDetail(BaseModel):
"""Full email log detail including body content."""
id: int
recipient_email: str
recipient_name: str | None = None
subject: str
status: str
template_code: str | None = None
provider: str | None = None
store_id: int | None = None
user_id: int | None = None
related_type: str | None = None
related_id: int | None = None
from_email: str | None = None
from_name: str | None = None
reply_to: str | None = None
body_html: str | None = None
body_text: str | None = None
error_message: str | None = None
retry_count: int = 0
provider_message_id: str | None = None
created_at: str | None = None
sent_at: str | None = None
delivered_at: str | None = None
opened_at: str | None = None
clicked_at: str | None = None
extra_data: str | None = None
class EmailLogListResponse(BaseModel):
"""Paginated email log list."""
items: list[EmailLogListItem]
total: int
page: int
per_page: int
total_pages: int
class EmailLogStatsResponse(BaseModel):
"""Email log statistics."""
by_status: dict[str, int] = Field(default_factory=dict)
by_template: dict[str, int] = Field(default_factory=dict)
total: int = 0