"""Run inside the configured source container. Export configuration, NEVER data. Usage: python prepare_production_settings.py /secure/new-directory Creates new files exclusively with mode 0600. No secrets go to stdout. """ import json import os from pathlib import Path import secrets import sys from cryptography.fernet import Fernet from app.runtime import get_runtime_settings def prepare(destination: Path) -> None: runtime = get_runtime_settings() keys = [ 'jellyfin_base_url', 'jellyfin_api_key', 'jellyfin_public_url', 'jellyseerr_base_url', 'jellyseerr_api_key', 'sonarr_base_url', 'sonarr_api_key', 'radarr_base_url', 'radarr_api_key', 'prowlarr_base_url', 'prowlarr_api_key', 'bazarr_base_url', 'bazarr_api_key', 'qbittorrent_base_url', 'qbittorrent_username', 'qbittorrent_password', 'magent_notify_enabled', 'magent_notify_email_enabled', 'magent_notify_email_smtp_host', 'magent_notify_email_smtp_port', 'magent_notify_email_smtp_username', 'magent_notify_email_smtp_password', 'magent_notify_email_from_address', 'magent_notify_email_from_name', 'magent_notify_email_use_tls', 'magent_notify_email_use_ssl', ] values = {key.upper(): getattr(runtime, key) for key in keys if getattr(runtime, key, None) is not None} password = secrets.token_urlsafe(30) values.update( APP_NAME='Magent', JWT_SECRET=secrets.token_urlsafe(48), SETTINGS_ENCRYPTION_KEY=Fernet.generate_key().decode('ascii'), ADMIN_USERNAME='admin', ADMIN_PASSWORD=password, AUTH_COOKIE_SECURE=True, AUTH_COOKIE_DOMAIN='magent.grizzlyflix.co.nz', AUTH_COOKIE_NAME='magent_auth', AUTH_STATE_COOKIE_NAME='magent_logged_in', CORS_ALLOW_ORIGIN='https://magent.grizzlyflix.co.nz', MAGENT_APPLICATION_URL='https://magent.grizzlyflix.co.nz', MAGENT_API_URL='https://magent.grizzlyflix.co.nz/api', SQLITE_PATH='/app/data/magent.db', LOG_FILE='/app/data/magent.log', SITE_BANNER_ENABLED=False, MAGENT_COMING_SOON=True, BACKGROUND_TASKS_ENABLED=False, ) destination.mkdir(mode=0o700, parents=True, exist_ok=False) def write_private(name, content): with os.fdopen(os.open(destination / name, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600), 'w') as stream: stream.write(content) # Compose single-quoted values preserve dollar signs in SMTP passwords. def encode(value): text = str(value).lower() if isinstance(value, bool) else str(value) if '\n' in text or '\r' in text: raise ValueError('Multiline configuration values require manual review') return "'" + text.replace('\\', '\\\\').replace("'", "\\'") + "'" write_private('.env', ''.join(f'{key}={encode(value)}\n' for key, value in values.items())) write_private('bootstrap-admin.json', json.dumps({'username': 'admin', 'password': password})) print(f'Prepared {len(keys)} allowlisted connection settings; fresh session and admin credentials. No client records copied.') if __name__ == '__main__': prepare(Path(sys.argv[1]))