114 lines
3.5 KiB
Python
114 lines
3.5 KiB
Python
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(),
|
|
}
|