diff --git a/.env.example b/.env.example index 86ab20d..d522b90 100644 --- a/.env.example +++ b/.env.example @@ -41,15 +41,15 @@ TZ=Europe/Rome # { # "id": "sync_identifier", # "bucket": "bucket-name", -# "path": "/mount/path/in/container", +# "local_path": "/mount/path/in/container", # "interval": 300 # } # # Esempio con 2 sync: # SYNC_CONFIGS='[ - {"id": "sync1", "bucket": "bucket-a", "path": "/data/local", "interval": 300}, - {"id": "sync2", "bucket": "bucket-b", "path": "/data/local2", "interval": 600} + {"id": "sync1", "bucket": "bucket-a", "local_path": "/data/local1", "interval": 300}, + {"id": "sync2", "bucket": "bucket-b", "local_path": "/data/local2", "interval": 600} ]' # ============================================================ @@ -67,8 +67,12 @@ GOTIFY_PRIORITY=5 # multiple sync nel docker-compose.yml: # # volumes: -# - ./data/sync1:/data/local # Primo sync +# - ./data/sync1:/data/local1 # Primo sync # - ./data/sync2:/data/local2 # Secondo sync # - sync-state:/data/state # Stato condiviso # +LOCAL_SYNC_PATH_1=./data/sync1 +LOCAL_SYNC_PATH_2=./data/sync2 + +# Fallback legacy (sync singola) LOCAL_SYNC_PATH=./data diff --git a/docker-compose.yml b/docker-compose.yml index f9bee4b..f003844 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,8 +13,8 @@ # # Formato .env per multiple sync: # SYNC_CONFIGS='[ -# {"id":"sync1", "bucket":"bucket-a", "path":"/data/sync1", "interval":300}, -# {"id":"sync2", "bucket":"bucket-b", "path":"/data/sync2", "interval":600} +# {"id":"sync1", "bucket":"bucket-a", "local_path":"/data/local1", "interval":300}, +# {"id":"sync2", "bucket":"bucket-b", "local_path":"/data/local2", "interval":600} # ]' # ============================================================ @@ -61,9 +61,12 @@ services: - TZ=${TZ:-Europe/Rome} # Volumi: - # - Cartella locale per i file sincronizzati + # - Cartelle locali per sync multiple (default) + # - Cartella locale legacy per sync singola # - Volume per lo stato interno (persistente tra i restart) volumes: + - ${LOCAL_SYNC_PATH_1:-./data/sync1}:/data/local1 + - ${LOCAL_SYNC_PATH_2:-./data/sync2}:/data/local2 - ${LOCAL_SYNC_PATH:-./data}:/data/local - sync-state:/data/state diff --git a/entrypoint.sh b/entrypoint.sh index 1947782..8dc35e8 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -46,7 +46,6 @@ else import json import os import subprocess -import signal import time sync_configs_json = os.environ.get('SYNC_CONFIGS', '[]') @@ -64,18 +63,33 @@ print(f"[ENTRYPOINT] Avvio {len(configs)} sync...") processes = [] for i, cfg in enumerate(configs): - sync_id = cfg.get('id', f'sync{i}') - bucket = cfg.get('bucket', 'default') + sync_id = cfg.get('id', f'sync{i+1}') + bucket = cfg.get('bucket') + local_path = cfg.get('local_path') or cfg.get('path') or f'/data/local{i+1}' + interval = str(cfg.get('interval', os.environ.get('SYNC_INTERVAL', '300'))) + prefix = cfg.get('prefix', os.environ.get('S3_PATH_PREFIX', '')) + schedule = cfg.get('schedule', os.environ.get('SYNC_SCHEDULE', '')) + + if not bucket: + print(f"[ERROR] Config sync '{sync_id}' senza campo 'bucket'") + exit(1) + state_dir = f"/data/state/{sync_id}" - print(f"[ENTRYPOINT] Avvio sync {i+1}/{len(configs)}: {sync_id} (bucket: {bucket})") + print(f"[ENTRYPOINT] Avvio sync {i+1}/{len(configs)}: {sync_id} (bucket: {bucket}, local: {local_path})") # Crea directory di stato os.makedirs(state_dir, exist_ok=True) + os.makedirs(local_path, exist_ok=True) # Lancia sync.sh in background con STATE_DIR specifico env = os.environ.copy() env['STATE_DIR'] = state_dir + env['S3_BUCKET'] = bucket + env['LOCAL_PATH'] = local_path + env['SYNC_INTERVAL'] = interval + env['S3_PATH_PREFIX'] = prefix + env['SYNC_SCHEDULE'] = schedule proc = subprocess.Popen( ['/usr/local/bin/sync.sh'], diff --git a/sync.sh b/sync.sh index 305dae6..b27b65a 100644 --- a/sync.sh +++ b/sync.sh @@ -411,7 +411,8 @@ SYNC_BANDWIDTH="${SYNC_BANDWIDTH:-0}" SYNC_LOG_LEVEL="${SYNC_LOG_LEVEL:-INFO}" SYNC_ON_START="${SYNC_ON_START:-true}" -LOCAL_PATH="/data/local" +LOCAL_PATH="${LOCAL_PATH:-/data/local}" +mkdir -p "${LOCAL_PATH}" # --- Helper cron (script Python per il parsing delle espressioni) --- CRON_HELPER="/app/web/cron_helper.py" diff --git a/web/app.py b/web/app.py index 906c258..0b8c230 100644 --- a/web/app.py +++ b/web/app.py @@ -40,7 +40,17 @@ def load_sync_configs(): if sync_configs_json: try: configs = json.loads(sync_configs_json) - return {cfg.get("id", f"sync{i}"): cfg for i, cfg in enumerate(configs)} + parsed = {} + for i, cfg in enumerate(configs): + sync_id = cfg.get("id", f"sync{i+1}") + parsed[sync_id] = { + "id": sync_id, + "bucket": cfg.get("bucket", "default"), + "prefix": cfg.get("prefix", ""), + "local_path": cfg.get("local_path") or cfg.get("path") or f"/data/local{i+1}", + } + if parsed: + return parsed except json.JSONDecodeError: pass @@ -49,7 +59,8 @@ def load_sync_configs(): "sync1": { "id": "sync1", "bucket": os.environ.get("S3_BUCKET", "default"), - "state_dir": STATE_DIR / "sync1" + "prefix": os.environ.get("S3_PATH_PREFIX", ""), + "local_path": os.environ.get("LOCAL_PATH", "/data/local"), } } @@ -57,9 +68,6 @@ SYNC_CONFIGS = load_sync_configs() def get_sync_state_dir(sync_id): """Ritorna la directory di stato per una specifica sync.""" - cfg = SYNC_CONFIGS.get(sync_id) - if cfg and "state_dir" in cfg: - return Path(cfg["state_dir"]) return STATE_DIR / sync_id def get_sync_bucket(sync_id): @@ -67,6 +75,19 @@ def get_sync_bucket(sync_id): cfg = SYNC_CONFIGS.get(sync_id) return cfg.get("bucket", sync_id) if cfg else sync_id +def get_sync_local_path(sync_id): + """Ritorna la cartella locale della sync specifica.""" + cfg = SYNC_CONFIGS.get(sync_id) + if cfg: + return cfg.get("local_path", "/data/local") + return "/data/local" + +def get_default_sync_id(): + """Ritorna il primo sync id disponibile.""" + if SYNC_CONFIGS: + return next(iter(SYNC_CONFIGS.keys())) + return "sync1" + # ============================================================ # Funzioni di utilità # ============================================================ @@ -123,6 +144,8 @@ def index(): """Pagina principale della dashboard con tab untuk multi-sync.""" sync_interval = int(os.environ.get("SYNC_INTERVAL", 300)) sync_schedule = os.environ.get("SYNC_SCHEDULE", "") + default_sync = get_default_sync_id() + default_cfg = SYNC_CONFIGS.get(default_sync, {}) if sync_schedule: from cron_helper import human_readable @@ -134,6 +157,8 @@ def index(): config = { "endpoint": os.environ.get("S3_ENDPOINT", "N/A"), + "bucket": default_cfg.get("bucket", os.environ.get("S3_BUCKET", "N/A")), + "prefix": default_cfg.get("prefix", os.environ.get("S3_PATH_PREFIX", "")) or "(tutto il bucket)", "sync_mode": os.environ.get("SYNC_MODE", "mirror"), "sync_interval": sync_interval, "sync_schedule": sync_schedule, @@ -174,10 +199,16 @@ def api_status(sync_id): # Aggiungi statistiche della cartella locale status["bucket"] = get_sync_bucket(sync_id) - status["folder_stats"] = get_folder_stats() + status["folder_stats"] = get_folder_stats(get_sync_local_path(sync_id)) return jsonify(status) +@app.route("/api/status") +def api_status_default(): + """Compat: stato della sync di default.""" + return api_status(get_default_sync_id()) + + @app.route("/api/changes/") def api_changes(sync_id): """API: restituisce le ultime modifiche ai file per una sync.""" @@ -187,6 +218,12 @@ def api_changes(sync_id): return jsonify(changes) +@app.route("/api/changes") +def api_changes_default(): + """Compat: modifiche della sync di default.""" + return api_changes(get_default_sync_id()) + + @app.route("/api/history/") def api_history(sync_id): """API: restituisce lo storico delle sincronizzazioni.""" @@ -196,6 +233,12 @@ def api_history(sync_id): return jsonify(history) +@app.route("/api/history") +def api_history_default(): + """Compat: storico della sync di default.""" + return api_history(get_default_sync_id()) + + @app.route("/api/stream/") def api_stream(sync_id): """SSE: stream in tempo reale dei log della sincronizzazione.""" @@ -240,6 +283,12 @@ def api_stream(sync_id): ) +@app.route("/api/stream") +def api_stream_default(): + """Compat: stream log della sync di default.""" + return api_stream(get_default_sync_id()) + + # ============================================================ # Avvio server # ============================================================