e90a907e9e
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e90a907e9e |
CLAUDE: Implement server-side OAuth flow with HttpOnly cookies
Fixes iPad Safari authentication issue where async JavaScript is blocked on OAuth callback pages after cross-origin redirects (Cloudflare + Safari ITP). **Problem**: iPad Safari blocks all async operations (Promises, setTimeout, onMounted) on the OAuth callback page, preventing frontend token exchange. **Solution**: Move entire OAuth flow to backend with HttpOnly cookies, eliminating JavaScript dependency on callback page. ## Backend Changes (7 files) ### New Files - app/services/oauth_state.py - Redis-based OAuth state management * CSRF protection with one-time use tokens (10min TTL) * Replaces frontend sessionStorage state validation - app/utils/cookies.py - HttpOnly cookie utilities * Access token: 1 hour, Path=/api * Refresh token: 7 days, Path=/api/auth * Security: HttpOnly, Secure (prod), SameSite=Lax ### Modified Files - app/api/routes/auth.py * NEW: GET /discord/login - Initiate OAuth with state creation * NEW: GET /discord/callback/server - Server-side callback handler * NEW: POST /logout - Clear auth cookies * UPDATED: GET /me - Cookie + header support (backwards compatible) * UPDATED: POST /refresh - Cookie + body support (backwards compatible) * FIXED: exchange_code_for_token() accepts redirect_uri parameter - app/config.py * Added discord_server_redirect_uri config * Added frontend_url config for post-auth redirects - app/websocket/handlers.py * Updated connect handler to parse cookies from environ * Falls back to auth object for backwards compatibility - .env.example * Added DISCORD_SERVER_REDIRECT_URI example * Added FRONTEND_URL example ## Frontend Changes (10 files) ### Core Auth Changes - store/auth.ts - Complete rewrite for cookie-based auth * Removed: token, refreshToken, tokenExpiresAt state (HttpOnly) * Added: checkAuth() - calls /api/auth/me with credentials * Updated: loginWithDiscord() - redirects to backend endpoint * Updated: logout() - calls backend logout endpoint * All $fetch calls use credentials: 'include' - pages/auth/callback.vue - Simplified to error handler * No JavaScript token exchange needed * Displays errors from query params * Verifies auth with checkAuth() on success - plugins/auth.client.ts * Changed from localStorage init to checkAuth() call * Async plugin to ensure auth state before navigation - middleware/auth.ts - Simplified * Removed token validity checks (HttpOnly cookies) * Simple isAuthenticated check ### Cleanup Changes - composables/useWebSocket.ts * Added withCredentials: true * Removed auth object with token * Updated canConnect to use isAuthenticated only - layouts/default.vue, layouts/game.vue, pages/index.vue, pages/games/[id].vue * Removed initializeAuth() calls (handled by plugin) ## Documentation - OAUTH_IPAD_ISSUE.md - Problem analysis and investigation notes - OAUTH_SERVER_SIDE_IMPLEMENTATION.md - Complete implementation guide * Security improvements summary * Discord Developer Portal setup instructions * Testing checklist * OAuth flow diagram ## Security Improvements - Tokens stored in HttpOnly cookies (XSS-safe) - OAuth state in Redis with one-time use (CSRF-safe) - Follows OAuth 2.0 Security Best Current Practice - Backwards compatible with Authorization header auth ## Testing - ✅ Backend OAuth endpoints functional - ✅ Token exchange with correct redirect_uri - ✅ Cookie-based auth working - ✅ WebSocket connection with cookies - ✅ Desktop browser flow verified - ⏳ iPad Safari testing pending Discord redirect URI config ## Next Steps 1. Add Discord redirect URI in Developer Portal: https://gameplay-demo.manticorum.com/api/auth/discord/callback/server 2. Test complete flow on iPad Safari 3. Verify WebSocket auto-reconnection with cookies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
a87d149788 |
CLAUDE: Implement game creation and lineup submission workflow
Complete implementation of pre-game setup flow allowing players to create games and submit lineups before gameplay starts. Backend Changes: - Extended games.py with create game, lineup submission, and game start endpoints - Added teams.py roster endpoint with season filtering - Enhanced SBA API client with player data fetching and caching - Comprehensive validation for lineup submission (position conflicts, DH rules) Frontend Changes: - Redesigned create.vue with improved team selection and game options - Enhanced index.vue with active/pending game filtering and navigation - Added lineup/[id].vue for interactive lineup builder with drag-and-drop - Implemented auth.client.ts plugin for client-side auth initialization - Added comprehensive TypeScript types for API contracts - Updated middleware for better auth handling Key Features: - Game creation with home/away team selection - Full lineup builder with position assignment and batting order - DH rule validation (pitcher can be excluded from batting order) - Season-based roster filtering (Season 3) - Auto-start game when both lineups submitted - Real-time game list updates Workflow: 1. Create game → select teams → set options 2. Submit home lineup → validate positions/order 3. Submit away lineup → validate positions/order 4. Game auto-starts → navigates to game page 5. WebSocket connection → loads game state Ready for Phase F4 - connecting gameplay UI to complete the at-bat loop. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
9b30d3dfb2 |
CLAUDE: Implement Discord OAuth authentication and SBA API integration
## 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> |
||
|
|
a4b99ee53e |
CLAUDE: Replace black and flake8 with ruff for formatting and linting
Migrated to ruff for faster, modern code formatting and linting: Configuration changes: - pyproject.toml: Added ruff 0.8.6, removed black/flake8 - Configured ruff with black-compatible formatting (88 chars) - Enabled comprehensive linting rules (pycodestyle, pyflakes, isort, pyupgrade, bugbear, comprehensions, simplify, return) - Updated CLAUDE.md: Changed code quality commands to use ruff Code improvements (490 auto-fixes): - Modernized type hints: List[T] → list[T], Dict[K,V] → dict[K,V], Optional[T] → T | None - Sorted all imports (isort integration) - Removed unused imports - Fixed whitespace issues - Reformatted 38 files for consistency Bug fixes: - app/core/play_resolver.py: Fixed type hint bug (any → Any) - tests/unit/core/test_runner_advancement.py: Removed obsolete random mock Testing: - All 739 unit tests passing (100%) - No regressions introduced 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
1a562a75d2 |
CLAUDE: Fix pitcher/catcher recovery and lineup data format
Backend fixes: - state_manager: Properly recover current_pitcher and current_catcher from fielding team during game state recovery (fixes pitcher badge not showing) - handlers: Add headshot field to lineup data, use lineup_service for proper player data loading on cache miss - lineup_service: Minor adjustments for headshot support Frontend fixes: - player.ts: Update Lineup type to match WebSocket event format - lineup_id (was 'id'), card_id fields - player.headshot for UI circles - Optional fields for event variations - CurrentSituation.vue: Adapt to updated type structure - Substitution selectors: Use updated Lineup type fields This fixes the issue where pitcher badge wouldn't show after game recovery because current_pitcher was being set from batting team instead of fielding team. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
b15f80310b |
CLAUDE: Add LineupService and SBA API client for player data integration
Created centralized services for SBA player data fetching at lineup creation: Backend - New Services: - app/services/sba_api_client.py: REST client for SBA API (api.sba.manticorum.com) with batch player fetching and caching support - app/services/lineup_service.py: High-level service combining DB operations with API calls for complete lineup entries with player data Backend - Refactored Components: - app/core/game_engine.py: Replaced raw API calls with LineupService, reduced _prepare_next_play() from ~50 lines to ~15 lines - app/core/substitution_manager.py: Updated pinch_hit(), defensive_replace(), change_pitcher() to use lineup_service.get_sba_player_data() - app/models/game_models.py: Added player_name/player_image to LineupPlayerState - app/services/__init__.py: Exported new LineupService components - app/websocket/handlers.py: Enhanced lineup state handling Frontend - SBA League: - components/Game/CurrentSituation.vue: Restored player images with fallback badges (P/B letters) for both mobile and desktop layouts - components/Game/GameBoard.vue: Enhanced game board visualization - composables/useGameActions.ts: Updated game action handling - composables/useWebSocket.ts: Improved WebSocket state management - pages/games/[id].vue: Enhanced game page with better state handling - types/game.ts: Updated type definitions - types/websocket.ts: Added WebSocket type support Architecture Improvement: All SBA player data fetching now goes through LineupService: - Lineup creation: add_sba_player_to_lineup() - Lineup loading: load_team_lineup_with_player_data() - Substitutions: get_sba_player_data() All 739 unit tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
eab61ad966 |
CLAUDE: Phases 3.5, F1-F5 Complete - Statistics & Frontend Components
This commit captures work from multiple sessions building the statistics system and frontend component library. Backend - Phase 3.5: Statistics System - Box score statistics with materialized views - Play stat calculator for real-time updates - Stat view refresher service - Alembic migration for materialized views - Test coverage: 41 new tests (all passing) Frontend - Phase F1: Foundation - Composables: useGameState, useGameActions, useWebSocket - Type definitions and interfaces - Store setup with Pinia Frontend - Phase F2: Game Display - ScoreBoard, GameBoard, CurrentSituation, PlayByPlay components - Demo page at /demo Frontend - Phase F3: Decision Inputs - DefensiveSetup, OffensiveApproach, StolenBaseInputs components - DecisionPanel orchestration - Demo page at /demo-decisions - Test coverage: 213 tests passing Frontend - Phase F4: Dice & Manual Outcome - DiceRoller component - ManualOutcomeEntry with validation - PlayResult display - GameplayPanel orchestration - Demo page at /demo-gameplay - Test coverage: 119 tests passing Frontend - Phase F5: Substitutions - PinchHitterSelector, DefensiveReplacementSelector, PitchingChangeSelector - SubstitutionPanel with tab navigation - Demo page at /demo-substitutions - Test coverage: 114 tests passing Documentation: - PHASE_3_5_HANDOFF.md - Statistics system handoff - PHASE_F2_COMPLETE.md - Game display completion - Frontend phase planning docs - NEXT_SESSION.md updated for Phase F6 Configuration: - Package updates (Nuxt 4 fixes) - Tailwind config enhancements - Game store updates Test Status: - Backend: 731/731 passing (100%) - Frontend: 446/446 passing (100%) - Total: 1,177 tests passing Next Phase: F6 - Integration (wire all components into game page) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
adf7c7646d |
CLAUDE: Phase 3E-Final - Redis Caching & X-Check WebSocket Integration
Completed Phase 3E-Final with Redis caching upgrade and WebSocket X-Check
integration for real-time defensive play resolution.
## Redis Caching System
### New Files
- app/services/redis_client.py - Async Redis client with connection pooling
* 10 connection pool size
* Automatic connect/disconnect lifecycle
* Ping health checks
* Environment-configurable via REDIS_URL
### Modified Files
- app/services/position_rating_service.py - Migrated from in-memory to Redis
* Redis key pattern: "position_ratings:{card_id}"
* TTL: 86400 seconds (24 hours)
* Graceful fallback if Redis unavailable
* Individual and bulk cache clearing (scan_iter)
* 760x performance improvement (0.274s API → 0.000361s Redis)
- app/main.py - Added Redis startup/shutdown events
* Connect on app startup with settings.redis_url
* Disconnect on shutdown
* Warning logged if Redis connection fails
- app/config.py - Added redis_url setting
* Default: "redis://localhost:6379/0"
* Override via REDIS_URL environment variable
- app/services/__init__.py - Export redis_client
### Testing
- test_redis_cache.py - Live integration test
* 10-step validation: connect, cache miss, cache hit, performance, etc.
* Verified 760x speedup with player 8807 (7 positions)
* Data integrity checks pass
## X-Check WebSocket Integration
### Modified Files
- app/websocket/handlers.py - Enhanced submit_manual_outcome handler
* Serialize XCheckResult to JSON when present
* Include x_check_details in play_resolved broadcast
* Fixed bug: Use result.outcome instead of submitted outcome
* Includes defender ratings, dice rolls, resolution steps
### New Files
- app/websocket/X_CHECK_FRONTEND_GUIDE.md - Comprehensive frontend documentation
* Event structure and field definitions
* Implementation examples (basic, enhanced, polished)
* Error handling and common pitfalls
* Test scenarios with expected data
* League differences (SBA vs PD)
* 500+ lines of frontend integration guide
- app/websocket/MANUAL_VS_AUTO_MODE.md - Workflow documentation
* Manual mode: Players read cards, submit outcomes
* Auto mode: System generates from ratings (PD only)
* X-Check resolution comparison
* UI recommendations for each mode
* Configuration reference
* Testing considerations
### Testing
- tests/integration/test_xcheck_websocket.py - WebSocket integration tests
* Test X-Check play includes x_check_details ✅
* Test non-X-Check plays don't include details ✅
* Full event structure validation
## Performance Impact
- Redis caching: 760x speedup for position ratings
- WebSocket: No performance impact (optional field)
- Graceful degradation: System works without Redis
## Phase 3E-Final Progress
- ✅ WebSocket event handlers for X-Check UI
- ✅ Frontend integration documentation
- ✅ Redis caching upgrade (from in-memory)
- ✅ Redis connection pool in app lifecycle
- ✅ Integration tests (2 WebSocket, 1 Redis)
- ✅ Manual vs Auto mode workflow documentation
Phase 3E-Final: 100% Complete
Phase 3 Overall: ~98% Complete
## Testing Results
All tests passing:
- X-Check table tests: 36/36 ✅
- WebSocket integration: 2/2 ✅
- Redis live test: 10/10 steps ✅
## Configuration
Development:
REDIS_URL=redis://localhost:6379/0 (Docker Compose)
Production options:
REDIS_URL=redis://10.10.0.42:6379/0 (DB server)
REDIS_URL=redis://your-redis-cloud.com:6379/0 (Managed)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
|
||
|
|
02e816a57f |
CLAUDE: Phase 3E-Main - Position Ratings Integration for X-Check Resolution
Complete integration of position ratings system enabling X-Check defensive plays to use actual player ratings from PD API with intelligent fallbacks for SBA. **Live API Testing Verified**: ✅ - Endpoint: GET https://pd.manticorum.com/api/v2/cardpositions?player_id=8807 - Response: 200 OK, 7 positions retrieved successfully - Cache performance: 16,601x faster (API: 0.214s, Cache: 0.000s) - Data quality: Real defensive ratings (range 1-5, error 0-88) **Architecture Overview**: - League-aware: PD league fetches ratings from API, SBA uses defaults - StateManager integration: Defenders retrieved from lineup cache - Self-contained GameState: All data needed for X-Check in memory - Graceful degradation: Falls back to league averages if ratings unavailable **Files Created**: 1. app/services/pd_api_client.py (NEW) - PdApiClient class for PD API integration - Endpoint: GET /api/v2/cardpositions?player_id={id}&position={pos} - Async HTTP client using httpx (already in requirements.txt) - Optional position filtering: get_position_ratings(8807, ['SS', '2B']) - Returns List[PositionRating] for all positions player can play - Handles both list and dict response formats - Comprehensive error handling with logging 2. app/services/position_rating_service.py (NEW) - PositionRatingService with in-memory caching - get_ratings_for_card(card_id, league_id) - All positions - get_rating_for_position(card_id, position, league_id) - Specific position - Cache performance: >16,000x faster on hits - Singleton pattern: position_rating_service instance - TODO Phase 3E-Final: Upgrade to Redis 3. app/services/__init__.py (NEW) - Package exports for clean imports 4. test_pd_api_live.py (NEW) - Live API integration test script - Tests with real PD player 8807 (7 positions) - Verifies caching, filtering, GameState integration - Run: `python test_pd_api_live.py` 5. test_pd_api_mock.py (NEW) - Mock integration test for CI/CD - Demonstrates flow without API dependency 6. tests/integration/test_position_ratings_api.py (NEW) - Pytest integration test suite - Real API tests with player 8807 - Cache verification, SBA skip logic - Full end-to-end GameState flow **Files Modified**: 1. app/models/game_models.py - LineupPlayerState: Added position_rating field (Optional[PositionRating]) - GameState: Added get_defender_for_position(position, state_manager) - Uses StateManager's lineup cache to find active defender by position - Iterates through lineup.players to match position + is_active 2. app/config/league_configs.py - SbaConfig: Added supports_position_ratings() → False - PdConfig: Added supports_position_ratings() → True - Enables league-specific behavior without hardcoded conditionals 3. app/core/play_resolver.py - __init__: Added state_manager parameter for X-Check defender lookup - _resolve_x_check(): Replaced placeholder defender ratings with actual lookup - Uses league config to check if ratings supported - Fetches defender via state.get_defender_for_position() - Falls back to defaults (range=3, error=15) if ratings unavailable - Detailed logging for debugging rating lookups 4. app/core/game_engine.py - Added _load_position_ratings_for_lineup() method - Loads all position ratings at game start for PD league - Skips loading for SBA (league config check) - start_game(): Calls rating loader for both teams before marking active - PlayResolver instantiation: Now passes state_manager parameter - Logs: "Loaded X/9 position ratings for team Y" **X-Check Resolution Flow**: 1. League check: config.supports_position_ratings()? 2. Get defender: state.get_defender_for_position(pos, state_manager) 3. If PD + defender.position_rating exists: Use actual range/error 4. Else if defender found: Use defaults (range=3, error=15) 5. Else: Log warning, use defaults **Position Rating Loading (Game Start)**: 1. Check if league supports ratings (PD only) 2. Get lineup from StateManager cache 3. For each player: - Fetch rating from position_rating_service (with caching) - Set player.position_rating field 4. Cache API responses (16,000x faster on subsequent access) 5. Log success: "Loaded X/9 position ratings for team Y" **Live Test Results (Player 8807)**: ``` Position Range Error Innings CF 3 2 372 2B 3 8 212 SS 4 12 159 RF 2 2 74 LF 3 2 62 1B 4 0 46 3B 3 65 34 ``` **Testing**: - ✅ Live API: Player 8807 → 7 positions retrieved successfully - ✅ Caching: 16,601x performance improvement - ✅ League config: SBA=False, PD=True - ✅ GameState integration: Defender lookup working - ✅ Existing tests: 27/28 config tests passing (1 pre-existing URL failure) - ✅ Syntax validation: All files compile successfully **Benefits**: - ✅ X-Check now uses real defensive ratings in PD league - ✅ SBA league continues working with manual entry (uses defaults) - ✅ No breaking changes to existing functionality - ✅ Graceful degradation if API unavailable - ✅ In-memory caching reduces API calls by >99% - ✅ League-agnostic design via config system - ✅ Production-ready with live API verification **Phase 3E Status**: Main complete (85% → 90%) **Next**: Phase 3E-Final (WebSocket events, Redis upgrade, full defensive lineup) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |