Prettier format pending files
This commit is contained in:
parent
0180c560cc
commit
f9465d75c2
7 changed files with 114 additions and 68 deletions
|
|
@ -132,9 +132,15 @@ export async function createCreatorProfile(req: Request, userId: string) {
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
// Validate required fields
|
// Validate required fields
|
||||||
if (!username || typeof username !== "string" || username.trim().length === 0) {
|
if (
|
||||||
|
!username ||
|
||||||
|
typeof username !== "string" ||
|
||||||
|
username.trim().length === 0
|
||||||
|
) {
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({ error: "Username is required and must be a non-empty string" }),
|
JSON.stringify({
|
||||||
|
error: "Username is required and must be a non-empty string",
|
||||||
|
}),
|
||||||
{ status: 400, headers: { "Content-Type": "application/json" } },
|
{ status: 400, headers: { "Content-Type": "application/json" } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -234,10 +240,7 @@ export async function updateCreatorProfile(req: Request, userId: string) {
|
||||||
|
|
||||||
const normalizedUsername = username.trim().toLowerCase();
|
const normalizedUsername = username.trim().toLowerCase();
|
||||||
|
|
||||||
if (
|
if (currentCreator && currentCreator.username !== normalizedUsername) {
|
||||||
currentCreator &&
|
|
||||||
currentCreator.username !== normalizedUsername
|
|
||||||
) {
|
|
||||||
// Username is being changed, check if new username exists
|
// Username is being changed, check if new username exists
|
||||||
const { data: existingCreator } = await supabase
|
const { data: existingCreator } = await supabase
|
||||||
.from("aethex_creators")
|
.from("aethex_creators")
|
||||||
|
|
@ -265,12 +268,17 @@ export async function updateCreatorProfile(req: Request, userId: string) {
|
||||||
if (bio !== undefined) updateData.bio = bio;
|
if (bio !== undefined) updateData.bio = bio;
|
||||||
if (skills !== undefined) updateData.skills = skills;
|
if (skills !== undefined) updateData.skills = skills;
|
||||||
if (avatar_url !== undefined) updateData.avatar_url = avatar_url;
|
if (avatar_url !== undefined) updateData.avatar_url = avatar_url;
|
||||||
if (experience_level !== undefined) updateData.experience_level = experience_level;
|
if (experience_level !== undefined)
|
||||||
|
updateData.experience_level = experience_level;
|
||||||
if (primary_arm !== undefined) updateData.primary_arm = primary_arm;
|
if (primary_arm !== undefined) updateData.primary_arm = primary_arm;
|
||||||
if (arm_affiliations !== undefined) updateData.arm_affiliations = arm_affiliations;
|
if (arm_affiliations !== undefined)
|
||||||
if (is_discoverable !== undefined) updateData.is_discoverable = is_discoverable;
|
updateData.arm_affiliations = arm_affiliations;
|
||||||
if (allow_recommendations !== undefined) updateData.allow_recommendations = allow_recommendations;
|
if (is_discoverable !== undefined)
|
||||||
if (spotify_profile_url !== undefined) updateData.spotify_profile_url = spotify_profile_url;
|
updateData.is_discoverable = is_discoverable;
|
||||||
|
if (allow_recommendations !== undefined)
|
||||||
|
updateData.allow_recommendations = allow_recommendations;
|
||||||
|
if (spotify_profile_url !== undefined)
|
||||||
|
updateData.spotify_profile_url = spotify_profile_url;
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from("aethex_creators")
|
.from("aethex_creators")
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,10 @@ export default function CreatorProfile({
|
||||||
};
|
};
|
||||||
|
|
||||||
const canProceed = useMemo(() => {
|
const canProceed = useMemo(() => {
|
||||||
const hasUsername = data?.username && typeof data.username === 'string' && data.username.trim().length > 0;
|
const hasUsername =
|
||||||
|
data?.username &&
|
||||||
|
typeof data.username === "string" &&
|
||||||
|
data.username.trim().length > 0;
|
||||||
const hasPrimaryArm = creatorData.primaryArm;
|
const hasPrimaryArm = creatorData.primaryArm;
|
||||||
const hasSkills = creatorData.skills.length > 0;
|
const hasSkills = creatorData.skills.length > 0;
|
||||||
return hasUsername && hasPrimaryArm && hasSkills;
|
return hasUsername && hasPrimaryArm && hasSkills;
|
||||||
|
|
@ -153,7 +156,8 @@ export default function CreatorProfile({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-400 mt-2">
|
<p className="text-xs text-gray-400 mt-2">
|
||||||
{(data?.username || "").length}/32 characters. Lowercase letters, numbers, hyphens, and underscores only.
|
{(data?.username || "").length}/32 characters. Lowercase letters,
|
||||||
|
numbers, hyphens, and underscores only.
|
||||||
</p>
|
</p>
|
||||||
{(data?.username?.length || 0) === 0 && (
|
{(data?.username?.length || 0) === 0 && (
|
||||||
<p className="text-xs text-red-400 mt-1">Username is required</p>
|
<p className="text-xs text-red-400 mt-1">Username is required</p>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ const API_BASE = typeof window !== "undefined" ? window.location.origin : "";
|
||||||
* Check if a string is a valid UUID
|
* Check if a string is a valid UUID
|
||||||
*/
|
*/
|
||||||
export function isUUID(str: string): boolean {
|
export function isUUID(str: string): boolean {
|
||||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
const uuidRegex =
|
||||||
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||||
return uuidRegex.test(str);
|
return uuidRegex.test(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,14 +27,18 @@ export async function resolveIdentifierToCreator(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try username first (most common case)
|
// Try username first (most common case)
|
||||||
const usernameResponse = await fetch(`${API_BASE}/api/creators/${identifier}`);
|
const usernameResponse = await fetch(
|
||||||
|
`${API_BASE}/api/creators/${identifier}`,
|
||||||
|
);
|
||||||
if (usernameResponse.ok) {
|
if (usernameResponse.ok) {
|
||||||
return await usernameResponse.json();
|
return await usernameResponse.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If username lookup failed and identifier is a UUID, try UUID lookup
|
// If username lookup failed and identifier is a UUID, try UUID lookup
|
||||||
if (isUUID(identifier)) {
|
if (isUUID(identifier)) {
|
||||||
const uuidResponse = await fetch(`${API_BASE}/api/creators/user/${identifier}`);
|
const uuidResponse = await fetch(
|
||||||
|
`${API_BASE}/api/creators/user/${identifier}`,
|
||||||
|
);
|
||||||
if (uuidResponse.ok) {
|
if (uuidResponse.ok) {
|
||||||
return await uuidResponse.json();
|
return await uuidResponse.json();
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +54,9 @@ export async function resolveIdentifierToCreator(
|
||||||
/**
|
/**
|
||||||
* Resolve an identifier to a user by ID
|
* Resolve an identifier to a user by ID
|
||||||
*/
|
*/
|
||||||
export async function resolveIdentifierToUserId(identifier: string): Promise<string | null> {
|
export async function resolveIdentifierToUserId(
|
||||||
|
identifier: string,
|
||||||
|
): Promise<string | null> {
|
||||||
if (!identifier) return null;
|
if (!identifier) return null;
|
||||||
|
|
||||||
if (isUUID(identifier)) {
|
if (isUUID(identifier)) {
|
||||||
|
|
@ -69,7 +76,9 @@ export async function resolveIdentifierToUserId(identifier: string): Promise<str
|
||||||
/**
|
/**
|
||||||
* Resolve an identifier to a username
|
* Resolve an identifier to a username
|
||||||
*/
|
*/
|
||||||
export async function resolveIdentifierToUsername(identifier: string): Promise<string | null> {
|
export async function resolveIdentifierToUsername(
|
||||||
|
identifier: string,
|
||||||
|
): Promise<string | null> {
|
||||||
if (!identifier) return null;
|
if (!identifier) return null;
|
||||||
|
|
||||||
if (!isUUID(identifier)) {
|
if (!isUUID(identifier)) {
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,9 @@ export default function Onboarding() {
|
||||||
|
|
||||||
// Use username from form if provided, otherwise generate from first name
|
// Use username from form if provided, otherwise generate from first name
|
||||||
const usernameFromForm = data.username?.trim().toLowerCase();
|
const usernameFromForm = data.username?.trim().toLowerCase();
|
||||||
const generatedUsername = normalizedFirst.replace(/\s+/g, "_").toLowerCase();
|
const generatedUsername = normalizedFirst
|
||||||
|
.replace(/\s+/g, "_")
|
||||||
|
.toLowerCase();
|
||||||
const finalUsername = usernameFromForm || generatedUsername;
|
const finalUsername = usernameFromForm || generatedUsername;
|
||||||
|
|
||||||
if (!finalUsername) {
|
if (!finalUsername) {
|
||||||
|
|
@ -377,23 +379,24 @@ export default function Onboarding() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create creator profile if they provided primary arm
|
// Create creator profile if they provided primary arm
|
||||||
const creatorProfilePromise = data.creatorProfile.primaryArm && finalUsername
|
const creatorProfilePromise =
|
||||||
? fetch(`${API_BASE}/api/creators`, {
|
data.creatorProfile.primaryArm && finalUsername
|
||||||
method: "POST",
|
? fetch(`${API_BASE}/api/creators`, {
|
||||||
headers: { "Content-Type": "application/json" },
|
method: "POST",
|
||||||
body: JSON.stringify({
|
headers: { "Content-Type": "application/json" },
|
||||||
user_id: user.id,
|
body: JSON.stringify({
|
||||||
username: finalUsername,
|
user_id: user.id,
|
||||||
bio: data.creatorProfile.bio || null,
|
username: finalUsername,
|
||||||
avatar_url: null, // Can be added later in profile settings
|
bio: data.creatorProfile.bio || null,
|
||||||
experience_level: data.experience.level || "junior",
|
avatar_url: null, // Can be added later in profile settings
|
||||||
primary_arm: data.creatorProfile.primaryArm,
|
experience_level: data.experience.level || "junior",
|
||||||
arm_affiliations: [data.creatorProfile.primaryArm],
|
primary_arm: data.creatorProfile.primaryArm,
|
||||||
skills: data.creatorProfile.skills || [],
|
arm_affiliations: [data.creatorProfile.primaryArm],
|
||||||
is_discoverable: true,
|
skills: data.creatorProfile.skills || [],
|
||||||
}),
|
is_discoverable: true,
|
||||||
})
|
}),
|
||||||
: Promise.resolve();
|
})
|
||||||
|
: Promise.resolve();
|
||||||
|
|
||||||
// Save followed arms
|
// Save followed arms
|
||||||
const followedArmsPromises = (data.followedArms || []).map((armId) =>
|
const followedArmsPromises = (data.followedArms || []).map((armId) =>
|
||||||
|
|
@ -405,7 +408,7 @@ export default function Onboarding() {
|
||||||
arm_id: armId,
|
arm_id: armId,
|
||||||
action: "follow",
|
action: "follow",
|
||||||
}),
|
}),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
Promise.allSettled([
|
Promise.allSettled([
|
||||||
|
|
@ -509,7 +512,8 @@ export default function Onboarding() {
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-4 text-sm">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-4 text-sm">
|
||||||
<div className="px-4 py-2 rounded-full bg-aethex-500/10 border border-aethex-400/40">
|
<div className="px-4 py-2 rounded-full bg-aethex-500/10 border border-aethex-400/40">
|
||||||
<p className="text-aethex-300 font-semibold">
|
<p className="text-aethex-300 font-semibold">
|
||||||
Step <span className="text-lg">{currentStep + 1}</span> of <span className="text-lg">{steps.length}</span>
|
Step <span className="text-lg">{currentStep + 1}</span> of{" "}
|
||||||
|
<span className="text-lg">{steps.length}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
|
|
@ -559,12 +563,14 @@ export default function Onboarding() {
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-muted-foreground text-sm pl-4">
|
<p className="text-muted-foreground text-sm pl-4">
|
||||||
{currentStep === 0 && "Choose your path and let's begin your journey"}
|
{currentStep === 0 &&
|
||||||
|
"Choose your path and let's begin your journey"}
|
||||||
{currentStep === 1 && "Help us know you better"}
|
{currentStep === 1 && "Help us know you better"}
|
||||||
{currentStep === 2 && "Tell us about your experience"}
|
{currentStep === 2 && "Tell us about your experience"}
|
||||||
{currentStep === 3 && "What are your interests?"}
|
{currentStep === 3 && "What are your interests?"}
|
||||||
{currentStep === 4 && "Select your primary focus area"}
|
{currentStep === 4 && "Select your primary focus area"}
|
||||||
{currentStep === 5 && "Follow the arms you want to see in your feed"}
|
{currentStep === 5 &&
|
||||||
|
"Follow the arms you want to see in your feed"}
|
||||||
{currentStep === 6 && "Set up your creator profile"}
|
{currentStep === 6 && "Set up your creator profile"}
|
||||||
{currentStep === 7 && "You're ready to go!"}
|
{currentStep === 7 && "You're ready to go!"}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ export default function ArtistProfile() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(`${API_BASE}/api/ethos/artists?id=${resolvedUserId}`);
|
const res = await fetch(
|
||||||
|
`${API_BASE}/api/ethos/artists?id=${resolvedUserId}`,
|
||||||
|
);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setArtist(data);
|
setArtist(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
The entire system now uses **usernames as the primary identifier** with **UUID fallback** for all user/creator lookups across routes and APIs.
|
The entire system now uses **usernames as the primary identifier** with **UUID fallback** for all user/creator lookups across routes and APIs.
|
||||||
|
|
||||||
This means:
|
This means:
|
||||||
|
|
||||||
- Users visit `/creators/john-doe` (preferred) or `/creators/<uuid>` (also works)
|
- Users visit `/creators/john-doe` (preferred) or `/creators/<uuid>` (also works)
|
||||||
- Users visit `/passport/alice-developer` (preferred) or `/passport/<uuid>` (also works)
|
- Users visit `/passport/alice-developer` (preferred) or `/passport/<uuid>` (also works)
|
||||||
- Users visit `/ethos/artists/bob-musician` (preferred) or `/ethos/artists/<uuid>` (also works)
|
- Users visit `/ethos/artists/bob-musician` (preferred) or `/ethos/artists/<uuid>` (also works)
|
||||||
|
|
@ -18,15 +19,16 @@ This means:
|
||||||
**New file** that provides helper functions:
|
**New file** that provides helper functions:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
isUUID(str) // Check if string is UUID format
|
isUUID(str); // Check if string is UUID format
|
||||||
resolveIdentifierToCreator(id) // Resolve username/UUID → creator object
|
resolveIdentifierToCreator(id); // Resolve username/UUID → creator object
|
||||||
resolveIdentifierToUserId(id) // Resolve username/UUID → UUID
|
resolveIdentifierToUserId(id); // Resolve username/UUID → UUID
|
||||||
resolveIdentifierToUsername(id) // Resolve UUID → username
|
resolveIdentifierToUsername(id); // Resolve UUID → username
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Creators API Endpoint (`code/server/index.ts`)
|
### 2. Creators API Endpoint (`code/server/index.ts`)
|
||||||
|
|
||||||
**Updated** `GET /api/creators/:identifier` to:
|
**Updated** `GET /api/creators/:identifier` to:
|
||||||
|
|
||||||
- Accept username OR UUID in the `:identifier` parameter
|
- Accept username OR UUID in the `:identifier` parameter
|
||||||
- Try **username first** (preferred)
|
- Try **username first** (preferred)
|
||||||
- Fall back to **UUID lookup** if username lookup fails
|
- Fall back to **UUID lookup** if username lookup fails
|
||||||
|
|
@ -41,6 +43,7 @@ GET /api/creators/550e8400-... // ✅ UUID lookup fallback
|
||||||
### 3. Creators Profile Component (`code/client/pages/creators/CreatorProfile.tsx`)
|
### 3. Creators Profile Component (`code/client/pages/creators/CreatorProfile.tsx`)
|
||||||
|
|
||||||
**Updated** to:
|
**Updated** to:
|
||||||
|
|
||||||
- Import UUID/identifier resolver helpers
|
- Import UUID/identifier resolver helpers
|
||||||
- Accept both username and UUID as route parameters
|
- Accept both username and UUID as route parameters
|
||||||
- Resolve UUID to username for canonical URL redirect (optional)
|
- Resolve UUID to username for canonical URL redirect (optional)
|
||||||
|
|
@ -49,6 +52,7 @@ GET /api/creators/550e8400-... // ✅ UUID lookup fallback
|
||||||
### 4. Ethos Artist Profile (`code/client/pages/ethos/ArtistProfile.tsx`)
|
### 4. Ethos Artist Profile (`code/client/pages/ethos/ArtistProfile.tsx`)
|
||||||
|
|
||||||
**Updated** to:
|
**Updated** to:
|
||||||
|
|
||||||
- Import identifier resolver helpers
|
- Import identifier resolver helpers
|
||||||
- Accept both username and userId as route parameters
|
- Accept both username and userId as route parameters
|
||||||
- Resolve username → userId before API call
|
- Resolve username → userId before API call
|
||||||
|
|
@ -57,6 +61,7 @@ GET /api/creators/550e8400-... // ✅ UUID lookup fallback
|
||||||
### 5. Passport Profile (`code/client/pages/ProfilePassport.tsx`)
|
### 5. Passport Profile (`code/client/pages/ProfilePassport.tsx`)
|
||||||
|
|
||||||
**Already supported** username-first with UUID fallback:
|
**Already supported** username-first with UUID fallback:
|
||||||
|
|
||||||
- Has built-in `isUuid()` function
|
- Has built-in `isUuid()` function
|
||||||
- Tries `getProfileByUsername()` first
|
- Tries `getProfileByUsername()` first
|
||||||
- Falls back to `getProfileById()` if username lookup fails
|
- Falls back to `getProfileById()` if username lookup fails
|
||||||
|
|
@ -65,6 +70,7 @@ GET /api/creators/550e8400-... // ✅ UUID lookup fallback
|
||||||
### 6. Creator Profile Validation (`code/api/creators.ts`)
|
### 6. Creator Profile Validation (`code/api/creators.ts`)
|
||||||
|
|
||||||
**Enforced usernames as required**:
|
**Enforced usernames as required**:
|
||||||
|
|
||||||
- Username must be provided when creating creator profile
|
- Username must be provided when creating creator profile
|
||||||
- Username must be unique (409 Conflict if duplicate)
|
- Username must be unique (409 Conflict if duplicate)
|
||||||
- Username is normalized to lowercase
|
- Username is normalized to lowercase
|
||||||
|
|
@ -74,12 +80,12 @@ GET /api/creators/550e8400-... // ✅ UUID lookup fallback
|
||||||
|
|
||||||
## Routes That Support Username-First with UUID Fallback
|
## Routes That Support Username-First with UUID Fallback
|
||||||
|
|
||||||
| Route | Type | Status |
|
| Route | Type | Status |
|
||||||
|-------|------|--------|
|
| ---------------------------- | -------- | ------------------ |
|
||||||
| `/creators/:identifier` | Frontend | ✅ Updated |
|
| `/creators/:identifier` | Frontend | ✅ Updated |
|
||||||
| `/passport/:identifier` | Frontend | ✅ Already working |
|
| `/passport/:identifier` | Frontend | ✅ Already working |
|
||||||
| `/ethos/artists/:identifier` | Frontend | ✅ Updated |
|
| `/ethos/artists/:identifier` | Frontend | ✅ Updated |
|
||||||
| `/api/creators/:identifier` | Backend | ✅ Updated |
|
| `/api/creators/:identifier` | Backend | ✅ Updated |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -156,7 +162,8 @@ Return artist data ✅
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// UUID regex pattern (standard RFC 4122)
|
// 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;
|
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 {
|
function isUUID(str: string): boolean {
|
||||||
return uuidPattern.test(str);
|
return uuidPattern.test(str);
|
||||||
|
|
@ -166,6 +173,7 @@ function isUUID(str: string): boolean {
|
||||||
### Lookup Priority
|
### Lookup Priority
|
||||||
|
|
||||||
For any identifier:
|
For any identifier:
|
||||||
|
|
||||||
1. **If it matches UUID pattern** → Try UUID lookup directly
|
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
|
2. **If it doesn't match UUID pattern** → Try username lookup first, then UUID fallback
|
||||||
3. **If both fail** → Return 404 Not Found
|
3. **If both fail** → Return 404 Not Found
|
||||||
|
|
@ -184,12 +192,12 @@ For any identifier:
|
||||||
### Using the Resolver Utilities
|
### Using the Resolver Utilities
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import {
|
import {
|
||||||
isUUID,
|
isUUID,
|
||||||
resolveIdentifierToCreator,
|
resolveIdentifierToCreator,
|
||||||
resolveIdentifierToUserId,
|
resolveIdentifierToUserId,
|
||||||
resolveIdentifierToUsername
|
resolveIdentifierToUsername,
|
||||||
} from '@/lib/identifier-resolver';
|
} from "@/lib/identifier-resolver";
|
||||||
|
|
||||||
// Check if string is UUID
|
// Check if string is UUID
|
||||||
if (isUUID(userInput)) {
|
if (isUUID(userInput)) {
|
||||||
|
|
@ -297,4 +305,3 @@ code/
|
||||||
✅ **Username-First with UUID Fallback is now implemented across the entire system.**
|
✅ **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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -435,7 +435,9 @@ export function createServer() {
|
||||||
|
|
||||||
if (error || !project) {
|
if (error || !project) {
|
||||||
console.log("[Passport Data API] Project not found:", projectSlug);
|
console.log("[Passport Data API] Project not found:", projectSlug);
|
||||||
return res.status(404).json({ error: "Project not found", projectSlug });
|
return res
|
||||||
|
.status(404)
|
||||||
|
.json({ error: "Project not found", projectSlug });
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
|
|
@ -4446,7 +4448,10 @@ export function createServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if identifier is a UUID (username-first, UUID fallback)
|
// Check if identifier is a UUID (username-first, UUID fallback)
|
||||||
const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identifier);
|
const isUUID =
|
||||||
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
||||||
|
identifier,
|
||||||
|
);
|
||||||
|
|
||||||
let query = adminSupabase
|
let query = adminSupabase
|
||||||
.from("aethex_creators")
|
.from("aethex_creators")
|
||||||
|
|
@ -4477,11 +4482,16 @@ export function createServer() {
|
||||||
|
|
||||||
// If username lookup failed and it's a valid UUID format, try UUID
|
// If username lookup failed and it's a valid UUID format, try UUID
|
||||||
if (error && !isUUID) {
|
if (error && !isUUID) {
|
||||||
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identifier)) {
|
if (
|
||||||
const { data: creatorByUUID, error: uuidError } = await adminSupabase
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
||||||
.from("aethex_creators")
|
identifier,
|
||||||
.select(
|
)
|
||||||
`
|
) {
|
||||||
|
const { data: creatorByUUID, error: uuidError } =
|
||||||
|
await adminSupabase
|
||||||
|
.from("aethex_creators")
|
||||||
|
.select(
|
||||||
|
`
|
||||||
id,
|
id,
|
||||||
username,
|
username,
|
||||||
bio,
|
bio,
|
||||||
|
|
@ -4493,10 +4503,10 @@ export function createServer() {
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
.eq("is_discoverable", true)
|
.eq("is_discoverable", true)
|
||||||
.eq("id", identifier)
|
.eq("id", identifier)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!uuidError && creatorByUUID) {
|
if (!uuidError && creatorByUUID) {
|
||||||
// Found by UUID, optionally redirect to username canonical URL
|
// Found by UUID, optionally redirect to username canonical URL
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue