4.9 KiB
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:
{
"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
# 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-defaultwith 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
# 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
MediaReferenceformat - 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:
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:
avatarReffield (JSONB column)
Team entities:
logoReffield (JSONB column)
League entities:
logoReffield (JSONB column)
Safety Features
- Dry Run Mode: Default behavior shows changes without applying them
- Validation: Only updates entries with invalid or missing references
- Error Handling: Continues on individual errors, reports all failures
- Idempotent: Safe to run multiple times
Testing the Migration
Test the migration logic without touching real data:
# 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:
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:
- Restore from database backup
- Or manually revert the
avatarRef/logoReffields
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:
- Verify data integrity in the database
- Test that avatars and logos render correctly
- Update any hardcoded URL references in the frontend
- Remove any legacy URL construction code