Files
gh-christianlouis-leagueledger/app/main.py
T
Christian Krakau-Louis 6306abf6d9 Add comprehensive documentation for LeagueLedger
- Created architecture overview in development/architecture.md
- Added installation guide in getting-started/installation.md
- Developed user guide with detailed instructions in user-guide/overview.md, user-guide/teams.md, user-guide/qr-codes.md
- Implemented social login setup documentation in social_login_setup.md
- Updated index.md to include links to new documentation sections
- Configured mkdocs.yml for site structure and theme
- Added requirements.txt for documentation dependencies
2025-04-15 12:32:03 +02:00

138 lines
4.8 KiB
Python

#!/usr/bin/env python3
from fastapi import FastAPI, Request, Depends, Form
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware
from pathlib import Path
import os
from starlette.middleware.sessions import SessionMiddleware
from starlette.middleware.authentication import AuthenticationMiddleware
from dotenv import load_dotenv
import logging
import contextlib
from .db import engine, get_db
from . import models
from .templates_config import templates
from .views import qr, redeem, teams, admin, leaderboard, dashboard, static, pages, auth, convenience
from .db_init import init_db
from .auth.middleware import SessionAuthBackend, on_auth_error
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Load environment variables
load_dotenv()
# Create the FastAPI application
app = FastAPI(title="LeagueLedger")
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, replace with specific origins
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Get secret key for the session
SECRET_KEY = os.getenv("SECRET_KEY", "a-very-secure-secret-key-for-development")
# Important: Order of middleware matters!
# First add AuthenticationMiddleware
app.add_middleware(
AuthenticationMiddleware,
backend=SessionAuthBackend(),
on_error=on_auth_error
)
# Then add SessionMiddleware (last added = first executed)
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY)
# Initialize database on startup
@app.on_event("startup")
async def startup_db_client():
logger.info("Starting database initialization")
try:
# Import models to ensure they're registered with Base before initialization
from . import models
# Initialize database (applies migrations and seeds data)
init_db()
logger.info("Database initialized and migrated successfully")
except Exception as e:
logger.error(f"Database initialization error: {str(e)}")
# We continue even if there was an error, as the application might still work with partial functionality
# Mount static files
app.mount("/static", StaticFiles(directory="app/static"), name="static")
# Configure static files
static.configure_static_files(app)
# Setup Jinja2 templates
templates = Jinja2Templates(directory="app/templates")
# User context middleware
@app.middleware("http")
async def add_template_globals(request: Request, call_next):
"""Add global variables to all templates."""
try:
# Set user in request state for templates
if hasattr(request, "session"):
user_id = request.session.get("user_id")
if user_id:
# Get a database session
from sqlalchemy.orm import Session
from .db import SessionLocal
from .models import User
db = SessionLocal()
try:
# Fetch actual user from database
user = db.query(User).filter(User.id == user_id).first()
if user:
request.state.user = user
else:
request.state.user = None
finally:
db.close()
else:
request.state.user = None
else:
request.state.user = None
except Exception as e:
# Log error but continue processing
logger.error(f"Error setting template globals: {str(e)}")
request.state.user = None
# Continue with request
response = await call_next(request)
return response
# Handle exceptions
@app.exception_handler(404)
async def not_found_exception_handler(request: Request, exc):
"""Handle 404 errors with a custom template."""
return templates.TemplateResponse(
"error.html",
{"request": request, "error": "Page not found"},
status_code=404
)
# Routers
app.include_router(pages.router, tags=["Pages"]) # Pages router for index and static pages
app.include_router(auth.router, prefix="/auth", tags=["auth"]) # Include the auth router
app.include_router(qr.router, prefix="/qr", tags=["QR"])
app.include_router(redeem.router, prefix="/redeem", tags=["Redeem"])
app.include_router(teams.router, prefix="/teams", tags=["teams"])
app.include_router(admin.router, prefix="/admin", tags=["Admin"])
app.include_router(leaderboard.router, prefix="/leaderboard", tags=["leaderboard"])
app.include_router(dashboard.router, prefix="/dashboard", tags=["Dashboard"])
app.include_router(static.router, tags=["Static"]) # Include the static router
app.include_router(convenience.router, tags=["Convenience"]) # Include convenience routes