Archived
Prepare KarlDNS production deployment
This commit is contained in:
1 parent
5a90bd9d87
commit
ce5b4e0ff2
101 files changed
+3364
-2018
No files matched your search
@@ -0,0 +1,203 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from .services.ddns import DdnsService
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
detail: str = Field(examples=["not found"])
|
||||
|
||||
|
||||
class HostnameCreateResponse(BaseModel):
|
||||
hostname: str
|
||||
status: str = Field(description="Lifecycle state. Newly generated hostnames start as pending until registered.")
|
||||
update_url: str = Field(description="Secret update URL for router DynDNS updates.")
|
||||
dashboard_url: str = Field(description="Secret dashboard URL for reading, rotating, or deleting this hostname.")
|
||||
registration_url: str = Field(description="Secret URL used to register the pending hostname after secrets are saved.")
|
||||
|
||||
|
||||
class HostnameStatusResponse(BaseModel):
|
||||
hostname: str
|
||||
status: str
|
||||
update_url: str = Field(description="Secret update URL for router DynDNS updates.")
|
||||
dashboard_url: str = Field(description="Secret dashboard URL for reading, rotating, or deleting this hostname.")
|
||||
ipv4: str | None = None
|
||||
ipv6: str | None = None
|
||||
updated_at: str | None = None
|
||||
history: list["HostnameUpdateEvent"] = Field(default_factory=list)
|
||||
|
||||
|
||||
class HostnameRotateUpdateResponse(BaseModel):
|
||||
hostname: str
|
||||
status: str
|
||||
update_url: str = Field(description="Secret replacement update URL for router DynDNS updates.")
|
||||
dashboard_url: str = Field(description="Secret dashboard URL for reading, rotating, or deleting this hostname.")
|
||||
|
||||
|
||||
class HostnameUpdateEvent(BaseModel):
|
||||
hostname: str
|
||||
ipv4: str | None = None
|
||||
ipv6: str | None = None
|
||||
status: str
|
||||
detail: str
|
||||
created_at: str
|
||||
count: int = 1
|
||||
|
||||
|
||||
class AdminHostname(BaseModel):
|
||||
hostname: str
|
||||
status: str
|
||||
ipv4: str | None = None
|
||||
ipv6: str | None = None
|
||||
updated_at: str | None = None
|
||||
created_at: str | None = None
|
||||
accepted_at: str | None = None
|
||||
disabled: bool = False
|
||||
|
||||
|
||||
class AdminRecord(BaseModel):
|
||||
hostname: str
|
||||
ipv4: str | None = None
|
||||
ipv6: str | None = None
|
||||
updated_at: str
|
||||
|
||||
|
||||
class AdminUpdateEvent(BaseModel):
|
||||
hostname: str
|
||||
ipv4: str | None = None
|
||||
ipv6: str | None = None
|
||||
status: str
|
||||
detail: str
|
||||
source_ip: str
|
||||
created_at: str
|
||||
count: int = 1
|
||||
|
||||
|
||||
class AdminCleanupRun(BaseModel):
|
||||
created_at: str
|
||||
unused_accounts_deleted: int
|
||||
|
||||
|
||||
class AdminScheduledTask(BaseModel):
|
||||
name: str
|
||||
interval_seconds: int
|
||||
next_run_at: str
|
||||
lease_owner: str | None = None
|
||||
lease_expires_at: str | None = None
|
||||
last_started_at: str | None = None
|
||||
last_finished_at: str | None = None
|
||||
last_status: str
|
||||
last_error: str
|
||||
updated_at: str
|
||||
|
||||
|
||||
class AdminMetrics(BaseModel):
|
||||
values: dict[str, float]
|
||||
average_update_latency_ms: float
|
||||
|
||||
|
||||
class AdminTotals(BaseModel):
|
||||
hostnames: int
|
||||
records: int
|
||||
update_events: int
|
||||
cleanup_runs: int
|
||||
|
||||
|
||||
class AdminDashboardResponse(BaseModel):
|
||||
totals: AdminTotals
|
||||
metrics: AdminMetrics
|
||||
hostnames: list[AdminHostname]
|
||||
update_events: list[AdminUpdateEvent]
|
||||
cleanup_runs: list[AdminCleanupRun]
|
||||
scheduled_tasks: list[AdminScheduledTask]
|
||||
limit: int
|
||||
|
||||
|
||||
class AdminHostnamesResponse(BaseModel):
|
||||
items: list[AdminHostname]
|
||||
limit: int
|
||||
offset: int
|
||||
total: int
|
||||
|
||||
|
||||
class AdminRecordsResponse(BaseModel):
|
||||
items: list[AdminRecord]
|
||||
limit: int
|
||||
offset: int
|
||||
total: int
|
||||
|
||||
|
||||
class AdminEventsResponse(BaseModel):
|
||||
items: list[AdminUpdateEvent]
|
||||
limit: int
|
||||
offset: int
|
||||
next_before: str | None = None
|
||||
total: int
|
||||
|
||||
|
||||
def _create_response(service: DdnsService, account: dict[str, str]) -> HostnameCreateResponse:
|
||||
return HostnameCreateResponse(
|
||||
hostname=account["hostname"],
|
||||
status="active" if account.get("accepted_at") else "pending",
|
||||
update_url=service.fritz_update_url(account),
|
||||
dashboard_url=service.dashboard_url(account),
|
||||
registration_url=service.registration_url(account),
|
||||
)
|
||||
|
||||
|
||||
def _hostname_status(
|
||||
service: DdnsService,
|
||||
account: dict[str, str | int | None],
|
||||
history: list[dict[str, str | int | None]] | None = None,
|
||||
) -> HostnameStatusResponse:
|
||||
account_for_url = {"update_slug": str(account["update_slug"])}
|
||||
account_for_dashboard = {"management_slug": str(account.get("management_slug", ""))}
|
||||
return HostnameStatusResponse(
|
||||
hostname=str(account["hostname"]),
|
||||
status="active" if account.get("accepted_at") else "pending",
|
||||
update_url=service.fritz_update_url(account_for_url),
|
||||
dashboard_url=service.dashboard_url(account_for_dashboard) if account_for_dashboard["management_slug"] else "",
|
||||
ipv4=str(account["ipv4"]) if account.get("ipv4") else None,
|
||||
ipv6=str(account["ipv6"]) if account.get("ipv6") else None,
|
||||
updated_at=str(account["updated_at"]) if account.get("updated_at") else None,
|
||||
history=[_hostname_update_event(event) for event in history or []],
|
||||
)
|
||||
|
||||
|
||||
def _hostname_rotate_update_response(service: DdnsService, account: dict[str, str | int | None], dashboard_secret: str) -> HostnameRotateUpdateResponse:
|
||||
return HostnameRotateUpdateResponse(
|
||||
hostname=str(account["hostname"]),
|
||||
status="active" if account.get("accepted_at") else "pending",
|
||||
update_url=service.fritz_update_url({"update_slug": str(account["update_slug"])}),
|
||||
dashboard_url=service.dashboard_url({"management_slug": dashboard_secret}),
|
||||
)
|
||||
|
||||
|
||||
def _hostname_update_event(row: dict[str, str | int | None]) -> HostnameUpdateEvent:
|
||||
return HostnameUpdateEvent(
|
||||
hostname=str(row["hostname"]),
|
||||
ipv4=str(row["ipv4"]) if row.get("ipv4") else None,
|
||||
ipv6=str(row["ipv6"]) if row.get("ipv6") else None,
|
||||
status=str(row["status"]),
|
||||
detail=str(row["detail"]),
|
||||
created_at=str(row["created_at"]),
|
||||
count=int(row.get("count") or 1),
|
||||
)
|
||||
|
||||
|
||||
def _admin_hostname(account: dict[str, str | int | None]) -> AdminHostname:
|
||||
return AdminHostname(
|
||||
hostname=str(account["hostname"]),
|
||||
status="disabled" if account.get("disabled") else ("active" if account.get("accepted_at") else "pending"),
|
||||
ipv4=str(account["ipv4"]) if account.get("ipv4") else None,
|
||||
ipv6=str(account["ipv6"]) if account.get("ipv6") else None,
|
||||
updated_at=str(account["updated_at"]) if account.get("updated_at") else None,
|
||||
created_at=str(account["created_at"]) if account.get("created_at") else None,
|
||||
accepted_at=str(account["accepted_at"]) if account.get("accepted_at") else None,
|
||||
disabled=bool(account.get("disabled")),
|
||||
)
|
||||
|
||||
|
||||
def _admin_metrics(metrics: dict[str, float]) -> AdminMetrics:
|
||||
samples = metrics.get("update_latency_samples_total", 0)
|
||||
average_latency = metrics.get("update_latency_ms_total", 0) / samples if samples else 0
|
||||
return AdminMetrics(values=metrics, average_update_latency_ms=average_latency)
|
||||
Reference in new issue
Block a user