harden media
This commit is contained in:
178
scripts/MIGRATION_GUIDE.md
Normal file
178
scripts/MIGRATION_GUIDE.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user