diff --git a/.woodpecker.yml b/.woodpecker.yml index 1285aa6..fcdfab1 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -117,7 +117,7 @@ steps: - push branch: - release - - release/* + - release/** # 3.3 深度测试 (release分支) e2e-deep: @@ -135,7 +135,7 @@ steps: - push branch: - release - - release/* + - release/** # 3.4 性能测试 (release分支) e2e-performance: @@ -153,7 +153,7 @@ steps: - push branch: - release - - release/* + - release/** # 3.5 可访问性测试 (release分支) e2e-accessibility: @@ -171,7 +171,7 @@ steps: - push branch: - release - - release/* + - release/** # 3.6 视觉回归测试 (release分支) e2e-visual: @@ -189,7 +189,7 @@ steps: - push branch: - release - - release/* + - release/** # ============================================ # 阶段4: 构建Docker镜像 (release分支) @@ -216,7 +216,7 @@ steps: - event: push branch: - release - - release/* + - release/** # ============================================ # 阶段5: 部署到生产环境 (release分支) @@ -314,7 +314,7 @@ steps: - push branch: - release - - release/* + - release/** # ============================================ # 阶段6: 归档到main分支 (release分支) @@ -369,7 +369,7 @@ steps: - push branch: - release - - release/* + - release/** status: - success diff --git a/docs/CICD_QUICK_START.md b/docs/CICD_QUICK_START.md new file mode 100644 index 0000000..cc7d2d7 --- /dev/null +++ b/docs/CICD_QUICK_START.md @@ -0,0 +1,357 @@ +# 🚀 CI/CD流水线快速设置指南 + +## 📋 前置条件 + +- ✅ Gitea已部署并配置 (https://git.f.novalon.cn) +- ✅ Woodpecker CI已部署并配置 (https://ci.f.novalon.cn) +- ✅ Docker Registry已部署并配置 (https://registry.f.novalon.cn) +- ✅ 服务器已配置SSH免密登录 + +## 🔧 快速配置步骤 + +### 步骤1: 配置Woodpecker CI密钥 + +#### 方式A: 使用自动化脚本 (推荐) + +```bash +# 1. 上传脚本到服务器 +scp scripts/setup-woodpecker-secrets.sh root@139.155.109.62:/home/novalon/scripts/ + +# 2. SSH到服务器 +ssh root@139.155.109.62 + +# 3. 运行配置脚本 +chmod +x /home/novalon/scripts/setup-woodpecker-secrets.sh +/home/novalon/scripts/setup-woodpecker-secrets.sh +``` + +#### 方式B: 手动配置 + +```bash +# 1. SSH到服务器 +ssh root@139.155.109.62 + +# 2. 设置SSH私钥 +woodpecker-cli secret add \ + --repository novalon/novalon-website \ + --name ssh_private_key \ + --value @- <<< "$(cat ~/.ssh/id_rsa)" + +# 3. 设置Webhook URL (可选) +woodpecker-cli secret add \ + --repository novalon/novalon-website \ + --name webhook_url \ + --value @- <<< "YOUR_WEBHOOK_URL" +``` + +### 步骤2: 在Gitea中创建仓库 + +```bash +# 1. 访问 https://git.f.novalon.cn +# 2. 使用管理员账户登录 +# 用户名: novalon-admin +# 密码: Novalon@Admin2026 +# 3. 创建新仓库: novalon/novalon-website +# 4. 添加远程仓库 +git remote add origin https://git.f.novalon.cn/novalon/novalon-website.git +``` + +### 步骤3: 在Woodpecker CI中激活仓库 + +```bash +# 1. 访问 https://ci.f.novalon.cn +# 2. 使用Gitea账户登录 (自动SSO) +# 3. 点击"Add Repository" +# 4. 选择 novalon/novalon-website 仓库 +# 5. 点击"Activate" +``` + +### 步骤4: 配置服务器部署目录 + +```bash +# SSH到服务器 +ssh root@139.155.109.62 + +# 创建部署目录 +mkdir -p /home/novalon/docker-app/novalon-website +cd /home/novalon/docker-app/novalon-website + +# 创建docker-compose.yml +cat > docker-compose.yml << 'EOF' +version: '3.8' + +services: + novalon-website: + image: registry.f.novalon.cn/novalon-website:latest + container_name: novalon-website + restart: always + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DATABASE_URL=file:/app/data/local.db + volumes: + - ./data:/app/data + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + networks: + - novalon-network + +networks: + novalon-network: + external: true +EOF + +# 创建数据目录 +mkdir -p data +``` + +### 步骤5: 提交代码并触发CI/CD + +```bash +# 在本地项目目录 +cd /Users/zhangxiang/Codes/Gitee/home-page/novalon-website + +# 添加所有文件 +git add . + +# 提交代码 +git commit -m "feat: 配置全自动CI/CD工作流 + +- 添加完整的CI/CD流水线配置 +- 配置代码质量检查(lint, type-check, security) +- 配置分层测试策略(fast, standard, deep) +- 配置Docker镜像构建和推送 +- 配置自动部署到staging和production环境 +- 配置健康检查和自动回滚 +- 配置成功/失败通知 +- 添加健康检查API端点 +- 创建CI/CD配置文档" + +# 推送到develop分支 +git push -u origin develop + +# 或者推送到main分支 +git push -u origin main +``` + +## 📊 验证CI/CD流水线 + +### 1. 查看构建状态 + +```bash +# 访问Woodpecker CI +https://ci.f.novalon.cn/novalon/website + +# 查看构建日志 +# 每个步骤都有详细的日志输出 +``` + +### 2. 验证部署 + +#### Staging环境 (develop分支) +```bash +# 检查容器状态 +ssh root@139.155.109.62 +docker ps | grep novalon-website + +# 查看容器日志 +docker logs novalon-website -f + +# 健康检查 +curl http://localhost:3000/api/health +``` + +#### Production环境 (main分支) +```bash +# 检查容器状态 +ssh root@139.155.109.62 +docker ps | grep novalon-website + +# 查看容器日志 +docker logs novalon-website -f + +# 健康检查 +curl https://novalon.cn/api/health +``` + +### 3. 验证通知 + +如果配置了Webhook,您应该会收到通知: +- ✅ 成功通知:绿色,包含构建信息 +- ❌ 失败通知:红色,包含错误信息和构建链接 + +## 🔄 日常使用流程 + +### 开发新功能 + +```bash +# 1. 创建功能分支 +git checkout -b feature/new-feature + +# 2. 开发并提交 +git add . +git commit -m "feat: 添加新功能" + +# 3. 推送到远程 +git push origin feature/new-feature + +# 4. 在Gitea创建Pull Request +# 访问: https://git.f.novalon.cn/novalon/novalon-website/pulls + +# 5. CI自动运行测试 +# - Lint检查 +# - 类型检查 +# - 单元测试 +# - Smoke测试 + +# 6. 代码审查通过后合并到develop +# - 自动触发完整测试 +# - 自动构建Docker镜像 +# - 自动部署到Staging环境 + +# 7. 测试通过后合并到main +# - 自动触发完整测试 +# - 自动构建Docker镜像 +# - 自动部署到Production环境 +``` + +### 紧急修复 + +```bash +# 1. 创建hotfix分支 +git checkout -b hotfix/critical-fix main + +# 2. 修复并提交 +git add . +git commit -m "fix: 修复关键问题" + +# 3. 推送并创建PR +git push origin hotfix/critical-fix + +# 4. 快速审查并合并到main +# - 自动部署到Production +# - 自动回滚机制保障 +``` + +## 🛠️ 故障排查 + +### 构建失败 + +```bash +# 1. 查看Woodpecker CI日志 +https://ci.f.novalon.cn/novalon/novalon-website + +# 2. 常见原因 +# - 依赖安装失败 +# - TypeScript类型错误 +# - 测试失败 +# - Docker构建失败 + +# 3. 本地重现 +npm ci +npm run lint +npm run type-check +npm run test:coverage:check +npm run build +``` + +### 部署失败 + +```bash +# 1. SSH到服务器 +ssh root@139.155.109.62 + +# 2. 检查容器状态 +docker ps -a | grep novalon-website + +# 3. 查看容器日志 +docker logs novalon-website + +# 4. 检查健康状态 +curl http://localhost:3000/api/health + +# 5. 手动回滚 +docker images | grep novalon-website +docker tag novalon-website:backup- novalon-website:latest +cd /home/novalon/docker-app/novalon-website +docker-compose up -d --no-deps novalon-website +``` + +### 测试失败 + +```bash +# 1. 本地运行测试 +npm run test:smoke # Smoke测试 +npm run test:tier:standard # 标准测试 +npm run test:tier:deep # 深度测试 + +# 2. 查看测试报告 +npm run test:allure:open + +# 3. 调试特定测试 +npx playwright test --debug +``` + +## 📈 性能优化建议 + +### 1. 加速构建 + +```yaml +# 在.woodpecker.yml中添加缓存 +cache: + - name: npm-cache + paths: + - node_modules + - e2e/node_modules +``` + +### 2. 并行执行 + +```yaml +# Woodpecker CI自动并行执行独立步骤 +# 无需额外配置 +``` + +### 3. 增量构建 + +```yaml +# 利用Docker层缓存 +# 在Dockerfile中优化层顺序 +``` + +## 🔐 安全最佳实践 + +### 1. 密钥管理 + +- ✅ 所有密钥存储在Woodpecker CI中 +- ✅ 不在代码中硬编码 +- ✅ 定期轮换密钥 + +### 2. 访问控制 + +- ✅ main分支受保护 +- ✅ PR需要代码审查 +- ✅ 部署需要审批 + +### 3. 安全扫描 + +- ✅ npm audit自动扫描 +- ✅ 定期更新依赖 +- ✅ 修复高危漏洞 + +## 📞 获取帮助 + +如有问题,请: +1. 查看 [CI/CD配置文档](./CICD_GUIDE.md) +2. 检查Woodpecker CI日志 +3. 联系运维团队: ops@novalon.cn + +--- + +**最后更新**: 2026-03-27 +**版本**: 1.0.0 diff --git a/docs/plans/2026-03-28-monorepo-multi-site-architecture.md b/docs/plans/2026-03-28-monorepo-multi-site-architecture.md new file mode 100644 index 0000000..1623290 --- /dev/null +++ b/docs/plans/2026-03-28-monorepo-multi-site-architecture.md @@ -0,0 +1,255 @@ +# Monorepo 多站点架构设计方案 + +## 背景 + +当企业需要为多个产品/项目创建独立展示时,面临架构选择:**单独页面** vs **独立网站**。 + +经过需求分析,确定以下约束条件: + +| 维度 | 需求 | 架构影响 | +|------|------|----------| +| 产品数量 | 动态增长,未来持续增加 | 需要高可扩展性 | +| 品牌关系 | 独立子品牌 | 需要视觉独立性 | +| 团队规模 | 1-2人精简团队 | 需要低维护成本 | +| SEO要求 | 高要求,独立域名 | 需要独立部署能力 | + +## 方案对比 + +### 方案A:独立网站(多仓库) + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ 产品A (独立仓库) │ │ 产品B (独立仓库) │ │ 产品C (独立仓库) │ +│ product-a.com │ │ product-b.com │ │ product-c.com │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +**优点**:完全独立、SEO最优、互不影响 +**缺点**:❌ 维护成本极高、代码重复严重、安全更新繁琐 + +### 方案B:单站内嵌页面 + +``` +┌──────────────────────────────────────────────────────┐ +│ novalon.cn (主站) │ +│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ +│ │ /product-a │ │ /product-b │ │ /product-c │ │ +│ └────────────┘ └────────────┘ └────────────┘ │ +└──────────────────────────────────────────────────────┘ +``` + +**优点**:维护成本最低、部署简单 +**缺点**:❌ 无法独立域名、SEO受限、品牌独立性差 + +### 方案C:Monorepo多站点架构 ⭐ 推荐 + +``` +┌─────────────────────────────────────────────────────────┐ +│ Monorepo (统一仓库) │ +├─────────────────────────────────────────────────────────┤ +│ apps/ │ +│ ├── main-site/ → novalon.cn │ +│ ├── product-a/ → product-a.com (独立域名) │ +│ ├── product-b/ → product-b.com (独立域名) │ +│ └── product-c/ → product-c.com (独立域名) │ +│ │ +│ packages/ (共享代码) │ +│ ├── ui/ → 共享组件库 │ +│ ├── config/ → 共享配置 │ +│ └── utils/ → 共享工具函数 │ +└─────────────────────────────────────────────────────────┘ +``` + +## 技术设计 + +### 目录结构 + +``` +novalon-website/ +├── apps/ # 应用层(独立部署) +│ ├── main-site/ # 主站 → novalon.cn +│ │ ├── src/ +│ │ ├── next.config.ts +│ │ └── package.json +│ │ +│ └── products/ # 产品站点集合 +│ ├── [product-slug]/ # 产品模板(可复制) +│ │ ├── src/ +│ │ ├── public/ +│ │ ├── next.config.ts +│ │ └── package.json +│ └── ... +│ +├── packages/ # 共享层(不独立部署) +│ ├── ui/ # 共享组件库 +│ │ ├── components/ +│ │ │ ├── base/ # 基础组件(Button、Card等) +│ │ │ └── layout/ # 布局组件(Header、Footer等) +│ │ └── package.json +│ │ +│ ├── config/ # 共享配置 +│ │ ├── tailwind/ # Tailwind预设 +│ │ ├── eslint/ # ESLint规则 +│ │ └── typescript/ # TS配置 +│ │ +│ └── utils/ # 共享工具 +│ ├── lib/ +│ └── package.json +│ +├── turbo.json # Turborepo配置 +├── pnpm-workspace.yaml # pnpm工作区配置 +└── package.json # 根package.json +``` + +### 共享组件库设计 + +``` +packages/ui/ +├── components/ +│ ├── base/ # 基础组件(完全共享) +│ │ ├── button/ +│ │ ├── card/ +│ │ ├── input/ +│ │ └── ... +│ │ +│ └── themed/ # 主题化组件(可覆盖) +│ ├── header/ +│ └── footer/ +│ +├── themes/ # 主题配置 +│ ├── default.ts # 默认主题 +│ ├── product-a.ts # 产品A主题 +│ └── product-b.ts # 产品B主题 +│ +└── lib/ + └── theme-context.tsx # 主题上下文 +``` + +**组件分层策略**: + +| 组件类型 | 共享程度 | 定制方式 | +|----------|----------|----------| +| 基础组件 | 100%共享 | 通过 props 和 CSS 变量覆盖样式 | +| 布局组件 | 接口共享 | 各应用可提供自己的实现 | +| 业务组件 | 不共享 | 各应用独立开发 | + +### CI/CD 流水线 + +``` +[代码推送] → [变更检测] → [增量构建] → [并行测试] → [智能部署] + ↓ ↓ ↓ + 哪些应用变了? 只构建变的应用 只部署变的应用 +``` + +**部署策略**: + +| 场景 | 构建范围 | 部署范围 | +|------|----------|----------| +| 只改了 `apps/product-a` | 只构建 product-a | 只部署 product-a | +| 改了 `packages/ui` | 构建所有应用 | 部署所有应用 | +| 改了 `packages/config` | 构建所有应用 | 部署所有应用 | + +### SEO优化策略 + +**独立域名架构**: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Nginx 反向代理 │ +├─────────────────────────────────────────────────────────────┤ +│ novalon.cn → 主站容器 (localhost:3000) │ +│ product-a.com → 产品A容器 (localhost:3001) │ +│ product-b.com → 产品B容器 (localhost:3002) │ +└─────────────────────────────────────────────────────────────┘ +``` + +**SEO关键优势**: + +| SEO 要素 | 独立站点优势 | +|----------|-------------| +| 独立域名 | 搜索引擎视为独立实体,权重互不影响 | +| 独立 sitemap | 精准控制索引范围,提升爬取效率 | +| 独立 metadata | 针对产品特性优化关键词,避免稀释 | +| 独立 robots.txt | 灵活控制爬虫访问策略 | + +## 迁移路径 + +``` +阶段1: 基础设施搭建 (1-2天) + ↓ +阶段2: 代码迁移与重构 (3-5天) + ↓ +阶段3: 共享组件抽取 (2-3天) + ↓ +阶段4: CI/CD 配置 (1-2天) + ↓ +阶段5: 第一个产品站点 (2-3天) +``` + +### 阶段1:基础设施搭建 + +```bash +# 1. 创建 Monorepo 根目录结构 +mkdir -p apps packages + +# 2. 初始化 pnpm 工作区 +cat > pnpm-workspace.yaml << EOF +packages: + - 'apps/*' + - 'apps/products/*' + - 'packages/*' +EOF + +# 3. 安装 Turborepo +pnpm add -Dw turbo +``` + +### 阶段2:代码迁移 + +```bash +# 将现有代码移动到 apps/main-site +mv src apps/main-site/src +mv public apps/main-site/public +mv next.config.ts apps/main-site/ +``` + +### 阶段3:共享组件抽取 + +```bash +# 创建共享 UI 包 +mkdir -p packages/ui/components + +# 抽取通用组件 +mv apps/main-site/src/components/ui packages/ui/components/base +``` + +### 阶段4:创建产品站点 + +```bash +# 复制主站作为模板 +cp -r apps/main-site apps/products/product-template + +# 创建新产品站点 +cp -r apps/products/product-template apps/products/product-a +``` + +## 决策总结 + +| 评估维度 | 独立网站 | 单站内嵌页面 | Monorepo多站点 | +|----------|----------|--------------|----------------| +| 独立品牌支持 | ✅ 完美 | ❌ 差 | ✅ 完美 | +| SEO独立性 | ✅ 最优 | ❌ 受限 | ✅ 最优 | +| 维护成本 | ❌ 极高 | ✅ 最低 | ✅ 低 | +| 代码复用 | ❌ 无 | ✅ 完全 | ✅ 高度复用 | +| 扩展性 | ⚠️ 中等 | ❌ 差 | ✅ 优秀 | +| 团队适配 | ❌ 不适合精简团队 | ⚠️ 不满足需求 | ✅ 完美适配 | + +## 结论 + +针对**动态增长 + 独立子品牌 + 精简团队 + 高SEO要求**的场景,**Monorepo多站点架构**是最佳选择: + +- ✅ 品牌独立:每个产品独立应用、独立域名、独立视觉 +- ✅ SEO最优:独立sitemap、独立metadata、独立域名权重 +- ✅ 维护高效:共享代码库、统一依赖、一次更新全局生效 +- ✅ 扩展简单:新增产品只需复制模板目录 +- ✅ 智能CI/CD:增量构建、按需部署、自动化流水线 diff --git a/scripts/set-woodpecker-trusted.sh b/scripts/set-woodpecker-trusted.sh new file mode 100644 index 0000000..f225356 --- /dev/null +++ b/scripts/set-woodpecker-trusted.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Woodpecker CI - 通过API设置仓库为Trusted +# 用途:解决 "Insufficient trust level to use volumes" 和 "Insufficient trust level to use privileged mode" 错误 + +set -e + +echo "==========================================" +echo "Woodpecker CI - 设置仓库为Trusted" +echo "==========================================" +echo "" + +# 配置 +WOODPECKER_SERVER="https://ci.f.novalon.cn" +REPO_OWNER="novalon" +REPO_NAME="novalon-website" + +echo "📋 方法1: 通过Web UI设置(推荐)" +echo "==========================================" +echo "" +echo "步骤1: 访问 ${WOODPECKER_SERVER}" +echo "步骤2: 登录(使用Gitea账号)" +echo "步骤3: 选择仓库 ${REPO_OWNER}/${REPO_NAME}" +echo "步骤4: 点击右上角 Settings" +echo "步骤5: 勾选 Trusted 选项" +echo "步骤6: 点击 Save" +echo "" + +echo "📋 方法2: 通过API设置" +echo "==========================================" +echo "" +echo "步骤1: 获取管理员Token" +echo " 1.1 访问 ${WOODPECKER_SERVER}" +echo " 1.2 点击右上角用户头像" +echo " 1.3 选择 Account" +echo " 1.4 复制 Token" +echo "" +echo "步骤2: 执行API请求" +echo " curl -X PATCH \"${WOODPECKER_SERVER}/api/repos/${REPO_OWNER}/${REPO_NAME}\" \\" +echo " -H \"Authorization: Bearer YOUR_TOKEN\" \\" +echo " -H \"Content-Type: application/json\" \\" +echo " -d '{\"trusted\": true}'" +echo "" + +echo "📋 方法3: 通过Woodpecker Server配置" +echo "==========================================" +echo "" +echo "如果以上方法不可行,可以在Server端配置:" +echo "" +echo "步骤1: 编辑 /home/novalon/docker-app/novalon-cicd/docker-compose.yml" +echo "步骤2: 在 woodpecker-server 服务中添加:" +echo " environment:" +echo " - WOODPECKER_OPEN=true" +echo " - WOODPECKER_ADMIN=your-admin-username" +echo "" +echo "步骤3: 重启服务:" +echo " cd /home/novalon/docker-app/novalon-cicd" +echo " docker-compose restart woodpecker-server" +echo "" + +echo "✅ 推荐使用方法1(Web UI设置)" +echo "" diff --git a/src/app/globals.css b/src/app/globals.css index aadb46d..a680c1e 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -5,11 +5,20 @@ src: url('/fonts/AoyagiReisho.ttf') format('truetype'); font-weight: normal; font-style: normal; - font-display: swap; + font-display: block; font-stretch: normal; unicode-range: U+4E00-9FFF, U+3400-4DBF, U+20000-2A6DF, U+2A700-2B73F, U+2B740-2B81F, U+2B820-2CEAF, U+F900-FAFF, U+2F800-2FA1F; } +/* 字体加载优化 - 防止 FOUT */ +.font-loading { + font-family: 'STKaiti', 'KaiTi', serif; +} + +.font-loaded { + font-family: 'Aoyagi Reisho', 'STKaiti', 'KaiTi', serif; +} + @theme inline { --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); @@ -39,8 +48,8 @@ /* 文字色系 - 墨色层次 */ --color-text-primary: #1C1C1C; --color-text-secondary: #3D3D3D; - --color-text-tertiary: #4A4A4A; - --color-text-muted: #6B6B6B; + --color-text-tertiary: #404040; /* 从 #4A4A4A 调整,提升对比度 */ + --color-text-muted: #595959; /* 从 #6B6B6B 调整,确保 WCAG AA 合规 */ /* 边框色系 */ --color-border-primary: #E5E5E5; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index aa392b2..a772be6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -8,6 +8,8 @@ import { Analytics } from "@vercel/analytics/react"; import { OrganizationSchema, WebsiteSchema } from "@/components/seo/structured-data"; import { MobileTabBar } from "@/components/layout/mobile-tab-bar"; import { ErrorBoundary } from "@/components/ui/error-boundary"; +import { ScrollProgress } from "@/components/ui/scroll-progress"; +import { BackToTop } from "@/components/ui/back-to-top"; import { SessionProvider } from "@/providers/session-provider"; import { initSentry } from "@/lib/sentry"; @@ -122,6 +124,14 @@ export default function RootLayout({ + {/* 字体预加载优化 */} +