# Media Reference Migration Guide This guide explains how to migrate existing seeded data from old URL formats to the new `MediaReference` format. ## Problem Old seeded data stores media references as URL strings: - `/api/avatar/{driverId}` - `/api/media/teams/{teamId}/logo` - `/api/media/leagues/{leagueId}/logo` New format uses `MediaReference` objects: ```json { "type": "system-default", "variant": "avatar", "avatarVariant": "male" } ``` ## Solutions ### Option 1: Migration Script (Preserve Data) **Best for:** Production databases or when you need to preserve existing data ```bash # Test what would change (dry run) npm run migrate:media:test # Execute the migration npm run migrate:media:exec ``` **What it does:** - Converts `/api/avatar/{id}` → `system-default` with deterministic variant - Converts `/api/media/teams/{id}/logo` → `generated` - Converts `/api/media/leagues/{id}/logo` → `generated` - Handles unknown formats → `none` - Skips already-migrated entries **Environment variables:** - `GRIDPILOT_API_PERSISTENCE=postgres|inmemory` (default: postgres) - `DATABASE_URL` (required for postgres) ### Option 2: Wipe and Reseed (Clean Slate) **Best for:** Development/testing when you don't care about existing data ```bash # Stop services and remove all volumes npm run docker:dev:clean # Rebuild and start fresh npm run docker:dev:build ``` **What it does:** - Deletes all existing data - Runs fresh seed with correct `MediaReference` format - No migration needed ## Migration Script Details ### Supported Old Formats | Old Format | New Reference | Example | |------------|---------------|---------| | `/api/avatar/{id}` | `system-default` (deterministic) | `/api/avatar/driver-1` → `male`/`female`/`neutral` | | `/api/media/teams/{id}/logo` | `generated` | `/api/media/teams/team-1/logo` → `generated:team-team-1` | | `/api/media/leagues/{id}/logo` | `generated` | `/api/media/leagues/league-1/logo` → `generated:league-league-1` | | `/images/avatars/male-default-avatar.jpg` | `system-default` (male) | Static files → `system-default` | | `https://external.com/...` | `none` | External URLs → `none` | | Empty/null | `none` | Missing values → `none` | ### Deterministic Avatar Selection Driver avatars use a hash-based selection for consistency: ```typescript const hash = hashCode(driverId); const variantIndex = Math.abs(hash) % 3; // 0 → male, 1 → female, 2 → neutral ``` This ensures the same driver ID always gets the same avatar variant. ### What Gets Updated **Driver entities:** - `avatarRef` field (JSONB column) **Team entities:** - `logoRef` field (JSONB column) **League entities:** - `logoRef` field (JSONB column) ### Safety Features 1. **Dry Run Mode:** Default behavior shows changes without applying them 2. **Validation:** Only updates entries with invalid or missing references 3. **Error Handling:** Continues on individual errors, reports all failures 4. **Idempotent:** Safe to run multiple times ## Testing the Migration Test the migration logic without touching real data: ```bash # Run test script npm run migrate:media:test ``` This will show you: - How each URL format is parsed - What MediaReference it becomes - Deterministic avatar variants for sample IDs ## When to Use Each Option ### Use Migration Script When: - ✅ You have production data to preserve - ✅ You want to see what changes will be made - ✅ You need a controlled, reversible process - ✅ You're migrating a live database ### Use Wipe and Reseed When: - ✅ You're in development/testing - ✅ You don't care about existing data - ✅ You want the fastest path to a clean state - ✅ You're setting up a new environment ## Troubleshooting ### Migration fails with "DATABASE_URL required" Set the environment variable: ```bash export DATABASE_URL=postgresql://user:pass@localhost:5432/dbname ``` ### Some entries weren't migrated Check the error output. Common issues: - Invalid URL format (will be converted to `none`) - Already valid MediaReference (skipped) - Database connection issues ### Want to rollback The migration only updates entries that need it. To rollback: 1. Restore from database backup 2. Or manually revert the `avatarRef`/`logoRef` fields ## Example Migration Output ``` [INFO] Starting media reference migration in DRY RUN mode [INFO] Persistence mode: postgres [INFO] Connecting to PostgreSQL database... [INFO] Database connection established [INFO] Found 150 drivers to migrate [INFO] Found 25 teams to migrate [INFO] Found 5 leagues to migrate [INFO] Migration completed: 150 drivers, 25 teams, 5 leagues updated === Migration Summary === Mode: DRY RUN Processed: 150 drivers, 25 teams, 5 leagues Updated: 150 drivers, 25 teams, 5 leagues ✅ Dry run completed successfully. Run with --execute to apply changes. ``` ## Next Steps After migration: 1. Verify data integrity in the database 2. Test that avatars and logos render correctly 3. Update any hardcoded URL references in the frontend 4. Remove any legacy URL construction code