112 lines
4.0 KiB
Python
112 lines
4.0 KiB
Python
import os
|
|
import json
|
|
import secrets
|
|
from pathlib import Path
|
|
from typing import Dict, Any
|
|
|
|
# Project base directory: can be overridden via APP_DIR or auto-detected from file location
|
|
DEFAULT_APP_DIR = Path(__file__).resolve().parent.parent
|
|
BASE_DIR = Path(os.environ.get("APP_DIR", str(DEFAULT_APP_DIR)))
|
|
|
|
# Persistent data directory: holds settings, SSH keys, job history, and backup index
|
|
DATA_DIR = Path(os.environ.get("DATA_DIR", str(BASE_DIR / "data")))
|
|
|
|
CONFIG_FILE = DATA_DIR / "settings.json"
|
|
BACKUP_INDEX_FILE = DATA_DIR / "backups_index.json"
|
|
JOBS_FILE = DATA_DIR / "jobs_history.json"
|
|
DEFAULT_KEY_FILE = DATA_DIR / "id_ed25519"
|
|
DEFAULT_PUB_KEY_FILE = DATA_DIR / "id_ed25519.pub"
|
|
|
|
def get_default_backup_dir() -> str:
|
|
if "BACKUP_DIR" in os.environ and os.environ["BACKUP_DIR"].strip():
|
|
return os.environ["BACKUP_DIR"].strip()
|
|
if os.path.exists("/backups"):
|
|
return "/backups"
|
|
if os.path.exists("/mnt/user"):
|
|
return "/mnt/user/backups/pangolin"
|
|
if os.path.exists("/var/backups"):
|
|
return "/var/backups/pangolin"
|
|
return str(DATA_DIR / "backups")
|
|
|
|
DEFAULT_LOCAL_BACKUP = get_default_backup_dir()
|
|
|
|
DEFAULT_SETTINGS = {
|
|
"auth_enabled": True,
|
|
"admin_password_hash": "",
|
|
"admin_password_salt": "",
|
|
"jwt_secret": secrets.token_hex(32),
|
|
|
|
# Remote VPS Configuration
|
|
"vps_host": "", # IP or Hostname of the Pangolin VPS
|
|
"vps_port": 22, # SSH Port
|
|
"vps_user": "root", # SSH Username
|
|
"vps_auth_method": "key", # "key" or "password"
|
|
"vps_ssh_key_path": str(DEFAULT_KEY_FILE),
|
|
"vps_ssh_password": "", # SSH Password (if method == "password")
|
|
"vps_pangolin_dir": "/opt/pangolin", # Path to Pangolin on remote VPS
|
|
|
|
# Local Unraid Storage & Retention
|
|
"backup_dir": DEFAULT_LOCAL_BACKUP,
|
|
"retention_count": 10,
|
|
|
|
# Automation & Scheduler
|
|
"scheduled_backup_enabled": True,
|
|
"scheduled_backup_cron": "0 3 * * *",
|
|
"scheduled_backup_scope": "pangolin",
|
|
"scheduled_update_check_enabled": True,
|
|
|
|
# Webhooks
|
|
"webhook_url": "",
|
|
"webhook_type": "discord",
|
|
"telegram_bot_token": "",
|
|
"telegram_chat_id": "",
|
|
"notify_on_backup": True,
|
|
"notify_on_update": True,
|
|
|
|
# App daemon
|
|
"app_port": int(os.environ.get("PORT", 8888)),
|
|
"app_host": os.environ.get("HOST", "0.0.0.0")
|
|
}
|
|
|
|
def load_settings() -> Dict[str, Any]:
|
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
if not CONFIG_FILE.exists():
|
|
save_settings(DEFAULT_SETTINGS)
|
|
return DEFAULT_SETTINGS.copy()
|
|
|
|
try:
|
|
with open(CONFIG_FILE, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
for k, v in DEFAULT_SETTINGS.items():
|
|
if k not in data:
|
|
data[k] = v
|
|
|
|
# Auto-heal SSH key path if configured path does not exist but default key does
|
|
cur_key_path = data.get("vps_ssh_key_path", "")
|
|
if (not cur_key_path or not os.path.exists(cur_key_path)) and DEFAULT_KEY_FILE.exists():
|
|
data["vps_ssh_key_path"] = str(DEFAULT_KEY_FILE)
|
|
|
|
# Auto-heal backup_dir if set to old non-existent path and /backups is mounted in container
|
|
cur_backup_dir = data.get("backup_dir", "")
|
|
if os.path.exists("/backups") and (not cur_backup_dir or not os.path.exists(cur_backup_dir)):
|
|
data["backup_dir"] = "/backups"
|
|
|
|
return data
|
|
except Exception as e:
|
|
print(f"Error loading settings: {e}. Using defaults.")
|
|
return DEFAULT_SETTINGS.copy()
|
|
|
|
def save_settings(settings: Dict[str, Any]) -> None:
|
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
with open(CONFIG_FILE, "w", encoding="utf-8") as f:
|
|
json.dump(settings, f, indent=2)
|
|
|
|
def get_setting(key: str, default: Any = None) -> Any:
|
|
settings = load_settings()
|
|
return settings.get(key, default)
|
|
|
|
def update_setting(key: str, value: Any) -> None:
|
|
settings = load_settings()
|
|
settings[key] = value
|
|
save_settings(settings)
|