docs: Handbuchseite zum Erfassen und Beschreibung der Schnittstelle
Die Beschreibung sagt auch, was die Schnittstelle bewusst nicht kann — Bereich setzen, Aufgaben loeschen, Zeitbloecke anfassen. Das ist eine Entscheidung und kein Versehen, und wer davorsteht, soll es erfahren, statt es fuer eine Luecke zu halten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
10b49cf8df
commit
2e57713a8e
@@ -39,6 +39,10 @@ wird.
|
|||||||
- **Integrationen**: Zammad-Tickets werden zu Aufgaben, Telegram erfasst und
|
- **Integrationen**: Zammad-Tickets werden zu Aufgaben, Telegram erfasst und
|
||||||
hakt ab, und der Spiegel bringt dienstliche Belegung nach Hause — ohne
|
hakt ab, und der Spiegel bringt dienstliche Belegung nach Hause — ohne
|
||||||
Titel
|
Titel
|
||||||
|
- **Schnellerfassung vom Handy**, die auch im Funkloch nichts verliert; der
|
||||||
|
Planer lässt sich als App auf dem Startbildschirm ablegen
|
||||||
|
- **Öffentliche Schnittstelle** mit Tokens je Anwendung, Rechten je Token und
|
||||||
|
jeder schreibenden Nutzung im Protokoll ([Beschreibung](docs/API.md))
|
||||||
- Anmeldung über einen OIDC-Anbieter, über Passkeys oder mit E-Mail und
|
- Anmeldung über einen OIDC-Anbieter, über Passkeys oder mit E-Mail und
|
||||||
Passwort — je Instanz einzeln zuschaltbar
|
Passwort — je Instanz einzeln zuschaltbar
|
||||||
- Rollen `Benutzer` und `Verwaltung`; jede schreibende Aktion wird
|
- Rollen `Benutzer` und `Verwaltung`; jede schreibende Aktion wird
|
||||||
@@ -49,8 +53,7 @@ wird.
|
|||||||
|
|
||||||
### Geplant
|
### Geplant
|
||||||
|
|
||||||
Schnellerfassung vom Handy, eine öffentliche API und ein MCP-Server.
|
Ein MCP-Server. Siehe [ROADMAP.md](ROADMAP.md).
|
||||||
Siehe [ROADMAP.md](ROADMAP.md).
|
|
||||||
|
|
||||||
## Bildergalerie
|
## Bildergalerie
|
||||||
|
|
||||||
|
|||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
# Öffentliche Schnittstelle
|
||||||
|
|
||||||
|
Für fremde Anwendungen, Skripte und Automaten. Wer den Planer selbst bedient,
|
||||||
|
braucht sie nicht.
|
||||||
|
|
||||||
|
Basisadresse: `<APP_URL>/api/v1`
|
||||||
|
|
||||||
|
## Ausweisen
|
||||||
|
|
||||||
|
Jede Anfrage trägt einen Token:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer planer_ab12cd34_…" \
|
||||||
|
https://planer.example.de/api/v1/selbst
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternativ `X-Planer-Token: planer_…`, falls das Werkzeug keine
|
||||||
|
Authorization-Kopfzeile kann.
|
||||||
|
|
||||||
|
Tokens legst du in der Anwendung unter **API** an (nur Verwaltung). Der Wert
|
||||||
|
erscheint **einmal**; danach liegt in der Datenbank nur sein Fingerabdruck.
|
||||||
|
Wer ihn verliert, legt einen neuen an.
|
||||||
|
|
||||||
|
## Rechte
|
||||||
|
|
||||||
|
Drei Stück, und **keines schließt ein anderes ein**:
|
||||||
|
|
||||||
|
| Recht | Was damit geht |
|
||||||
|
| ----------- | -------------------------------------- |
|
||||||
|
| `lesen` | Aufgaben abfragen |
|
||||||
|
| `erfassen` | Neue Aufgaben anlegen — mehr nicht |
|
||||||
|
| `schreiben` | Bestehende Aufgaben ändern und abhaken |
|
||||||
|
|
||||||
|
Ein Dienst, der nur meldet „hier ist etwas Neues", bekommt `erfassen` und
|
||||||
|
sonst nichts. Das ist der häufigste Fall und der, bei dem ein mitgelesener
|
||||||
|
Token am wenigsten anrichtet.
|
||||||
|
|
||||||
|
## Fehler
|
||||||
|
|
||||||
|
| Status | Bedeutung |
|
||||||
|
| ------ | ----------------------------------------------------------- |
|
||||||
|
| 400 | Die Anfrage war fehlerhaft (Feldliste steht in der Antwort) |
|
||||||
|
| 401 | Kein gültiger Token |
|
||||||
|
| 403 | Dieser Token darf das nicht |
|
||||||
|
| 404 | Gibt es nicht |
|
||||||
|
|
||||||
|
401 sieht immer gleich aus — ob ein Token unbekannt, abgelaufen oder
|
||||||
|
zurückgezogen ist, verrät die Schnittstelle nicht. Von außen soll sich nicht
|
||||||
|
abfragen lassen, welche Tokens es gibt.
|
||||||
|
|
||||||
|
## Endpunkte
|
||||||
|
|
||||||
|
### `GET /selbst`
|
||||||
|
|
||||||
|
Wer bin ich, was darf ich? Braucht **kein** Recht — sonst müsste man raten,
|
||||||
|
ob der Token falsch ist oder nur zu wenig darf.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "name": "Wartungsplaner-Import", "rechte": ["erfassen"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
### `GET /aufgaben` · Recht `lesen`
|
||||||
|
|
||||||
|
| Parameter | Standard | Bedeutung |
|
||||||
|
| --------- | -------- | ------------------- |
|
||||||
|
| `grenze` | 25 | Höchstens 100 |
|
||||||
|
| `versatz` | 0 | Zum Blättern |
|
||||||
|
| `offen` | — | `true` oder `false` |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"aufgaben": [
|
||||||
|
{
|
||||||
|
"id": "clx…",
|
||||||
|
"titel": "#4711 Drucker klemmt",
|
||||||
|
"notiz": null,
|
||||||
|
"status": "OFFEN",
|
||||||
|
"prioritaet": 3,
|
||||||
|
"aufwandMinuten": null,
|
||||||
|
"faelligAm": null,
|
||||||
|
"quelle": "API",
|
||||||
|
"ref": "ticket-4711",
|
||||||
|
"url": "https://…",
|
||||||
|
"erstelltAm": "2026-03-17T08:20:00.000Z",
|
||||||
|
"bereich": null,
|
||||||
|
"projekt": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gesamt": 42,
|
||||||
|
"grenze": 25,
|
||||||
|
"versatz": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `POST /aufgaben` · Recht `erfassen`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://planer.example.de/api/v1/aufgaben \
|
||||||
|
-H "Authorization: Bearer planer_…" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"titel":"Drucker klemmt","ref":"ticket-4711"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
| Feld | Pflicht | Bemerkung |
|
||||||
|
| ---------------- | ------- | ----------------------------------- |
|
||||||
|
| `titel` | ja | bis 200 Zeichen |
|
||||||
|
| `notiz` | nein | bis 4000 Zeichen |
|
||||||
|
| `ref` | nein | Kennung beim Aufrufer — siehe unten |
|
||||||
|
| `url` | nein | Verweis ins Ursprungssystem |
|
||||||
|
| `faelligAm` | nein | `JJJJ-MM-TT` |
|
||||||
|
| `prioritaet` | nein | 1 (dringend) bis 5 (irgendwann) |
|
||||||
|
| `aufwandMinuten` | nein | 1 bis 1440 |
|
||||||
|
|
||||||
|
Antwort: `201` mit der angelegten Aufgabe.
|
||||||
|
|
||||||
|
**`ref` macht den Aufruf wiederholbar.** Zweimal dieselbe Kennung ergibt
|
||||||
|
**eine** Aufgabe, nicht zwei — der zweite Aufruf frischt Titel, Notiz und
|
||||||
|
Verweis auf. Ohne `ref` entsteht jedes Mal eine neue Aufgabe; dann hast du es
|
||||||
|
so gewollt.
|
||||||
|
|
||||||
|
Benutze `ref`, wann immer es drüben eine stabile Kennung gibt. Das ist der
|
||||||
|
Unterschied zwischen einer Schnittstelle, die man nach einem Netzhänger
|
||||||
|
gefahrlos wiederholen kann, und einer, die dabei Karteileichen anlegt.
|
||||||
|
|
||||||
|
### `GET /aufgaben/<id>` · Recht `lesen`
|
||||||
|
|
||||||
|
### `PATCH /aufgaben/<id>` · Recht `schreiben`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X PATCH https://planer.example.de/api/v1/aufgaben/clx… \
|
||||||
|
-H "Authorization: Bearer planer_…" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"erledigt":true}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Änderbar sind `titel`, `notiz`, `faelligAm`, `prioritaet`, `aufwandMinuten`
|
||||||
|
und `erledigt`. Mindestens ein Feld muss dabei sein.
|
||||||
|
|
||||||
|
## Was die Schnittstelle bewusst **nicht** kann
|
||||||
|
|
||||||
|
- **Bereich und Projekt setzen.** Was von außen kommt, landet in der Inbox.
|
||||||
|
Die Einordnung ist Sache dessen, der plant, nicht dessen, der meldet —
|
||||||
|
sonst räumt ein fremder Dienst die eigene Ordnung um.
|
||||||
|
- **Aufgaben löschen.** Abhaken ja, löschen nein. Ein Fehler im fremden
|
||||||
|
Skript soll keine Planung vernichten.
|
||||||
|
- **Zeitblöcke, Bereiche, Projekte oder Kalender anfassen.**
|
||||||
|
|
||||||
|
Fehlt dir etwas davon, ist das eine Entscheidung und kein Versehen — sag
|
||||||
|
Bescheid, dann wird darüber geredet.
|
||||||
|
|
||||||
|
## Protokoll
|
||||||
|
|
||||||
|
Jede **schreibende** Nutzung landet im Protokoll, mit dem Token, über den sie
|
||||||
|
kam. Lesen nicht: Ein Dienst, der im Minutentakt abfragt, würde das Protokoll
|
||||||
|
unbrauchbar machen.
|
||||||
|
|
||||||
|
Auch ein abgewiesener Zugriff wird vermerkt. Ein Dienst, der immer wieder an
|
||||||
|
dieselbe Wand läuft, ist entweder falsch eingerichtet oder versucht etwas.
|
||||||
+14
-12
@@ -13,34 +13,36 @@ Anleitung für den Alltag mit dem Planer. Eine Seite je Thema.
|
|||||||
|
|
||||||
3. **[Aufgaben erfassen und wiederfinden](aufgaben.md)** – Schnellerfassung,
|
3. **[Aufgaben erfassen und wiederfinden](aufgaben.md)** – Schnellerfassung,
|
||||||
Inbox leeren, Filter, Status und Fälligkeit
|
Inbox leeren, Filter, Status und Fälligkeit
|
||||||
4. **[Ideen](ideen.md)** – festhalten, was noch keine Aufgabe ist
|
4. **[Schnellerfassung vom Handy](erfassen.md)** – als App ablegen, auch
|
||||||
5. **[Projekte](projekte.md)** – mehrere Aufgaben unter einem Ziel, mit
|
ohne Netz erfassen
|
||||||
|
5. **[Ideen](ideen.md)** – festhalten, was noch keine Aufgabe ist
|
||||||
|
6. **[Projekte](projekte.md)** – mehrere Aufgaben unter einem Ziel, mit
|
||||||
Fortschritt
|
Fortschritt
|
||||||
6. **[Kalenderkonten](kalenderkonten.md)** – Microsoft 365 und Google
|
7. **[Kalenderkonten](kalenderkonten.md)** – Microsoft 365 und Google
|
||||||
verbinden, Belegung verstehen
|
verbinden, Belegung verstehen
|
||||||
7. **[Wochenplanung](woche.md)** – Aufgaben per Ziehen und Ablegen in den
|
8. **[Wochenplanung](woche.md)** – Aufgaben per Ziehen und Ablegen in den
|
||||||
Kalender legen
|
Kalender legen
|
||||||
8. **[Benachrichtigungen](benachrichtigungen.md)** – Push, E-Mail und
|
9. **[Benachrichtigungen](benachrichtigungen.md)** – Push, E-Mail und
|
||||||
Telegram einrichten, Ruhezeiten setzen
|
Telegram einrichten, Ruhezeiten setzen
|
||||||
9. **[Integrationen](integrationen.md)** – Zammad, Telegram und der Spiegel
|
10. **[Integrationen](integrationen.md)** – Zammad, Telegram und der Spiegel
|
||||||
zwischen Firma und Daheim
|
zwischen Firma und Daheim
|
||||||
|
|
||||||
## Verwaltung
|
## Verwaltung
|
||||||
|
|
||||||
10. **[Protokoll](protokoll.md)** – nachsehen, wer wann was geändert hat
|
11. **[Protokoll](protokoll.md)** – nachsehen, wer wann was geändert hat
|
||||||
|
|
||||||
## Was noch kommt
|
## Was noch kommt
|
||||||
|
|
||||||
Diese Seiten entstehen mit den nächsten Meilensteinen:
|
Diese Seiten entstehen mit den nächsten Meilensteinen:
|
||||||
|
|
||||||
| Thema | Meilenstein |
|
| Thema | Meilenstein |
|
||||||
| ------------------------------- | ----------- |
|
| ----- | ----------- |
|
||||||
| Schnellerfassung vom Handy, API | M7 |
|
| MCP | M8 |
|
||||||
| MCP | M8 |
|
|
||||||
|
|
||||||
Der [Fahrplan](../../ROADMAP.md) sagt, was in welchem Meilenstein steckt.
|
Der [Fahrplan](../../ROADMAP.md) sagt, was in welchem Meilenstein steckt.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
Für fremde Anwendungen und Skripte: [Schnittstelle](../API.md).
|
||||||
Zur Einrichtung einer neuen Instanz: [Schnellstart](../SCHNELLSTART.md).
|
Zur Einrichtung einer neuen Instanz: [Schnellstart](../SCHNELLSTART.md).
|
||||||
Für Betrieb, Sicherung und alle Umgebungsvariablen: [README](../../README.md).
|
Für Betrieb, Sicherung und alle Umgebungsvariablen: [README](../../README.md).
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# Schnellerfassung vom Handy
|
||||||
|
|
||||||
|
Die Seite **Erfassen** ist für den Moment gemacht, in dem dir etwas einfällt
|
||||||
|
und du keine Zeit hast, es einzusortieren: ein Feld, ein Knopf, fertig.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Alles, was hier hereinkommt, landet in der **Inbox** — ohne Bereich, ohne
|
||||||
|
Termin. Das Einsortieren passiert später am Rechner, wenn Ruhe dafür ist.
|
||||||
|
|
||||||
|
## Als Anwendung ablegen
|
||||||
|
|
||||||
|
Der Planer lässt sich auf dem Startbildschirm ablegen und verhält sich dann
|
||||||
|
wie eine App: eigenes Symbol, kein Browserrahmen, direkt auf dieser Seite.
|
||||||
|
|
||||||
|
| Gerät | Weg |
|
||||||
|
| --------------------- | -------------------------------------------- |
|
||||||
|
| Android (Chrome) | Menü ⋮ → **App installieren** |
|
||||||
|
| iPhone (Safari) | Teilen-Symbol → **Zum Home-Bildschirm** |
|
||||||
|
| Rechner (Chrome/Edge) | Symbol in der Adresszeile → **Installieren** |
|
||||||
|
|
||||||
|
## Ohne Netz
|
||||||
|
|
||||||
|
**Das ist der eigentliche Punkt dieser Seite.** Im Keller, im Aufzug, im Zug
|
||||||
|
zwischen zwei Funkzellen: Erfasst wird trotzdem.
|
||||||
|
|
||||||
|
Was passiert:
|
||||||
|
|
||||||
|
1. Das Erfasste wird **zuerst im Gerät gemerkt**, dann verschickt. Nicht
|
||||||
|
andersherum — sonst wäre es weg, wenn du den Browser mitten im Senden
|
||||||
|
schließt.
|
||||||
|
2. Ohne Netz bleibt es liegen und steht unter dem Feld als „wartet".
|
||||||
|
3. Sobald wieder Netz da ist, geht es von selbst hinaus. Du bekommst eine
|
||||||
|
kurze Rückmeldung.
|
||||||
|
|
||||||
|
Der Knopf **Jetzt versuchen** stößt das von Hand an, wenn du nicht warten
|
||||||
|
magst.
|
||||||
|
|
||||||
|
> **Geht dabei etwas verloren?** Nein. Jede Erfassung bekommt eine eigene
|
||||||
|
> Kennung, die mitgeschickt wird. Kommt dieselbe Nachricht zweimal an — weil
|
||||||
|
> die Verbindung beim ersten Versuch abbrach, die Anfrage aber trotzdem
|
||||||
|
> durchging —, entsteht trotzdem nur **eine** Aufgabe.
|
||||||
|
|
||||||
|
Die Warteschlange liegt in der Datenbank des Browsers, nicht im flüchtigen
|
||||||
|
Speicher. Sie übersteht das Schließen des Tabs und einen Neustart des
|
||||||
|
Geräts.
|
||||||
|
|
||||||
|
Damit die Seite ohne Netz überhaupt aufgeht, hält der Planer genau **diese
|
||||||
|
eine Seite** vor. Alle anderen Seiten holt er immer frisch: Ein halb
|
||||||
|
veralteter Planer wäre schlimmer als gar keiner — ein leeres Eingabefeld
|
||||||
|
dagegen veraltet nicht.
|
||||||
|
|
||||||
|
## Warum nicht die Erfassung auf der Startseite?
|
||||||
|
|
||||||
|
Die gibt es weiterhin, und sie ist am Rechner meist der kürzere Weg. Der
|
||||||
|
Unterschied ist die Verlässlichkeit: Die Schnellerfassung ist die einzige
|
||||||
|
Stelle, die ohne Netz nichts verliert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Weiter zur [Inbox](aufgaben.md) oder zurück zum [Handbuch](README.md).
|
||||||
@@ -489,6 +489,44 @@ async function main(): Promise<void> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- API-Tokens -----------------------------------------------------------
|
||||||
|
// Erfundene Hashes: Mit diesen Zeilen laesst sich nichts aufrufen, sie
|
||||||
|
// zeigen nur, wie die Liste aussieht.
|
||||||
|
await db.apiToken.deleteMany({})
|
||||||
|
await db.apiToken.create({
|
||||||
|
data: {
|
||||||
|
benutzerId: benutzer.id,
|
||||||
|
name: 'Wartungsplaner-Import',
|
||||||
|
praefix: 'k7m2xq4b',
|
||||||
|
hash: 'demo-1',
|
||||||
|
rechte: 'erfassen',
|
||||||
|
erstelltAm: new Date(Date.UTC(2026, 0, 14, 9, 30)),
|
||||||
|
letzteNutzung: new Date(Date.UTC(2026, 2, 17, 5, 12)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await db.apiToken.create({
|
||||||
|
data: {
|
||||||
|
benutzerId: benutzer.id,
|
||||||
|
name: 'Auswertung Homelab',
|
||||||
|
praefix: 'r3wp9dt4',
|
||||||
|
hash: 'demo-2',
|
||||||
|
rechte: 'lesen',
|
||||||
|
gueltigBis: new Date(Date.UTC(2026, 8, 30, 22, 0)),
|
||||||
|
erstelltAm: new Date(Date.UTC(2026, 1, 2, 18, 45)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await db.apiToken.create({
|
||||||
|
data: {
|
||||||
|
benutzerId: benutzer.id,
|
||||||
|
name: 'Altes Skript',
|
||||||
|
praefix: 'z8n4vt6c',
|
||||||
|
hash: 'demo-3',
|
||||||
|
rechte: 'lesen,schreiben',
|
||||||
|
erstelltAm: new Date(Date.UTC(2025, 10, 3, 11, 0)),
|
||||||
|
widerrufenAm: new Date(Date.UTC(2026, 1, 20, 8, 15)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
console.log(`Demo-Daten stehen bereit. Anmeldung: ${DEMO_EMAIL}`)
|
console.log(`Demo-Daten stehen bereit. Anmeldung: ${DEMO_EMAIL}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ const AUFNAHMEN: Aufnahme[] = [
|
|||||||
{ name: 'anmelden', pfad: '/anmelden' },
|
{ name: 'anmelden', pfad: '/anmelden' },
|
||||||
{ name: 'start', pfad: '/' },
|
{ name: 'start', pfad: '/' },
|
||||||
{ name: 'inbox', pfad: '/inbox' },
|
{ name: 'inbox', pfad: '/inbox' },
|
||||||
|
{ name: 'erfassen', pfad: '/erfassen' },
|
||||||
{ name: 'aufgaben', pfad: '/aufgaben' },
|
{ name: 'aufgaben', pfad: '/aufgaben' },
|
||||||
{ name: 'woche', pfad: '/woche' },
|
{ name: 'woche', pfad: '/woche' },
|
||||||
{ name: 'projekte', pfad: '/projekte' },
|
{ name: 'projekte', pfad: '/projekte' },
|
||||||
@@ -50,6 +51,7 @@ const AUFNAHMEN: Aufnahme[] = [
|
|||||||
pfad: '/einstellungen',
|
pfad: '/einstellungen',
|
||||||
ausschnitt: '[data-bild="benachrichtigungen"]',
|
ausschnitt: '[data-bild="benachrichtigungen"]',
|
||||||
},
|
},
|
||||||
|
{ name: 'api', pfad: '/verwaltung/api' },
|
||||||
{ name: 'protokoll', pfad: '/verwaltung/protokoll' },
|
{ name: 'protokoll', pfad: '/verwaltung/protokoll' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user