vereinshaus/app/src/routes/(auth)/login/+page.svelte
rene 39981c0d17 Migrate: PocketBase → SvelteKit + better-sqlite3 + JWT
Vollständige Migration weg von PocketBase. Neuer Stack:
- better-sqlite3 (WAL-Mode, direkte SQLite-Abfragen)
- jose (JWT HS256, 30 Tage Laufzeit)
- bcryptjs (Passwort-Hashing, cost 12)

Neue Dateien:
- src/lib/server/db.ts    → SQLite-Singleton + Schema + Helpers
- src/lib/server/auth.ts  → JWT sign/verify, bcrypt, Bearer-Token
- src/lib/user.ts         → Svelte-Store (ersetzt pb.authStore)
- src/lib/api.ts          → fetch()-Wrapper (ersetzt pb.collection())
- src/app.d.ts            → App.Locals TypeScript-Deklaration
- 30 neue API-Routes unter src/routes/api/

Entfernt:
- Abhängigkeit von pocketbase npm-Paket (bleibt im package.json bis
  alle Referenzen bereinigt sind)
- PocketBase-Container aus docker-compose.yml
- Migrations und Hooks aus Deploy-Pipeline

Docker: Ein einziger Container, SQLite-Volume unter /data/
Makefile: PocketBase-spezifische Targets entfernt
seed.js: Komplett neu für neue REST-API
2026-05-21 21:55:04 +02:00

143 lines
2.3 KiB
Svelte

<script lang="ts">
import { goto } from '$app/navigation';
import { api } from '$lib/api';
import { user } from '$lib/user';
import type { AppUser } from '$lib/user';
let email = $state('');
let password = $state('');
let error = $state('');
let loading = $state(false);
async function login() {
error = '';
loading = true;
try {
const u = await api.post<AppUser & { token: string }>('/auth/login', { email, password });
user.set(u);
goto('/');
} catch {
error = 'E-Mail oder Passwort falsch.';
} finally {
loading = false;
}
}
</script>
<svelte:head>
<title>Anmelden — vereins.haus</title>
</svelte:head>
<h1>Anmelden</h1>
<form onsubmit={(e) => { e.preventDefault(); login(); }}>
<div class="field">
<label for="email">E-Mail</label>
<input
id="email"
type="email"
bind:value={email}
autocomplete="email"
required
/>
</div>
<div class="field">
<label for="password">Passwort</label>
<input
id="password"
type="password"
bind:value={password}
autocomplete="current-password"
required
/>
</div>
{#if error}
<p class="error">{error}</p>
{/if}
<button type="submit" disabled={loading}>
{loading ? 'Anmelden…' : 'Anmelden'}
</button>
</form>
<p class="switch">
Noch kein Konto? <a href="/register">Verein registrieren</a>
</p>
<style>
h1 {
font-size: 1.4rem;
font-weight: 700;
margin-bottom: 1.5rem;
color: #1e293b;
}
.field {
display: flex;
flex-direction: column;
gap: 0.35rem;
margin-bottom: 1rem;
}
label {
font-size: 0.9rem;
font-weight: 500;
color: #475569;
}
input {
padding: 0.65rem 0.85rem;
border: 1.5px solid #e2e8f0;
border-radius: 8px;
font-size: 1rem;
transition: border-color .15s;
background: #fff;
}
input:focus {
outline: none;
border-color: #1e40af;
}
.error {
color: #dc2626;
font-size: 0.9rem;
margin-bottom: 0.75rem;
}
button {
width: 100%;
padding: 0.75rem;
background: #1e40af;
color: #fff;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
margin-top: 0.5rem;
transition: background .15s;
}
button:hover:not(:disabled) {
background: #1d3a9e;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.switch {
margin-top: 1.25rem;
font-size: 0.9rem;
color: #64748b;
text-align: center;
}
.switch a {
color: #1e40af;
font-weight: 500;
}
</style>