主要变更: - 添加完整的项目结构和模块(admin、articles、comments、users、session、oauth2、email、moderation、analytics、jobs 等) - 实现系统初始化 API(/init/status 和 /init/run) - 重写部署流程:迁移到 package.json scripts,删除 Makefile - 优化部署脚本:deploy.sh、healthcheck.sh、backup.sh、restore.sh、verify-env.sh - 更新 README.md:简化文档,整合部署指南 - 优化 AGENTS.md:精简到约 150 行,包含完整的代码规范和命令速查 - 配置 Docker Compose 自动化部署(prisma migrate deploy + seed) - 生成 OAuth2 RSA 密钥对支持 - 添加环境变量验证和数据库备份恢复功能
313 lines
8.6 KiB
Bash
313 lines
8.6 KiB
Bash
#!/bin/bash
|
||
|
||
# ============================================
|
||
# LinkShare Blog - 现代化一键部署脚本
|
||
# ============================================
|
||
# 用途: 自动化部署流程,支持初始化检查
|
||
# 用法: ./scripts/deploy.sh [options]
|
||
# 示例: ./scripts/deploy.sh
|
||
# ./scripts/deploy.sh --dev
|
||
# ./scripts/deploy.sh --init-check
|
||
|
||
set -e
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||
cd "$PROJECT_ROOT"
|
||
|
||
# 颜色定义
|
||
GREEN='\033[0;32m'
|
||
BLUE='\033[0;34m'
|
||
YELLOW='\033[1;33m'
|
||
RED='\033[0;31m'
|
||
NC='\033[0m'
|
||
|
||
log_info() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||
log_step() { echo -e "${BLUE}[→]${NC} $1"; }
|
||
log_warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||
log_error() { echo -e "${RED}[✗]${NC} $1"; }
|
||
|
||
print_banner() {
|
||
echo "=========================================="
|
||
echo " LinkShare Blog 一键部署"
|
||
echo "=========================================="
|
||
echo ""
|
||
}
|
||
|
||
show_usage() {
|
||
echo "用法: $0 [选项]"
|
||
echo ""
|
||
echo "选项:"
|
||
echo " --dev, --development 开发环境部署"
|
||
echo " --prod, --production 生产环境部署 (默认)"
|
||
echo " --skip-build 跳过镜像构建"
|
||
echo " --skip-migrate 跳过数据库迁移"
|
||
echo " --skip-seed 跳过数据库种子"
|
||
echo " --init-check 部署后检查初始化状态"
|
||
echo " --admin-token TOKEN 管理员 token(用于初始化检查)"
|
||
echo " --no-deps 不启动依赖服务"
|
||
echo " --backup 部署前自动备份数据库"
|
||
echo " -h, --help 显示此帮助"
|
||
echo ""
|
||
echo "示例:"
|
||
echo " $0 # 完整生产部署"
|
||
echo " $0 --dev # 开发环境部署"
|
||
echo " $0 --skip-build --init-check # 跳过构建并检查初始化"
|
||
echo ""
|
||
exit 1
|
||
}
|
||
|
||
# 参数解析
|
||
ENV="production"
|
||
SKIP_BUILD=false
|
||
SKIP_MIGRATE=false
|
||
SKIP_SEED=false
|
||
INIT_CHECK=false
|
||
ADMIN_TOKEN=""
|
||
INCLUDE_DEPS=true
|
||
BACKUP_BEFORE=false
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
--dev|--development)
|
||
ENV="development"
|
||
shift
|
||
;;
|
||
--prod|--production)
|
||
ENV="production"
|
||
shift
|
||
;;
|
||
--skip-build)
|
||
SKIP_BUILD=true
|
||
shift
|
||
;;
|
||
--skip-migrate)
|
||
SKIP_MIGRATE=true
|
||
shift
|
||
;;
|
||
--skip-seed)
|
||
SKIP_SEED=true
|
||
shift
|
||
;;
|
||
--init-check)
|
||
INIT_CHECK=true
|
||
shift
|
||
;;
|
||
--admin-token)
|
||
ADMIN_TOKEN="$2"
|
||
shift 2
|
||
;;
|
||
--no-deps)
|
||
INCLUDE_DEPS=false
|
||
shift
|
||
;;
|
||
--backup)
|
||
BACKUP_BEFORE=true
|
||
shift
|
||
;;
|
||
-h|--help)
|
||
show_usage
|
||
;;
|
||
*)
|
||
log_error "未知选项: $1"
|
||
show_usage
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# 工具函数
|
||
wait_for_service() {
|
||
local url=$1
|
||
local name=$2
|
||
local max_attempts=30
|
||
local attempt=1
|
||
|
||
log_step "等待 $name 就绪..."
|
||
while [ $attempt -le $max_attempts ]; do
|
||
if curl -sf "$url" >/dev/null 2>&1; then
|
||
log_info "$name 已就绪"
|
||
return 0
|
||
fi
|
||
echo -n "."
|
||
sleep 2
|
||
((attempt++))
|
||
done
|
||
echo ""
|
||
log_error "$name 未能在预期时间内就绪"
|
||
return 1
|
||
}
|
||
|
||
check_deployment_success() {
|
||
log_step "验证部署结果..."
|
||
|
||
# 基本健康检查
|
||
if ! curl -sf http://localhost:3001/health >/dev/null; then
|
||
log_error "API 健康检查失败"
|
||
return 1
|
||
fi
|
||
|
||
log_info "API 健康检查通过"
|
||
|
||
# 检查初始化状态(如果要求)
|
||
if [ "$INIT_CHECK" = true ] && [ -n "$ADMIN_TOKEN" ]; then
|
||
local init_response=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||
http://localhost:3001/init/status)
|
||
|
||
if [ "$init_response" = "200" ]; then
|
||
log_info "初始化状态检查通过"
|
||
else
|
||
log_warn "初始化状态检查失败 (HTTP $init_response)"
|
||
fi
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# 主部署流程
|
||
main() {
|
||
print_banner
|
||
log_info "目标环境: $ENV"
|
||
echo ""
|
||
|
||
# 1. 环境验证
|
||
log_step "1/5 验证环境配置..."
|
||
if [ ! -f ".env" ]; then
|
||
log_warn ".env 文件不存在,从模板创建..."
|
||
if [ -f ".env.example" ]; then
|
||
cp .env.example .env
|
||
log_warn "请编辑 .env 文件设置必要的配置!"
|
||
else
|
||
log_error "未找到 .env.example 模板"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
if [ -f "scripts/verify-env.sh" ]; then
|
||
log_info "运行环境验证..."
|
||
if ! bash scripts/verify-env.sh; then
|
||
log_error "环境验证失败,请修复上述问题"
|
||
exit 1
|
||
fi
|
||
fi
|
||
echo ""
|
||
|
||
# 2. 备份数据库(可选)
|
||
if [ "$BACKUP_BEFORE" = true ]; then
|
||
log_step "2/5 备份数据库..."
|
||
if [ -f "scripts/backup.sh" ]; then
|
||
bash scripts/backup.sh "pre-deploy-$(date +%Y%m%d_%H%M%S)" || {
|
||
log_warn "备份失败,但继续部署..."
|
||
}
|
||
fi
|
||
echo ""
|
||
fi
|
||
|
||
# 3. 构建镜像
|
||
if [ "$SKIP_BUILD" = false ]; then
|
||
log_step "3/5 构建 Docker 镜像..."
|
||
if ! docker-compose build api; then
|
||
log_error "镜像构建失败"
|
||
exit 1
|
||
fi
|
||
log_info "镜像构建成功"
|
||
echo ""
|
||
fi
|
||
|
||
# 4. 启动服务
|
||
log_step "4/5 启动服务..."
|
||
if [ "$INCLUDE_DEPS" = false ]; then
|
||
docker-compose up -d api || {
|
||
log_error "API 服务启动失败"
|
||
exit 1
|
||
}
|
||
else
|
||
docker-compose up -d || {
|
||
log_error "服务启动失败"
|
||
exit 1
|
||
}
|
||
fi
|
||
log_info "服务已启动"
|
||
echo ""
|
||
|
||
# 5. 等待服务就绪
|
||
if ! wait_for_service "http://localhost:3001/health" "API"; then
|
||
log_error "API 服务未就绪"
|
||
docker-compose logs api --tail=50
|
||
exit 1
|
||
fi
|
||
echo ""
|
||
|
||
# 6. 数据库迁移和 seed
|
||
if [ "$SKIP_MIGRATE" = false ]; then
|
||
log_step "6/5 运行数据库迁移..."
|
||
if docker-compose exec -T api bunx prisma migrate deploy 2>/dev/null; then
|
||
log_info "数据库迁移完成"
|
||
else
|
||
log_warn "迁移失败或无需迁移"
|
||
fi
|
||
echo ""
|
||
fi
|
||
|
||
if [ "$SKIP_SEED" = false ] && [ "$SKIP_MIGRATE" = false ]; then
|
||
log_step "7/5 运行数据库种子..."
|
||
if docker-compose exec -T api bunx prisma db seed 2>/dev/null; then
|
||
log_info "数据库种子完成"
|
||
else
|
||
log_warn "种子执行失败或数据已存在"
|
||
fi
|
||
echo ""
|
||
fi
|
||
|
||
# 7. 验证部署
|
||
if ! check_deployment_success; then
|
||
log_error "部署验证失败"
|
||
echo ""
|
||
log_error "========== 部署失败 =========="
|
||
log_error "请检查以下内容:"
|
||
log_error " 1. 查看日志: docker-compose logs -f api"
|
||
log_error " 2. 检查配置: cat .env"
|
||
log_error " 3. 验证依赖: docker-compose ps"
|
||
log_error " 4. 手动健康检查: curl http://localhost:3001/health"
|
||
exit 1
|
||
fi
|
||
|
||
# 8. 显示部署信息
|
||
echo ""
|
||
log_step "部署完成!"
|
||
echo ""
|
||
echo "=========================================="
|
||
echo "服务状态:"
|
||
echo "=========================================="
|
||
docker-compose ps
|
||
echo ""
|
||
echo "=========================================="
|
||
echo "访问地址:"
|
||
echo "=========================================="
|
||
echo "API: http://localhost:3001"
|
||
echo "健康检查: http://localhost:3001/health"
|
||
echo "Swagger: http://localhost:3001/api-docs (开发环境)"
|
||
if [ "$INIT_CHECK" = true ]; then
|
||
echo "初始化状态: http://localhost:3001/init/status (需要管理员 Token)"
|
||
fi
|
||
echo ""
|
||
echo "=========================================="
|
||
echo "常用命令:"
|
||
echo "=========================================="
|
||
echo "查看日志: docker-compose logs -f api"
|
||
echo "重启服务: docker-compose restart"
|
||
echo "停止服务: docker-compose down"
|
||
echo "备份数据库: ./scripts/backup.sh"
|
||
echo "健康检查: ./scripts/healthcheck.sh"
|
||
if [ "$INIT_CHECK" = true ]; then
|
||
echo "初始化检查: ./scripts/healthcheck.sh --admin-token <token>"
|
||
fi
|
||
echo ""
|
||
|
||
if [ -f ".env" ]; then
|
||
log_warn "请确保 .env 文件中的敏感配置已正确设置!"
|
||
fi
|
||
}
|
||
|
||
main
|