架构设计¶
UniDNS 是单二进制 Rust 应用:axum Web 服务 + SQLite 存储 + Astro 静态前端
(rust-embed 编译期嵌入)。核心分层为 Web 层 → 同步引擎 → 平台适配器,
全部状态经 SharedState(Arc<AppState>)共享。
总体结构¶
flowchart LR
U[浏览器] -->|静态页面| FE[Astro 前端<br>rust-embed 嵌入]
U -->|/api/v1/*| API[axum Web 层<br>会话鉴权]
API --> SYNC[同步引擎]
API --> DB[(SQLite<br>WAL)]
SYNC --> DB
SYNC --> P[平台适配器<br>Provider trait]
P --> ALI[阿里云]
P --> DP[DNSPod 国内/国际]
P --> EO[EdgeOne 国内/国际]
P --> CF[CloudFlare]
模块划分(src/)¶
| 模块 | 职责 |
|---|---|
main.rs |
入口:加载配置、初始化密钥与连接池、启动 HTTP 服务 |
config.rs |
配置加载:默认值 ← 配置文件 ← 环境变量 |
crypto.rs |
凭据 AES-256-GCM 加解密、主密钥管理、管理员口令 Argon2id |
db.rs |
SQLite 连接池(WAL + 外键 + 忙等待)与迁移 |
models.rs |
数据行结构(AccountRow / DomainRow / RecordRow 等) |
providers/ |
平台适配器:DnsProvider trait + 六个实现 + 工厂 |
sync.rs |
同步引擎:账户并发、限流退避、运行记录 |
web/ |
axum 路由:auth / accounts / records / sync_api + 会话鉴权 |
frontend/ |
静态前端服务(rust-embed 嵌入 frontend/dist) |
error.rs |
统一错误 AppError:到 HTTP 状态码与 {"error": {code, message}} 响应体的映射 |
平台适配器¶
所有平台实现统一 trait(providers/mod.rs):
#[async_trait]
pub trait DnsProvider: Send + Sync {
async fn verify(&self) -> Result<(), ProviderError>;
async fn list_domains(&self) -> Result<Vec<ProviderDomain>, ProviderError>;
async fn list_records(&self, zone: &ProviderZone) -> Result<Vec<ProviderRecord>, ProviderError>;
async fn create_record(&self, zone: &ProviderZone, input: &RecordInput) -> Result<String, ProviderError>;
async fn update_record(&self, zone: &ProviderZone, record_id: &str, input: &RecordInput) -> Result<(), ProviderError>;
async fn delete_record(&self, zone: &ProviderZone, record_id: &str) -> Result<(), ProviderError>;
async fn set_record_status(&self, zone: &ProviderZone, record_id: &str, enabled: bool) -> Result<(), ProviderError>;
async fn set_record_remark(&self, zone: &ProviderZone, record_id: &str, remark: &str) -> Result<(), ProviderError>;
async fn set_domain_remark(&self, zone: &ProviderZone, remark: &str) -> Result<(), ProviderError>;
}
- 凭据以 JSON 字符串传入工厂
build_provider(type, credentials_json, proxy), 由各适配器自行解析;失败返回ProviderError::Auth。 ProviderError细分 Auth / RateLimited(含retry_after)/ NotFound / Unsupported / Network / Api 六类,error.rs据此映射 HTTP 状态码。- 签名实现各自独立:阿里云 RPC 签名(HMAC-SHA1)、腾讯云系列 TC3-HMAC-SHA256 (DNSPod 与 EdgeOne 共用)、CloudFlare Bearer Token。
同步引擎(sync.rs)¶
- 并发模型:
tokio::spawn每账户一个任务,Semaphore(3)控制并发上限; 单账户失败不影响其余,结果聚合为Vec<SyncOutcome>。 - 限流退避:捕获
ProviderError::RateLimited后按retry_after(上限 60 秒, 缺省 5 秒)等待并重试一次。 - 运行记录:每次同步写入
sync_runs(状态 / 起止时间 / 错误 / 统计 JSON), Web 层可查询历史;同一账户重复触发会被互斥标记拦截(sync_running)。 - 可同步开关:
settings表存储域名级同步开关,关闭的域名不参与同步, 本地备注不回写平台。
数据模型(migrations/0001_init.sql)¶
accounts:平台账户。provider_type受 CHECK 约束(六值枚举),credentials_enc为 AES-256-GCM 密文(nonce 前置),credentials_mask存打码展示串。domains:同步下来的域名,UNIQUE(account_id, name);remark为本地备注, 不回写平台。records:解析记录,含上游provider_record_id便于回写;pending_sync标记待同步变更。settings:KV 配置(管理员口令哈希、同步开关等)。sync_runs:同步运行历史。
前端(frontend/)¶
Astro 5 静态站点,构建产物输出到 frontend/dist,由 rust-embed 在
cargo build 时嵌入二进制。开发时 astro dev 将 /api 代理到本地 18686,
无需改动后端。