♻️ refactor: 提取初始化脚本为独立模块并清理过时架构文档

- 将 giffgaff 和 Simyo 页面的国际化初始化内联脚本提取为独立的 `init-i18n.js` 模块
- 将通知系统初始化内联脚本提取为独立的 `init-notification.js` 模块
- 移除 docs 目录下的 ARCHITECTURE.md 和 FUTURE_ARCHITECTURE.md 过期架构文档
- 更新 giffgaff_modular.html 和 simyo_modular.html 以引用新的独立脚本模块
This commit is contained in:
Abner
2026-06-13 21:58:09 +08:00
parent 3eb8550631
commit 8bd2bdb8a4
8 changed files with 24 additions and 409 deletions

View File

@@ -1,140 +0,0 @@
# eSIM-Tools 架构说明
## 当前架构
### 概述
eSIM-Tools 是一个 Web 应用,用于管理 Giffgaff 和 Simyo 运营商的 eSIM 激活。采用无服务器架构Netlify Functions 作为后端 API静态托管作为前端。
### 技术栈
- **前端**: 原生 JavaScript, Bootstrap, PWA
- **后端**: Node.js/Express (本地开发), Netlify Functions (生产环境)
- **构建工具**: Webpack, Babel, PostCSS
- **优化工具**: Sharp (图片), Terser (JS), Workbox (Service Worker)
### 部署架构
```
用户请求 → Netlify CDN (静态资源) → Netlify Functions (API) → 运营商 API
Edge Functions (BFF 代理层,注入 ACCESS_KEY)
```
### 模块依赖关系
```
┌─────────────────────────────────────────────────────────┐
│ 前端层 (Browser) │
├─────────────────────────────────────────────────────────┤
│ index.html → src/giffgaff/ → src/js/modules/ │
│ src/simyo/ → (通用工具模块) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Edge Functions (BFF) │
│ netlify/edge-functions/bff-proxy.js │
│ (ACCESS_KEY 注入 + 请求转发) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Netlify Functions (Serverless) │
├─────────────────────────────────────────────────────────┤
│ giffgaff-token-exchange.js giffgaff-graphql.js │
│ giffgaff-mfa-challenge.js giffgaff-sms-activate.js │
│ giffgaff-mfa-validation.js auto-activate-esim.js │
│ verify-cookie.js health.js │
│ public-config.js notifications.js │
│ notifications-internal.js │
│ _shared/middleware.js _shared/internal-headers.js │
└─────────────────────────────────────────────────────────┘
```
### 关键设计决策
1. **无框架设计**: 使用原生 JavaScript 避免框架依赖,保持最小打包体积
2. **BFF 模式**: Edge Functions 作为 Backend-For-Frontend 代理层,注入 ACCESS_KEY 并转发请求
3. **中间件统一**: 通过 `withAuth` 中间件统一处理鉴权、CORS、验证
4. **原生 ES6 模块**: 业务页面不经 Webpack 打包,按需加载
---
## 构建优化
### 1. 图片处理速度提升 2.4 倍 (`scripts/optimize-images.js`)
**问题**: 旧脚本串行处理图片10 张图片需要 60 秒。
**方案**: 改为并行处理(可配置并发数),加入智能缓存跳过已优化文件,支持 mozjpeg 和 adaptive filtering 压缩。
**效果**: 处理时间从 60 秒降至 25 秒,压缩率提升 30-40%。
### 2. 压缩率提升 15-20% (`scripts/compress.js`)
**问题**: 旧压缩脚本仅使用 Gzip且每次都全量压缩。
**方案**: 同时生成 Brotli + Gzip 双版本,加入基于缓存的跳过逻辑避免重复压缩。
**效果**: 压缩率提升 15-20%,后续构建只需压缩变更文件。
### 3. 滚动性能提升,内存泄漏消除 (`src/js/performance.js`)
**问题**: Intersection Observer 未清理导致内存泄漏,图片在进入视口后才开始加载。
**方案**: 统一 Observer 生命周期管理,视口前预加载图片,加入错误处理。
**效果**: 内存使用减少 10-15%,滚动更流畅。
### 4. 打包体积减少 25%,长期缓存更优 (`webpack.config.js`)
**问题**: 打包体积 ~450KB (gzip),第三方库未分离,缓存命中率低。
**方案**: 拆分第三方 chunk提取 runtime使用确定性模块 ID加入 Brotli 插件和 500KB 性能预算。
**效果**: 打包体积降至 ~340KB (gzip) / ~280KB (brotli)。
### 5. 通用工具函数库 (`src/js/modules/utils.js`)
提供 debounce (leading/trailing)、throttle (RAF)、记忆化、指数退避重试、bytes/JSON 格式化等常用工具。
### 6. 请求验证与安全防护 (`src/js/middleware/validation.js`)
提供请求体大小验证、Header 校验、XSS 清理、内存级速率限制、请求计时日志和异步错误边界。
---
## 性能基准
以下数据基于 Lighthouse 测量Chrome DevTools模拟 4G 网络):
### 优化前
- 打包体积: ~450KB (gzip)
- 图片优化: 10 张图片 60 秒
- First Contentful Paint: 1.8s
- Time to Interactive: 3.2s
### 优化后
- 打包体积: ~340KB (gzip), ~280KB (brotli)
- 图片优化: 10 张图片 25 秒 (2.4x 提升)
- First Contentful Paint: 1.2s (提升 33%)
- Time to Interactive: 2.1s (提升 34%)
---
## 实施路线
### 阶段 1: 代码质量 (已完成)
- 优化构建工具
- 添加工具库
- 改进错误处理
- 添加验证中间件
### 阶段 2: 基础设施 (进行中)
- ✅ Sentry 监控 (前后端)
- ✅ 内存级速率限制 (`_shared/rate-limiter.js`)
- 添加 Redis 缓存
- 添加端到端测试
> 更多未来架构规划请参考 [FUTURE_ARCHITECTURE.md](./FUTURE_ARCHITECTURE.md)

View File

@@ -1,249 +0,0 @@
# eSIM-Tools 未来架构规划
> 本文档记录当前尚未实施的架构规划,仅供未来参考。
## 微服务架构迁移
### 当前状态
- 单体 Netlify Functions
- 业务逻辑与 API 处理器紧耦合
- 复用性有限
### 目标架构
```
┌─────────────────────────────────────────────────┐
│ API Gateway / BFF Layer │
│ (Netlify Edge Functions / Cloudflare Workers) │
└─────────────────────────────────────────────────┘
┌──────────────┼──────────────┐
│ │ │
┌───────▼──────┐ ┌─────▼──────┐ ┌────▼────────┐
│ Auth Service │ │ Provider │ │ Activation │
│ (OAuth) │ │ Adapters │ │ Service │
└───────────────┘ └────────────┘ └─────────────┘
│ │ │
└──────────────┼──────────────┘
┌──────────────▼──────────────┐
│ Shared Middleware │
│ (Rate Limit, Auth, Log) │
└─────────────────────────────┘
```
**优势:**
- 独立部署和扩展
- 更好的关注点分离
- 更容易测试和维护
- 新运营商的插件架构
**实施计划:**
1. 将共享逻辑提取到 `src/services/core/`
2.`src/services/adapters/` 中创建运营商适配器
3.`src/services/middleware/` 中实现中间件链
4. 添加服务注册表用于动态运营商加载
**代码示例:**
```javascript
// src/services/adapters/BaseProvider.js
/**
* 运营商适配器基类
* @abstract
*/
class BaseProvider {
constructor(config) {
this.config = config;
}
/**
* 认证方法(子类必须实现)
* @param {Object} credentials - 认证凭据
* @returns {Promise<Object>} 认证结果
*/
async authenticate(credentials) {
throw new Error('Must implement authenticate()');
}
/**
* eSIM 激活方法(子类必须实现)
* @param {Object} data - 激活数据
* @returns {Promise<Object>} 激活结果
*/
async activateESIM(data) {
throw new Error('Must implement activateESIM()');
}
}
```
## 可扩展性策略
### 当前限制
- 无状态函数(无会话持久化)
- 有限的并发请求处理
- 无长时间运行任务的作业队列
- 直接 API 调用,无熔断器
### 目标方案
#### A. 添加数据库层
```
┌──────────────────────────────────────┐
│ Application Layer │
└──────────────────────────────────────┘
┌─────────────┼─────────────┐
│ │
┌───▼──────┐ ┌──────────▼────┐
│ Redis │ │ PostgreSQL │
│ (Cache) │ │ (Sessions) │
└──────────┘ └───────────────┘
```
**使用场景:**
- 多步骤 OAuth 流程的会话管理
- 跨 Serverless 实例的速率限制
- API 请求去重
- 用户偏好和设置
**技术建议:**
- **Redis**: Upstash Redis (Serverless 友好)
- **PostgreSQL**: Neon 或 Supabase (Serverless Postgres)
#### B. CDN 优先架构
```
用户请求
┌─────────────────┐
│ CDN Edge │ ← 静态资源 (HTML, CSS, JS, Images)
│ (Cloudflare) │ ← Service Worker precache
└────────┬────────┘
│ (Cache Miss)
┌─────────────────┐
│ Origin Server │ ← 仅动态 API 请求
│ (Netlify) │
└─────────────────┘
```
**优化:**
- 对哈希资源设置长缓存时间 (1 年)
- HTML 使用 stale-while-revalidate
- 实现边缘渲染个性化内容
- 预获取关键 API 响应
#### C. 后台处理作业队列
```
API Request → Enqueue Job → Return Job ID
┌──────────────┐
│ Job Queue │
│ (BullMQ) │
└──────┬───────┘
┌───────┴────────┐
│ │
┌────▼────┐ ┌─────▼────┐
│ Worker 1│ │ Worker 2 │
└─────────┘ └──────────┘
```
**使用场景:**
- 图片优化
- 批量 eSIM 激活
- 报告生成
- 邮件通知
**技术:** BullMQ + Redis 后端
## 未来功能规划
### A. 多运营商插件系统
**接口定义:**
```javascript
/**
* 运营商插件接口
* @interface ProviderPlugin
*/
// 所有方法由具体运营商适配器实现:
// - authenticate(credentials) → Promise<Token>
// - activateESIM(data) → Promise<Result>
// - getStatus(id) → Promise<Status>
// - validate(data) → ValidationResult
```
**插件注册表:**
```javascript
class PluginRegistry {
constructor() {
this.plugins = new Map();
}
async loadPlugin(name) {
const plugin = await import(`./plugins/${name}`);
this.plugins.set(name, plugin);
}
getProvider(name) {
return this.plugins.get(name);
}
}
```
**优势:**
- 轻松添加新运营商
- 社区贡献
- A/B 测试不同实现
- 运营商 API 渐进迁移
### B. 实时状态追踪
**WebSocket 架构:**
```
Client → WebSocket → Server → Provider API
│ │
└──────── Status Updates ──────┘
```
**Server-Sent Events 实现 (更简单):**
```javascript
// 客户端
const eventSource = new EventSource('/api/esim-status?id=123');
eventSource.onmessage = (event) => {
const status = JSON.parse(event.data);
updateUI(status);
};
```
### C. 离线优先 PWA 与后台同步
**增强的 Service Worker 策略:**
```javascript
// 后台同步失败的请求
self.addEventListener('sync', (event) => {
if (event.tag === 'esim-activation') {
event.waitUntil(retryActivation());
}
});
```
**功能:**
- 离线时队列 eSIM 激活请求
- 连接恢复后自动重试
- 使用 IndexedDB 进行本地状态管理
- 并发编辑的冲突解决
### D. 分析仪表板
**追踪指标:**
- 按运营商统计激活成功率
- 平均激活时间
- 错误率和类型
- 用户旅程分析
- 性能指标 (Core Web Vitals)

View File

@@ -875,18 +875,10 @@
<script src="/src/js/performance.js"></script>
<!-- 国际化初始化 -->
<script type="module">
import { initI18n } from '/src/js/modules/i18n.js';
initI18n('giffgaff');
</script>
<script type="module" src="/src/giffgaff/js/init-i18n.js"></script>
<!-- 通知系统初始化 -->
<script type="module">
import NotificationService from '/src/js/modules/notification-service.js';
document.addEventListener('DOMContentLoaded', () => {
NotificationService.init();
});
</script>
<script type="module" src="/src/giffgaff/js/init-notification.js"></script>
<!-- 主应用脚本(模块化) -->
<script type="module" src="/src/giffgaff/js/giffgaff-app.js"></script>

View File

@@ -0,0 +1,4 @@
// Giffgaff 页面国际化初始化
import { initI18n } from '/src/js/modules/i18n.js';
initI18n('giffgaff');

View File

@@ -0,0 +1,6 @@
// Giffgaff 页面通知系统初始化
import NotificationService from '/src/js/modules/notification-service.js';
document.addEventListener('DOMContentLoaded', () => {
NotificationService.init();
});

View File

@@ -0,0 +1,4 @@
// Simyo 页面国际化初始化
import { initI18n } from '/src/js/modules/i18n.js';
initI18n('simyo');

View File

@@ -0,0 +1,6 @@
// Simyo 页面通知系统初始化
import NotificationService from '/src/js/modules/notification-service.js';
document.addEventListener('DOMContentLoaded', () => {
NotificationService.init();
});

View File

@@ -555,18 +555,10 @@
<script src="/src/simyo/js/reveal-animation.js"></script>
<!-- 国际化初始化 -->
<script type="module">
import { initI18n } from '/src/js/modules/i18n.js';
initI18n('simyo');
</script>
<script type="module" src="/src/simyo/js/init-i18n.js"></script>
<!-- 通知系统初始化 -->
<script type="module">
import NotificationService from '/src/js/modules/notification-service.js';
document.addEventListener('DOMContentLoaded', () => {
NotificationService.init();
});
</script>
<script type="module" src="/src/simyo/js/init-notification.js"></script>
<!-- 主应用脚本(模块化) -->
<script type="module" src="/src/simyo/js/simyo-app.js"></script>