feat: WmailHub v1.0.0 —— Cloudflare Email Workers 邮件接收与转发平台(本地可用版本)
- 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 生成
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
-- 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
|
||||
);
|
||||
Reference in New Issue
Block a user