blob: 2b12a752ec140a5fb1bde26f5eeebafad4714700 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
<script lang="ts">
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
let errorMsg = $state('');
let loading = $state(false);
async function handleSubmit(e: Event) {
e.preventDefault();
loading = true;
errorMsg = '';
const formData = new FormData(e.target as HTMLFormElement);
try {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: formData.get('username'),
password: formData.get('password')
})
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
errorMsg = err.error || 'Credenciais inválidas';
return;
}
const { token } = await res.json();
document.cookie = `auth_token=${token}; path=/; max-age=${7 * 24 * 60 * 60}; SameSite=Lax`;
goto('/admin');
} catch {
errorMsg = 'Erro de conexão com o servidor';
} finally {
loading = false;
}
}
onMount(async () => {
const hasToken = document.cookie.split(';').some(c => c.trim().startsWith('auth_token='));
if (!hasToken) return;
try {
const res = await fetch('/api/auth/verify', {
headers: { Authorization: `Bearer ${getCookie('auth_token')}` }
});
if (res.ok) goto('/admin');
} catch { /* ignore */ }
});
function getCookie(name: string) {
return document.cookie.split('; ').find(r => r.startsWith(name + '='))?.split('=')[1] ?? '';
}
</script>
<svelte:head>
<title>Login — YAUM</title>
</svelte:head>
<div class="flex min-h-screen items-start justify-center pt-36">
<div class="w-full max-w-sm">
<div class="mb-8 text-center">
<h1 class="text-xl font-semibold tracking-tight text-white">YAUM</h1>
<p class="mt-1 text-sm text-[var(--text-muted)]">Yet Another Uptime Monitor</p>
</div>
<div class="rounded-2xl border border-[var(--border-color)] bg-[var(--bg-card)] p-6">
<form onsubmit={handleSubmit} class="space-y-4">
<div>
<label for="username" class="mb-1.5 block text-xs font-medium text-[var(--text-secondary)]">Usuário</label>
<input
id="username"
name="username"
type="text"
required
autocomplete="username"
placeholder="admin"
class="w-full rounded-xl border border-[var(--border-color)] bg-[var(--bg-secondary)] px-3.5 py-2.5 text-sm text-white outline-none transition-all placeholder:text-[var(--text-muted)] focus:border-[var(--green)]/50 focus:ring-1 focus:ring-[var(--green)]/20"
/>
</div>
<div>
<label for="password" class="mb-1.5 block text-xs font-medium text-[var(--text-secondary)]">Senha</label>
<input
id="password"
name="password"
type="password"
required
autocomplete="current-password"
placeholder="••••••••"
class="w-full rounded-xl border border-[var(--border-color)] bg-[var(--bg-secondary)] px-3.5 py-2.5 text-sm text-white outline-none transition-all placeholder:text-[var(--text-muted)] focus:border-[var(--green)]/50 focus:ring-1 focus:ring-[var(--green)]/20"
/>
</div>
{#if errorMsg}
<p class="rounded-lg bg-[var(--red)]/5 px-3 py-2 text-xs text-[var(--red)]">{errorMsg}</p>
{/if}
<button
type="submit"
disabled={loading}
class="w-full rounded-xl border border-[var(--green)]/50 bg-[var(--green)]/10 px-4 py-2.5 text-sm font-medium text-[var(--green)] transition-all hover:bg-[var(--green)]/20 disabled:opacity-50"
>
{loading ? 'Entrando…' : 'Entrar'}
</button>
</form>
</div>
<p class="mt-6 text-center text-xs text-[var(--text-muted)]">
<a href="/" class="underline transition-colors hover:text-white">← Voltar ao Dashboard</a>
</p>
</div>
</div>
|