Refactor user authentication and dashboard features
- Added `is_admin` field to User model for role management. - Updated user context middleware to include current user in templates. - Enhanced session management with improved debug middleware. - Refactored dashboard view to fetch user-specific data and recent events. - Improved login and registration templates for better user experience. - Added admin routes with access control for admin users. - Updated Docker configuration for better error logging and dependency management. - Updated requirements to include new dependencies and specify versions.
This commit is contained in:
@@ -13,6 +13,7 @@ import inspect as py_inspect
|
||||
from ..db import SessionLocal, Base
|
||||
from ..models import User, Team, TeamMembership, QRTicket
|
||||
from ..templates_config import templates
|
||||
from ..auth import require_admin
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -64,6 +65,7 @@ def get_relationships(model_class: Type[Base]) -> Dict[str, str]:
|
||||
return relationships
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
@require_admin
|
||||
async def admin_home(request: Request):
|
||||
"""Admin dashboard home."""
|
||||
model_list = [(key, name) for key, (_, name) in MODELS.items()]
|
||||
@@ -73,6 +75,7 @@ async def admin_home(request: Request):
|
||||
)
|
||||
|
||||
@router.get("/{model_name}", response_class=HTMLResponse)
|
||||
@require_admin
|
||||
async def list_records(
|
||||
request: Request,
|
||||
model_name: str,
|
||||
@@ -124,6 +127,7 @@ async def list_records(
|
||||
)
|
||||
|
||||
@router.get("/{model_name}/new", response_class=HTMLResponse)
|
||||
@require_admin
|
||||
async def create_record_form(
|
||||
request: Request,
|
||||
model_name: str,
|
||||
@@ -164,6 +168,7 @@ async def create_record_form(
|
||||
)
|
||||
|
||||
@router.post("/{model_name}/new")
|
||||
@require_admin
|
||||
async def create_record(
|
||||
request: Request,
|
||||
model_name: str,
|
||||
@@ -212,6 +217,7 @@ async def create_record(
|
||||
return RedirectResponse(f"/admin/{model_name}", status_code=303)
|
||||
|
||||
@router.get("/{model_name}/{record_id}", response_class=HTMLResponse)
|
||||
@require_admin
|
||||
async def edit_record_form(
|
||||
request: Request,
|
||||
model_name: str,
|
||||
@@ -263,6 +269,7 @@ async def edit_record_form(
|
||||
)
|
||||
|
||||
@router.post("/{model_name}/{record_id}")
|
||||
@require_admin
|
||||
async def update_record(
|
||||
request: Request,
|
||||
model_name: str,
|
||||
@@ -311,7 +318,9 @@ async def update_record(
|
||||
return RedirectResponse(f"/admin/{model_name}", status_code=303)
|
||||
|
||||
@router.get("/{model_name}/{record_id}/delete")
|
||||
@require_admin
|
||||
async def delete_record(
|
||||
request: Request,
|
||||
model_name: str,
|
||||
record_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
|
||||
+75
-128
@@ -1,137 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Dashboard views for user-specific information.
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, Request, Form
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from fastapi import APIRouter, Request, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.sql import func
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
|
||||
from ..db import SessionLocal
|
||||
from ..models import User, Team, TeamMembership, QRTicket
|
||||
from ..db import get_db
|
||||
from ..templates_config import templates
|
||||
from ..auth import require_login
|
||||
from .. import models
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
@router.get("/")
|
||||
@require_login
|
||||
def user_dashboard(request: Request, db: Session = Depends(get_db)):
|
||||
"""User dashboard showing teams, events and stats"""
|
||||
try:
|
||||
yield 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"
|
||||
user = request.session.get("user")
|
||||
user_id = user.get("id")
|
||||
|
||||
# Initialize default values in case of errors
|
||||
team_count = 0
|
||||
total_points = 0
|
||||
event_count = 0
|
||||
recent_events = []
|
||||
user_teams = []
|
||||
|
||||
# Check if TeamMember model exists before querying
|
||||
if hasattr(models, "TeamMember"):
|
||||
# Get the team count for this user
|
||||
team_count = db.query(func.count(models.TeamMember.team_id))\
|
||||
.filter(models.TeamMember.user_id == user_id)\
|
||||
.scalar() or 0
|
||||
|
||||
# Get user teams
|
||||
user_teams = db.query(models.Team)\
|
||||
.join(models.TeamMember)\
|
||||
.filter(models.TeamMember.user_id == user_id)\
|
||||
.all()
|
||||
|
||||
# Check if UserPoints model exists before querying
|
||||
if hasattr(models, "UserPoints"):
|
||||
# Get the total points safely
|
||||
total_points_result = db.query(func.sum(models.UserPoints.points))\
|
||||
.filter(models.UserPoints.user_id == user_id)\
|
||||
.first()
|
||||
|
||||
if total_points_result and total_points_result[0]:
|
||||
total_points = total_points_result[0]
|
||||
|
||||
# Check if EventAttendee model exists before querying
|
||||
if hasattr(models, "EventAttendee") and hasattr(models, "Event"):
|
||||
# Get event count safely
|
||||
event_count_result = db.query(func.count(models.EventAttendee.event_id))\
|
||||
.filter(models.EventAttendee.user_id == user_id)\
|
||||
.first()
|
||||
|
||||
if event_count_result and event_count_result[0]:
|
||||
event_count = event_count_result[0]
|
||||
|
||||
# Recent events - only if both models exist
|
||||
recent_events = db.query(models.Event)\
|
||||
.join(models.EventAttendee)\
|
||||
.filter(models.EventAttendee.user_id == user_id)\
|
||||
.order_by(models.Event.event_date.desc())\
|
||||
.limit(5)\
|
||||
.all()
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"dashboard/index.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"team_count": team_count,
|
||||
"total_points": total_points,
|
||||
"event_count": event_count,
|
||||
"recent_events": recent_events,
|
||||
"user_teams": user_teams
|
||||
}
|
||||
)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def user_dashboard(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Show the user's dashboard with team info and recent activity."""
|
||||
|
||||
# Get current user - using a default user for now
|
||||
# In a real app, this would come from auth system
|
||||
user = get_or_create_default_user(db)
|
||||
|
||||
# Get user's teams
|
||||
user_teams = db.query(Team).join(
|
||||
TeamMembership,
|
||||
TeamMembership.team_id == Team.id
|
||||
).filter(
|
||||
TeamMembership.user_id == user.id
|
||||
).all()
|
||||
|
||||
# Get team memberships with admin status
|
||||
team_memberships = db.query(
|
||||
TeamMembership
|
||||
).filter(
|
||||
TeamMembership.user_id == user.id
|
||||
).all()
|
||||
|
||||
admin_team_ids = [tm.team_id for tm in team_memberships if tm.is_admin]
|
||||
|
||||
# Get points per team
|
||||
team_points = {}
|
||||
for team in user_teams:
|
||||
points = db.query(func.sum(QRTicket.points)).filter(
|
||||
QRTicket.redeemed_at_team == team.id
|
||||
).scalar() or 0
|
||||
|
||||
# Get team ranking - simplified approach
|
||||
higher_teams = db.query(func.count(Team.id)).join(
|
||||
QRTicket,
|
||||
QRTicket.redeemed_at_team == Team.id
|
||||
).group_by(
|
||||
Team.id
|
||||
).having(
|
||||
func.sum(QRTicket.points) > points
|
||||
).scalar() or 0
|
||||
|
||||
rank = higher_teams + 1
|
||||
|
||||
team_points[team.id] = {
|
||||
'points': points,
|
||||
'rank': rank
|
||||
}
|
||||
|
||||
# Get recent activity
|
||||
# For simplicity, we're just getting recent QR code redemptions
|
||||
recent_activity = []
|
||||
|
||||
recent_tickets = db.query(QRTicket).filter(
|
||||
QRTicket.redeemed_by == user.id
|
||||
).order_by(
|
||||
QRTicket.id.desc() # Assuming higher ID = newer
|
||||
).limit(5).all()
|
||||
|
||||
for ticket in recent_tickets:
|
||||
team = db.query(Team).filter(Team.id == ticket.redeemed_at_team).first()
|
||||
activity = {
|
||||
'type': 'qr_redeem',
|
||||
'points': ticket.points,
|
||||
'team_name': team.name if team else "Unknown team",
|
||||
'date': "Recently" # Placeholder - would use ticket.created_at
|
||||
}
|
||||
recent_activity.append(activity)
|
||||
|
||||
# Get total points for user across all teams
|
||||
total_points = sum(team_data['points'] for team_data in team_points.values())
|
||||
|
||||
# Get best ranking
|
||||
best_rank = min(team_data['rank'] for team_data in team_points.values()) if team_points else None
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"dashboard.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"teams": user_teams,
|
||||
"team_points": team_points,
|
||||
"admin_team_ids": admin_team_ids,
|
||||
"recent_activity": recent_activity,
|
||||
"total_points": total_points,
|
||||
"best_rank": best_rank,
|
||||
"team_count": len(user_teams)
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/scan", response_class=HTMLResponse)
|
||||
async def scan_qr(request: Request):
|
||||
"""Show QR scanning interface."""
|
||||
return templates.TemplateResponse(
|
||||
"scan_qr.html",
|
||||
{"request": request}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Dashboard error: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Dashboard error: {str(e)}")
|
||||
|
||||
Reference in New Issue
Block a user