added backends, improved templating, rbac

This commit is contained in:
2026-06-10 14:40:22 +02:00
parent d9ca48addc
commit ce43f2658f
28 changed files with 1183 additions and 78 deletions

View File

@@ -1,10 +1,12 @@
from __future__ import annotations
from datetime import datetime
from typing import Any
from typing import Any, Literal
from pydantic import BaseModel, ConfigDict, Field
from app.mailer.campaign.models import ImapConfig, SmtpConfig
class CampaignCreateRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
@@ -126,6 +128,43 @@ class BuildCampaignRequest(BaseModel):
write_eml: bool = True
class MailSmtpTestRequest(SmtpConfig):
"""SMTP settings supplied directly from the WebUI mail settings form."""
class MailImapTestRequest(ImapConfig):
"""IMAP settings supplied directly from the WebUI mail settings form."""
enabled: bool = True
class MailConnectionTestResponse(BaseModel):
ok: bool
protocol: Literal["smtp", "imap"]
host: str | None = None
port: int | None = None
security: str | None = None
message: str
details: dict[str, Any] = Field(default_factory=dict)
class MailImapFolderResponse(BaseModel):
name: str
flags: list[str] = Field(default_factory=list)
class MailImapFolderListResponse(BaseModel):
ok: bool
protocol: Literal["imap"] = "imap"
host: str | None = None
port: int | None = None
security: str | None = None
message: str
folders: list[MailImapFolderResponse] = Field(default_factory=list)
detected_sent_folder: str | None = None
details: dict[str, Any] = Field(default_factory=dict)
class ApiKeyCreateRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
@@ -200,3 +239,70 @@ class AuditLogItemResponse(BaseModel):
class AuditLogListResponse(BaseModel):
items: list[AuditLogItemResponse]
class LoginRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
email: str
password: str
# Kept optional for backwards compatibility and future tenant-switch login flows.
# The WebUI no longer sends it. If omitted, the backend resolves the user by email.
tenant_slug: str | None = None
class TenantInfo(BaseModel):
id: str
slug: str
name: str
class TenantMembershipInfo(TenantInfo):
roles: list[str] = Field(default_factory=list)
is_active: bool = True
class UserInfo(BaseModel):
id: str
email: str
display_name: str | None = None
is_tenant_admin: bool = False
class RoleInfo(BaseModel):
id: str
slug: str
name: str
permissions: list[str] = Field(default_factory=list)
class GroupInfo(BaseModel):
id: str
slug: str
name: str
class LoginResponse(BaseModel):
access_token: str
token_type: str = "bearer"
expires_at: datetime
user: UserInfo
# Backwards-compatible alias for the active tenant.
tenant: TenantInfo
active_tenant: TenantInfo
tenants: list[TenantMembershipInfo] = Field(default_factory=list)
scopes: list[str]
roles: list[RoleInfo] = Field(default_factory=list)
groups: list[GroupInfo] = Field(default_factory=list)
class MeResponse(BaseModel):
user: UserInfo
# Backwards-compatible alias for the active tenant.
tenant: TenantInfo
active_tenant: TenantInfo
tenants: list[TenantMembershipInfo] = Field(default_factory=list)
scopes: list[str]
roles: list[RoleInfo] = Field(default_factory=list)
groups: list[GroupInfo] = Field(default_factory=list)