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>
This commit is contained in:
144
backend-core/main.py
Normal file
144
backend-core/main.py
Normal file
@@ -0,0 +1,144 @@
|
||||
"""
|
||||
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",
|
||||
)
|
||||
Reference in New Issue
Block a user