diff --git a/remote_fix.sh b/remote_fix.sh new file mode 100644 index 0000000..179a82c --- /dev/null +++ b/remote_fix.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +echo "=== Beeper Bridge Remote Fix Tool ===" +echo "Run this script ON THE REMOTE MACHINE where Docker is running." + +# Create the python repair script +cat << 'PYTHON_SCRIPT' > fix_configs.py +import sys +import os +import shutil + +# Install ruamel.yaml if missing +try: + from ruamel.yaml import YAML, YAMLError +except ImportError: + print("Installing ruamel.yaml...") + import subprocess + subprocess.check_call([sys.executable, "-m", "pip", "install", "ruamel.yaml"]) + from ruamel.yaml import YAML, YAMLError + +yaml = YAML() +yaml.preserve_quotes = True + +services = ["whatsapp", "telegram", "signal", "googlechat"] +base_dir = "data" + +# Docker-friendly logging config (Stdout/Stderr) +logging_config = { + "version": 1, + "formatters": { + "colored": { + "format": "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s", + "datefmt": "%b %d %H:%M:%S" + } + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "formatter": "colored", + "stream": "ext://sys.stdout" + } + }, + "root": { + "level": "INFO", + "handlers": ["console"] + } +} + +def fix_config(service): + config_path = os.path.join(base_dir, service, "config.yaml") + + if not os.path.exists(config_path): + print(f"[{service}] Config not found at {config_path}. Skipping.") + return + + print(f"[{service}] Checking config...") + + try: + with open(config_path, 'r') as f: + data = yaml.load(f) + + # If we reached here, YAML is valid. Apply patches. + modified = False + + # 1. Fix Database URI + if 'appservice' in data and 'database' in data['appservice']: + db_conf = data['appservice']['database'] + if isinstance(db_conf, str) and db_conf != "sqlite:////data/bridge.db": + data['appservice']['database'] = "sqlite:////data/bridge.db" + modified = True + elif isinstance(db_conf, dict) and db_conf.get('uri') != "sqlite:////data/bridge.db": + data['appservice']['database']['uri'] = "sqlite:////data/bridge.db" + modified = True + + if 'database' in data and isinstance(data['database'], dict): + if data['database'].get('uri') != "sqlite:////data/bridge.db": + data['database']['uri'] = "sqlite:////data/bridge.db" + modified = True + + # 2. Fix Logging (Always apply to ensure stdout is used) + # We check if it looks different to avoid unnecessary writes, + # but simplistic check is: is it using stream handler? + current_logging = data.get('logging', {}) + # Just overwrite it to be safe and ensure Docker compatibility + data['logging'] = logging_config + modified = True + + if modified: + print(f"[{service}] Applying fixes (Logging/Database)...") + with open(config_path, 'w') as f: + yaml.dump(data, f) + else: + print(f"[{service}] Config looked OK.") + + except (YAMLError, Exception) as e: + print(f"[{service}] ERROR: Config file is corrupt or invalid YAML.") + print(f" -> {e}") + backup_path = config_path + ".corrupt.bak" + print(f"[{service}] ACTION: Moving corrupt config to {backup_path}") + print(f"[{service}] ACTION: The container will regenerate a fresh valid config on next start.") + shutil.move(config_path, backup_path) + +# Main execution +if __name__ == "__main__": + for service in services: + fix_config(service) +PYTHON_SCRIPT + +echo "[*] Executing fix script using python:3.11-slim container..." +# We use a docker container to run the python script so we don't mess with host python +docker run --rm \ + -v "$(pwd):/work" \ + -w /work \ + python:3.11-slim \ + sh -c "pip install ruamel.yaml && python fix_configs.py" + +# Cleanup +rm fix_configs.py + +echo "=== Fix Complete ===" +echo "1. If Google Chat config was corrupt, it was backed up." +echo "2. Run 'docker compose restart' (or 'up -d') now." +echo "3. The bridges should generate fresh configs or use the fixed ones."