Files
mysqlbackup/10 Backup/10 KanboardBackup.py
2026-01-28 09:51:46 +01:00

125 lines
3.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import gzip
import shutil
import subprocess
from datetime import datetime, timedelta
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
MYSQLDUMP = PROJECT_ROOT / "bin" / "mysqldump.exe"
def load_dotenv(dotenv_path: Path) -> None:
if not dotenv_path.exists():
return
for line in dotenv_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
k, v = line.split("=", 1)
k = k.strip()
v = v.strip().strip('"').strip("'")
os.environ.setdefault(k, v)
def require_env(name: str) -> str:
v = os.getenv(name)
if not v:
raise SystemExit(f"Chybí proměnná {name} (dej ji do .env nebo env).")
return v
def run(cmd: list[str], *, cwd: Path | None = None) -> None:
# pro ladění si můžeš odkomentovat print(cmd)
p = subprocess.run(cmd, cwd=str(cwd) if cwd else None)
if p.returncode != 0:
raise SystemExit(f"Příkaz selhal (exit={p.returncode}): {' '.join(cmd)}")
def main() -> None:
here = Path(__file__).resolve().parent
load_dotenv(here / ".env")
host = require_env("KB_MYSQL_HOST")
port = require_env("KB_MYSQL_PORT")
user = require_env("KB_MYSQL_USER")
password = require_env("KB_MYSQL_PASSWORD")
db = require_env("KB_MYSQL_DB")
backup_dir = Path(os.getenv("KB_BACKUP_DIR", "./backups")).resolve()
keep_days = int(os.getenv("KB_KEEP_DAYS", "30"))
backup_dir.mkdir(parents=True, exist_ok=True)
ts = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
sql_path = backup_dir / f"kanboard_{db}_{ts}.sql"
gz_path = backup_dir / f"{sql_path.name}.gz"
# Bezpečně: vytvoříme dočasný mysql config soubor, aby heslo nebylo v "ps"
cnf_path = backup_dir / f".mysql_{db}_{ts}.cnf"
cnf_path.write_text(
"[client]\n"
f"host={host}\n"
f"port={port}\n"
f"user={user}\n"
f"password={password}\n",
encoding="utf-8"
)
try:
# omezit práva (na Linuxu/Unraid ok; na Windows to ignoruj)
try:
os.chmod(cnf_path, 0o600)
except Exception:
pass
# Mysqldump kompletní pro obnovu
dump_cmd = [
str(MYSQLDUMP),
f"--defaults-extra-file={str(cnf_path)}",
"--single-transaction",
"--routines",
"--triggers",
"--events",
"--hex-blob",
"--default-character-set=utf8mb4",
"--set-gtid-purged=OFF",
"--databases", db,
]
print(f"[OK] Dělám dump DB '{db}' -> {gz_path}")
# Dump do .sql a rovnou gzip
with subprocess.Popen(dump_cmd, stdout=subprocess.PIPE) as proc:
if proc.stdout is None:
raise SystemExit("Nepodařilo se získat stdout z mysqldump.")
with gzip.open(gz_path, "wb", compresslevel=6) as gz:
shutil.copyfileobj(proc.stdout, gz)
rc = proc.wait()
if rc != 0:
raise SystemExit(f"mysqldump selhal (exit={rc}).")
# Malá kontrola velikosti
size = gz_path.stat().st_size
if size < 200: # hodně hrubé
raise SystemExit(f"Dump je podezřele malý ({size} B) něco je špatně.")
# Retence
cutoff = datetime.now() - timedelta(days=keep_days)
for f in backup_dir.glob("kanboard_*_*.sql.gz"):
try:
if datetime.fromtimestamp(f.stat().st_mtime) < cutoff:
f.unlink(missing_ok=True)
except Exception:
pass
print(f"[OK] Hotovo. Velikost: {size/1024/1024:.2f} MB")
finally:
try:
cnf_path.unlink(missing_ok=True)
except Exception:
pass
if __name__ == "__main__":
main()