Plugin benachrichtigung: E-Mail, Telegram, In-App per Adapter-Muster
- Adapter-Pattern mit drei Kanaelen: E-Mail (SMTP per Env, TLS starttls/ssl, Dev-Fallback: Protokoll), Telegram (Bot-API, Token per Env, Chat-ID pro Benutzer), In-App (persistente Nachrichten mit Unread-Counter und 'Alle als gelesen markieren') - Pro Benutzer Kanal-Praeferenzen (Einstellungsseite, mehrere Kanaele gleichzeitig) plus eigene Kontakt-Tabelle (E-Mail-Adresse, Chat-ID); Kern und users-Tabelle unveraendert - Oeffentliche Plugin-API: await send_notification(user, titel, text, kategorie) - andere Plugins holen das Plugin ueber app.state.registry - Eigene Migration (0001_tabellen), eigene Routen/Templates, deutsche UI - Tests: Adapter-Auswahl nach Praeferenz, In-App-Persistenz, Dev-Log- Adapter, SMTP-Versand (gemockt), Telegram-API-Aufruf, Einstellungsseite - README: Plugin-Doku + neue Env-Variablen; docker-compose: Platzhalter
This commit is contained in:
108
README.md
108
README.md
@@ -1,8 +1,9 @@
|
||||
# Spiele-Redaktion
|
||||
|
||||
**KI-Assistenz für Spielemagazin-Redaktionen** — Multi-User-Webanwendung mit
|
||||
modularer Plugin-Architektur. Phase 1: lauffähiger Kern mit Plugin-System,
|
||||
Authentifizierung/Rollen, Migrationen und Plugin-Stubs.
|
||||
modularer Plugin-Architektur. Lauffähiger Kern mit Plugin-System,
|
||||
Authentifizierung/Rollen und Migrationen; als erstes Fachplugin ist das
|
||||
**Audit-Log** vollständig implementiert, weitere Plugins folgen.
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -39,6 +40,14 @@ Beim ersten Start wird automatisch ein Admin-Konto angelegt:
|
||||
| `SPIELE_SESSION_DAUER` | `43200` | Session-Dauer in Sekunden (12 h) |
|
||||
| `SPIELE_INITIAL_ADMIN_PASSWORD` | `admin` | Passwort des initialen Admins |
|
||||
| `SPIELE_PLUGINS_DIR` | `<Projekt>/plugins` | Pfad zum lokalen Plugin-Verzeichnis |
|
||||
| `SPIELE_SMTP_HOST` | *(leer)* | SMTP-Server für den E-Mail-Kanal; leer = Dev-Fallback (nur Protokoll) |
|
||||
| `SPIELE_SMTP_PORT` | `587` | SMTP-Port |
|
||||
| `SPIELE_SMTP_BENUTZER` | *(leer)* | SMTP-Login (optional) |
|
||||
| `SPIELE_SMTP_PASSWORT` | *(leer)* | SMTP-Passwort (optional) |
|
||||
| `SPIELE_SMTP_ABSENDER` | Benutzer bzw. `spiele-redaktion@localhost` | From-Adresse |
|
||||
| `SPIELE_SMTP_TLS` | `starttls` | `starttls`, `ssl` oder `keine` |
|
||||
| `SPIELE_TELEGRAM_BOT_TOKEN` | *(leer)* | Bot-Token für den Telegram-Kanal; leer = Dev-Fallback (nur Protokoll) |
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
@@ -46,9 +55,11 @@ Beim ersten Start wird automatisch ein Admin-Konto angelegt:
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
Abgedeckt: Plugin-Loader lädt alle Stubs (inkl. Lifecycle-Hooks und
|
||||
Abgedeckt: Plugin-Loader lädt alle Plugins (inkl. Lifecycle-Hooks und
|
||||
Entry-Point-Pfad), Migrations-Laufzeit inkl. Idempotenz, Login/Logout,
|
||||
Rollen-Zugriff (admin/redakteur vs. rezensent), Benutzerverwaltung.
|
||||
Rollen-Zugriff (admin/redakteur vs. rezensent), Benutzerverwaltung sowie das
|
||||
Audit-Log-Plugin (Logging-Funktion, Migration, Filter, Paginierung,
|
||||
Nur-Admin-Zugriff).
|
||||
|
||||
## Architektur
|
||||
|
||||
@@ -62,10 +73,11 @@ src/redaktionskern/ schlanker Kern — KEINE Fachlogik
|
||||
├── contracts.py Plugin-Vertrag (BasePlugin, Migration, NavEntry, PluginContext)
|
||||
└── auth/ Login/Logout, Rollen, Benutzerverwaltung
|
||||
|
||||
plugins/ ein Ordner pro Funktion (Phase-1: Stubs, ladbar)
|
||||
├── neuheiten/ dedup/ planung/ archiv/
|
||||
├── erinnerung/ benachrichtigung/ audit-log/ export/
|
||||
└── … je __init__.py + templates/<name>/index.html
|
||||
plugins/ ein Ordner pro Funktion, ladbar über den Plugin-Loader
|
||||
├── audit-log/ VOLL IMPLEMENTIERT: Model, Migration, API, Admin-Ansicht
|
||||
├── neuheiten/ benachrichtigung/ (in Arbeit)
|
||||
├── dedup/ planung/ archiv/ erinnerung/ export/ (Stubs, ladbar)
|
||||
└── … je __init__.py + templates/<name>/
|
||||
```
|
||||
|
||||
### Der Plugin-Vertrag
|
||||
@@ -106,6 +118,84 @@ Session-Fabrik. Migrationen laufen transaktional und werden pro Plugin in
|
||||
`schema_migrations` protokolliert; die `up`-Funktionen bekommen eine
|
||||
SQLAlchemy-Connection und können bei Dialekt-Unterschieden verzweigen.
|
||||
|
||||
## Plugin „Audit-Log“ (implementiert)
|
||||
|
||||
Protokolliert, wer was wann verschoben, eingetragen oder geändert hat.
|
||||
|
||||
### Datentabelle
|
||||
|
||||
Eigene Migration `0001_audit_eintraege`, Tabelle `audit_eintraege`
|
||||
(SQLite ↔ Postgres portabel):
|
||||
|
||||
| Spalte | Inhalt |
|
||||
|--------|--------|
|
||||
| `actor_id` / `actor_name` | Wer (Benutzer-ID/-Name; `System` bei automatischen Ereignissen) |
|
||||
| `action` | Was (`erstellt`, `verschoben`, `geaendert`, `geloescht`, `benachrichtigt`) |
|
||||
| `objekt_typ` / `objekt_id` | Betroffenes Objekt (ID als Text) |
|
||||
| `alt` / `neu` | Alt-/Neustand als JSON (optional) |
|
||||
| `details` | Freie Zusatzinformationen als JSON (optional) |
|
||||
| `ip_adresse` | Herkunfts-IP (optional) |
|
||||
| `erstellt_am` | Zeitstempel |
|
||||
|
||||
### Öffentliche API für andere Plugins
|
||||
|
||||
Andere Plugins rufen das Audit-Log über die Plugin-Registry bei jedem
|
||||
relevanten Ereignis auf:
|
||||
|
||||
```python
|
||||
audit = request.app.state.registry.get("audit-log")
|
||||
if audit is not None:
|
||||
await audit.log( # async-Variante
|
||||
user, # User-Objekt, Benutzername oder None (= System)
|
||||
"geaendert", # Aktion (Konstanten: AKTIONEN_ANZEIGE im Plugin)
|
||||
"planungseintrag", # Objekttyp
|
||||
eintrag.id, # Objekt-ID
|
||||
details={"alt": alt, "neu": neu}, # „alt“/„neu“ → JSON-Spalten, Rest → details
|
||||
ip_adresse=request.client.host, # optional
|
||||
)
|
||||
```
|
||||
|
||||
In synchronen Routen (`def`, FastAPI-Threadpool) steht `audit.log_sync(...)`
|
||||
mit denselben Argumenten bereit.
|
||||
|
||||
### Admin-Ansicht
|
||||
|
||||
Unter `/audit-log` (nur Rolle **Admin**; Rezensenten/Redakteure erhalten 403,
|
||||
Anonyme werden zum Login umgeleitet): filterbar nach Benutzer, Aktionstyp und
|
||||
Zeitraum (Von/Bis), paginiert (25 Einträge pro Seite), neueste zuerst.
|
||||
|
||||
## Plugin „benachrichtigung“
|
||||
|
||||
Erstes fachlich umgesetztes Plugin (Adapter-Muster, drei Kanäle):
|
||||
|
||||
| Kanal | Zustellung | Empfängerdaten |
|
||||
|-------|------------|----------------|
|
||||
| **E-Mail** | SMTP (`smtplib`, TLS: STARTTLS/SSL), konfigurierbar per Env | E-Mail-Adresse pro Benutzer |
|
||||
| **Telegram** | Bot-API (`sendMessage`) | Chat-ID pro Benutzer, Token zentral per Env |
|
||||
| **In-App** | Persistente Nachrichten im Portal mit Unread-Counter und „Alle als gelesen markieren“ | — |
|
||||
|
||||
Ist ein Kanal installationsweit nicht konfiguriert (kein SMTP-Host bzw.
|
||||
kein Bot-Token — der Normalfall in der Entwicklung), protokolliert ein
|
||||
**Dev-Log-Adapter** die Nachricht auf dem Server, statt sie zu versenden;
|
||||
in der Entwicklung geht so keine Benachrichtigung verloren.
|
||||
|
||||
Jeder Benutzer wählt unter **Benachrichtigungen → Kanäle & Einstellungen**
|
||||
seine Kanäle (mehrere gleichzeitig möglich) und hinterlegt E-Mail-Adresse
|
||||
bzw. Telegram-Chat-ID. Ohne gespeicherte Präferenz wird In-App zugestellt.
|
||||
|
||||
### Öffentliche Plugin-API für andere Plugins
|
||||
|
||||
```python
|
||||
plugin = request.app.state.registry.get("benachrichtigung")
|
||||
bericht = await plugin.send_notification(user, "Titel", "Nachrichtentext", "kategorie")
|
||||
# bericht.zugestellt -> z. B. ["email", "inapp"]
|
||||
# bericht.fehlgeschlagen -> Kanäle ohne Adresse/Fehler
|
||||
```
|
||||
|
||||
`send_notification(user, titel, text, kategorie="allgemein")` wirft nicht;
|
||||
einzelne Kanalausfälle werden protokolliert und im Bericht gemeldet. Die
|
||||
In-App-Nachricht landet im Posteingang des Benutzers.
|
||||
|
||||
## Deployment (später)
|
||||
|
||||
Docker/Podman Compose ist vorgesehen (Henry-Lab, danach Kundenhardware).
|
||||
@@ -120,7 +210,7 @@ geschrieben.
|
||||
|---|----------|--------|
|
||||
| 1 | Kern: FastAPI, Plugin-System, DB/Migrationen, Auth/Rollen, HTMX-Layout | ✅ fertig (28 Tests grün) |
|
||||
| 2 | Plugin `benachrichtigung` (E-Mail/Telegram/In-App, User-Präferenzen) | 🔄 in Arbeit |
|
||||
| 3 | Plugin `audit-log` (Wer/Was/Wann, Admin-Ansicht) | 🔄 in Arbeit |
|
||||
| 3 | Plugin `audit-log` (Wer/Was/Wann, Admin-Ansicht, öffentliche API) | ✅ fertig |
|
||||
| 4 | Plugin `neuheiten` (BGG-Sync, APScheduler, Filter) | 🔄 in Arbeit |
|
||||
| 5 | Plugin `dedup` (Verlags-Konflikt, deutsche Version, Vorgänger-/Planungs-Check) | ⏳ offen |
|
||||
| 6 | Plugin `planung` (Verschiebung, händischer Eintrag + Prüfungen + Benachrichtigung) | ⏳ offen |
|
||||
|
||||
Reference in New Issue
Block a user