# 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. ## Stack - **Backend:** Python 3.11+, FastAPI, SQLAlchemy 2 (SQLite, Postgres-fähig) - **Frontend:** HTMX + Jinja2 + Tailwind (CDN) + Alpine.js — kein Build-Step - **Auth:** Session-Cookies (signiert), Passwort-Hashing mit Argon2id - **Plugins:** Entry-Points (`importlib.metadata`, Gruppe `spiele_redaktion.plugins`) plus lokales `plugins/`-Verzeichnis - **Dependencies/Tests:** [uv](https://docs.astral.sh/uv/) + pytest ## Setup ```bash uv sync # venv + Dependencies uv run uvicorn main:app --reload # Dev-Server auf http://127.0.0.1:8000 ``` Beim ersten Start wird automatisch ein Admin-Konto angelegt: | Benutzername | Passwort | Rolle | |--------------|----------|--------| | `admin` | `admin` | admin | > ⚠️ Für alles außer der lokalen Entwicklung vor dem Start > `SPIELE_INITIAL_ADMIN_PASSWORD` setzen und das Passwort nach dem ersten > Login ändern. ### Umgebungsvariablen | Variable | Default | Bedeutung | |----------|---------|-----------| | `SPIELE_DATABASE_URL` | `sqlite:///./data/spiele-redaktion.db` | SQLAlchemy-URL (Postgres: z. B. `postgresql+psycopg://…`) | | `SPIELE_SESSION_SECRET` | Dev-Wert | Secret für signierte Session-Cookies | | `SPIELE_SESSION_DAUER` | `43200` | Session-Dauer in Sekunden (12 h) | | `SPIELE_INITIAL_ADMIN_PASSWORD` | `admin` | Passwort des initialen Admins | | `SPIELE_PLUGINS_DIR` | `/plugins` | Pfad zum lokalen Plugin-Verzeichnis | ## Tests ```bash uv run pytest ``` Abgedeckt: Plugin-Loader lädt alle Stubs (inkl. Lifecycle-Hooks und Entry-Point-Pfad), Migrations-Laufzeit inkl. Idempotenz, Login/Logout, Rollen-Zugriff (admin/redakteur vs. rezensent), Benutzerverwaltung. ## Architektur ``` src/redaktionskern/ schlanker Kern — KEINE Fachlogik ├── app.py App-Fabrik: Discovery → Migrations → on_load → Routen ├── config.py Einstellungen (Umgebungsvariablen) ├── db.py Engine/Session (SQLite ↔ Postgres portabel) ├── migrationen.py Migrations-Laufzeit (Tabelle schema_migrations pro Plugin) ├── plugin_loader.py Entry-Points + plugins/-Verzeichnis, eindeutige Namen ├── 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//index.html ``` ### Der Plugin-Vertrag Ein Plugin ist ein Paketordner unter `plugins/` mit einem `__init__.py`, das ein Modulattribut `plugin` (Instanz einer `BasePlugin`-Unterklasse) bereitstellt. Alternativ: installiertes Paket mit Entry-Point in der Gruppe `spiele_redaktion.plugins`. ```python from fastapi import Depends, Request from redaktionskern.auth.deps import require_user from redaktionskern.contracts import BasePlugin, Migration, NavEntry class MeinPlugin(BasePlugin): name = "meinplugin" # eindeutig, = Ordnername title = "Mein Plugin" # deutscher Anzeigename description = "Was es tut." def migrations(self): # eigene Schema-Migrationen (optional) return [] def navigation(self): # Einträge in der Hauptnavigation (optional) return [NavEntry(label=self.title, url="/meinplugin")] def on_load(self, context): # Lifecycle-Hook beim Start super().on_load(context) # context.engine/.session_factory/.templates/.settings def on_unload(self): # Lifecycle-Hook beim Herunterfahren super().on_unload() plugin = MeinPlugin() ``` Der Kern stellt jedem Plugin über `context.templates` eine gemeinsame Jinja- Umgebung bereit (Plugin-Templates erben `base.html`), dazu Engine und 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. ## Deployment (später) Docker/Podman Compose ist vorgesehen (Henry-Lab, danach Kundenhardware). Für Postgres genügt `SPIELE_DATABASE_URL`; die Migrationen sind portabel geschrieben. ## Offene Punkte (nicht Teil von Phase 1) - CSRF-Schutz für Formulare (aktuell SameSite=Lax-Cookie als Basisschutz) - Fachliche Plugins (Neuheiten/BGG-Sync, Dedup, Planung, Archiv, Erinnerung, Benachrichtigung mit Adapter-Muster, Audit-Log, CSV/PDF-Export) - Hintergrund-Jobs (APScheduler) für BGG-Sync und Erinnerungen - WeasyPrint für PDF-Export (Systemabhängigkeiten im Container) - Tailwind via CDN nur für Dev; für Produktion lokal gehostete Assets - Docker-/Podman-Compose-Setup