#!/bin/bash
# =====================================================================
# SAO_LUU.SH — sao lưu dự phòng cho máy web và máy key (26/09/2026)
# =====================================================================
# Chủ dự án chốt nguyên tắc 3-2-1: nhiều bản, hai máy, một bản ngoài (Google Drive).
#   csdl      (máy web, mỗi giờ)  dump admin_tradingauto, kiểm "Dump completed", giữ SAOLUU_GIU_CSDL_GIO giờ
#   he_thong  (hai máy, mỗi ngày) gói cấu hình hệ thống + mã ngoài git, MÃ HOÁ, giữ SAOLUU_GIU_HE_THONG_NGAY
#   day_cheo  (hai máy, mỗi ngày) rsync bản sao lưu sang máy kia qua Tailscale (user saoluu, rrsync chỉ-ghi)
#   don_nhan  (hai máy, mỗi ngày) xoá bản NHẬN từ máy kia cũ hơn SAOLUU_GIU_NHAN_NGAY
#   len_drive (máy web, mỗi ngày) đẩy lên Google Drive qua rclone crypt (mã hoá phía mình), giữ 30 ngày
#             remote gdrive-mahoa = crypt trên gdrive:tradingauto-sao-luu, mật khẩu = nội dung /root/.sao-luu-khoa
#             (rclone obscure - < /root/.sao-luu-khoa). Khôi phục từ máy khác: tạo lại remote y như vậy.
#             gdrive: scope drive.file, client ID riêng (Desktop app, đã Publish — để Testing thì token chết sau 7 ngày),
#             root_folder_id = thư mục chủ dự án chỉ định (1w0OddlzWy1H1c7s2JRFjhKYSGxMecer9).
#             Sửa remote drive: dùng `rclone config update ... config_refresh_token=false`, không thì nó đòi đăng nhập lại.
#
# Cấu hình: /etc/tradingauto-saoluu.env (root 600). Khoá mã hoá: /root/.sao-luu-khoa — chủ dự án
# PHẢI cất một bản ngoài máy (trình quản lý mật khẩu): mất khoá = không giải mã được bản sao lưu.
#
# GIẢI MÃ bản he_thong:
#   openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 -pass file:/root/.sao-luu-khoa -in X.tar.zst.enc \
#     | zstd -d | tar -tvf -          # -tvf để xem, -xf - -C /thu/muc/tam để giải nén
# KHÔI PHỤC CSDL:
#   zstd -dc admin_tradingauto-YYYYmmdd-HH.sql.zst | mariadb admin_tradingauto
# =====================================================================
set -euo pipefail
umask 077

ENV=/etc/tradingauto-saoluu.env
[ -f "$ENV" ] && . "$ENV"
MAY=${SAOLUU_MAY:-$(hostname)}
THU_MUC=${SAOLUU_THU_MUC:-/var/backups/sao-luu}
KHOA=${SAOLUU_KHOA:-/root/.sao-luu-khoa}
NHAN=${SAOLUU_NHAN:-/srv/sao-luu/nhan}
LUC=$(date +%Y%m%d-%H%M)

ghi() { echo "[$MAY $(date '+%F %T')] $*"; }

csdl() {
    local d="$THU_MUC/csdl" t f
    install -d -m 700 "$d"
    for db in ${SAOLUU_CSDL:?thiếu SAOLUU_CSDL}; do
        f="$d/$db-$LUC.sql.zst"; t="$f.tam"
        mariadb-dump --single-transaction --quick --routines --triggers --events "$db" | zstd -q -3 -o "$t" -f
        zstd -q -t "$t"
        # Dump bị cắt giữa chừng thì không có dòng kết thúc — không cất bản hỏng.
        zstd -dc "$t" | tail -n 3 | grep -q "Dump completed" || { rm -f "$t"; ghi "LOI: dump $db khong tron ven"; exit 1; }
        mv "$t" "$f"
        ghi "csdl $db: $(basename "$f") $(stat -c %s "$f") byte"
    done
    find "$d" -name '*.sql.zst' -mmin +$(( ${SAOLUU_GIU_CSDL_GIO:-48} * 60 )) -delete
}

he_thong() {
    local d="$THU_MUC/he-thong" f="$THU_MUC/he-thong/$MAY-he-thong-$LUC.tar.zst.enc" ds
    [ -s "$KHOA" ] || { ghi "LOI: chua co khoa ma hoa $KHOA"; exit 1; }
    install -d -m 700 "$d"
    ds=$(mktemp); trap 'rm -f "$ds"' RETURN
    # Liệt kê tệp: bỏ thư mục trong SAOLUU_BO_THU_MUC (môi trường ảo, bộ nhớ đệm, log…), log,
    # bản .bak, tệp > SAOLUU_TOI_DA_MB. Máy key thêm "data" để bỏ dữ liệu phân tích 3 GB của SMART DCA;
    # máy web KHÔNG được bỏ "data" (mất /usr/local/hestia/data — người dùng, tường lửa Hestia).
    local bo=() t ds_nguon
    for t in ${SAOLUU_BO_THU_MUC:-venv .venv __pycache__ node_modules logs .git}; do
        bo+=(-name "$t" -o)
    done
    unset 'bo[${#bo[@]}-1]'
    # Tách theo khoảng trắng, KHÔNG eval: eval hiểu "=>web" của SAOLUU_DRIVE_NGUON thành chuyển hướng ra tệp.
    read -ra ds_nguon <<< "${SAOLUU_HE_THONG_DS:?thiếu SAOLUU_HE_THONG_DS}"; set -- "${ds_nguon[@]}"
    for p in "$@"; do
        [ -e "$p" ] || continue
        find "$p" \( "${bo[@]}" \) -prune -o \
             -type f ! -name '*.log' ! -name '*.log.*' ! -name '*.pyc' ! -name '*.bak*' \
             -size -"${SAOLUU_TOI_DA_MB:-5}"M -print0
    done > "$ds"
    tar --null -T "$ds" -cf - 2>/dev/null | zstd -q -10 \
        | openssl enc -aes-256-cbc -pbkdf2 -iter 200000 -salt -pass file:"$KHOA" -out "$f.tam"
    # Thử giải mã lại ngay: bản mã hoá không đọc được thì coi như không có.
    openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 -pass file:"$KHOA" -in "$f.tam" | zstd -q -t
    mv "$f.tam" "$f"
    ghi "he_thong: $(basename "$f") $(stat -c %s "$f") byte, $(tr -cd '\0' < "$ds" | wc -c) tep"
    find "$d" -name '*.tar.zst.enc' -mtime +"${SAOLUU_GIU_HE_THONG_NGAY:-30}" -delete
}

day_cheo() {
    local dich=${SAOLUU_DAY_DICH:?thiếu SAOLUU_DAY_DICH}
    local ds_nguon
    read -ra ds_nguon <<< "${SAOLUU_DAY_NGUON:?thiếu SAOLUU_DAY_NGUON}"; set -- "${ds_nguon[@]}"
    rsync -a --partial --timeout=600 \
          -e "ssh -i /root/.ssh/sao_luu_ed25519 -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=20" \
          "$@" "$dich"
    ghi "day_cheo: xong -> ${dich%%:*}"
}

don_nhan() {
    [ -d "$NHAN" ] || return 0
    find "$NHAN" -type f -mtime +"${SAOLUU_GIU_NHAN_NGAY:-14}" -delete
    find "$NHAN" -mindepth 1 -type d -empty -delete
    ghi "don_nhan: con $(find "$NHAN" -type f | wc -l) tep, $(du -sh "$NHAN" | cut -f1)"
}

len_drive() {
    local dich=${SAOLUU_DRIVE:-gdrive-mahoa:}
    rclone listremotes 2>/dev/null | grep -qx "${dich%%:*}:" || { ghi "BO QUA len_drive: chua cau hinh rclone ($dich)"; return 0; }
    local ds_nguon
    read -ra ds_nguon <<< "${SAOLUU_DRIVE_NGUON:?thiếu SAOLUU_DRIVE_NGUON}"; set -- "${ds_nguon[@]}"
    for cap in "$@"; do                    # dạng  thu_muc_nguon=>thu_muc_tren_drive
        rclone copy "${cap%%=>*}" "$dich${cap##*=>}" --max-age 26h --transfers 2 --retries 5 --low-level-retries 20
    done
    rclone delete "$dich" --min-age "${SAOLUU_GIU_DRIVE_NGAY:-30}d"
    date +%s > "$THU_MUC/.len-drive-lan-cuoi"
    ghi "len_drive: xong"
}

case "${1:-}" in
    csdl|he_thong|day_cheo|don_nhan|len_drive) "$1" ;;
    *) echo "Dung: $0 csdl|he_thong|day_cheo|don_nhan|len_drive" >&2; exit 2 ;;
esac
