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
This commit is contained in:
parent
61c430f2e6
commit
39981c0d17
58 changed files with 2313 additions and 651 deletions
377
scripts/seed.js
377
scripts/seed.js
|
|
@ -1,264 +1,153 @@
|
|||
#!/usr/bin/env node
|
||||
// Testdaten für vereins.haus – Staging
|
||||
// Aufruf: PB_URL=http://localhost:8090 PB_EMAIL=admin@test.de PB_PASSWORD=Test123456! node scripts/seed.js
|
||||
// Seed-Script für vereins.haus (SvelteKit + SQLite, kein PocketBase)
|
||||
// Verwendung: APP_URL=https://staging.vereins.haus node scripts/seed.js
|
||||
|
||||
const PB_URL = process.env.PB_URL || 'http://localhost:8090';
|
||||
const PB_EMAIL = process.env.PB_EMAIL || '';
|
||||
const PB_PWD = process.env.PB_PASSWORD || '';
|
||||
const BASE = process.env.APP_URL || 'http://localhost:3000';
|
||||
|
||||
if (!PB_EMAIL || !PB_PWD) {
|
||||
console.error('Fehler: PB_EMAIL und PB_PASSWORD setzen.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let token = '';
|
||||
|
||||
async function pb(method, path, body) {
|
||||
const res = await fetch(`${PB_URL}/api/${path}`, {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: token } : {}) },
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
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)}`);
|
||||
if (!res.ok) throw new Error(`${method} ${path} → ${res.status}: ${JSON.stringify(json)}`);
|
||||
return json;
|
||||
}
|
||||
|
||||
async function find(collection, filter) {
|
||||
const r = await pb('GET', `collections/${collection}/records?filter=${encodeURIComponent(filter)}&perPage=1`);
|
||||
return r.items?.[0] ?? null;
|
||||
}
|
||||
async function main() {
|
||||
console.log(`→ Seed gegen ${BASE}`);
|
||||
|
||||
async function create(collection, data) {
|
||||
return pb('POST', `collections/${collection}/records`, data);
|
||||
}
|
||||
|
||||
async function update(collection, id, data) {
|
||||
return pb('PATCH', `collections/${collection}/records/${id}`, data);
|
||||
}
|
||||
|
||||
// ── 1. Admin-Login ──────────────────────────────────────────────────────────
|
||||
console.log('→ Admin-Login…');
|
||||
const auth = await pb('POST', 'collections/_superusers/auth-with-password', {
|
||||
identity: PB_EMAIL, password: PB_PWD,
|
||||
});
|
||||
token = auth.token;
|
||||
console.log(' ✓ Eingeloggt als', PB_EMAIL);
|
||||
|
||||
// ── 2. Verein ────────────────────────────────────────────────────────────────
|
||||
console.log('→ Verein anlegen…');
|
||||
let verein = await find('vereine', 'name = "TSV Musterstadt 1983 e.V."');
|
||||
if (!verein) {
|
||||
verein = await create('vereine', {
|
||||
name: 'TSV Musterstadt 1983 e.V.',
|
||||
adresse: 'Vereinsstraße 12',
|
||||
plz: '80333',
|
||||
ort: 'Musterstadt',
|
||||
bundesland: 'BY',
|
||||
plan: 'starter',
|
||||
dosb_mitglied: true,
|
||||
email: 'info@tsv-musterstadt.de',
|
||||
telefon: '089 123456',
|
||||
website: 'https://tsv-musterstadt.de',
|
||||
glaeubigerid: 'DE98ZZZ09999999999',
|
||||
iban: 'DE89370400440532013000',
|
||||
bic: 'COBADEFFXXX',
|
||||
});
|
||||
}
|
||||
console.log(' ✓ Verein:', verein.name, `(${verein.id})`);
|
||||
|
||||
// ── 3. Admin-User ────────────────────────────────────────────────────────────
|
||||
console.log('→ Admin-User…');
|
||||
let adminUser = await find('users', 'email = "vorstand@tsv-musterstadt.de"');
|
||||
if (!adminUser) {
|
||||
adminUser = await create('users', {
|
||||
// 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!', passwordConfirm: 'Test123456!',
|
||||
name: 'Max Mustermann', verein_id: verein.id, rolle: null, // null = admin
|
||||
emailVisibility: true,
|
||||
password: 'Test123456!',
|
||||
name: 'Max Vorstand',
|
||||
});
|
||||
}
|
||||
console.log(' ✓ Admin:', adminUser.email);
|
||||
const T = auth.token;
|
||||
const VID = auth.verein_id;
|
||||
console.log(` ✓ Verein ID: ${VID}`);
|
||||
console.log(` ✓ User: vorstand@tsv-musterstadt.de`);
|
||||
|
||||
// ── 4. Gruppen ───────────────────────────────────────────────────────────────
|
||||
console.log('→ Gruppen…');
|
||||
const gruppenDef = ['Vorstand', 'Aktive Mitglieder', 'Jugend U15', 'Senioren'];
|
||||
const gruppen = {};
|
||||
for (const name of gruppenDef) {
|
||||
let g = await find('gruppen', `name = "${name}" && verein_id = "${verein.id}"`);
|
||||
if (!g) g = await create('gruppen', { verein_id: verein.id, name });
|
||||
gruppen[name] = g.id;
|
||||
}
|
||||
console.log(' ✓', Object.keys(gruppen).join(', '));
|
||||
// 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);
|
||||
|
||||
// ── 5. Mitglieder ────────────────────────────────────────────────────────────
|
||||
console.log('→ Mitglieder (18)…');
|
||||
const mitgliederDef = [
|
||||
// Vorstand
|
||||
{ vorname: 'Max', nachname: 'Mustermann', email: 'max.mustermann@example.de', telefon: '0170 1111111', geburtsdatum: '1975-03-15', eintrittsdatum: '2000-01-01', strasse: 'Hauptstraße 1', plz: '80333', ort: 'Musterstadt', iban: 'DE89370400440532013000', bic: 'COBADEFFXXX', status: 'aktiv', gruppe: 'Vorstand', mandatsreferenz: 'MANDAT-001', mandatsdatum: '2020-01-10' },
|
||||
{ vorname: 'Sabine', nachname: 'Richter', email: 'sabine.richter@example.de', telefon: '0171 2222222', geburtsdatum: '1980-07-22', eintrittsdatum: '2005-03-01', strasse: 'Birkenweg 5', plz: '80334', ort: 'Musterstadt', iban: 'DE27100777770209299700', bic: 'SSKMDEMMXXX', status: 'aktiv', gruppe: 'Vorstand', mandatsreferenz: 'MANDAT-002', mandatsdatum: '2020-01-10' },
|
||||
// Aktive mit IBAN
|
||||
{ vorname: 'Anna', nachname: 'Schmidt', email: 'anna.schmidt@example.de', telefon: '0172 3333333', geburtsdatum: '1990-11-05', eintrittsdatum: '2015-09-01', strasse: 'Rosenstraße 8', plz: '80333', ort: 'Musterstadt', iban: 'DE61500105179767440929', bic: 'BELADEBEXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-003', mandatsdatum: '2021-04-15' },
|
||||
{ vorname: 'Peter', nachname: 'Wagner', email: 'peter.wagner@example.de', telefon: '0173 4444444', geburtsdatum: '1985-02-28', eintrittsdatum: '2010-01-15', strasse: 'Gartenweg 12', plz: '80334', ort: 'Musterstadt', iban: 'DE24500105171911148770', bic: 'BELADEBEXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-004', mandatsdatum: '2021-04-15' },
|
||||
{ vorname: 'Thomas', nachname: 'Fischer', email: 'thomas.fischer@example.de', telefon: '0174 5555555', geburtsdatum: '1978-06-10', eintrittsdatum: '2008-04-01', strasse: 'Lindenallee 3', plz: '80335', ort: 'Musterstadt', iban: 'DE12500105176228935005', bic: 'BELADEBEXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-005', mandatsdatum: '2021-04-15' },
|
||||
{ vorname: 'Claudia', nachname: 'König', email: 'claudia.koenig@example.de', telefon: '0175 6666666', geburtsdatum: '1992-09-17', eintrittsdatum: '2018-06-01', strasse: 'Feldweg 7', plz: '80333', ort: 'Musterstadt', iban: 'DE67200501001234567890', bic: 'HASPDEHHXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-006', mandatsdatum: '2022-01-20' },
|
||||
{ vorname: 'Michael', nachname: 'Koch', email: 'michael.koch@example.de', telefon: '0176 7777777', geburtsdatum: '1983-04-03', eintrittsdatum: '2012-02-01', strasse: 'Bergstraße 22', plz: '80336', ort: 'Musterstadt', iban: 'DE86200400600526015800', bic: 'COBADEFFXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-007', mandatsdatum: '2021-04-15' },
|
||||
{ vorname: 'Lisa', nachname: 'Zimmermann', email: 'lisa.zimm@example.de', telefon: '0177 8888888', geburtsdatum: '1995-12-25', eintrittsdatum: '2020-10-01', strasse: 'Blumenstraße 4', plz: '80333', ort: 'Musterstadt', iban: 'DE21700519950021267002', bic: 'BYLADEM1AUG', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-008', mandatsdatum: '2022-03-05' },
|
||||
{ vorname: 'Petra', nachname: 'Schreiber', email: 'petra.schreiber@example.de', telefon: '0178 9999999', geburtsdatum: '1988-08-14', eintrittsdatum: '2014-07-01', strasse: 'Weinbergweg 9', plz: '80334', ort: 'Musterstadt', iban: 'DE36200400600532013004', bic: 'COBADEFFXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-009', mandatsdatum: '2021-04-15' },
|
||||
// Aktive ohne IBAN
|
||||
{ vorname: 'Maria', nachname: 'Becker', email: 'maria.becker@example.de', telefon: '0179 1010101', geburtsdatum: '1987-01-30', eintrittsdatum: '2016-03-01', strasse: 'Kirchgasse 6', plz: '80335', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: null, mandatsdatum: null },
|
||||
{ vorname: 'Julia', nachname: 'Müller', email: 'julia.mueller@example.de', telefon: '0151 1111222', geburtsdatum: '1993-05-19', eintrittsdatum: '2019-01-01', strasse: 'Parkstraße 11', plz: '80333', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: null, mandatsdatum: null },
|
||||
{ vorname: 'Markus', nachname: 'Schäfer', email: 'markus.schaefer@example.de', telefon: '0152 3333444', geburtsdatum: '1991-07-07', eintrittsdatum: '2021-05-01', strasse: 'Schulweg 2', plz: '80336', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: null, mandatsdatum: null },
|
||||
// Jugend
|
||||
{ vorname: 'Lena', nachname: 'Bauer', email: null, telefon: '0160 1234567', geburtsdatum: '2012-03-22', eintrittsdatum: '2023-09-01', strasse: 'Schulstraße 14', plz: '80333', ort: 'Musterstadt', iban: 'DE47200400600128491600', bic: 'COBADEFFXXX', status: 'aktiv', gruppe: 'Jugend U15', mandatsreferenz: 'MANDAT-013', mandatsdatum: '2023-09-01' },
|
||||
{ vorname: 'Kevin', nachname: 'Hoffmann', email: null, telefon: '0160 7654321', geburtsdatum: '2013-11-08', eintrittsdatum: '2023-09-01', strasse: 'Waldweg 3', plz: '80334', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Jugend U15', mandatsreferenz: null, mandatsdatum: null },
|
||||
{ vorname: 'Emma', nachname: 'Klein', email: null, telefon: '0160 1122334', geburtsdatum: '2011-06-15', eintrittsdatum: '2022-09-01', strasse: 'Seeweg 7', plz: '80335', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Jugend U15', mandatsreferenz: null, mandatsdatum: null },
|
||||
// Senioren
|
||||
{ vorname: 'Gertrude', nachname: 'Neumann', email: 'g.neumann@example.de', telefon: '089 9876543', geburtsdatum: '1948-04-12', eintrittsdatum: '1990-01-01', strasse: 'Ahornweg 1', plz: '80333', ort: 'Musterstadt', iban: 'DE43500105176118506698', bic: 'BELADEBEXXX', status: 'aktiv', gruppe: 'Senioren', mandatsreferenz: 'MANDAT-016', mandatsdatum: '2019-06-01' },
|
||||
// Passiv / Ausgetreten
|
||||
{ vorname: 'Hans', nachname: 'Schneider', email: 'hans.schneider@example.de', telefon: '0173 0000001', geburtsdatum: '1965-10-20', eintrittsdatum: '1995-04-01', strasse: 'Bergblick 5', plz: '80334', ort: 'Musterstadt', iban: null, bic: null, status: 'passiv', gruppe: 'Senioren', mandatsreferenz: null, mandatsdatum: null },
|
||||
{ vorname: 'Horst', nachname: 'Braun', email: 'horst.braun@example.de', telefon: null, geburtsdatum: '1960-02-14', eintrittsdatum: '1988-01-01', strasse: 'Alte Gasse 3', plz: '80336', ort: 'Musterstadt', iban: null, bic: null, status: 'ausgetreten', gruppe: 'Aktive Mitglieder', mandatsreferenz: null, mandatsdatum: null, austrittsdatum: '2024-12-31' },
|
||||
];
|
||||
// 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`);
|
||||
|
||||
let mitgliederIds = [];
|
||||
for (const m of mitgliederDef) {
|
||||
let rec = await find('mitglieder', `vorname = "${m.vorname}" && nachname = "${m.nachname}" && verein_id = "${verein.id}"`);
|
||||
if (!rec) {
|
||||
rec = await create('mitglieder', {
|
||||
verein_id: verein.id,
|
||||
vorname: m.vorname,
|
||||
nachname: m.nachname,
|
||||
email: m.email || null,
|
||||
telefon: m.telefon || null,
|
||||
geburtsdatum: m.geburtsdatum,
|
||||
eintrittsdatum: m.eintrittsdatum,
|
||||
austrittsdatum: m.austrittsdatum || null,
|
||||
strasse: m.strasse,
|
||||
plz: m.plz,
|
||||
ort: m.ort,
|
||||
iban: m.iban || null,
|
||||
bic: m.bic || null,
|
||||
mandatsreferenz:m.mandatsreferenz || null,
|
||||
mandatsdatum: m.mandatsdatum || null,
|
||||
status: m.status,
|
||||
gruppe_ids: [gruppen[m.gruppe]].filter(Boolean),
|
||||
// 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',
|
||||
});
|
||||
}
|
||||
mitgliederIds.push(rec.id);
|
||||
}
|
||||
console.log(` ✓ ${mitgliederIds.length} Mitglieder`);
|
||||
for (const t of termine) await req('POST', '/termine', t, T);
|
||||
console.log(` ✓ ${termine.length} Termine`);
|
||||
|
||||
// ── 6. Veranstaltungsorte ────────────────────────────────────────────────────
|
||||
console.log('→ Veranstaltungsorte…');
|
||||
const orteDef = [
|
||||
{ name: 'Turnhalle Grundschule Muster', adresse: 'Schulstraße 1, 80333 Musterstadt', typ: 'halle', aktiv: true },
|
||||
{ name: 'Vereinsheim TSV', adresse: 'Vereinsstraße 12, 80333 Musterstadt', typ: 'gebaeude', aktiv: true },
|
||||
{ name: 'Sportplatz West', adresse: 'Weststraße 99, 80335 Musterstadt', typ: 'platz', aktiv: true },
|
||||
];
|
||||
const orte = {};
|
||||
for (const o of orteDef) {
|
||||
let rec = await find('veranstaltungsorte', `name = "${o.name}" && verein_id = "${verein.id}"`);
|
||||
if (!rec) rec = await create('veranstaltungsorte', { verein_id: verein.id, ...o });
|
||||
orte[o.name] = rec.id;
|
||||
}
|
||||
// Ausfall: Turnhalle gesperrt wegen Schulveranstaltung
|
||||
const turnhalleId = orte['Turnhalle Grundschule Muster'];
|
||||
const existingAusfall = await find('ort_ausfaelle', `ort_id = "${turnhalleId}"`);
|
||||
if (!existingAusfall) {
|
||||
const nextWeek = new Date(); nextWeek.setDate(nextWeek.getDate() + 7);
|
||||
const nextWeek2 = new Date(); nextWeek2.setDate(nextWeek2.getDate() + 9);
|
||||
await create('ort_ausfaelle', {
|
||||
ort_id: turnhalleId,
|
||||
von: nextWeek.toISOString().slice(0,10),
|
||||
bis: nextWeek2.toISOString().slice(0,10),
|
||||
grund: 'Schulveranstaltung – Halle nicht verfügbar',
|
||||
});
|
||||
}
|
||||
console.log(' ✓ Orte:', Object.keys(orte).join(', '));
|
||||
// 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`);
|
||||
|
||||
// ── 7. Beitragsarten ─────────────────────────────────────────────────────────
|
||||
console.log('→ Beitragsarten…');
|
||||
const beitraegeDef = [
|
||||
{ name: 'Jahresbeitrag Erwachsene', betrag: 48, rhythmus: 'jaehrlich', beschreibung: 'Für Mitglieder ab 18 Jahren' },
|
||||
{ name: 'Jahresbeitrag Jugend', betrag: 24, rhythmus: 'jaehrlich', beschreibung: 'Für Mitglieder unter 18 Jahren' },
|
||||
{ name: 'Aufnahmegebühr', betrag: 20, rhythmus: 'einmalig', beschreibung: 'Einmalig bei Vereinseintritt' },
|
||||
{ name: 'Monatsbeitrag Fitness', betrag: 15, rhythmus: 'monatlich', beschreibung: 'Für die Fitnessgruppe (optional)' },
|
||||
];
|
||||
for (const b of beitraegeDef) {
|
||||
const ex = await find('beitraege', `name = "${b.name}" && verein_id = "${verein.id}"`);
|
||||
if (!ex) await create('beitraege', { verein_id: verein.id, ...b });
|
||||
}
|
||||
console.log(' ✓', beitraegeDef.map(b => b.name).join(', '));
|
||||
|
||||
// ── 8. Termine ───────────────────────────────────────────────────────────────
|
||||
console.log('→ Termine…');
|
||||
|
||||
const d = (offsetDays, h = 0, m = 0) => {
|
||||
const dt = new Date();
|
||||
dt.setDate(dt.getDate() + offsetDays);
|
||||
dt.setHours(h, m, 0, 0);
|
||||
return dt.toISOString();
|
||||
};
|
||||
|
||||
const termineDef = [
|
||||
// Vergangen
|
||||
{ titel: 'Jahreshauptversammlung 2026', beginn: d(-60, 19, 0), ende: d(-60, 21, 0), ort_id: orte['Vereinsheim TSV'], gruppe_ids: Object.values(gruppen), beschreibung: 'Jahresabrechnung, Vorstandswahl, Planung Sommerfest' },
|
||||
{ titel: 'Trainingssession', beginn: d(-14, 18, 0), ende: d(-14, 20, 0), ort_id: orte['Turnhalle Grundschule Muster'], gruppe_ids: [gruppen['Aktive Mitglieder']] },
|
||||
{ titel: 'Jugendtraining', beginn: d(-7, 16, 0), ende: d(-7, 17, 30), ort_id: orte['Turnhalle Grundschule Muster'], gruppe_ids: [gruppen['Jugend U15']] },
|
||||
// Upcoming
|
||||
{ titel: 'Vorstandssitzung', beginn: d(3, 19, 30), ende: d(3, 21, 0), ort_id: orte['Vereinsheim TSV'], gruppe_ids: [gruppen['Vorstand']], beschreibung: 'Vorbereitung Sommerfest, Kassenstand Q1' },
|
||||
{ titel: 'Sommerfest TSV', beginn: d(32, 14, 0), ende: d(32, 22, 0), ort_id: orte['Sportplatz West'], gruppe_ids: Object.values(gruppen), beschreibung: 'Für Mitglieder und Familien – Grillen, Spiele, Live-Musik' },
|
||||
{ titel: 'Auswärtsspiel Musterliga', beginn: d(18, 11, 0), ende: d(18, 13, 0), ort_id: null, gruppe_ids: [gruppen['Aktive Mitglieder']], ort: 'FC Gegner – Stadionstraße 1', beschreibung: 'Hinfahrt 10:00 Uhr am Vereinsheim' },
|
||||
];
|
||||
|
||||
// Wöchentliches Training als Serie
|
||||
const serie_id = 'seed-serie-dienstag-2026';
|
||||
const trainingsTermine = [];
|
||||
for (let i = 1; i <= 8; i++) {
|
||||
// Nächster Dienstag + i Wochen
|
||||
const dt = new Date();
|
||||
const daysToTue = (2 - dt.getDay() + 7) % 7 || 7;
|
||||
dt.setDate(dt.getDate() + daysToTue + (i - 1) * 7);
|
||||
dt.setHours(18, 30, 0, 0);
|
||||
trainingsTermine.push({
|
||||
titel: 'Dienstags-Training',
|
||||
beginn: dt.toISOString(),
|
||||
ende: new Date(dt.getTime() + 90 * 60 * 1000).toISOString(),
|
||||
ort_id: orte['Turnhalle Grundschule Muster'],
|
||||
gruppe_ids:[gruppen['Aktive Mitglieder']],
|
||||
rrule: 'FREQ=WEEKLY;BYDAY=TU',
|
||||
serie_id,
|
||||
});
|
||||
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}`);
|
||||
}
|
||||
|
||||
const allTermine = [...termineDef, ...trainingsTermine];
|
||||
let termineAngelegt = 0;
|
||||
for (const t of allTermine) {
|
||||
const ex = await find('termine', `titel = "${t.titel}" && verein_id = "${verein.id}" && beginn = "${t.beginn}"`);
|
||||
if (!ex) {
|
||||
await create('termine', { verein_id: verein.id, verfuegbarkeit: 'offen', ...t });
|
||||
termineAngelegt++;
|
||||
}
|
||||
}
|
||||
console.log(` ✓ ${termineAngelegt} Termine (inkl. ${trainingsTermine.length} in Dienstags-Serie)`);
|
||||
|
||||
// ── 9. Nachricht ─────────────────────────────────────────────────────────────
|
||||
console.log('→ Beispiel-Nachricht…');
|
||||
const exMsg = await find('nachrichten', `verein_id = "${verein.id}"`);
|
||||
if (!exMsg) {
|
||||
await create('nachrichten', {
|
||||
verein_id: verein.id,
|
||||
autor_id: adminUser.id,
|
||||
betreff: 'Willkommen bei vereins.haus!',
|
||||
text: 'Liebe Mitglieder,\n\nwir haben unsere Vereinsverwaltung auf vereins.haus umgestellt. Hier findet ihr alle Termine, Nachrichten und Informationen rund um unseren Verein.\n\nBei Fragen wendet euch an den Vorstand.\n\nEuer Vorstand\nTSV Musterstadt 1983 e.V.',
|
||||
gruppe_ids: Object.values(gruppen),
|
||||
gesendet_am: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
// ── Zusammenfassung ──────────────────────────────────────────────────────────
|
||||
console.log('\n✓ Seed abgeschlossen!\n');
|
||||
console.log(' Verein: ', verein.name);
|
||||
console.log(' Login: vorstand@tsv-musterstadt.de / Test123456!');
|
||||
console.log(' Mitglieder:', mitgliederIds.length);
|
||||
console.log(' PocketBase:', PB_URL);
|
||||
main().catch(e => { console.error('✗ Seed-Fehler:', e.message); process.exit(1); });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue