import json import os _APP_CONFIG_PATH = '/data/app_config.json' _app_config_cache = None _app_config_mtime = None def _load_app_config(): global _app_config_cache, _app_config_mtime try: mtime = os.path.getmtime(_APP_CONFIG_PATH) if _app_config_cache is not None and mtime == _app_config_mtime: return _app_config_cache with open(_APP_CONFIG_PATH) as f: _app_config_cache = json.load(f) _app_config_mtime = mtime return _app_config_cache except Exception: return {} def product_name(): return os.environ.get('PRODUCT_NAME', 'routlin') def web_app_display_name(): edition = 'Pro' if is_pro() else 'CE' return os.environ.get('WEB_APP_DISPLAY_NAME', f'{product_name().capitalize()}-{edition} Dashboard') def is_production(): return not os.environ.get('DEV_MODE', '').lower() in ('1', 'true', 'yes') def routlin_location(): return os.environ.get('ROUTLIN_LOCATION', '/routlin_location') def is_pro(): try: return bool(open(f'{routlin_location()}/.license').read().strip()) except OSError: return False def get_host_utc_offset(): # Returns signed integer seconds east of UTC (e.g. -21600 for UTC-6, +19800 for UTC+5:30). import time return time.localtime().tm_gmtoff def get_client_utc_offset(timezone_str): """Return signed integer UTC offset in seconds for the given IANA timezone string.""" try: from zoneinfo import ZoneInfo from datetime import datetime return int(datetime.now(ZoneInfo(timezone_str)).utcoffset().total_seconds()) except Exception: return get_host_utc_offset() def get_host_timezone(): """Return the host timezone name (e.g. 'America/New_York'), or '' if unknown.""" tz = os.environ.get('TZ', '').strip() if tz: return tz try: with open('/etc/timezone') as f: return f.read().strip() except OSError: pass return '' def get_initial_manager_email(): cfg = _load_app_config() return str(cfg.get('initial_manager_email') or os.environ.get('INITIAL_MANAGER_EMAIL', '')).strip().lower() def is_single_user(): return 'initial_manager_password' in _load_app_config() def get_initial_manager_password_hash(): return _load_app_config().get('initial_manager_password', '') def get_credentials_key(): """Return a Fernet-compatible key derived from the credentials_key in app_config.json (or CREDENTIALS_KEY env var as fallback), or None if not set. SHA-256 hashes the raw string to produce 32 bytes, URL-safe base64-encoded as required by Fernet.""" import base64 import hashlib cfg = _load_app_config() key_str = str(cfg.get('credentials_key') or os.environ.get('CREDENTIALS_KEY', '')).strip() if not key_str: return None raw = hashlib.sha256(key_str.encode()).digest() return base64.urlsafe_b64encode(raw) def get_smtp_config(): """Return SMTP settings from app_config.json, falling back to env vars.""" cfg = _load_app_config() smtp = cfg.get('smtp', {}) user = str(smtp.get('user') or os.environ.get('SMTP_USER', '')).strip() return { 'host': str(smtp.get('host') or os.environ.get('SMTP_HOST', '')).strip(), 'port': int(smtp.get('port') or os.environ.get('SMTP_PORT', 587)), 'user': user, 'password': str(smtp.get('password') or os.environ.get('SMTP_PASSWORD', '')).strip(), 'from': str(smtp.get('from') or os.environ.get('SMTP_FROM', user)).strip(), }