WuYongJie 75db836d29 feat(3D核心功能): 完成所有必需实现任务
- 文件上传验证: ModelUploader组件增强、上传API错误处理
- GLTF解析器: 增强节点提取和元数据
- 板材编辑: PanelEditor客户端验证 + panels API服务端验证
- 装配步骤编辑: 修复未使用代码,添加预览按钮
- 装配动画播放: AssemblyPlayer步骤导航边界检查优化
- 分享链接: 完整CRUD API(创建、获取、删除),含验证和过期检查

跳过可选测试任务以加快MVP开发
2025-12-22 16:18:13 +08:00

260 lines
6.1 KiB
Bash
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.

#!/bin/bash
# ============================================
# 数据库备份脚本
# ============================================
# 功能:备份 PostgreSQL 数据库
# 使用:./backup-db.sh [备份目录]
# ============================================
set -e # 遇到错误立即退出
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 配置
BACKUP_DIR="${1:-./backups/database}"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="db-backup-${TIMESTAMP}.sql.gz"
LOG_FILE="backup-db-${TIMESTAMP}.log"
# 从环境变量读取数据库连接信息
if [ -f .env ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
DATABASE_URL="${DATABASE_URL:-}"
# 日志函数
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "${BACKUP_DIR}/${LOG_FILE}"
}
error() {
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1" | tee -a "${BACKUP_DIR}/${LOG_FILE}"
}
warn() {
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1" | tee -a "${BACKUP_DIR}/${LOG_FILE}"
}
# 检查依赖
check_dependencies() {
log "检查依赖..."
if ! command -v pg_dump &> /dev/null; then
error "pg_dump 未安装,请安装 PostgreSQL 客户端工具"
exit 1
fi
if ! command -v gzip &> /dev/null; then
error "gzip 未安装"
exit 1
fi
log "✅ 依赖检查通过"
}
# 验证数据库连接
check_database_connection() {
log "验证数据库连接..."
if [ -z "$DATABASE_URL" ]; then
error "DATABASE_URL 环境变量未设置"
exit 1
fi
# 提取数据库连接信息
if [[ $DATABASE_URL =~ postgresql://([^:]+):([^@]+)@([^:]+):([^/]+)/(.+) ]]; then
DB_USER="${BASH_REMATCH[1]}"
DB_PASS="${BASH_REMATCH[2]}"
DB_HOST="${BASH_REMATCH[3]}"
DB_PORT="${BASH_REMATCH[4]}"
DB_NAME="${BASH_REMATCH[5]}"
else
error "DATABASE_URL 格式无效"
exit 1
fi
# 测试连接
export PGPASSWORD="$DB_PASS"
if ! psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "SELECT 1" &> /dev/null; then
error "无法连接到数据库"
exit 1
fi
log "✅ 数据库连接成功"
}
# 创建备份目录
create_backup_directory() {
log "创建备份目录: ${BACKUP_DIR}"
mkdir -p "${BACKUP_DIR}"
if [ ! -d "${BACKUP_DIR}" ]; then
error "无法创建备份目录"
exit 1
fi
log "✅ 备份目录已创建"
}
# 检查磁盘空间
check_disk_space() {
log "检查磁盘空间..."
# 获取可用空间(MB)
AVAILABLE_SPACE=$(df -m "${BACKUP_DIR}" | awk 'NR==2 {print $4}')
# 估算数据库大小(MB)
DB_SIZE=$(psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT pg_database_size('$DB_NAME') / 1024 / 1024" | xargs)
DB_SIZE=${DB_SIZE%.*} # 取整
# 需要至少 2 倍的数据库大小空间
REQUIRED_SPACE=$((DB_SIZE * 2))
log "数据库大小: ${DB_SIZE} MB"
log "可用空间: ${AVAILABLE_SPACE} MB"
log "需要空间: ${REQUIRED_SPACE} MB"
if [ "$AVAILABLE_SPACE" -lt "$REQUIRED_SPACE" ]; then
error "磁盘空间不足"
exit 1
fi
log "✅ 磁盘空间充足"
}
# 执行备份
perform_backup() {
log "开始备份数据库..."
log "备份文件: ${BACKUP_FILE}"
START_TIME=$(date +%s)
# 执行备份并压缩
if pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \
--format=plain \
--no-owner \
--no-acl \
--clean \
--if-exists \
| gzip > "${BACKUP_DIR}/${BACKUP_FILE}"; then
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
log "✅ 备份完成,耗时: ${DURATION} 秒"
else
error "备份失败"
exit 1
fi
}
# 验证备份文件
verify_backup() {
log "验证备份文件..."
if [ ! -f "${BACKUP_DIR}/${BACKUP_FILE}" ]; then
error "备份文件不存在"
exit 1
fi
# 检查文件大小
FILE_SIZE=$(du -h "${BACKUP_DIR}/${BACKUP_FILE}" | cut -f1)
log "备份文件大小: ${FILE_SIZE}"
# 验证 gzip 文件完整性
if gzip -t "${BACKUP_DIR}/${BACKUP_FILE}" 2>/dev/null; then
log "✅ 备份文件完整性验证通过"
else
error "备份文件损坏"
exit 1
fi
# 生成 checksum
CHECKSUM=$(sha256sum "${BACKUP_DIR}/${BACKUP_FILE}" | cut -d' ' -f1)
echo "$CHECKSUM" > "${BACKUP_DIR}/${BACKUP_FILE}.sha256"
log "✅ Checksum: ${CHECKSUM}"
}
# 清理旧备份
cleanup_old_backups() {
log "清理旧备份..."
# 保留最近 7 个备份
KEEP_COUNT=7
# 获取备份文件列表(按时间排序)
BACKUP_FILES=($(ls -t "${BACKUP_DIR}"/db-backup-*.sql.gz 2>/dev/null || true))
BACKUP_COUNT=${#BACKUP_FILES[@]}
if [ "$BACKUP_COUNT" -gt "$KEEP_COUNT" ]; then
DELETE_COUNT=$((BACKUP_COUNT - KEEP_COUNT))
log "发现 ${BACKUP_COUNT} 个备份,删除最旧的 ${DELETE_COUNT} 个"
for ((i=KEEP_COUNT; i<BACKUP_COUNT; i++)); do
OLD_BACKUP="${BACKUP_FILES[$i]}"
log "删除: $(basename $OLD_BACKUP)"
rm -f "$OLD_BACKUP"
rm -f "${OLD_BACKUP}.sha256"
done
log "✅ 清理完成"
else
log "当前有 ${BACKUP_COUNT} 个备份,无需清理"
fi
}
# 生成备份摘要
generate_summary() {
log "生成备份摘要..."
cat > "${BACKUP_DIR}/backup-summary-${TIMESTAMP}.json" << EOF
{
"timestamp": "$(date -Iseconds)",
"backup_file": "${BACKUP_FILE}",
"database": "${DB_NAME}",
"host": "${DB_HOST}",
"size": "$(du -b "${BACKUP_DIR}/${BACKUP_FILE}" | cut -f1)",
"checksum": "$(cat "${BACKUP_DIR}/${BACKUP_FILE}.sha256")",
"status": "success"
}
EOF
log "✅ 备份摘要已生成"
}
# 主函数
main() {
echo "========================================"
echo " 数据库备份脚本"
echo "========================================"
echo ""
check_dependencies
check_database_connection
create_backup_directory
check_disk_space
perform_backup
verify_backup
cleanup_old_backups
generate_summary
echo ""
echo "========================================"
log "🎉 备份成功完成!"
echo "========================================"
echo ""
echo "备份文件: ${BACKUP_DIR}/${BACKUP_FILE}"
echo "日志文件: ${BACKUP_DIR}/${LOG_FILE}"
echo ""
}
# 执行主函数
main