┌─────────────────────────────────────────────────────┐
│ 用户层 │
│ 浏览器 / PWA / 移动设备 │
└──────────────────┬──────────────────────────────────┘
│ HTTPS
▼
┌─────────────────────────────────────────────────────┐
│ Cloudflare Edge │
│ CDN + DDoS 保护 + SSL + 全球分布 │
└──────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Cloudflare Workers(应用层) │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │
│ │ 路由处理 │ │ API服务 │ │ UI渲染 │ │
│ └─────────────┘ └─────────────┘ └────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │
│ │ 认证系统 │ │ 加密系统 │ │ 监控系统 │ │
│ └─────────────┘ └─────────────┘ └────────────┘ │
└──────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Cloudflare KV(数据存储层) │
│ 全球分布式键值存储 + 自动加密 + 低延迟 │
└─────────────────────────────────────────────────────┘
- 无服务器架构: 基于 Cloudflare Workers,无需维护服务器
- 全球分布: 在全球 300+ 个城市的边缘节点上运行
- 高可用性: 自动故障转移和负载均衡
- 低延迟: 就近服务,平均响应时间 < 50ms
- 自动扩展: 根据流量自动扩缩容
- 零冷启动: V8 隔离技术,无冷启动延迟
| 技术 | 用途 | 版本 |
|---|---|---|
| Cloudflare Workers | 运行时环境 | V8 Engine |
| ES Modules | 模块系统 | ES2022 |
| Web Crypto API | 加密操作 | 标准 API |
| Cloudflare KV | 数据存储 | - |
| 技术 | 用途 | 说明 |
|---|---|---|
| HTML5 | 页面结构 | 语义化标签 |
| CSS3 | 样式系统 | 模块化 CSS |
| JavaScript (ES2022) | 交互逻辑 | 原生 JS,无框架 |
| PWA | 应用增强 | Service Worker + Manifest |
| jsQR | 二维码识别 | CDN 引入 |
| qrcode-generator | 二维码生成 | CDN 引入 |
| 工具 | 用途 |
|---|---|
| Wrangler CLI | 开发和部署工具 |
| Git | 版本控制 |
| Node.js | 构建工具运行环境 |
src/
├── worker.js # 🎯 Worker 主入口
│ # - Fetch 事件处理
│ # - CORS 处理
│ # - 全局错误捕获
│ # - 监控系统初始化
│
├── router/
│ └── handler.js # 🛣️ 路由处理器
│ # - 路径解析和分发
│ # - 认证检查
│ # - API 路由映射
│
├── api/
│ ├── secrets/ # 🔌 密钥管理 API(模块化)
│ │ ├── index.js # 统一导出(Barrel Export)
│ │ ├── shared.js # 共享工具(saveSecretsToKV, getAllSecrets)
│ │ ├── crud.js # CRUD 操作
│ │ ├── batch.js # 批量导入
│ │ ├── backup.js # 备份创建和列表
│ │ ├── restore.js # 备份恢复和导出
│ │ └── otp.js # OTP 生成
│ └── favicon.js # 🌐 Favicon 代理 API
│
├── otp/
│ └── generator.js # 🔐 OTP 算法实现
│ # - TOTP (RFC 6238)
│ # - HOTP (RFC 4226)
│ # - Base32 编解码
│
├── ui/
│ ├── page.js # 🎨 主页面生成
│ │ # - HTML 结构
│ │ # - 样式集成
│ │ # - 脚本集成
│ │
│ ├── manifest.js # 📱 PWA Manifest
│ │ # - 应用信息
│ │ # - 图标定义
│ │ # - 快捷方式
│ │
│ ├── serviceworker.js # ⚙️ Service Worker
│ │ # - 缓存策略
│ │ # - 离线支持
│ │ # - CDN 资源缓存
│ │
│ ├── scripts/ # 📜 前端 JavaScript 模块
│ │ ├── index.js # 模块集成入口
│ │ ├── state.js # 全局状态管理
│ │ ├── auth.js # 认证逻辑
│ │ ├── core.js # 核心业务逻辑
│ │ ├── utils.js # 工具函数
│ │ └── pwa.js # PWA 功能
│ │
│ └── styles/ # 🎨 前端 CSS 模块
│ ├── index.js # 样式集成入口
│ ├── base.js # 基础样式
│ ├── components.js # 组件样式
│ ├── modals.js # 模态框样式
│ └── responsive.js # 响应式样式
│
└── utils/ # 🛠️ 工具模块
├── auth.js # 🔑 认证系统
│ # - Token 验证
│ # - HttpOnly Cookie
│ # - 自动刷新
│
├── backup.js # 💾 智能备份系统
│ # - 事件驱动备份
│ # - 防抖机制
│ # - 自动清理
│
├── constants.js # 📋 常量定义
│ # - KV 键名
│ # - 配置常量
│
├── crypto.js # 🔐 加密工具
│ # - HMAC-SHA1/256
│ # - Base32 编解码
│
├── encryption.js # 🔒 数据加密
│ # - AES-GCM 256
│ # - 密钥派生
│ # - 自动加解密
│
├── errors.js # ❌ 统一错误分类
│ # - 自定义错误类
│ # - 错误处理和响应格式
│
├── logger.js # 📝 日志系统
│ # - 结构化日志
│ # - 性能计时
│ # - 日志级别
│
├── monitoring.js # 📊 监控系统
│ # - 错误追踪
│ # - 性能监控
│ # - Sentry 集成
│
├── rateLimit.js # 🛡️ 请求限流
│ # - 固定窗口计数器算法
│ # - 可配置策略
│ # - 基于 KV 存储
│
├── response.js # 📡 响应工具
│ # - 标准化响应格式
│ # - CORS 头处理
│
├── security.js # 🔒 安全工具
│ # - CORS 配置
│ # - CSP 头
│ # - 预检请求
│
└── validation.js # ✅ 数据验证
# - Base32 验证
# - 输入校验
# - 业务规则检查
职责: Cloudflare Worker 的入口点,处理所有传入请求
核心功能:
export default {
async fetch(request, env, ctx) {
// 1. 初始化日志和监控
// 2. 处理 CORS 预检请求
// 3. 开始请求追踪
// 4. 调用路由处理器
// 5. 记录响应和性能指标
// 6. 全局错误处理
},
async scheduled(event, env, ctx) {
// 定时任务:自动备份(每天)
},
};集成的系统:
- 日志系统 (
logger.js) - 监控系统 (
monitoring.js) - 路由处理 (
router/handler.js) - CORS 处理 (
utils/security.js)
职责: 解析 URL 路径并分发到对应的处理函数
路由表:
| 路由 | 方法 | 处理器 | 认证 |
|---|---|---|---|
/ |
GET | createMainPage() |
❌ |
/setup |
GET | createSetupPage() |
❌ |
/manifest.json |
GET | createManifest() |
❌ |
/sw.js |
GET | createServiceWorker() |
❌ |
/icon-*.png |
GET | createDefaultIcon() |
❌ |
/modules/{name} |
GET | getModuleCode() |
✅ |
/api/setup |
POST | handleFirstTimeSetup() |
❌ |
/api/login |
POST | handleLogin() |
❌ |
/api/refresh-token |
POST | handleRefreshToken() |
✅ |
/api/secrets |
GET | handleGetSecrets() |
✅ |
/api/secrets |
POST | handleAddSecret() |
✅ |
/api/secrets/{id} |
PUT | handleUpdateSecret() |
✅ |
/api/secrets/{id} |
DELETE | handleDeleteSecret() |
✅ |
/api/secrets/batch |
POST | handleBatchAddSecrets() |
✅ |
/api/backup |
GET | handleGetBackups() |
✅ |
/api/backup |
POST | handleBackupSecrets() |
✅ |
/api/backup/restore |
POST | handleRestoreBackup() |
✅ |
/api/backup/export/{key} |
GET | handleExportBackup() |
✅ |
/api/favicon/{domain} |
GET | handleFaviconProxy() |
✅ |
/otp/{secret} |
GET | handleGenerateOTP() |
❌ |
职责: 处理 2FA 密钥的 CRUD 操作和备份管理
模块化组织:
shared.js- 共享工具函数(saveSecretsToKV, getAllSecrets)crud.js- CRUD 操作(GET/POST/PUT/DELETE)batch.js- 批量导入backup.js- 备份创建和列表restore.js- 备份恢复和导出otp.js- OTP 生成index.js- 统一导出(Barrel Export)
核心功能:
async function saveSecretsToKV(env, secrets, reason) {
// 1. 排序密钥
sortSecretsByName(secrets);
// 2. 加密数据(如果配置了 ENCRYPTION_KEY)
const encryptedData = await encryptSecrets(secrets, env);
// 3. 保存到 KV
await env.SECRETS_KV.put('secrets', encryptedData);
// 4. 触发事件驱动备份
await triggerBackup(secrets, env, { reason });
}export async function handleAddSecret(request, env) {
// 1. 检查限流
const rateLimitResult = await checkRateLimit(request, env, RATE_LIMIT_PRESETS.api);
if (!rateLimitResult.allowed) {
return createRateLimitResponse(rateLimitResult);
}
// 2. 处理请求...
}职责: 实现 TOTP/HOTP 算法,生成一次性密码
支持的算法:
/**
* 算法流程:
* 1. 计算时间计数器 (counter = floor(currentTime / 30))
* 2. 将 Base32 密钥解码为字节数组
* 3. 使用 HMAC-SHA1 计算哈希值
* 4. 动态截断生成 6 位数字 OTP
*/
export async function generateTOTP(secret, options = {}) {
const {
timeStep = 30, // 时间步长(秒)
digits = 6, // OTP 长度
algorithm = 'SHA1', // 哈希算法
} = options;
const counter = Math.floor(Date.now() / 1000 / timeStep);
return await generateHOTP(secret, counter, { digits, algorithm });
}export async function generateHOTP(secret, counter, options = {}) {
// 1. Base32 解码
const key = base32Decode(secret);
// 2. 计数器转字节数组
const counterBytes = new ArrayBuffer(8);
const view = new DataView(counterBytes);
view.setUint32(4, counter, false); // 大端序
// 3. HMAC-SHA1
const hmac = await crypto.subtle.sign('HMAC', key, counterBytes);
// 4. 动态截断
const offset = hmac[hmac.length - 1] & 0x0f;
const binary =
((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | (hmac[offset + 3] & 0xff);
// 5. 生成 OTP
const otp = binary % Math.pow(10, digits);
return otp.toString().padStart(digits, '0');
}职责: 管理用户身份认证和授权
架构设计:
┌───────────┐ 登录请求 ┌───────────┐
│ 浏览器 │ ──────────────→ │ Worker │
└───────────┘ └─────┬─────┘
↑ │
│ ▼
│ 验证密码(KV存储)
│ │
│ ▼
│ Set-Cookie: 生成 JWT
│ auth_token=... │
│ HttpOnly; Secure │
│ ←───────────────────────────┘
│
│ 后续请求(自动携带 Cookie)
│ ──────────────────────────────→
│
│ 验证 JWT + 自动刷新
│ ←──────────────────────────────
核心功能:
// 生成认证 Cookie
function createAuthCookie(token, expiresAt) {
const maxAge = Math.floor((expiresAt - Date.now()) / 1000);
return [
`auth_token=${token}`,
'HttpOnly', // 防止 XSS
'Secure', // 仅 HTTPS
'SameSite=Strict', // 防止 CSRF
`Max-Age=${maxAge}`,
'Path=/',
].join('; ');
}export async function handleRefreshToken(request, env) {
// 1. 验证当前 Token
const currentToken = extractTokenFromCookie(request);
if (!isValidToken(currentToken)) {
return createUnauthorizedResponse();
}
// 2. 生成新 Token
const newToken = await generateJWT({
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, // 7天
});
// 3. 设置新 Cookie
return new Response(JSON.stringify({ success: true }), {
headers: {
'Set-Cookie': createAuthCookie(newToken, Date.now() + 7 * 24 * 60 * 60 * 1000),
'Content-Type': 'application/json',
},
});
}职责: 使用 AES-GCM 256 位加密保护敏感数据
加密流程:
明文数据 → JSON.stringify → UTF-8 编码
↓
生成随机 IV (96 bits)
↓
AES-GCM 256 加密
↓
认证标签 (128 bits)
↓
{encrypted: base64(密文), iv: base64(IV)}
↓
JSON.stringify → Base64 编码
↓
存储到 KV
核心实现:
export async function encryptData(data, env) {
// 1. 检查是否配置了加密密钥
if (!env.ENCRYPTION_KEY) {
// 未配置密钥,返回明文
return typeof data === 'string' ? data : JSON.stringify(data);
}
// 2. 导入加密密钥
const keyBuffer = base64ToArrayBuffer(env.ENCRYPTION_KEY);
const key = await crypto.subtle.importKey('raw', keyBuffer, { name: 'AES-GCM' }, false, ['encrypt']);
// 3. 生成随机 IV
const iv = crypto.getRandomValues(new Uint8Array(12));
// 4. 加密数据
const plaintext = new TextEncoder().encode(typeof data === 'string' ? data : JSON.stringify(data));
const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, plaintext);
// 5. 打包加密结果
const encrypted = {
encrypted: arrayBufferToBase64(ciphertext),
iv: arrayBufferToBase64(iv.buffer),
};
// 6. 添加加密标记并返回
return `__ENCRYPTED__${btoa(JSON.stringify(encrypted))}`;
}自动检测和解密:
export async function decryptSecrets(data, env) {
if (!data) return [];
// 检查是否已加密
if (isEncrypted(data)) {
// 数据已加密,需要解密
if (!env.ENCRYPTION_KEY) {
console.error('数据已加密但未配置 ENCRYPTION_KEY');
return [];
}
return await decryptData(data, env);
} else {
// 数据未加密(明文或旧数据)
try {
return JSON.parse(data);
} catch (error) {
console.error('解析数据失败:', error);
return [];
}
}
}职责: 实现智能备份策略,防止数据丢失
备份策略:
用户操作 → 数据变更 → 触发备份
↓
防抖检查 (5分钟内只备份一次)
↓
执行备份 → 加密 → 存储到 KV
↓
自动清理 (保留最新100个)
Cron 触发 (每天)
↓
检查数据是否变化
↓
如果变化 → 执行备份
核心实现:
class BackupManager {
constructor(env) {
this.env = env;
this.lastBackupTime = 0;
this.pendingBackup = null;
this.backupInProgress = false;
}
/**
* 触发备份(带防抖)
*/
async triggerBackup(secrets, options = {}) {
const { immediate = false, reason = 'event-driven' } = options;
// 如果正在备份,跳过
if (this.backupInProgress) {
return null;
}
// 防抖检查(除非是立即备份)
if (!immediate && !this.shouldBackup()) {
// 取消之前的待处理备份
if (this.pendingBackup) {
clearTimeout(this.pendingBackup);
}
// 设置延迟备份
const remainingTime = DEBOUNCE_INTERVAL - (Date.now() - this.lastBackupTime);
this.pendingBackup = setTimeout(() => {
this.executeBackup(secrets, reason);
}, remainingTime);
return { scheduled: true, delay: remainingTime };
}
// 立即执行备份
return await this.executeBackup(secrets, reason);
}
/**
* 执行备份
*/
async executeBackup(secrets, reason) {
this.backupInProgress = true;
const startTime = Date.now();
try {
// 1. 生成备份ID
const backupId = `backup_${Date.now()}`;
// 2. 加密备份数据
const backupData = {
id: backupId,
timestamp: new Date().toISOString(),
count: secrets.length,
reason,
data: secrets,
};
const encrypted = await encryptData(backupData, this.env);
// 3. 保存到 KV
await this.env.SECRETS_KV.put(backupId, encrypted);
// 4. 更新备份列表
await this._updateBackupsList(backupId);
// 5. 清理旧备份
await this._cleanupOldBackupsAsync();
// 6. 记录性能指标
const duration = Date.now() - startTime;
this.logger.info('✅ 备份成功', {
backupId,
reason,
count: secrets.length,
duration: `${duration}ms`,
});
this.lastBackupTime = Date.now();
return { success: true, backupId, duration };
} catch (error) {
this.logger.error('❌ 备份失败', { reason }, error);
throw error;
} finally {
this.backupInProgress = false;
}
}
/**
* 清理旧备份
*/
async _cleanupOldBackupsAsync() {
const list = await this.env.SECRETS_KV.list({ prefix: 'backup_' });
const backups = list.keys;
if (backups.length > MAX_BACKUPS) {
// 按时间排序,删除最旧的备份
const toDelete = backups.sort((a, b) => a.name.localeCompare(b.name)).slice(0, backups.length - MAX_BACKUPS);
for (const backup of toDelete) {
await this.env.SECRETS_KV.delete(backup.name);
}
this.logger.info(`🗑️ 已清理 ${toDelete.length} 个旧备份`);
}
}
}职责: 提供结构化日志和错误追踪
日志系统架构:
┌───────────────┐
│ Logger API │
│ (logger.js) │
└───────┬───────┘
│
├─→ Console (开发环境)
├─→ Sentry (生产环境)
└─→ Cloudflare Analytics
核心功能:
class Logger {
constructor(env, context = {}) {
this.env = env;
this.context = context;
this.level = env.LOG_LEVEL || 'INFO';
}
info(message, meta = {}) {
this._log('INFO', message, meta);
}
error(message, meta = {}, error = null) {
this._log('ERROR', message, { ...meta, error: error?.stack });
// 同时发送到错误监控
if (error) {
monitoring.captureError(error, meta, ErrorSeverity.ERROR);
}
}
_log(level, message, meta) {
if (!this._shouldLog(level)) return;
const logEntry = {
level,
message,
timestamp: new Date().toISOString(),
context: this.context,
meta,
};
console.log(JSON.stringify(logEntry));
}
}class PerformanceTimer {
constructor(name, logger) {
this.name = name;
this.logger = logger;
this.startTime = Date.now();
this.checkpoints = [];
}
checkpoint(label) {
const elapsed = Date.now() - this.startTime;
this.checkpoints.push({ label, elapsed });
}
end(meta = {}) {
const duration = Date.now() - this.startTime;
this.logger.info(`⏱️ ${this.name} completed`, {
duration: `${duration}ms`,
checkpoints: this.checkpoints,
...meta,
});
// 记录到性能监控
monitoring.recordMetric(this.name, duration, 'ms', meta);
}
}class ErrorMonitor {
constructor(env) {
this.env = env;
this.sentryDSN = env.SENTRY_DSN;
this.enabled = !!this.sentryDSN;
}
captureError(error, context = {}, severity = ErrorSeverity.ERROR) {
// 生成错误ID
const errorId = `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
// 本地日志
console.error('[ErrorMonitor]', {
errorId,
message: error.message,
stack: error.stack,
context,
severity,
});
// 发送到 Sentry(如果配置)
if (this.enabled) {
this._sendToSentry({
errorId,
error,
context,
severity,
timestamp: new Date().toISOString(),
});
}
return { errorId, captured: this.enabled };
}
async _sendToSentry(data) {
// Sentry API 集成
try {
await fetch(this.sentryDSN, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event_id: data.errorId,
message: data.error.message,
level: data.severity.toLowerCase(),
exception: {
values: [
{
type: data.error.name,
value: data.error.message,
stacktrace: { frames: this._parseStackTrace(data.error.stack) },
},
],
},
extra: data.context,
timestamp: data.timestamp,
}),
});
} catch (err) {
console.error('[Sentry] Failed to send error:', err);
}
}
}职责: 防止 API 滥用和 DDoS 攻击
算法: 固定窗口计数器 (Fixed Window Counter)
时间轴: ───────────────────────────→
│ 窗口 1 │ 窗口 2 │
t=0s t=60s t=120s
└─────────────┘─────────────┘
计数器重置 计数器重置
窗口内计数: count ≤ maxAttempts
窗口过期: resetAt 时间到达时,创建新窗口
核心实现:
export async function checkRateLimit(key, env, options) {
const { maxAttempts = 5, windowSeconds = 60 } = options;
const rateLimitKey = `ratelimit:${key}`;
// 1. 获取当前限流数据
const data = await env.SECRETS_KV.get(rateLimitKey, 'json');
const now = Date.now();
// 2. 如果没有数据或窗口已过期,创建新窗口
if (!data || now > data.resetAt) {
await env.SECRETS_KV.put(
rateLimitKey,
JSON.stringify({
count: 1,
resetAt: now + windowSeconds * 1000,
firstRequest: now,
}),
{
expirationTtl: windowSeconds + 10,
},
);
return {
allowed: true,
remaining: maxAttempts - 1,
resetAt: now + windowSeconds * 1000,
limit: maxAttempts,
};
}
// 3. 检查是否超过限制
if (data.count >= maxAttempts) {
return {
allowed: false,
remaining: 0,
resetAt: data.resetAt,
limit: maxAttempts,
};
}
// 4. 增加计数
data.count++;
await env.SECRETS_KV.put(rateLimitKey, JSON.stringify(data), {
expirationTtl: Math.ceil((data.resetAt - now) / 1000) + 10,
});
return {
allowed: true,
remaining: maxAttempts - data.count,
resetAt: data.resetAt,
limit: maxAttempts,
};
}预设策略:
export const RATE_LIMIT_PRESETS = {
// 登录:每分钟 5 次
login: {
maxAttempts: 5,
windowSeconds: 60,
},
// API 操作:每分钟 30 次
api: {
maxAttempts: 30,
windowSeconds: 60,
},
// 敏感操作:每分钟 10 次
sensitive: {
maxAttempts: 10,
windowSeconds: 60,
},
// 批量操作:每 5 分钟 5 次
bulk: {
maxAttempts: 5,
windowSeconds: 300,
},
// 全局保护:每分钟 100 次
global: {
maxAttempts: 100,
windowSeconds: 60,
},
};算法选择理由:
- ✅ 实现简单,性能高效(单次 KV 操作)
- ✅ 内存占用低(只存储计数和窗口结束时间)
- ✅ 完美适配 Cloudflare Workers 无状态架构
⚠️ 存在窗口边界效应(可能 2倍突发流量)
graph TD
A[用户请求] --> B{CORS 预检?}
B -->|是| C[返回 CORS 响应]
B -->|否| D[初始化日志和监控]
D --> E[开始性能追踪]
E --> F{需要认证?}
F -->|是| G{Token 有效?}
G -->|否| H[返回 401 未授权]
G -->|是| I[路由解析]
F -->|否| I
I --> J{路由类型}
J -->|静态页面| K[生成 HTML]
J -->|API 请求| L{检查限流}
J -->|PWA 资源| M[返回 Manifest/SW/Icon]
L -->|超过限流| N[返回 429]
L -->|通过| O[处理 API 请求]
O --> P{操作类型}
P -->|读取| Q[从 KV 读取]
P -->|写入| R[验证数据]
Q --> S[解密数据]
R --> T[加密数据]
S --> U[返回响应]
T --> V[保存到 KV]
V --> W[触发备份]
W --> U
K --> U
M --> U
U --> X[记录性能指标]
X --> Y[结束]
graph LR
A[用户提交密钥] --> B[客户端验证]
B --> C[发送到 API]
C --> D{ENCRYPTION_KEY<br/>已配置?}
D -->|是| E[生成随机 IV]
E --> F[AES-GCM 加密]
F --> G[生成认证标签]
G --> H[打包: encrypted+iv]
H --> I[Base64 编码]
I --> J[添加加密标记]
J --> K[存储到 KV]
D -->|否| L[JSON.stringify]
L --> K
K2[从 KV 读取] --> M{检测加密标记}
M -->|已加密| N[Base64 解码]
N --> O[提取 encrypted+iv]
O --> P[AES-GCM 解密]
P --> Q[验证认证标签]
Q --> R[返回明文数据]
M -->|未加密| S[JSON.parse]
S --> R
graph TD
A[数据变更操作] --> B[保存到 KV]
B --> C[触发备份]
C --> D{正在备份?}
D -->|是| E[跳过]
D -->|否| F{防抖检查}
F -->|< 5分钟| G[取消旧备份]
G --> H[设置延迟备份]
F -->|≥ 5分钟| I[立即执行备份]
I --> J[加密备份数据]
J --> K[生成备份ID]
K --> L[保存到 KV]
L --> M[更新备份列表]
M --> N{备份数量 > 100?}
N -->|是| O[删除最旧备份]
N -->|否| P[完成]
O --> P
Q[定时任务<br/>每天] --> R{数据有变化?}
R -->|是| C
R -->|否| S[跳过]
scripts/
├── state.js # 全局状态变量
│ └── 定义所有全局变量(secrets, scannerStream, etc.)
│
├── auth.js # 认证逻辑
│ ├── showLoginModal()
│ ├── hideLoginModal()
│ ├── checkAuth()
│ ├── refreshAuthToken()
│ └── authenticatedFetch()
│
├── core.js # 核心业务逻辑 (最大模块,106KB)
│ ├── loadSecrets()
│ ├── renderSecrets()
│ ├── handleAddSecret()
│ ├── handleEditSecret()
│ ├── handleDeleteSecret()
│ ├── startOTPCountdown()
│ ├── showQRCodeModal()
│ ├── startQRScanner()
│ ├── handleBatchImport()
│ └── ... (50+ 个函数)
│
├── utils.js # 工具函数
│ ├── showCenterToast()
│ ├── copyToClipboard()
│ ├── validateBase32()
│ ├── generateQRCodeDataURL()
│ ├── waitForQRCodeLibrary()
│ ├── formatOTPAuthURL()
│ └── ... (20+ 个工具函数)
│
└── pwa.js # PWA 功能
├── Service Worker 注册
├── PWA 模式检测
└── 更新检查
模块加载流程:
page.js → scripts/index.js
├─ import state.js (全局变量初始化)
├─ import auth.js (认证函数定义)
├─ import core.js (核心业务逻辑)
├─ import utils.js (工具函数)
└─ import pwa.js (PWA 功能)
↓
inline <script>
↓
页面加载完成执行
styles/
├── base.js # 基础样式
│ ├── * { box-sizing, margin, padding }
│ ├── body { font, background }
│ ├── .container
│ ├── .header
│ └── .theme-toggle
│
├── components.js # 组件样式
│ ├── .secret-card
│ ├── .otp-preview
│ ├── .progress-bar
│ ├── .action-menu
│ └── .search-bar
│
├── modals.js # 模态框样式
│ ├── .modal
│ ├── .modal-content
│ ├── .modal-header
│ ├── .form-group
│ └── .btn-*
│
└── responsive.js # 响应式样式
├── @media (max-width: 480px)
├── @media (min-width: 481px)
└── @media (min-width: 1200px)
样式加载流程:
page.js → styles/index.js
├─ import base.js
├─ import components.js
├─ import modals.js
└─ import responsive.js
↓
合并为单个 <style> 标签
↓
inline 到 HTML
Service Worker (sw.js)
├── Static Cache (v2)
│ ├── / (主页面)
│ ├── /manifest.json
│ ├── /icon-192.png
│ └── /icon-512.png
│
├── CDN Cache (白名单)
│ ├── jsQR.min.js
│ └── qrcode.min.js
│
└── Runtime Cache
└── API 响应(不缓存)
缓存策略:
- 静态资源: Cache First (缓存优先)
- CDN 资源: Cache First + CORS 处理
- API 请求: Network Only (始终从网络获取)
- 其他外部资源: Network Only + 静默失败
更新机制:
// 1. Service Worker 安装
self.addEventListener('install', (event) => {
// 预缓存静态资源
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(STATIC_RESOURCES);
}),
);
self.skipWaiting(); // 立即激活
});
// 2. Service Worker 激活
self.addEventListener('activate', (event) => {
// 清理旧缓存
event.waitUntil(
caches.keys().then((names) => {
return Promise.all(names.filter((name) => name !== CACHE_NAME && name !== RUNTIME_CACHE).map((name) => caches.delete(name)));
}),
);
self.clients.claim(); // 立即控制页面
});
// 3. 拦截请求
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
if (url.origin === location.origin) {
// 本地资源:缓存优先
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
}),
);
} else if (isCDNLibrary(url)) {
// CDN 资源:缓存优先 + CORS
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request, { mode: 'cors' });
}),
);
} else {
// 其他资源:直接透传
event.respondWith(fetch(event.request));
}
});原则: 每个模块负责单一职责
✅ 好的模块设计:
- auth.js: 只处理认证相关逻辑
- encryption.js: 只处理加密解密
- backup.js: 只处理备份逻辑
❌ 不好的设计:
- utils.js: 混杂了认证、加密、备份等所有功能
应用: 环境变量 (env) 通过参数传递
// ✅ 好的设计
export async function handleGetSecrets(env) {
const logger = getLogger(env); // 注入依赖
const data = await env.SECRETS_KV.get('secrets');
}
// ❌ 不好的设计
let globalEnv;
export function initEnv(env) {
globalEnv = env;
}
export async function handleGetSecrets() {
const data = await globalEnv.SECRETS_KV.get('secrets');
}应用: 创建响应对象
// 工厂函数
export function createJsonResponse(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: {
'Content-Type': 'application/json',
...getCORSHeaders(),
},
});
}
// 使用
return createJsonResponse({ success: true, data: secrets });应用: OTP 算法选择
// 策略接口
const OTP_STRATEGIES = {
totp: generateTOTP,
hotp: generateHOTP,
};
// 使用策略
export async function generateOTP(secret, type = 'totp', options = {}) {
const strategy = OTP_STRATEGIES[type];
if (!strategy) {
throw new Error(`Unsupported OTP type: ${type}`);
}
return await strategy(secret, options);
}应用: 性能监控包装
// 装饰器
function withPerformanceLogging(fn, name) {
return async function (...args) {
const timer = new PerformanceTimer(name, logger);
try {
const result = await fn(...args);
timer.end({ success: true });
return result;
} catch (error) {
timer.cancel();
throw error;
}
};
}
// 使用
const handleGetSecrets = withPerformanceLogging(async (env) => {
// 原始逻辑
}, 'GetSecrets');应用: 请求处理流水线
请求 → CORS中间件 → 认证中间件 → 限流中间件 → 路由处理 → 响应
// 中间件链
async function handleRequest(request, env) {
// 中间件 1: CORS
const corsResponse = handleCORS(request);
if (corsResponse) return corsResponse;
// 中间件 2: 认证
if (requiresAuth(pathname)) {
const isAuthorized = await verifyAuth(request, env);
if (!isAuthorized) return createUnauthorizedResponse();
}
// 中间件 3: 限流
const rateLimitResult = await checkRateLimit(request, env);
if (!rateLimitResult.allowed) {
return createRateLimitResponse(rateLimitResult);
}
// 最终处理
return await routeRequest(pathname, method, request, env);
}应用: 数据变更 → 备份触发
// 主题 (Subject)
async function saveSecretsToKV(env, secrets, reason) {
// 保存数据
await env.SECRETS_KV.put('secrets', encrypted);
// 通知观察者
await triggerBackup(secrets, env, { reason }); // 观察者
}
// 观察者 (Observer)
export async function triggerBackup(secrets, env, options) {
// 响应数据变更事件
await backupManager.executeBackup(secrets, options.reason);
}应用: 备份管理器、监控系统
// 单例模式
let backupManagerInstance = null;
export function getBackupManager(env) {
if (!backupManagerInstance) {
backupManagerInstance = new BackupManager(env);
}
return backupManagerInstance;
}- 模块化: 拆分为小模块,便于维护和缓存
- 懒加载: Service Worker 按需缓存资源
- 最小化: 减少不必要的计算和内存使用
- 静态资源: 长期缓存(PWA)
- CDN 资源: 缓存优先策略
- API 响应: 不缓存(实时数据)
- 批量操作: 一次性读取和写入
- 数据压缩: 使用加密同时压缩数据
- 索引优化: 使用有意义的 KV key
- 全球 CDN: Cloudflare Edge Network
- HTTP/2: 多路复用
- 压缩: Gzip/Brotli 自动压缩
┌────────────────────────────────────────┐
│ 1. Cloudflare Edge 层 │
│ - DDoS 防护 │
│ - WAF (Web Application Firewall) │
│ - Bot 管理 │
└────────────┬───────────────────────────┘
│
┌────────────▼───────────────────────────┐
│ 2. 应用层安全 │
│ - HttpOnly Cookie 认证 │
│ - CORS 白名单 │
│ - CSP Header │
│ - Rate Limiting │
└────────────┬───────────────────────────┘
│
┌────────────▼───────────────────────────┐
│ 3. 数据层安全 │
│ - AES-GCM 256 位加密 │
│ - Cloudflare Secrets 存储密钥 │
│ - 加密备份 │
└────────────┬───────────────────────────┘
│
┌────────────▼───────────────────────────┐
│ 4. 监控和审计 │
│ - 结构化日志 │
│ - 错误追踪 │
│ - 性能监控 │
└────────────────────────────────────────┘
- ✅ 无状态设计:每个请求独立处理
- ✅ 全球分布:自动在边缘节点运行
- ✅ 自动扩缩容:根据流量自动调整
- ✅ 插件式架构:新功能作为独立模块添加
- ✅ 策略模式:易于添加新的 OTP 算法
- ✅ 中间件模式:易于添加新的请求处理逻辑
2FA 采用现代化的无服务器架构,具有以下特点:
| 特性 | 说明 |
|---|---|
| 高性能 | 全球 CDN + 边缘计算,平均响应 < 50ms |
| 高可用 | 99.99% SLA,自动故障转移 |
| 高安全 | 多层安全防护 + AES-256 加密 |
| 易维护 | 模块化设计 + 完整监控 |
| 易扩展 | 无状态 + 插件式架构 |
| 低成本 | 按需计费 + 免费额度 |
相关文档: