chore: initial project setup with backend, frontend, Android app, and CI/CD
Add complete NexaMFA push MFA system with: - FastAPI backend with PostgreSQL, Redis, OIDC provider, and Prometheus metrics - React TypeScript admin console - Android Kotlin/Jetpack Compose app with biometric authentication - Docker Compose deployment configuration - Gitea CI workflow for backend, frontend, and Android builds - Environment configuration template with security settings - Documentation for security model, deployment
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI, Request, Response
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from prometheus_client import CONTENT_TYPE_LATEST, Counter, Histogram, generate_latest
|
||||
from slowapi import Limiter
|
||||
from slowapi.errors import RateLimitExceeded
|
||||
from slowapi.middleware import SlowAPIMiddleware
|
||||
from slowapi.util import get_remote_address
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
from app.api import admin, oidc, public
|
||||
from app.core.config import get_settings
|
||||
from app.core.database import create_all
|
||||
from app.core.security import load_or_create_oidc_key
|
||||
|
||||
settings = get_settings()
|
||||
REQUESTS = Counter("nexamfa_http_requests_total", "HTTP requests", ["method", "path", "status"])
|
||||
LATENCY = Histogram("nexamfa_http_request_duration_seconds", "HTTP request latency", ["method", "path"])
|
||||
limiter = Limiter(key_func=get_remote_address, default_limits=[settings.rate_limit_default])
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
oidc.set_oidc_private_key(load_or_create_oidc_key(settings.oidc_signing_key_pem))
|
||||
await create_all()
|
||||
yield
|
||||
|
||||
|
||||
app = FastAPI(title="NexaMFA", version="0.1.0", lifespan=lifespan)
|
||||
app.state.limiter = limiter
|
||||
app.add_middleware(SlowAPIMiddleware)
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.cors_origin_list,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
@app.exception_handler(RateLimitExceeded)
|
||||
async def rate_limit_handler(_: Request, exc: RateLimitExceeded):
|
||||
return JSONResponse({"detail": "Rate limit exceeded"}, status_code=429)
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def metrics_middleware(request: Request, call_next):
|
||||
with LATENCY.labels(request.method, request.url.path).time():
|
||||
response = await call_next(request)
|
||||
REQUESTS.labels(request.method, request.url.path, str(response.status_code)).inc()
|
||||
return response
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "ok", "service": "NexaMFA"}
|
||||
|
||||
|
||||
@app.get("/metrics")
|
||||
async def metrics():
|
||||
return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)
|
||||
|
||||
|
||||
app.include_router(public.router)
|
||||
app.include_router(admin.router)
|
||||
app.include_router(oidc.router)
|
||||
Reference in New Issue
Block a user