mirror of
https://github.com/guyg1474/hcrb.git
synced 2026-10-15 23:13:28 +02:00
Erste Version: HCRB Spieltag-Kalender und Fahrtplaner
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
10400eee59
30 files changed
+3771
No files matched your search
@@ -0,0 +1,89 @@
|
||||
// Benutzerverwaltung für den Login
|
||||
//
|
||||
// npm run user -- list
|
||||
// npm run user -- add <name> Benutzer anlegen (Passwort wird abgefragt)
|
||||
// npm run user -- passwd <name> Passwort ändern (meldet alle Sitzungen des Benutzers ab)
|
||||
// npm run user -- remove <name> Benutzer löschen
|
||||
|
||||
import { createInterface } from 'node:readline';
|
||||
import { hashPassword, readUsers, writeUsers, normalizeName, MIN_PASSWORD, USERS_FILE } from '../auth.js';
|
||||
|
||||
// Passwort verdeckt einlesen (bei umgeleiteter Eingabe zeilenweise)
|
||||
let pipedLines;
|
||||
function askPassword(prompt) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { stdin, stdout } = process;
|
||||
stdout.write(prompt);
|
||||
if (!stdin.isTTY) {
|
||||
pipedLines ??= createInterface({ input: stdin })[Symbol.asyncIterator]();
|
||||
pipedLines.next().then(({ value }) => { stdout.write('\n'); resolve(value ?? ''); }, reject);
|
||||
return;
|
||||
}
|
||||
let input = '';
|
||||
stdin.setRawMode(true);
|
||||
stdin.setEncoding('utf8');
|
||||
stdin.resume();
|
||||
const onData = (chunk) => {
|
||||
for (const ch of chunk) {
|
||||
if (ch === '\r' || ch === '\n') {
|
||||
stdin.setRawMode(false);
|
||||
stdin.pause();
|
||||
stdin.off('data', onData);
|
||||
stdout.write('\n');
|
||||
return resolve(input);
|
||||
}
|
||||
if (ch === '\u0003') { // Strg+C
|
||||
stdin.setRawMode(false);
|
||||
stdout.write('\n');
|
||||
return reject(new Error('Abgebrochen.'));
|
||||
}
|
||||
if (ch === '\u007f' || ch === '\b') input = input.slice(0, -1);
|
||||
else input += ch;
|
||||
}
|
||||
};
|
||||
stdin.on('data', onData);
|
||||
});
|
||||
}
|
||||
|
||||
async function newPassword() {
|
||||
const pw = await askPassword('Neues Passwort: ');
|
||||
if (pw.length < MIN_PASSWORD) throw new Error(`Das Passwort muss mindestens ${MIN_PASSWORD} Zeichen haben.`);
|
||||
if (await askPassword('Passwort wiederholen: ') !== pw) throw new Error('Die Passwörter stimmen nicht überein.');
|
||||
return hashPassword(pw);
|
||||
}
|
||||
|
||||
const [cmd, rawName] = process.argv.slice(2);
|
||||
const name = normalizeName(rawName);
|
||||
const users = readUsers();
|
||||
const user = users.find((u) => u.name === name);
|
||||
|
||||
try {
|
||||
if (cmd === 'list') {
|
||||
console.log(users.length ? users.map((u) => u.name).join('\n') : 'Noch keine Benutzer angelegt.');
|
||||
} else if (cmd === 'add' && name) {
|
||||
if (!/^[a-z0-9._-]{2,32}$/.test(name)) {
|
||||
throw new Error('Benutzername: 2–32 Zeichen, nur a–z, 0–9, Punkt, Minus, Unterstrich.');
|
||||
}
|
||||
if (user) throw new Error(`Benutzer „${name}“ gibt es schon.`);
|
||||
users.push({ name, hash: await newPassword(), changedAt: Date.now() });
|
||||
writeUsers(users);
|
||||
console.log(`Benutzer „${name}“ angelegt.`);
|
||||
} else if (cmd === 'passwd' && name) {
|
||||
if (!user) throw new Error(`Benutzer „${name}“ gibt es nicht.`);
|
||||
user.hash = await newPassword();
|
||||
user.changedAt = Date.now();
|
||||
writeUsers(users);
|
||||
console.log(`Passwort von „${name}“ geändert. Bestehende Anmeldungen sind abgemeldet.`);
|
||||
} else if (cmd === 'remove' && name) {
|
||||
if (!user) throw new Error(`Benutzer „${name}“ gibt es nicht.`);
|
||||
writeUsers(users.filter((u) => u !== user));
|
||||
console.log(`Benutzer „${name}“ gelöscht.`);
|
||||
} else {
|
||||
console.log('Benutzung: npm run user -- list | add <name> | passwd <name> | remove <name>');
|
||||
console.log(`Benutzerdatei: ${USERS_FILE}`);
|
||||
process.exitCode = cmd ? 1 : 0;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err.message);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
Reference in new issue
Block a user