"""资产数据库模型 包含四种核心资产模型: - Asset: 资产主表(统一登记 VPS/域名/AI 账号/Cloudflare 等一切资产) - VPSDetail: VPS 资产详情(与 Asset 一对一关联) - DomainDetail: 域名资产详情(与 Asset 一对一关联) - AIAccount: AI Agent 账号详情(与 Asset 一对一关联) """ from datetime import date, datetime from enum import Enum from typing import Optional from sqlmodel import Field, SQLModel class AssetType(str, Enum): """资产类型""" VPS = "vps" # 云服务器 DOMAIN = "domain" # 域名 AI_AGENT = "ai_agent" # AI Agent 账号(OpenAI/Kimi/DeepSeek 等) CLOUDFLARE = "cloudflare" # Cloudflare 账号/子资产(DNS/Workers/R2/Tunnel) OTHER = "other" # 其他 class AssetStatus(str, Enum): """资产运行状态""" ACTIVE = "active" # 正常使用中 EXPIRED = "expired" # 已过期 STOPPED = "stopped" # 已停止(不再使用) CANCELLED = "cancelled" # 已注销 UNKNOWN = "unknown" # 未知(待确认) class Asset(SQLModel, table=True): """资产主表:所有资产在此统一登记,用于续费提醒与状态总览""" __tablename__ = "assets" id: Optional[int] = Field(default=None, primary_key=True) name: str = Field(index=True, description="资产名称,如:博客主站 VPS") asset_type: AssetType = Field(index=True, description="资产类型") provider: str = Field(index=True, description="服务商 slug,如 aliyun/vultr/namesilo/openai") provider_id: Optional[int] = Field( default=None, foreign_key="providers.id", index=True, description="关联平台 ID" ) external_id: Optional[str] = Field( default=None, index=True, description="平台内实例 ID(用于 SDK 同步去重)" ) renewal_cycle: Optional[str] = Field( default=None, description="续费周期:monthly/quarterly/yearly" ) renew_url: Optional[str] = Field( default=None, description="续费网址(去哪续费)" ) cancel_url: Optional[str] = Field( default=None, description="取消订阅网址(去哪取消)" ) account: Optional[str] = Field( default=None, description="所属账号标识(如 Cloudflare 账号邮箱)" ) expiry_date: Optional[date] = Field( default=None, index=True, description="到期/续费日期,用于续费提醒" ) auto_renew: bool = Field(default=False, description="是否自动续费") cost: float = Field(default=0, description="单次续费费用") currency: str = Field(default="USD", description="币种(USD/CNY/JPY)") status: AssetStatus = Field(default=AssetStatus.ACTIVE, index=True, description="运行状态") is_archived: bool = Field( default=False, description="已归档:不再使用但可能仍在续费,需重点排查" ) remark: Optional[str] = Field(default=None, description="备注") created_at: datetime = Field(default_factory=datetime.utcnow, description="创建时间") updated_at: datetime = Field( default_factory=datetime.utcnow, sa_column_kwargs={"onupdate": datetime.utcnow}, description="更新时间", ) class VPSDetail(SQLModel, table=True): """VPS 资产详情(一对一关联 Asset)""" __tablename__ = "vps_details" id: Optional[int] = Field(default=None, primary_key=True) asset_id: int = Field( foreign_key="assets.id", unique=True, index=True, description="关联资产 ID" ) ip_address: str = Field(index=True, description="公网 IP") tailscale_ip: Optional[str] = Field(default=None, description="Tailscale 内网 IP") region: Optional[str] = Field(default=None, description="机房区域,如 Japan/US West") os: Optional[str] = Field(default=None, description="操作系统,如 Debian 12") cpu_cores: int = Field(default=1, description="CPU 核数") memory_gb: float = Field(default=1, description="内存大小(GB)") disk_gb: int = Field(default=20, description="磁盘大小(GB)") bandwidth_gb: Optional[int] = Field(default=None, description="月流量配额(GB)") ssh_port: int = Field(default=22, description="SSH 端口") panel_url: Optional[str] = Field(default=None, description="服务商管理面板 URL") ssh_user: Optional[str] = Field(default=None, description="SSH 登录用户,如 root") login_method: Optional[str] = Field( default="key", description="登录方式:key/password/other" ) ssh_key_encrypted: Optional[str] = Field(default=None, description="加密的 SSH 私钥") password_encrypted: Optional[str] = Field(default=None, description="加密的登录密码") purpose: Optional[str] = Field(default=None, description="用途 / 运行的项目") class DomainDetail(SQLModel, table=True): """域名资产详情(一对一关联 Asset)""" __tablename__ = "domain_details" id: Optional[int] = Field(default=None, primary_key=True) asset_id: int = Field( foreign_key="assets.id", unique=True, index=True, description="关联资产 ID" ) domain_name: str = Field(index=True, description="域名,如 example.com") registrar: Optional[str] = Field( default=None, description="注册商:namesilo/新网/腾讯/阿里/dynadot/cloudflare" ) dns_provider: Optional[str] = Field( default=None, description="DNS 托管商:cloudflare/阿里云/注册商默认" ) cloudflare_account: Optional[str] = Field( default=None, description="归属的 Cloudflare 账号标识" ) is_using: bool = Field(default=True, description="是否仍在实际使用") redirect_target: Optional[str] = Field( default=None, description="若为跳转用途域名,填写目标地址" ) bind_asset_id: Optional[int] = Field( default=None, index=True, description="绑定的资产 ID(如关联的 VPS/项目)" ) class AIAccount(SQLModel, table=True): """AI Agent 账号资产详情(一对一关联 Asset)""" __tablename__ = "ai_accounts" id: Optional[int] = Field(default=None, primary_key=True) asset_id: int = Field( foreign_key="assets.id", unique=True, index=True, description="关联资产 ID" ) provider: str = Field( index=True, description="服务商:openai/minimax/kimi/agnes/deepseek" ) api_key: Optional[str] = Field( default=None, description="(已弃用)明文 API Key,新数据用 api_key_encrypted" ) api_key_encrypted: Optional[str] = Field( default=None, description="加密的 API Key(Fernet)" ) plan: Optional[str] = Field(default=None, description="订阅计划,如 ChatGPT Plus") balance: Optional[float] = Field(default=None, description="当前余额") currency: str = Field(default="USD", description="币种") monthly_usage: Optional[float] = Field(default=None, description="本月用量") monthly_limit: Optional[float] = Field(default=None, description="月度限额") last_synced_at: Optional[datetime] = Field( default=None, description="最近一次自动同步余额/用量的时间" ) class CloudflareDetail(SQLModel, table=True): """Cloudflare 子资产详情(一对一关联 Asset) 一个 Cloudflare 账号可有多个子资产(zone/worker/r2/tunnel/mail)。 每个子资产是一个 Asset(asset_type=cloudflare) + CloudflareDetail。 """ __tablename__ = "cloudflare_details" id: Optional[int] = Field(default=None, primary_key=True) asset_id: int = Field( foreign_key="assets.id", unique=True, index=True, description="关联资产 ID" ) account_email: Optional[str] = Field( default=None, index=True, description="Cloudflare 账号邮箱(区分多账号)" ) sub_type: str = Field( default="zone", index=True, description="子资产类型:zone/worker/r2/tunnel/mail/dns_record/other" ) sub_name: Optional[str] = Field( default=None, description="子资产名称(如 worker 名、bucket 名)" ) zone_name: Optional[str] = Field( default=None, index=True, description="关联的 zone/域名" ) status: Optional[str] = Field(default=None, description="子资产状态")