refactor: 完成静态网站转换,移除所有 CMS 和动态功能

- 删除数据库相关代码 (src/db/)
- 删除 API 路由 (src/app/api/)
- 删除认证相关代码 (src/lib/auth/, src/providers/)
- 删除监控和安全中间件 (src/lib/security/, src/lib/monitoring/)
- 删除 hooks (use-news, use-products, use-services)
- 更新组件为静态数据源
- 添加 nginx 静态配置和部署脚本
- 添加 static-link 组件
This commit is contained in:
张翔
2026-04-21 07:53:56 +08:00
parent cd1d6aa28a
commit 6403489954
197 changed files with 654 additions and 24762 deletions
+84
View File
@@ -0,0 +1,84 @@
#!/bin/bash
# Novalon 静态站点部署脚本
# 用法: bash scripts/deploy-static.sh [环境]
# 环境参数: production (默认)
set -euo pipefail
ENV="${1:-production}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
BUILD_DIR="$PROJECT_DIR/dist"
DEPLOY_DIR="/var/www/novalon"
BACKUP_DIR="/var/www/novalon-backup"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
echo "========================================="
echo " Novalon 静态站点部署"
echo " 环境: $ENV"
echo " 时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================="
# 步骤 1: 构建
echo ""
echo "[1/5] 构建静态站点..."
cd "$PROJECT_DIR"
npm run build
if [ ! -d "$BUILD_DIR" ]; then
echo "❌ 构建失败:dist 目录不存在"
exit 1
fi
echo "✅ 构建完成,产物大小:$(du -sh "$BUILD_DIR" | cut -f1)"
# 步骤 2: 备份当前版本
echo ""
echo "[2/5] 备份当前版本..."
if [ -d "$DEPLOY_DIR" ]; then
mkdir -p "$BACKUP_DIR"
cp -r "$DEPLOY_DIR" "$BACKUP_DIR/novalon-backup-$TIMESTAMP"
# 只保留最近 3 个备份
ls -t "$BACKUP_DIR"/ | tail -n +4 | xargs -I {} rm -rf "$BACKUP_DIR/{}"
echo "✅ 备份完成: $BACKUP_DIR/novalon-backup-$TIMESTAMP"
else
echo "⚠️ 首次部署,无需备份"
mkdir -p "$DEPLOY_DIR"
fi
# 步骤 3: 部署新版本
echo ""
echo "[3/5] 部署新版本..."
# 清空目标目录(保留 .well-known
if [ -d "$DEPLOY_DIR/.well-known" ]; then
mv "$DEPLOY_DIR/.well-known" /tmp/well-known-backup
fi
rm -rf "$DEPLOY_DIR"/*
cp -r "$BUILD_DIR"/* "$DEPLOY_DIR/"
if [ -d /tmp/well-known-backup ]; then
mv /tmp/well-known-backup "$DEPLOY_DIR/.well-known"
fi
echo "✅ 文件部署完成"
# 步骤 4: 设置权限
echo ""
echo "[4/5] 设置文件权限..."
chmod -R 755 "$DEPLOY_DIR"
echo "✅ 权限设置完成"
# 步骤 5: 重载 Nginx
echo ""
echo "[5/5] 重载 Nginx..."
if nginx -t 2>/dev/null; then
nginx -s reload
echo "✅ Nginx 重载成功"
else
echo "⚠️ Nginx 配置检查失败,跳过重载(请手动检查)"
fi
echo ""
echo "========================================="
echo " ✅ 部署完成!"
echo " 站点地址: https://www.novalon.cn"
echo " 部署目录: $DEPLOY_DIR"
echo "========================================="
-53
View File
@@ -1,53 +0,0 @@
#!/bin/bash
# 备份脚本
# 用法: ./scripts/backup.sh
set -e
BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="backup_$DATE"
# 创建备份目录
mkdir -p "$BACKUP_DIR/$BACKUP_NAME"
echo "开始备份..."
# 备份数据库
if [ -f "./data/prod.db" ]; then
echo "备份数据库..."
cp ./data/prod.db "$BACKUP_DIR/$BACKUP_NAME/database.db"
else
echo "警告: 数据库文件不存在"
fi
# 备份上传文件
if [ -d "./uploads" ]; then
echo "备份上传文件..."
cp -r ./uploads "$BACKUP_DIR/$BACKUP_NAME/uploads"
else
echo "警告: uploads目录不存在"
fi
# 备份配置
if [ -f ".env.production" ]; then
echo "备份配置..."
cp .env.production "$BACKUP_DIR/$BACKUP_NAME/.env.production"
else
echo "警告: .env.production文件不存在"
fi
# 压缩备份
echo "压缩备份..."
tar -czf "$BACKUP_DIR/$BACKUP_NAME.tar.gz" -C "$BACKUP_DIR" "$BACKUP_NAME"
# 删除临时目录
rm -rf "$BACKUP_DIR/$BACKUP_NAME"
# 保留最近7天的备份
echo "清理旧备份..."
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete
echo "备份完成: $BACKUP_DIR/$BACKUP_NAME.tar.gz"
echo "备份大小: $(du -h "$BACKUP_DIR/$BACKUP_NAME.tar.gz" | cut -f1)"
+4 -28
View File
@@ -4,26 +4,16 @@ set -e
echo "🚀 开始部署到生产环境..."
# 加载生产环境变量
export NODE_ENV=production
# 检查是否已安装依赖
if [ ! -d "node_modules" ]; then
echo "📦 安装依赖..."
npm ci --production=false
fi
# 运行测试
echo "🧪 运行测试..."
cd e2e
TEST_ENV=development npx playwright test --reporter=list
cd ..
# 构建生产版本
echo "🔨 构建生产版本..."
echo "🔨 构建静态网站..."
npm run build
# 备份当前版本(如果存在)
if [ -d "dist_backup" ]; then
rm -rf dist_backup
fi
@@ -32,20 +22,6 @@ if [ -d "dist" ]; then
mv dist dist_backup
fi
# 启动生产服务器
echo "🌟 启动生产服务器..."
npm start &
# 等待服务器启动
echo "⏳ 等待服务器启动..."
sleep 10
# 健康检查
echo "🏥 健康检查..."
curl -f http://localhost:3000/api/health || {
echo "❌ 健康检查失败!"
exit 1
}
echo "✅ 部署成功!"
echo "📊 访问 http://localhost:3000"
echo "✅ 构建完成!"
echo "📊 静态文件位于 dist/ 目录"
echo "🌐 可部署到 Nginx、CDN 或任何静态托管服务"
-69
View File
@@ -1,69 +0,0 @@
#!/bin/bash
# 恢复脚本
# 用法: ./scripts/restore.sh <backup_file.tar.gz>
set -e
if [ -z "$1" ]; then
echo "错误: 请指定备份文件"
echo "用法: ./scripts/restore.sh <backup_file.tar.gz>"
exit 1
fi
BACKUP_FILE="$1"
if [ ! -f "$BACKUP_FILE" ]; then
echo "错误: 备份文件不存在: $BACKUP_FILE"
exit 1
fi
echo "警告: 此操作将覆盖当前数据!"
read -p "确认继续? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "操作已取消"
exit 0
fi
# 创建临时目录
TEMP_DIR="./temp_restore_$(date +%s)"
mkdir -p "$TEMP_DIR"
echo "解压备份..."
tar -xzf "$BACKUP_FILE" -C "$TEMP_DIR"
# 获取备份目录名
BACKUP_DIR_NAME=$(ls "$TEMP_DIR")
BACKUP_PATH="$TEMP_DIR/$BACKUP_DIR_NAME"
# 恢复数据库
if [ -f "$BACKUP_PATH/database.db" ]; then
echo "恢复数据库..."
cp "$BACKUP_PATH/database.db" ./data/prod.db
else
echo "警告: 备份中没有数据库文件"
fi
# 恢复上传文件
if [ -d "$BACKUP_PATH/uploads" ]; then
echo "恢复上传文件..."
rm -rf ./uploads/*
cp -r "$BACKUP_PATH/uploads"/* ./uploads/ 2>/dev/null || true
else
echo "警告: 备份中没有uploads目录"
fi
# 恢复配置
if [ -f "$BACKUP_PATH/.env.production" ]; then
echo "恢复配置..."
cp "$BACKUP_PATH/.env.production" ./.env.production
else
echo "警告: 备份中没有配置文件"
fi
# 清理临时文件
rm -rf "$TEMP_DIR"
echo "恢复完成!"
echo "请重启应用以使更改生效"
-62
View File
@@ -1,62 +0,0 @@
#!/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 "✅ 推荐使用方法1Web UI设置)"
echo ""
-125
View File
@@ -1,125 +0,0 @@
#!/bin/bash
echo "========================================="
echo "Woodpecker CI密钥配置脚本"
echo "========================================="
echo ""
echo "此脚本将帮助您配置Woodpecker CI所需的密钥"
echo ""
# 检查是否在服务器上
if [ "$HOSTNAME" != "novalon-server" ]; then
echo "⚠️ 请在服务器上运行此脚本"
echo " ssh root@139.155.109.62"
echo " 然后运行: bash /home/novalon/scripts/setup-woodpecker-secrets.sh"
exit 1
fi
# Woodpecker CI CLI命令
WOODPECKER_CLI="woodpecker-cli"
# 检查woodpecker-cli是否安装
if ! command -v $WOODPECKER_CLI &> /dev/null; then
echo "❌ woodpecker-cli未安装"
echo " 请先安装: https://woodpecker-ci.org/docs/cli"
exit 1
fi
echo "步骤1: 配置SSH私钥"
echo "----------------------------------------"
echo "请确保您已经生成了SSH密钥对"
echo "公钥已添加到服务器的authorized_keys中"
echo ""
# 读取SSH私钥
if [ -f ~/.ssh/id_rsa ]; then
echo "✅ 找到SSH私钥: ~/.ssh/id_rsa"
SSH_KEY=$(cat ~/.ssh/id_rsa)
else
echo "❌ 未找到SSH私钥"
echo " 请先生成SSH密钥对: ssh-keygen -t rsa -b 4096"
exit 1
fi
echo ""
echo "步骤2: 配置企业微信通知"
echo "----------------------------------------"
echo "已配置企业微信Webhook URL:"
echo "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=bb7efcdc-c32f-47b7-a437-d76cab9fba74"
echo ""
WEBHOOK_URL="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=bb7efcdc-c32f-47b7-a437-d76cab9fba74"
echo "✅ 企业微信通知已配置"
echo ""
echo "步骤3: 配置Docker Registry密码"
echo "----------------------------------------"
echo "请输入Docker Registry密码:"
echo "用于推送到 registry.f.novalon.cn"
read -s -p "密码: " REGISTRY_PASSWORD
echo ""
echo ""
echo "步骤4: 设置Woodpecker CI密钥"
echo "----------------------------------------"
# 设置SSH私钥
echo "设置SSH_PRIVATE_KEY..."
echo "$SSH_KEY" | $WOODPECKER_CLI secret add \
--repository novalon/novalon-website \
--name ssh_private_key \
--value @-
if [ $? -eq 0 ]; then
echo "✅ SSH_PRIVATE_KEY设置成功"
else
echo "❌ SSH_PRIVATE_KEY设置失败"
exit 1
fi
# 设置Registry密码
echo "设置REGISTRY_PASSWORD..."
echo "$REGISTRY_PASSWORD" | $WOODPECKER_CLI secret add \
--repository novalon/novalon-website \
--name registry_password \
--value @-
if [ $? -eq 0 ]; then
echo "✅ REGISTRY_PASSWORD设置成功"
else
echo "❌ REGISTRY_PASSWORD设置失败"
exit 1
fi
# 设置Webhook URL
if [ -n "$WEBHOOK_URL" ]; then
echo "设置WEBHOOK_URL..."
echo "$WEBHOOK_URL" | $WOODPECKER_CLI secret add \
--repository novalon/novalon-website \
--name webhook_url \
--value @-
if [ $? -eq 0 ]; then
echo "✅ WEBHOOK_URL设置成功"
else
echo "❌ WEBHOOK_URL设置失败"
exit 1
fi
fi
echo ""
echo "========================================="
echo "✅ 密钥配置完成!"
echo "========================================="
echo ""
echo "已配置的密钥:"
echo " - SSH_PRIVATE_KEY ✅"
echo " - REGISTRY_PASSWORD ✅"
if [ -n "$WEBHOOK_URL" ]; then
echo " - WEBHOOK_URL ✅"
fi
echo ""
echo "下一步:"
echo " 1. 提交.woodpecker.yml到代码仓库"
echo " 2. 在Woodpecker CI中激活仓库"
echo " 3. 推送代码触发CI/CD流水线"
echo ""
echo "========================================="
-164
View File
@@ -1,164 +0,0 @@
#!/bin/bash
echo "🚀 分层测试系统验证"
echo "=========================="
echo ""
# 颜色定义
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 验证计数器
TOTAL_CHECKS=0
PASSED_CHECKS=0
FAILED_CHECKS=0
# 验证函数
check_file() {
local file=$1
local description=$2
TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
if [ -f "$file" ]; then
echo -e "${GREEN}${NC} $description: $file"
PASSED_CHECKS=$((PASSED_CHECKS + 1))
return 0
else
echo -e "${RED}${NC} $description: $file (文件不存在)"
FAILED_CHECKS=$((FAILED_CHECKS + 1))
return 1
fi
}
check_dir() {
local dir=$1
local description=$2
TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
if [ -d "$dir" ]; then
echo -e "${GREEN}${NC} $description: $dir"
PASSED_CHECKS=$((PASSED_CHECKS + 1))
return 0
else
echo -e "${RED}${NC} $description: $dir (目录不存在)"
FAILED_CHECKS=$((FAILED_CHECKS + 1))
return 1
fi
}
check_script() {
local script=$1
local description=$2
TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
if [ -f "$script" ] && [ -x "$script" ]; then
echo -e "${GREEN}${NC} $description: $script"
PASSED_CHECKS=$((PASSED_CHECKS + 1))
return 0
else
echo -e "${RED}${NC} $description: $script (文件不存在或不可执行)"
FAILED_CHECKS=$((FAILED_CHECKS + 1))
return 1
fi
}
check_npm_script() {
local script_name=$1
local description=$2
TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
if npm run | grep -q "$script_name"; then
echo -e "${GREEN}${NC} $description: npm run $script_name"
PASSED_CHECKS=$((PASSED_CHECKS + 1))
return 0
else
echo -e "${RED}${NC} $description: npm run $script_name (脚本不存在)"
FAILED_CHECKS=$((FAILED_CHECKS + 1))
return 1
fi
}
echo "📁 检查配置文件"
echo "-------------------"
check_file "e2e/src/config/test-tiers.ts" "测试层级配置"
check_file "e2e/src/config/test-tags.ts" "测试标记配置"
check_file "e2e/playwright.config.tiered.ts" "分层测试Playwright配置"
check_file ".woodpecker/test-tiered.yml" "Woodpecker CI配置"
check_file ".woodpecker/test-tiered-simple.yml" "简化版Woodpecker CI配置"
echo ""
echo "🔧 检查工具文件"
echo "-------------------"
check_file "e2e/src/utils/test-history.ts" "测试历史管理器"
check_file "e2e/src/utils/test-scheduler.ts" "智能测试调度器"
check_file "e2e/src/utils/test-reporter.ts" "测试报告生成器"
check_file "e2e/src/utils/test-monitor.ts" "测试监控器"
check_file "e2e/src/utils/test-alert.ts" "测试告警管理器"
check_file "e2e/src/utils/test-optimizer.ts" "测试性能优化器"
echo ""
echo "📝 检查脚本文件"
echo "-------------------"
check_file "e2e/global-setup.ts" "全局设置脚本"
check_file "e2e/global-teardown.ts" "全局清理脚本"
check_file "e2e/scripts/generate-report.js" "CI报告生成脚本"
check_file "scripts/validate-woodpecker-config.js" "Woodpecker配置验证脚本"
echo ""
echo "📚 检查文档文件"
echo "-------------------"
check_file "README-TIERED-TESTING.md" "快速入门指南"
check_file "docs/test-optimization-guide.md" "测试优化指南"
check_file "docs/test-tiering-best-practices.md" "最佳实践文档"
echo ""
echo "🎯 检查NPM脚本"
echo "-------------------"
check_npm_script "test:tier:fast" "快速层测试脚本"
check_npm_script "test:tier:standard" "标准层测试脚本"
check_npm_script "test:tier:deep" "深度层测试脚本"
check_npm_script "test:tier:all" "所有层级测试脚本"
check_npm_script "test:tier:ci" "CI测试脚本"
echo ""
echo "📊 检查测试文件"
echo "-------------------"
check_dir "e2e/src/tests/smoke" "冒烟测试目录"
check_dir "e2e/src/tests/api" "API测试目录"
check_dir "e2e/src/tests/admin" "管理后台测试目录"
echo ""
echo "🔍 验证TypeScript编译"
echo "-------------------"
TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
if cd e2e && npx tsc --noEmit src/config/test-tiers.ts src/config/test-tags.ts src/utils/*.ts 2>/dev/null; then
echo -e "${GREEN}${NC} TypeScript编译通过"
PASSED_CHECKS=$((PASSED_CHECKS + 1))
else
echo -e "${RED}${NC} TypeScript编译失败"
FAILED_CHECKS=$((FAILED_CHECKS + 1))
fi
cd ..
echo ""
echo "📈 生成验证报告"
echo "=========================="
echo ""
echo "总检查项: $TOTAL_CHECKS"
echo -e "${GREEN}通过: $PASSED_CHECKS${NC}"
echo -e "${RED}失败: $FAILED_CHECKS${NC}"
echo ""
if [ $FAILED_CHECKS -eq 0 ]; then
echo -e "${GREEN}🎉 所有验证通过!分层测试系统已就绪。${NC}"
exit 0
else
echo -e "${YELLOW}⚠️ 发现 $FAILED_CHECKS 个问题,请检查并修复。${NC}"
exit 1
fi
@@ -1,59 +0,0 @@
const fs = require('fs');
const path = require('path');
console.log('🔍 验证Woodpecker CI配置...');
const configFiles = [
'.woodpecker/test-tiered.yml',
'.woodpecker/test-tiered-simple.yml',
];
let allValid = true;
for (const configFile of configFiles) {
const filePath = path.join(__dirname, '..', configFile);
if (!fs.existsSync(filePath)) {
console.log(`❌ 配置文件不存在: ${configFile}`);
allValid = false;
continue;
}
const content = fs.readFileSync(filePath, 'utf-8');
if (content.includes('when:') && content.includes('pipeline:')) {
console.log(`${configFile} - 配置格式正确`);
} else {
console.log(`${configFile} - 配置格式错误`);
allValid = false;
}
if (content.includes('TEST_TIER')) {
console.log(`${configFile} - 包含分层测试环境变量`);
} else {
console.log(`${configFile} - 缺少分层测试环境变量`);
allValid = false;
}
if (content.includes('depends_on')) {
console.log(`${configFile} - 包含任务依赖配置`);
} else {
console.log(`⚠️ ${configFile} - 未配置任务依赖`);
}
}
const reportScript = path.join(__dirname, '..', 'e2e/scripts/generate-report.js');
if (fs.existsSync(reportScript)) {
console.log(`✅ 测试报告脚本存在`);
} else {
console.log(`❌ 测试报告脚本不存在`);
allValid = false;
}
if (allValid) {
console.log('\n✅ 所有配置验证通过');
process.exit(0);
} else {
console.log('\n❌ 部分配置验证失败');
process.exit(1);
}