Käfer-Button unten rechts: nimmt den sichtbaren Bildschirm mit html2canvas (selbst gehostet) auf, Vorschau mit roten Rechtecken, Beschreibung, Name optional, Senden ohne Anmeldung. Aus offenen Dialogen heraus wird der Dialog selbst aufgenommen. Bearbeiter sehen die Meldungen unter „Fehler" mit Zähler, kopieren einen fertigen Claude-Prompt und den Screenshot. Serverseitig: Pfade nur in exakter Schreibweise, /api standardmäßig nur für Bearbeiter mit ausdrücklicher Freigabeliste, Bremse zählt jeden Versuch vor dem Lesen großer Bodies, X-Forwarded-For nur von vertrauten Proxys, Nutzertext im Prompt eingezäunt. HTTP-Tests für die Sperren. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
154 lines
8.0 KiB
Markdown
154 lines
8.0 KiB
Markdown
# Rotatory
|
||
|
||
Rotatory plant die Moderation des werktäglichen Morning Meetings: Pro Tag werden ein
|
||
**Hauptmoderator** und ein **Nebenmoderator** aus der Belegschaft fair und zufällig
|
||
ausgelost. Die Monatsansicht zeigt auf jedem Tag die Fotos der beiden Personen. Der fertige
|
||
Plan lässt sich per Knopfdruck als Termine in einen Nextcloud-Kalender schreiben.
|
||
|
||
<img src="public/logo/rotatory-mark.svg" alt="" width="64" height="64" align="left">
|
||
|
||
## Funktionen
|
||
|
||
- Personen mit Vor- und Nachname, optionaler E-Mail, Foto (Upload, wird im Browser auf
|
||
320 px zugeschnitten) und Rolle: Hauptmoderation / Nebenmoderation / beides / keins
|
||
(Nicht-Moderator). Feste freie Wochentage für Teilzeitkräfte. Inaktive Personen bleiben
|
||
angelegt, werden aber nicht ausgelost.
|
||
- Personenimport aus Nextcloud: alle Konten aus dem Systemadressbuch zur Auswahl, Profilbilder
|
||
werden übernommen.
|
||
- Abwesenheiten (Urlaub, geplante Krankheit, Frei, Sonstiges) je Person mit Zeitraum – manuell
|
||
oder importiert aus einem Nextcloud-Kalender mit Terminen wie „Vorname Nachname (Urlaub)",
|
||
„… (krank)", „… (frei)". Wiederholungen werden aufgelöst, Namen den Personen zugeordnet,
|
||
nicht zuordenbare Namen aufgelistet.
|
||
- Feiertage: gesetzliche Feiertage je Bundesland auf Knopfdruck erzeugen, dazu eigene
|
||
betriebsfreie Tage. An diesen Tagen wird nicht geplant.
|
||
- Auslosung für beliebige Monatsbereiche. Regeln: nur Mo–Fr, keine Feiertage, keine
|
||
Abwesenden, niemand an zwei Werktagen hintereinander (wenn vermeidbar), Einsätze
|
||
gleichmäßig verteilt (Fairness-Fenster berücksichtigt auch vergangene Einsätze), Rollen
|
||
werden abgewechselt, dieselbe Paarung wiederholt sich ungern.
|
||
- Einzelne Tage manuell setzen, fixieren (überlebt jede Neuauslosung), neu auslosen oder
|
||
als „Kein Meeting" markieren.
|
||
- Einsatzstatistik je Monat.
|
||
- Export als ICS-Datei oder direkt per CalDAV in einen Nextcloud-Kalender: wahlweise ein
|
||
vorhandener (auch geteilter) Kalender oder ein neu angelegter. Wiederholtes Schreiben
|
||
aktualisiert bestehende Termine und löscht entfallene; fremde Termine im Zeitraum können auf
|
||
Wunsch mit entfernt werden.
|
||
|
||
## Installation
|
||
|
||
Voraussetzung: Node.js 20 oder neuer.
|
||
|
||
```bash
|
||
npm install
|
||
```
|
||
|
||
## Starten
|
||
|
||
```bash
|
||
npm run dev # Entwicklung, startet bei Änderungen neu
|
||
npm start # Produktion
|
||
```
|
||
|
||
Die App lauscht auf `PORT` (Vorgabe 3000) und speichert alle Daten unter `VCH_DATA_DIR`
|
||
(Vorgabe `./data`): eine JSON-Datei plus die Fotos. Ein Datenbankserver ist nicht nötig.
|
||
Eine `.env` im Projektstamm wird beim Start gelesen (bereits gesetzte Variablen gewinnen).
|
||
Beim ersten Start werden die gesetzlichen Feiertage für dieses und nächstes Jahr angelegt;
|
||
Vorgaben: Bayern, Meeting 09:30 Uhr, 15 Minuten.
|
||
|
||
## Konfiguration
|
||
|
||
Alle Variablen sind optional, siehe `.env.example`:
|
||
|
||
| Variable | Bedeutung |
|
||
|---|---|
|
||
| `NEXTCLOUD_URL` | Basis-URL der Nextcloud, z. B. `https://cloud.example.de` |
|
||
| `NEXTCLOUD_USER` | Anmeldename, E-Mail-Adresse geht auch; die Benutzer-ID wird per CalDAV-Discovery ermittelt |
|
||
| `NEXTCLOUD_APP_PASSWORD` | App-Passwort (Nextcloud → Einstellungen → Sicherheit → Geräte & Sitzungen) |
|
||
| `NEXTCLOUD_CALENDAR` | ID des eigenen Kalenders, falls kein vorhandener Zielkalender gewählt wird (Vorgabe `morning-meeting`) |
|
||
| `NEXTCLOUD_INSECURE_TLS` | `true`, wenn die Nextcloud ein selbstsigniertes Zertifikat nutzt |
|
||
| `EDIT_PASSWORD` | Bearbeitungspasswort. Wenn gesetzt, sehen alle nur den Kalender; Auslosen, Personen, Abwesenheiten, Einstellungen und Nextcloud erfordern die Anmeldung über „Anmelden" in der Kopfzeile |
|
||
| `APP_PASSWORD` | Wenn gesetzt, schützt HTTP Basic Auth die ganze App inklusive Ansehen (Benutzername beliebig) |
|
||
|
||
Ohne Nextcloud-Variablen läuft die App vollständig, nur der Button „Go – schreiben" ist
|
||
deaktiviert; der ICS-Download bleibt verfügbar.
|
||
|
||
## Teilen im Team
|
||
|
||
Die Adresse der App kann an alle weitergegeben werden. Mit gesetztem `EDIT_PASSWORD` sehen
|
||
Besucher den Monatskalender mit Fotos und die Einsatzstatistik, aber keine E-Mail-Adressen,
|
||
Notizen oder Abwesenheiten, und sie können nichts ändern. Wer bearbeiten will, klickt oben rechts
|
||
auf „Anmelden". Die Anmeldung gilt 30 Tage im jeweiligen Browser; nach fünf falschen Versuchen
|
||
wartet der Server eine Minute. Ohne `EDIT_PASSWORD` darf jeder alles, wie bei einem Werkzeug
|
||
für eine einzelne Person.
|
||
|
||
## Tauschanfragen
|
||
|
||
Wer an seinem Tag nicht kann, klickt den Tag im Kalender an, wählt seinen Namen, optional die
|
||
Person und den Tag für den Tausch, und schreibt eine Notiz. Dafür ist keine Anmeldung nötig;
|
||
pro Adresse sind zehn Anfragen je Stunde möglich. Der Tag bekommt im Kalender eine Markierung.
|
||
Bearbeiter sehen alle Anfragen unter „Anfragen" mit Zähler in der Navigation und entscheiden:
|
||
„Ausführen" tauscht wie gewünscht (über Kreuz oder Übernahme des Tages), „Tag öffnen" lässt
|
||
manuell wählen, „Erledigt" und „Ablehnen" schließen die Anfrage ohne Änderung. Nach einem
|
||
Tausch den Zeitraum erneut nach Nextcloud schreiben.
|
||
|
||
## Fehler melden
|
||
|
||
Unten rechts sitzt ein Käfer-Button. Ein Klick nimmt den sichtbaren Bildschirm auf (mit der
|
||
selbst gehosteten Bibliothek html2canvas, die die Seite aus dem Seiteninhalt nachzeichnet),
|
||
zeigt ihn als Vorschau, in der sich rote Rechtecke aufziehen lassen, und fragt nach einer
|
||
Beschreibung und optional dem Namen. Dafür ist keine Anmeldung nötig; pro Adresse sind zehn
|
||
Meldungen je Stunde möglich. Bearbeiter sehen die Meldungen unter „Fehler" mit Zähler.
|
||
„Prompt kopieren" legt eine fertige Anweisung für Claude Code in die Zwischenablage, die
|
||
Vorlage dafür steht in den Einstellungen; „Screenshot kopieren" das Bild dazu. Bilder in die
|
||
Zwischenablage legen erlauben Browser nur über https; auf einer http-Adresse öffnet
|
||
„Screenshot öffnen" das Bild zum Kopieren per Rechtsklick. Screenshots sind nur angemeldet
|
||
abrufbar, weil sie Namen und Planung zeigen.
|
||
|
||
## Nextcloud
|
||
|
||
1. Umgebungsvariablen setzen und App neu starten.
|
||
2. **Personen** → „Aus Nextcloud importieren …": Konten auswählen, Fotos kommen mit.
|
||
3. **Abwesenheiten** → Quellkalender (z. B. „Urlaub/Krankheit") und Zeitraum wählen,
|
||
importieren. Vor jeder Auslosung wiederholen, damit neue Urlaube berücksichtigt sind.
|
||
4. **Kalender** → „Auslosen …" für die kommenden Monate.
|
||
5. **Kalender** → „Nextcloud …": Zielkalender wählen (ein vorhandener geteilter Kalender wird
|
||
vorgeschlagen), **Verbindung testen**, Zeitraum wählen und **Go – schreiben**. Je Werktag
|
||
entsteht ein Termin „Morning Meeting: Haupt / Neben" zur eingestellten Uhrzeit.
|
||
|
||
Termine tragen eine stabile UID je Tag (`rotatory-<datum>@<host>`), daher aktualisiert ein
|
||
erneutes Schreiben statt zu duplizieren. Termine anderer Herkunft im Zeitraum werden gezählt
|
||
und nur auf ausdrücklichen Wunsch (Häkchen) entfernt.
|
||
|
||
Mit der Einstellung „Moderatoren als Teilnehmer eintragen" werden die beiden Personen (bei
|
||
hinterlegter E-Mail) als Teilnehmer eingetragen; Nextcloud verschickt dann Einladungen.
|
||
|
||
## Tests und Prüfung
|
||
|
||
```bash
|
||
npm test # Auslosung, Feiertage, ICS, iCal/vCard-Parser, CalDAV-Client (mit simulierter Nextcloud)
|
||
npm run lint # Syntaxprüfung aller Quelldateien
|
||
```
|
||
|
||
## Aufbau
|
||
|
||
```
|
||
server/
|
||
index.js Express-App, REST-API, statische Dateien
|
||
store.js JSON-Speicher (atomar) und Fotoablage
|
||
scheduler.js Auslosungslogik (rein, testbar)
|
||
holidays.js Gesetzliche Feiertage je Bundesland
|
||
ics.js iCalendar-Erzeugung
|
||
nextcloud.js CalDAV/CardDAV-Client (Discovery, PROPFIND, MKCALENDAR, REPORT, PUT, DELETE)
|
||
requests.js Tauschanfragen prüfen und ausführen
|
||
bugs.js Fehlermeldungen: Screenshot prüfen, Prompt-Vorlage
|
||
auth.js Bearbeitungsschutz (Cookie, Bremse)
|
||
ical.js iCalendar- und vCard-Leser, Namenszuordnung
|
||
env.js .env-Leser
|
||
public/
|
||
index.html, app.js, style.css Oberfläche ohne Framework, relative Pfade
|
||
test/ node:test
|
||
```
|
||
|
||
Abhängigkeiten: nur `express`; im Browser zusätzlich die selbst gehostete Bibliothek
|
||
html2canvas (`public/vendor/`, MIT) für Screenshots in Fehlermeldungen. Fotos werden im Browser
|
||
per Canvas verkleinert, `fetch` und `crypto` kommen aus Node.
|