Files
Webklar-Kundenbereich/scripts/mitarbeiter-zugang.mjs
T

136 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 <userId>[,<userId>...] [--yes] Label "mitarbeiter" vergeben (nur ausdrueckliche Konten)
* ... --eintrag-loeschen <employeeId> [--yes] employees-Eintrag eines Kunden loeschen (Konto bleibt)
* ... --rechte [--offen-lassen <id>[,<id>...]] Probelauf: welche Rechte sich aendern (IDs bleiben offen)
* [--ohne-buckets] Buckets auslassen (Key ohne buckets-Scope, dann in der Konsole)
* ... --rechte --anwenden [--sicherung <datei>] [--yes] Rechte umstellen, vorher Sicherung als JSON
* ... --rechte --zurueck <sicherung.json> [--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 keep = (flag('--offen-lassen') || '').split(',').map((id) => id.trim()).filter(Boolean)
const buckets = !args.includes('--ohne-buckets')
const { changes, errors } = await planPermissionChanges(undefined, { keep, buckets })
for (const e of errors) console.log(`NICHT LESBAR ${e.path}: ${e.error}`)
if (keep.length) console.log(`Bleiben offen: ${keep.join(', ')}`)
if (!buckets) console.log('Buckets übersprungen – deren Rechte bitte in der Appwrite-Konsole umstellen.')
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.`)
if (errors.length) throw new Error('Nicht alles lesbar (siehe oben) - erst den Key-Scope klären, dann erneut.')
const backup = flag('--sicherung') || join(tmpdir(), `mitarbeiter-rechte-${new Date().toISOString().replace(/[:.]/g, '-')}.json`)
writeFileSync(backup, JSON.stringify(changes, null, 2))
console.log(`Sicherung: ${backup}`)
console.log(` aus dem Container holen: docker cp webklar-kundenbereich-server:${backup} /root/reports/`)
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 <userId>[,...] | --eintrag-loeschen <employeeId> | --rechte [--offen-lassen <id>,...] [--ohne-buckets] [--anwenden [--sicherung <datei>] | --zurueck <datei>] [--yes]')
}
main().catch((err) => {
console.error(err.message)
process.exit(1)
})