Files
asd-backend/deploy.sh
milky0217 2ce97243ab
Some checks failed
Deploy Backend / deploy (push) Has been cancelled
fix: 全量代码审计修复 — 30项
P0 - Panic 风险修复:
  - payment.rs: unwrap() → let-else safe handling
  - payment.rs: get_jwt_secret() expect → Result/AppError
  - auth.rs: openid 切片添加 len >= 8 守卫
  - main.rs: 启动时 expect → unwrap_or_else 描述性 panic
  - main.rs: Directive::from_str 添加 fallback

P1 - 逻辑/安全修复:
  - payment.rs: urlencoding() + 解码 bug 修复 (移除 had_escape)
  - payment.rs: Mock 支付添加 check_mock_payment_allowed 检查
  - db.rs: 永久会员 NULL → 2099-12-31 一致化
  - user.rs: 维护模式添加安全说明注释
  - 自动清理 unused_variables 警告 (_is_mobile)

P2 - 错误吞没修复:
  - main.rs: 3 处定时任务 let _ = → if let Err = tracing::error!
  - db.rs + admin.rs: 7 处通知/审计日志 let _ = → tracing::warn!
  - auth.rs: refresh token 保存 add warn 日志

P3 - 死代码清理:
  - models.rs: 移除 TokenResponse (dead)
  - models.rs: 移除 AppState 中 5 个未使用字段 (env var 直接读取)
  - error.rs: 移除 3 个 dead ErrorResponse 方法
  - rate_limiter.rs: extract_client_ip_from_header → #[cfg(test)]
  - models.rs: 注释 typo fix (user_ytpe → user_type)
  - db.rs: RefreshToken 添加 deserialization 注释

Shell 脚本修复:
  - deploy.sh: run_migrations 移到 restart_service 之前
  - test.sh: 移除 EXIT trap 覆盖; heredoc 引号修复; 维护模式添加 restart
  - common.sh: mock_key 添加 sed 转义 (防 / & 注入)

验证: cargo check 0 warnings, 8 tests passed
2026-07-23 12:40:47 +08:00

212 lines
7.8 KiB
Bash
Executable File
Raw Permalink 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
# ===========================================
# Rust Backend Deployment Script
# ===========================================
# Usage: ./deploy.sh [development|production] [options]
#
# Options:
# --dry-run 预览模式
# --yes, -y 跳过确认
# --skip-tests 跳过部署后测试
# --rollback 回滚到指定备份
# --backup-list 列出可用备份
# --logs [N] 查看后端日志默认50行
# --tail [N] 实时跟踪日志
# --status 查看服务状态
# --post-logs 部署后显示日志
# --init-env 初始化 systemd service 模板
# --deploy-service 上传 systemd service 文件到服务器
# --target blue|green 蓝绿部署目标(默认单实例)
# --remote-host IP 部署目标服务器(默认 1panel-server
# ===========================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/lib/common.sh"
source "${SCRIPT_DIR}/lib/test.sh"
# ---------- 默认值 ----------
PROJECT_NAME="rust-backend"
REMOTE_USER="root"
REMOTE_HOST="aliyun-server"
APP_ENV="production"
DRY_RUN=false; SKIP_CONFIRM=false; SKIP_TESTS=false
ROLLBACK=false; LIST_BACKUPS=false; VIEW_LOGS=false; TAIL_LOGS=false
LOG_LINES=50; VIEW_STATUS=false; POST_DEPLOY_LOGS=false; INIT_ENV=false
DEPLOY_SERVICE=false; BACKUP_PATH=""
# ---------- 选项解析 ----------
BG_TARGET="" # blue 或 green仅 production 有效)
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=true ;;
--yes|-y) SKIP_CONFIRM=true ;;
--skip-tests) SKIP_TESTS=true ;;
--rollback) ROLLBACK=true ;;
--backup-list) LIST_BACKUPS=true ;;
--logs) VIEW_LOGS=true ;;
--tail) TAIL_LOGS=true ;;
--logs=*) VIEW_LOGS=true; LOG_LINES="${1#*=}" ;;
--tail=*) TAIL_LOGS=true; LOG_LINES="${1#*=}" ;;
--status) VIEW_STATUS=true ;;
--post-logs) POST_DEPLOY_LOGS=true ;;
--init-env) INIT_ENV=true ;;
--deploy-service) DEPLOY_SERVICE=true ;;
--target) BG_TARGET="${2:-}"; shift ;;
--remote-host) REMOTE_HOST="${2:-}"; shift ;;
--help|-h)
echo "用法: $0 [development|production] [options]"
echo ""
echo "选项:"
echo " --dry-run 预览模式"
echo " --yes, -y 跳过确认"
echo " --skip-tests 跳过部署后测试"
echo " --target blue|green 蓝绿部署目标"
echo " --remote-host IP 部署目标服务器(默认 aliyun-server"
echo " --rollback 回滚到指定备份"
echo " --backup-list 列出可用备份"
echo " --logs [N] 查看后端日志默认50行"
echo " --tail [N] 实时跟踪日志"
echo " --status 查看服务状态"
echo " --post-logs 部署后显示日志"
echo " --init-env 初始化 systemd service 模板"
echo " --deploy-service 上传 systemd service 文件到服务器"
exit 0 ;;
*)
if [[ "$1" == "development" || "$1" == "production" ]]; then
APP_ENV="$1"
else
echo "未知选项: $1"; exit 1
fi ;;
esac; shift
done
# 提前建立 SSH 连接(蓝绿检测需要 remote()
ssh_connect
# ---------- 蓝绿目标自动检测 + 配置 ----------
# 优先级: 手动 --target > 自动检测
detect_blue_green() {
local proxy_conf; proxy_conf=$(get_proxy_conf)
local active
active=$(remote "sudo cat ${proxy_conf} 2>/dev/null" 2>/dev/null | grep -oP '127\.0\.0\.1:\d+' | head -1 || echo "")
case "${APP_ENV}:${active}" in
*:4433) echo "blue" ;;
*:4434) echo "green" ;;
*:8083) echo "blue" ;;
*:8084) echo "green" ;;
*) echo "" ;;
esac
}
if [ -z "$BG_TARGET" ] && [ "${APP_ENV}" = "production" ]; then
# 自动检测:部署到待命环境(仅生产环境支持蓝绿)
active_target=$(detect_blue_green)
if [ -n "$active_target" ]; then
BG_TARGET=$([ "$active_target" = "blue" ] && echo "green" || echo "blue")
log_info "自动检测: 当前活动 ${active_target},部署到 ${BG_TARGET}"
fi
fi
if [ -n "$BG_TARGET" ]; then
case "${APP_ENV}:${BG_TARGET}" in
production:blue) BG_PORT="4433"; BG_DIR="rust_backend_blue"; BG_SVC="rust-backend-blue.service" ;;
production:green) BG_PORT="4434"; BG_DIR="rust_backend_green"; BG_SVC="rust-backend-green.service" ;;
development:blue) BG_PORT="8083"; BG_DIR="rust_backend_dev_blue"; BG_SVC="rust-backend-dev-blue.service" ;;
development:green)BG_PORT="8084"; BG_DIR="rust_backend_dev_green";BG_SVC="rust-backend-dev-green.service" ;;
*) echo "错误: --target 只能是 blue 或 green"; exit 1 ;;
esac
fi
# ---------- 环境配置 ----------
case "${APP_ENV}" in
development)
if [ -n "$BG_TARGET" ]; then
REMOTE_DIR="/root/rust/${BG_DIR}"
SERVICE_NAME="${BG_SVC}"
BACKEND_PORT="${BG_PORT}"
else
REMOTE_DIR="/root/rust/rust_backend_dev"
SERVICE_NAME="rust-backend-dev.service"
BACKEND_PORT="8080"
fi
TEST_DOMAIN="https://dev.xmclassmate.top"
DB_CONTAINER="postgres"
DB_NAME="milkydata_dev"
DB_USER="milkydata" ;;
production)
if [ -n "$BG_TARGET" ]; then
REMOTE_DIR="/root/rust/${BG_DIR}"
SERVICE_NAME="${BG_SVC}"
BACKEND_PORT="${BG_PORT}"
else
REMOTE_DIR="/root/rust/rust_backend"
SERVICE_NAME="rust-backend.service"
BACKEND_PORT="4433"
fi
TEST_DOMAIN="https://xmclassmate.top"
DB_CONTAINER="postgres"
DB_NAME="milkydata"
DB_USER="milkydata" ;;
esac
# ---------- 快捷命令 ----------
GIT_VERSION=$(get_git_info)
if [ "$LIST_BACKUPS" = true ]; then list_backups; exit 0; fi
if [ "$INIT_ENV" = true ]; then init_env; exit 0; fi
if [ "$DEPLOY_SERVICE" = true ]; then deploy_service_file; exit 0; fi
if [ "$VIEW_LOGS" = true ]; then view_logs; exit 0; fi
if [ "$TAIL_LOGS" = true ]; then tail_logs; exit 0; fi
if [ "$VIEW_STATUS" = true ]; then status; exit 0; fi
if [ "$ROLLBACK" = true ]; then rollback; exit 0; fi
# ---------- 主部署流程 ----------
show_deploy_info
confirm_deploy
check_not_active
check_dependencies
check_service_file
check_ssl_expiry
build_project
backup_old_version
cleanup_old_backups 5
upload_binary
upload_dir "迁移文件" migrations
upload_script "测试脚本" test_deployment.sh test_deployment.sh
upload_script "清理脚本" scripts/cleanup_refresh_tokens.sh scripts/cleanup_refresh_tokens.sh
upload_script "备份脚本" scripts/backup-db.sh scripts/backup-db.sh
deploy_service_file
run_migrations # Moved BEFORE restart
restart_service
if run_tests; then
log_to_file "DEPLOY success"
send_webhook "success" "部署完成"
echo ""; log_info "========== 部署完成!=========="
echo -e " Git: ${GIT_VERSION}"; echo -e " 环境: ${APP_ENV}"
if [ -n "$BG_TARGET" ]; then
echo -e " 目标: ${BG_TARGET} (:${BG_PORT})"
echo -e ""
echo -e " ${YELLOW}自动切换代理...${NC}"
if bash "${SCRIPT_DIR}/scripts/switch-env.sh" --switch "${BG_TARGET}" 2>&1; then
log_info "代理已切换至 ${BG_TARGET} (:${BG_PORT})"
else
log_warn "代理切换失败,请手动执行: ./scripts/switch-env.sh --switch ${BG_TARGET}"
fi
fi
echo ""
[ "$POST_DEPLOY_LOGS" = true ] && { echo ""; view_logs; }
else
log_error "========== 部署失败,已自动回滚 =========="
exit 1
fi
exit 0