8.7 KiB
Media Streamlining Plan: Driver Avatars, Team Logos, League Logos
Goal
Create one clean, conflict-free way to represent and deliver:
- Driver avatars (defaults now; user uploads later)
- Team logos (seeded)
- League logos (seeded)
So that:
- Seeding never produces conflicting behavior across environments.
- The UI never has to guess whether a value is a file path, an API route, a generated asset, or an uploaded asset.
- There is exactly one place that decides the final image URL for each entity.
What exists today (inventory, by responsibility)
Driver avatars
Where they surface:
- Driver lists, driver leaderboards, race entry lists, dashboard summaries, and social/friend UI elements.
- API payloads sometimes include an avatar URL; other times the client constructs a URL from the driver id.
- There are multiple fallback strategies: empty string, null, or client-side default image.
Where they come from:
- A “default avatar set” of three files exists in the website public assets.
- There is also a server route that can generate an avatar image for a driver id.
- Some parts of the system treat driver avatar as a user-uploadable media setting.
Observed problems:
- Mixed meaning of the avatar field: sometimes it is an absolute URL, sometimes a relative path, sometimes a server route string.
- Multiple fallbacks implemented in multiple places leads to inconsistent UI and hard-to-debug “missing image” bugs.
- Multiple “demo/fake” avatar generators exist, creating divergent behavior between environments.
Team logos
Where they surface:
- Team cards, team leaderboards, recruiting/featured team sections.
- Sometimes the UI uses
logoUrlfrom the API payload; other times it falls back to a server route based on id.
Where they come from:
- A server route can generate a team logo image for a team id.
- Seed logic also “pre-seeds” team logos by writing route strings into an in-memory store.
Observed problems:
- Team “logoUrl” may be an actual URL, or it may be a placeholder, or it may be a server route string stored as data.
- Storing route strings as if they were media values creates conflicts when routes change.
- In some persistence modes the “seeded logo store” is not truly persisted, so bootstrapping may re-trigger reseeding or create inconsistent results.
League logos
Where they surface:
- League cards, league headers, league pages.
- UI tends to call a client-side helper that builds a league-logo URL from id.
Where they come from:
- A server route can generate a league logo image for a league id.
- Seed logic also “pre-seeds” league logos by writing route strings into an in-memory store.
Observed problems:
- Same class of conflicts as team logos.
- There is no single authoritative rule for when a league has a “real” logo versus a generated one.
Proposed streamlined model (single canonical representation)
Canonical concept: Media Reference (not a URL)
Instead of treating stored values as “final URLs”, define a single canonical media reference for each entity image.
Media reference types:
- System default: a fixed asset shipped with the website (driver defaults: male/female/neutral).
- Generated: deterministically generated from an entity id and a seeded pseudo-random source (team/league logos).
- Uploaded: a user-uploaded object managed by the media subsystem.
- None: intentionally unset.
Key rule: only one layer resolves media references into URLs.
URL resolution responsibilities
- Backend resolves references into final URLs for API payloads.
- Backend also serves the image bytes for generated assets and uploaded assets.
- Frontend treats received URLs as ready-to-render and does not invent additional fallbacks beyond a single last-resort placeholder.
Seeding strategy (cleanest route)
Teams and leagues: seeded via faker, but without storing URLs
Requirement: “seed team and league logos via faker”.
Clean approach:
- During seed, assign each team/league a Generated media reference.
- The generator uses faker with a seed derived from the entity id to produce a deterministic “logo identity” (colors, initials, shapes, etc.).
- The stored value is only the reference (type + seed key), not a route string and not a URL.
- When the UI needs to show the logo, it either receives a resolved URL in the API payload or uses a single, standardized media URL builder.
Benefits:
- Deterministic results: same team id always yields the same logo.
- No conflicts when URLs/routes change.
- No need to persist binary files for seeded logos.
Drivers: seeded from the 3 default avatar images
Requirement: “seed driver logos from these defaults” and later “normally these would be user uploads”.
Clean approach:
- During seed, assign each driver a System default media reference selecting one of:
- male-default-avatar
- female-default-avatar
- neutral-default-avatar
- Selection is deterministic (based on driver id) so reseeding does not change faces randomly.
- Later, if a user uploads an avatar, the reference switches to Uploaded and overrides the default.
Benefits:
- No dependency on generated avatars for baseline.
- No ambiguous meaning of the avatar field.
Contract rules (what the UI can rely on)
Field semantics
- Every API payload that includes a driver/team/league image should provide a single resolved URL field for that image.
- Resolved URL is either:
- a valid URL string the UI can render immediately, or
- null (meaning: show a generic placeholder).
- Never send empty strings.
- Never send “sometimes relative file path, sometimes server route” style mixed values.
Fallback rules
- The backend resolver must guarantee a valid URL whenever it can (system default or generated).
- The frontend uses exactly one last-resort placeholder if it receives null.
- No per-component bespoke fallbacks.
Streamlining work items (what changes where)
1) Centralize media reference resolution
Create one “media resolver” concept used by:
- API payload assembly for all places that include avatars/logos.
- Image-serving routes for generated assets and uploaded assets.
This resolver is the only place that knows:
- how to map media references to a concrete image URL
- what the fallback is when no uploaded media exists
2) Stop storing server route strings as data
Remove the pattern where seed logic writes values like “/api/media/.../logo” into an in-memory media store.
Replace it with:
- stored media references (generated/system-default/uploaded)
- consistent URL resolution at response time
3) Normalize route prefixes and caching behavior
- Choose one public URL shape for these images and apply it universally.
- Add consistent cache headers for generated assets (deterministic) so the browser and CDN can cache safely.
4) Align frontend consumption
- Ensure the UI always prefers the resolved URL from the API payload.
- Where the UI only has an id (e.g. very lightweight list items), use a single shared “URL builder” instead of ad-hoc string concatenation.
- Remove duplicate “if missing then fallback to …” logic sprinkled across components.
5) Align tests and demo fakes
- Eliminate competing fake avatar/logo generators.
- Ensure all test fixtures use the same deterministic rules as seed and runtime generation.
- Ensure snapshot/contract tests treat empty string as invalid and expect null instead.
6) Make bootstrapping/reseeding conflict-proof
- Reseed decision should be based on durable data correctness (presence of required entities) rather than transient “in-memory media store” state.
- Ensure “missing avatar/logo” checks are aligned with the new media reference model.
7) Migration and cleanup
- Define how existing seeded databases are handled:
- either a one-time cleanup that rewrites old stored values into the new reference model, or
- a documented wipe-and-reseed path for local/dev environments.
- Ensure the migration path eliminates stored route strings.
Mermaid: Target flow
flowchart TD
UI[Website UI] --> API[API payloads include resolved image URLs]
API --> RES[Media resolver]
RES --> UP[Uploaded media storage]
RES --> GEN[Deterministic generator]
RES --> DEF[System default assets]
GEN --> IMG[Image response with cache headers]
UP --> IMG
DEF --> UI
IMG --> UI
Acceptance criteria
- Driver avatars always render with one of the three defaults unless an upload exists.
- Team and league logos always render deterministically in dev/test seed, without persisting URLs.
- No API payload returns empty string for avatar/logo.
- No UI component constructs its own bespoke fallback logic.
- No bootstrapping loop caused by “missing media” when media is generated or defaults are available.