from pydantic import BaseModel, ConfigDict, Field from .ddns_service import DdnsService class ErrorResponse(BaseModel): detail: str = Field(examples=["not found"]) class MagicHostnameRequest(BaseModel): model_config = ConfigDict(extra="forbid") username: str | None = Field(default=None, max_length=128, examples=["home-router"]) class HostnameCredentials(BaseModel): hostname: str username: str password: str = Field(description="Secret router password. Store it now; it is not recoverable later.") update_url: str = Field(description="Secret bearer update URL for router DynDNS updates.") credentialed_update_url: str = Field( description="Secret update URL with router credentials embedded in the query string." ) management_url: str = Field(description="Secret bearer management URL for reading or deleting this hostname.") class ManagedRecord(BaseModel): hostname: str username: str update_url: str = Field(description="Secret bearer update URL for router DynDNS updates.") ipv4: str | None = None ipv6: str | None = None updated_at: str | None = None class DomainChallengeRequest(BaseModel): model_config = ConfigDict(extra="forbid") domain: str = Field(examples=["example.net"]) class DomainChallengeResponse(BaseModel): domain: str txt_name: str txt_value: str claim_secret: str = Field(description="Secret bearer token for returning to and verifying this domain claim.") class VerifyDomainRequest(BaseModel): model_config = ConfigDict(extra="forbid") domain: str = Field(examples=["example.net"]) claim_secret: str class VerifyDomainResponse(BaseModel): domain: str verified: bool message: str claim_secret: str = Field(description="Secret bearer token for continuing this domain claim flow.") class CustomHostnameRequest(BaseModel): model_config = ConfigDict(extra="forbid") hostname: str = Field(examples=["home.example.net"]) claim_secret: str username: str | None = Field(default=None, max_length=128, examples=["home-router"]) class RouterUpdateResponse(BaseModel): status: str = Field(examples=["good"]) hostname: str ip: str def _credentials_response(service: DdnsService, account: dict[str, str]) -> HostnameCredentials: return HostnameCredentials( hostname=account["hostname"], username=account["username"], password=account["password"], update_url=service.fritz_update_url(account), credentialed_update_url=service.credentialed_update_url(account), management_url=service.magic_management_url(account), ) def _managed_record(service: DdnsService, account: dict[str, str | int | None]) -> ManagedRecord: account_for_url = {"update_slug": str(account["update_slug"])} return ManagedRecord( hostname=str(account["hostname"]), username=str(account["username"]), update_url=service.fritz_update_url(account_for_url), 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, )