aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/routes/login/+page.svelte
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>