- Console:React 19 + antd 6.6.5 前端(登录 admin/admin)+ Hono 管理 API (Nodes/Domains/Mailboxes/Emails/Webhooks/Keys/OAuth/Admin) - Node:Bun 转发节点(HMAC 签名、有限重试、心跳、结构化日志、优雅退出) - Email Worker:Cloudflare email handler + 本地联调入口 - shared:HMAC 协议(docs/04)、wmh_ Key(32 位)、AES-GCM 加密、SSRF 防护 - 邮件管线:postal-mime 解析、附件、catch-all、限额、malformed 降级、TTL 1 天 - Webhook:签名投递、指数退避重试、SSRF 校验、投递日志 - API v1 + MCP(JSON-RPC,六工具,凭据 kind 隔离) - Cloudflare OAuth:真实 + 本地 sim 模式,自动安装管线(幂等,无需手工命令) - 测试:bun test 41 例(协议安全/管线/TTL/Webhook/SSRF/MCP/OAuth/鉴权)+ Playwright UI 冒烟 - LOGO/favicon 由 codex CLI 生成
174 lines
5.0 KiB
SQL
174 lines
5.0 KiB
SQL
-- WmailHub D1 初始schema
|
|
-- Console 运行时也会在本地模式下自动执行等价初始化(幂等)
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
username TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL
|
|
);
|
|
|
|
-- 节点(转发平面)
|
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
cloudflare_account_id TEXT,
|
|
credential_id TEXT,
|
|
status TEXT NOT NULL DEFAULT 'pending_oauth', -- pending_oauth | provisioning | offline | online | revoked
|
|
version TEXT,
|
|
last_seen_at TEXT,
|
|
ingress_url TEXT,
|
|
ingress_secret_enc TEXT,
|
|
enrollment_token TEXT,
|
|
enrollment_expires_at TEXT,
|
|
provision_steps TEXT, -- JSON: 各安装步骤状态
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
-- 节点凭据:secret_hash 为摘要,secret_enc 为 AES-GCM 密文(HMAC 验签需要原文,加密存储防拖库泄露)
|
|
CREATE TABLE IF NOT EXISTS node_credentials (
|
|
id TEXT PRIMARY KEY,
|
|
node_id TEXT NOT NULL,
|
|
secret_hash TEXT NOT NULL,
|
|
secret_enc TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'active', -- active | revoked
|
|
created_at TEXT NOT NULL,
|
|
revoked_at TEXT
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_node_credentials_node ON node_credentials(node_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS domains (
|
|
id TEXT PRIMARY KEY,
|
|
cloudflare_account_id TEXT NOT NULL,
|
|
node_id TEXT,
|
|
name TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'pending', -- pending | active | error
|
|
worker_id TEXT,
|
|
zone_id TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
UNIQUE(cloudflare_account_id, name)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS mailboxes (
|
|
id TEXT PRIMARY KEY,
|
|
domain_id TEXT NOT NULL,
|
|
address TEXT NOT NULL UNIQUE,
|
|
status TEXT NOT NULL DEFAULT 'active', -- active | disabled
|
|
retention_seconds INTEGER, -- NULL = 使用全局默认 86400
|
|
is_catch_all INTEGER NOT NULL DEFAULT 0,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_mailboxes_domain ON mailboxes(domain_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS emails (
|
|
id TEXT PRIMARY KEY,
|
|
node_id TEXT,
|
|
mailbox_id TEXT,
|
|
message_id TEXT,
|
|
from_address TEXT,
|
|
to_addresses TEXT, -- JSON array
|
|
cc_addresses TEXT, -- JSON array
|
|
subject TEXT,
|
|
text_body TEXT,
|
|
html_body TEXT,
|
|
raw_storage_key TEXT,
|
|
size INTEGER NOT NULL DEFAULT 0,
|
|
received_at TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_emails_expires ON emails(expires_at);
|
|
CREATE INDEX IF NOT EXISTS idx_emails_mailbox ON emails(mailbox_id);
|
|
CREATE INDEX IF NOT EXISTS idx_emails_received ON emails(received_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_emails_message_id ON emails(message_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS email_attachments (
|
|
id TEXT PRIMARY KEY,
|
|
email_id TEXT NOT NULL,
|
|
filename TEXT,
|
|
content_type TEXT,
|
|
size INTEGER NOT NULL DEFAULT 0,
|
|
storage_key TEXT,
|
|
content_id TEXT
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_attachments_email ON email_attachments(email_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS webhooks (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
endpoint TEXT NOT NULL,
|
|
secret TEXT NOT NULL,
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS webhook_deliveries (
|
|
id TEXT PRIMARY KEY,
|
|
webhook_id TEXT NOT NULL,
|
|
email_id TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'pending', -- pending | success | failed | exhausted
|
|
attempt_count INTEGER NOT NULL DEFAULT 0,
|
|
next_retry_at TEXT,
|
|
last_error TEXT,
|
|
delivered_at TEXT,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_deliveries_retry ON webhook_deliveries(status, next_retry_at);
|
|
CREATE INDEX IF NOT EXISTS idx_deliveries_webhook ON webhook_deliveries(webhook_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS oauth_connections (
|
|
id TEXT PRIMARY KEY,
|
|
provider TEXT NOT NULL,
|
|
account_id TEXT,
|
|
encrypted_access_token TEXT NOT NULL,
|
|
refresh_token TEXT,
|
|
expires_at TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
-- OAuth CSRF state(node_id 记录本次授权为哪个节点安装)
|
|
CREATE TABLE IF NOT EXISTS oauth_states (
|
|
state TEXT PRIMARY KEY,
|
|
node_id TEXT,
|
|
created_at TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL
|
|
);
|
|
|
|
-- Node 请求 nonce 防重放(唯一约束即拒绝重复)
|
|
CREATE TABLE IF NOT EXISTS nonces (
|
|
nonce TEXT PRIMARY KEY,
|
|
node_id TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL
|
|
);
|
|
|
|
-- Console API / MCP 凭据(wmh_ 开头,总长 32;库中只存 SHA-256)
|
|
CREATE TABLE IF NOT EXISTS api_credentials (
|
|
id TEXT PRIMARY KEY,
|
|
kind TEXT NOT NULL, -- api | mcp
|
|
name TEXT,
|
|
key_hash TEXT NOT NULL UNIQUE,
|
|
key_last4 TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
revoked_at TEXT,
|
|
last_used_at TEXT
|
|
);
|
|
|
|
-- 本地 sim 模式:模拟 Cloudflare API 已创建的资源,供 UI 展示安装结果
|
|
CREATE TABLE IF NOT EXISTS cf_sim_resources (
|
|
id TEXT PRIMARY KEY,
|
|
kind TEXT NOT NULL, -- email_worker | routing_rule | zone | account
|
|
ref_id TEXT,
|
|
payload TEXT,
|
|
created_at TEXT NOT NULL
|
|
);
|