ariadne/ariadne/settings.py

546 lines
24 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
import os
def _env(name: str, default: str = "") -> str:
value = os.getenv(name, default)
return value.strip() if isinstance(value, str) else default
def _env_bool(name: str, default: str = "false") -> bool:
return _env(name, default).lower() in {"1", "true", "yes", "y", "on"}
def _env_int(name: str, default: int) -> int:
raw = _env(name, str(default))
try:
return int(raw)
except ValueError:
return default
def _env_float(name: str, default: float) -> float:
raw = _env(name, str(default))
try:
return float(raw)
except ValueError:
return default
@dataclass(frozen=True)
class Settings:
app_name: str
bind_host: str
bind_port: int
ariadne_database_url: str
portal_database_url: str
portal_public_base_url: str
log_level: str
ariadne_db_pool_min: int
ariadne_db_pool_max: int
ariadne_db_connect_timeout_sec: int
ariadne_db_lock_timeout_sec: int
ariadne_db_statement_timeout_sec: int
ariadne_db_idle_in_tx_timeout_sec: int
ariadne_run_migrations: bool
keycloak_url: str
keycloak_realm: str
keycloak_client_id: str
keycloak_issuer: str
keycloak_jwks_url: str
keycloak_admin_url: str
keycloak_admin_realm: str
keycloak_admin_client_id: str
keycloak_admin_client_secret: str
portal_admin_users: list[str]
portal_admin_groups: list[str]
account_allowed_groups: list[str]
allowed_flag_groups: list[str]
default_user_groups: list[str]
mailu_domain: str
mailu_sync_url: str
mailu_event_min_interval_sec: float
mailu_sync_wait_timeout_sec: float
mailu_mailbox_wait_timeout_sec: float
mailu_db_host: str
mailu_db_port: int
mailu_db_name: str
mailu_db_user: str
mailu_db_password: str
mailu_host: str
mailu_default_quota: int
mailu_system_users: list[str]
mailu_system_password: str
nextcloud_namespace: str
nextcloud_pod_label: str
nextcloud_container: str
nextcloud_exec_timeout_sec: float
nextcloud_db_host: str
nextcloud_db_port: int
nextcloud_db_name: str
nextcloud_db_user: str
nextcloud_db_password: str
nextcloud_url: str
nextcloud_admin_user: str
nextcloud_admin_password: str
wger_namespace: str
wger_user_sync_wait_timeout_sec: float
wger_pod_label: str
wger_container: str
wger_admin_username: str
wger_admin_password: str
wger_admin_email: str
firefly_namespace: str
firefly_user_sync_wait_timeout_sec: float
firefly_pod_label: str
firefly_container: str
firefly_cron_base_url: str
firefly_cron_token: str
firefly_cron_timeout_sec: float
vault_namespace: str
vault_addr: str
vault_token: str
vault_k8s_role: str
vault_k8s_role_ttl: str
vault_k8s_token_reviewer_jwt: str
vault_k8s_token_reviewer_jwt_file: str
vault_oidc_discovery_url: str
vault_oidc_client_id: str
vault_oidc_client_secret: str
vault_oidc_default_role: str
vault_oidc_scopes: str
vault_oidc_user_claim: str
vault_oidc_groups_claim: str
vault_oidc_token_policies: str
vault_oidc_admin_group: str
vault_oidc_admin_policies: str
vault_oidc_dev_group: str
vault_oidc_dev_policies: str
vault_oidc_user_group: str
vault_oidc_user_policies: str
vault_oidc_redirect_uris: str
vault_oidc_bound_audiences: str
vault_oidc_bound_claims_type: str
comms_namespace: str
comms_synapse_base: str
comms_auth_base: str
comms_mas_admin_api_base: str
comms_mas_token_url: str
comms_mas_admin_client_id: str
comms_mas_admin_client_secret: str
comms_server_name: str
comms_room_alias: str
comms_room_name: str
comms_pin_message: str
comms_seeder_user: str
comms_seeder_password: str
comms_bot_user: str
comms_bot_password: str
comms_synapse_db_host: str
comms_synapse_db_port: int
comms_synapse_db_name: str
comms_synapse_db_user: str
comms_synapse_db_password: str
comms_synapse_admin_token: str
comms_timeout_sec: float
comms_guest_stale_days: int
image_sweeper_namespace: str
image_sweeper_service_account: str
image_sweeper_job_ttl_sec: int
image_sweeper_wait_timeout_sec: float
vaultwarden_namespace: str
vaultwarden_pod_label: str
vaultwarden_pod_port: int
vaultwarden_service_host: str
vaultwarden_admin_secret_name: str
vaultwarden_admin_secret_key: str
vaultwarden_admin_session_ttl_sec: float
vaultwarden_admin_rate_limit_backoff_sec: float
vaultwarden_retry_cooldown_sec: float
vaultwarden_failure_bailout: int
vaultwarden_invite_refresh_sec: float
smtp_host: str
smtp_port: int
smtp_username: str
smtp_password: str
smtp_starttls: bool
smtp_use_tls: bool
smtp_from: str
smtp_timeout_sec: float
welcome_email_enabled: bool
provision_poll_interval_sec: float
provision_retry_cooldown_sec: float
schedule_tick_sec: float
k8s_api_timeout_sec: float
vm_url: str
cluster_state_vm_timeout_sec: float
mailu_sync_cron: str
nextcloud_sync_cron: str
nextcloud_cron: str
nextcloud_maintenance_cron: str
vaultwarden_sync_cron: str
wger_user_sync_cron: str
wger_admin_cron: str
firefly_user_sync_cron: str
firefly_cron: str
pod_cleaner_cron: str
opensearch_prune_cron: str
image_sweeper_cron: str
vault_k8s_auth_cron: str
vault_oidc_cron: str
comms_guest_name_cron: str
comms_pin_invite_cron: str
comms_reset_room_cron: str
comms_seed_room_cron: str
keycloak_profile_cron: str
cluster_state_cron: str
cluster_state_keep: int
opensearch_url: str
opensearch_limit_bytes: int
opensearch_index_patterns: str
opensearch_timeout_sec: float
metrics_path: str
@classmethod
def _keycloak_config(cls) -> dict[str, Any]:
keycloak_url = _env("KEYCLOAK_URL", "https://sso.bstein.dev").rstrip("/")
keycloak_realm = _env("KEYCLOAK_REALM", "atlas")
keycloak_client_id = _env("KEYCLOAK_CLIENT_ID", "bstein-dev-home")
keycloak_issuer = _env("KEYCLOAK_ISSUER", f"{keycloak_url}/realms/{keycloak_realm}").rstrip("/")
keycloak_jwks_url = _env("KEYCLOAK_JWKS_URL", f"{keycloak_issuer}/protocol/openid-connect/certs").rstrip("/")
return {
"keycloak_url": keycloak_url,
"keycloak_realm": keycloak_realm,
"keycloak_client_id": keycloak_client_id,
"keycloak_issuer": keycloak_issuer,
"keycloak_jwks_url": keycloak_jwks_url,
"keycloak_admin_url": _env("KEYCLOAK_ADMIN_URL", keycloak_url).rstrip("/"),
"keycloak_admin_realm": _env("KEYCLOAK_ADMIN_REALM", keycloak_realm),
"keycloak_admin_client_id": _env("KEYCLOAK_ADMIN_CLIENT_ID", ""),
"keycloak_admin_client_secret": _env("KEYCLOAK_ADMIN_CLIENT_SECRET", ""),
}
@classmethod
def _portal_group_config(cls) -> dict[str, Any]:
return {
"portal_admin_users": [u for u in (_env("PORTAL_ADMIN_USERS", "bstein")).split(",") if u.strip()],
"portal_admin_groups": [g for g in (_env("PORTAL_ADMIN_GROUPS", "admin")).split(",") if g.strip()],
"account_allowed_groups": [
g for g in (_env("ACCOUNT_ALLOWED_GROUPS", "dev,admin")).split(",") if g.strip()
],
"allowed_flag_groups": [g for g in (_env("ALLOWED_FLAG_GROUPS", "demo,test")).split(",") if g.strip()],
"default_user_groups": [g for g in (_env("DEFAULT_USER_GROUPS", "dev")).split(",") if g.strip()],
}
@classmethod
def _mailu_config(cls) -> dict[str, Any]:
mailu_domain = _env("MAILU_DOMAIN", "bstein.dev")
return {
"mailu_domain": mailu_domain,
"mailu_sync_url": _env(
"MAILU_SYNC_URL",
"http://mailu-sync-listener.mailu-mailserver.svc.cluster.local:8080/events",
).rstrip("/"),
"mailu_event_min_interval_sec": _env_float("MAILU_EVENT_MIN_INTERVAL_SEC", 10.0),
"mailu_sync_wait_timeout_sec": _env_float("MAILU_SYNC_WAIT_TIMEOUT_SEC", 60.0),
"mailu_mailbox_wait_timeout_sec": _env_float("MAILU_MAILBOX_WAIT_TIMEOUT_SEC", 60.0),
"mailu_db_host": _env("MAILU_DB_HOST", "postgres-service.postgres.svc.cluster.local"),
"mailu_db_port": _env_int("MAILU_DB_PORT", 5432),
"mailu_db_name": _env("MAILU_DB_NAME", "mailu"),
"mailu_db_user": _env("MAILU_DB_USER", "mailu"),
"mailu_db_password": _env("MAILU_DB_PASSWORD", ""),
"mailu_host": _env("MAILU_HOST", f"mail.{mailu_domain}"),
"mailu_default_quota": _env_int("MAILU_DEFAULT_QUOTA", 20000000000),
"mailu_system_users": [u for u in _env("MAILU_SYSTEM_USERS", "").split(",") if u.strip()],
"mailu_system_password": _env("MAILU_SYSTEM_PASSWORD", ""),
}
@classmethod
def _smtp_config(cls, mailu_domain: str) -> dict[str, Any]:
return {
"smtp_host": _env("SMTP_HOST", ""),
"smtp_port": _env_int("SMTP_PORT", 25),
"smtp_username": _env("SMTP_USERNAME", ""),
"smtp_password": _env("SMTP_PASSWORD", ""),
"smtp_starttls": _env_bool("SMTP_STARTTLS", "false"),
"smtp_use_tls": _env_bool("SMTP_USE_TLS", "false"),
"smtp_from": _env("SMTP_FROM", f"postmaster@{mailu_domain}"),
"smtp_timeout_sec": _env_float("SMTP_TIMEOUT_SEC", 10.0),
"welcome_email_enabled": _env_bool("WELCOME_EMAIL_ENABLED", "true"),
}
@classmethod
def _nextcloud_config(cls) -> dict[str, Any]:
return {
"nextcloud_namespace": _env("NEXTCLOUD_NAMESPACE", "nextcloud"),
"nextcloud_pod_label": _env("NEXTCLOUD_POD_LABEL", "app=nextcloud"),
"nextcloud_container": _env("NEXTCLOUD_CONTAINER", "nextcloud"),
"nextcloud_exec_timeout_sec": _env_float("NEXTCLOUD_EXEC_TIMEOUT_SEC", 120.0),
"nextcloud_db_host": _env("NEXTCLOUD_DB_HOST", "postgres-service.postgres.svc.cluster.local"),
"nextcloud_db_port": _env_int("NEXTCLOUD_DB_PORT", 5432),
"nextcloud_db_name": _env("NEXTCLOUD_DB_NAME", "nextcloud"),
"nextcloud_db_user": _env("NEXTCLOUD_DB_USER", "nextcloud"),
"nextcloud_db_password": _env("NEXTCLOUD_DB_PASSWORD", ""),
"nextcloud_url": _env("NEXTCLOUD_URL", "https://cloud.bstein.dev").rstrip("/"),
"nextcloud_admin_user": _env("NEXTCLOUD_ADMIN_USER", ""),
"nextcloud_admin_password": _env("NEXTCLOUD_ADMIN_PASSWORD", ""),
}
@classmethod
def _wger_config(cls) -> dict[str, Any]:
return {
"wger_namespace": _env("WGER_NAMESPACE", "health"),
"wger_user_sync_wait_timeout_sec": _env_float("WGER_USER_SYNC_WAIT_TIMEOUT_SEC", 60.0),
"wger_pod_label": _env("WGER_POD_LABEL", "app=wger"),
"wger_container": _env("WGER_CONTAINER", "wger"),
"wger_admin_username": _env("WGER_ADMIN_USERNAME", ""),
"wger_admin_password": _env("WGER_ADMIN_PASSWORD", ""),
"wger_admin_email": _env("WGER_ADMIN_EMAIL", ""),
}
@classmethod
def _firefly_config(cls) -> dict[str, Any]:
return {
"firefly_namespace": _env("FIREFLY_NAMESPACE", "finance"),
"firefly_user_sync_wait_timeout_sec": _env_float("FIREFLY_USER_SYNC_WAIT_TIMEOUT_SEC", 90.0),
"firefly_pod_label": _env("FIREFLY_POD_LABEL", "app=firefly"),
"firefly_container": _env("FIREFLY_CONTAINER", "firefly"),
"firefly_cron_base_url": _env(
"FIREFLY_CRON_BASE_URL",
"http://firefly.finance.svc.cluster.local/api/v1/cron",
),
"firefly_cron_token": _env("FIREFLY_CRON_TOKEN", ""),
"firefly_cron_timeout_sec": _env_float("FIREFLY_CRON_TIMEOUT_SEC", 30.0),
}
@classmethod
def _vault_config(cls) -> dict[str, Any]:
return {
"vault_namespace": _env("VAULT_NAMESPACE", "vault"),
"vault_addr": _env("VAULT_ADDR", "http://vault.vault.svc.cluster.local:8200").rstrip("/"),
"vault_token": _env("VAULT_TOKEN", ""),
"vault_k8s_role": _env("VAULT_K8S_ROLE", "vault"),
"vault_k8s_role_ttl": _env("VAULT_K8S_ROLE_TTL", "1h"),
"vault_k8s_token_reviewer_jwt": _env("VAULT_K8S_TOKEN_REVIEWER_JWT", ""),
"vault_k8s_token_reviewer_jwt_file": _env("VAULT_K8S_TOKEN_REVIEWER_JWT_FILE", ""),
"vault_oidc_discovery_url": _env("VAULT_OIDC_DISCOVERY_URL", ""),
"vault_oidc_client_id": _env("VAULT_OIDC_CLIENT_ID", ""),
"vault_oidc_client_secret": _env("VAULT_OIDC_CLIENT_SECRET", ""),
"vault_oidc_default_role": _env("VAULT_OIDC_DEFAULT_ROLE", "admin"),
"vault_oidc_scopes": _env("VAULT_OIDC_SCOPES", "openid profile email groups"),
"vault_oidc_user_claim": _env("VAULT_OIDC_USER_CLAIM", "preferred_username"),
"vault_oidc_groups_claim": _env("VAULT_OIDC_GROUPS_CLAIM", "groups"),
"vault_oidc_token_policies": _env("VAULT_OIDC_TOKEN_POLICIES", ""),
"vault_oidc_admin_group": _env("VAULT_OIDC_ADMIN_GROUP", "admin"),
"vault_oidc_admin_policies": _env("VAULT_OIDC_ADMIN_POLICIES", "default,vault-admin"),
"vault_oidc_dev_group": _env("VAULT_OIDC_DEV_GROUP", "dev"),
"vault_oidc_dev_policies": _env("VAULT_OIDC_DEV_POLICIES", "default,dev-kv"),
"vault_oidc_user_group": _env("VAULT_OIDC_USER_GROUP", ""),
"vault_oidc_user_policies": _env("VAULT_OIDC_USER_POLICIES", ""),
"vault_oidc_redirect_uris": _env(
"VAULT_OIDC_REDIRECT_URIS",
"https://secret.bstein.dev/ui/vault/auth/oidc/oidc/callback",
),
"vault_oidc_bound_audiences": _env("VAULT_OIDC_BOUND_AUDIENCES", ""),
"vault_oidc_bound_claims_type": _env("VAULT_OIDC_BOUND_CLAIMS_TYPE", "string"),
}
@classmethod
def _comms_config(cls) -> dict[str, Any]:
return {
"comms_namespace": _env("COMMS_NAMESPACE", "comms"),
"comms_synapse_base": _env(
"COMMS_SYNAPSE_BASE",
"http://othrys-synapse-matrix-synapse:8008",
).rstrip("/"),
"comms_auth_base": _env(
"COMMS_AUTH_BASE",
"http://matrix-authentication-service:8080",
).rstrip("/"),
"comms_mas_admin_api_base": _env(
"COMMS_MAS_ADMIN_API_BASE",
"http://matrix-authentication-service:8081/api/admin/v1",
).rstrip("/"),
"comms_mas_token_url": _env(
"COMMS_MAS_TOKEN_URL",
"http://matrix-authentication-service:8080/oauth2/token",
),
"comms_mas_admin_client_id": _env("COMMS_MAS_ADMIN_CLIENT_ID", "01KDXMVQBQ5JNY6SEJPZW6Z8BM"),
"comms_mas_admin_client_secret": _env("COMMS_MAS_ADMIN_CLIENT_SECRET", ""),
"comms_server_name": _env("COMMS_SERVER_NAME", "live.bstein.dev"),
"comms_room_alias": _env("COMMS_ROOM_ALIAS", "#othrys:live.bstein.dev"),
"comms_room_name": _env("COMMS_ROOM_NAME", "Othrys"),
"comms_pin_message": _env(
"COMMS_PIN_MESSAGE",
"Invite guests: share https://live.bstein.dev/#/room/#othrys:live.bstein.dev?action=join and choose 'Continue' -> 'Join as guest'.",
),
"comms_seeder_user": _env("COMMS_SEEDER_USER", "othrys-seeder"),
"comms_seeder_password": _env("COMMS_SEEDER_PASSWORD", ""),
"comms_bot_user": _env("COMMS_BOT_USER", "atlasbot"),
"comms_bot_password": _env("COMMS_BOT_PASSWORD", ""),
"comms_synapse_db_host": _env(
"COMMS_SYNAPSE_DB_HOST",
"postgres-service.postgres.svc.cluster.local",
),
"comms_synapse_db_port": _env_int("COMMS_SYNAPSE_DB_PORT", 5432),
"comms_synapse_db_name": _env("COMMS_SYNAPSE_DB_NAME", "synapse"),
"comms_synapse_db_user": _env("COMMS_SYNAPSE_DB_USER", "synapse"),
"comms_synapse_db_password": _env("COMMS_SYNAPSE_DB_PASSWORD", ""),
"comms_synapse_admin_token": _env("COMMS_SYNAPSE_ADMIN_TOKEN", ""),
"comms_timeout_sec": _env_float("COMMS_TIMEOUT_SEC", 30.0),
"comms_guest_stale_days": _env_int("COMMS_GUEST_STALE_DAYS", 14),
}
@classmethod
def _image_sweeper_config(cls) -> dict[str, Any]:
return {
"image_sweeper_namespace": _env("IMAGE_SWEEPER_NAMESPACE", "maintenance"),
"image_sweeper_service_account": _env("IMAGE_SWEEPER_SERVICE_ACCOUNT", "node-image-sweeper"),
"image_sweeper_job_ttl_sec": _env_int("IMAGE_SWEEPER_JOB_TTL_SEC", 3600),
"image_sweeper_wait_timeout_sec": _env_float("IMAGE_SWEEPER_WAIT_TIMEOUT_SEC", 1200.0),
}
@classmethod
def _vaultwarden_config(cls) -> dict[str, Any]:
return {
"vaultwarden_namespace": _env("VAULTWARDEN_NAMESPACE", "vaultwarden"),
"vaultwarden_pod_label": _env("VAULTWARDEN_POD_LABEL", "app=vaultwarden"),
"vaultwarden_pod_port": _env_int("VAULTWARDEN_POD_PORT", 80),
"vaultwarden_service_host": _env(
"VAULTWARDEN_SERVICE_HOST",
"vaultwarden-service.vaultwarden.svc.cluster.local",
),
"vaultwarden_admin_secret_name": _env("VAULTWARDEN_ADMIN_SECRET_NAME", "vaultwarden-admin"),
"vaultwarden_admin_secret_key": _env("VAULTWARDEN_ADMIN_SECRET_KEY", "ADMIN_TOKEN"),
"vaultwarden_admin_session_ttl_sec": _env_float("VAULTWARDEN_ADMIN_SESSION_TTL_SEC", 300.0),
"vaultwarden_admin_rate_limit_backoff_sec": _env_float("VAULTWARDEN_ADMIN_RATE_LIMIT_BACKOFF_SEC", 600.0),
"vaultwarden_retry_cooldown_sec": _env_float("VAULTWARDEN_RETRY_COOLDOWN_SEC", 1800.0),
"vaultwarden_failure_bailout": _env_int("VAULTWARDEN_FAILURE_BAILOUT", 2),
"vaultwarden_invite_refresh_sec": _env_float("VAULTWARDEN_INVITE_REFRESH_SEC", 86400.0),
}
@classmethod
def _schedule_config(cls) -> dict[str, Any]:
return {
"mailu_sync_cron": _env("ARIADNE_SCHEDULE_MAILU_SYNC", "30 4 * * *"),
"nextcloud_sync_cron": _env("ARIADNE_SCHEDULE_NEXTCLOUD_SYNC", "0 5 * * *"),
"nextcloud_cron": _env("ARIADNE_SCHEDULE_NEXTCLOUD_CRON", "*/5 * * * *"),
"nextcloud_maintenance_cron": _env("ARIADNE_SCHEDULE_NEXTCLOUD_MAINTENANCE", "30 4 * * *"),
"vaultwarden_sync_cron": _env("ARIADNE_SCHEDULE_VAULTWARDEN_SYNC", "0 * * * *"),
"wger_user_sync_cron": _env("ARIADNE_SCHEDULE_WGER_USER_SYNC", "0 5 * * *"),
"wger_admin_cron": _env("ARIADNE_SCHEDULE_WGER_ADMIN", "15 3 * * *"),
"firefly_user_sync_cron": _env("ARIADNE_SCHEDULE_FIREFLY_USER_SYNC", "0 6 * * *"),
"firefly_cron": _env("ARIADNE_SCHEDULE_FIREFLY_CRON", "0 3 * * *"),
"pod_cleaner_cron": _env("ARIADNE_SCHEDULE_POD_CLEANER", "0 * * * *"),
"opensearch_prune_cron": _env("ARIADNE_SCHEDULE_OPENSEARCH_PRUNE", "23 3 * * *"),
"image_sweeper_cron": _env("ARIADNE_SCHEDULE_IMAGE_SWEEPER", "30 4 * * 0"),
"vault_k8s_auth_cron": _env("ARIADNE_SCHEDULE_VAULT_K8S_AUTH", "0 * * * *"),
"vault_oidc_cron": _env("ARIADNE_SCHEDULE_VAULT_OIDC", "0 * * * *"),
"comms_guest_name_cron": _env("ARIADNE_SCHEDULE_COMMS_GUEST_NAME", "*/5 * * * *"),
"comms_pin_invite_cron": _env("ARIADNE_SCHEDULE_COMMS_PIN_INVITE", "*/30 * * * *"),
"comms_reset_room_cron": _env("ARIADNE_SCHEDULE_COMMS_RESET_ROOM", "0 0 1 1 *"),
"comms_seed_room_cron": _env("ARIADNE_SCHEDULE_COMMS_SEED_ROOM", "*/10 * * * *"),
"keycloak_profile_cron": _env("ARIADNE_SCHEDULE_KEYCLOAK_PROFILE", "0 */6 * * *"),
}
@classmethod
def _cluster_state_config(cls) -> dict[str, Any]:
return {
"vm_url": _env(
"ARIADNE_VM_URL",
"http://victoria-metrics-single-server.monitoring.svc.cluster.local:8428",
).rstrip("/"),
"cluster_state_vm_timeout_sec": _env_float("ARIADNE_CLUSTER_STATE_VM_TIMEOUT_SEC", 5.0),
"cluster_state_cron": _env("ARIADNE_SCHEDULE_CLUSTER_STATE", "*/15 * * * *"),
"cluster_state_keep": _env_int("ARIADNE_CLUSTER_STATE_KEEP", 168),
}
@classmethod
def _opensearch_config(cls) -> dict[str, Any]:
return {
"opensearch_url": _env(
"OPENSEARCH_URL",
"http://opensearch-master.logging.svc.cluster.local:9200",
).rstrip("/"),
"opensearch_limit_bytes": _env_int("OPENSEARCH_LIMIT_BYTES", 1024**4),
"opensearch_index_patterns": _env("OPENSEARCH_INDEX_PATTERNS", "kube-*,journald-*"),
"opensearch_timeout_sec": _env_float("OPENSEARCH_TIMEOUT_SEC", 30.0),
}
@classmethod
def from_env(cls) -> "Settings":
keycloak_cfg = cls._keycloak_config()
portal_cfg = cls._portal_group_config()
mailu_cfg = cls._mailu_config()
smtp_cfg = cls._smtp_config(mailu_cfg["mailu_domain"])
nextcloud_cfg = cls._nextcloud_config()
wger_cfg = cls._wger_config()
firefly_cfg = cls._firefly_config()
vault_cfg = cls._vault_config()
comms_cfg = cls._comms_config()
image_cfg = cls._image_sweeper_config()
vaultwarden_cfg = cls._vaultwarden_config()
schedule_cfg = cls._schedule_config()
cluster_cfg = cls._cluster_state_config()
opensearch_cfg = cls._opensearch_config()
portal_db = _env("PORTAL_DATABASE_URL", "")
ariadne_db = _env("ARIADNE_DATABASE_URL", portal_db)
return cls(
app_name=_env("ARIADNE_APP_NAME", "ariadne"),
bind_host=_env("ARIADNE_BIND_HOST", "0.0.0.0"),
bind_port=_env_int("ARIADNE_BIND_PORT", 8080),
ariadne_database_url=ariadne_db,
portal_database_url=portal_db,
portal_public_base_url=_env("PORTAL_PUBLIC_BASE_URL", "https://bstein.dev").rstrip("/"),
log_level=_env("ARIADNE_LOG_LEVEL", "INFO"),
ariadne_db_pool_min=_env_int("ARIADNE_DB_POOL_MIN", 0),
ariadne_db_pool_max=_env_int("ARIADNE_DB_POOL_MAX", 5),
ariadne_db_connect_timeout_sec=_env_int("ARIADNE_DB_CONNECT_TIMEOUT_SEC", 5),
ariadne_db_lock_timeout_sec=_env_int("ARIADNE_DB_LOCK_TIMEOUT_SEC", 5),
ariadne_db_statement_timeout_sec=_env_int("ARIADNE_DB_STATEMENT_TIMEOUT_SEC", 30),
ariadne_db_idle_in_tx_timeout_sec=_env_int("ARIADNE_DB_IDLE_IN_TX_TIMEOUT_SEC", 10),
ariadne_run_migrations=_env_bool("ARIADNE_RUN_MIGRATIONS", "false"),
provision_poll_interval_sec=_env_float("ARIADNE_PROVISION_POLL_INTERVAL_SEC", 5.0),
provision_retry_cooldown_sec=_env_float("ARIADNE_PROVISION_RETRY_COOLDOWN_SEC", 30.0),
schedule_tick_sec=_env_float("ARIADNE_SCHEDULE_TICK_SEC", 5.0),
k8s_api_timeout_sec=_env_float("K8S_API_TIMEOUT_SEC", 5.0),
metrics_path=_env("METRICS_PATH", "/metrics"),
**keycloak_cfg,
**portal_cfg,
**mailu_cfg,
**smtp_cfg,
**nextcloud_cfg,
**wger_cfg,
**firefly_cfg,
**vault_cfg,
**comms_cfg,
**image_cfg,
**vaultwarden_cfg,
**schedule_cfg,
**cluster_cfg,
**opensearch_cfg,
)
settings = Settings.from_env()