Add full SkinsPins application
- FastAPI + SQLite + Alpine.js + HTMX PWA for Bingo Bango Bongo golf scoring - Passwordless magic link auth with email (aiosmtplib) and admin invite system - Real-time score entry via WebSocket with drag-and-drop player ordering - Course management with per-hole par and stroke index - Scorecard with golf score markers (birdie, eagle, bogey, etc.) - Two-step new game form with tee selection per player - Dashboard with stats, live games social stream, and recent game history - Game summary view (read-only scorecard with leaderboard) - User profile pages with win stats - PWA install support with generated PNG icons - Admin panel for users, courses, games, and invitations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import Request, Depends
|
||||
import aiosqlite
|
||||
|
||||
from app.database import get_db
|
||||
|
||||
ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "")
|
||||
SESSION_COOKIE = "session"
|
||||
|
||||
|
||||
class NeedsLogin(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NeedsOnboarding(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NeedsAdmin(Exception):
|
||||
pass
|
||||
|
||||
|
||||
async def get_current_user(request: Request, db: aiosqlite.Connection = Depends(get_db)):
|
||||
token = request.cookies.get(SESSION_COOKIE)
|
||||
if not token:
|
||||
raise NeedsLogin()
|
||||
|
||||
async with db.execute(
|
||||
"""
|
||||
SELECT u.id, u.email, u.name, u.handicap, u.avatar, u.is_onboarded
|
||||
FROM sessions s
|
||||
JOIN users u ON s.user_id = u.id
|
||||
WHERE s.token = ? AND s.expires_at > ?
|
||||
""",
|
||||
(token, datetime.now(timezone.utc).isoformat()),
|
||||
) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if row is None:
|
||||
raise NeedsLogin()
|
||||
|
||||
user = dict(row)
|
||||
|
||||
if not user["is_onboarded"] and request.url.path not in ("/onboarding", "/logout"):
|
||||
raise NeedsOnboarding()
|
||||
|
||||
return user
|
||||
|
||||
|
||||
async def require_admin(user: dict = Depends(get_current_user)):
|
||||
if user["email"] != ADMIN_EMAIL:
|
||||
raise NeedsAdmin()
|
||||
return user
|
||||
Reference in New Issue
Block a user