#!/bin/bash # scripts/expire-paid-user.sh # 通过 SSH 在服务器上执行 SQL,使用户的付费时间过期(仅修改 paid_expires_at) # 与 set-paid-user.sh --expires 的区别:此脚本专用于"过期"场景, # 保持 is_paid=true(用户仍标记为付费),仅将 paid_expires_at 设到过去。 # # 使用 config/development.toml 中的数据库配置 # 密码由服务器的 1panel/环境变量提供,无需手动传入 # # 用法: # ./scripts/expire-paid-user.sh # ./scripts/expire-paid-user.sh --date YYYY-MM-DD # # 示例: # ./scripts/expire-paid-user.sh 1 # 设为昨天到期(立即过期) # ./scripts/expire-paid-user.sh 1 --date 2025-01-01 # 设为指定日期到期(已过期) SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" CONFIG_DIR="$PROJECT_ROOT/config" ENV_FILE="$PROJECT_ROOT/.env" get_ssh_host() { grep '^SSH_SERVER=' "$ENV_FILE" 2>/dev/null | head -1 | cut -d'=' -f2- | tr -d '"' | tr -d "'" | xargs } usage() { echo "用法: $0 [--date YYYY-MM-DD]" echo "" echo " user_id 用户 ID(必填)" echo " --date DATE 指定过期日期(默认: 昨天)" echo "" echo "说明:" echo " 将用户 paid_expires_at 设到过去日期(is_paid 保持 true)" echo " 用户的 is_paid_active 会变为 false" echo "" echo "数据源:" echo " SSH 服务器: .env 中 SSH_SERVER" echo " 数据库配置: config/development.toml (host/port/dbname)" echo " 密码: 服务器上的环境变量/1panel 自动提供" echo "" echo "示例:" echo " $0 1 # 设为昨天到期" echo " $0 1 --date 2025-01-01 # 设为 2025-01-01" exit 0 } validate_date() { local date="$1" if [[ ! "$date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then echo "错误: 日期格式无效,请使用 YYYY-MM-DD" exit 1 fi } # 解析 toml 文件中的 database_url parse_toml_db_url() { local file="$1" grep '^database_url' "$file" 2>/dev/null | head -1 | sed 's/^database_url *= *["'"'"']//' | sed 's/["'"'"']*$//' | tr -d ' ' } # 解析 postgres:// URL parse_db_url() { local db_url="$1" local rest="${db_url#postgres://}" DB_USER="${rest%%:*}" local host_port="${rest##*@}" host_port="${host_port%%/*}" if [[ "$host_port" == *:* ]]; then DB_HOST="${host_port%%:*}" DB_PORT="${host_port##*:}" else DB_HOST="$host_port" DB_PORT="5432" fi DB_NAME="${rest##*/}" DB_NAME="${DB_NAME%%\?*}" } info() { echo "[INFO] $1"; } warn() { echo "[WARN] $1"; } error() { echo "[ERROR] $1" >&2; } step() { echo ""; echo ">>> 第 $1 步: $2"; echo ""; } # 在服务器上通过 docker exec 执行 SQL exec_sql() { local sql="$1" ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=5 "$SSH_HOST" \ "cd /root/rust/rust_backend_dev 2>/dev/null || true; docker exec 1Panel-postgresql-FtMo psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql}\"" 2>&1 } main() { if [[ $# -lt 1 ]]; then usage fi USER_ID="$1" shift if [[ ! "$USER_ID" =~ ^[0-9]+$ ]]; then error "user_id 必须是数字" exit 1 fi EXPIRES_DATE="" while [[ $# -gt 0 ]]; do case "$1" in --date) EXPIRES_DATE="$2" validate_date "$EXPIRES_DATE" shift 2 ;; -h|--help) usage ;; *) error "未知参数: $1"; usage ;; esac done # 默认过期日期:昨天 if [[ -z "$EXPIRES_DATE" ]]; then if date --version 2>/dev/null | grep -q GNU; then EXPIRES_DATE=$(date -d "yesterday" "+%Y-%m-%d") else EXPIRES_DATE=$(date -v -1d "+%Y-%m-%d") fi fi # ===== Step 1: 读取配置 ===== step 1 "读取配置" SSH_HOST="$(get_ssh_host)" if [[ -z "$SSH_HOST" ]]; then error "未找到 SSH_SERVER 配置,请检查 .env 文件" exit 1 fi info "SSH 服务器: $SSH_HOST" CONFIG_FILE="$CONFIG_DIR/development.toml" if [[ ! -f "$CONFIG_FILE" ]]; then error "配置文件不存在: $CONFIG_FILE" exit 1 fi TOML_DB_URL="$(parse_toml_db_url "$CONFIG_FILE")" if [[ -z "$TOML_DB_URL" ]]; then error "development.toml 中未找到 database_url" exit 1 fi parse_db_url "$TOML_DB_URL" info "数据库: ${DB_USER}@${DB_HOST}:${DB_PORT}/${DB_NAME}" # ===== Step 2: 构造 SQL ===== step 2 "构造 SQL 语句" SQL="UPDATE users SET is_paid = true, paid_expires_at = '${EXPIRES_DATE}'::date WHERE id = ${USER_ID};" info "设置过期日期: $EXPIRES_DATE" echo " SQL: $SQL" # ===== Step 3: 执行 SQL ===== step 3 "通过 SSH 在服务器上执行 SQL" info "连接 $SSH_HOST (超时 15s) ..." SQL_RAW=$(exec_sql "$SQL") SQL_EXIT=$? if [[ $SQL_EXIT -ne 0 ]]; then error "执行失败(退出码: $SQL_EXIT)" echo "$SQL_RAW" | sed 's/^/ /' >&2 exit 1 fi echo "$SQL_RAW" if echo "$SQL_RAW" | grep -q "UPDATE [01]"; then ROWS=$(echo "$SQL_RAW" | grep "UPDATE" | grep -oP '\d+' | head -1) if [[ "${ROWS:-0}" == "0" ]]; then warn "未更新任何行(用户 ID $USER_ID 可能不存在)" else info "成功更新 ${ROWS} 行" fi else warn "无法确认更新结果" fi # ===== Step 4: 验证结果 ===== step 4 "查询用户信息确认" QUERY_SQL="SELECT id, name, phone, type, is_paid, is_admin, paid_expires_at FROM users WHERE id = ${USER_ID};" info "SQL: $QUERY_SQL" QUERY_RAW=$(exec_sql "$QUERY_SQL") QUERY_EXIT=$? if [[ $QUERY_EXIT -ne 0 ]]; then warn "查询失败(退出码: $QUERY_EXIT),但数据更新已执行" echo "$QUERY_RAW" | sed 's/^/ /' else echo "$QUERY_RAW" fi # ===== 总结 ===== echo "" echo "============================================" echo " 执行完成" echo "============================================" echo " 用户 ID: $USER_ID" echo " 过期日期: $EXPIRES_DATE" echo " 服务器: $SSH_HOST" echo "============================================" echo "" echo "提示: is_paid 保持 true,is_paid_active 会变为 false(付费已过期)" } main "$@"