Files
lernbar/README.md
T
Florian WildeandClaude Opus 5.5 44e6c8bb79 Module abschaltbar machen
Merkkarten, Praxisaufgaben, Checklisten, Berichtsheft, Lernzielkontrollen
und Feedbackgespraech lassen sich unter Verwaltung -> Module fuer die ganze
Lern-Bar ab- und einschalten. Wochenplan, Handouts, Glossar, Auswertung und
Verwaltung bleiben immer an.

Gesperrt wird an drei Stellen, nicht im Layout (Next rendert Layouts beim
Seitenwechsel darunter nicht neu): Seiten mit erfordereModul, Routen fuer
PDF, Anhaenge und Lernstand mit modulAn, alle 37 Serveraktionen der Module
mit pruefeModul. Die Anmeldepruefung kommt jeweils zuerst, damit ein
Unangemeldeter nicht am 404 abliest, was abgeschaltet ist.

Ein abgeschaltetes Modul traegt nirgends bei: Wochenplan, Fortschritt,
Was-wartet, Mein Stand, Ergebnisverlauf, Rahmenplanabdeckung, Vorschlaege
fuers Berichtsheft, Karten im Handout, Kurzbefehle im Manifest. Die
Lernstand-Schnittstelle antwortet mit 410, das Geraet verwirft daraufhin
seine Offline-Warteschlange. Abschalten loescht nichts.

Gespeichert in modul_einstellung (Migration 0010); fehlt die Zeile, ist das
Modul an, eine bestehende Datenbank verhaelt sich also wie vorher.

12 Tests fuer die Regeln, Abschnitt A8 im Abnahmelauf, Anleitung Kapitel 9
mit Bildschirmfoto, README, E43.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 16:58:32 +02:00

336 lines
11 KiB
Markdown
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.
# Lern-Bar
Selbst gehostete Anwendung für die betriebliche Ausbildung zum/zur
**Fachinformatiker/-in für Systemintegration**. Sie ersetzt den Stapel PDFs:
Wochenplan, Handouts, Merkkarten, Praxisaufgaben, Checklisten, Lernzielkontrollen
und – der wichtigste Teil – den gesetzlich vorgeschriebenen Ausbildungsnachweis
(„Berichtsheft").
Gebaut für **einen Ausbilder und ein bis drei Auszubildende**. Kein
Lernplattform-Baukasten, keine Mandanten, keine Cloud.
> **Stand:** Alle zehn Bausteine sind fertig – Fassung 1.0.0.
>
> - [**Anleitung mit Bildschirmfotos**](docs/ANLEITUNG.md) – wie man damit arbeitet
> - [**Betrieb auf Proxmox**](docs/BETRIEB-PROXMOX.md) – von der leeren VM bis zum Echtbetrieb
> - [Abnahmeprotokoll](docs/ABNAHME.md) – die Abnahmekriterien, einzeln durchgespielt
> - [Änderungsprotokoll](CHANGELOG.md) · [Fahrplan](ROADMAP.md) ·
> [Entscheidungen](ENTSCHEIDUNGEN.md)
---
## In fünf Minuten zum laufenden System
### Voraussetzungen
- **Betrieb:** Docker mit Compose-Erweiterung (Docker 24 oder neuer)
- **Entwicklung:** Node.js 22 oder neuer, Git
- Ein Reverse Proxy, der TLS beendet (nginx, Caddy, Traefik) – die Anwendung
selbst spricht nur HTTP und lauscht nur auf der Schleife. **HTTPS ist Pflicht:**
Das Anmeldecookie ist im Betrieb als `Secure` gesetzt, über `http://` scheitert
die Anmeldung ohne Fehlermeldung
### Betrieb mit Docker
```bash
git clone <repository-url> lernbar
cd lernbar
docker compose up -d --build
```
Die Anwendung lauscht danach auf `127.0.0.1:3000`. Erstes Ausbilderkonto
anlegen:
```bash
docker compose exec lernbar node werkzeuge/konto.js \
--name "Vorname Nachname" --email ausbilder@betrieb.de --rolle ausbilder
```
Ohne `--passwort` wird eines erzeugt und ausgegeben. Gibt es die Adresse
schon, setzt derselbe Aufruf das Passwort neu – das ist zugleich der Weg, wenn
das einzige Ausbilderkonto sein Passwort vergessen hat.
Danach im Browser anmelden, unter **Verwaltung** das Azubikonto anlegen und
das Ausbildungsverhältnis mit Beginndatum einrichten.
### Entwicklung
```bash
npm install
npm approve-scripts better-sqlite3 esbuild unrs-resolver # nur beim ersten Mal
npm run konto:anlegen -- --name "Vorname Nachname" --email du@betrieb.de --rolle ausbilder
npm run dev
```
Ohne `npm approve-scripts` bleiben `better-sqlite3` und `esbuild` unübersetzt –
npm blockiert seit Version 11 die Installationsskripte fremder Pakete.
Die Anwendung läuft dann auf <http://localhost:3000>. Datenbank und Anhänge
landen unter `daten/` (nicht im Repository).
---
## Beispiel für einen Reverse Proxy
```nginx
server {
listen 443 ssl http2;
server_name lernbar.betrieb.local;
ssl_certificate /etc/ssl/lernbar.crt;
ssl_certificate_key /etc/ssl/lernbar.key;
client_max_body_size 30m; # Fotos vom Aufbau als Anhang (Grenze der Anwendung: 25 MB)
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
---
## Inhalte pflegen
Die fachlichen Inhalte sind **Dateien im Repository**, keine
Datenbankeinträge – Bearbeiten in VS Code, Versionieren mit Git, Sichern mit
`git clone`.
```
content/
wochen/ 01-arbeitsalltag.yaml …
handouts/ N3-subnetting-rechnen.md …
aufgaben/ 2.2-notebooks-vergleichen.md …
checklisten/ 02-windows-installieren.yaml …
lernzielkontrollen/ woche-03.yaml …
glossar.yaml
feedbackgespraech.yaml Leitfaden und Kriterien für das Gespräch
rahmenplan.yaml Berufsbildpositionen und Lernfelder
```
Ein Handout ist Markdown mit YAML-Kopf:
```markdown
---
id: N3
titel: Subnetting rechnen – der Weg in vier Schritten
block: netzwerk
woche: 3
worum: Eine Adresse und eine Maske reichen, um Netz, Broadcast und Hostbereich zu bestimmen.
rahmenplan: [B2, A8]
lernfeld: LF3
merksatz: Blockgröße finden, Vielfache aufschreiben, eigene Adresse einsortieren.
karten:
- frage: Zu welchem Netz gehört 192.168.10.200 bei der Maske 255.255.255.224?
antwort: Blockgröße 32, Netze bei .192 und .224 – die Adresse gehört zu 192.168.10.192/27.
---
## Das Wesentliche
…
```
Beim Start werden alle Inhalte eingelesen und geprüft: Jede Kennung muss es
geben, jedes Handout gehört zu einer Woche, jede Rahmenplanposition steht in
`rahmenplan.yaml`. Passt etwas nicht, startet die Anwendung nicht und nennt
Datei und Stelle. Prüfen ohne Start:
```bash
npm run inhalte:pruefen
```
---
## Module ein- und ausschalten
Unter **Verwaltung → Module** lassen sich Merkkarten, Praxisaufgaben,
Checklisten, Berichtsheft, Lernzielkontrollen und Feedbackgespräch abschalten –
für die ganze Lern-Bar, sofort und ohne Neustart. Wochenplan, Handouts, Glossar,
Auswertung und Verwaltung bleiben immer an.
Ein abgeschaltetes Modul verschwindet aus Navigation, Wochenplan, Fortschritt,
Auswertung und Rahmenplanabdeckung, und seine Seiten, PDFs und Serveraktionen
sind gesperrt. **Gelöscht wird nichts:** Nach dem Einschalten ist alles wieder
da. Einzelheiten in der [Anleitung](docs/ANLEITUNG.md#9-module-ein--und-ausschalten),
die Begründung in [E43](ENTSCHEIDUNGEN.md).
---
## Datensicherung
Gesichert werden **Datenbank und Anhänge**. Die Inhalte stecken im Repository
und brauchen keine eigene Sicherung.
### Sicherung ziehen
```bash
# im laufenden Container
docker compose exec lernbar /app/werkzeuge/sicherung.sh /daten/sicherungen
# aus dem Volume herausholen
docker compose cp lernbar:/daten/sicherungen ./sicherungen
```
Auf dem Entwicklungsrechner:
```bash
LERNBAR_DATEN=./daten npm run backup -- ./sicherungen
```
Das Archiv heißt `lernbar-JJJJMMTT-HHMMSS.tar.gz` und enthält
`lernbar.sqlite`, `anhaenge/` und eine kurze Beschreibung. Die Kopie der
Datenbank entsteht über die Sicherungsschnittstelle von SQLite, ist also auch
im laufenden Betrieb in sich stimmig.
### Zurückspielen
```bash
docker compose stop lernbar
docker compose cp ./sicherungen/lernbar-20260910-0300.tar.gz lernbar:/tmp/
docker compose start lernbar
docker compose exec lernbar /app/werkzeuge/wiederherstellen.sh /tmp/lernbar-20260910-0300.tar.gz
docker compose restart lernbar
```
Der bisherige Stand wird nicht gelöscht, sondern nach
`/daten/verdraengt-<zeitstempel>/` verschoben.
### Nächtlich per Cronjob
`/etc/cron.d/lernbar-sicherung`:
```cron
# Jede Nacht um 03:00 sichern, Archive älter als 30 Tage wegräumen.
SHELL=/bin/sh
PATH=/usr/local/bin:/usr/bin:/bin
0 3 * * * root cd /opt/lernbar && docker compose exec -T lernbar /app/werkzeuge/sicherung.sh /daten/sicherungen >> /var/log/lernbar-sicherung.log 2>&1
30 3 * * * root cd /opt/lernbar && docker compose exec -T lernbar find /daten/sicherungen -name 'lernbar-*.tar.gz' -mtime +30 -delete
```
Liegt das Volume auf einem Pfad, den die Sicherung des Servers ohnehin
mitnimmt, ist damit alles erledigt. Sonst zusätzlich mit `docker compose cp`
herauskopieren und wegschaffen.
---
## Aktualisieren
Auf dem Linux-Server:
```bash
cd /opt/lernbar
./update.sh
```
Das Skript sichert zuerst, holt den Quelltext, baut das Abbild neu, schaltet um
und wartet auf das Lebenszeichen. Meldet sich die neue Fassung nicht gesund,
schaltet es selbsttätig auf das vorherige Abbild zurück und zeigt die letzten
Protokollzeilen.
| Angabe | Wirkung |
|---|---|
| *(ohne)* | sichern, holen, bauen, umschalten |
| `--ohne-git` | nur neu bauen und neu starten |
| `--ohne-sicherung` | Sicherung überspringen (nicht empfohlen) |
| `--zweig main` | einen bestimmten Git-Zweig holen |
| `--hilfe` | Kurzhilfe |
---
## Umgebungsvariablen
| Name | Vorgabe im Container | Bedeutung |
|---|---|---|
| `PORT` | `3000` | Lauschport |
| `TZ` | `Europe/Berlin` | Zeitzone |
| `LERNBAR_DATEN` | `/daten` | Wurzel für Datenbank und Anhänge |
| `LERNBAR_DB` | `<daten>/lernbar.sqlite` | abweichender Pfad der Datenbank |
| `LERNBAR_INHALTE` | `/app/content` | Wurzel der Inhaltsdateien |
| `LERNBAR_MIGRATIONEN` | `/app/migrationen` | Verzeichnis der SQL-Migrationen |
---
## Prüfen und Testen
```bash
npm run typecheck # TypeScript
npm run lint # ESLint
npm run test # Vitest – Logik, die wehtut (341 Tests)
npm run abnahme # der Abnahmelauf, siehe unten
npm run abnahme:container # derselbe Weg gegen das Docker-Abbild
```
### Der Abnahmelauf
`npm run abnahme` ist die Prüfung, auf die es ankommt. Er legt ein
**Wegwerfverzeichnis** an, startet die Anwendung mit einer **frischen, leeren
Datenbank** und spielt den ganzen Weg durch:
1. Migrationen laufen an, das erste Ausbilderkonto entsteht auf der Kommandozeile
2. Die Inhaltsprüfung wird mit einem absichtlich eingebauten Fehler auf die
Probe gestellt – sie muss Datei, Zeile und Wert nennen
3. Der Ausbilder legt ein Azubikonto an; das Passwort darf danach nirgends mehr
auftauchen, in der Datenbank steht ein Argon2id-Hash
4. Der Azubi liest ein Handout, lernt eine Karte, meldet eine Aufgabe zur Abnahme
5. Der Ausbilder nimmt sie ab; sie schlägt in der Rahmenplanabdeckung durch
6. Das Wochenblatt wird angelegt und als PDF ausgegeben
7. Berichtsheft und Merkkarten werden über die Verwaltung abgeschaltet: Seite
und PDF sind für beide Rollen gesperrt, nach dem Einschalten ist das
Wochenblatt unverändert da
8. **Gesichert, absichtlich beschädigt, zurückgespielt.** Eine Sicherung, die
nie zurückgespielt wurde, ist keine Sicherung
Das Protokoll landet in [`docs/ABNAHME.md`](docs/ABNAHME.md).
Voraussetzung: `npm run build` ist gelaufen, und Port 3399 ist frei – sonst
bricht der Lauf mit einer Meldung ab, statt gegen einen fremden Datenbestand zu
prüfen.
### Der Containerlauf
`npm run abnahme` prüft über `next start`, betrieben wird aber der Container.
`npm run abnahme:container` baut deshalb das Abbild, startet es mit leerem
Volume und geht den Weg aus dieser README: Lebenszeichen, erstes Konto über
`werkzeuge/konto.js`, Anmeldung, Passwort neu setzen, Sperre nach
Fehlversuchen, Sicherung im Container.
Danach werden Container, Volume und Abbild wieder entfernt.
Voraussetzung: Docker läuft, Port 3398 ist frei (`LERNBAR_CONTAINER_PORT`).
### Einzelne Zusagen
```bash
node scripts/pruefungen/musterloesung-dichtheit.mjs # Musterlösung dicht?
node scripts/pruefungen/baustein-9.mjs # Abdeckung, Feedback
node scripts/pruefungen/baustein-10.mjs # PWA, Offline, Druck
```
Diese drei brauchen eine **laufende** Anwendung unter `http://localhost:3000`
mit den Beispieldaten.
---
## Aufbau
```
src/
app/ Seiten und Serveraktionen (App Router)
komponenten/ wiederverwendete Bausteine der Oberfläche
lib/
auth/ Passwörter, Sitzungen, Rechteregeln
db/ Verbindung, Schema, Migrationen
repos/ der gesamte Datenzugriff – nur hier wird SQL gesprochen
zeit/ Europe/Berlin, ISO-Kalenderwochen
content/ fachliche Inhalte als Markdown und YAML
scripts/ Werkzeuge für die Kommandozeile
im-container/ Sicherung und Wiederherstellung (Shell, läuft überall)
```
Weiterführend: [Fahrplan](ROADMAP.md) ·
[Änderungsprotokoll](CHANGELOG.md) · [Arbeitstagebuch](DEVLOG.md) ·
[Entscheidungen](ENTSCHEIDUNGEN.md)