mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-03 06:24:20 +08:00
♻️ 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:
@@ -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)
|
|
||||||
@@ -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)
|
|
||||||
@@ -875,18 +875,10 @@
|
|||||||
<script src="/src/js/performance.js"></script>
|
<script src="/src/js/performance.js"></script>
|
||||||
|
|
||||||
<!-- 国际化初始化 -->
|
<!-- 国际化初始化 -->
|
||||||
<script type="module">
|
<script type="module" src="/src/giffgaff/js/init-i18n.js"></script>
|
||||||
import { initI18n } from '/src/js/modules/i18n.js';
|
|
||||||
initI18n('giffgaff');
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- 通知系统初始化 -->
|
<!-- 通知系统初始化 -->
|
||||||
<script type="module">
|
<script type="module" src="/src/giffgaff/js/init-notification.js"></script>
|
||||||
import NotificationService from '/src/js/modules/notification-service.js';
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
NotificationService.init();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- 主应用脚本(模块化) -->
|
<!-- 主应用脚本(模块化) -->
|
||||||
<script type="module" src="/src/giffgaff/js/giffgaff-app.js"></script>
|
<script type="module" src="/src/giffgaff/js/giffgaff-app.js"></script>
|
||||||
|
|||||||
4
src/giffgaff/js/init-i18n.js
Normal file
4
src/giffgaff/js/init-i18n.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Giffgaff 页面国际化初始化
|
||||||
|
import { initI18n } from '/src/js/modules/i18n.js';
|
||||||
|
|
||||||
|
initI18n('giffgaff');
|
||||||
6
src/giffgaff/js/init-notification.js
Normal file
6
src/giffgaff/js/init-notification.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Giffgaff 页面通知系统初始化
|
||||||
|
import NotificationService from '/src/js/modules/notification-service.js';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
NotificationService.init();
|
||||||
|
});
|
||||||
4
src/simyo/js/init-i18n.js
Normal file
4
src/simyo/js/init-i18n.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Simyo 页面国际化初始化
|
||||||
|
import { initI18n } from '/src/js/modules/i18n.js';
|
||||||
|
|
||||||
|
initI18n('simyo');
|
||||||
6
src/simyo/js/init-notification.js
Normal file
6
src/simyo/js/init-notification.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Simyo 页面通知系统初始化
|
||||||
|
import NotificationService from '/src/js/modules/notification-service.js';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
NotificationService.init();
|
||||||
|
});
|
||||||
@@ -555,18 +555,10 @@
|
|||||||
<script src="/src/simyo/js/reveal-animation.js"></script>
|
<script src="/src/simyo/js/reveal-animation.js"></script>
|
||||||
|
|
||||||
<!-- 国际化初始化 -->
|
<!-- 国际化初始化 -->
|
||||||
<script type="module">
|
<script type="module" src="/src/simyo/js/init-i18n.js"></script>
|
||||||
import { initI18n } from '/src/js/modules/i18n.js';
|
|
||||||
initI18n('simyo');
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- 通知系统初始化 -->
|
<!-- 通知系统初始化 -->
|
||||||
<script type="module">
|
<script type="module" src="/src/simyo/js/init-notification.js"></script>
|
||||||
import NotificationService from '/src/js/modules/notification-service.js';
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
NotificationService.init();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- 主应用脚本(模块化) -->
|
<!-- 主应用脚本(模块化) -->
|
||||||
<script type="module" src="/src/simyo/js/simyo-app.js"></script>
|
<script type="module" src="/src/simyo/js/simyo-app.js"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user