initial commit
This commit is contained in:
98
backend/app/main.py
Normal file
98
backend/app/main.py
Normal file
@@ -0,0 +1,98 @@
|
||||
"""FluentGerman.ai — FastAPI application entry point."""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.auth import hash_password
|
||||
from app.config import get_settings
|
||||
from app.database import Base, engine, async_session
|
||||
from app.models import User
|
||||
from app.routers import auth, chat, instructions, users, voice
|
||||
|
||||
# ── Logging ──────────────────────────────────────────────────────────
|
||||
import os
|
||||
os.makedirs("logs", exist_ok=True)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||||
handlers=[
|
||||
logging.StreamHandler(),
|
||||
logging.FileHandler("logs/app.log", mode="a"),
|
||||
],
|
||||
)
|
||||
logger = logging.getLogger("fluentgerman")
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Create tables and bootstrap admin user on startup."""
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
# Bootstrap admin if not exists
|
||||
settings = get_settings()
|
||||
async with async_session() as db:
|
||||
result = await db.execute(select(User).where(User.is_admin == True)) # noqa: E712
|
||||
if not result.scalar_one_or_none():
|
||||
admin = User(
|
||||
username=settings.admin_username,
|
||||
email=settings.admin_email,
|
||||
hashed_password=hash_password(settings.admin_password),
|
||||
is_admin=True,
|
||||
)
|
||||
db.add(admin)
|
||||
await db.commit()
|
||||
logger.info("Admin user created: %s", settings.admin_username)
|
||||
|
||||
logger.info("FluentGerman.ai started — LLM: %s/%s, Voice: %s",
|
||||
settings.llm_provider, settings.llm_model, settings.voice_mode)
|
||||
yield
|
||||
logger.info("FluentGerman.ai shutting down")
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title="FluentGerman.ai",
|
||||
description="Personalized LLM-powered German language learning platform",
|
||||
version="1.0.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
|
||||
# ── Request logging middleware ────────────────────────────────────────
|
||||
@app.middleware("http")
|
||||
async def log_requests(request: Request, call_next):
|
||||
start = time.time()
|
||||
response = await call_next(request)
|
||||
duration = round((time.time() - start) * 1000)
|
||||
# Skip logging static file requests to keep logs clean
|
||||
path = request.url.path
|
||||
if path.startswith("/api/"):
|
||||
logger.info("%s %s → %s (%dms)", request.method, path, response.status_code, duration)
|
||||
return response
|
||||
|
||||
|
||||
# CORS — restrict in production
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# API routers
|
||||
app.include_router(auth.router)
|
||||
app.include_router(users.router)
|
||||
app.include_router(instructions.router)
|
||||
app.include_router(chat.router)
|
||||
app.include_router(voice.router)
|
||||
|
||||
# Serve frontend static files
|
||||
app.mount("/", StaticFiles(directory="../frontend", html=True), name="frontend")
|
||||
Reference in New Issue
Block a user