Implement team join request functionality with views and actions

- Added join_requests.html template for displaying pending join requests.
- Created join_team.html template for users to submit join requests.
- Implemented request_processed.html template to show the result of join request processing.
- Developed authentication utilities for user session management.
- Introduced convenience redirects for common URL patterns.
- Established team management routes and actions for creating, editing, and joining teams.
- Added functionality for approving and denying join requests with email notifications.
- Enhanced team views to include user permissions and team member details.
- Implemented utility functions for team-related operations such as calculating total points and team rank.
This commit is contained in:
Christian Krakau-Louis
2025-04-14 17:00:57 +02:00
parent 5cf3f944b1
commit 7323c12168
26 changed files with 2138 additions and 319 deletions
+52 -52
View File
@@ -3,6 +3,7 @@ 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
@@ -13,7 +14,7 @@ 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 .views import qr, redeem, teams, admin, leaderboard, dashboard, static, pages, auth, convenience
from .db_init import seed_db
from .db_migrations import apply_migrations
@@ -27,9 +28,18 @@ 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=["*"],
)
# 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"))
SECRET_KEY = os.getenv("SECRET_KEY", "a-very-secure-secret-key-for-development")
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY)
# Initialize database on startup
@app.on_event("startup")
@@ -69,68 +79,58 @@ templates = Jinja2Templates(directory="app/templates")
# User context middleware
@app.middleware("http")
async def add_template_globals(request: Request, call_next):
"""Add template globals"""
"""Add global variables to all templates."""
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
# 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)}")
# Process the request
request.state.user = None
# Continue with 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)
}
# 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(
"index.html",
{"request": request, "user": user}
"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) # Include the auth router
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(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(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)
app.include_router(convenience.router, tags=["Convenience"]) # Include convenience routes