from __future__ import annotations from datetime import datetime, timezone from typing import Optional from sqlmodel import Field, SQLModel def _now() -> datetime: return datetime.now(timezone.utc) class Registry(SQLModel, table=True): """Credentials for one container registry. ``host`` is the canonical registry hostname as :func:`services.registry_service.canonical_host` produces it, so the lookup from an image reference is a dict hit and Docker Hub's several spellings all land on one row. The password is encrypted at rest (see ``services/crypto_service.py``) and never leaves the API — reads return it masked. """ id: Optional[int] = Field(default=None, primary_key=True) name: str host: str = Field(index=True) username: str password: str # encrypted created_at: datetime = Field(default_factory=_now) updated_at: datetime = Field(default_factory=_now) # --- API schemas --- class RegistryCreate(SQLModel): name: Optional[str] = None host: str username: str password: str class RegistryUpdate(SQLModel): name: Optional[str] = None host: Optional[str] = None username: Optional[str] = None # Omitted leaves the stored password alone, so the UI can save a row it only # ever received masked. password: Optional[str] = None class RegistryRead(SQLModel): id: int name: str host: str username: str has_password: bool created_at: datetime updated_at: datetime class RegistryTestRequest(SQLModel): """An unsaved set of credentials to try, for the "Test" button.""" host: str username: str password: Optional[str] = None