diff --git a/DOCKER_AUTH_FIXES_SUMMARY.md b/DOCKER_AUTH_FIXES_SUMMARY.md deleted file mode 100644 index 08a03c636..000000000 --- a/DOCKER_AUTH_FIXES_SUMMARY.md +++ /dev/null @@ -1,148 +0,0 @@ -# Docker Auth/Session Test Fixes Summary - -## Problem -The docker-compose.test.yml setup had 18 failing tests related to authentication session issues. The main problems were: - -1. **Service dependency issues**: Website container started before deps container finished installing -2. **Cookie domain problems**: Mock API cookies weren't working properly in Docker environment -3. **Network connectivity**: Website couldn't reach API due to timing and configuration issues - -## Root Causes - -### 1. Missing Service Dependencies -- Website container didn't wait for deps container to complete -- API container didn't wait for deps container -- This caused "next: not found" and module resolution errors - -### 2. Cookie Domain Issues -- Mock API set cookies without domain specification -- In Docker, cookies need proper domain settings to work across containers -- Browser at localhost:3100 couldn't access cookies from API at localhost:3101 - -### 3. Slow npm Install -- deps container took too long to install packages -- Website container would timeout waiting -- No proper health checks or completion signals - -## Fixes Applied - -### 1. Updated `docker-compose.test.yml` - -**Before:** -```yaml -website: - depends_on: - api: - condition: service_healthy -``` - -**After:** -```yaml -deps: - command: ["sh", "-lc", "echo '[deps] Ready' && sleep infinity"] - # Simple command that completes immediately - -api: - depends_on: - deps: - condition: service_started - # Added deps dependency - -website: - depends_on: - deps: - condition: service_started # Wait for deps - api: - condition: service_healthy # Wait for API - command: - - sh - - -lc - - | - # Check if node_modules exist, install if needed - if [ ! -d "node_modules" ] || [ ! -f "node_modules/.bin/next" ]; then - echo "[website] Installing dependencies..." - npm install --no-package-lock --include-workspace-root --no-audit --fund=false --prefer-offline - else - echo "[website] node_modules already present" - fi - echo "[website] Starting Next.js dev server..." - npm run dev -``` - -### 2. Fixed `testing/mock-api-server.cjs` - -**Before:** -```javascript -const cookies = [ - `gp_session=${encodeURIComponent(gpSessionValue)}; Path=/; HttpOnly`, - `gridpilot_demo_mode=${encodeURIComponent(mode)}; Path=/`, -]; -``` - -**After:** -```javascript -// Set cookies with proper domain for Docker environment -const domain = 'localhost'; -const cookies = [ - `gp_session=${encodeURIComponent(gpSessionValue)}; Path=/; HttpOnly; Domain=${domain}`, - `gridpilot_demo_mode=${encodeURIComponent(mode)}; Path=/; Domain=${domain}`, -]; -``` - -### 3. Verified `playwright.website.config.ts` -- Already correctly configured for Docker -- Uses `http://localhost:3100` when `DOCKER_SMOKE=true` -- Proper timeout and retry settings - -## Key Configuration Changes - -### Environment Variables -- `API_BASE_URL=http://api:3000` (internal Docker network) -- `NEXT_PUBLIC_API_BASE_URL=http://localhost:3101` (external for browser) -- `DOCKER_SMOKE=true` (tells tests to use Docker ports) - -### Cookie Settings -- Added `Domain=localhost` to all Set-Cookie headers -- Ensures cookies work across localhost:3100 and localhost:3101 - -### Service Dependencies -- deps → api → website (proper startup order) -- Health checks ensure services are ready before dependent services start - -## Testing the Fixes - -### Quick Test -```bash -# Start services -docker-compose -f docker-compose.test.yml up -d - -# Wait for startup -sleep 30 - -# Run tests -DOCKER_SMOKE=true npx playwright test --config=playwright.website.config.ts -``` - -### Verification Steps -1. Check deps container starts immediately -2. API container waits for deps and becomes healthy -3. Website container waits for both deps and API -4. Cookies are set with proper domain -5. Tests can access both website and API - -## Expected Results -- All 93 tests should pass -- No "next: not found" errors -- No connection refused errors -- Auth sessions work properly in Docker -- Cookie-based authentication flows correctly - -## Files Modified -1. `docker-compose.test.yml` - Service dependencies and startup logic -2. `testing/mock-api-server.cjs` - Cookie domain settings -3. `test-docker-fix.sh` - Verification script (new) - -## Notes -- The fixes address the core infrastructure issues that were causing auth/session failures -- The mock API now properly simulates real authentication flows -- Docker networking is properly configured for cross-container communication \ No newline at end of file diff --git a/DOCKER_SETUP_ANALYSIS.md b/DOCKER_SETUP_ANALYSIS.md deleted file mode 100644 index 4608b32e8..000000000 --- a/DOCKER_SETUP_ANALYSIS.md +++ /dev/null @@ -1,175 +0,0 @@ -# Docker Setup Analysis & Verification - -## Summary -I have thoroughly analyzed and tested the Docker setup for both development and production environments. Here's what I found: - -## ✅ Development Setup - WORKING PERFECTLY - -### Status: **OPERATIONAL** -- **API Service**: Running on port 3000 (with debug on 9229) -- **Website Service**: Running on port 3001 -- **Database Service**: PostgreSQL 15-alpine on port 5432 -- **Hot Reloading**: Enabled via volume mounts -- **Health Checks**: All services healthy - -### Commands: -```bash -# Start development -npm run docker:dev:build - -# View logs -npm run docker:dev:logs - -# Stop services -npm run docker:dev:down - -# Clean everything -npm run docker:dev:clean -``` - -### Architecture: -- **API**: NestJS with TypeScript, hot-reload enabled -- **Website**: Next.js with hot-reload enabled -- **Database**: PostgreSQL with persistent volume -- **Network**: Custom bridge network (gridpilot-network) - -## ⚠️ Production Setup - NEEDS ATTENTION - -### Status: **CONFIGURATION COMPLETE, BUILD PENDING** - -### Issues Found & Fixed: - -#### 1. **Missing .env.production.example** ✅ FIXED -- **Issue**: No example file for production environment variables -- **Solution**: Created `.env.production.example` with all required variables -- **Action Required**: Copy to `.env.production` and update with real credentials - -#### 2. **SSL Directory Missing** ✅ FIXED -- **Issue**: `nginx/ssl/` directory referenced but didn't exist -- **Solution**: Created empty directory for future SSL certificates -- **Note**: HTTPS server is commented out in nginx config for local testing - -#### 3. **Environment Variables** ✅ FIXED -- **Issue**: Production env file had placeholder values that could cause issues -- **Solution**: Updated `.env.production` with safe defaults for local testing -- **Action Required**: Update with real production credentials before deployment - -#### 4. **Docker Build Resource Constraints** ⚠️ IDENTIFIED -- **Issue**: Production builds are resource-intensive and may get killed -- **Solution**: Build in stages or increase Docker resource limits -- **Recommendation**: Use `docker-compose -f docker-compose.prod.yml build --no-cache` with adequate resources - -### Production Architecture: -- **API**: Multi-stage build, optimized production image -- **Website**: Next.js production build with optimized dependencies -- **Database**: PostgreSQL 15-alpine with performance tuning -- **Redis**: Cache layer with LRU eviction and persistence -- **Nginx**: Reverse proxy with rate limiting, security headers, caching - -### Commands: -```bash -# Build production images (may need increased resources) -npm run docker:prod:build - -# Start production (detached) -npm run docker:prod - -# View logs -npm run docker:prod:logs - -# Stop services -npm run docker:prod:down - -# Clean everything -npm run docker:prod:clean -``` - -## 🔧 Files Created/Updated - -### New Files: -- `.env.production.example` - Production environment template -- `nginx/ssl/` - Directory for SSL certificates -- `DOCKER_SETUP_ANALYSIS.md` - This analysis document - -### Updated Files: -- `.env.production` - Fixed with safe defaults -- `.dockerignore` - Enhanced to include production example - -## 🚀 Deployment Checklist - -Before deploying to production: - -1. **Environment Variables**: - ```bash - cp .env.production.example .env.production - # Edit .env.production with real credentials - ``` - -2. **SSL Certificates** (for HTTPS): - - Place certificates in `nginx/ssl/` - - Uncomment HTTPS server block in `nginx/nginx.conf` - - Update domain names in environment variables - -3. **Database Credentials**: - - Update `POSTGRES_PASSWORD` with strong password - - Update `DATABASE_URL` with production database - -4. **Redis Password**: - - Update `REDIS_PASSWORD` with a strong password - - No `REDIS_URL` is required (the Redis container is configured via `REDIS_PASSWORD` in `docker-compose.prod.yml`) - -5. **Vercel KV** (if using): - - Get credentials from Vercel dashboard - - Update `KV_REST_API_URL` and `KV_REST_API_TOKEN` - -6. **Domain Configuration**: - - Update `NEXT_PUBLIC_SITE_URL` with your domain - - Update `NEXT_PUBLIC_API_BASE_URL` with your public API base (often `https://your-domain.com/api` when nginx proxies `/api`) - -7. **Build & Deploy**: - ```bash - # Build with adequate resources - docker-compose -f docker-compose.prod.yml build - - # Start services - docker-compose -f docker-compose.prod.yml up -d - - # Verify health - docker-compose -f docker-compose.prod.yml ps - ``` - -## 📊 Health Check Endpoints - -### API Health: -- **URL**: `http://localhost:3000/health` (dev) or `http://localhost/api/health` (prod) -- **Response**: `{"status":"ok"}` - -### Website Health: -- **URL**: `http://localhost:3001` (dev) or `http://localhost` (prod) -- **Response**: Next.js application running - -### Nginx Health: -- **URL**: `http://localhost/health` -- **Response**: `healthy` - -## 🎯 Key Improvements Made - -1. **Documentation**: Created comprehensive environment example -2. **Security**: Added SSL directory structure -3. **Reliability**: Fixed environment variable placeholders -4. **Maintainability**: Enhanced .dockerignore rules -5. **Testing**: Verified both dev and prod configurations - -## 📝 Notes - -- **Development**: Fully operational and ready for use -- **Production**: Configuration complete, ready for deployment with proper credentials -- **Performance**: Production setup includes resource limits and health checks -- **Security**: Nginx configured with rate limiting and security headers -- **Scalability**: Ready for container orchestration (Kubernetes, etc.) - -## 🎉 Conclusion - -The Docker setup is **production-ready**! Both development and production configurations are properly set up. The development environment works perfectly, and the production environment is configured correctly - it just needs real credentials and adequate build resources. - -**Next Steps**: Follow the deployment checklist above to deploy to production. \ No newline at end of file diff --git a/README.docker.md b/README.docker.md index c5cf8440d..fc84322e5 100644 --- a/README.docker.md +++ b/README.docker.md @@ -62,16 +62,19 @@ Access: ### Testing (Docker) The goal of Docker-backed tests is to catch *wiring* issues between Website ↔ API (wrong hostnames/ports/env vars, missing CORS for credentialed requests, etc.) in a deterministic environment. -- `npm run test:docker:website` - Start a dedicated test stack (ports `3100/3101`) and run Playwright smoke tests against it. - - Uses [`docker-compose.test.yml`](docker-compose.test.yml:1). - - Runs the Website in Docker and talks to an **API mock** container. - - Validates that pages render and that core API fetches succeed (hostname + CORS + routing). +- `npm run test:docker:website` - Start API/DB in Docker, run website locally via Playwright, and execute e2e tests. + - Uses [`docker-compose.test.yml`](docker-compose.test.yml:1) for API and PostgreSQL. + - Playwright starts the website locally via `webServer` config (not in Docker). + - Tests run against `http://localhost:3000` (website) talking to `http://localhost:3101` (API). + - Validates that pages render, middleware works, and API connections succeed. + +**Important**: The website runs locally (not in Docker) to avoid Next.js SWC/compilation issues in containers. Supporting scripts: -- `npm run docker:test:deps` - Install monorepo deps inside a reusable Docker volume (one-shot). -- `npm run docker:test:up` - Bring up the test stack. -- `npm run docker:test:wait` - Wait for `http://localhost:3100` and `http://localhost:3101/health` to be ready. -- `npm run docker:test:down` - Tear the test stack down (including volumes). +- `npm run docker:test:deps` - Verify monorepo dependencies are installed. +- `npm run docker:test:up` - Start API and PostgreSQL containers. +- `npm run docker:test:wait` - Wait for API health check at `http://localhost:3101/health`. +- `npm run docker:test:down` - Stop containers and clean up. ## Environment Variables @@ -117,16 +120,23 @@ The single source of truth for "what base URL should I use?" is [`getWebsiteApiB #### Test Docker defaults (docker-compose.test.yml) This stack is intended for deterministic smoke tests and uses different host ports to avoid colliding with `docker:dev`: -- Website: `http://localhost:3100` -- API mock: `http://localhost:3101` (maps to container `api:3000`) +- Website: `http://localhost:3000` (started by Playwright webServer, not Docker) +- API: `http://localhost:3101` (maps to container `api:3000`) +- PostgreSQL: `localhost:5433` (maps to container `5432`) - `NEXT_PUBLIC_API_BASE_URL=http://localhost:3101` (browser → host port) -- `API_BASE_URL=http://api:3000` (website container → api container) +- `API_BASE_URL=http://localhost:3101` (Playwright webServer → host port) -Important: the test stack's API is a mock server defined inline in [`docker-compose.test.yml`](docker-compose.test.yml:24). It exists to validate Website ↔ API wiring, not domain correctness. +**Important**: +- The website runs locally via Playwright's `webServer` config to avoid Next.js SWC compilation issues in Docker. +- The API is a real TypeORM/PostgreSQL server (not a mock) for testing actual database interactions. +- Playwright automatically starts the website server before running tests. #### Troubleshooting -- If `docker:dev` is running, use `npm run docker:dev:down` before `npm run test:docker:website` to avoid port conflicts. -- If Docker volumes get stuck, run `npm run docker:test:down` (it uses `--remove-orphans` + `rm -f`). +- **Port conflicts**: If `docker:dev` is running, use `npm run docker:dev:down` before `npm run test:docker:website` to avoid port conflicts (dev uses 3001, test uses 3101). +- **Website not starting**: Playwright's webServer may fail if dependencies are missing. Run `npm install` first. +- **Cookie errors**: The `WebsiteAuthManager` requires both `url` and `path` properties for cookies. Check Playwright version compatibility. +- **Docker volumes stuck**: Run `npm run docker:test:down` (uses `--remove-orphans` + `rm -f`). +- **SWC compilation issues**: If website fails to start in Docker, use the local webServer approach (already configured in `playwright.website.config.ts`). ### API "Real vs In-Memory" Mode diff --git a/cookies.txt b/cookies.txt deleted file mode 100644 index 949a8429e..000000000 --- a/cookies.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Netscape HTTP Cookie File -# https://curl.se/docs/http-cookies.html -# This file was generated by libcurl! Edit at your own risk. - -#HttpOnly_localhost FALSE / FALSE 1767404531 gp_session gp_6b1738c6-8a80-407d-b934-b14fb9834ba1 diff --git a/split.js b/split.js deleted file mode 100644 index 21420adbf..000000000 --- a/split.js +++ /dev/null @@ -1,119 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const filePath = 'apps/api/src/domain/league/dtos/LeagueDto.ts'; -const content = fs.readFileSync(filePath, 'utf8'); -const lines = content.split('\n'); - -let allImports = lines.filter(line => line.startsWith('import ')); - -let classBlocks = []; -let currentClassLines = []; -let inClass = false; - -for (let i = 0; i < lines.length; i++) { - let line = lines[i]; - if (line.startsWith('export class ')) { - if (inClass) { - classBlocks.push(currentClassLines); - currentClassLines = []; - } - inClass = true; - } - if (inClass) { - currentClassLines.push(line); - if (line.trim() === '}') { - classBlocks.push(currentClassLines); - currentClassLines = []; - inClass = false; - } - } -} - -if (currentClassLines.length > 0) { - classBlocks.push(currentClassLines); -} - -// Function to get imports for class -function getImportsForClass(classLines, className) { - let classContent = classLines.join('\n'); - let neededImports = []; - - // ApiProperty - if (classContent.includes('@ApiProperty')) { - neededImports.push("import { ApiProperty } from '@nestjs/swagger';"); - } - - // class-validator - let validators = []; - if (classContent.includes('@IsString')) validators.push('IsString'); - if (classContent.includes('@IsNumber')) validators.push('IsNumber'); - if (classContent.includes('@IsBoolean')) validators.push('IsBoolean'); - if (classContent.includes('@IsDate')) validators.push('IsDate'); - if (classContent.includes('@IsOptional')) validators.push('IsOptional'); - if (classContent.includes('@IsEnum')) validators.push('IsEnum'); - if (classContent.includes('@IsArray')) validators.push('IsArray'); - if (classContent.includes('@ValidateNested')) validators.push('ValidateNested'); - if (validators.length > 0) { - neededImports.push(`import { ${validators.join(', ')} } from 'class-validator';`); - } - - // class-transformer - if (classContent.includes('@Type')) { - neededImports.push("import { Type } from 'class-transformer';"); - } - - // Other DTOs - if (classContent.includes('DriverDto')) { - neededImports.push("import { DriverDto } from '../../driver/dto/DriverDto';"); - } - if (classContent.includes('RaceDto')) { - neededImports.push("import { RaceDto } from '../../race/dto/RaceDto';"); - } - - // Local DTOs - let localDTOs = ['LeagueSettingsDTO', 'LeagueWithCapacityDTO', 'LeagueSummaryDTO', 'AllLeaguesWithCapacityDTO', 'AllLeaguesWithCapacityAndScoringDTO', 'LeagueStatsDTO', 'ProtestDTO', 'SeasonDTO', 'LeagueJoinRequestDTO', 'GetLeagueJoinRequestsQueryDTO', 'ApproveJoinRequestInputDTO', 'ApproveJoinRequestOutputDTO', 'RejectJoinRequestInputDTO', 'RejectJoinRequestOutputDTO', 'GetLeagueAdminPermissionsInputDTO', 'LeagueAdminPermissionsDTO', 'RemoveLeagueMemberInputDTO', 'RemoveLeagueMemberOutputDTO', 'UpdateLeagueMemberRoleInputDTO', 'UpdateLeagueMemberRoleOutputDTO', 'GetLeagueOwnerSummaryQueryDTO', 'LeagueOwnerSummaryDTO', 'LeagueConfigFormModelBasicsDTO', 'LeagueConfigFormModelStructureDTO', 'LeagueConfigFormModelScoringDTO', 'LeagueConfigFormModelDropPolicyDTO', 'LeagueConfigFormModelStewardingDTO', 'LeagueConfigFormModelTimingsDTO', 'LeagueConfigFormModelDTO', 'GetLeagueAdminConfigQueryDTO', 'GetLeagueAdminConfigOutputDTO', 'LeagueAdminConfigDTO', 'GetLeagueProtestsQueryDTO', 'LeagueAdminProtestsDTO', 'GetLeagueSeasonsQueryDTO', 'LeagueSeasonSummaryDTO', 'LeagueAdminDTO', 'LeagueMemberDTO', 'LeagueMembershipsDTO', 'LeagueStandingDTO', 'LeagueStandingsDTO', 'LeagueScheduleDTO', 'LeagueStatsDTO', 'CreateLeagueInputDTO', 'CreateLeagueOutputDTO']; - - for (let dto of localDTOs) { - if (dto !== className && classContent.includes(dto)) { - neededImports.push(`import { ${dto} } from './${dto}';`); - } - } - - return neededImports; -} - -for (let classLines of classBlocks) { - let classNameLine = classLines.find(line => line.startsWith('export class ')); - if (classNameLine) { - let match = classNameLine.match(/export class (\w+)/); - if (match) { - let className = match[1]; - // Rename - if (className.endsWith('ViewModel')) { - className = className.replace('ViewModel', 'DTO'); - } else if (className.endsWith('Dto')) { - className = className.replace('Dto', 'DTO'); - } else if (className.endsWith('Input')) { - className = className + 'DTO'; - } else if (className.endsWith('Output')) { - className = className + 'DTO'; - } else if (className.endsWith('Query')) { - className = className + 'DTO'; - } else { - className = className + 'DTO'; - } - let fileName = className + '.ts'; - // Update class name in lines - classLines[0] = classLines[0].replace(/export class \w+/, 'export class ' + className); - // Update references in the class - for (let i = 1; i < classLines.length; i++) { - classLines[i] = classLines[i].replace(/ViewModel/g, 'DTO').replace(/Dto/g, 'DTO').replace(/Input\b/g, 'InputDTO').replace(/Output\b/g, 'OutputDTO').replace(/Query\b/g, 'QueryDTO'); - classLines[i] = classLines[i].replace(/DriverDTO/g, 'DriverDto').replace(/RaceDTO/g, 'RaceDto'); - } - let imports = getImportsForClass(classLines, className); - let fileContent = imports.join('\n') + '\n\n' + classLines.join('\n'); - fs.writeFileSync(path.join('apps/api/src/domain/league/dtos', fileName), fileContent); - } - } -} \ No newline at end of file diff --git a/test-api-signup.sh b/test-api-signup.sh deleted file mode 100644 index 2c419e35b..000000000 --- a/test-api-signup.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -# Test API signup endpoint - -echo "Testing API signup endpoint..." -echo "" - -# Test 1: Check if API is reachable -echo "1. Checking if API is reachable..." -curl -s -o /dev/null -w "HTTP Status: %{http_code}\n" http://localhost:3001/health || echo "Health endpoint not found" - -echo "" - -# Test 2: Try signup with correct data -echo "2. Attempting signup with correct data..." -curl -X POST http://localhost:3001/auth/signup \ - -H "Content-Type: application/json" \ - -d '{ - "email": "testuser@example.com", - "password": "TestPass123", - "displayName": "Test User" - }' \ - -w "\nHTTP Status: %{http_code}\n" \ - -s - -echo "" - -# Test 3: Try signup with extra fields (should fail with whitelist error) -echo "3. Attempting signup with extra fields (should fail)..." -curl -X POST http://localhost:3001/auth/signup \ - -H "Content-Type: application/json" \ - -d '{ - "email": "testuser2@example.com", - "password": "TestPass123", - "displayName": "Test User 2", - "extraField": "should not exist" - }' \ - -w "\nHTTP Status: %{http_code}\n" \ - -s - -echo "" -echo "Done." \ No newline at end of file diff --git a/test-docker-fix.sh b/test-docker-fix.sh deleted file mode 100755 index e3bdcb2b3..000000000 --- a/test-docker-fix.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Test script to verify docker auth/session fixes - -echo "=== Testing Docker Auth/Session Fixes ===" - -# Clean up any existing containers -echo "1. Cleaning up existing containers..." -docker-compose -f docker-compose.test.yml down -v 2>/dev/null || true - -# Start services -echo "2. Starting services..." -docker-compose -f docker-compose.test.yml up -d - -# Wait for services to be ready -echo "3. Waiting for services to be ready..." -sleep 30 - -# Check service status -echo "4. Checking service status..." -docker-compose -f docker-compose.test.yml ps - -# Check website logs for any errors -echo "5. Checking website logs..." -docker-compose -f docker-compose.test.yml logs --tail=10 website - -# Check API health -echo "6. Testing API health..." -curl -f http://localhost:3101/health && echo " ✓ API is healthy" || echo " ✗ API health check failed" - -# Test website accessibility -echo "7. Testing website accessibility..." -curl -f http://localhost:3100/ && echo " ✓ Website is accessible" || echo " ✗ Website accessibility failed" - -echo "" -echo "=== Setup Complete ===" -echo "To run tests: DOCKER_SMOKE=true npx playwright test --config=playwright.website.config.ts" -echo "To stop: docker-compose -f docker-compose.test.yml down" \ No newline at end of file diff --git a/test_fix_verification.md b/test_fix_verification.md deleted file mode 100644 index ca6b731ae..000000000 --- a/test_fix_verification.md +++ /dev/null @@ -1,89 +0,0 @@ -# Fix Verification for Team Logo Issue - -## Problem Summary -- **Issue**: Teams with stale `logoRef` values (`system-default/logo`) persist across force reseeds -- **Root Cause**: `clearExistingRacingData()` didn't clear `racing_teams` table -- **Impact**: API returns `/media/default/logo.png` instead of `/media/teams/{id}/logo` - -## Fix Applied -Updated `adapters/bootstrap/SeedRacingData.ts` `clearExistingRacingData()` method to: - -1. **Clear team join requests** - Before deleting teams -2. **Clear team memberships** - Before deleting teams -3. **Clear teams** - The critical fix (teams have stale logoRef) -4. **Clear related racing data** - Results, standings, races, etc. - -## Key Changes -```typescript -// Before (incomplete): -private async clearExistingRacingData(): Promise { - // Only cleared drivers and leagues - // Missing: teams, team_memberships, team_join_requests -} - -// After (complete): -private async clearExistingRacingData(): Promise { - // Clear stats - await this.seedDeps.driverStatsRepository.clear(); - await this.seedDeps.teamStatsRepository.clear(); - - // Clear race registrations - const races = await this.seedDeps.raceRepository.findAll(); - for (const race of races) { - await this.seedDeps.raceRegistrationRepository.clearRaceRegistrations(race.id.toString()); - } - - // Clear team join requests - const teams = await this.seedDeps.teamRepository.findAll(); - for (const team of teams) { - const joinRequests = await this.seedDeps.teamMembershipRepository.getJoinRequests(team.id.toString()); - for (const request of joinRequests) { - await this.seedDeps.teamMembershipRepository.removeJoinRequest(request.id); - } - } - - // Clear team memberships - for (const team of teams) { - const memberships = await this.seedDeps.teamMembershipRepository.getTeamMembers(team.id.toString()); - for (const membership of memberships) { - await this.seedDeps.teamMembershipRepository.removeMembership(team.id.toString(), membership.driverId.toString()); - } - } - - // Clear teams (CRITICAL FIX) - for (const team of teams) { - await this.seedDeps.teamRepository.delete(team.id.toString()); - } - - // Clear other racing data... - // Results, standings, races, league memberships, etc. -} -``` - -## Expected Behavior After Fix - -### Before Fix: -1. Start dev with `GRIDPILOT_API_FORCE_RESEED=1` -2. Teams from previous seed remain with `logoRef: {"type":"system-default","variant":"logo"}` -3. `GET /teams/all` returns `"logoUrl": "/media/default/logo.png"` - -### After Fix: -1. Start dev with `GRIDPILOT_API_FORCE_RESEED=1` -2. All racing data cleared including teams -3. New teams seeded with `logoRef: MediaReference.generated('team', teamId)` -4. `GET /teams/all` returns `"logoUrl": "/media/teams/{id}/logo"` - -## Verification Steps - -1. **Start with existing data**: Run API with force reseed enabled -2. **Check database**: Verify `racing_teams` table is cleared -3. **Verify new data**: Teams should have generated logoRef -4. **Test API**: `/teams/all` should return correct logo URLs - -## Files Modified -- `adapters/bootstrap/SeedRacingData.ts` - Enhanced `clearExistingRacingData()` method - -## Related Code -- `RacingTeamFactory.createTeams()` - Sets `logoRef: MediaReference.generated('team', teamId)` -- `AllTeamsPresenter.present()` - Derives `logoUrl` from `logoRef` -- `MediaController.debugResolve()` - Validates media resolution \ No newline at end of file diff --git a/tests/shared/website/WebsiteAuthManager.ts b/tests/shared/website/WebsiteAuthManager.ts index 9cab781eb..b40967527 100644 --- a/tests/shared/website/WebsiteAuthManager.ts +++ b/tests/shared/website/WebsiteAuthManager.ts @@ -33,11 +33,12 @@ export class WebsiteAuthManager { const token = await WebsiteAuthManager.loginViaApi(request, apiBaseUrl, role); // Critical: the website (localhost:3000) must receive `gp_session` so middleware can forward it. + // Playwright cookie format - either url OR domain+path await context.addCookies([ { name: 'gp_session', value: token, - url: baseURL, + domain: 'localhost', path: '/', httpOnly: true, sameSite: 'Lax', diff --git a/type-inventory.md b/type-inventory.md deleted file mode 100644 index 7aaab3c40..000000000 --- a/type-inventory.md +++ /dev/null @@ -1,234 +0,0 @@ -# Type Inventory & Classification - -This document catalogs all types from the monolithic `apps/website/lib/apiClient.ts` file (lines 13-634) and inline DTOs from `apps/website/app/races/[id]/results/page.tsx` (lines 17-56). Types are organized by domain and classified according to Clean Architecture principles. - -## Summary -- **Total Types**: 89 types in apiClient.ts + 5 inline DTOs = 94 types -- **Domains**: Common/Shared, League, Driver, Team, Race, Sponsor, Media, Analytics, Auth, Payments -- **Classification Breakdown**: - - DTO: 45 (pure transport objects, no business logic) - - ViewModel: 44 (UI-ready with potential computed properties) - - Input: 3 (request parameters) - - Output: 2 (response wrappers) - -## Extraction Strategy Overview -- **DTOs**: Move to `apps/website/lib/dtos/{domain}/` with filename `{TypeName}.ts` -- **ViewModels**: Move to `apps/website/lib/view-models/{domain}/` with filename `{TypeName}.ts` -- **Inputs/Outputs**: Treat as DTOs in appropriate domain -- **Dependencies**: Update imports after extraction -- **No Empty Files**: Ensure each extracted file has content -- **Clean Architecture**: Maintain separation between transport (DTO) and presentation (ViewModel) layers - ---- - -## Common/Shared Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| DriverDTO | 13-19 | DTO | `apps/website/lib/dtos/common/DriverDto.ts` | None | LeagueAdminPresenter.ts, TeamAdminPresenter.ts, presenters (multiple) | -| ProtestViewModel | 21-29 | ViewModel | `apps/website/lib/view-models/common/ProtestViewModel.ts` | None | LeagueAdminProtestsDto.ts | -| LeagueMemberViewModel | 31-36 | ViewModel | `apps/website/lib/view-models/league/LeagueMemberViewModel.ts` | DriverDTO | LeagueMembershipsDto.ts, LeagueAdminDto.ts, leagueMembership.ts | -| StandingEntryViewModel | 38-46 | ViewModel | `apps/website/lib/view-models/league/StandingEntryViewModel.ts` | DriverDTO | LeagueStandingsDto.ts, LeagueStandingsPresenter.ts | -| ScheduledRaceViewModel | 48-54 | ViewModel | `apps/website/lib/view-models/race/ScheduledRaceViewModel.ts` | None | LeagueScheduleDto.ts | - ---- - -## League Domain Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| LeagueSummaryViewModel | 57-70 | ViewModel | `apps/website/lib/view-models/league/LeagueSummaryViewModel.ts` | None | AllLeaguesWithCapacityAndScoringPresenter.ts | -| AllLeaguesWithCapacityViewModel | 72-74 | ViewModel | `apps/website/lib/view-models/league/AllLeaguesWithCapacityViewModel.ts` | LeagueSummaryViewModel | ScheduleRaceFormPresenter.ts, AllLeaguesWithCapacityAndScoringPresenter.ts | -| LeagueStatsDto | 76-79 | DTO | `apps/website/lib/dtos/league/LeagueStatsDto.ts` | None | None found | -| LeagueJoinRequestViewModel | 80-86 | ViewModel | `apps/website/lib/view-models/league/LeagueJoinRequestViewModel.ts` | None | LeagueAdminDto.ts, LeagueAdminPresenter.ts | -| LeagueAdminPermissionsViewModel | 88-95 | ViewModel | `apps/website/lib/view-models/league/LeagueAdminPermissionsViewModel.ts` | None | LeagueAdminPermissionsDto.ts (existing), LeagueAdminPresenter.ts | -| LeagueOwnerSummaryViewModel | 97-102 | ViewModel | `apps/website/lib/view-models/league/LeagueOwnerSummaryViewModel.ts` | None | LeagueAdminPresenter.ts | -| LeagueConfigFormModelDto | 104-111 | DTO | `apps/website/lib/dtos/league/LeagueConfigFormModelDto.ts` | None | LeagueAdminDto.ts | -| LeagueAdminProtestsViewModel | 113-115 | ViewModel | `apps/website/lib/view-models/league/LeagueAdminProtestsViewModel.ts` | ProtestViewModel | LeagueAdminProtestsDto.ts, LeagueAdminPresenter.ts | -| LeagueSeasonSummaryViewModel | 117-123 | ViewModel | `apps/website/lib/view-models/league/LeagueSeasonSummaryViewModel.ts` | None | LeagueAdminPresenter.ts | -| LeagueMembershipsViewModel | 125-127 | ViewModel | `apps/website/lib/view-models/league/LeagueMembershipsViewModel.ts` | LeagueMemberViewModel | LeagueMembershipsDto.ts, leagueMembership.ts | -| LeagueStandingsViewModel | 129-131 | ViewModel | `apps/website/lib/view-models/league/LeagueStandingsViewModel.ts` | StandingEntryViewModel | LeagueStandingsPresenter.ts, TeamStandingsPresenter.ts | -| LeagueScheduleViewModel | 133-135 | ViewModel | `apps/website/lib/view-models/league/LeagueScheduleViewModel.ts` | ScheduledRaceViewModel | LeagueScheduleDto.ts | -| LeagueStatsViewModel | 137-145 | ViewModel | `apps/website/lib/view-models/league/LeagueStatsViewModel.ts` | None | None found | -| LeagueAdminViewModel | 147-151 | ViewModel | `apps/website/lib/view-models/league/LeagueAdminViewModel.ts` | LeagueConfigFormModelDto, LeagueMemberViewModel, LeagueJoinRequestViewModel | None found | -| CreateLeagueInput | 153-159 | Input | `apps/website/lib/dtos/league/CreateLeagueInput.ts` | None | leagueWizardService.ts | -| CreateLeagueOutput | 161-164 | Output | `apps/website/lib/dtos/league/CreateLeagueOutput.ts` | None | None found | - ---- - -## Driver Domain Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| DriverLeaderboardItemViewModel | 167-175 | ViewModel | `apps/website/lib/view-models/driver/DriverLeaderboardItemViewModel.ts` | None | DriversLeaderboardPresenter.ts | -| DriversLeaderboardViewModel | 177-179 | ViewModel | `apps/website/lib/view-models/driver/DriversLeaderboardViewModel.ts` | DriverLeaderboardItemViewModel | DriversLeaderboardPresenter.ts | -| DriverStatsDto | 181-183 | DTO | `apps/website/lib/dtos/driver/DriverStatsDto.ts` | None | None found | -| CompleteOnboardingInput | 185-188 | Input | `apps/website/lib/dtos/driver/CompleteOnboardingInput.ts` | None | None found | -| CompleteOnboardingOutput | 190-193 | Output | `apps/website/lib/dtos/driver/CompleteOnboardingOutput.ts` | None | None found | -| DriverRegistrationStatusViewModel | 195-199 | ViewModel | `apps/website/lib/view-models/driver/DriverRegistrationStatusViewModel.ts` | None | None found | - ---- - -## Team Domain Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| TeamSummaryViewModel | 202-208 | ViewModel | `apps/website/lib/view-models/team/TeamSummaryViewModel.ts` | None | AllTeamsPresenter.ts | -| AllTeamsViewModel | 210-212 | ViewModel | `apps/website/lib/view-models/team/AllTeamsViewModel.ts` | TeamSummaryViewModel | AllTeamsPresenter.ts | -| TeamMemberViewModel | 214-219 | ViewModel | `apps/website/lib/view-models/team/TeamMemberViewModel.ts` | DriverDTO | TeamDetailsPresenter.ts, TeamRosterPresenter.ts | -| TeamJoinRequestItemViewModel | 221-227 | ViewModel | `apps/website/lib/view-models/team/TeamJoinRequestItemViewModel.ts` | None | TeamAdminPresenter.ts | -| TeamDetailsViewModel | 229-237 | ViewModel | `apps/website/lib/view-models/team/TeamDetailsViewModel.ts` | TeamMemberViewModel | TeamDetailsPresenter.ts | -| TeamMembersViewModel | 239-241 | ViewModel | `apps/website/lib/view-models/team/TeamMembersViewModel.ts` | TeamMemberViewModel | TeamRosterPresenter.ts | -| TeamJoinRequestsViewModel | 243-245 | ViewModel | `apps/website/lib/view-models/team/TeamJoinRequestsViewModel.ts` | TeamJoinRequestItemViewModel | None found | -| DriverTeamViewModel | 247-252 | ViewModel | `apps/website/lib/view-models/team/DriverTeamViewModel.ts` | None | DriverTeamPresenter.ts | -| CreateTeamInput | 254-258 | Input | `apps/website/lib/dtos/team/CreateTeamInput.ts` | None | None found | -| CreateTeamOutput | 260-263 | Output | `apps/website/lib/dtos/team/CreateTeamOutput.ts` | None | None found | -| UpdateTeamInput | 265-269 | Input | `apps/website/lib/dtos/team/UpdateTeamInput.ts` | None | TeamAdminPresenter.ts | -| UpdateTeamOutput | 271-273 | Output | `apps/website/lib/dtos/team/UpdateTeamOutput.ts` | None | None found | - ---- - -## Race Domain Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| RaceListItemViewModel | 276-284 | ViewModel | `apps/website/lib/view-models/race/RaceListItemViewModel.ts` | None | None found | -| AllRacesPageViewModel | 286-288 | ViewModel | `apps/website/lib/view-models/race/AllRacesPageViewModel.ts` | RaceListItemViewModel | None found | -| RaceStatsDto | 290-292 | DTO | `apps/website/lib/dtos/race/RaceStatsDto.ts` | None | None found | -| RaceDetailEntryViewModel | 295-302 | ViewModel | `apps/website/lib/view-models/race/RaceDetailEntryViewModel.ts` | None | None found | -| RaceDetailUserResultViewModel | 304-313 | ViewModel | `apps/website/lib/view-models/race/RaceDetailUserResultViewModel.ts` | None | None found | -| RaceDetailRaceViewModel | 315-326 | ViewModel | `apps/website/lib/view-models/race/RaceDetailRaceViewModel.ts` | None | None found | -| RaceDetailLeagueViewModel | 328-336 | ViewModel | `apps/website/lib/view-models/race/RaceDetailLeagueViewModel.ts` | None | None found | -| RaceDetailRegistrationViewModel | 338-341 | ViewModel | `apps/website/lib/view-models/race/RaceDetailRegistrationViewModel.ts` | None | None found | -| RaceDetailViewModel | 343-350 | ViewModel | `apps/website/lib/view-models/race/RaceDetailViewModel.ts` | RaceDetailRaceViewModel, RaceDetailLeagueViewModel, RaceDetailEntryViewModel, RaceDetailRegistrationViewModel, RaceDetailUserResultViewModel | None found | -| RacesPageDataRaceViewModel | 352-364 | ViewModel | `apps/website/lib/view-models/race/RacesPageDataRaceViewModel.ts` | None | races/page.tsx, races/all/page.tsx | -| RacesPageDataViewModel | 366-368 | ViewModel | `apps/website/lib/view-models/race/RacesPageDataViewModel.ts` | RacesPageDataRaceViewModel | races/page.tsx, races/all/page.tsx | -| RaceResultViewModel | 370-381 | ViewModel | `apps/website/lib/view-models/race/RaceResultViewModel.ts` | None | None found | -| RaceResultsDetailViewModel | 383-387 | ViewModel | `apps/website/lib/view-models/race/RaceResultsDetailViewModel.ts` | RaceResultViewModel | races/[id]/results/page.tsx | -| RaceWithSOFViewModel | 389-393 | ViewModel | `apps/website/lib/view-models/race/RaceWithSOFViewModel.ts` | None | races/[id]/results/page.tsx | -| RaceProtestViewModel | 395-405 | ViewModel | `apps/website/lib/view-models/race/RaceProtestViewModel.ts` | None | None found | -| RaceProtestsViewModel | 407-410 | ViewModel | `apps/website/lib/view-models/race/RaceProtestsViewModel.ts` | RaceProtestViewModel | races/[id]/stewarding/page.tsx | -| RacePenaltyViewModel | 412-421 | ViewModel | `apps/website/lib/view-models/race/RacePenaltyViewModel.ts` | None | None found | -| RacePenaltiesViewModel | 423-426 | ViewModel | `apps/website/lib/view-models/race/RacePenaltiesViewModel.ts` | RacePenaltyViewModel | races/[id]/stewarding/page.tsx | -| RegisterForRaceParams | 428-431 | Input | `apps/website/lib/dtos/race/RegisterForRaceParams.ts` | None | None found | -| WithdrawFromRaceParams | 433-435 | Input | `apps/website/lib/dtos/race/WithdrawFromRaceParams.ts` | None | None found | -| ImportRaceResultsInput | 437-439 | Input | `apps/website/lib/dtos/race/ImportRaceResultsInput.ts` | None | None found | -| ImportRaceResultsSummaryViewModel | 441-447 | ViewModel | `apps/website/lib/view-models/race/ImportRaceResultsSummaryViewModel.ts` | None | None found | - ---- - -## Sponsor Domain Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| GetEntitySponsorshipPricingResultDto | 450-454 | DTO | `apps/website/lib/dtos/sponsor/GetEntitySponsorshipPricingResultDto.ts` | None | None found | -| SponsorViewModel | 456-462 | ViewModel | `apps/website/lib/view-models/sponsor/SponsorViewModel.ts` | None | None found | -| GetSponsorsOutput | 463-465 | Output | `apps/website/lib/dtos/sponsor/GetSponsorsOutput.ts` | SponsorViewModel | None found | -| CreateSponsorInput | 467-472 | Input | `apps/website/lib/dtos/sponsor/CreateSponsorInput.ts` | None | None found | -| CreateSponsorOutput | 474-477 | Output | `apps/website/lib/dtos/sponsor/CreateSponsorOutput.ts` | None | None found | -| SponsorDashboardDTO | 479-485 | DTO | `apps/website/lib/dtos/sponsor/SponsorDashboardDto.ts` | None | None found | -| SponsorshipDetailViewModel | 487-497 | ViewModel | `apps/website/lib/view-models/sponsor/SponsorshipDetailViewModel.ts` | None | None found | -| SponsorSponsorshipsDTO | 499-502 | DTO | `apps/website/lib/dtos/sponsor/SponsorSponsorshipsDto.ts` | SponsorshipDetailViewModel | None found | - ---- - -## Media Domain Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| RequestAvatarGenerationInput | 505-508 | Input | `apps/website/lib/dtos/media/RequestAvatarGenerationInput.ts` | None | None found | -| RequestAvatarGenerationOutput | 510-514 | Output | `apps/website/lib/dtos/media/RequestAvatarGenerationOutput.ts` | None | None found | - ---- - -## Analytics Domain Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| RecordPageViewInput | 517-521 | Input | `apps/website/lib/dtos/analytics/RecordPageViewInput.ts` | None | None found | -| RecordPageViewOutput | 523-525 | Output | `apps/website/lib/dtos/analytics/RecordPageViewOutput.ts` | None | None found | -| RecordEngagementInput | 527-532 | Input | `apps/website/lib/dtos/analytics/RecordEngagementInput.ts` | None | None found | -| RecordEngagementOutput | 534-536 | Output | `apps/website/lib/dtos/analytics/RecordEngagementOutput.ts` | None | None found | - ---- - -## Auth Domain Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| LoginParams | 539-542 | Input | `apps/website/lib/dtos/auth/LoginParams.ts` | None | None found | -| SignupParams | 544-548 | Input | `apps/website/lib/dtos/auth/SignupParams.ts` | None | None found | -| SessionData | 550-556 | DTO | `apps/website/lib/dtos/auth/SessionData.ts` | None | None found | - ---- - -## Payments Domain Types - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| PaymentViewModel | 559-565 | ViewModel | `apps/website/lib/view-models/payments/PaymentViewModel.ts` | None | None found | -| GetPaymentsOutput | 567-569 | Output | `apps/website/lib/dtos/payments/GetPaymentsOutput.ts` | PaymentViewModel | None found | -| CreatePaymentInput | 571-577 | Input | `apps/website/lib/dtos/payments/CreatePaymentInput.ts` | None | None found | -| CreatePaymentOutput | 579-582 | Output | `apps/website/lib/dtos/payments/CreatePaymentOutput.ts` | None | None found | -| MembershipFeeViewModel | 584-589 | ViewModel | `apps/website/lib/view-models/payments/MembershipFeeViewModel.ts` | None | None found | -| MemberPaymentViewModel | 591-596 | ViewModel | `apps/website/lib/view-models/payments/MemberPaymentViewModel.ts` | None | None found | -| GetMembershipFeesOutput | 598-601 | Output | `apps/website/lib/dtos/payments/GetMembershipFeesOutput.ts` | MembershipFeeViewModel, MemberPaymentViewModel | None found | -| PrizeViewModel | 603-609 | ViewModel | `apps/website/lib/view-models/payments/PrizeViewModel.ts` | None | None found | -| GetPrizesOutput | 611-613 | Output | `apps/website/lib/dtos/payments/GetPrizesOutput.ts` | PrizeViewModel | None found | -| WalletTransactionViewModel | 615-621 | ViewModel | `apps/website/lib/view-models/payments/WalletTransactionViewModel.ts` | None | None found | -| WalletViewModel | 623-628 | ViewModel | `apps/website/lib/view-models/payments/WalletViewModel.ts` | WalletTransactionViewModel | None found | -| GetWalletOutput | 630-632 | Output | `apps/website/lib/dtos/payments/GetWalletOutput.ts` | WalletViewModel | None found | - ---- - -## Inline DTOs from Results Page - -| Type Name | Line Range | Classification | Target Location | Dependencies | Used By | -|-----------|------------|----------------|-----------------|--------------|---------| -| PenaltyTypeDTO | 17-24 | DTO | `apps/website/lib/dtos/race/PenaltyTypeDto.ts` | None | PenaltyTypeDto.ts (existing file) | -| PenaltyData | 26-30 | DTO | `apps/website/lib/dtos/race/PenaltyData.ts` | PenaltyTypeDTO | None found | -| RaceResultRowDTO | 32-41 | DTO | `apps/website/lib/dtos/race/RaceResultRowDto.ts` | None | None found | -| DriverRowDTO | 43-46 | DTO | `apps/website/lib/dtos/race/DriverRowDto.ts` | None | None found | -| ImportResultRowDTO | 48-56 | DTO | `apps/website/lib/dtos/race/ImportResultRowDto.ts` | None | None found | - ---- - -## Extraction Recommendations - -1. **Create Directory Structure**: - - `apps/website/lib/dtos/common/` - - `apps/website/lib/dtos/league/` - - `apps/website/lib/dtos/driver/` - - `apps/website/lib/dtos/team/` - - `apps/website/lib/dtos/race/` - - `apps/website/lib/dtos/sponsor/` - - `apps/website/lib/dtos/media/` - - `apps/website/lib/dtos/analytics/` - - `apps/website/lib/dtos/auth/` - - `apps/website/lib/dtos/payments/` - - `apps/website/lib/view-models/common/` - - `apps/website/lib/view-models/league/` - - `apps/website/lib/view-models/driver/` - - `apps/website/lib/view-models/team/` - - `apps/website/lib/view-models/race/` - - `apps/website/lib/view-models/sponsor/` - - `apps/website/lib/view-models/payments/` - -2. **Move Types Sequentially**: - - Start with types that have no dependencies - - Update imports in apiClient.ts and dependent files - - Test after each major domain extraction - -3. **Update Existing DTOs**: - - Some DTOs already exist (e.g., LeagueAdminPermissionsDto.ts) - - Ensure consistency in naming and structure - -4. **Clean Architecture Compliance**: - - DTOs: Pure data transfer, no methods - - ViewModels: May include computed properties for UI - - Clear separation maintained - -5. **Post-Extraction**: - - Remove types from apiClient.ts - - Update all import statements - - Run tests to ensure no breaking changes \ No newline at end of file