Initial TrueGrowth source import

This commit is contained in:
2026-07-07 09:36:36 +08:00
commit 3b6781d695
2283 changed files with 691996 additions and 0 deletions

396
docs/CDN_DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,396 @@
# 多 CDN 智能部署方案
## 概述
本文档描述 Opentu 的多 CDN 智能部署策略,实现:
1. **Service Worker 缓存优先** - 最快加载速度
2. **多 CDN 自动回退** - 高可用性
3. **本地服务器兜底** - 最终保障
4. **零服务器流量成本** - 利用免费 CDN
5. **安全混合部署** - HTML 在自有服务器,静态资源在 CDN
## 安全混合部署(推荐)
### 为什么需要混合部署?
- **HTML 文件**可能包含用户配置、API 密钥等敏感信息
- CDN 是公开的,任何人都可以访问
- 将 HTML 保留在自有服务器,只将静态资源放到 CDN
### 架构
```
用户访问 your-domain.com
自有服务器(完整副本)
├── HTML 文件(安全)
└── 全部静态资源(兜底)
HTML 加载资源(路径指向 CDN
┌──────┼──────┐
↓ ↓ ↓
unpkg jsdelivr 服务器
(优先) (备用) (兜底)
```
### 使用方法
```bash
# 一键部署:构建 + 发布 CDN + 部署服务器
pnpm run deploy --otp=123456
# 预览模式(不实际执行)
pnpm run deploy:dry
# 只发布到 CDN跳过服务器部署
pnpm run deploy:cdn-only --otp=123456
# 只部署到服务器(跳过 npm 发布)
pnpm run deploy:server-only
```
### 服务器配置
`.env` 文件中添加:
```bash
# 服务器信息
DEPLOY_HOST=your-server.com
DEPLOY_USER=username
DEPLOY_PORT=22
DEPLOY_WEB_DIR=/var/www/aitu
# SSH 认证(二选一)
DEPLOY_SSH_KEY=~/.ssh/id_rsa
# 或
DEPLOY_SSH_PASSWORD=your-password
```
### 文件分布
| 文件类型 | CDN | 服务器 | 说明 |
|---------|:---:|:------:|------|
| `*.html` | ❌ | ✅ | 安全:不在 CDN 公开 |
| `sw.js` | ❌ | ✅ | Service Worker 必须同源 |
| `init.json` | ❌ | ✅ | 初始化配置 |
| `assets/*.js` | ✅ | ✅ | CDN 优先,服务器兜底 |
| `assets/*.css` | ✅ | ✅ | CDN 优先,服务器兜底 |
| `icons/*` | ✅ | ✅ | CDN 优先,服务器兜底 |
| `manifest.json` | ✅ | ✅ | CDN 优先,服务器兜底 |
### 加载顺序
1. **Service Worker 缓存** - 最快,离线可用
2. **CDN unpkg** - 优先加载,节约服务器流量
3. **CDN jsdelivr** - unpkg 失败时备用
4. **自有服务器** - 所有 CDN 失败时兜底
## 架构图
```
用户请求
┌─────────────────────────────────────────────────┐
│ Service Worker │
│ ┌─────────────────────────────────────────┐ │
│ │ 1. 检查本地缓存 (Cache Storage) │ │
│ │ ↓ 缓存命中 → 直接返回 │ │
│ │ ↓ 缓存未命中 │ │
│ │ │ │
│ │ 2. 尝试 CDN 1: unpkg.com │ │
│ │ ↓ 成功 → 缓存并返回 │ │
│ │ ↓ 失败 │ │
│ │ │ │
│ │ 3. 尝试 CDN 2: jsdelivr.net │ │
│ │ ↓ 成功 → 缓存并返回 │ │
│ │ ↓ 失败 │ │
│ │ │ │
│ │ 4. 回退本地服务器 │ │
│ │ ↓ 成功 → 缓存并返回 │ │
│ │ ↓ 失败 → 显示离线页面 │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
```
## CDN 源对比
| CDN | 免费额度 | 特点 | 访问地址 |
|-----|----------|------|----------|
| unpkg | 无限 | npm 官方托管,全球节点 | `unpkg.com/aitu-app@{version}/` |
| jsdelivr | 无限 | 国内访问快,有缓存 | `cdn.jsdelivr.net/npm/aitu-app@{version}/` |
| 本地服务器 | 自有流量 | 完全可控,最终兜底 | 自有域名 |
## 部署流程
### 方式 1安全混合部署推荐
```bash
# 步骤 1升级版本
pnpm run version:patch
# 步骤 2构建混合部署包
pnpm run cdn:build
# 步骤 3发布静态资源到 npm
pnpm run cdn:publish --skip-build --otp=123456
# 步骤 4部署 HTML 到自有服务器
scp -r dist/deploy/server/* user@your-server:/path/to/web/
```
**CDN 上没有 HTML 文件**,用户信息安全。
### 方式 2完整发布到 npm包含 HTML
```bash
# 升级版本并发布
pnpm run version:patch
pnpm run npm:publish --otp=123456
```
发布后CDN 自动可用:
- unpkg: `https://unpkg.com/aitu-app@{version}/index.html`
- jsdelivr: `https://cdn.jsdelivr.net/npm/aitu-app@{version}/index.html`
> ⚠️ 注意:此方式 HTML 文件也在 CDN 上公开
### 2. 配置自有域名(可选)
#### 方案 ACloudflare Pages推荐
1. 登录 [Cloudflare Pages](https://dash.cloudflare.com/pages)
2. 创建项目,连接 GitHub 仓库
3. 配置构建:
```
Build command: pnpm run build:web
Output directory: dist/apps/web
Environment: NODE_VERSION=20
```
4. 添加自定义域名
#### 方案 B自有服务器 + CDN 代理
1. 部署到自有服务器(使用现有脚本):
```bash
pnpm run deploy:package
pnpm run deploy:upload
```
2. 配置 Cloudflare CDN 代理:
- 将域名 DNS 指向 Cloudflare
- 开启橙色云朵(代理模式)
- 静态资源自动缓存
### 3. Service Worker 配置
项目已内置多 CDN 回退逻辑,关键文件:
- `apps/web/src/sw/cdn-fallback.ts` - CDN 回退策略
- `apps/web/public/cdn-config.js` - CDN 选择器(可选)
## 核心代码说明
### CDN 回退策略 (`cdn-fallback.ts`)
```typescript
// CDN 源配置
const CDN_SOURCES = [
{
name: 'unpkg',
urlTemplate: 'https://unpkg.com/aitu-app@{version}/{path}',
priority: 1,
},
{
name: 'jsdelivr',
urlTemplate: 'https://cdn.jsdelivr.net/npm/aitu-app@{version}/{path}',
priority: 2,
},
];
// 智能回退逻辑
async function fetchFromCDNWithFallback(resourcePath, version, localOrigin) {
// 1. 尝试所有 CDN
for (const cdn of getAvailableCDNs()) {
const response = await tryFetch(cdn, resourcePath, version);
if (response.ok) return response;
}
// 2. 回退本地服务器
return fetch(localOrigin + resourcePath);
}
```
### 健康检测机制
- 自动检测 CDN 可用性
- 失败超过 3 次自动降级
- 1 分钟后自动恢复尝试
```typescript
// 标记 CDN 失败
markCDNFailure(cdnName);
// 检查 CDN 是否可用
if (isCDNAvailable(cdnName)) {
// 尝试使用
}
```
## 缓存策略
| 资源类型 | 策略 | Cache-Control |
|----------|------|---------------|
| HTML | Network First | max-age=0, must-revalidate |
| JS/CSS | Cache First | max-age=31536000, immutable |
| 图片 | Cache First | max-age=31536000, immutable |
| Service Worker | Network First | max-age=0, must-revalidate |
| version.json | Network First | max-age=0, must-revalidate |
## 使用场景
### 场景 1正常访问CDN 可用)
```
用户 → SW 缓存(命中)→ 立即返回
用户 → SW 缓存(未命中)→ unpkg → 缓存 → 返回
```
### 场景 2unpkg 不可用
```
用户 → SW 缓存(未命中)→ unpkg失败→ jsdelivr → 缓存 → 返回
```
### 场景 3所有 CDN 不可用
```
用户 → SW 缓存(未命中)→ unpkg失败→ jsdelivr失败→ 本地服务器 → 返回
```
### 场景 4完全离线
```
用户 → SW 缓存(命中)→ 返回(离线可用)
```
## 调试命令
```javascript
// 在浏览器控制台执行
// 查看 CDN 状态
__OPENTU_CDN_API__.sources
// 强制重新选择 CDN
__OPENTU_CDN_API__.reselectCDN()
// 清除 CDN 缓存
__OPENTU_CDN_API__.clearCDNCache()
> 兼容说明:运行时仍保留 `__AITU_CDN__` / `__AITU_CDN_API__` 旧别名读取能力,用于兼容历史缓存与调试入口,但新代码应统一使用 `__OPENTU_CDN__` / `__OPENTU_CDN_API__`。
```
## 监控与告警
建议接入监控:
1. **CDN 可用性监控**:定期检测各 CDN 节点
2. **加载性能监控**:记录资源加载时间
3. **错误率监控**:统计各源失败率
可通过 PostHog 自定义事件实现:
```typescript
// 记录 CDN 加载事件
posthog.capture('cdn_load', {
source: 'unpkg',
resource: 'index.js',
latency: 150,
success: true,
});
```
## 成本对比
| 方案 | 月流量成本 | 可用性 | 速度 |
|------|-----------|--------|------|
| 纯自有服务器 | ~$50-200/100GB | 99.9% | 一般 |
| Cloudflare Pages | $0 | 99.99% | 快 |
| npm CDN 回退 | $0 | 99.999% | 快 |
| 本方案(组合) | ~$0 | 99.999%+ | 最快 |
## 开发模式
本地开发时(`localhost` / `127.0.0.1`CDN 逻辑会自动跳过:
### 自动跳过的逻辑
| 组件 | 开发模式行为 |
|------|-------------|
| `cdn-config.js` | 跳过 CDN 检测,直接设置 `cdn: 'local'` |
| `cdn-fallback.ts` | `fetchFromCDNWithFallback()` 直接返回 `null` |
| Service Worker | 跳过 CDN 回退,直接使用本地服务器 |
### 开发模式判断
```javascript
// 以下情况被识别为开发模式:
const isDevelopment =
location.hostname === 'localhost' ||
location.hostname === '127.0.0.1' ||
location.hostname.endsWith('.localhost');
```
### 开发时的资源加载流程
```
开发模式:
用户请求 → SW 缓存(可选)→ 本地 Vite 服务器 → 返回
↓ 失败
显示错误(不尝试 CDN
```
### 强制测试 CDN 回退
如果需要在本地测试 CDN 回退逻辑,可以:
1. **使用 `ngrok` 或类似工具**:将本地服务暴露为公网域名
2. **修改 hosts 文件**:将测试域名指向 127.0.0.1
3. **构建后预览**`pnpm run build:web && pnpm run preview`preview 模式不被识别为开发模式)
## 常见问题
### Q: CDN 缓存更新延迟怎么办?
A:
- unpkg通常 1-5 分钟
- jsdelivr可能需要手动刷新缓存
- 使用版本号指定(`@0.5.16`)确保获取正确版本
### Q: 如何强制刷新 CDN 缓存?
A:
```bash
# jsdelivr 刷新
curl -X PURGE https://purge.jsdelivr.net/npm/aitu-app@0.5.16/
# unpkg 自动同步,无需手动刷新
```
### Q: 国内访问慢怎么办?
A:
1. jsdelivr 通常国内访问较快
2. 考虑添加国内 CDN 源(如 bootcdn、cdnjs
3. 使用 Cloudflare 中国节点
## 相关文件
- `/scripts/publish-npm.js` - npm 发布脚本
- `/apps/web/src/sw/cdn-fallback.ts` - CDN 回退策略
- `/apps/web/public/cdn-config.js` - CDN 选择器
- `/apps/web/public/_headers` - 缓存头配置
- `/docs/CFPAGE-DEPLOY.md` - Cloudflare Pages 部署
- `/docs/NPM_CDN_DEPLOY.md` - npm CDN 部署详情