- 文件上传验证: ModelUploader组件增强、上传API错误处理 - GLTF解析器: 增强节点提取和元数据 - 板材编辑: PanelEditor客户端验证 + panels API服务端验证 - 装配步骤编辑: 修复未使用代码,添加预览按钮 - 装配动画播放: AssemblyPlayer步骤导航边界检查优化 - 分享链接: 完整CRUD API(创建、获取、删除),含验证和过期检查 跳过可选测试任务以加快MVP开发
260 lines
6.1 KiB
Bash
260 lines
6.1 KiB
Bash
#!/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
|