#!/bin/bash set -eo pipefail SCRIPT_NAME=$(basename "$0") START_TIME=$(date +%s) MAX_DB_RETRIES=10 DB_RETRY_INTERVAL=5 TEMP_FILES=() cleanup() { log_info "正在清理资源..." for temp_file in "${TEMP_FILES[@]}"; do if [[ -f "$temp_file" ]]; then rm -f "$temp_file" log_debug "已删除临时文件: $temp_file" fi done log_info "清理完成" } handle_exit() { log_info "接收到退出信号,正在退出..." cleanup exit 0 } trap handle_exit SIGTERM SIGINT trap cleanup EXIT LOG_LEVEL_DEBUG=0 LOG_LEVEL_INFO=1 LOG_LEVEL_WARN=2 LOG_LEVEL_ERROR=3 CURRENT_LOG_LEVEL=${LOG_LEVEL_INFO} if [[ "$LOG_LEVEL" == "debug" ]]; then CURRENT_LOG_LEVEL=${LOG_LEVEL_DEBUG} elif [[ "$LOG_LEVEL" == "info" ]]; then CURRENT_LOG_LEVEL=${LOG_LEVEL_INFO} elif [[ "$LOG_LEVEL" == "warn" ]]; then CURRENT_LOG_LEVEL=${LOG_LEVEL_WARN} elif [[ "$LOG_LEVEL" == "error" ]]; then CURRENT_LOG_LEVEL=${LOG_LEVEL_ERROR} fi log_debug() { if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_DEBUG} ]]; then echo "🔍 [$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG] $*" fi } log_info() { if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_INFO} ]]; then echo "ℹ️ [$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*" fi } log_warn() { if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_WARN} ]]; then echo "⚠️ [$(date '+%Y-%m-%d %H:%M:%S')] [WARN] $*" fi } log_error() { if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_ERROR} ]]; then echo "❌ [$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" fi } log_success() { if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_INFO} ]]; then echo "✅ [$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS] $*" fi } # 创建安全的临时文件 create_temp_file() { local temp_file temp_file=$(mktemp) TEMP_FILES+=("$temp_file") echo "$temp_file" } safe_db_url() { # 直接返回固定的数据库路径 echo "sqlite://data/upage.db" } # 设置数据库信息 extract_db_info() { DB_FILE="data/upage.db" return 0 } # 检查数据库连接 check_db_connection() { log_info "检查 SQLite 数据库..." if ! extract_db_info; then return 1 fi log_info "SQLite 数据库文件路径: $DB_FILE" # 如果数据库文件已存在,检查是否可读写 if [[ -f "$DB_FILE" && ! -w "$DB_FILE" ]]; then log_error "SQLite 数据库文件存在但不可写: $DB_FILE" return 1 fi # 验证 Prisma 配置 local output_file output_file=$(create_temp_file) if pnpm prisma validate --schema=./prisma/schema.prisma > "$output_file" 2>&1; then log_success "Prisma 配置验证成功" else log_warn "Prisma 配置验证警告,但将继续执行:" cat "$output_file" fi log_success "SQLite 数据库检查通过" return 0 } # 等待数据库就绪 wait_for_db() { log_info "准备 SQLite 数据库..." if check_db_connection; then log_success "SQLite 数据库就绪" return 0 else log_error "SQLite 数据库检查失败" return 1 fi } # 处理数据库迁移 handle_db_migration() { log_info "处理数据库迁移..." # 1. 尝试直接应用迁移 local migrate_output migrate_output=$(create_temp_file) log_info "尝试应用数据库迁移..." if pnpm prisma migrate deploy --schema=./prisma/schema.prisma > "$migrate_output" 2>&1; then log_success "数据库迁移成功应用" return 0 fi if grep -q "P3005" "$migrate_output" || grep -q "数据库架构不为空" "$migrate_output"; then log_warn "检测到已存在的数据库结构,需要应用 baseline..." local migration_dirs migration_dirs=$(find prisma/migrations -maxdepth 1 -type d | grep -v "^prisma/migrations$" | sort) if [[ -z "$migration_dirs" ]]; then log_warn "未找到迁移,跳过 baseline 处理" else log_info "找到以下迁移:" for migration_dir in $migration_dirs; do local migration_name migration_name=$(basename "$migration_dir") log_info " - $migration_name" log_info "将迁移 $migration_name 标记为已应用..." local resolve_output resolve_output=$(create_temp_file) if ! pnpm prisma migrate resolve --applied "$migration_name" > "$resolve_output" 2>&1; then log_warn "标记迁移 $migration_name 时出错:" cat "$resolve_output" fi done log_success "Baseline 应用成功" log_info "检查并应用新的迁移..." local deploy_output deploy_output=$(create_temp_file) if ! pnpm prisma migrate deploy > "$deploy_output" 2>&1; then log_warn "应用新迁移时出错:" cat "$deploy_output" log_info "尝试使用 db push 作为备选方案..." local push_output push_output=$(create_temp_file) if ! pnpm prisma db push --accept-data-loss --skip-generate > "$push_output" 2>&1; then log_error "使用 db push 也失败了:" cat "$push_output" return 1 else log_success "使用 db push 成功应用架构" fi else log_success "新迁移应用成功" fi fi else log_warn "迁移失败,错误信息:" cat "$migrate_output" log_info "尝试使用 db push 作为备选方案..." local push_output push_output=$(create_temp_file) if ! pnpm prisma db push --accept-data-loss --skip-generate > "$push_output" 2>&1; then log_error "使用 db push 也失败了:" cat "$push_output" return 1 else log_success "使用 db push 成功应用架构" fi fi return 0 } main() { log_info "🚀 启动 UPage 应用..." log_info "系统环境信息:" log_info " - NODE_ENV: $NODE_ENV" log_info " - 当前用户: $(whoami)" log_info " - 工作目录: $(pwd)" if extract_db_info; then log_info "SQLite 数据库信息:" log_info " - 数据库文件: $DB_FILE" fi if ! wait_for_db; then log_error "数据库连接失败,退出启动流程" exit 1 fi if ! handle_db_migration; then log_error "数据库迁移失败,退出启动流程" exit 1 fi local end_time end_time=$(date +%s) local duration=$((end_time - START_TIME)) log_success "初始化完成,耗时 ${duration} 秒" log_info "启动应用服务..." log_info "执行命令: $*" exec "$@" } main "$@"