Files
breakpilot-core/backend-core/main.py
Benjamin Boenisch ad111d5e69 Initial commit: breakpilot-core - Shared Infrastructure
Docker Compose with 24+ services:
- PostgreSQL (PostGIS), Valkey, MinIO, Qdrant
- Vault (PKI/TLS), Nginx (Reverse Proxy)
- Backend Core API, Consent Service, Billing Service
- RAG Service, Embedding Service
- Gitea, Woodpecker CI/CD
- Night Scheduler, Health Aggregator
- Jitsi (Web/XMPP/JVB/Jicofo), Mailpit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:13 +01:00

145 lines
5.2 KiB
Python

"""
BreakPilot Core Backend
Shared APIs for authentication, RBAC, notifications, email templates,
system health, security (DevSecOps), and common middleware.
This is the extracted "core" service from the monorepo backend.
It runs on port 8000 and uses the `core` schema in PostgreSQL.
"""
import os
import logging
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
# ---------------------------------------------------------------------------
# Router imports (shared APIs only)
# ---------------------------------------------------------------------------
from auth_api import router as auth_router
from rbac_api import router as rbac_router
from notification_api import router as notification_router
from email_template_api import (
router as email_template_router,
versions_router as email_template_versions_router,
)
from system_api import router as system_router
from security_api import router as security_router
# ---------------------------------------------------------------------------
# Middleware imports
# ---------------------------------------------------------------------------
from middleware import (
RequestIDMiddleware,
SecurityHeadersMiddleware,
RateLimiterMiddleware,
PIIRedactor,
InputGateMiddleware,
)
# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
logging.basicConfig(
level=os.getenv("LOG_LEVEL", "INFO"),
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logger = logging.getLogger("backend-core")
# ---------------------------------------------------------------------------
# Application
# ---------------------------------------------------------------------------
app = FastAPI(
title="BreakPilot Core Backend",
description="Shared APIs: Auth, RBAC, Notifications, Email Templates, System, Security",
version="1.0.0",
)
# ---------------------------------------------------------------------------
# CORS
# ---------------------------------------------------------------------------
ALLOWED_ORIGINS = os.getenv("CORS_ORIGINS", "*").split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ---------------------------------------------------------------------------
# Custom middleware stack (order matters -- outermost first)
# ---------------------------------------------------------------------------
# 1. Request-ID (outermost so every response has it)
app.add_middleware(RequestIDMiddleware)
# 2. Security headers
app.add_middleware(SecurityHeadersMiddleware)
# 3. Input gate (body-size / content-type validation)
app.add_middleware(InputGateMiddleware)
# 4. Rate limiter (Valkey-backed)
VALKEY_URL = os.getenv("VALKEY_URL", os.getenv("REDIS_URL", "redis://valkey:6379/0"))
app.add_middleware(RateLimiterMiddleware, valkey_url=VALKEY_URL)
# ---------------------------------------------------------------------------
# Routers
# ---------------------------------------------------------------------------
# Auth (proxy to consent-service)
app.include_router(auth_router, prefix="/api")
# RBAC (teacher / role management)
app.include_router(rbac_router, prefix="/api")
# Notifications (proxy to consent-service)
app.include_router(notification_router, prefix="/api")
# Email templates (proxy to consent-service)
app.include_router(email_template_router) # already has /api/consent/admin/email-templates prefix
app.include_router(email_template_versions_router) # already has /api/consent/admin/email-template-versions prefix
# System (health, local-ip)
app.include_router(system_router) # already has paths defined in router
# Security / DevSecOps dashboard
app.include_router(security_router, prefix="/api")
# ---------------------------------------------------------------------------
# Startup / Shutdown events
# ---------------------------------------------------------------------------
@app.on_event("startup")
async def on_startup():
logger.info("backend-core starting up")
# Ensure DATABASE_URL uses search_path=core,public
db_url = os.getenv("DATABASE_URL", "")
if db_url and "search_path" not in db_url:
separator = "&" if "?" in db_url else "?"
new_url = f"{db_url}{separator}search_path=core,public"
os.environ["DATABASE_URL"] = new_url
logger.info("DATABASE_URL updated with search_path=core,public")
elif "search_path" in db_url:
logger.info("DATABASE_URL already contains search_path")
else:
logger.warning("DATABASE_URL is not set -- database features will fail")
@app.on_event("shutdown")
async def on_shutdown():
logger.info("backend-core shutting down")
# ---------------------------------------------------------------------------
# Entrypoint (for `python main.py` during development)
# ---------------------------------------------------------------------------
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=int(os.getenv("PORT", "8000")),
reload=os.getenv("ENVIRONMENT", "development") == "development",
)