Refactor gateway into modular architecture
Split 1878-line server.py into 15 focused modules:
- config.py: all env vars and constants
- database.py: schema, init, seed logic
- sessions.py: session/token CRUD
- proxy.py: proxy_request, SERVICE_MAP, resolve_service
- responses.py: ResponseMixin for handler helpers
- auth.py: login/logout/register handlers
- dashboard.py: dashboard, apps, connections, pinning
- command.py: AI command bar
- integrations/booklore.py: auth, books, cover, import
- integrations/kindle.py: send-to-kindle, file finder
- integrations/karakeep.py: save/delete bookmarks
- integrations/qbittorrent.py: download status
- integrations/image_proxy.py: external image proxy
server.py is now thin routing only (~344 lines).
All routes, methods, status codes, and responses preserved exactly.
Added PYTHONUNBUFFERED=1 to Dockerfile for live logging.
2026-03-29 00:14:46 -05:00
|
|
|
"""
|
|
|
|
|
Platform Gateway — Configuration constants and environment variables.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import ssl
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
# ── Server ──
|
|
|
|
|
PORT = int(os.environ.get("PORT", 8100))
|
|
|
|
|
DATA_DIR = Path(os.environ.get("DATA_DIR", "/app/data"))
|
|
|
|
|
DB_PATH = DATA_DIR / "platform.db"
|
|
|
|
|
|
|
|
|
|
# ── Service backends ──
|
|
|
|
|
TRIPS_URL = os.environ.get("TRIPS_BACKEND_URL", "http://localhost:8087")
|
|
|
|
|
FITNESS_URL = os.environ.get("FITNESS_BACKEND_URL", "http://localhost:8095")
|
|
|
|
|
INVENTORY_URL = os.environ.get("INVENTORY_BACKEND_URL", "http://localhost:4499")
|
|
|
|
|
NOCODB_API_TOKEN = os.environ.get("NOCODB_API_TOKEN", "")
|
|
|
|
|
MINIFLUX_URL = os.environ.get("MINIFLUX_URL", "http://localhost:8767")
|
|
|
|
|
MINIFLUX_API_KEY = os.environ.get("MINIFLUX_API_KEY", "")
|
|
|
|
|
TRIPS_API_TOKEN = os.environ.get("TRIPS_API_TOKEN", "")
|
|
|
|
|
SHELFMARK_URL = os.environ.get("SHELFMARK_URL", "http://shelfmark:8084")
|
|
|
|
|
SPOTIZERR_URL = os.environ.get("SPOTIZERR_URL", "http://spotizerr-app:7171")
|
|
|
|
|
BUDGET_URL = os.environ.get("BUDGET_BACKEND_URL", "http://localhost:3001")
|
|
|
|
|
|
2026-03-29 09:06:41 -05:00
|
|
|
# ── Service API keys (for internal service auth) ──
|
|
|
|
|
INVENTORY_SERVICE_API_KEY = os.environ.get("INVENTORY_SERVICE_API_KEY", "")
|
|
|
|
|
BUDGET_SERVICE_API_KEY = os.environ.get("BUDGET_SERVICE_API_KEY", "")
|
|
|
|
|
|
Refactor gateway into modular architecture
Split 1878-line server.py into 15 focused modules:
- config.py: all env vars and constants
- database.py: schema, init, seed logic
- sessions.py: session/token CRUD
- proxy.py: proxy_request, SERVICE_MAP, resolve_service
- responses.py: ResponseMixin for handler helpers
- auth.py: login/logout/register handlers
- dashboard.py: dashboard, apps, connections, pinning
- command.py: AI command bar
- integrations/booklore.py: auth, books, cover, import
- integrations/kindle.py: send-to-kindle, file finder
- integrations/karakeep.py: save/delete bookmarks
- integrations/qbittorrent.py: download status
- integrations/image_proxy.py: external image proxy
server.py is now thin routing only (~344 lines).
All routes, methods, status codes, and responses preserved exactly.
Added PYTHONUNBUFFERED=1 to Dockerfile for live logging.
2026-03-29 00:14:46 -05:00
|
|
|
# ── Booklore (book library manager) ──
|
|
|
|
|
BOOKLORE_URL = os.environ.get("BOOKLORE_URL", "http://booklore:6060")
|
|
|
|
|
BOOKLORE_USER = os.environ.get("BOOKLORE_USER", "")
|
|
|
|
|
BOOKLORE_PASS = os.environ.get("BOOKLORE_PASS", "")
|
|
|
|
|
BOOKLORE_BOOKS_DIR = Path("/booklore-books")
|
|
|
|
|
BOOKDROP_DIR = Path("/bookdrop")
|
|
|
|
|
|
|
|
|
|
# ── SMTP2GO (email / Send to Kindle) ──
|
|
|
|
|
SMTP2GO_API_KEY = os.environ.get("SMTP2GO_API_KEY", "")
|
|
|
|
|
SMTP2GO_FROM_EMAIL = os.environ.get("SMTP2GO_FROM_EMAIL", "")
|
|
|
|
|
SMTP2GO_FROM_NAME = os.environ.get("SMTP2GO_FROM_NAME", "Platform")
|
|
|
|
|
KINDLE_EMAIL_1 = os.environ.get("KINDLE_EMAIL_1", "")
|
|
|
|
|
KINDLE_EMAIL_2 = os.environ.get("KINDLE_EMAIL_2", "")
|
|
|
|
|
KINDLE_LABELS = os.environ.get("KINDLE_LABELS", "Kindle 1,Kindle 2")
|
|
|
|
|
|
|
|
|
|
# ── Karakeep (bookmarking) ──
|
|
|
|
|
KARAKEEP_URL = os.environ.get("KARAKEEP_URL", "http://192.168.1.42:3005")
|
|
|
|
|
KARAKEEP_API_KEY = os.environ.get("KARAKEEP_API_KEY", "")
|
|
|
|
|
|
2026-03-29 07:02:09 -05:00
|
|
|
# ── qBittorrent ──
|
|
|
|
|
QBITTORRENT_HOST = os.environ.get("QBITTORRENT_HOST", "192.168.1.42")
|
|
|
|
|
QBITTORRENT_PORT = os.environ.get("QBITTORRENT_PORT", "8080")
|
|
|
|
|
QBITTORRENT_USERNAME = os.environ.get("QBITTORRENT_USERNAME", "admin")
|
|
|
|
|
QBITTORRENT_PASSWORD = os.environ.get("QBITTORRENT_PASSWORD", "")
|
|
|
|
|
|
Refactor gateway into modular architecture
Split 1878-line server.py into 15 focused modules:
- config.py: all env vars and constants
- database.py: schema, init, seed logic
- sessions.py: session/token CRUD
- proxy.py: proxy_request, SERVICE_MAP, resolve_service
- responses.py: ResponseMixin for handler helpers
- auth.py: login/logout/register handlers
- dashboard.py: dashboard, apps, connections, pinning
- command.py: AI command bar
- integrations/booklore.py: auth, books, cover, import
- integrations/kindle.py: send-to-kindle, file finder
- integrations/karakeep.py: save/delete bookmarks
- integrations/qbittorrent.py: download status
- integrations/image_proxy.py: external image proxy
server.py is now thin routing only (~344 lines).
All routes, methods, status codes, and responses preserved exactly.
Added PYTHONUNBUFFERED=1 to Dockerfile for live logging.
2026-03-29 00:14:46 -05:00
|
|
|
# ── AI ──
|
|
|
|
|
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
|
|
|
|
|
OPENAI_MODEL = os.environ.get("OPENAI_MODEL", "gpt-5.2")
|
|
|
|
|
|
|
|
|
|
# ── Session config ──
|
|
|
|
|
SESSION_MAX_AGE = int(os.environ.get("SESSION_MAX_AGE", 30 * 86400)) # 30 days
|
|
|
|
|
|
|
|
|
|
# ── Ensure data dir exists ──
|
|
|
|
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
2026-03-29 09:13:37 -05:00
|
|
|
# ── SSL contexts ──
|
|
|
|
|
# Internal: skip verification for Docker-internal services (no valid certs)
|
|
|
|
|
_internal_ssl_ctx = ssl.create_default_context()
|
|
|
|
|
_internal_ssl_ctx.check_hostname = False
|
|
|
|
|
_internal_ssl_ctx.verify_mode = ssl.CERT_NONE
|
|
|
|
|
|