diff --git a/frontend/src/lib/auth.ts b/frontend/src/lib/auth.ts index 368cdeb..219da7c 100644 --- a/frontend/src/lib/auth.ts +++ b/frontend/src/lib/auth.ts @@ -26,7 +26,10 @@ function metadataString(user: SupabaseUser, ...keys: string[]): string | undefin } export function toAppUser(user: SupabaseUser): User { - const handle = metadataString(user, 'user_name', 'preferred_username') ?? user.email?.split('@')[0] ?? user.id + // The auth trigger stores profile handles lowercase (profiles_handle_format), while GitHub's + // user_name keeps its original casing. Handles are compared exactly everywhere (profile routes, + // owner filters), so the session handle must be derived the same way the trigger does. + const handle = (metadataString(user, 'user_name', 'preferred_username') ?? user.email?.split('@')[0] ?? user.id).toLowerCase() return { id: user.id, diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 8642555..9f9f1df 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -1,4 +1,4 @@ -import { Link, useParams, useSearchParams } from 'react-router-dom' +import { Link, Navigate, useParams, useSearchParams } from 'react-router-dom' import { ExternalLink, GitFork } from 'lucide-react' import { Skeleton } from '@/components/ui/skeleton' import { UserAvatar } from '@/components/UserAvatar' @@ -33,11 +33,17 @@ export default function Profile() { const [sp, setSp] = useSearchParams() const tab = (sp.get('tab') as Tab) || 'rigs' const cat = useCatalog() + // Handles are stored lowercase, but profile URLs are often typed or shared with + // GitHub's casing (e.g. /u/SergiioB). Resolve against the lowercase handle and + // canonicalize the address bar without dropping the tab query. + const canonical = handle.toLowerCase() const data = useAsync( - () => Promise.all([api.user(handle), api.userRigs(handle), api.userResults(handle), api.customRuntimes({ owner: handle })]), - [handle], + () => Promise.all([api.user(canonical), api.userRigs(canonical), api.userResults(canonical), api.customRuntimes({ owner: canonical })]), + [canonical], ) - usePageTitle(handle ? `@${handle}` : 'Profile') + usePageTitle(canonical ? `@${canonical}` : 'Profile') + if (canonical !== handle) + return if (data.error) return (