Baustein 1: Grundgeruest, Datenbank, Anmeldung, zwei Rollen

Legt das Fundament der Lern-Bar an:

- Next.js 15 (App Router), React 19, TypeScript, ESLint, Vitest
- SQLite ueber Drizzle hinter einer Repository-Schicht
- Eigener Migrationslauf mit Pruefsummen, beim Start angewendet
- Argon2id, serverseitige Sitzungen, HttpOnly-Cookie, SameSite=Lax
- Rollen Ausbilder und Azubi, Rechteregeln als reine Funktionen
- Verwaltung: Azubikonto und Ausbildung anlegen, Passwort, Sperren
- Gestaltung Petrol/Bernstein, helle und dunkle Darstellung
- Dockerfile, docker-compose, Lebenszeichen, update.sh mit Rueckfall
- Sicherung und Wiederherstellung als Shell-Skripte
- 29 Tests fuer Rechte, Zeitrechnung und Migrationslauf

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-10 00:52:44 +02:00
co-authored by Claude Opus 5
commit e856349cd4
51 changed files with 11883 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
/**
* Legt ein Konto an – gedacht für das allererste Ausbilderkonto.
*
* npm run konto:anlegen -- --name "Max Mustermann" --email max@betrieb.de --rolle ausbilder
*
* Ohne --passwort wird eines erzeugt und ausgegeben.
*/
import crypto from 'node:crypto';
import { benutzerAnlegen, benutzerNachEmail, passwortSetzen } from '../src/lib/repos/benutzer';
import { rohVerbindung } from '../src/lib/db';
function argument(name: string): string | undefined {
const index = process.argv.indexOf(`--${name}`);
if (index === -1) return undefined;
return process.argv[index + 1];
}
function erzeugePasswort(): string {
// Gut vorlesbar: keine leicht verwechselbaren Zeichen.
const zeichen = 'abcdefghijkmnpqrstuvwxyz23456789';
const roh = crypto.randomBytes(16);
return Array.from(roh, (b) => zeichen[b % zeichen.length]).join('');
}
async function main() {
rohVerbindung(); // Migrationen anwenden
const name = argument('name');
const email = argument('email');
const rolle = (argument('rolle') ?? 'ausbilder') as 'ausbilder' | 'azubi';
const passwort = argument('passwort') ?? erzeugePasswort();
if (!name || !email) {
console.error('Aufruf: npm run konto:anlegen -- --name "Vorname Nachname" --email adresse@betrieb.de [--rolle ausbilder|azubi] [--passwort ...]');
process.exit(1);
}
if (rolle !== 'ausbilder' && rolle !== 'azubi') {
console.error('--rolle muss "ausbilder" oder "azubi" sein.');
process.exit(1);
}
const vorhanden = benutzerNachEmail(email);
if (vorhanden) {
await passwortSetzen(vorhanden.id, passwort);
console.log(`Konto ${email} gab es schon – Passwort neu gesetzt.`);
} else {
await benutzerAnlegen({ name, email, passwort, rolle });
console.log(`Konto angelegt: ${name} <${email}> als ${rolle}.`);
}
console.log(`Passwort: ${passwort}`);
console.log('Bitte notieren und nach der ersten Anmeldung ändern.');
}
main().catch((fehler) => {
console.error(fehler);
process.exit(1);
});