feat: 云厂商SDK适配层(Vultr/DO/Cloudflare/阿里云/腾讯云)+ 同步服务 + 测试连接/同步接口与前端
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
"""云厂商 SDK 适配层 — 抽象基类与标准化数据结构
|
||||
|
||||
各云厂商适配器继承 BaseAdapter,实现统一接口,将平台特定的 API 返回
|
||||
转换为标准化的资产结构(NormalizedVPS / NormalizedDomain / AccountInfo),
|
||||
供同步服务写入数据库。这样上层逻辑无需关心各平台 API 差异。
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class NormalizedVPS:
|
||||
"""标准化的 VPS 实例"""
|
||||
|
||||
external_id: str
|
||||
name: str
|
||||
ip_address: Optional[str] = None
|
||||
region: Optional[str] = None
|
||||
os: Optional[str] = None
|
||||
cpu_cores: Optional[int] = None
|
||||
memory_gb: Optional[float] = None
|
||||
disk_gb: Optional[int] = None
|
||||
status: str = "active"
|
||||
monthly_cost: Optional[float] = None
|
||||
currency: str = "USD"
|
||||
raw: dict = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
@dataclass
|
||||
class NormalizedDomain:
|
||||
"""标准化的域名"""
|
||||
|
||||
external_id: str
|
||||
domain_name: str
|
||||
registrar: Optional[str] = None
|
||||
expiry_date: Optional[str] = None # ISO 日期字符串 YYYY-MM-DD
|
||||
status: str = "active"
|
||||
raw: dict = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AccountInfo:
|
||||
"""标准化的账户信息"""
|
||||
|
||||
balance: Optional[float] = None
|
||||
currency: str = "USD"
|
||||
pending_charges: Optional[float] = None
|
||||
raw: dict = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
def _overridden(instance: "BaseAdapter", method_name: str) -> bool:
|
||||
"""判断子类是否重写了某方法(用于能力探测)"""
|
||||
return type(instance).__dict__.get(method_name) is not None
|
||||
|
||||
|
||||
class BaseAdapter(ABC):
|
||||
"""云厂商适配器抽象基类
|
||||
|
||||
子类需:
|
||||
- 通过 @register("xxx-sdk") 注册到注册表
|
||||
- 设置 required_config(所需凭证字段,供前端表单提示)
|
||||
- 实现 test_connection;按需重写 list_vps / list_domains / get_account
|
||||
"""
|
||||
|
||||
#: 适配器所需的配置字段名(如 ["api_key"] 或 ["access_key_id", "access_key_secret"])
|
||||
required_config: list = []
|
||||
|
||||
def __init__(self, config: Optional[dict] = None):
|
||||
self.config = config or {}
|
||||
|
||||
@abstractmethod
|
||||
def test_connection(self) -> dict:
|
||||
"""测试连接 / 凭证有效性,返回 {"ok": bool, "message": str, ...}"""
|
||||
|
||||
def list_vps(self) -> list:
|
||||
raise NotImplementedError(f"{type(self).__name__} 未实现 list_vps")
|
||||
|
||||
def list_domains(self) -> list:
|
||||
raise NotImplementedError(f"{type(self).__name__} 未实现 list_domains")
|
||||
|
||||
def get_account(self) -> AccountInfo:
|
||||
raise NotImplementedError(f"{type(self).__name__} 未实现 get_account")
|
||||
|
||||
def capabilities(self) -> dict:
|
||||
"""返回适配器实际支持的能力(是否重写了相应方法)"""
|
||||
return {
|
||||
"list_vps": _overridden(self, "list_vps"),
|
||||
"list_domains": _overridden(self, "list_domains"),
|
||||
"get_account": _overridden(self, "get_account"),
|
||||
}
|
||||
Reference in New Issue
Block a user