Refactor OAuth to use env vars
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import beatleaderLogo from '$lib/assets/beatleader-logo.png';
|
||||
|
||||
const links = [
|
||||
{ href: '/', label: 'Home' },
|
||||
{ href: '/tools', label: 'Tools' },
|
||||
@@ -7,7 +10,81 @@
|
||||
let open = false;
|
||||
const toggle = () => (open = !open);
|
||||
const close = () => (open = false);
|
||||
const year = new Date().getFullYear();
|
||||
|
||||
type BeatLeaderIdentity = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
type BeatLeaderPlayer = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
avatar?: string | null;
|
||||
};
|
||||
|
||||
type BeatLeaderProfile = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
avatar?: string;
|
||||
};
|
||||
|
||||
let user: BeatLeaderProfile | null = null;
|
||||
let loginHref = '/auth/beatleader/login';
|
||||
let checkingSession = true;
|
||||
|
||||
const getProfileUrl = (id?: string) => (id ? `https://beatleader.com/u/${encodeURIComponent(id)}` : 'https://beatleader.com');
|
||||
|
||||
function extractPlayer(payload: unknown): BeatLeaderProfile | null {
|
||||
if (!payload || typeof payload !== 'object') return null;
|
||||
const { identity, player } = payload as { identity?: BeatLeaderIdentity | null; player?: BeatLeaderPlayer | null };
|
||||
|
||||
const sourcePlayer = player ?? null;
|
||||
const sourceIdentity = identity ?? null;
|
||||
|
||||
const id = sourcePlayer?.id ?? sourceIdentity?.id;
|
||||
const name = sourcePlayer?.name ?? sourceIdentity?.name;
|
||||
const avatar = sourcePlayer?.avatar ?? null;
|
||||
|
||||
if (!id && !name) return null;
|
||||
|
||||
return {
|
||||
id: typeof id === 'string' ? id : undefined,
|
||||
name: typeof name === 'string' ? name : undefined,
|
||||
avatar: typeof avatar === 'string' ? avatar : undefined
|
||||
};
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const redirectTarget = `${window.location.pathname}${window.location.search}${window.location.hash}` || '/';
|
||||
loginHref = `/auth/beatleader/login?redirect_uri=${encodeURIComponent(redirectTarget)}`;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const res = await fetch('/api/beatleader/me');
|
||||
if (res.ok) {
|
||||
const json = (await res.json()) as unknown;
|
||||
const profile = extractPlayer(json);
|
||||
if (profile) {
|
||||
user = profile;
|
||||
}
|
||||
} else if (res.status === 401) {
|
||||
try {
|
||||
const body = (await res.json()) as Record<string, unknown>;
|
||||
const suggested = body?.login;
|
||||
if (typeof suggested === 'string') {
|
||||
loginHref = suggested;
|
||||
}
|
||||
} catch {
|
||||
// ignore JSON parsing errors for 401 responses
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to determine BeatLeader session state', err);
|
||||
} finally {
|
||||
checkingSession = false;
|
||||
}
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
|
||||
<header class="sticky top-0 z-40 backdrop-blur supports-[backdrop-filter]:bg-surface/50 border-b border-white/10">
|
||||
@@ -24,7 +101,30 @@
|
||||
{#each links as link}
|
||||
<a href={link.href} class="text-muted hover:text-white transition">{link.label}</a>
|
||||
{/each}
|
||||
<a href="/tools/beatleader-compare" class="btn-neon">Compare Players</a>
|
||||
{#if checkingSession}
|
||||
<span class="text-sm text-muted">Connecting…</span>
|
||||
{:else if user}
|
||||
<a
|
||||
href={getProfileUrl(user.id)}
|
||||
class="flex items-center gap-3 rounded-md border border-white/10 px-3 py-1.5 text-sm transition hover:bg-white/10"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
title="View your BeatLeader profile"
|
||||
>
|
||||
<img
|
||||
src={user.avatar ?? beatleaderLogo}
|
||||
alt="BeatLeader avatar"
|
||||
class="h-8 w-8 rounded-full object-cover shadow-sm"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="font-medium text-white">{user.name ?? 'BeatLeader user'}</span>
|
||||
</a>
|
||||
{:else}
|
||||
<a href={loginHref} class="btn-neon inline-flex items-center gap-2 px-3 py-1.5">
|
||||
<img src={beatleaderLogo} alt="BeatLeader" class="h-6 w-6" />
|
||||
<span>Login</span>
|
||||
</a>
|
||||
{/if}
|
||||
</nav>
|
||||
|
||||
<button class="md:hidden btn-neon px-3 py-1.5" on:click={toggle} aria-expanded={open} aria-controls="mobile-nav">
|
||||
@@ -39,7 +139,30 @@
|
||||
{#each links as link}
|
||||
<a href={link.href} on:click={close} class="text-muted hover:text-white transition">{link.label}</a>
|
||||
{/each}
|
||||
<a href="/tools/beatleader-compare" on:click={close} class="btn-neon w-max">Compare Players</a>
|
||||
{#if checkingSession}
|
||||
<span class="text-sm text-muted">Connecting…</span>
|
||||
{:else if user}
|
||||
<a
|
||||
href={getProfileUrl(user.id)}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
on:click={close}
|
||||
class="flex items-center gap-3 rounded-md border border-white/10 px-3 py-2 text-sm transition hover:bg-white/10"
|
||||
>
|
||||
<img
|
||||
src={user.avatar ?? beatleaderLogo}
|
||||
alt="BeatLeader avatar"
|
||||
class="h-10 w-10 rounded-full object-cover shadow-sm"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="font-medium text-white">{user.name ?? 'BeatLeader user'}</span>
|
||||
</a>
|
||||
{:else}
|
||||
<a href={loginHref} on:click={close} class="btn-neon inline-flex items-center gap-2 w-max px-3 py-2">
|
||||
<img src={beatleaderLogo} alt="BeatLeader" class="h-6 w-6" />
|
||||
<span>Login</span>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user