This commit is contained in:
2026-05-26 15:20:00 +08:00
committed by milky0217
parent 8d77a240da
commit d03acf0add
7 changed files with 856 additions and 2 deletions

55
scripts/backup-db.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
# ===========================================
# 数据库备份脚本
# 用途: 备份 PostgreSQL 数据库到本地文件
# 建议: 每天通过 cron 执行一次
# Crontab 示例:
# 0 4 * * * /root/rust/rust_backend/scripts/backup-db.sh >> /root/rust/rust_backend/logs/backup.log 2>&1
# ===========================================
set -euo pipefail
DB_CONTAINER="1Panel-postgresql-FtMo"
DB_NAME="${1:-milkydata}"
DB_USER="milkydata"
BACKUP_DIR="/root/rust/rust_backend/backups"
RETENTION_DAYS=7
LOG_PREFIX="[$(date '+%Y-%m-%d %H:%M:%S')]"
echo "$LOG_PREFIX 开始备份数据库: ${DB_NAME}"
# 确保备份目录存在
mkdir -p "$BACKUP_DIR"
# 检查容器是否运行
if ! docker ps --format '{{.Names}}' | grep -q "^${DB_CONTAINER}$"; then
echo "$LOG_PREFIX 错误: Docker 容器 ${DB_CONTAINER} 未运行"
exit 1
fi
# 执行备份(自定义格式,可压缩)
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_$(date +%Y%m%d_%H%M%S).dump"
echo "$LOG_PREFIX 导出到: ${BACKUP_FILE}"
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -Fc "$DB_NAME" > "$BACKUP_FILE"
# 检查备份是否成功
if [ -f "$BACKUP_FILE" ] && [ -s "$BACKUP_FILE" ]; then
FILE_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
echo "$LOG_PREFIX 备份成功: ${BACKUP_FILE} (${FILE_SIZE})"
else
echo "$LOG_PREFIX 错误: 备份文件为空或不存在"
exit 1
fi
# 删除旧备份(保留 RETENTION_DAYS 天)
echo "$LOG_PREFIX 清理 ${RETENTION_DAYS} 天前的旧备份..."
find "$BACKUP_DIR" -name "${DB_NAME}_*.dump" -mtime "+${RETENTION_DAYS}" -delete
# 记录保留的备份
echo "$LOG_PREFIX 当前备份列表:"
ls -lh "$BACKUP_DIR"/*.dump 2>/dev/null || echo " (无备份文件)"
echo "$LOG_PREFIX 备份完成"

View File

@@ -0,0 +1,35 @@
#!/bin/bash
# ===========================================
# 清理过期的 refresh_token
# 用途: 删除 refresh_tokens 表中已过期的记录
# 建议: 通过 cron 每天执行一次
# Crontab 示例:
# 0 3 * * * /root/rust/rust_backend/scripts/cleanup_refresh_tokens.sh >> /root/rust/rust_backend/logs/cleanup.log 2>&1
# ===========================================
set -euo pipefail
DB_CONTAINER="1Panel-postgresql-FtMo"
DB_NAME="${1:-milkydata}"
DB_USER="milkydata"
LOG_PREFIX="[$(date '+%Y-%m-%d %H:%M:%S')]"
echo "$LOG_PREFIX 开始清理过期的 refresh_token..."
# 检查表是否存在
table_exists=$(docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -tAc \
"SELECT 1 FROM information_schema.tables WHERE table_name = 'refresh_tokens'" 2>/dev/null || echo "0")
if [ "$table_exists" != "1" ]; then
echo "$LOG_PREFIX 表 refresh_tokens 不存在,跳过清理"
exit 0
fi
# 删除过期记录
deleted=$(docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -tAc \
"DELETE FROM refresh_tokens WHERE expires_at <= NOW(); SELECT COUNT(*);" 2>/dev/null || echo "0")
echo "$LOG_PREFIX 已删除 ${deleted} 条过期的 refresh_token"
echo "$LOG_PREFIX 清理完成"

141
scripts/send-notification.sh Executable file
View File

@@ -0,0 +1,141 @@
#!/bin/bash
# ===========================================
# 发送通知 - 管理员工具
# 通过 API 创建系统广播或定向通知
# ===========================================
# 用法:
# ./scripts/send-notification.sh --all --type system_maintenance --title "维护通知" --content "内容"
# ./scripts/send-notification.sh --user 1 --type payment_success --title "开通成功" --link /upgrade
#
# 选项:
# --all 发送给所有用户scope=all
# --user <id> 发送给指定用户scope=user
# --type <type> 通知类型(必填)
# --title <text> 通知标题(必填)
# --content <text> 通知内容(可选)
# --priority <lvl> 优先级: high/normal/low默认 normal
# --link <url> 点击跳转路径(可选)
# --dev 目标: milkydata_dev默认 milkydata
# --dry-run 预览不执行
# ===========================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
ENV_FILE="$PROJECT_ROOT/.env"
# ---------- 默认值 ----------
SCOPE=""
USER_ID=""
NOTIF_TYPE=""
TITLE=""
CONTENT=""
PRIORITY="normal"
LINK=""
DB_NAME="milkydata"
DRY_RUN=false
# ---------- 获取 SSH 主机 ----------
get_ssh_host() {
grep '^SSH_SERVER=' "$ENV_FILE" 2>/dev/null | head -1 | cut -d'=' -f2- | tr -d '"' | tr -d "'" | xargs
}
# ---------- 使用说明 ----------
usage() {
echo "用法: $0 (--all | --user <id>) --type <type> --title <title> [options]"
echo ""
echo "必填:"
echo " --all 发送给所有用户"
echo " --user <id> 发送给指定用户"
echo " --type <type> 通知类型: system_maintenance / version_update /"
echo " payment_success / member_expiry_soon / member_expired"
echo " --title <text> 通知标题"
echo ""
echo "可选:"
echo " --content <text> 通知内容"
echo " --priority <lvl> high / normal / low默认 normal"
echo " --link <path> 点击跳转路径(如 /pkg-extra/upgrade/upgrade"
echo " --dev 操作开发库 milkydata_dev"
echo " --dry-run 预览,不执行"
echo ""
echo "示例:"
echo " $0 --all --type system_maintenance --title '维护通知' --content '今晚维护' --priority high"
echo " $0 --user 1 --type payment_success --title '开通成功' --link /upgrade --dev"
exit 0
}
# ---------- 参数解析 ----------
while [[ $# -gt 0 ]]; do
case "$1" in
--all) SCOPE="all" ;;
--user) SCOPE="user"; USER_ID="$2"; shift ;;
--type) NOTIF_TYPE="$2"; shift ;;
--title) TITLE="$2"; shift ;;
--content) CONTENT="$2"; shift ;;
--priority) PRIORITY="$2"; shift ;;
--link) LINK="$2"; shift ;;
--dev) DB_NAME="milkydata_dev" ;;
--dry-run) DRY_RUN=true ;;
--help|-h) usage ;;
*) echo "未知选项: $1"; usage ;;
esac
shift
done
# ---------- 必填校验 ----------
[ -z "$SCOPE" ] && { echo "错误: 必须指定 --all 或 --user"; usage; }
[ "$SCOPE" = "user" ] && [ -z "$USER_ID" ] && { echo "错误: --user 必须指定用户 ID"; usage; }
[ -z "$NOTIF_TYPE" ] && { echo "错误: --type 必填"; usage; }
[ -z "$TITLE" ] && { echo "错误: --title 必填"; usage; }
# ---------- 连接信息 ----------
SSH_HOST=$(get_ssh_host)
[ -z "$SSH_HOST" ] && { echo "错误: 未配置 SSH_SERVER"; exit 1; }
DB_CONTAINER="1Panel-postgresql-FtMo"
DB_USER="milkydata"
# ---------- 构建 SQL ----------
USER_VAL="NULL"
[ "$SCOPE" = "user" ] && USER_VAL="$USER_ID"
CONTENT_VAL="NULL"
[ -n "$CONTENT" ] && CONTENT_VAL="'${CONTENT//\'/\'\'}'"
LINK_VAL="NULL"
[ -n "$LINK" ] && LINK_VAL="'${LINK//\'/\'\'}'"
SQL="INSERT INTO notifications (scope, user_id, type, title, content, priority, link)
VALUES ('${SCOPE}', ${USER_VAL}, '${NOTIF_TYPE}', '${TITLE//\'/\'\'}', ${CONTENT_VAL}, '${PRIORITY}', ${LINK_VAL});"
# ---------- 确认信息 ----------
echo ""
echo "═══════════════════════════════════════"
echo " 操作: 发送通知"
echo " 范围: ${SCOPE}"
echo " 类型: ${NOTIF_TYPE}"
echo " 标题: ${TITLE}"
echo " 数据库: ${DB_NAME}${SSH_HOST}"
echo "═══════════════════════════════════════"
echo ""
echo "将要执行:"
echo " ${SQL}"
echo ""
[ "$DRY_RUN" = true ] && { echo "预览模式,未执行。"; exit 0; }
read -p "确认发送? [y/N] " -n 1 -r; echo
[[ ! $REPLY =~ ^[Yy]$ ]] && { echo "已取消"; exit 0; }
echo ""
echo "执行中..."
ssh "$SSH_HOST" "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${SQL}\"" && echo "✅ 通知已发送" || echo "❌ 发送失败"
# 验证
echo ""
echo "最近通知:"
VERIFY_SQL="SELECT id, scope, user_id, type, title, priority, created_at::date
FROM notifications ORDER BY id DESC LIMIT 5;"
ssh "$SSH_HOST" "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${VERIFY_SQL}\""
echo ""

102
scripts/set-paid-user.sh Executable file
View File

@@ -0,0 +1,102 @@
#!/bin/bash
# ===========================================
# 通过 SSH 在服务器上执行 SQL设置指定用户的付费状态
# ===========================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
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 <user_id> [--permanent|--expires YYYY-MM-DD|--cancel]"
echo ""
echo " user_id 用户 ID必填"
echo " --monthly 设为月会员30天
--permanent 设为永久会员is_member=true, membership_expires_at=NULL"
echo " --expires DATE 设为指定日期到期(格式: YYYY-MM-DD"
echo " --cancel 取消付费状态is_member=false, membership_expires_at=NULL"
echo ""
echo "示例:"
echo " $0 1 --permanent # 用户 1 永久会员"
echo " $0 1 --expires 2027-12-31 # 2027-12-31 到期"
echo " $0 1 --cancel # 取消付费"
exit 0
}
# ---------- 参数解析 ----------
USER_ID=""; ACTION=""; EXPIRES_DATE=""
while [[ $# -gt 0 ]]; do
case "$1" in
--permanent) ACTION="permanent" ;;
--expires) ACTION="expires"; EXPIRES_DATE="$2"; shift ;;
--monthly) ACTION="monthly" ;;
--cancel) ACTION="cancel" ;;
--dev) DB_NAME="milkydata_dev" ;;
--help|-h) usage ;;
*) USER_ID="$1" ;;
esac
shift
done
[ -z "$USER_ID" ] && usage
[ -z "$ACTION" ] && usage
# ---------- 连接信息 ----------
SSH_HOST=$(get_ssh_host)
[ -z "$SSH_HOST" ] && { echo "错误: 未配置 SSH_SERVER"; exit 1; }
DB_CONTAINER="1Panel-postgresql-FtMo"
DB_USER="milkydata"
# ---------- 构建 SQL ----------
case "$ACTION" in
permanent)
SQL="UPDATE users SET is_member = true, membership_expires_at = NULL WHERE id = ${USER_ID};"
DESC="永久会员"
;;
monthly)
SQL="UPDATE users SET is_member = true, membership_expires_at = NOW() + INTERVAL '30 days' WHERE id = ${USER_ID};"
DESC="月会员30天"
;;
expires)
SQL="UPDATE users SET is_member = true, membership_expires_at = '${EXPIRES_DATE}' WHERE id = ${USER_ID};"
DESC="到期: ${EXPIRES_DATE}"
;;
cancel)
SQL="UPDATE users SET is_member = false, membership_expires_at = NULL WHERE id = ${USER_ID};"
DESC="取消付费"
;;
esac
echo ""
echo "═══════════════════════════════════════"
echo " 操作: 用户 ${USER_ID}${DESC}"
echo " 数据库: ${DB_NAME}${SSH_HOST}"
echo "═══════════════════════════════════════"
echo ""
echo "当前状态:"
CURRENT_SQL="SELECT id, name, is_member, membership_expires_at FROM users WHERE id = ${USER_ID};"
ssh "$SSH_HOST" "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${CURRENT_SQL}\""
echo ""
echo "将要执行:"
echo " ${SQL}"
echo ""
read -p "确认执行? [y/N] " -n 1 -r; echo
[[ ! $REPLY =~ ^[Yy]$ ]] && { echo "已取消"; exit 0; }
echo ""
echo "执行中..."
ssh "$SSH_HOST" "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${SQL}\"" && echo "✅ 操作成功" || echo "❌ 操作失败"
echo ""
echo "验证结果:"
VERIFY_SQL="SELECT id, name, phone, type, is_member, is_admin, membership_expires_at FROM users WHERE id = ${USER_ID};"
ssh "$SSH_HOST" "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${VERIFY_SQL}\""
echo ""