Refactor authentication and session management
- Updated Dockerfile to use Python 3.13 and streamline system dependencies installation. - Enhanced user session handling in auth.py, introducing a centralized method for retrieving the current user from the session. - Removed deprecated auth routes and consolidated logic for user authentication and profile management. - Improved error handling and user feedback in redeem.py and teams.py, ensuring users are redirected to login when not authenticated. - Updated requirements.txt to include necessary packages for enhanced security and functionality. - Added phpMyAdmin service to docker-compose for easier database management.
This commit is contained in:
@@ -1,560 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Authentication routes for user login, registration, and management.
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Request, Form, Response
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import inspect
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, Dict, Any
|
||||
import smtplib
|
||||
from email.message import EmailMessage
|
||||
import os
|
||||
|
||||
from ..db import SessionLocal, engine
|
||||
from ..models import User
|
||||
from ..security import (
|
||||
verify_password, get_password_hash, create_access_token, generate_token,
|
||||
SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES
|
||||
)
|
||||
from ..dependencies import get_db, get_current_user, get_user_from_session
|
||||
from ..templates_config import templates
|
||||
from ..schemas import UserCreate, UserLogin, UserUpdate, PasswordReset
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Check if all required user columns exist
|
||||
def get_available_user_columns():
|
||||
inspector = inspect(engine)
|
||||
if 'users' in inspector.get_table_names():
|
||||
return [col['name'] for col in inspector.get_columns('users')]
|
||||
return []
|
||||
|
||||
# --- HTML ROUTES (Web UI) ---
|
||||
|
||||
@router.get("/login", response_class=HTMLResponse)
|
||||
async def login_page(request: Request, next: str = "/"):
|
||||
"""Display login page."""
|
||||
# Debug print to check what's happening in the route
|
||||
print("Login page accessed, checking session...")
|
||||
|
||||
# DO NOT redirect if user is logged in - there's likely an issue with session handling
|
||||
# Just render the login page regardless of session state for now
|
||||
return templates.TemplateResponse(
|
||||
"auth/login.html",
|
||||
{
|
||||
"request": request,
|
||||
"next": next,
|
||||
"messages": []
|
||||
}
|
||||
)
|
||||
|
||||
@router.post("/login", response_class=HTMLResponse)
|
||||
async def login(
|
||||
request: Request,
|
||||
response: Response,
|
||||
db: Session = Depends(get_db),
|
||||
username: str = Form(...),
|
||||
password: str = Form(...),
|
||||
remember: bool = Form(False),
|
||||
next: str = Form("/")
|
||||
):
|
||||
"""Process login form."""
|
||||
# Try to authenticate user
|
||||
user = db.query(User).filter((User.username == username) | (User.email == username)).first()
|
||||
|
||||
if not user or not verify_password(password, user.hashed_password):
|
||||
return templates.TemplateResponse(
|
||||
"auth/login.html",
|
||||
{
|
||||
"request": request,
|
||||
"next": next,
|
||||
"messages": [{"type": "error", "text": "Invalid username or password"}],
|
||||
"username": username
|
||||
},
|
||||
status_code=status.HTTP_401_UNAUTHORIZED
|
||||
)
|
||||
|
||||
# Check if is_active column exists and if user is active
|
||||
columns = get_available_user_columns()
|
||||
if 'is_active' in columns and hasattr(user, 'is_active') and not user.is_active:
|
||||
return templates.TemplateResponse(
|
||||
"auth/login.html",
|
||||
{
|
||||
"request": request,
|
||||
"next": next,
|
||||
"messages": [{"type": "error", "text": "Account is deactivated"}],
|
||||
"username": username
|
||||
},
|
||||
status_code=status.HTTP_401_UNAUTHORIZED
|
||||
)
|
||||
|
||||
# Update last login time if column exists
|
||||
if 'last_login' in columns and hasattr(user, 'last_login'):
|
||||
user.last_login = datetime.utcnow()
|
||||
db.commit()
|
||||
|
||||
# Debug info
|
||||
print(f"User authenticated: {user.username}")
|
||||
|
||||
# Set session data with better error handling
|
||||
try:
|
||||
# Use directly accessible dictionary
|
||||
request.session["user_id"] = user.id
|
||||
request.session["username"] = user.username
|
||||
request.session["_permanent"] = True
|
||||
|
||||
# Check if is_admin attribute exists
|
||||
if hasattr(user, "is_admin"):
|
||||
request.session["is_admin"] = user.is_admin
|
||||
else:
|
||||
request.session["is_admin"] = False
|
||||
|
||||
# Add timestamp for session creation
|
||||
request.session["created_at"] = str(datetime.now())
|
||||
|
||||
# Debug session data
|
||||
print(f"Session data set: {dict(request.session)}")
|
||||
except Exception as e:
|
||||
print(f"Error setting session: {str(e)}")
|
||||
|
||||
# Redirect to next page or home
|
||||
return RedirectResponse(url=next, status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
@router.get("/register", response_class=HTMLResponse)
|
||||
async def register_page(request: Request):
|
||||
"""Display registration page."""
|
||||
# Check if user is already logged in
|
||||
user = await get_user_from_session(request)
|
||||
if user:
|
||||
return RedirectResponse(url="/")
|
||||
|
||||
return templates.TemplateResponse("auth/register.html", {"request": request})
|
||||
|
||||
@router.post("/register", response_class=HTMLResponse)
|
||||
async def register(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
username: str = Form(...),
|
||||
email: str = Form(...),
|
||||
password: str = Form(...),
|
||||
confirm_password: str = Form(...),
|
||||
):
|
||||
"""Process registration form."""
|
||||
# Validate the form data
|
||||
if password != confirm_password:
|
||||
return templates.TemplateResponse(
|
||||
"auth/register.html",
|
||||
{
|
||||
"request": request,
|
||||
"messages": [{"type": "error", "text": "Passwords do not match"}],
|
||||
"username": username,
|
||||
"email": email
|
||||
}
|
||||
)
|
||||
|
||||
# Check if username already exists
|
||||
if db.query(User).filter(User.username == username).first():
|
||||
return templates.TemplateResponse(
|
||||
"auth/register.html",
|
||||
{
|
||||
"request": request,
|
||||
"messages": [{"type": "error", "text": "Username already exists"}],
|
||||
"username": username,
|
||||
"email": email
|
||||
}
|
||||
)
|
||||
|
||||
# Check if email already exists
|
||||
if db.query(User).filter(User.email == email).first():
|
||||
return templates.TemplateResponse(
|
||||
"auth/register.html",
|
||||
{
|
||||
"request": request,
|
||||
"messages": [{"type": "error", "text": "Email already exists"}],
|
||||
"username": username,
|
||||
"email": email
|
||||
}
|
||||
)
|
||||
|
||||
# Create new user
|
||||
hashed_password = get_password_hash(password)
|
||||
|
||||
# Get available columns
|
||||
columns = get_available_user_columns()
|
||||
user_data = {
|
||||
"username": username,
|
||||
"email": email,
|
||||
"hashed_password": hashed_password
|
||||
}
|
||||
|
||||
# Add optional fields only if they exist in the database
|
||||
if 'is_active' in columns:
|
||||
user_data["is_active"] = True
|
||||
if 'is_verified' in columns:
|
||||
user_data["is_verified"] = False
|
||||
if 'verification_token' in columns:
|
||||
user_data["verification_token"] = generate_token()
|
||||
|
||||
new_user = User(**user_data)
|
||||
|
||||
db.add(new_user)
|
||||
db.commit()
|
||||
db.refresh(new_user)
|
||||
|
||||
# In a real app, send verification email here
|
||||
# For now, just redirect to a success page
|
||||
|
||||
# Log the user in
|
||||
request.session["user_id"] = new_user.id
|
||||
|
||||
return RedirectResponse(
|
||||
url="/auth/registration-success",
|
||||
status_code=status.HTTP_303_SEE_OTHER
|
||||
)
|
||||
|
||||
@router.get("/registration-success", response_class=HTMLResponse)
|
||||
async def registration_success(request: Request):
|
||||
"""Display registration success page."""
|
||||
return templates.TemplateResponse("auth/registration_success.html", {"request": request})
|
||||
|
||||
@router.get("/logout")
|
||||
async def logout(request: Request):
|
||||
"""Log user out by clearing session."""
|
||||
request.session.clear()
|
||||
return RedirectResponse(url="/")
|
||||
|
||||
@router.get("/profile", response_class=HTMLResponse)
|
||||
async def profile_page(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Display user profile page."""
|
||||
user = await get_user_from_session(request)
|
||||
if not user:
|
||||
return RedirectResponse(url="/auth/login?next=/auth/profile")
|
||||
|
||||
# Get user's teams (using fresh user object from database to ensure relationships are loaded)
|
||||
user = db.query(User).filter(User.id == user.id).first()
|
||||
|
||||
user_teams = []
|
||||
for membership in user.memberships:
|
||||
user_teams.append({
|
||||
"team": membership.team,
|
||||
"is_admin": membership.is_admin
|
||||
})
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"auth/profile.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"user_teams": user_teams
|
||||
}
|
||||
)
|
||||
|
||||
@router.post("/update-profile", response_class=HTMLResponse)
|
||||
async def update_profile(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
username: str = Form(None),
|
||||
email: str = Form(None),
|
||||
):
|
||||
"""Update user profile information."""
|
||||
user = await get_user_from_session(request)
|
||||
if not user:
|
||||
return RedirectResponse(url="/auth/login?next=/auth/profile")
|
||||
|
||||
# Check for username collision
|
||||
if username and username != user.username:
|
||||
existing_user = db.query(User).filter(User.username == username).first()
|
||||
if existing_user:
|
||||
return templates.TemplateResponse(
|
||||
"auth/profile.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"messages": [{"type": "error", "text": "Username already exists"}]
|
||||
}
|
||||
)
|
||||
user.username = username
|
||||
|
||||
# Check for email collision
|
||||
if email and email != user.email:
|
||||
existing_user = db.query(User).filter(User.email == email).first()
|
||||
if existing_user:
|
||||
return templates.TemplateResponse(
|
||||
"auth/profile.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"messages": [{"type": "error", "text": "Email already exists"}]
|
||||
}
|
||||
)
|
||||
user.email = email
|
||||
|
||||
db.commit()
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"auth/profile.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"messages": [{"type": "success", "text": "Profile updated successfully"}]
|
||||
}
|
||||
)
|
||||
|
||||
@router.post("/change-password", response_class=HTMLResponse)
|
||||
async def change_password(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_password: str = Form(...),
|
||||
new_password: str = Form(...),
|
||||
confirm_password: str = Form(...),
|
||||
):
|
||||
"""Change user password."""
|
||||
user = await get_user_from_session(request)
|
||||
if not user:
|
||||
return RedirectResponse(url="/auth/login?next=/auth/profile")
|
||||
|
||||
# Verify current password
|
||||
if not verify_password(current_password, user.hashed_password):
|
||||
return templates.TemplateResponse(
|
||||
"auth/profile.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"messages": [{"type": "error", "text": "Current password is incorrect"}]
|
||||
}
|
||||
)
|
||||
|
||||
# Check if new passwords match
|
||||
if new_password != confirm_password:
|
||||
return templates.TemplateResponse(
|
||||
"auth/profile.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"messages": [{"type": "error", "text": "New passwords do not match"}]
|
||||
}
|
||||
)
|
||||
|
||||
# Update password
|
||||
user.hashed_password = get_password_hash(new_password)
|
||||
db.commit()
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"auth/profile.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"messages": [{"type": "success", "text": "Password changed successfully"}]
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/forgot-password", response_class=HTMLResponse)
|
||||
async def forgot_password_page(request: Request):
|
||||
"""Display forgot password page."""
|
||||
return templates.TemplateResponse("auth/forgot_password.html", {"request": request})
|
||||
|
||||
@router.post("/forgot-password")
|
||||
async def forgot_password(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
email: str = Form(...)
|
||||
):
|
||||
"""Process forgot password form."""
|
||||
# Find user by email
|
||||
user = db.query(User).filter(User.email == email).first()
|
||||
|
||||
# Always show success to prevent email enumeration
|
||||
if not user:
|
||||
return templates.TemplateResponse(
|
||||
"auth/forgot_password_sent.html",
|
||||
{"request": request}
|
||||
)
|
||||
|
||||
# Generate reset token
|
||||
reset_token = generate_token()
|
||||
user.reset_token = reset_token
|
||||
user.reset_token_expires_at = datetime.utcnow() + timedelta(hours=1)
|
||||
db.commit()
|
||||
|
||||
# In a real app, send email with reset link
|
||||
# For demo, just show the reset link on the success page
|
||||
reset_url = f"/auth/reset-password?token={reset_token}"
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"auth/forgot_password_sent.html",
|
||||
{
|
||||
"request": request,
|
||||
"reset_url": reset_url # Remove in production, just for demo
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/reset-password", response_class=HTMLResponse)
|
||||
async def reset_password_page(
|
||||
request: Request,
|
||||
token: str,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Display reset password page."""
|
||||
# Check if token exists and is valid
|
||||
user = db.query(User).filter(
|
||||
User.reset_token == token,
|
||||
User.reset_token_expires_at > datetime.utcnow()
|
||||
).first()
|
||||
|
||||
if not user:
|
||||
return templates.TemplateResponse(
|
||||
"auth/reset_password_error.html",
|
||||
{"request": request}
|
||||
)
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"auth/reset_password.html",
|
||||
{"request": request, "token": token}
|
||||
)
|
||||
|
||||
@router.post("/reset-password")
|
||||
async def reset_password(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
token: str = Form(...),
|
||||
new_password: str = Form(...),
|
||||
confirm_password: str = Form(...)
|
||||
):
|
||||
"""Process reset password form."""
|
||||
# Check if token exists and is valid
|
||||
user = db.query(User).filter(
|
||||
User.reset_token == token,
|
||||
User.reset_token_expires_at > datetime.utcnow()
|
||||
).first()
|
||||
|
||||
if not user:
|
||||
return templates.TemplateResponse(
|
||||
"auth/reset_password_error.html",
|
||||
{"request": request}
|
||||
)
|
||||
|
||||
# Check if passwords match
|
||||
if new_password != confirm_password:
|
||||
return templates.TemplateResponse(
|
||||
"auth/reset_password.html",
|
||||
{
|
||||
"request": request,
|
||||
"token": token,
|
||||
"messages": [{"type": "error", "text": "Passwords do not match"}]
|
||||
}
|
||||
)
|
||||
|
||||
# Update password
|
||||
user.hashed_password = get_password_hash(new_password)
|
||||
user.reset_token = None
|
||||
user.reset_token_expires_at = None
|
||||
db.commit()
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"auth/reset_password_success.html",
|
||||
{"request": request}
|
||||
)
|
||||
|
||||
@router.get("/delete-account", response_class=HTMLResponse)
|
||||
async def delete_account_page(request: Request):
|
||||
"""Display delete account confirmation page."""
|
||||
user = await get_user_from_session(request)
|
||||
if not user:
|
||||
return RedirectResponse(url="/auth/login?next=/auth/delete-account")
|
||||
|
||||
return templates.TemplateResponse("auth/delete_account.html", {"request": request})
|
||||
|
||||
@router.post("/delete-account")
|
||||
async def delete_account(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
password: str = Form(...)
|
||||
):
|
||||
"""Process account deletion."""
|
||||
user = await get_user_from_session(request)
|
||||
if not user:
|
||||
return RedirectResponse(url="/auth/login?next=/auth/delete-account")
|
||||
|
||||
# Verify password
|
||||
if not verify_password(password, user.hashed_password):
|
||||
return templates.TemplateResponse(
|
||||
"auth/delete_account.html",
|
||||
{
|
||||
"request": request,
|
||||
"messages": [{"type": "error", "text": "Incorrect password"}]
|
||||
}
|
||||
)
|
||||
|
||||
# In a real app, you might want to anonymize the user data instead
|
||||
# of deleting it completely, but for this demo we'll delete
|
||||
|
||||
# Clear session
|
||||
request.session.clear()
|
||||
|
||||
# Delete user
|
||||
db.delete(user)
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
# --- API ROUTES (for potential SPA frontend) ---
|
||||
|
||||
@router.post("/token")
|
||||
async def login_api(
|
||||
form_data: OAuth2PasswordRequestForm = Depends(),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""API login endpoint returning JWT token."""
|
||||
# Authenticate user
|
||||
user = db.query(User).filter((User.username == form_data.username) | (User.email == form_data.username)).first()
|
||||
|
||||
if not user or not verify_password(form_data.password, user.hashed_password):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
columns = get_available_user_columns()
|
||||
if 'is_active' in columns and hasattr(user, 'is_active') and not user.is_active:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Inactive user",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
if 'last_login' in columns and hasattr(user, 'last_login'):
|
||||
user.last_login = datetime.utcnow()
|
||||
db.commit()
|
||||
|
||||
# Create access token
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user.username},
|
||||
expires_delta=access_token_expires
|
||||
)
|
||||
|
||||
return {
|
||||
"access_token": access_token,
|
||||
"token_type": "bearer",
|
||||
"user_id": user.id,
|
||||
"username": user.username
|
||||
}
|
||||
|
||||
@router.get("/session-test")
|
||||
async def test_session(request: Request):
|
||||
"""Test endpoint to verify session data persistence"""
|
||||
has_session = hasattr(request, "session")
|
||||
session_data = dict(request.session) if has_session else {}
|
||||
|
||||
return {
|
||||
"has_session": has_session,
|
||||
"session_data": session_data,
|
||||
"authenticated": "user_id" in session_data
|
||||
}
|
||||
+33
-10
@@ -8,6 +8,8 @@ from sqlalchemy.orm import Session
|
||||
from ..db import SessionLocal
|
||||
from ..models import QRTicket, User, Team, TeamMembership
|
||||
from ..templates_config import templates
|
||||
from ..dependencies import get_user_from_session
|
||||
from ..auth import require_login
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -24,19 +26,30 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)):
|
||||
Display a page to let the user choose which team to apply points to.
|
||||
If not logged in, prompt them.
|
||||
"""
|
||||
# In real app, you'd check user session or redirect to login
|
||||
ticket = db.query(QRTicket).filter_by(code=code, used=False).first()
|
||||
if not ticket:
|
||||
return "Invalid or already used code."
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{
|
||||
"request": request,
|
||||
"error_title": "Invalid Code",
|
||||
"error_message": "This code is invalid or has already been used."
|
||||
}
|
||||
)
|
||||
|
||||
# Skeleton: you'd get the user's teams from session user
|
||||
# For now, we mock a user ID = 1:
|
||||
user = db.query(User).filter_by(id=1).first()
|
||||
# Get the authenticated user
|
||||
user = get_user_from_session(request, db)
|
||||
if not user:
|
||||
return "User not found. Please log in."
|
||||
# Store the redeem URL for after login - with try/except
|
||||
try:
|
||||
if hasattr(request, "session") and isinstance(request.session, dict):
|
||||
request.session["redirect_after_login"] = f"/redeem/{code}"
|
||||
except Exception as e:
|
||||
print(f"Error setting redirect URL in session: {str(e)}")
|
||||
return RedirectResponse(url="/auth/login", status_code=302)
|
||||
|
||||
# This is where you'd show the team selection or "create new team" UI
|
||||
user_teams = [m.team for m in user.memberships]
|
||||
# Get user teams
|
||||
user_teams = [m.team for m in user.memberships] if hasattr(user, 'memberships') else []
|
||||
|
||||
return templates.TemplateResponse("redeem.html", {
|
||||
"request": request,
|
||||
@@ -45,6 +58,7 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)):
|
||||
})
|
||||
|
||||
@router.post("/apply/{code}")
|
||||
@require_login
|
||||
async def apply_code(
|
||||
request: Request,
|
||||
code: str,
|
||||
@@ -54,6 +68,11 @@ async def apply_code(
|
||||
Apply the QR code to a selected team (if user is a member),
|
||||
or set to pending if user isn't a member yet.
|
||||
"""
|
||||
# Get the authenticated user
|
||||
user = get_user_from_session(request, db)
|
||||
if not user:
|
||||
return RedirectResponse(url=f"/auth/login?next=/redeem/{code}", status_code=302)
|
||||
|
||||
# Get form data
|
||||
form_data = await request.form()
|
||||
team_id = int(form_data.get("team_id", 0))
|
||||
@@ -79,8 +98,6 @@ async def apply_code(
|
||||
}
|
||||
)
|
||||
|
||||
# For skeleton, assume user = 1
|
||||
user = db.query(User).filter_by(id=1).first()
|
||||
team = db.query(Team).filter_by(id=team_id).first()
|
||||
|
||||
if not team:
|
||||
@@ -129,6 +146,7 @@ async def apply_code(
|
||||
)
|
||||
|
||||
@router.post("/manual")
|
||||
@require_login
|
||||
async def manual_code_entry(
|
||||
request: Request,
|
||||
code: str = Form(...),
|
||||
@@ -138,6 +156,11 @@ async def manual_code_entry(
|
||||
Handle manual code entry from the form.
|
||||
This redirects to the normal redeem flow after validating the code.
|
||||
"""
|
||||
# Make sure the user is authenticated
|
||||
user = get_user_from_session(request, db)
|
||||
if not user:
|
||||
return RedirectResponse(url="/auth/login?next=/dashboard/", status_code=302)
|
||||
|
||||
# Check if the code exists
|
||||
ticket = db.query(QRTicket).filter_by(code=code, used=False).first()
|
||||
|
||||
|
||||
+24
-23
@@ -13,6 +13,8 @@ from ..db import SessionLocal
|
||||
from ..models import Team, TeamMembership, User, QRTicket, TeamAchievement
|
||||
from ..schemas import TeamCreate
|
||||
from ..templates_config import templates
|
||||
from ..dependencies import get_user_from_session
|
||||
from ..auth import require_login
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -23,27 +25,12 @@ def get_db():
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def get_or_create_default_user(db: Session):
|
||||
"""Get user ID 1 or create it if it doesn't exist."""
|
||||
user = db.query(User).filter_by(id=1).first()
|
||||
if not user:
|
||||
# Create a default user
|
||||
user = User(
|
||||
username="default_user",
|
||||
email="default@example.com",
|
||||
hashed_password="placeholder"
|
||||
)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
def list_teams(request: Request, db: Session = Depends(get_db)):
|
||||
teams = db.query(Team).all()
|
||||
|
||||
# Get the user's teams to highlight teams they're already in
|
||||
user = db.query(User).filter_by(id=1).first()
|
||||
user = get_user_from_session(request, db)
|
||||
user_team_ids = []
|
||||
|
||||
if user:
|
||||
@@ -67,8 +54,11 @@ def list_teams(request: Request, db: Session = Depends(get_db)):
|
||||
)
|
||||
|
||||
@router.post("/create")
|
||||
def create_team(name: str = Form(...), db: Session = Depends(get_db)):
|
||||
user = get_or_create_default_user(db)
|
||||
@require_login
|
||||
def create_team(request: Request, name: str = Form(...), db: Session = Depends(get_db)):
|
||||
user = get_user_from_session(request, db)
|
||||
if not user:
|
||||
return RedirectResponse("/auth/login?next=/teams/", status_code=302)
|
||||
|
||||
# Create team
|
||||
new_team = Team(name=name)
|
||||
@@ -84,8 +74,11 @@ def create_team(name: str = Form(...), db: Session = Depends(get_db)):
|
||||
return RedirectResponse("/teams/", status_code=303)
|
||||
|
||||
@router.post("/join/{team_id}")
|
||||
def join_team(team_id: int, db: Session = Depends(get_db)):
|
||||
user = get_or_create_default_user(db)
|
||||
@require_login
|
||||
def join_team(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
user = get_user_from_session(request, db)
|
||||
if not user:
|
||||
return RedirectResponse("/auth/login?next=/teams/", status_code=302)
|
||||
|
||||
team = db.query(Team).filter_by(id=team_id).first()
|
||||
if not team:
|
||||
@@ -111,8 +104,11 @@ def team_detail(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
if not team:
|
||||
raise HTTPException(status_code=404, detail="Team not found")
|
||||
|
||||
# Get current user - using a default user for now
|
||||
user = get_or_create_default_user(db)
|
||||
# Get current user from session
|
||||
user = get_user_from_session(request, db)
|
||||
if not user:
|
||||
# If no authenticated user, redirect to login
|
||||
return RedirectResponse("/auth/login?next=/teams/"+str(team_id), status_code=302)
|
||||
|
||||
# Get team members with admin status
|
||||
memberships = db.query(TeamMembership).filter_by(team_id=team_id).all()
|
||||
@@ -248,14 +244,19 @@ def team_detail(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
)
|
||||
|
||||
@router.post("/{team_id}/update")
|
||||
@require_login
|
||||
def update_team(
|
||||
request: Request,
|
||||
team_id: int,
|
||||
team_name: str = Form(...),
|
||||
is_public: bool = Form(False),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Update team details."""
|
||||
user = get_or_create_default_user(db)
|
||||
user = get_user_from_session(request, db)
|
||||
if not user:
|
||||
return RedirectResponse("/auth/login?next=/teams/"+str(team_id), status_code=302)
|
||||
|
||||
team = db.query(Team).filter_by(id=team_id).first()
|
||||
|
||||
if not team:
|
||||
|
||||
Reference in New Issue
Block a user