from datetime import datetime from typing import Any from pydantic import BaseModel, ConfigDict, Field class OrmModel(BaseModel): model_config = ConfigDict(from_attributes=True) class TokenPair(BaseModel): access_token: str refresh_token: str token_type: str = "bearer" class LoginRequest(BaseModel): email: str password: str class SetupStatus(BaseModel): complete: bool has_users: bool has_clusters: bool class SetupCompleteRequest(BaseModel): admin_email: str admin_name: str admin_password: str = Field(min_length=12) cluster_name: str | None = None cluster_api_url: str | None = None cluster_api_token: str | None = None cluster_provider: str = "proxmox" cluster_mode: str = "read_only" verify_tls: bool = True class UserRead(OrmModel): id: str email: str display_name: str is_active: bool class UserCreate(BaseModel): email: str display_name: str password: str = Field(min_length=12) role_ids: list[str] = [] class RoleCreate(BaseModel): name: str permissions: list[str] = [] class RoleRead(OrmModel): id: str name: str permissions: list[str] class ClusterCreate(BaseModel): name: str api_url: str api_token: str = Field(min_length=8) provider: str = "proxmox" mode: str = "read_only" verify_tls: bool = True class ClusterUpdate(BaseModel): name: str api_url: str api_token: str | None = Field(default=None, min_length=8) provider: str = "proxmox" mode: str = "read_only" verify_tls: bool = True class TenantCreate(BaseModel): name: str description: str | None = None class ProjectCreate(BaseModel): tenant_id: str name: str description: str | None = None class NetworkCreate(BaseModel): cluster_id: str project_id: str | None = None name: str kind: str = "bridge" vlan_id: int | None = None mtu: int = 1500 gateway: str | None = None dns: list[str] = [] dhcp_enabled: bool = False tags: list[str] = [] description: str | None = None class SubnetCreate(BaseModel): network_id: str cidr: str gateway: str | None = None dns: list[str] = [] dhcp_enabled: bool = False class SecurityGroupCreate(BaseModel): project_id: str | None = None name: str description: str | None = None class SecurityRuleCreate(BaseModel): security_group_id: str direction: str = "ingress" action: str = "allow" protocol: str = "tcp" source: str = "any" destination: str = "any" port: str | None = None priority: int = 1000 logging: bool = False description: str | None = None class PolicyCreate(BaseModel): project_id: str | None = None name: str enabled: bool = True definition: dict[str, Any] class IpReservationCreate(BaseModel): subnet_id: str address: str status: str = "reserved" note: str | None = None class ServiceCatalogCreate(BaseModel): name: str protocol: str ports: str editable: bool = True class FirewallApplyRequest(BaseModel): policy_id: str cluster_id: str | None = None confirm: bool = False dry_run: bool = True class ClusterRead(OrmModel): id: str name: str api_url: str provider: str mode: str verify_tls: bool last_sync_at: datetime | None last_sync_status: str | None last_sync_error: str | None class NodeRead(OrmModel): id: str cluster_id: str name: str status: str cpu_count: int memory_mb: int class WorkloadRead(OrmModel): id: str cluster_id: str node_id: str project_id: str | None external_id: str name: str kind: str status: str tags: list[str] class NetworkRead(OrmModel): id: str cluster_id: str project_id: str | None name: str kind: str vlan_id: int | None mtu: int gateway: str | None dns: list[str] dhcp_enabled: bool tags: list[str] description: str | None class SubnetRead(OrmModel): id: str network_id: str cidr: str gateway: str | None dns: list[str] dhcp_enabled: bool class IpAddressRead(OrmModel): id: str subnet_id: str subnet_cidr: str | None = None address: str status: str workload_id: str | None workload_name: str | None = None workload_external_id: str | None = None note: str | None class TenantRead(OrmModel): id: str name: str description: str | None class ProjectRead(OrmModel): id: str tenant_id: str name: str description: str | None class SecurityGroupRead(OrmModel): id: str project_id: str | None name: str description: str | None class SecurityRuleRead(OrmModel): id: str security_group_id: str direction: str action: str protocol: str source: str destination: str port: str | None priority: int logging: bool description: str | None class PolicyRead(OrmModel): id: str project_id: str | None name: str version: int enabled: bool enforcement_mode: str definition: dict[str, Any] last_compiled: dict[str, Any] | None class WorkloadInsight(BaseModel): workload: WorkloadRead assigned_ips: list[IpAddressRead] traffic: list[dict[str, Any]] matching_policies: list[PolicyRead] effective_decision: str audit_mode_notes: list[str] class ServiceCatalogRead(OrmModel): id: str name: str protocol: str ports: str editable: bool class FirewallPreview(BaseModel): policy_id: str dry_run: bool = True generated_rules: list[dict[str, Any]] warnings: list[str] conflicts: list[str] class JobRead(OrmModel): id: str kind: str status: str progress: int started_at: datetime | None finished_at: datetime | None logs: list[str] error: str | None class AuditLogRead(OrmModel): id: str created_at: datetime user_id: str | None action: str object_type: str object_id: str | None old_values: dict[str, Any] | None new_values: dict[str, Any] | None ip_address: str | None user_agent: str | None result: str error_text: str | None