63 lines
2.1 KiB
Python
63 lines
2.1 KiB
Python
"""每日 02:00 数据库备份 job —— 包装 backend/scripts/pg_backup.sh。
|
||||
|
|
|
|||
|
|
独立脚本可手动/CI 直跑;系统调度经此包装,保证与脚本同一份备份逻辑与轮转策略。
|
|||
|
|
Windows 下须显式定位 Git Bash(PATH 里的 bash 可能是 WSL shim,mount/PATH 均不匹配),
|
|||
|
|
脚本路径统一转 MSYS /d/... 格式传给子进程。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import asyncio
|
|||
|
|
import logging
|
|||
|
|
import shutil
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
# backend/app/utils/backup_job.py → parents[2] = backend/
|
|||
|
|
_SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "pg_backup.sh"
|
|||
|
|
|
|||
|
|
_GIT_BASH_CANDIDATES = (
|
|||
|
|
Path(r"C:\Program Files\Git\bin\bash.exe"),
|
|||
|
|
Path(r"C:\Program Files\Git\usr\bin\bash.exe"),
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _find_git_bash() -> str | None:
|
|||
|
|
for cand in _GIT_BASH_CANDIDATES:
|
|||
|
|
if cand.exists():
|
|||
|
|
return str(cand)
|
|||
|
|
return shutil.which("bash")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _msys_path(p: Path) -> str:
|
|||
|
|
"""D:/dpb/... → /d/dpb/...(Git Bash 挂载约定)。"""
|
|||
|
|
s = p.as_posix()
|
|||
|
|
if len(s) >= 2 and s[1] == ":":
|
|||
|
|
return f"/{s[0].lower()}{s[2:]}"
|
|||
|
|
return s
|
|||
|
|
|
|||
|
|
|
|||
|
|
async def run_daily_backup() -> bool:
|
|||
|
|
"""每日备份:调用 pg_backup.sh(独立脚本,pg_dump 自定义格式 + N 天轮转)。"""
|
|||
|
|
script = _SCRIPT
|
|||
|
|
if not script.exists():
|
|||
|
|
alt = Path.cwd() / "scripts" / "pg_backup.sh"
|
|||
|
|
script = alt if alt.exists() else script
|
|||
|
|
if not script.exists():
|
|||
|
|
logger.error(f"备份脚本不存在: {script}")
|
|||
|
|
return False
|
|||
|
|
git_bash = _find_git_bash()
|
|||
|
|
if not git_bash:
|
|||
|
|
logger.error("未找到 Git Bash(C:/Program Files/Git/bin/bash.exe)")
|
|||
|
|
return False
|
|||
|
|
proc = await asyncio.create_subprocess_exec(
|
|||
|
|
git_bash, _msys_path(script),
|
|||
|
|
stdout=asyncio.subprocess.PIPE,
|
|||
|
|
stderr=asyncio.subprocess.PIPE,
|
|||
|
|
)
|
|||
|
|
stdout, stderr = await proc.communicate()
|
|||
|
|
if proc.returncode == 0:
|
|||
|
|
logger.info(f"每日备份完成: {stdout.decode().strip()}")
|
|||
|
|
return True
|
|||
|
|
logger.error(f"每日备份失败(rc={proc.returncode}): {stderr.decode()[:500]}")
|
|||
|
|
return False
|