Files
gridpilot.gg/plans/media-seeding-plan.md
2025-12-30 18:33:15 +01:00

9.3 KiB

Media Seeding Plan for Team Logos and Driver Avatars

Executive Summary

This plan addresses the robust seeding of media assets (driver avatars and team logos) in the GridPilot development environment. The solution leverages existing static files for avatars and provides a reliable, Docker-compatible approach for team logos using Next.js API routes that serve SVG placeholders.

Current State Analysis

What Exists

  1. Static Avatar Files: Three default avatars exist in apps/website/public/images/avatars/:

    • male-default-avatar.jpg
    • female-default-avatar.jpeg
    • neutral-default-avatar.jpeg
  2. Next.js Image Configuration: apps/website/next.config.mjs is configured with:

    • Remote patterns for localhost:3001 (API)
    • Remote patterns for api:3000 (Docker API)
    • SVG support enabled
    • Image optimization disabled in development
  3. Media Controller: apps/api/src/domain/media/MediaController.ts already generates SVG placeholders for:

    • Team logos (/api/media/teams/:teamId/logo)
    • Driver avatars (/api/media/drivers/:driverId/avatar)
    • League logos, covers, track images, etc.
  4. Current Seeding Logic: SeedRacingData.ts calls seedMediaAssets() which sets URLs in the media repository.

Problems Identified

  1. Driver Avatars: Current seeding uses /api/media/avatar/:driverId which generates SVG placeholders, not static files
  2. Team Logos: Current seeding uses /api/media/teams/:teamId/logo which generates SVG placeholders
  3. InMemoryMediaRepository: Stores URLs but doesn't provide actual file serving
  4. No Static File Integration: The existing static avatars aren't being used in seeding

Solution Design

1. Driver Avatars Strategy (Static Files)

Goal: Use existing static avatar files for all seeded drivers.

Implementation:

A. Enhanced Driver Seeding Logic

// In adapters/bootstrap/SeedRacingData.ts - seedMediaAssets() method
private async seedMediaAssets(seed: any): Promise<void> {
  const baseUrl = this.getMediaBaseUrl();
  
  // Seed driver avatars using static files
  for (const driver of seed.drivers) {
    const avatarUrl = this.getDriverAvatarUrl(driver.id);
    
    const mediaRepo = this.seedDeps.mediaRepository as any;
    if (mediaRepo.setDriverAvatar) {
      mediaRepo.setDriverAvatar(driver.id, avatarUrl);
    }
  }
  
  // ... rest of seeding
}

private getDriverAvatarUrl(driverId: string): string {
  // Use deterministic selection based on driver ID
  const numericSuffixMatch = driverId.match(/(\d+)$/);
  let useFemale = false;
  
  if (numericSuffixMatch) {
    const numericSuffix = parseInt(numericSuffixMatch[1], 10);
    useFemale = numericSuffix % 2 === 0;
  } else {
    // Fallback hash
    let hash = 0;
    for (let i = 0; i < driverId.length; i++) {
      hash = (hash * 31 + driverId.charCodeAt(i)) | 0;
    }
    useFemale = Math.abs(hash) % 2 === 0;
  }
  
  // Return static file paths that Next.js can serve
  if (useFemale) {
    return '/images/avatars/female-default-avatar.jpeg';
  } else {
    return '/images/avatars/male-default-avatar.jpg';
  }
}

B. Next.js Static File Serving

The existing Next.js configuration already supports serving static files from public/images/avatars/. These URLs will work directly from the website container.

2. Team Logos Strategy (SVG Generation)

Goal: Provide reliable, Docker-compatible team logos that work without external dependencies.

Implementation:

A. Enhanced Team Seeding Logic

// In adapters/bootstrap/SeedRacingData.ts - seedMediaAssets() method
// Seed team logos
for (const team of seed.teams) {
  const logoUrl = `${baseUrl}/api/media/teams/${team.id}/logo`;
  
  const mediaRepo = this.seedDeps.mediaRepository as any;
  if (mediaRepo.setTeamLogo) {
    mediaRepo.setTeamLogo(team.id, logoUrl);
  }
}

B. API Route Enhancement

The existing MediaController.ts already provides /api/media/teams/:teamId/logo which generates SVG placeholders. This is perfect for Docker development because:

  • No external network dependencies
  • Deterministic generation based on team ID
  • Works in Docker containers
  • Served via the API container (port 3001)

C. Next.js Rewrites Configuration

The existing next.config.mjs already has rewrites that route /api/* to the API container:

async rewrites() {
  const baseUrl = 'http://api:3000';
  return [
    {
      source: '/api/:path*',
      destination: `${baseUrl}/:path*`,
    },
  ];
}

This means:

  • Website requests /api/media/teams/team-1/logo → Next.js rewrites to http://api:3000/api/media/teams/team-1/logo
  • API serves SVG placeholder
  • Next.js Image component can optimize/cache it

3. Architecture Flow

Seeding Phase:
1. SeedRacingData.execute() creates drivers/teams
2. seedMediaAssets() calculates URLs
3. InMemoryMediaRepository stores: driverId → avatarUrl, teamId → logoUrl
4. URLs are stored in database entities

Runtime Phase (Website):
1. Component requests driver avatar: `/images/avatars/male-default-avatar.jpg`
2. Next.js serves static file directly from public directory

Runtime Phase (Team Logo):
1. Component requests team logo: `/api/media/teams/team-1/logo`
2. Next.js rewrite: → `http://api:3000/api/media/teams/team-1/logo`
3. API generates SVG and returns
4. Next.js Image component optimizes/caches

Implementation Steps

Step 1: Update SeedRacingData.ts

Modify the seedMediaAssets() method to use static files for drivers and existing API routes for teams.

Step 2: Update InMemoryMediaRepository

Ensure it has methods for storing/retrieving media URLs:

setDriverAvatar(driverId: string, url: string): void;
setTeamLogo(teamId: string, url: string): void;
getDriverAvatar(driverId: string): Promise<string | null>;
getTeamLogo(teamId: string): Promise<string | null>;

Step 3: Verify Next.js Configuration

Ensure next.config.mjs has:

  • Proper remote patterns for localhost and Docker API
  • SVG support enabled
  • Image optimization disabled in dev

Step 4: Test the Flow

  1. Start Docker development environment
  2. Trigger database seeding
  3. Verify driver avatars point to static files
  4. Verify team logos point to API routes
  5. Test that URLs resolve correctly in website

Benefits of This Approach

Reliability

  • No external dependencies: No network calls to external services
  • Docker-compatible: Works entirely within Docker network
  • Deterministic: Same IDs always produce same URLs

Performance

  • Fast: Static files served directly, SVG generated on-demand
  • Cached: Next.js can cache API responses
  • No cold starts: No external service initialization needed

Maintainability

  • Clean architecture: Follows existing patterns
  • Testable: Easy to verify URLs are correct
  • Extensible: Can add more media types easily

Developer Experience

  • Simple: No API keys or external services to configure
  • Fast: No waiting for external API responses
  • Offline-capable: Works without internet connection

Risk Mitigation

Risk: Static files not accessible in Docker

Mitigation: Files are in apps/website/public/images/avatars/ which is mounted via volumes in docker-compose.dev.yml

Risk: API routes not working in Docker

Mitigation: Next.js rewrites already route /api/* to http://api:3000 which is the Docker API service

Risk: Image optimization fails

Mitigation: Set unoptimized: true in development, which is already configured

Risk: URLs don't match between seeding and runtime

Mitigation: Use consistent URL generation logic in both seeding and display components

Testing Strategy

Unit Tests

  • Verify getDriverAvatarUrl() returns correct static file paths
  • Verify seedMediaAssets() calls repository methods correctly

Integration Tests

  • Verify seeding creates correct URLs in database
  • Verify API routes return SVG for team logos
  • Verify static files are accessible via Next.js

E2E Tests

  • Load dashboard page
  • Verify driver avatars display correctly
  • Verify team logos display correctly
  • Verify no console errors for missing images

Files to Modify

  1. adapters/bootstrap/SeedRacingData.ts

    • Update seedMediaAssets() method
    • Add getDriverAvatarUrl() helper
  2. adapters/racing/persistence/media/InMemoryMediaRepository.ts

    • Ensure methods exist for avatar/logo storage
  3. apps/website/next.config.mjs

    • Verify configuration is correct for Docker
  4. docker-compose.dev.yml

    • Ensure volumes mount public directory

Success Criteria

Every driver has an avatar URL pointing to static files
Every team has a logo URL pointing to API routes
URLs work in Docker development environment
No external network dependencies required
Images display correctly in website UI
Seeding is deterministic and reproducible

Conclusion

This plan provides a robust, Docker-compatible solution for media seeding that leverages existing infrastructure:

  • Driver avatars: Static files for reliability and speed
  • Team logos: SVG generation for consistency and no external dependencies
  • Architecture: Follows clean architecture principles
  • Docker support: Works seamlessly in containerized development

The solution is simple, maintainable, and addresses all the constraints mentioned in the task.