sandbox/src/routes/login/+page.svelte

151 lines
3.7 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/stores';
$: errorParam = $page.url.searchParams.get('error');
$: errorMessage = (() => {
if (!errorParam) return null;
if (errorParam === 'not_on_allowlist') return 'Your Discord account is not on the allowlist for this site.';
if (errorParam === 'local_login_invalid') return 'Wrong password.';
if (errorParam === 'local_login_disabled') return 'Local login is not enabled.';
return `Sign-in error: ${errorParam}`;
})();
let localLoginEnabled = false;
onMount(async () => {
try {
const res = await fetch('/api/auth/local-enabled', { credentials: 'same-origin' });
if (res.ok) {
const data = await res.json();
localLoginEnabled = data.enabled === true;
}
} catch {}
});
</script>
<svelte:head>
<title>Sign in — sandbox</title>
</svelte:head>
<main class="login">
<article class="card">
<p class="kicker">sandbox / sign in</p>
<h1>Access required</h1>
{#if errorMessage}
<p class="error">! {errorMessage}</p>
{/if}
<a class="btn discord" href="/auth/discord">
<span>Continue with Discord</span>
</a>
{#if localLoginEnabled}
<div class="or">— or —</div>
<form method="POST" class="local-form" action="/auth/local">
<label for="local-password">Local password</label>
<input id="local-password" name="password" type="password" required autocomplete="current-password" />
<button type="submit" class="local-submit">Sign in</button>
</form>
{/if}
</article>
</main>
<style>
/* Monospace, blueprint feel. NOT Editorial. */
:global(body) {
background: #0a0a0a;
color: #c8c8c8;
font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
}
.login {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 20px;
}
.card {
width: 100%;
max-width: 400px;
background: #0e0e0e;
border: 1px solid #1f1f1f;
padding: 28px 24px;
}
.kicker {
color: #5a5a5a;
font-size: 10px;
letter-spacing: 1.5px;
text-transform: uppercase;
margin: 0 0 6px;
}
h1 {
font-size: 16px;
font-weight: 600;
color: #d8d8d8;
margin: 0 0 20px;
letter-spacing: 0.5px;
}
.error {
color: #d96a7a;
font-size: 12px;
padding: 8px 10px;
background: rgba(217, 106, 122, 0.08);
border: 1px solid rgba(217, 106, 122, 0.25);
margin: 0 0 16px;
}
.btn {
display: block;
width: 100%;
padding: 9px 12px;
text-align: center;
text-decoration: none;
font-family: inherit;
font-size: 13px;
font-weight: 600;
color: #d8d8d8;
background: #1a1a1a;
border: 1px solid #2a2a2a;
transition: background 0.15s;
}
.btn.discord:hover {
background: #232323;
border-color: #3a3a3a;
}
.or {
text-align: center;
color: #4a4a4a;
font-size: 11px;
margin: 16px 0;
}
.local-form { display: flex; flex-direction: column; gap: 8px; }
.local-form label {
font-size: 11px;
color: #6a6a6a;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0;
}
.local-form input {
background: #0a0a0a;
border: 1px solid #2a2a2a;
color: #d8d8d8;
padding: 8px 10px;
font-family: inherit;
font-size: 13px;
}
.local-form input:focus { outline: none; border-color: #4a4a4a; }
.local-submit {
background: #1a1a1a;
border: 1px solid #2a2a2a;
color: #d8d8d8;
padding: 8px 12px;
font-family: inherit;
font-size: 12px;
font-weight: 600;
cursor: pointer;
}
.local-submit:hover { background: #232323; }
</style>