Backend: - src/routers/auth.py NEU: POST /api/auth/magic-link + POST /api/auth/verify - src/auth.py: verify_password/hash_password raus, generate_magic_token rein - src/main.py: alter Login-Endpoint + Brute-Force-Logik raus, neuer auth-Router eingebunden - src/config.py: ALLOWED_EMAIL + PORTAL_MAGIC_LINK_* hinzu - src/models.py: LoginRequest raus, MagicLinkRequest etc. rein - src/email_utils/templates.py: portal_magic_link_email Template Frontend: - src/static/index.html: Email-Eingabe statt Passwort, Token-Verify-Logik fuer ?token= aus URL Datenbank-Migration (migrations/2026-05-09_portal_magic_link.py): - portal_magic_links + portal_magic_link_attempts neu - portal_login_attempts gedroppt - portal_admins.email Spalte hinzu, password_hash geleert Whitelist info@aegis-sight.de, Rate-Limit 5/15 Min, Anti-Enumeration generische Antwort.
63 Zeilen
1.6 KiB
Python
63 Zeilen
1.6 KiB
Python
"""Verwaltungsportal - FastAPI Anwendung.
|
|
|
|
Auth: Magic-Link (analog Monitor). Passwort-Login wurde mit Migration
|
|
2026-05-09 entfernt. Erlaubte Email-Adresse(n) sind in config.ALLOWED_EMAIL.
|
|
"""
|
|
import logging
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.responses import FileResponse
|
|
|
|
from config import STATIC_DIR, PORT
|
|
from routers import auth, organizations, licenses, users, dashboard, sources, token_usage, audit
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
|
|
)
|
|
logger = logging.getLogger("verwaltung")
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
logger.info("Verwaltungsportal gestartet auf Port %s", PORT)
|
|
yield
|
|
logger.info("Verwaltungsportal beendet")
|
|
|
|
|
|
app = FastAPI(
|
|
title="AegisSight Verwaltungsportal",
|
|
version="2.0.0",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
# --- Routen ---
|
|
app.include_router(auth.router)
|
|
app.include_router(organizations.router)
|
|
app.include_router(licenses.router)
|
|
app.include_router(users.router)
|
|
app.include_router(dashboard.router)
|
|
app.include_router(sources.router)
|
|
app.include_router(token_usage.router)
|
|
app.include_router(audit.router)
|
|
|
|
# --- Statische Dateien ---
|
|
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
|
|
|
|
|
@app.get("/")
|
|
async def index():
|
|
return FileResponse(f"{STATIC_DIR}/index.html")
|
|
|
|
|
|
@app.get("/dashboard")
|
|
async def dashboard_page():
|
|
return FileResponse(f"{STATIC_DIR}/dashboard.html")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
uvicorn.run("main:app", host="0.0.0.0", port=PORT, reload=True)
|