#!/usr/bin/env node /** * Zugang zum Ticketsystem nur fuer Mitarbeiter (Label "mitarbeiter" oder "admin"). * * Usage (im Container, mit dem Server-Key): * docker exec -it -w /app webklar-kundenbereich-server node scripts/mitarbeiter-zugang.mjs --status * ... --label [,...] [--yes] Label "mitarbeiter" vergeben (nur ausdrueckliche Konten) * ... --eintrag-loeschen [--yes] employees-Eintrag eines Kunden loeschen (Konto bleibt) * ... --rechte Probelauf: welche Rechte sich aendern * ... --rechte --anwenden [--yes] Rechte umstellen, Sicherung als JSON (Pfad wird ausgegeben) * ... --rechte --zurueck [--yes] Rechte aus einer Sicherung wiederherstellen */ import 'dotenv/config' import { readFileSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { createInterface } from 'node:readline/promises' import { applyPermissionChanges, grantStaffLabel, planPermissionChanges, removeEmployeeEntry, staffAccessStatus, } from '../server/services/staffAccessAdmin.js' const args = process.argv.slice(2) const flag = (name) => { const i = args.indexOf(name) return i === -1 ? null : args[i + 1] || '' } async function confirm(question) { if (args.includes('--yes')) return true const rl = createInterface({ input: process.stdin, output: process.stdout }) const answer = await rl.question(`${question} (Ja/Nein) `) rl.close() return /^j(a)?$/i.test(answer.trim()) } function where(change) { if (change.kind === 'collection') return `Collection ${change.databaseId}/${change.id} (${change.name})` if (change.kind === 'document') return `Dokument ${change.collectionId}/${change.id}` if (change.kind === 'bucket') return `Bucket ${change.id} (${change.name})` return `Datei ${change.bucketId}/${change.id}` } function printChanges(changes) { for (const change of changes) { console.log(where(change)) console.log(` vorher: ${change.before.join(' ') || '(keine)'}`) console.log(` nachher: ${change.after.join(' ') || '(keine)'}`) } } async function status() { const { rows, labelOnly } = await staffAccessStatus() console.log('employees-Einträge:') for (const r of rows) { const state = r.staff ? 'Zugang: ja ' : 'Zugang: NEIN' const note = r.customer ? ` ← KUNDE (${r.customer}), Eintrag löschen` : r.login ? '' : ' (kein Konto)' console.log(` ${r.employeeId.padEnd(22)} ${r.userId.padEnd(38)} ${r.name.padEnd(20)} ${r.email.padEnd(32)} ${state} [${r.labels.join(',')}]${note}`) } if (labelOnly.length) { console.log('Konten mit Mitarbeiter-Label ohne employees-Eintrag:') for (const u of labelOnly) console.log(` ${u.userId.padEnd(38)} ${u.name.padEnd(20)} ${u.email.padEnd(32)} [${u.labels.join(',')}]`) } } async function label(ids) { const userIds = ids.split(',').map((id) => id.trim()).filter(Boolean) if (!userIds.length) throw new Error('Bitte userIds angeben (kommagetrennt).') console.log(`Label "mitarbeiter" für: ${userIds.join(', ')}`) if (!(await confirm('Diese Konten bekommen Zugang zum Ticketsystem. Fortfahren?'))) return console.log('Abgebrochen.') for (const userId of userIds) { const r = await grantStaffLabel(userId) console.log(`Zugang: ${r.name} <${r.email}> [${r.labels.join(',')}]`) } } async function removeEntry(employeeId) { if (!(await confirm(`employees-Eintrag ${employeeId} löschen (das Konto bleibt)?`))) return console.log('Abgebrochen.') const r = await removeEmployeeEntry(employeeId) console.log(`Eintrag gelöscht: ${r.name} (Konto ${r.userId} bleibt)`) } function report(results) { const failed = results.filter((r) => !r.ok) for (const r of failed) console.log(`FEHLER ${where(r.change)}: ${r.error}`) console.log(`${results.length - failed.length} von ${results.length} Stellen geschrieben.`) if (failed.length) process.exitCode = 1 } async function permissions() { const restore = flag('--zurueck') if (restore) { const changes = JSON.parse(readFileSync(restore, 'utf8')) printChanges(changes.map((c) => ({ ...c, before: c.after, after: c.before }))) if (!(await confirm(`${changes.length} Stellen auf den Stand der Sicherung zurücksetzen?`))) return console.log('Abgebrochen.') return report(await applyPermissionChanges(changes, { direction: 'before' })) } const changes = await planPermissionChanges() if (!changes.length) return console.log('Keine offenen Rechte (any/guests/users) gefunden.') printChanges(changes) if (!args.includes('--anwenden')) return console.log(`Probelauf: ${changes.length} Stellen. Mit --anwenden umstellen.`) const backup = join(tmpdir(), `mitarbeiter-rechte-${new Date().toISOString().replace(/[:.]/g, '-')}.json`) writeFileSync(backup, JSON.stringify(changes, null, 2)) console.log(`Sicherung: ${backup}`) if (!(await confirm(`${changes.length} Stellen nur noch für Mitarbeiter freigeben?`))) return console.log('Abgebrochen.') report(await applyPermissionChanges(changes)) } async function main() { if (args.includes('--status')) return status() const ids = flag('--label') if (ids !== null) return label(ids) const employeeId = flag('--eintrag-loeschen') if (employeeId) return removeEntry(employeeId) if (args.includes('--rechte')) return permissions() console.log('Usage: --status | --label [,...] | --eintrag-loeschen | --rechte [--anwenden | --zurueck ] [--yes]') } main().catch((err) => { console.error(err.message) process.exit(1) })