"""云资源管理动作相关 Schema 动作请求体单独建模而非直接透传 dict:period_months 必须有上界, 否则一次误传 9999 就会真实扣掉一笔巨额续费账单。 """ from typing import Optional from pydantic import Field, field_validator from sqlmodel import SQLModel #: 允许的实例动作 VALID_ACTIONS = ("start", "stop", "reboot", "renew") #: 允许的 DNS 记录类型(各厂商交集,避免写入后被 API 拒绝) VALID_RECORD_TYPES = ( "A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV", "CAA", "LOC", "URL", ) class InstanceActionRequest(SQLModel): """实例动作请求""" action: str = Field(description="start/stop/reboot/renew") period_months: int = Field(default=1, ge=1, le=60, description="续费周期(月),仅 renew 用") @field_validator("action") @classmethod def _check_action(cls, v: str) -> str: if v not in VALID_ACTIONS: raise ValueError(f"action 必须是 {'/'.join(VALID_ACTIONS)} 之一,收到:{v}") return v class DNSRecordIn(SQLModel): """DNS 记录输入(create/update 共用) name 传完整域名(如 www.example.com)或子域(如 www)均可, 各适配器按自身约定拆分;content 即记录值。 """ name: str = Field(min_length=1, description="记录名,完整域名或子域") type: str = Field(default="A", description="记录类型") content: str = Field(min_length=1, description="记录值") ttl: Optional[int] = Field(default=None, ge=60, le=86400, description="TTL 秒,留空用厂商默认") proxied: Optional[bool] = Field(default=None, description="Cloudflare 专用:是否走代理") priority: Optional[int] = Field(default=None, ge=0, le=65535, description="MX/SRV 优先级") comment: Optional[str] = Field(default=None, max_length=255, description="备注") @field_validator("type") @classmethod def _check_type(cls, v: str) -> str: upper = (v or "").upper() if upper not in VALID_RECORD_TYPES: raise ValueError(f"不支持的记录类型:{v}(可选 {'/'.join(VALID_RECORD_TYPES)})") return upper