From 161618f6fbe0f1612f5d2990b03ba66a4b560926 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Sat, 14 Feb 2026 15:58:11 +0100 Subject: [PATCH] =?UTF-8?q?F=C3=BCge=20Skripte=20zum=20Aufteilen=20von=20N?= =?UTF-8?q?amen=20in=20firstName=20und=20lastName=20f=C3=BCr=20Mitglieder?= =?UTF-8?q?=20und=20Bewerbungen=20hinzu,=20einschlie=C3=9Flich=20Backup-Fu?= =?UTF-8?q?nktionalit=C3=A4t.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/README-split-names.md | 25 +++++++ scripts/split-names-in-members.js | 80 +++++++++++++++++++++++ scripts/split-names-in-membership-apps.js | 57 ++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 scripts/README-split-names.md create mode 100644 scripts/split-names-in-members.js create mode 100644 scripts/split-names-in-membership-apps.js diff --git a/scripts/README-split-names.md b/scripts/README-split-names.md new file mode 100644 index 0000000..d85e786 --- /dev/null +++ b/scripts/README-split-names.md @@ -0,0 +1,25 @@ +Split-Name Scripts + +Diese Scripts helfen, das Feld `name` in `firstName` und `lastName` zu splitten, für verschiedene Datenquellen im Projekt. + +Available scripts: + +- `scripts/split-names-in-users.js` (CommonJS) + - Splittet `server/data/users.json` und ergänzt fehlende `firstName`/`lastName`. + - Erstellt ein Backup `users.json.bak.` falls Änderungen gemacht werden. + - Ausführen: `node scripts/split-names-in-users.js` + +- `scripts/split-names-in-members.js` (ESM) + - Liest `members.json` über `server/utils/members.js` (beachtet Verschlüsselung), führt Dry-Run by default. + - Mit `--apply` werden Änderungen geschrieben und ein Backup erstellt. + - Ausführen (dry-run): `node scripts/split-names-in-members.js` + - Ausführen (apply): `node scripts/split-names-in-members.js --apply` + +- `scripts/split-names-in-membership-apps.js` (CommonJS) + - Bearbeitet alle JSON-Dateien in `server/data/membership-applications/` und erstellt `.bak` Backups pro Datei. + - Ausführen: `node scripts/split-names-in-membership-apps.js` + +Hinweis: +- Die Scripts sind vorsichtig: sie erstellen Backups bevor sie schreiben (außer beim Dry-Run für members.js). +- `split-names-in-members.js` nutzt die vorhandenen `readMembers`/`writeMembers` Utilities, um Verschlüsselung zu respektieren. +- Teste zuerst mit DRY-RUN oder in einer Kopie des Datenverzeichnisses. diff --git a/scripts/split-names-in-members.js b/scripts/split-names-in-members.js new file mode 100644 index 0000000..f852a28 --- /dev/null +++ b/scripts/split-names-in-members.js @@ -0,0 +1,80 @@ +#!/usr/bin/env node +import fs from 'fs' +import { promises as fsp } from 'fs' +import path from 'path' +import { readMembers, writeMembers } from '../server/utils/members.js' + +// Script to split `name` into firstName/lastName for members.json. +// Usage: +// node scripts/split-names-in-members.js # dry-run, no writes +// node scripts/split-names-in-members.js --apply # apply changes and create backup + +const MEMBERS_FILE_PATH = path.join(process.cwd(), 'server/data/members.json') + +function extractNames(name) { + if (!name || typeof name !== 'string') return { firstName: '', lastName: '' } + const parts = name.trim().split(/\s+/) + if (parts.length === 1) return { firstName: parts[0], lastName: '' } + return { firstName: parts[0], lastName: parts.slice(1).join(' ') } +} + +async function main() { + const apply = process.argv.includes('--apply') + + console.log('Reading members via server utils (handles encryption)...') + const members = await readMembers() + if (!Array.isArray(members)) { + console.error('Unerwartetes Format von members:', typeof members) + process.exit(2) + } + + let changed = false + for (const m of members) { + if ((!m.firstName || !m.lastName) && m.name) { + const { firstName, lastName } = extractNames(m.name) + if (!m.firstName) m.firstName = firstName + if (!m.lastName) m.lastName = lastName + changed = true + } + } + + if (!changed) { + console.log('Keine Änderungen erforderlich. Alle Mitglieder haben firstName/lastName.') + return + } + + console.log(`Gefundene Änderungen: Mitglieder mit ergänztenn Namen werden ${apply ? 'angewendet' : 'nur angezeigt (dry-run)'}.`) + + if (!apply) { + console.log('Vorschau der Änderungen (erstes 10 geänderte Mitglieder):') + let count = 0 + for (const m of members) { + if (m.firstName || m.lastName) { + console.log('-', m.id || '(keine id)', m.firstName, m.lastName, '-', m.name) + count++ + if (count >= 10) break + } + } + console.log('\nFühre das Skript mit --apply aus, um die Änderungen dauerhaft zu schreiben (Backup wird erstellt).') + return + } + + // Create backup of raw file (may be encrypted) + const timestamp = new Date().toISOString().replace(/[:.]/g, '-') + const backupPath = MEMBERS_FILE_PATH + `.bak.${timestamp}` + try { + await fsp.copyFile(MEMBERS_FILE_PATH, backupPath) + console.log('Backup erstellt:', backupPath) + } catch (err) { + console.warn('Konnte kein Backup anlegen (Datei evtl. nicht vorhanden):', err.message) + } + + // Write members using writeMembers (will handle encryption) + await writeMembers(members) + console.log('Mitglieder erfolgreich aktualisiert und verschlüsselt gespeichert.') +} + +main().catch(err => { + console.error('Fehler:', err) + process.exit(1) +}) diff --git a/scripts/split-names-in-membership-apps.js b/scripts/split-names-in-membership-apps.js new file mode 100644 index 0000000..93109f7 --- /dev/null +++ b/scripts/split-names-in-membership-apps.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node +// Script to split name field in membership application JSON files under server/data/membership-applications/ +// It will create backups for each modified file. + +const fs = require('fs') +const path = require('path') + +const APPS_DIR = path.join(__dirname, '../server/data/membership-applications') + +function extractNames(name) { + if (!name || typeof name !== 'string') return { firstName: '', lastName: '' } + const parts = name.trim().split(/\s+/) + if (parts.length === 1) return { firstName: parts[0], lastName: '' } + return { firstName: parts[0], lastName: parts.slice(1).join(' ') } +} + +function main() { + if (!fs.existsSync(APPS_DIR)) { + console.error('membership-applications Verzeichnis nicht gefunden:', APPS_DIR) + process.exit(1) + } + + const files = fs.readdirSync(APPS_DIR).filter(f => f.endsWith('.json')) + if (files.length === 0) { + console.log('Keine Bewerbungsdateien gefunden.') + return + } + + let modified = 0 + for (const file of files) { + const p = path.join(APPS_DIR, file) + let data + try { + data = JSON.parse(fs.readFileSync(p, 'utf8')) + } catch (err) { + console.error('Fehler beim Lesen von', p, err.message) + continue + } + + if ((!data.firstName || !data.lastName) && data.name) { + const { firstName, lastName } = extractNames(data.name) + data.firstName = data.firstName || firstName + data.lastName = data.lastName || lastName + + // Backup + const backup = p + '.bak' + fs.copyFileSync(p, backup) + fs.writeFileSync(p, JSON.stringify(data, null, 2)) + modified++ + console.log('Updated', p, '-> backup at', backup) + } + } + + console.log('Done. Modified files:', modified) +} + +main()