laobinghu 37742571ae feat: 完成项目初始化并重构部署流程
主要变更:
- 添加完整的项目结构和模块(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 密钥对支持
- 添加环境变量验证和数据库备份恢复功能
2026-03-28 16:53:25 +08:00

166 lines
4.1 KiB
Bash

#!/bin/bash
# ============================================
# LinkShare Blog - 数据库恢复脚本
# ============================================
# 用途: 从备份恢复 PostgreSQL 数据库
# 用法: ./scripts/restore.sh <backup_file> [options]
# 示例: ./scripts/restore.sh daily_20260328.sql.gz
set -e
# 配置
DB_CONTAINER="blog-postgres"
DB_NAME="linkshare"
DB_USER="blog"
# 颜色定义
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
# 显示用法
show_usage() {
echo "用法: $0 <backup_file> [options]"
echo ""
echo "参数:"
echo " backup_file 备份文件路径 (必需)"
echo ""
echo "选项:"
echo " --drop 恢复前删除现有数据库 (危险!)"
echo " --dry-run 仅验证备份文件,不执行恢复"
echo " --help 显示此帮助信息"
echo ""
echo "示例:"
echo " $0 daily_20260328.sql.gz # 恢复备份"
echo " $0 daily_20260328.sql.gz --drop # 删除并恢复"
echo " $0 daily_20260328.sql.gz --dry-run # 验证备份"
echo ""
echo "可用备份:"
ls -1 backups/*.sql.gz 2>/dev/null || echo " (无备份文件)"
}
# 检查参数
if [ $# -lt 1 ] || [ "$1" = "--help" ]; then
show_usage
exit 1
fi
BACKUP_FILE=$1
shift
# 解析选项
DROP_DB=false
DRY_RUN=false
while [[ $# -gt 0 ]]; do
case $1 in
--drop)
DROP_DB=true
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
*)
log_error "未知选项: $1"
show_usage
exit 1
;;
esac
done
# 检查备份文件
if [ ! -f "$BACKUP_FILE" ]; then
log_error "备份文件不存在: $BACKUP_FILE"
exit 1
fi
# 验证备份文件
log_info "验证备份文件..."
if gzip -t "$BACKUP_FILE" 2>/dev/null; then
log_info "✓ 备份文件完整性检查通过"
else
log_error "备份文件损坏或不是有效的 gzip 文件"
exit 1
fi
# dry-run 模式
if [ "$DRY_RUN" = true ]; then
log_info "Dry-run 模式: 不执行恢复"
log_info "备份信息:"
ls -lh "$BACKUP_FILE"
gzip -dc "$BACKUP_FILE" | head -n 20
log_info "备份文件前20行预览完成"
exit 0
fi
# 检查数据库容器
if ! docker ps | grep -q "$DB_CONTAINER"; then
log_error "数据库容器 '$DB_CONTAINER' 未运行"
exit 1
fi
# 警告确认
echo ""
log_warn "即将执行数据库恢复!"
log_warn "备份文件: $BACKUP_FILE"
log_warn "数据库: $DB_NAME"
if [ "$DROP_DB" = true ]; then
log_warn "警告: 将删除现有数据库!"
fi
echo ""
read -p "确认继续? (yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
log_info "操作已取消"
exit 0
fi
# 执行恢复
log_info "开始恢复数据库..."
if [ "$DROP_DB" = true ]; then
log_info "删除现有数据库..."
docker exec "$DB_CONTAINER" psql -U "$DB_USER" -c "DROP DATABASE IF EXISTS $DB_NAME;"
docker exec "$DB_CONTAINER" psql -U "$DB_USER" -c "CREATE DATABASE $DB_NAME;"
log_info "数据库已重建"
fi
# 恢复数据
if gzip -dc "$BACKUP_FILE" | docker exec -i "$DB_CONTAINER" psql -U "$DB_USER" "$DB_NAME"; then
log_info "✓ 数据库恢复成功!"
# 验证恢复
log_info "验证恢复结果..."
TABLE_COUNT=$(gzip -dc "$BACKUP_FILE" | grep -c "^CREATE TABLE" || echo "0")
log_info "备份中发现的表数量: $TABLE_COUNT"
ACTUAL_COUNT=$(docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public';" 2>/dev/null | tr -d '[:space:]')
log_info "恢复后的表数量: $ACTUAL_COUNT"
if [ "$TABLE_COUNT" -eq "$ACTUAL_COUNT" ] || [ "$ACTUAL_COUNT" -gt 0 ]; then
log_info "✓ 验证通过"
exit 0
else
log_warn "表数量不匹配,请手动验证数据完整性"
exit 0
fi
else
log_error "数据库恢复失败"
exit 1
fi