All checks were successful
CI / test-bqas (push) Successful in 27s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 27s
CI / test-python-voice (push) Successful in 28s
Woodpecker wird nicht mehr verwendet. Wir migrieren vollstaendig auf Gitea Actions (gitea.meghsakha.com). Entfernt: - woodpecker-server + woodpecker-agent Container (docker-compose.yml) - woodpecker_data Volume - backend-core/woodpecker_proxy_api.py (SQLite-DB Proxy) - admin-core/app/api/admin/infrastructure/woodpecker/route.ts - admin-core/app/api/webhooks/woodpecker/route.ts - .woodpecker/main.yml (alte CI-Pipeline-Konfiguration) Bereinigt: - ci-cd/page.tsx: Woodpecker-Tab + Status-Karte + State entfernt - types/infrastructure-modules.ts: Woodpecker-Typen + API-Endpunkte - DevOpsPipelineSidebar.tsx: Textbeschreibungen auf Gitea Actions - dashboard/page.tsx: Woodpecker aus Service-Health-Liste - sbom/page.tsx: Woodpecker aus SBOM-Liste - navigation.ts: Beschreibung aktualisiert - .env.example: WOODPECKER_* Variablen entfernt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
144 lines
5.2 KiB
Python
144 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",
|
|
)
|