vereinshaus/scripts/seed.js
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

153 lines
9.2 KiB
JavaScript

#!/usr/bin/env node
// Seed-Script für vereins.haus (SvelteKit + SQLite, kein PocketBase)
// Verwendung: APP_URL=https://staging.vereins.haus node scripts/seed.js
const BASE = process.env.APP_URL || 'http://localhost:3000';
async function req(method, path, body, token) {
const headers = { 'Content-Type': 'application/json' };
if (token) headers['Authorization'] = `Bearer ${token}`;
const res = await fetch(`${BASE}/api${path}`, {
method, headers, body: body ? JSON.stringify(body) : undefined,
});
const json = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(`${method} ${path}${res.status}: ${JSON.stringify(json)}`);
return json;
}
async function main() {
console.log(`→ Seed gegen ${BASE}`);
// 1. Verein + Admin registrieren
console.log('→ Registrierung...');
const auth = await req('POST', '/auth/register', {
vereinName: 'TSV Musterstadt 1983 e.V.',
email: 'vorstand@tsv-musterstadt.de',
password: 'Test123456!',
name: 'Max Vorstand',
});
const T = auth.token;
const VID = auth.verein_id;
console.log(` ✓ Verein ID: ${VID}`);
console.log(` ✓ User: vorstand@tsv-musterstadt.de`);
// 2. Verein-Details ergänzen
await req('PATCH', '/vereine', {
adresse: 'Musterstraße 1', plz: '12345', ort: 'Musterstadt',
bundesland: 'Bayern', plan: 'starter', dosb_mitglied: true,
email: 'info@tsv-musterstadt.de', telefon: '01234 56789',
glaeubigerid: 'DE98ZZZ09999999999',
iban: 'DE89370400440532013000', bic: 'COBADEFFXXX',
}, T);
// 3. Gruppen
console.log('→ Gruppen...');
const gruppen = {};
for (const name of ['Vorstand', 'Aktive Mitglieder', 'Jugend U15', 'Senioren']) {
const g = await req('POST', '/gruppen', { name }, T);
gruppen[name] = g.id;
}
console.log(`${Object.keys(gruppen).length} Gruppen`);
// 4. Mitglieder
console.log('→ Mitglieder (18)...');
const mitgliederDaten = [
{ vorname: 'Max', nachname: 'Vorstand', email: 'vorstand@tsv-musterstadt.de', status: 'aktiv', gruppe_ids: [gruppen['Vorstand'], gruppen['Aktive Mitglieder']], eintrittsdatum: '2010-01-01' },
{ vorname: 'Anna', nachname: 'Schmidt', email: 'anna.schmidt@example.de', status: 'aktiv', gruppe_ids: [gruppen['Aktive Mitglieder']], eintrittsdatum: '2015-03-15', iban: 'DE89370400440532013001' },
{ vorname: 'Thomas', nachname: 'Müller', email: 'thomas.mueller@example.de', status: 'aktiv', gruppe_ids: [gruppen['Aktive Mitglieder'], gruppen['Senioren']], eintrittsdatum: '2012-06-01' },
{ vorname: 'Lisa', nachname: 'Weber', email: 'lisa.weber@example.de', status: 'aktiv', gruppe_ids: [gruppen['Jugend U15']], eintrittsdatum: '2022-09-01', geburtsdatum: '2010-04-12' },
{ vorname: 'Klaus', nachname: 'Fischer', email: 'klaus.fischer@example.de', status: 'aktiv', gruppe_ids: [gruppen['Senioren']], eintrittsdatum: '2008-01-15' },
{ vorname: 'Maria', nachname: 'Bauer', email: 'maria.bauer@example.de', status: 'aktiv', gruppe_ids: [gruppen['Aktive Mitglieder']], eintrittsdatum: '2019-02-20' },
{ vorname: 'Peter', nachname: 'Wagner', email: 'peter.wagner@example.de', status: 'aktiv', gruppe_ids: [gruppen['Aktive Mitglieder']], eintrittsdatum: '2017-05-10', iban: 'DE89370400440532013002' },
{ vorname: 'Julia', nachname: 'Becker', email: 'julia.becker@example.de', status: 'aktiv', gruppe_ids: [gruppen['Jugend U15']], eintrittsdatum: '2023-01-01', geburtsdatum: '2011-07-22' },
{ vorname: 'Stefan', nachname: 'Hoffmann', email: 'stefan.hoffmann@example.de', status: 'passiv', gruppe_ids: [], eintrittsdatum: '2005-03-01' },
{ vorname: 'Sandra', nachname: 'Koch', email: 'sandra.koch@example.de', status: 'aktiv', gruppe_ids: [gruppen['Senioren']], eintrittsdatum: '2014-11-15' },
{ vorname: 'Michael', nachname: 'Schäfer', email: 'michael.schaefer@example.de', status: 'aktiv', gruppe_ids: [gruppen['Aktive Mitglieder']], eintrittsdatum: '2020-04-01' },
{ vorname: 'Sabine', nachname: 'Zimmermann', email: 'sabine.zimm@example.de', status: 'aktiv', gruppe_ids: [gruppen['Aktive Mitglieder']], eintrittsdatum: '2018-08-20' },
{ vorname: 'Andreas', nachname: 'Braun', email: 'andreas.braun@example.de', status: 'aktiv', gruppe_ids: [gruppen['Senioren']], eintrittsdatum: '2011-01-01' },
{ vorname: 'Monika', nachname: 'Richter', email: 'monika.richter@example.de', status: 'passiv', gruppe_ids: [], eintrittsdatum: '2009-06-15' },
{ vorname: 'Tobias', nachname: 'Wolf', email: 'tobias.wolf@example.de', status: 'aktiv', gruppe_ids: [gruppen['Jugend U15']], eintrittsdatum: '2022-01-15', geburtsdatum: '2012-01-30' },
{ vorname: 'Eva', nachname: 'Krause', email: 'eva.krause@example.de', status: 'aktiv', gruppe_ids: [gruppen['Aktive Mitglieder']], eintrittsdatum: '2021-03-01' },
{ vorname: 'Markus', nachname: 'Schwarz', email: 'markus.schwarz@example.de', status: 'aktiv', gruppe_ids: [gruppen['Aktive Mitglieder'], gruppen['Vorstand']], eintrittsdatum: '2013-09-01' },
{ vorname: 'Nina', nachname: 'Lange', email: 'nina.lange@example.de', status: 'ausgetreten', gruppe_ids: [], eintrittsdatum: '2016-02-01', austrittsdatum: '2023-12-31' },
];
for (const m of mitgliederDaten) await req('POST', '/mitglieder', m, T);
console.log(` ✓ 18 Mitglieder`);
// 5. Veranstaltungsorte
console.log('→ Orte...');
const orte = {};
for (const o of [
{ name: 'Turnhalle Grundschule Muster', adresse: 'Schulweg 5, 12345 Musterstadt', typ: 'halle', aktiv: true },
{ name: 'Vereinsheim TSV', adresse: 'Musterstraße 3, 12345 Musterstadt', typ: 'gebaeude', aktiv: true },
{ name: 'Sportplatz West', adresse: 'Weststraße 10, 12345 Musterstadt', typ: 'platz', aktiv: true },
]) {
const r = await req('POST', '/orte', o, T);
orte[o.name] = r.id;
}
console.log(` ✓ 3 Orte`);
// 6. Beitragsarten
console.log('→ Beitragsarten...');
for (const b of [
{ name: 'Jahresbeitrag Erwachsene', betrag: 120, rhythmus: 'jaehrlich', beschreibung: 'Normalbeitrag für erwachsene Mitglieder' },
{ name: 'Jahresbeitrag Jugend', betrag: 60, rhythmus: 'jaehrlich', beschreibung: 'Ermäßigter Beitrag bis 18 Jahre' },
{ name: 'Aufnahmegebühr', betrag: 25, rhythmus: 'einmalig', beschreibung: 'Einmalige Gebühr bei Eintritt' },
{ name: 'Monatsbeitrag Fitness', betrag: 15, rhythmus: 'monatlich', beschreibung: 'Zusatzbeitrag Fitnessraum' },
]) {
await req('POST', '/beitraege', b, T);
}
console.log(` ✓ 4 Beitragsarten`);
// 7. Termine
console.log('→ Termine...');
const now = new Date();
const dt = (offsetDays, h = 18, m = 0) => {
const d = new Date(now);
d.setDate(d.getDate() + offsetDays);
d.setHours(h, m, 0, 0);
return d.toISOString().slice(0, 19);
};
const termine = [
{ titel: 'Vorstandssitzung', beginn: dt(3, 19), ort_id: orte['Vereinsheim TSV'], gruppe_ids: [gruppen['Vorstand']], verfuegbarkeit: 'bestaetigt' },
{ titel: 'Vereinsmeisterschaft', beginn: dt(14, 9), ort_id: orte['Sportplatz West'], gruppe_ids: [], beschreibung: 'Jährliche Meisterschaft', verfuegbarkeit: 'bestaetigt' },
{ titel: 'Jugendtraining', beginn: dt(2, 16), ort_id: orte['Turnhalle Grundschule Muster'], gruppe_ids: [gruppen['Jugend U15']], verfuegbarkeit: 'bestaetigt' },
{ titel: 'Mitgliederversammlung', beginn: dt(21, 19,30),ort_id: orte['Vereinsheim TSV'], gruppe_ids: [], beschreibung: 'Ordentliche Jahreshauptversammlung', verfuegbarkeit: 'offen' },
{ titel: 'Seniorensport', beginn: dt(7, 10), ort_id: orte['Turnhalle Grundschule Muster'], gruppe_ids: [gruppen['Senioren']], verfuegbarkeit: 'bestaetigt' },
{ titel: 'Sommerfest', beginn: dt(45, 14), ort_id: orte['Sportplatz West'], gruppe_ids: [], beschreibung: 'Großes Vereinssommerfest', verfuegbarkeit: 'offen' },
];
// 8 Wochen Dienstags-Training
let di = new Date(now);
di.setDate(di.getDate() + ((2 - di.getDay() + 7) % 7 || 7));
di.setHours(18, 0, 0, 0);
for (let i = 0; i < 8; i++) {
const b = new Date(di); b.setDate(b.getDate() + i * 7);
termine.push({
titel: 'Training Aktive', beginn: b.toISOString().slice(0, 19),
ort_id: orte['Turnhalle Grundschule Muster'],
gruppe_ids: [gruppen['Aktive Mitglieder']], verfuegbarkeit: 'offen',
rrule: 'FREQ=WEEKLY;BYDAY=TU',
});
}
for (const t of termine) await req('POST', '/termine', t, T);
console.log(`${termine.length} Termine`);
// 8. Nachricht
console.log('→ Nachricht...');
await req('POST', '/nachrichten', {
betreff: 'Willkommen in vereins.haus!',
text: '<p>Hallo und herzlich willkommen! Dies ist eine Beispiel-Nachricht an alle Mitglieder.</p>',
gruppe_ids: [],
}, T);
console.log(` ✓ Nachricht erstellt`);
console.log('');
console.log('✓ Seed abgeschlossen!');
console.log('');
console.log(` Verein: TSV Musterstadt 1983 e.V.`);
console.log(` Login: vorstand@tsv-musterstadt.de / Test123456!`);
console.log(` App: ${BASE}`);
}
main().catch(e => { console.error('✗ Seed-Fehler:', e.message); process.exit(1); });