Files
NextEdu/.gitea/workflows/ci.yml
SpecialX 6585e10c6f feat(P2): 实现质量保障类5项功能(无障碍/视觉回归/通知渠道/漏洞扫描/灾备)
## 新增功能

### 1. 屏幕阅读器兼容性增强(a11y)
- 无障碍工具库:src/shared/lib/a11y.ts
- aria-live Hook:src/shared/hooks/use-aria-live.ts
- a11y 组件:skip-link/visually-hidden/focus-trap/aria-status
- 增强 UI:table.tsx 系统性 ARIA role,dialog.tsx aria-modal
- 审计文档:docs/accessibility/a11y-audit.md(WCAG 2.1 AA 清单)

### 2. 视觉回归测试
- 测试套件:tests/visual/(homepage + 3 个 dashboard)
- 3 视口(desktop/tablet/mobile)× 2 主题(light/dark)
- 动态元素遮罩,避免误报
- playwright.config.ts 新增 visual-chromium 项目
- 文档:docs/testing/visual-regression.md

### 3. 短信/微信推送渠道集成
- 新模块:src/modules/notifications/
- 4 个渠道:SMS(阿里云/腾讯云)、WeChat(公众号)、Email(SMTP)、In-App
- 分发器按用户偏好并行多渠道发送
- 外部 SDK 动态 import,Mock 模式开发可用
- 文档:docs/notifications/channels.md

### 4. 漏洞扫描 CI 集成
- CI security-scan job:npm audit + Snyk + Trivy FS + OWASP ZAP
- 独立工作流 security.yml:每周一深度扫描 + 容器镜像扫描
- 配置:suppressions.json + .trivyignore
- 本地脚本:security-scan.sh/ps1
- 文档:docs/security/scanning.md(SLA 分级)

### 5. 灾备方案
- 脚本:backup-verify/backup-offsite-sync/dr-drill/failover/health-check
- CI 增强:备份后校验+异地同步,每周灾备演练
- 独立工作流 dr-drill.yml:每周一凌晨 4 点自动演练
- 文档:docs/dr/dr-plan.md(RTO 4h/RPO 24h)+ dr-runbook.md(6 故障场景)

## 验证
- npx tsc --noEmit:0 错误
- npm run lint:0 错误 0 警告
2026-06-17 20:18:29 +08:00

277 lines
8.6 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
schedule:
- cron: "0 2 * * *" # 每天凌晨 2 点触发定时备份
jobs:
build-deploy:
runs-on: CDCD
# 合并 Job统一使用带 Docker 的 Node 镜像
container: dockerreg.eazygame.cn/node-with-docker:22
env:
SKIP_ENV_VALIDATION: "1"
NEXT_TELEMETRY_DISABLED: "1"
steps:
- name: Checkout
uses: actions/checkout@v3
# 1. 增加 Cache 策略,显著加快 npm ci 速度
- name: Cache npm dependencies
uses: actions/cache@v3
id: npm-cache
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# 【保留增强】配置代理,确保 npm install 能通
- name: Configure npm proxy
run: |
GATEWAY_IP=$(ip route show | grep default | awk '{print $3}')
echo "Detected Docker Gateway: $GATEWAY_IP"
if [ -z "$GATEWAY_IP" ]; then
echo "Warning: Could not detect gateway IP, falling back to 172.17.0.1"
GATEWAY_IP="172.17.0.1"
fi
PROXY_URL="http://$GATEWAY_IP:7890"
echo "Using Proxy: $PROXY_URL"
# 设置 npm 代理
npm config set proxy "$PROXY_URL"
npm config set https-proxy "$PROXY_URL"
# 设置环境变量供后续步骤使用
echo "http_proxy=$PROXY_URL" >> $GITHUB_ENV
echo "https_proxy=$PROXY_URL" >> $GITHUB_ENV
echo "HTTP_PROXY=$PROXY_URL" >> $GITHUB_ENV
echo "HTTPS_PROXY=$PROXY_URL" >> $GITHUB_ENV
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Typecheck
run: npm run typecheck
- name: Install Playwright Chromium
run: npx playwright install chromium
- name: Integration tests
run: npm run test:integration
- name: E2E full regression tests
run: npm run test:e2e
# 2. 增加 Next.js 构建缓存
- name: Cache Next.js build
uses: actions/cache@v3
with:
path: |
~/.npm
${{ github.workspace }}/.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
- name: Build
run: npm run build
- name: Prepare standalone build
run: |
mkdir -p .next/standalone/public
mkdir -p .next/standalone/.next/static
cp -r public/* .next/standalone/public/
cp -r .next/static/* .next/standalone/.next/static/
cp Dockerfile .next/standalone/Dockerfile
# 【核心变更】合并 Deploy 步骤,直接构建镜像,无需 artifact
- name: Deploy to Docker
run: |
# 1. 进入 standalone 目录
cd .next/standalone
# 2. 构建镜像 (使用 standalone 目录下的 Dockerfile)
echo "Building Docker image from standalone..."
docker build --no-cache --pull -t nextjs-app .
# 3. 优雅停止
docker stop nextjs-app || true
docker rm nextjs-app || true
# 4. 运行容器
# 使用你后来补充的完整配置 (包含 network 和 NEXTAUTH)
docker run -d \
--init \
-p 8015:3000 \
--restart unless-stopped \
--name nextjs-app \
--network 1panel-network \
-e NODE_ENV=production \
-e DATABASE_URL=${{ secrets.DATABASE_URL }} \
-e NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET }} \
-e NEXTAUTH_URL=${{ secrets.NEXTAUTH_URL }} \
-e NEXT_TELEMETRY_DISABLED=1 \
nextjs-app
echo "Deploy complete!"
security-scan:
runs-on: ubuntu-latest
needs: build-deploy
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
# 1. npm audit(保留)
- name: npm audit
run: |
npm audit --audit-level=moderate || true
npm audit --json > audit-report.json || true
continue-on-error: true
# 2. Snyk 扫描(深度依赖分析)
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --sarif-file-output=snyk.sarif
continue-on-error: true
# 3. Trivy 文件系统扫描(扫描项目代码和依赖)
- name: Trivy FS Scan
run: |
trivy fs --format json --output trivy-fs-report.json --exit-code 0 .
trivy fs --format table --exit-code 0 .
continue-on-error: true
# 4. OWASP ZAP 基线扫描(扫描部署后的应用)
- name: OWASP ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.10.0
with:
target: ${{ secrets.NEXTAUTH_URL || 'http://localhost:8015' }}
cmd_options: '-a -j'
continue-on-error: true
# 5. 上传所有报告(失败不阻塞,但生成报告)
- uses: actions/upload-artifact@v3
if: always()
with:
name: security-reports
path: |
audit-report.json
trivy-fs-report.json
snyk.sarif
scheduled-backup:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run database backup
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BACKUP_DIR: ./backups
run: |
chmod +x scripts/backup-db.sh
./scripts/backup-db.sh
- name: Verify backup integrity
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BACKUP_DIR: ./backups
run: |
chmod +x scripts/backup-verify.sh
./scripts/backup-verify.sh
- name: Sync backup to offsite storage
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BACKUP_DIR: ./backups
BACKUP_OFFSITE_BACKEND: ${{ secrets.BACKUP_OFFSITE_BACKEND }}
BACKUP_OFFSITE_REMOTE: ${{ secrets.BACKUP_OFFSITE_REMOTE }}
BACKUP_OFFSITE_BUCKET: ${{ secrets.BACKUP_OFFSITE_BUCKET }}
BACKUP_OFFSITE_ACCESS_KEY: ${{ secrets.BACKUP_OFFSITE_ACCESS_KEY }}
BACKUP_OFFSITE_SECRET_KEY: ${{ secrets.BACKUP_OFFSITE_SECRET_KEY }}
BACKUP_OFFSITE_REGION: ${{ secrets.BACKUP_OFFSITE_REGION }}
run: |
chmod +x scripts/backup-offsite-sync.sh
./scripts/backup-offsite-sync.sh || echo "WARN: Offsite sync failed, continuing"
- uses: actions/upload-artifact@v3
with:
name: db-backup
path: backups/
retention-days: 30
backup-verify:
if: github.event_name == 'schedule'
needs: scheduled-backup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
with:
name: db-backup
path: backups/
- name: Verify backup integrity
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BACKUP_DIR: ./backups
run: |
chmod +x scripts/backup-verify.sh
./scripts/backup-verify.sh
- name: Run health check
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BACKUP_DIR: ./backups
HEALTH_CHECK_URL: ${{ secrets.HEALTH_CHECK_URL }}
run: |
chmod +x scripts/health-check.sh
./scripts/health-check.sh > health-report.json || true
- uses: actions/upload-artifact@v3
if: always()
with:
name: backup-verify-report
path: |
backups/
health-report.json
retention-days: 7
weekly-dr-drill:
if: github.event_name == 'schedule' && github.run_attempt % 7 == 0
needs: backup-verify
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run disaster recovery drill
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BACKUP_DIR: ./backups
DR_DRILL_TEST_DB: next_edu_dr_drill
run: |
chmod +x scripts/dr-drill.sh
./scripts/dr-drill.sh || echo "WARN: DR drill failed, see report"
- uses: actions/upload-artifact@v3
if: always()
with:
name: dr-drill-report
path: docs/dr/reports/
retention-days: 90