diff --git a/docker-compose.yml b/docker-compose.yml index 544a3f9..b7eb08f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,34 +1,31 @@ version: "3.9" services: - savefiles_logger: + gamewatcher: image: python:3.11-slim - container_name: savefiles_logger - working_dir: /app + container_name: gamewatcher + working_dir: /data/app command: ["python3", "watcher.py"] restart: unless-stopped volumes: - # Código - - /home/sergio/gitea/savefiles_logger:/app - - # Datos persistentes (backups + logs) - - savefiles_data:/app/data + # Volumen persistente con scripts y backups + - gamewatcher_data:/data # Carpeta de saves del host - /sustancia/home/saves:/saves healthcheck: - test: ["CMD", "python3", "-c", "import os; exit(0 if os.path.exists('/app/watcher.py') else 1)"] + test: ["CMD", "python3", "-c", "import os; exit(0 if os.path.exists('/data/app/watcher.py') else 1)"] interval: 30s timeout: 5s retries: 3 start_period: 10s volumes: - savefiles_data: + gamewatcher_data: driver: local driver_opts: type: none o: bind - device: /var/volumes/savefiles_logger + device: /var/volumes/gamewatcher diff --git a/watcher.py b/watcher.py index ac32cb6..226ca7d 100644 --- a/watcher.py +++ b/watcher.py @@ -2,42 +2,23 @@ import os import time import shutil import hashlib -import logging from datetime import datetime from files import FILES -CHECK_INTERVAL = 60 # segundos +CHECK_INTERVAL = 300 # 5 minutos -# Rutas base (dentro del contenedor) -DATA_DIR = "data" +# Rutas dentro del contenedor +DATA_DIR = "/data" +APP_DIR = os.path.join(DATA_DIR, "app") BACKUPS_ROOT = os.path.join(DATA_DIR, "backups") -LOGS_DIR = os.path.join(DATA_DIR, "logs") os.makedirs(BACKUPS_ROOT, exist_ok=True) -os.makedirs(LOGS_DIR, exist_ok=True) -# Configuración de logging -LOG_FILE = os.path.join(LOGS_DIR, "watcher.log") -logging.basicConfig( - filename=LOG_FILE, - level=logging.INFO, - format="%(asctime)s [%(levelname)s] %(message)s" -) - -def log_info(msg): - print(msg) - logging.info(msg) - -def log_warn(msg): - print(msg) - logging.warning(msg) - -def log_error(msg): - print(msg) - logging.error(msg) +def log(msg): + ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + print(f"[{ts}] {msg}", flush=True) def sha256sum(path): - """Devuelve el hash SHA256 del fichero.""" h = hashlib.sha256() with open(path, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): @@ -45,7 +26,6 @@ def sha256sum(path): return h.hexdigest() def ensure_backup_dir(file_path): - """Crea data/backups// si no existe.""" file_name = os.path.basename(file_path) name, _ = os.path.splitext(file_name) backup_dir = os.path.join(BACKUPS_ROOT, name) @@ -53,7 +33,6 @@ def ensure_backup_dir(file_path): return backup_dir def get_latest_backup(file_path): - """Devuelve la ruta del backup más reciente o None si no hay.""" backup_dir = ensure_backup_dir(file_path) files = sorted(os.listdir(backup_dir), reverse=True) if not files: @@ -61,25 +40,23 @@ def get_latest_backup(file_path): return os.path.join(backup_dir, files[0]) def backup_file(file_path): - """Crea una copia con timestamp.""" backup_dir = ensure_backup_dir(file_path) file_name = os.path.basename(file_path) name, ext = os.path.splitext(file_name) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") dest = os.path.join(backup_dir, f"{name}_{timestamp}{ext}") shutil.copy2(file_path, dest) - log_info(f"[BACKUP] Copia creada: {dest}") + log(f"[BACKUP] Copia creada: {dest}") def process_file(file_path): - """Comprueba si hay cambios y crea backup si es necesario.""" if not os.path.isfile(file_path): - log_warn(f"[WARN] No existe: {file_path}") + log(f"[WARN] No existe: {file_path}") return latest = get_latest_backup(file_path) if latest is None: - log_info(f"[INIT] No hay copia previa de {file_path}, creando primera copia...") + log(f"[INIT] Primera copia de {file_path}") backup_file(file_path) return @@ -87,19 +64,18 @@ def process_file(file_path): current_hash = sha256sum(file_path) latest_hash = sha256sum(latest) except Exception as e: - log_error(f"[ERROR] Calculando hash: {e}") + log(f"[ERROR] Hash: {e}") return if current_hash != latest_hash: - log_info(f"[CHANGE] Detectado cambio en {file_path}, creando nueva copia...") + log(f"[CHANGE] Detectado cambio en {file_path}") backup_file(file_path) else: - log_info(f"[OK] Sin cambios en {file_path}") + log(f"[OK] Sin cambios en {file_path}") def main(): - log_info("Iniciando watcher de saves...") - log_info(f"Backups en: {os.path.abspath(BACKUPS_ROOT)}") - log_info(f"Logs en: {os.path.abspath(LOGS_DIR)}") + log("GameWatcher iniciado") + log(f"Backups en: {BACKUPS_ROOT}") while True: for f in FILES: