from __future__ import annotations import os def _env_bool(name: str, default: str = "false") -> bool: return os.getenv(name, default).lower() in ("1", "true", "yes") MONERO_GET_INFO_URL = os.getenv("MONERO_GET_INFO_URL", "http://monerod.crypto.svc.cluster.local:18081/get_info") VM_BASE_URL = os.getenv( "VM_BASE_URL", "http://victoria-metrics-single-server.monitoring.svc.cluster.local:8428", ).rstrip("/") VM_QUERY_TIMEOUT_SEC = float(os.getenv("VM_QUERY_TIMEOUT_SEC", "2")) HTTP_CHECK_TIMEOUT_SEC = float(os.getenv("HTTP_CHECK_TIMEOUT_SEC", "2")) K8S_API_TIMEOUT_SEC = float(os.getenv("K8S_API_TIMEOUT_SEC", "5")) LAB_STATUS_CACHE_SEC = float(os.getenv("LAB_STATUS_CACHE_SEC", "30")) GRAFANA_HEALTH_URL = os.getenv("GRAFANA_HEALTH_URL", "http://grafana.monitoring.svc.cluster.local/api/health") OCEANUS_NODE_EXPORTER_URL = os.getenv("OCEANUS_NODE_EXPORTER_URL", "http://192.168.22.24:9100/metrics") AI_CHAT_API = os.getenv("AI_CHAT_API", "http://ollama.ai.svc.cluster.local:11434").rstrip("/") AI_CHAT_MODEL = os.getenv("AI_CHAT_MODEL", "qwen2.5-coder:7b-instruct-q4_0") AI_CHAT_SYSTEM_PROMPT = os.getenv( "AI_CHAT_SYSTEM_PROMPT", "You are the Titan Lab assistant for bstein.dev. Be concise and helpful.", ) AI_CHAT_TIMEOUT_SEC = float(os.getenv("AI_CHAT_TIMEOUT_SEC", "20")) AI_NODE_NAME = os.getenv("AI_CHAT_NODE_NAME") or os.getenv("AI_NODE_NAME") or "ai-cluster" AI_GPU_DESC = os.getenv("AI_CHAT_GPU_DESC") or "local GPU (dynamic)" AI_PUBLIC_ENDPOINT = os.getenv("AI_PUBLIC_CHAT_ENDPOINT", "https://chat.ai.bstein.dev/api/chat") AI_K8S_LABEL = os.getenv("AI_K8S_LABEL", "app=ollama") AI_K8S_NAMESPACE = os.getenv("AI_K8S_NAMESPACE", "ai") AI_MODEL_ANNOTATION = os.getenv("AI_MODEL_ANNOTATION", "ai.bstein.dev/model") AI_GPU_ANNOTATION = os.getenv("AI_GPU_ANNOTATION", "ai.bstein.dev/gpu") AI_WARM_INTERVAL_SEC = float(os.getenv("AI_WARM_INTERVAL_SEC", "300")) AI_WARM_ENABLED = _env_bool("AI_WARM_ENABLED", "true") KEYCLOAK_ENABLED = _env_bool("KEYCLOAK_ENABLED", "false") KEYCLOAK_URL = os.getenv("KEYCLOAK_URL", "https://sso.bstein.dev").rstrip("/") KEYCLOAK_REALM = os.getenv("KEYCLOAK_REALM", "atlas") KEYCLOAK_CLIENT_ID = os.getenv("KEYCLOAK_CLIENT_ID", "bstein-dev-home") KEYCLOAK_ISSUER = os.getenv("KEYCLOAK_ISSUER", f"{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}").rstrip("/") KEYCLOAK_JWKS_URL = os.getenv("KEYCLOAK_JWKS_URL", f"{KEYCLOAK_ISSUER}/protocol/openid-connect/certs").rstrip("/") KEYCLOAK_ADMIN_URL = os.getenv("KEYCLOAK_ADMIN_URL", KEYCLOAK_URL).rstrip("/") KEYCLOAK_ADMIN_CLIENT_ID = os.getenv("KEYCLOAK_ADMIN_CLIENT_ID", "") KEYCLOAK_ADMIN_CLIENT_SECRET = os.getenv("KEYCLOAK_ADMIN_CLIENT_SECRET", "") KEYCLOAK_ADMIN_REALM = os.getenv("KEYCLOAK_ADMIN_REALM", KEYCLOAK_REALM) ARIADNE_URL = os.getenv("ARIADNE_URL", "").strip() ARIADNE_TIMEOUT_SEC = float(os.getenv("ARIADNE_TIMEOUT_SEC", "10")) ARIADNE_RETRY_COUNT = int(os.getenv("ARIADNE_RETRY_COUNT", "2")) ARIADNE_RETRY_BACKOFF_SEC = float(os.getenv("ARIADNE_RETRY_BACKOFF_SEC", "0.2")) ACCOUNT_ALLOWED_GROUPS = [ g.strip() for g in os.getenv("ACCOUNT_ALLOWED_GROUPS", "dev,admin").split(",") if g.strip() ] PORTAL_DATABASE_URL = os.getenv("PORTAL_DATABASE_URL", "").strip() PORTAL_ADMIN_USERS = [u.strip() for u in os.getenv("PORTAL_ADMIN_USERS", "bstein").split(",") if u.strip()] PORTAL_ADMIN_GROUPS = [g.strip() for g in os.getenv("PORTAL_ADMIN_GROUPS", "admin").split(",") if g.strip()] DEFAULT_USER_GROUPS = [g.strip() for g in os.getenv("DEFAULT_USER_GROUPS", "dev").split(",") if g.strip()] ACCESS_REQUEST_ENABLED = _env_bool("ACCESS_REQUEST_ENABLED", "true") ACCESS_REQUEST_RATE_LIMIT = int(os.getenv("ACCESS_REQUEST_RATE_LIMIT", "5")) ACCESS_REQUEST_RATE_WINDOW_SEC = int(os.getenv("ACCESS_REQUEST_RATE_WINDOW_SEC", str(60 * 60))) ACCESS_REQUEST_SUBMIT_RATE_LIMIT = int( os.getenv("ACCESS_REQUEST_SUBMIT_RATE_LIMIT", str(ACCESS_REQUEST_RATE_LIMIT)) ) ACCESS_REQUEST_SUBMIT_RATE_WINDOW_SEC = int( os.getenv("ACCESS_REQUEST_SUBMIT_RATE_WINDOW_SEC", str(ACCESS_REQUEST_RATE_WINDOW_SEC)) ) ACCESS_REQUEST_STATUS_RATE_LIMIT = int(os.getenv("ACCESS_REQUEST_STATUS_RATE_LIMIT", "60")) ACCESS_REQUEST_STATUS_RATE_WINDOW_SEC = int(os.getenv("ACCESS_REQUEST_STATUS_RATE_WINDOW_SEC", "60")) ACCESS_REQUEST_EMAIL_VERIFY_TTL_SEC = int(os.getenv("ACCESS_REQUEST_EMAIL_VERIFY_TTL_SEC", str(24 * 60 * 60))) ACCESS_REQUEST_INTERNAL_EMAIL_ALLOWLIST = { address.strip().lower() for address in os.getenv("ACCESS_REQUEST_INTERNAL_EMAIL_ALLOWLIST", "").split(",") if address.strip() } ACCESS_REQUEST_PROVISION_RETRY_COOLDOWN_SEC = float( os.getenv("ACCESS_REQUEST_PROVISION_RETRY_COOLDOWN_SEC", "30") ) PORTAL_PUBLIC_BASE_URL = os.getenv("PORTAL_PUBLIC_BASE_URL", "https://bstein.dev").rstrip("/") MAILU_DOMAIN = os.getenv("MAILU_DOMAIN", "bstein.dev") MAILU_SYNC_URL = os.getenv( "MAILU_SYNC_URL", "http://mailu-sync-listener.mailu-mailserver.svc.cluster.local:8080/events", ).rstrip("/") NEXTCLOUD_NAMESPACE = os.getenv("NEXTCLOUD_NAMESPACE", "nextcloud").strip() NEXTCLOUD_MAIL_SYNC_CRONJOB = os.getenv("NEXTCLOUD_MAIL_SYNC_CRONJOB", "nextcloud-mail-sync").strip() NEXTCLOUD_MAIL_SYNC_WAIT_TIMEOUT_SEC = float(os.getenv("NEXTCLOUD_MAIL_SYNC_WAIT_TIMEOUT_SEC", "90")) NEXTCLOUD_MAIL_SYNC_JOB_TTL_SEC = int(os.getenv("NEXTCLOUD_MAIL_SYNC_JOB_TTL_SEC", "3600")) WGER_NAMESPACE = os.getenv("WGER_NAMESPACE", "health").strip() WGER_USER_SYNC_CRONJOB = os.getenv("WGER_USER_SYNC_CRONJOB", "wger-user-sync").strip() WGER_USER_SYNC_WAIT_TIMEOUT_SEC = float(os.getenv("WGER_USER_SYNC_WAIT_TIMEOUT_SEC", "60")) FIREFLY_NAMESPACE = os.getenv("FIREFLY_NAMESPACE", "finance").strip() FIREFLY_USER_SYNC_CRONJOB = os.getenv("FIREFLY_USER_SYNC_CRONJOB", "firefly-user-sync").strip() FIREFLY_USER_SYNC_WAIT_TIMEOUT_SEC = float(os.getenv("FIREFLY_USER_SYNC_WAIT_TIMEOUT_SEC", "90")) SMTP_HOST = os.getenv("SMTP_HOST", "mailu-front.mailu-mailserver.svc.cluster.local").strip() SMTP_PORT = int(os.getenv("SMTP_PORT", "25")) SMTP_USERNAME = os.getenv("SMTP_USERNAME", "").strip() SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "").strip() SMTP_STARTTLS = _env_bool("SMTP_STARTTLS", "false") SMTP_USE_TLS = _env_bool("SMTP_USE_TLS", "false") SMTP_FROM = os.getenv("SMTP_FROM", "").strip() or f"postmaster@{MAILU_DOMAIN}" SMTP_TIMEOUT_SEC = float(os.getenv("SMTP_TIMEOUT_SEC", "10")) JELLYFIN_SYNC_URL = os.getenv("JELLYFIN_SYNC_URL", "").rstrip("/") JELLYFIN_LDAP_HOST = os.getenv("JELLYFIN_LDAP_HOST", "openldap.sso.svc.cluster.local").strip() JELLYFIN_LDAP_PORT = int(os.getenv("JELLYFIN_LDAP_PORT", "389")) JELLYFIN_LDAP_CHECK_TIMEOUT_SEC = float(os.getenv("JELLYFIN_LDAP_CHECK_TIMEOUT_SEC", "1")) VAULTWARDEN_NAMESPACE = os.getenv("VAULTWARDEN_NAMESPACE", "vaultwarden").strip() VAULTWARDEN_POD_LABEL = os.getenv("VAULTWARDEN_POD_LABEL", "app=vaultwarden").strip() VAULTWARDEN_POD_PORT = int(os.getenv("VAULTWARDEN_POD_PORT", "80")) VAULTWARDEN_SERVICE_HOST = os.getenv("VAULTWARDEN_SERVICE_HOST", "vaultwarden-service.vaultwarden.svc.cluster.local").strip() VAULTWARDEN_ADMIN_SECRET_NAME = os.getenv("VAULTWARDEN_ADMIN_SECRET_NAME", "vaultwarden-admin").strip() VAULTWARDEN_ADMIN_SECRET_KEY = os.getenv("VAULTWARDEN_ADMIN_SECRET_KEY", "ADMIN_TOKEN").strip() VAULTWARDEN_ADMIN_SESSION_TTL_SEC = float(os.getenv("VAULTWARDEN_ADMIN_SESSION_TTL_SEC", "300")) VAULTWARDEN_ADMIN_RATE_LIMIT_BACKOFF_SEC = float( os.getenv("VAULTWARDEN_ADMIN_RATE_LIMIT_BACKOFF_SEC", "600") )