## Authentication Implementation ### Backend - Implemented complete Discord OAuth flow in auth.py: * POST /api/auth/discord/callback - Exchange code for tokens * POST /api/auth/refresh - Refresh JWT tokens * GET /api/auth/me - Get authenticated user info * GET /api/auth/verify - Verify auth status - JWT token creation with 7-day expiration - Refresh token support for session persistence - Bearer token authentication for Discord API calls ### Frontend - Created auth/login.vue - Discord OAuth initiation page - Created auth/callback.vue - OAuth callback handler with states - Integrated with existing auth store (already implemented) - LocalStorage persistence for tokens and user data - Full error handling and loading states ### Configuration - Updated backend .env with Discord OAuth credentials - Updated frontend .env with Discord Client ID - Fixed redirect URI to port 3001 ## SBA API Integration ### Backend - Extended SbaApiClient with get_teams(season, active_only=True) - Added bearer token auth support (_get_headers method) - Created /api/teams route with TeamResponse model - Registered teams router in main.py - Filters out IL (Injured List) teams automatically - Returns team data: id, abbrev, names, color, gmid, division ### Integration - Connected to production SBA API: https://api.sba.manticorum.com - Bearer token authentication working - Successfully fetches ~16 active Season 3 teams ## Documentation - Created SESSION_NOTES.md - Current session accomplishments - Created NEXT_SESSION.md - Game creation implementation guide - Updated implementation/NEXT_SESSION.md ## Testing - ✅ Discord OAuth flow tested end-to-end - ✅ User authentication and session persistence verified - ✅ Teams API returns real data from production - ✅ All services running and communicating ## What Works Now - User can sign in with Discord - Sessions persist across reloads - Backend fetches real teams from SBA API - Ready for game creation implementation ## Next Steps See .claude/NEXT_SESSION.md for detailed game creation implementation plan. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
104 lines
2.7 KiB
Python
104 lines
2.7 KiB
Python
import logging
|
|
from contextlib import asynccontextmanager
|
|
|
|
import socketio
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from app.api.routes import auth, games, health, teams
|
|
from app.config import get_settings
|
|
from app.database.session import init_db
|
|
from app.services import redis_client
|
|
from app.utils.logging import setup_logging
|
|
from app.websocket.connection_manager import ConnectionManager
|
|
from app.websocket.handlers import register_handlers
|
|
|
|
logger = logging.getLogger(f"{__name__}.main")
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
"""Startup and shutdown events"""
|
|
settings = get_settings()
|
|
|
|
# Startup
|
|
logger.info("Starting Paper Dynasty Game Backend")
|
|
setup_logging()
|
|
|
|
# Initialize database
|
|
await init_db()
|
|
logger.info("Database initialized")
|
|
|
|
# Initialize Redis
|
|
try:
|
|
redis_url = settings.redis_url
|
|
await redis_client.connect(redis_url)
|
|
logger.info(f"Redis initialized: {redis_url}")
|
|
except Exception as e:
|
|
logger.warning(
|
|
f"Redis connection failed: {e}. Position rating caching will be unavailable."
|
|
)
|
|
|
|
yield
|
|
|
|
# Shutdown
|
|
logger.info("Shutting down Paper Dynasty Game Backend")
|
|
|
|
# Disconnect Redis
|
|
if redis_client.is_connected:
|
|
await redis_client.disconnect()
|
|
logger.info("Redis disconnected")
|
|
|
|
|
|
# Initialize FastAPI app
|
|
app = FastAPI(
|
|
title="Paper Dynasty Game Backend",
|
|
description="Real-time baseball game engine for Paper Dynasty leagues",
|
|
version="1.0.0",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
# CORS middleware
|
|
settings = get_settings()
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=settings.cors_origins,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# Initialize Socket.io
|
|
sio = socketio.AsyncServer(
|
|
async_mode="asgi",
|
|
cors_allowed_origins=settings.cors_origins,
|
|
logger=True,
|
|
engineio_logger=False,
|
|
)
|
|
|
|
# Create Socket.io ASGI app
|
|
socket_app = socketio.ASGIApp(sio, app)
|
|
|
|
# Initialize connection manager and register handlers
|
|
connection_manager = ConnectionManager(sio)
|
|
register_handlers(sio, connection_manager)
|
|
|
|
# Include API routes
|
|
app.include_router(health.router, prefix="/api", tags=["health"])
|
|
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
|
|
app.include_router(games.router, prefix="/api/games", tags=["games"])
|
|
app.include_router(teams.router, prefix="/api/teams", tags=["teams"])
|
|
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
return {"message": "Paper Dynasty Game Backend", "version": "1.0.0"}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
|
|
uvicorn.run(
|
|
"app.main:socket_app", host="0.0.0.0", port=8000, reload=True, log_level="info"
|
|
)
|