5cf3f944b1
- Updated docker-compose.yml to include Mailhog service for email testing. - Added environment variables for email configuration in docker-compose. - Updated requirements.txt to include fastapi-mail for email support. - Created templates for password reset, email verification, and welcome emails. - Developed mail utility module to handle sending emails using FastAPI-Mail. - Added documentation for email testing with Mailhog.
137 lines
4.9 KiB
Python
137 lines
4.9 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 pathlib import Path
|
|
import os
|
|
from starlette.middleware.sessions import SessionMiddleware
|
|
from dotenv import load_dotenv
|
|
import logging
|
|
import contextlib
|
|
|
|
from .db import init_db, engine, get_db
|
|
from . import models
|
|
from .templates_config import templates
|
|
from .views import qr, redeem, teams, admin, leaderboard, dashboard, static, pages, auth
|
|
from .db_init import seed_db
|
|
from .db_migrations import apply_migrations
|
|
|
|
# Configure logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Load environment variables
|
|
load_dotenv()
|
|
|
|
# Create the FastAPI application
|
|
app = FastAPI(title="LeagueLedger")
|
|
|
|
# Add SessionMiddleware with a secure secret key
|
|
# Must be added first before other middleware to ensure it's available
|
|
app.add_middleware(SessionMiddleware, secret_key=os.getenv("SESSION_SECRET_KEY", "your-very-secret-session-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
|
|
|
|
# Create all tables first
|
|
init_db()
|
|
logger.info("Base tables created successfully")
|
|
|
|
# Apply migrations to add additional columns and constraints
|
|
with contextlib.suppress(Exception):
|
|
apply_migrations()
|
|
logger.info("Migrations applied successfully")
|
|
|
|
# Seed the database with test data if needed
|
|
with contextlib.suppress(Exception):
|
|
seed_db()
|
|
logger.info("Database seeded 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 template globals"""
|
|
try:
|
|
# Update template globals for all templates
|
|
user = None
|
|
if hasattr(request, "session") and "user_id" in request.session and request.session.get("is_authenticated"):
|
|
# Mock user object - in a real app, you'd fetch this from the database
|
|
user = {
|
|
"id": request.session["user_id"],
|
|
"username": request.session.get("username", "User"),
|
|
"is_admin": request.session.get("is_admin", False)
|
|
}
|
|
templates.env.globals["current_user"] = user
|
|
except Exception as e:
|
|
logger.error(f"Error setting template globals: {str(e)}")
|
|
|
|
# Process the request
|
|
response = await call_next(request)
|
|
return response
|
|
|
|
@app.get("/", response_class=HTMLResponse)
|
|
async def read_root(request: Request):
|
|
user = None
|
|
if "user_id" in request.session and request.session.get("is_authenticated"):
|
|
user = {
|
|
"id": request.session["user_id"],
|
|
"username": request.session.get("username", "User"),
|
|
"is_admin": request.session.get("is_admin", False)
|
|
}
|
|
return templates.TemplateResponse(
|
|
"index.html",
|
|
{"request": request, "user": user}
|
|
)
|
|
|
|
# Routers
|
|
app.include_router(pages.router, tags=["Pages"]) # Pages router for index and static pages
|
|
app.include_router(auth.router) # 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
|
|
|
|
# Add a direct route for /scan that redirects to /dashboard/scan
|
|
@app.get("/scan")
|
|
async def scan_redirect():
|
|
return RedirectResponse("/dashboard/scan", status_code=303)
|
|
|
|
# Add convenience routes for auth paths
|
|
@app.get("/login")
|
|
async def login_redirect():
|
|
return RedirectResponse("/auth/login", status_code=303)
|
|
|
|
@app.get("/register")
|
|
async def register_redirect():
|
|
return RedirectResponse("/auth/register", status_code=303)
|
|
|
|
@app.get("/profile")
|
|
async def profile_redirect():
|
|
return RedirectResponse("/auth/profile", status_code=303)
|
|
|
|
@app.get("/logout")
|
|
async def logout_redirect():
|
|
return RedirectResponse("/auth/logout", status_code=303)
|