from __future__ import annotations from typing import Literal from pydantic import BaseModel, Field, field_validator, model_validator AccountProvider = Literal["aws", "cloudflare"] class AccountCreate(BaseModel): provider: AccountProvider name: str = Field(min_length=1, max_length=80) use_default_aws_credentials: bool = False aws_access_key_id: str | None = Field(default=None, max_length=128) aws_secret_access_key: str | None = Field(default=None, max_length=256) aws_session_token: str | None = Field(default=None, max_length=4096) cloudflare_api_token: str | None = Field(default=None, max_length=2048) @field_validator("name") @classmethod def normalize_name(cls, value: str) -> str: normalized = value.strip() if not normalized: raise ValueError("账号名称不能为空") return normalized @field_validator( "aws_access_key_id", "aws_secret_access_key", "aws_session_token", "cloudflare_api_token", mode="before", ) @classmethod def normalize_secret(cls, value: object) -> object: if value is None: return None normalized = str(value).strip() return normalized or None @model_validator(mode="after") def validate_provider_credentials(self) -> AccountCreate: if self.provider == "aws": if self.cloudflare_api_token: raise ValueError("AWS 账号不能包含 Cloudflare API Token") if self.use_default_aws_credentials: if self.aws_access_key_id or self.aws_secret_access_key or self.aws_session_token: raise ValueError("使用 AWS 默认凭据链时不能同时填写 API 密钥") elif not (self.aws_access_key_id and self.aws_secret_access_key): raise ValueError("AWS Access Key ID 和 Secret Access Key 必须同时填写") else: if self.use_default_aws_credentials or any( (self.aws_access_key_id, self.aws_secret_access_key, self.aws_session_token) ): raise ValueError("Cloudflare 账号不能包含 AWS 凭据") if not self.cloudflare_api_token: raise ValueError("Cloudflare API Token 不能为空") return self class AccountUpdate(BaseModel): config_version: int = Field(ge=1) name: str | None = Field(default=None, min_length=1, max_length=80) use_default_aws_credentials: bool | None = None aws_access_key_id: str | None = Field(default=None, max_length=128) aws_secret_access_key: str | None = Field(default=None, max_length=256) aws_session_token: str | None = Field(default=None, max_length=4096) cloudflare_api_token: str | None = Field(default=None, max_length=2048) @field_validator("name") @classmethod def normalize_name(cls, value: str | None) -> str | None: if value is None: return None normalized = value.strip() if not normalized: raise ValueError("账号名称不能为空") return normalized @field_validator( "aws_access_key_id", "aws_secret_access_key", "aws_session_token", "cloudflare_api_token", mode="before", ) @classmethod def normalize_secret(cls, value: object) -> object: if value is None: return None normalized = str(value).strip() return normalized or None @model_validator(mode="after") def validate_update(self) -> AccountUpdate: fields = self.model_fields_set access_fields = {"aws_access_key_id", "aws_secret_access_key"} supplied_access_fields = fields & access_fields if supplied_access_fields and supplied_access_fields != access_fields: raise ValueError("AWS Access Key ID 和 Secret Access Key 必须同时填写") if bool(self.aws_access_key_id) != bool(self.aws_secret_access_key): raise ValueError("AWS Access Key ID 和 Secret Access Key 必须同时填写") if not fields - {"config_version"}: raise ValueError("请至少修改一项账号配置") return self class AccountSecretFlags(BaseModel): aws_access_key_id: bool = False aws_secret_access_key: bool = False aws_session_token: bool = False cloudflare_api_token: bool = False class AccountView(BaseModel): id: str provider: AccountProvider name: str use_default_aws_credentials: bool | None config_version: int secrets_configured: AccountSecretFlags created_at: str updated_at: str archived_at: str | None