From bded31a7e797d9c2b44e7e2c3e4fa619330712cc Mon Sep 17 00:00:00 2001 From: dekun Date: Sun, 17 May 2026 09:28:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_monitor_binance/.env.example | 5 + crypto_monitor_binance/scripts/backup_data.sh | 109 ++++++++++++++++++ .../scripts/install_backup_cron.sh | 38 ++++++ crypto_monitor_binance/使用说明.md | 2 +- crypto_monitor_binance/更新文档.md | 11 +- crypto_monitor_binance/部署文档.md | 61 +++++++++- crypto_monitor_gate/.env.example | 5 + crypto_monitor_gate/scripts/backup_data.sh | 109 ++++++++++++++++++ .../scripts/install_backup_cron.sh | 38 ++++++ crypto_monitor_gate/使用说明.md | 2 +- crypto_monitor_gate/更新文档.md | 11 +- crypto_monitor_gate/部署文档.md | 17 ++- crypto_monitor_gate_bot/.env.example | 5 + .../scripts/backup_data.sh | 109 ++++++++++++++++++ .../scripts/install_backup_cron.sh | 38 ++++++ crypto_monitor_gate_bot/部署文档.md | 17 ++- 16 files changed, 568 insertions(+), 9 deletions(-) create mode 100644 crypto_monitor_binance/scripts/backup_data.sh create mode 100644 crypto_monitor_binance/scripts/install_backup_cron.sh create mode 100644 crypto_monitor_gate/scripts/backup_data.sh create mode 100644 crypto_monitor_gate/scripts/install_backup_cron.sh create mode 100644 crypto_monitor_gate_bot/scripts/backup_data.sh create mode 100644 crypto_monitor_gate_bot/scripts/install_backup_cron.sh diff --git a/crypto_monitor_binance/.env.example b/crypto_monitor_binance/.env.example index 110d17f..209324b 100644 --- a/crypto_monitor_binance/.env.example +++ b/crypto_monitor_binance/.env.example @@ -37,6 +37,11 @@ DB_PATH=crypto.db # 交易截图上传目录 UPLOAD_DIR=static/images +# 自动备份(scripts/backup_data.sh + cron,可选;默认即可) +# BACKUP_ROOT=/root/backups +# BACKUP_RETENTION_DAYS=30 +# BACKUP_INSTANCE=crypto_monitor_binance + # 已废弃:资金账户仅显示交易所 funding 余额,不再读取此变量 # TOTAL_CAPITAL=100 # 每天起始基数(U) diff --git a/crypto_monitor_binance/scripts/backup_data.sh b/crypto_monitor_binance/scripts/backup_data.sh new file mode 100644 index 0000000..9a25287 --- /dev/null +++ b/crypto_monitor_binance/scripts/backup_data.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# Daily backup: SQLite DB + static/images → /root/backups/// +# Prune backup folders older than RETENTION_DAYS (default 30). +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +cd "$PROJECT_DIR" + +BACKUP_ROOT="${BACKUP_ROOT:-/root/backups}" +RETENTION_DAYS="${RETENTION_DAYS:-30}" +INSTANCE_NAME="${BACKUP_INSTANCE:-$(basename "$PROJECT_DIR")}" +TZ_NAME="${BACKUP_TZ:-Asia/Shanghai}" + +log() { + printf '[%s] %s\n' "$(TZ="$TZ_NAME" date '+%Y-%m-%d %H:%M:%S %Z')" "$*" +} + +read_env_var() { + local key="$1" + local default="$2" + local line + if [[ ! -f .env ]]; then + printf '%s' "$default" + return + fi + line="$(grep -E "^${key}=" .env 2>/dev/null | tail -1 || true)" + if [[ -z "$line" ]]; then + printf '%s' "$default" + return + fi + printf '%s' "${line#*=}" | tr -d '\r' +} + +resolve_project_path() { + local p="$1" + if [[ "$p" == /* ]]; then + printf '%s' "$p" + else + printf '%s' "$PROJECT_DIR/$p" + fi +} + +prune_old_backups() { + local base="$BACKUP_ROOT/$INSTANCE_NAME" + [[ -d "$base" ]] || return 0 + local cutoff + cutoff="$(TZ="$TZ_NAME" date -d "-${RETENTION_DAYS} days" +%Y-%m-%d 2>/dev/null || true)" + if [[ -z "$cutoff" ]]; then + find "$base" -mindepth 1 -maxdepth 1 -type d -mtime +"$RETENTION_DAYS" -print0 | + xargs -r -0 rm -rf + return 0 + fi + local dir name + for dir in "$base"/*/; do + [[ -d "$dir" ]] || continue + name="$(basename "$dir")" + [[ "$name" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || continue + if [[ "$name" < "$cutoff" ]]; then + log "prune: remove $dir (older than ${RETENTION_DAYS} days)" + rm -rf "$dir" + fi + done +} + +DB_REL="$(read_env_var DB_PATH crypto.db)" +UPLOAD_REL="$(read_env_var UPLOAD_DIR static/images)" +BACKUP_ROOT="$(read_env_var BACKUP_ROOT "$BACKUP_ROOT")" +RETENTION_DAYS="$(read_env_var BACKUP_RETENTION_DAYS "$RETENTION_DAYS")" +INSTANCE_NAME="$(read_env_var BACKUP_INSTANCE "$INSTANCE_NAME")" + +DB_PATH="$(resolve_project_path "$DB_REL")" +UPLOAD_DIR="$(resolve_project_path "$UPLOAD_REL")" +DATE_TAG="$(TZ="$TZ_NAME" date +%Y-%m-%d)" +DEST="$BACKUP_ROOT/$INSTANCE_NAME/$DATE_TAG" + +if [[ ! -f "$DB_PATH" ]]; then + log "error: database not found: $DB_PATH" + exit 1 +fi + +mkdir -p "$DEST" +log "start backup instance=$INSTANCE_NAME dest=$DEST" + +if command -v sqlite3 >/dev/null 2>&1; then + sqlite3 "$DB_PATH" ".backup '$DEST/crypto.db'" + log "db: sqlite3 backup -> $DEST/crypto.db" +else + cp -a "$DB_PATH" "$DEST/crypto.db" + log "db: cp -> $DEST/crypto.db (sqlite3 not installed)" +fi + +if [[ -d "$UPLOAD_DIR" ]]; then + tar -czf "$DEST/static_images.tar.gz" -C "$(dirname "$UPLOAD_DIR")" "$(basename "$UPLOAD_DIR")" + log "images: $UPLOAD_DIR -> $DEST/static_images.tar.gz" +else + log "warn: upload dir missing, skip images: $UPLOAD_DIR" +fi + +{ + echo "instance=$INSTANCE_NAME" + echo "project_dir=$PROJECT_DIR" + echo "backup_date=$DATE_TAG" + echo "db_path=$DB_PATH" + echo "upload_dir=$UPLOAD_DIR" +} >"$DEST/manifest.txt" + +prune_old_backups +log "done" diff --git a/crypto_monitor_binance/scripts/install_backup_cron.sh b/crypto_monitor_binance/scripts/install_backup_cron.sh new file mode 100644 index 0000000..96053f4 --- /dev/null +++ b/crypto_monitor_binance/scripts/install_backup_cron.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Install daily backup cron: Beijing 00:00 (CRON_TZ=Asia/Shanghai). +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +BACKUP_SCRIPT="$SCRIPT_DIR/backup_data.sh" +INSTANCE_NAME="${BACKUP_INSTANCE:-$(basename "$PROJECT_DIR")}" +LOG_FILE="${BACKUP_CRON_LOG:-/var/log/crypto-monitor-backup-${INSTANCE_NAME}.log}" +if [[ ! -x "$BACKUP_SCRIPT" ]]; then + chmod +x "$BACKUP_SCRIPT" +fi + +TMP="$(mktemp)" +trap 'rm -f "$TMP"' EXIT + +{ + crontab -l 2>/dev/null | grep -vF "$BACKUP_SCRIPT" || true + echo "CRON_TZ=Asia/Shanghai" + echo "0 0 * * * $BACKUP_SCRIPT >> $LOG_FILE 2>&1" +} >"$TMP" + +# Keep a single CRON_TZ line at top. +awk ' + BEGIN { tz = 0 } + /^CRON_TZ=Asia\/Shanghai$/ { + if (tz++) next + } + { print } +' "$TMP" >"${TMP}.2" +mv "${TMP}.2" "$TMP" + +crontab "$TMP" +echo "Installed cron for $INSTANCE_NAME" +echo " Schedule : daily 00:00 Asia/Shanghai" +echo " Script : $BACKUP_SCRIPT" +echo " Log : $LOG_FILE" +crontab -l | grep -F "$BACKUP_SCRIPT" || true diff --git a/crypto_monitor_binance/使用说明.md b/crypto_monitor_binance/使用说明.md index 694a951..72871f8 100644 --- a/crypto_monitor_binance/使用说明.md +++ b/crypto_monitor_binance/使用说明.md @@ -106,7 +106,7 @@ 1. **先用 `LIVE_TRADING_ENABLED=false`** 熟悉流程再实盘。 2. **API 权限**最小化,密钥勿泄露。 3. **同一账户避免多程序重复开仓**。 -4. **备份数据库**后再升级迁移。 +4. **自动备份**:服务器上执行 `bash scripts/install_backup_cron.sh`(每天北京时间 0:00 → `/root/backups`,保留 30 天);升级前也可 `bash scripts/backup_data.sh` 手动跑一次。 5. 升级代码后留意 **首轮启动**有无数据库迁移报错。 --- diff --git a/crypto_monitor_binance/更新文档.md b/crypto_monitor_binance/更新文档.md index 386526b..f0f6501 100644 --- a/crypto_monitor_binance/更新文档.md +++ b/crypto_monitor_binance/更新文档.md @@ -30,8 +30,15 @@ 详见 `.env.example` 中「关键位门控」「交易执行 / 人工风控」注释段。 +## 自动备份(服务器) + +- 脚本:`scripts/backup_data.sh`(`crypto.db` + `static/images`) +- 定时:`scripts/install_backup_cron.sh` → 每天 **北京时间 0:00**,目录 **`/root/backups/<实例名>/YYYY-MM-DD/`**,保留 **30** 天 +- 详见 `部署文档.md` 第 5.3 节 + ## 升级步骤 1. `git pull` 后对比 `.env.example`,把新增变量合并进本地 `.env`。 -2. 重启服务(如 `pm2 restart`);SQLite 会在启动时自动 `ALTER` 新列。 -3. 浏览器强刷(Ctrl+F5)避免旧版 `index.html` 缓存。 +2. 在 VPS 上为 Binance / Gate / Gate Bot **各执行一次** `bash scripts/install_backup_cron.sh`(若尚未安装)。 +3. 重启服务(如 `pm2 restart`);SQLite 会在启动时自动 `ALTER` 新列。 +4. 浏览器强刷(Ctrl+F5)避免旧版 `index.html` 缓存。 diff --git a/crypto_monitor_binance/部署文档.md b/crypto_monitor_binance/部署文档.md index d9dd8ad..9bff01e 100644 --- a/crypto_monitor_binance/部署文档.md +++ b/crypto_monitor_binance/部署文档.md @@ -147,7 +147,66 @@ cp .env .env.backup.$(date +%Y%m%d) - **换机 / 迁移**:用 `scp` 复制整份 `.env` 到新机器对应目录;或在新机重新 `cp .env.example .env` 后填写。 -### 5.3 必填项检查(Binance + 代理) +### 5.3 自动备份(数据库 + 复盘图片) + +默认每天 **北京时间 0:00** 备份到 **`/root/backups`**,保留 **30 天** 后自动删除更早的目录。 + +备份内容(路径来自 `.env` 的 `DB_PATH`、`UPLOAD_DIR`): + +- `crypto.db`(优先 `sqlite3 .backup` 热备) +- `static/images` 打包为 `static_images.tar.gz` + +目录结构示例: + +```text +/root/backups/crypto_monitor_binance/2026-05-17/ + crypto.db + static_images.tar.gz + manifest.txt +``` + +**一次性安装定时任务**(在对应项目目录执行,Binance / Gate 各执行一次): + +```bash +cd /opt/crypto_monitor/crypto_monitor_binance +chmod +x scripts/backup_data.sh scripts/install_backup_cron.sh +bash scripts/install_backup_cron.sh +``` + +Gate 实例: + +```bash +cd /opt/crypto_monitor/crypto_monitor_gate +bash scripts/install_backup_cron.sh +``` + +Gate Bot 实例(趋势回调等): + +```bash +cd /opt/crypto_monitor/crypto_monitor_gate_bot +bash scripts/install_backup_cron.sh +``` + +**立即试跑**(不写 cron): + +```bash +bash scripts/backup_data.sh +``` + +日志默认:`/var/log/crypto-monitor-backup-<项目目录名>.log`。可选在 `.env` 中覆盖:`BACKUP_ROOT`、`BACKUP_RETENTION_DAYS`、`BACKUP_INSTANCE`。 + +**恢复示例**(先停 PM2,再覆盖文件): + +```bash +pm2 stop crypto-monitor-binance +cp /root/backups/crypto_monitor_binance/2026-05-16/crypto.db ./crypto.db +tar -xzf /root/backups/crypto_monitor_binance/2026-05-16/static_images.tar.gz -C . +pm2 start ecosystem.config.cjs +``` + +建议安装:`apt install -y sqlite3`(热备更稳)。 + +### 5.4 必填项检查(Binance + 代理) 与交易所相关的变量使用 **`BINANCE_`** 前缀(与代码一致)。至少确认: diff --git a/crypto_monitor_gate/.env.example b/crypto_monitor_gate/.env.example index 56471a8..15cfe99 100644 --- a/crypto_monitor_gate/.env.example +++ b/crypto_monitor_gate/.env.example @@ -37,6 +37,11 @@ DB_PATH=crypto.db # 交易截图上传目录 UPLOAD_DIR=static/images +# 自动备份(scripts/backup_data.sh + cron,可选;默认即可) +# BACKUP_ROOT=/root/backups +# BACKUP_RETENTION_DAYS=30 +# BACKUP_INSTANCE=crypto_monitor_gate + # 已废弃:资金账户仅显示交易所 funding 余额,不再读取此变量 # TOTAL_CAPITAL=100 # 每天起始基数(U) diff --git a/crypto_monitor_gate/scripts/backup_data.sh b/crypto_monitor_gate/scripts/backup_data.sh new file mode 100644 index 0000000..9a25287 --- /dev/null +++ b/crypto_monitor_gate/scripts/backup_data.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# Daily backup: SQLite DB + static/images → /root/backups/// +# Prune backup folders older than RETENTION_DAYS (default 30). +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +cd "$PROJECT_DIR" + +BACKUP_ROOT="${BACKUP_ROOT:-/root/backups}" +RETENTION_DAYS="${RETENTION_DAYS:-30}" +INSTANCE_NAME="${BACKUP_INSTANCE:-$(basename "$PROJECT_DIR")}" +TZ_NAME="${BACKUP_TZ:-Asia/Shanghai}" + +log() { + printf '[%s] %s\n' "$(TZ="$TZ_NAME" date '+%Y-%m-%d %H:%M:%S %Z')" "$*" +} + +read_env_var() { + local key="$1" + local default="$2" + local line + if [[ ! -f .env ]]; then + printf '%s' "$default" + return + fi + line="$(grep -E "^${key}=" .env 2>/dev/null | tail -1 || true)" + if [[ -z "$line" ]]; then + printf '%s' "$default" + return + fi + printf '%s' "${line#*=}" | tr -d '\r' +} + +resolve_project_path() { + local p="$1" + if [[ "$p" == /* ]]; then + printf '%s' "$p" + else + printf '%s' "$PROJECT_DIR/$p" + fi +} + +prune_old_backups() { + local base="$BACKUP_ROOT/$INSTANCE_NAME" + [[ -d "$base" ]] || return 0 + local cutoff + cutoff="$(TZ="$TZ_NAME" date -d "-${RETENTION_DAYS} days" +%Y-%m-%d 2>/dev/null || true)" + if [[ -z "$cutoff" ]]; then + find "$base" -mindepth 1 -maxdepth 1 -type d -mtime +"$RETENTION_DAYS" -print0 | + xargs -r -0 rm -rf + return 0 + fi + local dir name + for dir in "$base"/*/; do + [[ -d "$dir" ]] || continue + name="$(basename "$dir")" + [[ "$name" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || continue + if [[ "$name" < "$cutoff" ]]; then + log "prune: remove $dir (older than ${RETENTION_DAYS} days)" + rm -rf "$dir" + fi + done +} + +DB_REL="$(read_env_var DB_PATH crypto.db)" +UPLOAD_REL="$(read_env_var UPLOAD_DIR static/images)" +BACKUP_ROOT="$(read_env_var BACKUP_ROOT "$BACKUP_ROOT")" +RETENTION_DAYS="$(read_env_var BACKUP_RETENTION_DAYS "$RETENTION_DAYS")" +INSTANCE_NAME="$(read_env_var BACKUP_INSTANCE "$INSTANCE_NAME")" + +DB_PATH="$(resolve_project_path "$DB_REL")" +UPLOAD_DIR="$(resolve_project_path "$UPLOAD_REL")" +DATE_TAG="$(TZ="$TZ_NAME" date +%Y-%m-%d)" +DEST="$BACKUP_ROOT/$INSTANCE_NAME/$DATE_TAG" + +if [[ ! -f "$DB_PATH" ]]; then + log "error: database not found: $DB_PATH" + exit 1 +fi + +mkdir -p "$DEST" +log "start backup instance=$INSTANCE_NAME dest=$DEST" + +if command -v sqlite3 >/dev/null 2>&1; then + sqlite3 "$DB_PATH" ".backup '$DEST/crypto.db'" + log "db: sqlite3 backup -> $DEST/crypto.db" +else + cp -a "$DB_PATH" "$DEST/crypto.db" + log "db: cp -> $DEST/crypto.db (sqlite3 not installed)" +fi + +if [[ -d "$UPLOAD_DIR" ]]; then + tar -czf "$DEST/static_images.tar.gz" -C "$(dirname "$UPLOAD_DIR")" "$(basename "$UPLOAD_DIR")" + log "images: $UPLOAD_DIR -> $DEST/static_images.tar.gz" +else + log "warn: upload dir missing, skip images: $UPLOAD_DIR" +fi + +{ + echo "instance=$INSTANCE_NAME" + echo "project_dir=$PROJECT_DIR" + echo "backup_date=$DATE_TAG" + echo "db_path=$DB_PATH" + echo "upload_dir=$UPLOAD_DIR" +} >"$DEST/manifest.txt" + +prune_old_backups +log "done" diff --git a/crypto_monitor_gate/scripts/install_backup_cron.sh b/crypto_monitor_gate/scripts/install_backup_cron.sh new file mode 100644 index 0000000..2ebe5cc --- /dev/null +++ b/crypto_monitor_gate/scripts/install_backup_cron.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Install daily backup cron: Beijing 00:00 (CRON_TZ=Asia/Shanghai). +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +BACKUP_SCRIPT="$SCRIPT_DIR/backup_data.sh" +INSTANCE_NAME="${BACKUP_INSTANCE:-$(basename "$PROJECT_DIR")}" +LOG_FILE="${BACKUP_CRON_LOG:-/var/log/crypto-monitor-backup-${INSTANCE_NAME}.log}" + +if [[ ! -x "$BACKUP_SCRIPT" ]]; then + chmod +x "$BACKUP_SCRIPT" +fi + +TMP="$(mktemp)" +trap 'rm -f "$TMP"' EXIT + +{ + crontab -l 2>/dev/null | grep -vF "$BACKUP_SCRIPT" || true + echo "CRON_TZ=Asia/Shanghai" + echo "0 0 * * * $BACKUP_SCRIPT >> $LOG_FILE 2>&1" +} >"$TMP" + +awk ' + BEGIN { tz = 0 } + /^CRON_TZ=Asia\/Shanghai$/ { + if (tz++) next + } + { print } +' "$TMP" >"${TMP}.2" +mv "${TMP}.2" "$TMP" + +crontab "$TMP" +echo "Installed cron for $INSTANCE_NAME" +echo " Schedule : daily 00:00 Asia/Shanghai" +echo " Script : $BACKUP_SCRIPT" +echo " Log : $LOG_FILE" +crontab -l | grep -F "$BACKUP_SCRIPT" || true diff --git a/crypto_monitor_gate/使用说明.md b/crypto_monitor_gate/使用说明.md index 5fa4e82..e98c68d 100644 --- a/crypto_monitor_gate/使用说明.md +++ b/crypto_monitor_gate/使用说明.md @@ -114,7 +114,7 @@ 1. **先用 `LIVE_TRADING_ENABLED=false`** 验证页面、录入、推送,再开小资金开实盘。 2. **API 权限**:仅开所需合约权限;勿泄露密钥;定期轮换。 3. **单进程控盘**:同一账户避免本程序与其他机器人 **重复开仓**。 -4. **数据库**:`*.sqlite`(或你在 `.env` 里指向的库文件)包含监控与订单,**备份后再升级/迁移**。 +4. **自动备份**:服务器上执行 `bash scripts/install_backup_cron.sh`(每天北京时间 0:00 → `/root/backups`,保留 30 天);升级前也可 `bash scripts/backup_data.sh` 手动跑一次。 5. **升级代码后**:启动时会跑 **数据库迁移**(如新列 `order_monitors.monitor_type`);首次启动关注一下日志或无报错页面。 --- diff --git a/crypto_monitor_gate/更新文档.md b/crypto_monitor_gate/更新文档.md index a4cccea..1b1a023 100644 --- a/crypto_monitor_gate/更新文档.md +++ b/crypto_monitor_gate/更新文档.md @@ -30,8 +30,15 @@ 详见 `.env.example` 中「关键位门控」「交易执行 / 人工风控」注释段。Gate 专用项(`GATE_*`、止盈止损触发等)保持原有段落不变。 +## 自动备份(服务器) + +- 脚本:`scripts/backup_data.sh`(`crypto.db` + `static/images`) +- 定时:`scripts/install_backup_cron.sh` → 每天 **北京时间 0:00**,目录 **`/root/backups/<实例名>/YYYY-MM-DD/`**,保留 **30** 天 +- 详见 `部署文档.md` 第 5.3 节 + ## 升级步骤 1. `git pull` 后对比 `.env.example`,把新增变量合并进本地 `.env`。 -2. 重启 Gate 实例服务(如 `pm2 restart`)。 -3. 浏览器强刷(Ctrl+F5)避免旧版 `index.html` 缓存。 +2. 在 VPS 上为 Binance / Gate / Gate Bot **各执行一次** `bash scripts/install_backup_cron.sh`(若尚未安装)。 +3. 重启 Gate 实例服务(如 `pm2 restart`)。 +4. 浏览器强刷(Ctrl+F5)避免旧版 `index.html` 缓存。 diff --git a/crypto_monitor_gate/部署文档.md b/crypto_monitor_gate/部署文档.md index 36dcffa..ae35ead 100644 --- a/crypto_monitor_gate/部署文档.md +++ b/crypto_monitor_gate/部署文档.md @@ -138,7 +138,22 @@ nano .env - **升级前备份**:`cp .env .env.backup.$(date +%Y%m%d)`;恢复:`cp .env.backup.YYYYMMDD .env`。 - **换机**:`scp` 复制 `.env`,或新机 `cp .env.example .env` 后重填。 -### 5.3 必填项检查(Gate + 代理) +### 5.3 自动备份(数据库 + 复盘图片) + +与 Binance 实例相同:每天 **北京时间 0:00** → **`/root/backups`**,保留 **30 天**。 + +```bash +cd /opt/crypto_monitor/crypto_monitor_gate +chmod +x scripts/backup_data.sh scripts/install_backup_cron.sh +bash scripts/install_backup_cron.sh +bash scripts/backup_data.sh # 试跑 +``` + +备份目录:`/root/backups/crypto_monitor_gate/YYYY-MM-DD/`。详见 Binance 项目 `部署文档.md` 第 5.3 节(恢复步骤、可选 `.env` 变量相同)。 + +若还部署了 **`crypto_monitor_gate_bot`**,请在该目录同样执行 `bash scripts/install_backup_cron.sh`。 + +### 5.4 必填项检查(Gate + 代理) 与交易所相关的变量必须是 **Gate** 前缀(**不要**再写 OKX 变量,否则代理不会生效、密钥也不会被识别)。至少确认: diff --git a/crypto_monitor_gate_bot/.env.example b/crypto_monitor_gate_bot/.env.example index 2aac7e9..9f2b4c8 100644 --- a/crypto_monitor_gate_bot/.env.example +++ b/crypto_monitor_gate_bot/.env.example @@ -37,6 +37,11 @@ DB_PATH=crypto.db # 交易截图上传目录 UPLOAD_DIR=static/images +# 自动备份(scripts/backup_data.sh + cron,可选;默认即可) +# BACKUP_ROOT=/root/backups +# BACKUP_RETENTION_DAYS=30 +# BACKUP_INSTANCE=crypto_monitor_gate_bot + # 已废弃:资金账户仅显示交易所 funding 余额,不再读取此变量 # TOTAL_CAPITAL=100 # 每天起始基数(U) diff --git a/crypto_monitor_gate_bot/scripts/backup_data.sh b/crypto_monitor_gate_bot/scripts/backup_data.sh new file mode 100644 index 0000000..9a25287 --- /dev/null +++ b/crypto_monitor_gate_bot/scripts/backup_data.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# Daily backup: SQLite DB + static/images → /root/backups/// +# Prune backup folders older than RETENTION_DAYS (default 30). +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +cd "$PROJECT_DIR" + +BACKUP_ROOT="${BACKUP_ROOT:-/root/backups}" +RETENTION_DAYS="${RETENTION_DAYS:-30}" +INSTANCE_NAME="${BACKUP_INSTANCE:-$(basename "$PROJECT_DIR")}" +TZ_NAME="${BACKUP_TZ:-Asia/Shanghai}" + +log() { + printf '[%s] %s\n' "$(TZ="$TZ_NAME" date '+%Y-%m-%d %H:%M:%S %Z')" "$*" +} + +read_env_var() { + local key="$1" + local default="$2" + local line + if [[ ! -f .env ]]; then + printf '%s' "$default" + return + fi + line="$(grep -E "^${key}=" .env 2>/dev/null | tail -1 || true)" + if [[ -z "$line" ]]; then + printf '%s' "$default" + return + fi + printf '%s' "${line#*=}" | tr -d '\r' +} + +resolve_project_path() { + local p="$1" + if [[ "$p" == /* ]]; then + printf '%s' "$p" + else + printf '%s' "$PROJECT_DIR/$p" + fi +} + +prune_old_backups() { + local base="$BACKUP_ROOT/$INSTANCE_NAME" + [[ -d "$base" ]] || return 0 + local cutoff + cutoff="$(TZ="$TZ_NAME" date -d "-${RETENTION_DAYS} days" +%Y-%m-%d 2>/dev/null || true)" + if [[ -z "$cutoff" ]]; then + find "$base" -mindepth 1 -maxdepth 1 -type d -mtime +"$RETENTION_DAYS" -print0 | + xargs -r -0 rm -rf + return 0 + fi + local dir name + for dir in "$base"/*/; do + [[ -d "$dir" ]] || continue + name="$(basename "$dir")" + [[ "$name" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || continue + if [[ "$name" < "$cutoff" ]]; then + log "prune: remove $dir (older than ${RETENTION_DAYS} days)" + rm -rf "$dir" + fi + done +} + +DB_REL="$(read_env_var DB_PATH crypto.db)" +UPLOAD_REL="$(read_env_var UPLOAD_DIR static/images)" +BACKUP_ROOT="$(read_env_var BACKUP_ROOT "$BACKUP_ROOT")" +RETENTION_DAYS="$(read_env_var BACKUP_RETENTION_DAYS "$RETENTION_DAYS")" +INSTANCE_NAME="$(read_env_var BACKUP_INSTANCE "$INSTANCE_NAME")" + +DB_PATH="$(resolve_project_path "$DB_REL")" +UPLOAD_DIR="$(resolve_project_path "$UPLOAD_REL")" +DATE_TAG="$(TZ="$TZ_NAME" date +%Y-%m-%d)" +DEST="$BACKUP_ROOT/$INSTANCE_NAME/$DATE_TAG" + +if [[ ! -f "$DB_PATH" ]]; then + log "error: database not found: $DB_PATH" + exit 1 +fi + +mkdir -p "$DEST" +log "start backup instance=$INSTANCE_NAME dest=$DEST" + +if command -v sqlite3 >/dev/null 2>&1; then + sqlite3 "$DB_PATH" ".backup '$DEST/crypto.db'" + log "db: sqlite3 backup -> $DEST/crypto.db" +else + cp -a "$DB_PATH" "$DEST/crypto.db" + log "db: cp -> $DEST/crypto.db (sqlite3 not installed)" +fi + +if [[ -d "$UPLOAD_DIR" ]]; then + tar -czf "$DEST/static_images.tar.gz" -C "$(dirname "$UPLOAD_DIR")" "$(basename "$UPLOAD_DIR")" + log "images: $UPLOAD_DIR -> $DEST/static_images.tar.gz" +else + log "warn: upload dir missing, skip images: $UPLOAD_DIR" +fi + +{ + echo "instance=$INSTANCE_NAME" + echo "project_dir=$PROJECT_DIR" + echo "backup_date=$DATE_TAG" + echo "db_path=$DB_PATH" + echo "upload_dir=$UPLOAD_DIR" +} >"$DEST/manifest.txt" + +prune_old_backups +log "done" diff --git a/crypto_monitor_gate_bot/scripts/install_backup_cron.sh b/crypto_monitor_gate_bot/scripts/install_backup_cron.sh new file mode 100644 index 0000000..2ebe5cc --- /dev/null +++ b/crypto_monitor_gate_bot/scripts/install_backup_cron.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Install daily backup cron: Beijing 00:00 (CRON_TZ=Asia/Shanghai). +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +BACKUP_SCRIPT="$SCRIPT_DIR/backup_data.sh" +INSTANCE_NAME="${BACKUP_INSTANCE:-$(basename "$PROJECT_DIR")}" +LOG_FILE="${BACKUP_CRON_LOG:-/var/log/crypto-monitor-backup-${INSTANCE_NAME}.log}" + +if [[ ! -x "$BACKUP_SCRIPT" ]]; then + chmod +x "$BACKUP_SCRIPT" +fi + +TMP="$(mktemp)" +trap 'rm -f "$TMP"' EXIT + +{ + crontab -l 2>/dev/null | grep -vF "$BACKUP_SCRIPT" || true + echo "CRON_TZ=Asia/Shanghai" + echo "0 0 * * * $BACKUP_SCRIPT >> $LOG_FILE 2>&1" +} >"$TMP" + +awk ' + BEGIN { tz = 0 } + /^CRON_TZ=Asia\/Shanghai$/ { + if (tz++) next + } + { print } +' "$TMP" >"${TMP}.2" +mv "${TMP}.2" "$TMP" + +crontab "$TMP" +echo "Installed cron for $INSTANCE_NAME" +echo " Schedule : daily 00:00 Asia/Shanghai" +echo " Script : $BACKUP_SCRIPT" +echo " Log : $LOG_FILE" +crontab -l | grep -F "$BACKUP_SCRIPT" || true diff --git a/crypto_monitor_gate_bot/部署文档.md b/crypto_monitor_gate_bot/部署文档.md index 79a76d5..162292d 100644 --- a/crypto_monitor_gate_bot/部署文档.md +++ b/crypto_monitor_gate_bot/部署文档.md @@ -138,7 +138,22 @@ nano .env - **升级前备份**:`cp .env .env.backup.$(date +%Y%m%d)`;恢复:`cp .env.backup.YYYYMMDD .env`。 - **换机**:`scp` 复制 `.env`,或新机 `cp .env.example .env` 后重填。 -### 5.3 必填项检查(Gate + 代理) +### 5.3 自动备份(数据库 + 复盘图片) + +每天 **北京时间 0:00** 备份到 **`/root/backups`**,保留 **30 天**(`crypto.db` + `static/images`)。 + +```bash +cd /opt/crypto_monitor/crypto_monitor_gate_bot +chmod +x scripts/backup_data.sh scripts/install_backup_cron.sh +bash scripts/install_backup_cron.sh +bash scripts/backup_data.sh # 试跑 +``` + +备份目录:`/root/backups/crypto_monitor_gate_bot/YYYY-MM-DD/`。与 Binance / Gate 实例规则相同,详见 `crypto_monitor_binance/部署文档.md` 第 5.3 节(恢复步骤、可选 `.env` 变量)。 + +若服务器同时跑 **binance、gate、gate_bot** 三个实例,请在**各自项目目录**各执行一次 `install_backup_cron.sh`。 + +### 5.4 必填项检查(Gate + 代理) 与交易所相关的变量必须是 **Gate** 前缀(**不要**再写 OKX 变量,否则代理不会生效、密钥也不会被识别)。至少确认: