"""Application configuration via environment variables.""" import os DB_PATH: str = os.getenv("TEN31_DB_PATH", "/data/ten31portal/portal.db") SESSION_SECRET: str = os.getenv("TEN31_SESSION_SECRET", "change-me-in-production") # Session secrets that must never reach production. The app refuses to start on any of these # (see main.lifespan) so a packaging slip cannot silently boot on a source-visible key that # would let anyone forge a session cookie. INSECURE_SESSION_SECRETS: frozenset[str] = frozenset({ "", "change-me", "change-me-in-production", }) # Send the session cookie only over HTTPS. Off by default so local dev and the test client # (both plain HTTP) work; the container turns it on via TEN31_SESSION_COOKIE_SECURE=true. SESSION_COOKIE_SECURE: bool = os.getenv( "TEN31_SESSION_COOKIE_SECURE", "false" ).lower() in ("1", "true", "yes") # Session lifetime. Default 12 hours (was the Starlette 14-day default), so a captured cookie # ages out in a workday rather than a fortnight. SESSION_MAX_AGE: int = int(os.getenv("TEN31_SESSION_MAX_AGE", str(12 * 60 * 60))) DOCS_DIR: str = os.getenv("TEN31_DOCS_DIR", "/data/ten31portal/documents") # Cap on a single uploaded document. The docs dir shares the data volume with the DB, so an # unbounded upload could fill the disk and take the portal down. Default 50 MB. The same cap # also bounds spreadsheet imports (which must be read fully into memory to parse). MAX_UPLOAD_SIZE: int = int(os.getenv("TEN31_MAX_UPLOAD_SIZE", str(50 * 1024 * 1024))) # Investor accounts created by the eNAV import (and existing no-login accounts converted via # the enable-investor-logins CLI/action) start with this password so the admin can hand out # credentials easily; each investor changes it via the portal's own Change password. DEFAULT_INVESTOR_PASSWORD: str = os.getenv("TEN31_DEFAULT_INVESTOR_PASSWORD", "Ten31Portal") # Where start.sh records the randomly-generated initial admin password on first boot, so the # operator can retrieve it once (via the "Show Initial Admin Password" service action) and then # change it. Lives next to the DB on the 0600 data volume; removed once the password is reset. ADMIN_PASSWORD_FILE: str = os.path.join(os.path.dirname(DB_PATH) or ".", ".admin-password")