# Username-First with UUID Fallback ## Overview The entire system now uses **usernames as the primary identifier** with **UUID fallback** for all user/creator lookups across routes and APIs. This means: - Users visit `/creators/john-doe` (preferred) or `/creators/` (also works) - Users visit `/passport/alice-developer` (preferred) or `/passport/` (also works) - Users visit `/ethos/artists/bob-musician` (preferred) or `/ethos/artists/` (also works) --- ## Changes Made ### 1. Core Utility: Identifier Resolver (`code/lib/identifier-resolver.ts`) **New file** that provides helper functions: ```typescript isUUID(str); // Check if string is UUID format resolveIdentifierToCreator(id); // Resolve username/UUID → creator object resolveIdentifierToUserId(id); // Resolve username/UUID → UUID resolveIdentifierToUsername(id); // Resolve UUID → username ``` ### 2. Creators API Endpoint (`code/server/index.ts`) **Updated** `GET /api/creators/:identifier` to: - Accept username OR UUID in the `:identifier` parameter - Try **username first** (preferred) - Fall back to **UUID lookup** if username lookup fails - Return 404 if neither works ```javascript // Examples that now work: GET /api/creators/john-doe // ✅ Username lookup GET /api/creators/550e8400-... // ✅ UUID lookup fallback ``` ### 3. Creators Profile Component (`code/client/pages/creators/CreatorProfile.tsx`) **Updated** to: - Import UUID/identifier resolver helpers - Accept both username and UUID as route parameters - Resolve UUID to username for canonical URL redirect (optional) - Maintain backwards compatibility with old UUID-based URLs ### 4. Ethos Artist Profile (`code/client/pages/ethos/ArtistProfile.tsx`) **Updated** to: - Import identifier resolver helpers - Accept both username and userId as route parameters - Resolve username → userId before API call - Handle both patterns seamlessly ### 5. Passport Profile (`code/client/pages/ProfilePassport.tsx`) **Already supported** username-first with UUID fallback: - Has built-in `isUuid()` function - Tries `getProfileByUsername()` first - Falls back to `getProfileById()` if username lookup fails - No changes needed - already implemented! ### 6. Creator Profile Validation (`code/api/creators.ts`) **Enforced usernames as required**: - Username must be provided when creating creator profile - Username must be unique (409 Conflict if duplicate) - Username is normalized to lowercase - Validation on both client and server --- ## Routes That Support Username-First with UUID Fallback | Route | Type | Status | | ---------------------------- | -------- | ------------------ | | `/creators/:identifier` | Frontend | ✅ Updated | | `/passport/:identifier` | Frontend | ✅ Already working | | `/ethos/artists/:identifier` | Frontend | ✅ Updated | | `/api/creators/:identifier` | Backend | ✅ Updated | --- ## How It Works: Flow Example ### Username Lookup (Preferred) ``` User visits: /creators/john-doe ↓ CreatorProfile component ↓ Component calls: GET /api/creators/john-doe ↓ Server checks: isUUID("john-doe") → false ↓ Query database: SELECT * FROM aethex_creators WHERE username = "john-doe" ↓ Return creator data ✅ ``` ### UUID Lookup (Fallback) ``` User visits: /creators/550e8400-e29b-41d4-a716-446655440000 ↓ CreatorProfile component ↓ Component calls: GET /api/creators/550e8400-e29b-41d4-a716-446655440000 ↓ Server checks: isUUID("550e8400-...") → true ↓ Query database: SELECT * FROM aethex_creators WHERE username = "550e8400-..." ↓ No match, try UUID fallback: SELECT * FROM aethex_creators WHERE id = "550e8400-..." ↓ Return creator data ✅ ``` ### Username Resolution (For Ethos Artists) ``` User visits: /ethos/artists/bob-musician ↓ ArtistProfile component ↓ Component calls: resolveIdentifierToUserId("bob-musician") ↓ Utility function calls: GET /api/creators/bob-musician ↓ Creator found! Extract user_id = "550e8400-..." ↓ Component calls: GET /api/ethos/artists?id=550e8400-... ↓ Return artist data ✅ ``` --- ## Benefits ✅ **SEO-friendly URLs** - Usernames are more human-readable than UUIDs ✅ **Backwards compatible** - Old UUID-based links still work ✅ **Consistent behavior** - All user profile routes work the same way ✅ **User-friendly** - Share `/creators/john-doe` instead of UUID ✅ **Enforced usernames** - Everyone has a username, eliminates UUID-only profiles --- ## Implementation Details ### UUID Detection ```typescript // UUID regex pattern (standard RFC 4122) const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; function isUUID(str: string): boolean { return uuidPattern.test(str); } ``` ### Lookup Priority For any identifier: 1. **If it matches UUID pattern** → Try UUID lookup directly 2. **If it doesn't match UUID pattern** → Try username lookup first, then UUID fallback 3. **If both fail** → Return 404 Not Found ### Username Normalization - Usernames stored as **lowercase** - Lookups are **case-insensitive** - Users can visit `/creators/John-Doe`, `/creators/JOHN-DOE`, `/creators/john-doe` - all work - URLs always redirect to the canonical username (lowercase) --- ## Client-Side API Functions ### Using the Resolver Utilities ```typescript import { isUUID, resolveIdentifierToCreator, resolveIdentifierToUserId, resolveIdentifierToUsername, } from "@/lib/identifier-resolver"; // Check if string is UUID if (isUUID(userInput)) { // It's a UUID, use as user ID } // Resolve identifier to creator profile const creator = await resolveIdentifierToCreator("john-doe"); // or const creator = await resolveIdentifierToCreator("550e8400-..."); // Get just the user ID const userId = await resolveIdentifierToUserId("john-doe"); // Get just the username const username = await resolveIdentifierToUsername("550e8400-..."); ``` --- ## Database Consistency ### Username Uniqueness - Usernames are unique in `aethex_creators` table - Enforced in API validation (returns 409 if duplicate) - Usernames are indexed for fast lookups ### Required Field - `username` column is **NOT NULL** - All profiles created after enforcement have usernames - Migration: Existing profiles without usernames should be given auto-generated usernames --- ## Future Improvements 1. **Profile slugs** - Use human-friendly slugs instead of usernames (e.g., `/profile/john-doe-developer`) 2. **Username changes** - Allow users to change usernames with proper redirects 3. **Vanity URLs** - Custom profile URLs (e.g., `/john`) 4. **Social aliases** - Link multiple usernames to one profile --- ## Testing ### Test Cases ``` ✅ GET /api/creators/john-doe → Returns creator ✅ GET /api/creators/JOHN-DOE → Returns same creator (case-insensitive) ✅ GET /api/creators/550e8400-... → Returns creator (UUID fallback) ✅ GET /api/creators/nonexistent → Returns 404 ✅ GET /api/creators/invalid-uuid → Tries username, then returns 404 ✅ /creators/john-doe → Loads profile ✅ /creators/550e8400-... → Loads profile (UUID fallback) ✅ /passport/john-doe → Loads passport ✅ /passport/550e8400-... → Loads passport (UUID fallback) ✅ /ethos/artists/bob-musician → Loads artist ✅ /ethos/artists/550e8400-... → Loads artist (UUID fallback) ``` --- ## Migration Checklist - [x] Create identifier resolver utility - [x] Update creators API endpoint to support both username and UUID - [x] Update CreatorProfile component to handle both patterns - [x] Update ArtistProfile component to handle both patterns - [x] Enforce usernames as required field - [x] Document the implementation - [ ] **TODO:** Migrate any existing profiles without usernames to auto-generated usernames - [ ] **TODO:** Add URL redirects for canonical username-based URLs - [ ] **TODO:** Update all link generation to prefer usernames --- ## Files Modified ``` code/ ├── lib/ │ └── identifier-resolver.ts (NEW - UUID/username detection) ├── api/ │ └── creators.ts (UPDATED - enforce username required) ├── server/ │ └── index.ts (UPDATED - /api/creators/:identifier) ├── client/ │ ├── pages/ │ │ ├── creators/CreatorProfile.tsx (UPDATED - UUID fallback) │ │ ├── ethos/ArtistProfile.tsx (UPDATED - UUID fallback) │ │ └── ProfilePassport.tsx (NO CHANGE - already working) │ └── App.tsx (NO CHANGE - routes unchanged) └── docs/ └── USERNAME-FIRST-UUID-FALLBACK.md (NEW - this file) ``` --- ## Summary ✅ **Username-First with UUID Fallback is now implemented across the entire system.** All user-facing routes and APIs prefer usernames while maintaining backward compatibility with UUID-based URLs. Users must have a username to create a profile, ensuring consistent, SEO-friendly URLs throughout the AeThex ecosystem.