151 lines
3.7 KiB
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>
|