2490 lines
67 KiB
Markdown
2490 lines
67 KiB
Markdown
# PROJECT_DESIGN.md — Ehrenamt-Plattform
|
||
|
||
> **Status:** Verbindliche Architektur- und Produktgrundlage für v1
|
||
> **Stand:** 13.08.2026
|
||
> **Arbeitsname:** Ehrenamt-Plattform
|
||
> **Ausgangspunkt:** bestehende CheckPoint-Ehrenamt-App, künftig als modulare Plattform für mehrere Ehrenamtsteams der Aidshilfe Köln e.V.
|
||
|
||
---
|
||
|
||
## 0. Zweck dieses Dokuments
|
||
|
||
Dieses Dokument ist die **zentrale Quelle der Wahrheit** für Produktumfang, Architektur, Rollen, Berechtigungen, Datenmodell, Module, UI-Grundsätze, Sicherheit, Deployment und Weiterentwicklung der Ehrenamt-Plattform.
|
||
|
||
Es ersetzt die bisher verteilten bzw. teilweise widersprüchlichen Architekturentscheidungen aus älteren Konzept-, `CLAUDE.md`- und `TASKS.md`-Ständen.
|
||
|
||
### Dokument-Hierarchie
|
||
|
||
1. **`PROJECT_DESIGN.md`** — verbindliche Produkt- und Architekturentscheidung.
|
||
2. **Code + Tests** — Implementierung dieser Entscheidungen.
|
||
3. **`TASKS.md`** — daraus abgeleiteter Arbeits-/Migrationsplan; darf keine eigene Architektur erfinden.
|
||
4. **`CLAUDE.md` / andere Agentenhinweise** — wenn weiterhin verwendet, nur als kompakte Arbeitsanweisung mit Verweis auf `PROJECT_DESIGN.md`, nicht als zweite Architekturquelle.
|
||
5. Alte Konzeptdateien werden als historische Referenz archiviert und nicht parallel gepflegt.
|
||
|
||
Bei einem Widerspruch zwischen Implementierung und diesem Dokument wird **nicht geraten**. Die Abweichung wird bewusst entschieden und anschließend entweder der Code oder dieses Dokument angepasst.
|
||
|
||
---
|
||
|
||
# 1. Produktziel
|
||
|
||
Die Anwendung ist eine **interne, mobile-first Web-App für Ehrenamtsteams**. Sie soll alltägliche Teamorganisation einfacher, klarer und datensparsamer machen als eine Mischung aus WhatsApp-/Signal-Gruppen, Tabellen, Umfragen und verstreuten Dateien.
|
||
|
||
Die erste Organisation ist:
|
||
|
||
- **Aidshilfe Köln e.V.**
|
||
|
||
Die Plattform soll von Anfang an mehrere Teams dieser Organisation unterstützen. Perspektivisch sind ungefähr mehrere unterschiedliche Ehrenamtsbereiche denkbar, die nicht alle dieselben Funktionen benötigen.
|
||
|
||
### Leitprinzipien
|
||
|
||
1. **Lesbarkeit und Orientierung vor Funktionsfülle.**
|
||
2. **Mobile Nutzung ist der Normalfall**, nicht die abgespeckte Desktop-Version.
|
||
3. **Technisch wenig affine Menschen müssen die App ohne Schulungsbedarf bedienen können.**
|
||
4. **Ein Team sieht nur sein Team.** Organisationsrechte sind kein Generalschlüssel für Team-Inhalte.
|
||
5. **Module sind optional pro Team.** Nicht jedes Team bekommt Funktionen, die es nicht braucht.
|
||
6. **Automatisierung nur bei eindeutigem Ergebnis.** Die App unterstützt Entscheidungen; sie entscheidet nicht unnötig für Menschen.
|
||
7. **Dashboard zeigt Zustände, Benachrichtigungen melden Ereignisse.**
|
||
8. **Erweiterbarkeit entsteht durch klare Grenzen**, nicht durch ein maximal generisches System.
|
||
9. **Deployment, Backup und Wiederherstellung werden von Anfang an mitgebaut.**
|
||
|
||
---
|
||
|
||
# 2. Scope von v1
|
||
|
||
## 2.1 Core
|
||
|
||
Der Core enthält die Plattformfunktionen, ohne die die Anwendung als Mehrteam-System nicht funktionieren kann:
|
||
|
||
- Passwordless Auth per E-Mail-Code / Direktlink
|
||
- Accounts
|
||
- Device Sessions
|
||
- Organisation
|
||
- Teams
|
||
- Team-Mitgliedschaften
|
||
- Team-Einladungen
|
||
- Organisationsadministration
|
||
- Rollen und Berechtigungen
|
||
- Brand Profiles / Corporate Design
|
||
- zentraler Datei-/Storage-Service
|
||
- In-App-Benachrichtigungen
|
||
- Team-Dashboard und Verwaltungsdashboard
|
||
- Team-Mitgliederverzeichnis / freiwillige Teamkontakte
|
||
- Profile und Avatare
|
||
- Audit Log
|
||
- Health-/Deployment-Grundlagen
|
||
|
||
## 2.2 Fachmodule
|
||
|
||
v1 enthält genau drei fachliche Module:
|
||
|
||
1. **Scheduling / Einsatzplanung**
|
||
2. **Documents / Dokumente**
|
||
3. **Chat / Teamkommunikation**
|
||
|
||
Nicht jedes Team muss alle drei Module verwenden.
|
||
|
||
---
|
||
|
||
# 3. Bewusst nicht Teil von v1
|
||
|
||
Folgende Funktionen werden nicht in v1 gezogen, nur weil sie technisch möglich wären:
|
||
|
||
- öffentliche Seiten
|
||
- Klient:innen- oder Beratungsdaten
|
||
- Bezahlfunktionen
|
||
- Kalender-Synchronisierung
|
||
- automatische Dienstverteilung
|
||
- 1:1-Direktnachrichten
|
||
- Bilder oder Dateien im Chat
|
||
- GIFs / Sticker / Meme-Previews
|
||
- Emoji-Reactions
|
||
- `@mentions`
|
||
- operative Push-Benachrichtigungen
|
||
- operative E-Mail-Benachrichtigungen
|
||
- Dokumentordner
|
||
- Dokumentversionierung
|
||
- vollwertige Dokumentbearbeitung
|
||
- Fotogalerie
|
||
- Inventar-/Materialverwaltung
|
||
- Qualifikations-/Schulungsverwaltung
|
||
- feingranulare Spezialrollen
|
||
- Passkeys / 2FA als regulärer Loginweg
|
||
- PostgreSQL in v1
|
||
- dynamisches Installieren/Entfernen von Python-Modulen zur Laufzeit
|
||
|
||
Diese Punkte dürfen später ergänzt werden, wenn ein realer Bedarf besteht und der Modulvertrag eingehalten wird.
|
||
|
||
---
|
||
|
||
# 4. Technischer Rahmen
|
||
|
||
| Bereich | Entscheidung |
|
||
|---|---|
|
||
| Backend | Python + Flask |
|
||
| App-Struktur | Flask App Factory + Blueprints |
|
||
| Rendering | Jinja2, server-rendered Multi-Page-App |
|
||
| ORM | SQLAlchemy / Flask-SQLAlchemy |
|
||
| Datenbank | SQLite für v1 |
|
||
| Schemaänderungen | Flask-Migrate / Alembic ab Beginn der neuen Architektur |
|
||
| CSS | eigenes Designsystem, kein CSS-Framework |
|
||
| JavaScript | Vanilla JS, nur wo es echten Nutzen bringt |
|
||
| Chat-Aktualisierung | kleines Fetch-Polling, keine WebSockets in v1 |
|
||
| Produktionsserver | Gunicorn |
|
||
| Container | Docker / Compose, non-root |
|
||
| Reverse Proxy | Caddy |
|
||
| Hosting | eigener VPS in der EU |
|
||
| UI-Sprache | Deutsch |
|
||
| Zielplattform | Browser-App, mobil zum Homescreen hinzufügbar |
|
||
|
||
### Architekturregel
|
||
|
||
Server-rendered HTML bleibt die Basis. JavaScript verbessert Komfort, darf aber nicht unnötig die gesamte App in eine SPA verwandeln.
|
||
|
||
---
|
||
|
||
# 5. Organisations- und Teammodell
|
||
|
||
Die zentrale Hierarchie lautet:
|
||
|
||
```text
|
||
Organization
|
||
└── Team
|
||
└── TeamMembership
|
||
└── Account
|
||
```
|
||
|
||
## 5.1 Organization
|
||
|
||
Die erste Organisation ist Aidshilfe Köln e.V.
|
||
|
||
`Organization` existiert trotzdem als eigenes Modell, damit die Anwendung strukturell nicht auf ein einzelnes Team oder eine einzelne Abteilung fest verdrahtet wird.
|
||
|
||
Konzeptionelle Felder:
|
||
|
||
```text
|
||
Organization
|
||
├── id
|
||
├── name
|
||
├── status
|
||
└── created_at
|
||
```
|
||
|
||
## 5.2 Team
|
||
|
||
Ein Team ist ein klar abgegrenzter Arbeitsbereich innerhalb der Organisation.
|
||
|
||
```text
|
||
Team
|
||
├── id
|
||
├── organization_id
|
||
├── name
|
||
├── slug
|
||
├── brand_profile_id
|
||
├── status
|
||
└── created_at
|
||
```
|
||
|
||
Status mindestens:
|
||
|
||
- `active`
|
||
- `archived`
|
||
|
||
Teams werden **archiviert statt gelöscht**, solange keine zwingende Datenschutz-/Löschanforderung etwas anderes verlangt.
|
||
|
||
Ein aktives Team muss mindestens eine Team-Verwaltung besitzen.
|
||
|
||
---
|
||
|
||
# 6. Accounts, Mitgliedschaften und Rollen
|
||
|
||
## 6.1 Account ist eine Person, kein Teammitglied
|
||
|
||
Ein Account gehört **nicht** direkt einem Team.
|
||
|
||
```text
|
||
Account
|
||
├── id
|
||
├── email
|
||
├── display_name
|
||
├── status
|
||
├── created_at
|
||
└── last_login_at
|
||
```
|
||
|
||
Die E-Mail-Adresse ist:
|
||
|
||
- eindeutig und normalisiert,
|
||
- Login-Identität,
|
||
- nicht automatisch öffentliche Kontaktadresse,
|
||
- nicht Teil des normalen Teamverzeichnisses.
|
||
|
||
Account-Status mindestens:
|
||
|
||
- `active`
|
||
- `disabled`
|
||
|
||
Accounts werden nicht leichtfertig hart gelöscht, damit historische Zuteilungen und andere Referenzen erhalten bleiben können.
|
||
|
||
## 6.2 TeamMembership
|
||
|
||
Die Beziehung einer Person zu einem Team wird separat modelliert:
|
||
|
||
```text
|
||
TeamMembership
|
||
├── id
|
||
├── team_id
|
||
├── account_id
|
||
├── role
|
||
├── status
|
||
├── joined_at
|
||
└── ended_at
|
||
```
|
||
|
||
Rollen in v1:
|
||
|
||
- `member`
|
||
- `manager`
|
||
|
||
Status mindestens:
|
||
|
||
- `active`
|
||
- `inactive`
|
||
|
||
Pro Account und Team darf nur eine aktive Mitgliedschaft existieren.
|
||
|
||
### Wichtige Semantik
|
||
|
||
`manager` ist **keine getrennte Benutzerart**. Eine Team-Verwaltung ist immer auch normales Mitglied des Teams und kann die Mitgliederoberfläche genauso benutzen wie andere Teammitglieder.
|
||
|
||
---
|
||
|
||
# 7. Organisationsadministration
|
||
|
||
Organisationale Rechte werden separat von Team-Mitgliedschaften modelliert:
|
||
|
||
```text
|
||
OrganizationMembership
|
||
├── id
|
||
├── organization_id
|
||
├── account_id
|
||
├── role
|
||
└── status
|
||
```
|
||
|
||
Für v1 reicht:
|
||
|
||
- `organization_admin`
|
||
|
||
## 7.1 Organisationsadministration darf
|
||
|
||
- Teams anlegen und archivieren,
|
||
- Brand Profiles anlegen/zuordnen,
|
||
- Module für Teams aktivieren/deaktivieren,
|
||
- Team-Verwalter:innen ernennen und entfernen,
|
||
- organisationsweite technische/strukturelle Einstellungen verwalten,
|
||
- weitere Organisationsadmins über einen gesicherten Prozess ernennen,
|
||
- sicherheitsrelevante Account-Sperren durchführen.
|
||
|
||
## 7.2 Organisationsadministration darf ausdrücklich nicht automatisch
|
||
|
||
- Teamchats lesen,
|
||
- Dokumente eines Teams sehen,
|
||
- Einsatzplanung oder Verfügbarkeiten eines Teams sehen,
|
||
- normale Teamkontakte sehen,
|
||
- die vollständigen Mitgliederlisten aller Teams durchsuchen,
|
||
- allein wegen der Org-Rolle Team-Fachmodule öffnen.
|
||
|
||
Die Organisationsverwaltung sieht **Teamstruktur und Team-Verwaltungen**, nicht automatisch den internen Teambetrieb.
|
||
|
||
Wenn eine organisationsadministrierende Person interne Teamdaten sehen soll, braucht sie zusätzlich eine normale `TeamMembership` in diesem Team.
|
||
|
||
### Sicherheitsinvariante
|
||
|
||
> `organization_admin` darf niemals eine fehlende `TeamMembership` für Team-Inhalte umgehen.
|
||
|
||
Diese Regel erhält eigene automatisierte Tests.
|
||
|
||
---
|
||
|
||
# 8. Ernennung von Team-Verwaltungen
|
||
|
||
Nur die **Organisationsverwaltung** darf Team-Verwalter:innen ernennen oder entfernen.
|
||
|
||
Team-Verwalter:innen dürfen:
|
||
|
||
- normale Mitglieder einladen,
|
||
- normale Teammitgliedschaften verwalten,
|
||
- Inhalte und Modulkonfiguration ihres Teams verwalten,
|
||
|
||
aber **keine weiteren Team-Verwaltungsrechte vergeben**.
|
||
|
||
Zusätzliche Regeln:
|
||
|
||
1. Eine Team-Verwaltung kann sich nicht selbst zur normalen Mitgliedschaft herunterstufen.
|
||
2. Ein aktives Team darf nicht ohne Team-Verwaltung zurückbleiben.
|
||
3. Beim Entfernen der letzten Team-Verwaltung muss zuerst eine neue bestimmt werden.
|
||
4. Eine neue Team-Verwaltung kann von der Organisationsverwaltung aus einer bestehenden Teammitgliedschaft befördert oder direkt als Team-Verwaltung eingeladen werden, ohne dass der Org-Admin dazu die komplette Teammitgliederliste sehen muss.
|
||
|
||
---
|
||
|
||
# 9. Technische Serveradministration ist keine App-Rolle
|
||
|
||
Zugriff auf VPS, Docker, Caddy, Backups oder Git verleiht **keine fachliche Rolle innerhalb der App**.
|
||
|
||
Technische Administration und Anwendungsberechtigungen bleiben konzeptionell getrennt.
|
||
|
||
---
|
||
|
||
# 10. Passwordless Auth
|
||
|
||
## 10.1 Grundprinzip
|
||
|
||
Es gibt in v1 **keine Benutzerpasswörter**.
|
||
|
||
Login erfolgt per E-Mail:
|
||
|
||
1. Person gibt ihre E-Mail-Adresse ein oder folgt einer Einladung.
|
||
2. App erzeugt einen zeitlich begrenzten Auth-Challenge.
|
||
3. E-Mail enthält:
|
||
- einen kurzen Einmal-Code,
|
||
- zusätzlich einen Direktlink.
|
||
4. Nach erfolgreicher Verifikation wird eine langlebige, widerrufbare Device Session angelegt.
|
||
5. Beim normalen erneuten Öffnen der Homescreen-/Browser-App ist die Person weiterhin angemeldet.
|
||
|
||
Es gibt damit keinen Passwort-Reset-Flow.
|
||
|
||
## 10.2 AuthChallenge
|
||
|
||
```text
|
||
AuthChallenge
|
||
├── id
|
||
├── email
|
||
├── code_hash
|
||
├── link_token_hash
|
||
├── created_at
|
||
├── expires_at
|
||
├── attempts
|
||
└── consumed_at
|
||
```
|
||
|
||
Code und Linktoken werden nicht im Klartext gespeichert.
|
||
|
||
Ablaufzeit, maximale Fehlversuche und Rate Limits sind Konfiguration, nicht Geschäftslogik.
|
||
|
||
## 10.3 DeviceSession
|
||
|
||
```text
|
||
DeviceSession
|
||
├── id
|
||
├── account_id
|
||
├── token_hash
|
||
├── created_at
|
||
├── last_seen_at
|
||
├── expires_at
|
||
├── revoked_at
|
||
└── device_label
|
||
```
|
||
|
||
Session-Cookies müssen mindestens:
|
||
|
||
- `Secure`,
|
||
- `HttpOnly`,
|
||
- sinnvolle `SameSite`-Einstellung
|
||
|
||
verwenden.
|
||
|
||
Gerätesitzungen sind serverseitig widerrufbar. Eine spätere Profilseite „Angemeldete Geräte“ ist mit dem Modell bereits möglich.
|
||
|
||
## 10.4 Frische Re-Authentifizierung
|
||
|
||
Besonders sensible Organisationsaktionen, z. B. Änderung von Org-Admin- oder Team-Manager-Rechten, verlangen eine **frische E-Mail-Re-Authentifizierung**, auch wenn eine langlebige Device Session besteht.
|
||
|
||
Passkeys/2FA können später ergänzt werden, sind nicht Teil von v1.
|
||
|
||
---
|
||
|
||
# 11. Einladungen
|
||
|
||
## 11.1 TeamInvitation
|
||
|
||
```text
|
||
TeamInvitation
|
||
├── id
|
||
├── team_id
|
||
├── email
|
||
├── role
|
||
├── invited_by
|
||
├── status
|
||
├── created_at
|
||
└── expires_at
|
||
```
|
||
|
||
Status mindestens:
|
||
|
||
- `pending`
|
||
- `accepted`
|
||
- `revoked`
|
||
- `expired`
|
||
|
||
Normale Team-Verwaltungen dürfen nur `role = member` einladen.
|
||
|
||
### Ablauf
|
||
|
||
```text
|
||
Team-Verwaltung trägt E-Mail ein
|
||
→ Einladung wird erstellt
|
||
→ Empfänger:in verifiziert die E-Mail
|
||
→ vorhandenen Account finden oder neuen Account anlegen
|
||
→ TeamMembership erstellen
|
||
→ bei neuem Account: Onboarding für Anzeigename
|
||
```
|
||
|
||
## 11.2 OrganizationAdminInvitation
|
||
|
||
Organisationsadministration erhält einen getrennten, sicherheitssensiblen Einladungsflow:
|
||
|
||
```text
|
||
OrganizationAdminInvitation
|
||
├── id
|
||
├── organization_id
|
||
├── email
|
||
├── invited_by
|
||
├── status
|
||
├── created_at
|
||
└── expires_at
|
||
```
|
||
|
||
Der erste Organisationsadmin wird über einen CLI-Bootstrap angelegt/eingeladen. Es gibt keine hardcodierte Admin-E-Mail.
|
||
|
||
Nach vorhandenem Org-Admin soll der Bootstrap standardmäßig nicht einfach weitere globale Administratoren erzeugen.
|
||
|
||
Operational wird empfohlen, mindestens zwei Org-Admins zu haben, damit die Organisation nicht von einer einzelnen Person abhängig ist.
|
||
|
||
---
|
||
|
||
# 12. Profil und Teamkontakte
|
||
|
||
## 12.1 Anzeigename
|
||
|
||
Die Login-E-Mail ist nicht der öffentliche Name.
|
||
|
||
Beim ersten erfolgreichen Login wird, falls noch kein Anzeigename existiert, gefragt:
|
||
|
||
> Wie sollen wir dich nennen?
|
||
|
||
`display_name`:
|
||
|
||
- wird in Chat, Einsatzplanung und Teamverzeichnis verwendet,
|
||
- darf von der Person im Profil geändert werden,
|
||
- muss nicht organisationsweit eindeutig sein.
|
||
|
||
Team-spezifische Anzeigenamen werden in v1 nicht gebaut.
|
||
|
||
## 12.2 Profilbild und Avatare
|
||
|
||
Ein Account kann optional ein eigenes Profilbild verwenden.
|
||
|
||
Alternativ stehen drei rein visuelle Standardvarianten zur Auswahl:
|
||
|
||
- Avatar A
|
||
- Avatar B
|
||
- Avatar C
|
||
|
||
Die Avatare werden **nicht geschlechtlich beschriftet**.
|
||
|
||
Wenn nichts gewählt wurde, kann Avatar A als neutraler technischer Default verwendet werden.
|
||
|
||
Das Profilbild gilt accountweit und wird z. B. angezeigt in:
|
||
|
||
- Teamverzeichnis,
|
||
- Chat,
|
||
- Einsatzansichten.
|
||
|
||
Uploadbilder werden serverseitig auf geeignete Größen/Thumbnails verarbeitet. Erlaubte Profilbildformate orientieren sich an den Bildformaten des Dokumenten-Uploads, einschließlich moderner Smartphone-Formate.
|
||
|
||
## 12.3 Freiwilliges TeamMemberProfile
|
||
|
||
Kontaktdaten sind **teambezogen und freiwillig**:
|
||
|
||
```text
|
||
TeamMemberProfile
|
||
├── team_membership_id
|
||
├── phone_number optional
|
||
├── contact_by_call optional
|
||
├── contact_by_sms optional
|
||
├── contact_by_signal optional
|
||
├── contact_note optional
|
||
└── share_contact_details
|
||
```
|
||
|
||
Beispiel:
|
||
|
||
```text
|
||
Andrea
|
||
[Profilbild]
|
||
Team-Verwaltung
|
||
|
||
📞 0176 ...
|
||
Erreichbar per Anruf / Signal
|
||
„Bei kurzfristigen Dienstausfällen gerne anrufen.“
|
||
```
|
||
|
||
### Datenschutzregeln
|
||
|
||
- `share_contact_details` ist standardmäßig aus.
|
||
- Freigegebene Kontaktdaten sehen nur aktive Mitglieder desselben Teams.
|
||
- Eine Person kann in einem Team teilen und in einem anderen nicht.
|
||
- Nicht freigegebene freiwillige Kontaktdaten werden auch Team-Verwaltungen nicht „heimlich“ angezeigt.
|
||
- Beim Ende der Teammitgliedschaft endet der Zugriff für andere Teammitglieder.
|
||
- Es gibt in v1 keinen organisationsweiten Kontaktdatenexport.
|
||
|
||
### Login-E-Mail
|
||
|
||
Die Login-E-Mail wird **nicht** normalen Teammitgliedern angezeigt.
|
||
|
||
Team-Verwaltungen dürfen sie für die Mitgliederadministration ihres Teams sehen.
|
||
|
||
Organisationsadmins sehen nicht automatisch die Login-E-Mails aller normalen Teammitglieder; ihr struktureller Zugriff bleibt auf ihre Aufgaben begrenzt.
|
||
|
||
Eine spätere freiwillige `contact_email` wäre ein separates Feld und niemals automatisch `Account.email`.
|
||
|
||
---
|
||
|
||
# 13. Teammitgliederverzeichnis
|
||
|
||
Normale Teammitglieder sehen im eigenen Team:
|
||
|
||
- Anzeigename,
|
||
- Profilbild / Avatar,
|
||
- Kennzeichnung „Team-Verwaltung“, falls zutreffend,
|
||
- freiwillig freigegebene Teamkontaktdaten.
|
||
|
||
Sie sehen nicht:
|
||
|
||
- Login-E-Mail,
|
||
- Device Sessions,
|
||
- Auth-Daten,
|
||
- interne Account-/Einladungsmetadaten.
|
||
|
||
Team-Verwaltungen sehen zusätzlich für die Mitgliederadministration:
|
||
|
||
- Login-E-Mail,
|
||
- Mitgliedsstatus,
|
||
- Einladungsstatus,
|
||
- Beitrittsdatum.
|
||
|
||
---
|
||
|
||
# 14. Brand Profiles / Corporate Design
|
||
|
||
Branding ist Core-Infrastruktur, kein Fachmodul.
|
||
|
||
```text
|
||
BrandProfile
|
||
├── id
|
||
├── organization_id
|
||
├── name
|
||
├── logo
|
||
├── primary_color
|
||
├── primary_strong
|
||
├── accent_color
|
||
├── accent_strong
|
||
├── heading_font
|
||
└── body_font
|
||
```
|
||
|
||
Die genaue Tokenmenge darf sich bei der Implementierung verfeinern. Entscheidend ist:
|
||
|
||
- Brand Profiles speichern **Design-Eingaben**, kein beliebiges CSS.
|
||
- Ein Brand Profile gehört der Organisation.
|
||
- Mehrere Teams dürfen dasselbe Brand Profile verwenden.
|
||
- In der Startkonfiguration sind mehrere unterschiedliche Profile vorgesehen, z. B. Aidshilfe-Standard, Café Bach, CheckPoint/Vor-Ort.
|
||
- Layout und Komponenten bleiben gleich; Farben, Schriften und Logo verändern die Identität.
|
||
- Light-/Dark-Darstellung wird über semantische CSS-Tokens abgeleitet.
|
||
|
||
Der bestehende Ansatz mit getrennten Brand- und semantischen Tokens wird beibehalten, aber auf das neue BrandProfile-Modell ausgerichtet.
|
||
|
||
## 14.1 Initiales Brand Profile „Vor-Ort-Arbeit“
|
||
|
||
Für die **Vor-Ort-Arbeit** wird die visuelle Basis aus dem vorhandenen CheckPoint-CSS übernommen. Es werden nur die grundlegenden Markenfarben und Schriften als Ausgangsmaterial verwendet; die Ehrenamt-Plattform übernimmt nicht das gesamte Layout der Quell-App.
|
||
|
||
### Typografie
|
||
|
||
| Einsatz | Schrift | Nutzung |
|
||
|---|---|---|
|
||
| Display / große Überschriften | **Big Shoulders Display** | Seitentitel, große Kennzahlen, markante Abschnittsüberschriften |
|
||
| Body / UI | **Noto Sans** | Fließtext, Navigation, Formulare, Buttons, Tabellen, Hilfetexte |
|
||
|
||
Grundregel: **Big Shoulders Display sparsam und gezielt**, Noto Sans trägt den Großteil der Oberfläche. Das hält die App charakteristisch, ohne sie plakativ oder unruhig wirken zu lassen.
|
||
|
||
### Basisfarben
|
||
|
||
| Token / Rolle | Basiswert | Zweck |
|
||
|---|---:|---|
|
||
| Ink / Navy | `#07182E` | Haupttext, sehr dunkle Flächen, Navigation |
|
||
| Deep | `#061126` | tiefster Hintergrund / Sidebar-Basis |
|
||
| Teal strong | `#005F61` | dunkle Markenfläche, Sidebar / Header |
|
||
| Teal | `#00858A` | primäre Markenfarbe und aktive Navigation |
|
||
| Aqua | `#52E0CF` | freundlicher Akzent, Fokus, leichte Highlights |
|
||
| Blue | `#2596BE` | sekundärer Informationsakzent |
|
||
| Coral | `#F04A67` | wichtig / Aktion; Rot nur gezielt einsetzen |
|
||
| Yellow | `#F6E74A` | sparsame Hervorhebung / Hinweis |
|
||
| Mist | `#F4FAFC` | ruhiger Seitenhintergrund |
|
||
| Paper | `#FFFFFF` | Karten und Arbeitsflächen |
|
||
|
||
Für helle Arbeitsflächen dürfen kontrastoptimierte semantische Ableitungen verwendet werden, z. B. dunkleres Teal oder Blue für Textlinks und Buttons. Die oben genannten Werte bleiben die **Brand-Basis**, nicht zwangsläufig der direkte CSS-Wert jedes Elements.
|
||
|
||
### Logo-Nutzung
|
||
|
||
Das Vor-Ort-Logo darf und soll **sichtbar wiederkehren**, insbesondere:
|
||
|
||
- im mobilen Header bzw. App-Kopf,
|
||
- oben in der Desktop-Sidebar,
|
||
- in Team-/Brand-Kontexten,
|
||
- auf Login-/Einladungsansichten, sofern das Team bereits feststeht.
|
||
|
||
Das Logo ist ein Identitätsanker, ersetzt aber keine Überschrift und soll nicht als dekoratives Hintergrundmotiv vervielfacht werden.
|
||
|
||
## 14.2 Visuelle Referenz — Quick Mockup v1
|
||
|
||
Die abgestimmte visuelle Richtung ist bewusst **clean, ruhig und informationszentriert**. Bilder und motivierende Illustrationen sind nicht Teil des normalen Dashboard-Rasters. Die Oberfläche soll freundlich wirken, aber primär beim Arbeiten helfen.
|
||
|
||
Verbindliche Gestaltungsrichtung:
|
||
|
||
- helle Arbeitsfläche mit viel Weißraum,
|
||
- dunkle teal/navy Navigation als klarer Rahmen,
|
||
- weiße Cards mit leichter Kontur bzw. sehr zurückhaltendem Schatten,
|
||
- wenige, klare Statusfarben,
|
||
- Logo gut sichtbar,
|
||
- Icons funktional statt dekorativ,
|
||
- keine großflächigen Motivbilder im Dashboard,
|
||
- keine regelmäßigen Motivationssprüche oder Gamification-Elemente,
|
||
- große Display-Schrift nur für Hierarchie und Orientierung, nicht für jeden Kartentitel.
|
||
|
||
Die drei Dashboard-Profile teilen dieselbe visuelle Sprache, unterscheiden sich aber in Informationsdichte und Aufgabenfokus:
|
||
|
||
1. **Mitglieder-Dashboard** — persönlich, mobil-first, Fokus auf nächste Einsätze, offene Rückmeldungen, Verfügbarkeit und ungelesenen Teamchat.
|
||
2. **Teamverwaltungs-Dashboard** — sachlicher und dichter, Fokus auf offene Aufgaben, Rückmeldungen, Freigaben, Einladungen und unbesetzte Positionen.
|
||
3. **Organisationsverwaltungs-Dashboard** — am nüchternsten; Fokus ausschließlich auf Struktur, Teams, Module, Brand Profiles, Einladungen und organisationsweite Verwaltungsaufgaben. Keine teaminternen Fachinhalte.
|
||
|
||
Quick-Mockup-Referenz im Projektordner:
|
||
|
||
```text
|
||
vor_ort_arbeit_dashboard_mockups.png
|
||
```
|
||
|
||
Diese Grafik ist eine **visuelle Referenz, kein Pixel-Perfect-Vertrag**. Bei der Implementierung haben Barrierearmut, Responsive-Verhalten, reale Inhalte und das zentrale Komponentensystem Vorrang. Die Grundwirkung — Farben, Typografie, Informationsruhe, Logo-Präsenz und klare Dashboard-Hierarchie — soll jedoch erhalten bleiben.
|
||
|
||
---
|
||
|
||
# 15. Modularchitektur
|
||
|
||
## 15.1 Abhängigkeitsregel
|
||
|
||
```text
|
||
Fachmodul → Core erlaubt
|
||
Core → Fachmodul verboten
|
||
```
|
||
|
||
Der Core darf nicht wissen, wie ein Einsatz, Dokument oder Chat fachlich funktioniert.
|
||
|
||
## 15.2 Verzeichnisstruktur
|
||
|
||
Zielstruktur, sinngemäß:
|
||
|
||
```text
|
||
app/
|
||
├── core/
|
||
│ ├── auth/
|
||
│ ├── accounts/
|
||
│ ├── organizations/
|
||
│ ├── teams/
|
||
│ ├── memberships/
|
||
│ ├── invitations/
|
||
│ ├── permissions/
|
||
│ ├── branding/
|
||
│ ├── storage/
|
||
│ ├── notifications/
|
||
│ ├── dashboard/
|
||
│ ├── profiles/
|
||
│ └── audit/
|
||
│
|
||
└── modules/
|
||
├── scheduling/
|
||
├── documents/
|
||
└── chat/
|
||
```
|
||
|
||
Ein größeres Modul darf intern besitzen:
|
||
|
||
```text
|
||
module/
|
||
├── __init__.py
|
||
├── models.py
|
||
├── routes.py
|
||
├── services.py
|
||
├── permissions.py
|
||
├── forms.py
|
||
├── templates/
|
||
└── tests/
|
||
```
|
||
|
||
Nicht jedes Modul braucht jede Datei; die Verantwortlichkeiten bleiben aber getrennt.
|
||
|
||
## 15.3 Routes sind dünn
|
||
|
||
Eine Route erledigt hauptsächlich:
|
||
|
||
```text
|
||
Request
|
||
→ Team-Kontext auflösen
|
||
→ Berechtigung prüfen
|
||
→ Service aufrufen
|
||
→ Response / Redirect / Template
|
||
```
|
||
|
||
Fachlogik gehört in Services, nicht in große Routendateien.
|
||
|
||
## 15.4 Permission Helpers statt Rollenabfragen überall
|
||
|
||
Nicht überall:
|
||
|
||
```python
|
||
if membership.role == "manager":
|
||
```
|
||
|
||
sondern fachliche Funktionen wie:
|
||
|
||
```text
|
||
can_manage_schedule()
|
||
can_review_document()
|
||
can_manage_channel()
|
||
```
|
||
|
||
Dadurch können Rollen später feiner werden, ohne die gesamte Codebasis umzuschreiben.
|
||
|
||
---
|
||
|
||
# 16. Modulregistrierung
|
||
|
||
Die Existenz eines Moduls wird **im Code** definiert, nicht in einer Datenbanktabelle.
|
||
|
||
Beispiel:
|
||
|
||
```text
|
||
ModuleDefinition
|
||
├── key
|
||
├── name
|
||
├── description
|
||
├── version
|
||
├── navigation
|
||
├── member_dashboard_provider
|
||
├── manager_dashboard_provider
|
||
└── dependencies
|
||
```
|
||
|
||
Stabile Modulkeys für v1:
|
||
|
||
- `scheduling`
|
||
- `documents`
|
||
- `chat`
|
||
|
||
Alle installierten Module/Blueprints werden beim App-Start registriert.
|
||
|
||
### TeamModule
|
||
|
||
Die Datenbank entscheidet nur, ob ein vorhandenes Modul für ein Team aktiv ist:
|
||
|
||
```text
|
||
TeamModule
|
||
├── team_id
|
||
├── module_key
|
||
└── enabled
|
||
```
|
||
|
||
Die Kombination `team_id + module_key` ist eindeutig.
|
||
|
||
### Verantwortlichkeiten
|
||
|
||
**Organisationsverwaltung:** aktiviert/deaktiviert Module für Teams.
|
||
**Team-Verwaltung:** konfiguriert und nutzt bereits aktivierte Module.
|
||
|
||
Ein deaktiviertes Modul ist nicht nur aus der Navigation verschwunden. Direkte Routes müssen ebenfalls Zugriff verweigern.
|
||
|
||
Prüfreihenfolge:
|
||
|
||
```text
|
||
aktive TeamMembership?
|
||
→ Modul für Team aktiviert?
|
||
→ fachliche Berechtigung vorhanden?
|
||
→ Zugriff
|
||
```
|
||
|
||
## 16.1 Modulabhängigkeiten
|
||
|
||
Abhängigkeiten müssen explizit deklariert sein. v1 soll möglichst ohne Fachmodul-zu-Fachmodul-Abhängigkeiten auskommen:
|
||
|
||
```text
|
||
scheduling → core
|
||
documents → core
|
||
chat → core
|
||
```
|
||
|
||
Module dürfen nicht ungeordnet gegenseitig interne Modelle abfragen. Falls Modul A Funktionalität von Modul B benötigt, geschieht das über eine definierte Service-Schnittstelle oder die gemeinsame Funktion gehört in den Core.
|
||
|
||
## 16.2 Moduleinstellungen
|
||
|
||
Komplexere Moduleinstellungen gehören dem jeweiligen Modul, z. B. `SchedulingSettings`.
|
||
|
||
Kein vorschnelles generisches JSON-Einstellungsmonster.
|
||
|
||
---
|
||
|
||
# 17. Team-Datenbesitz und Scoping
|
||
|
||
Alle teaminternen Fachobjekte tragen explizit `team_id`, auch wenn die Teamzugehörigkeit theoretisch über eine Beziehung ableitbar wäre.
|
||
|
||
Beispiele:
|
||
|
||
```text
|
||
Shift.team_id
|
||
Document.team_id
|
||
Channel.team_id
|
||
Message.team_id
|
||
```
|
||
|
||
Grundprüfung:
|
||
|
||
```text
|
||
requested_object.team_id == current_team.id
|
||
```
|
||
|
||
und aktive TeamMembership.
|
||
|
||
Diese Redundanz ist bewusst: Teamgrenzen sollen in Code, Queries und Tests klar sichtbar bleiben.
|
||
|
||
---
|
||
|
||
# 18. Dashboard- und Navigationsarchitektur
|
||
|
||
## 18.1 Eindeutiger Team-Kontext
|
||
|
||
Die App arbeitet bei Teamfunktionen immer in einem klaren Teamkontext, vorzugsweise auch sichtbar in der URL:
|
||
|
||
```text
|
||
/team/<slug>/
|
||
/team/<slug>/scheduling/
|
||
/team/<slug>/documents/
|
||
/team/<slug>/chat/
|
||
```
|
||
|
||
Die Session darf das zuletzt verwendete Team merken, aber **keine Berechtigung daraus ableiten**.
|
||
|
||
### Loginverhalten
|
||
|
||
- nur ein aktives Team → direkt dort starten,
|
||
- mehrere Teams + bekannte letzte Auswahl → dort starten,
|
||
- erster Login mit mehreren Teams → einmalige Teamauswahl.
|
||
|
||
## 18.2 Team-Startseite
|
||
|
||
Die normale Startseite zeigt persönliche/relevante Informationen des aktuellen Teams.
|
||
|
||
Beispiel:
|
||
|
||
```text
|
||
Guten Morgen, Patsy
|
||
|
||
Dringend
|
||
Heute wird noch ein Springer gesucht.
|
||
|
||
Deine nächsten Einsätze
|
||
...
|
||
|
||
Verfügbarkeit
|
||
4 Termine noch offen
|
||
|
||
Teamchat
|
||
3 neue Nachrichten
|
||
```
|
||
|
||
Team-Verwalter:innen landen nicht automatisch in einem Backend, sondern benutzen dieselbe Mitgliederstartseite.
|
||
|
||
## 18.3 Team-Verwaltungsbereich
|
||
|
||
Zusätzlich existiert:
|
||
|
||
```text
|
||
/team/<slug>/manage/
|
||
```
|
||
|
||
Nur Team-Verwaltungen dürfen ihn öffnen.
|
||
|
||
Dort erscheinen Aufgaben/Zustände wie:
|
||
|
||
- unterbesetzte Einsätze,
|
||
- fehlende Verfügbarkeiten,
|
||
- Dokumentfreigaben,
|
||
- offene Einladungen.
|
||
|
||
## 18.4 Organisationsverwaltung
|
||
|
||
Separater Bereich:
|
||
|
||
```text
|
||
/org/<slug>/manage/
|
||
```
|
||
|
||
Dieser zeigt ausschließlich strukturelle Organisationsinformationen, z. B.:
|
||
|
||
- Teams,
|
||
- Team-Verwaltungen,
|
||
- Brand Profiles,
|
||
- Modulaktivierungen,
|
||
- Org-Admin-Verwaltung.
|
||
|
||
Keine Teamchat-, Dokument- oder Scheduling-Inhalte.
|
||
|
||
---
|
||
|
||
# 19. Dashboard Extension Contract
|
||
|
||
Der Core kennt keine Fachmodule, muss deren relevante Hinweise aber darstellen können.
|
||
|
||
Deshalb registrieren Module strukturierte Dashboard-Beiträge.
|
||
|
||
```text
|
||
DashboardItem
|
||
├── title
|
||
├── text
|
||
├── category
|
||
├── count optional
|
||
├── action_label optional
|
||
├── action_endpoint optional
|
||
└── module_key
|
||
```
|
||
|
||
Erlaubte Kategorien:
|
||
|
||
- `URGENT`
|
||
- `ACTION`
|
||
- `UPCOMING`
|
||
- `INFO`
|
||
|
||
Sortierung zentral:
|
||
|
||
1. Dringend
|
||
2. Handlungsbedarf
|
||
3. Bevorstehend
|
||
4. Information
|
||
|
||
Das Modul liefert Daten, **nicht eigenes Dashboard-HTML oder eigene Farben**. Der Core rendert standardisierte Komponenten und wendet das Brand Profile an.
|
||
|
||
Es gibt in v1 keine Tabellen wie `DashboardWidget` oder personalisierbare Dashboard-Layouts. Karten werden aus dem aktuellen Systemzustand berechnet.
|
||
|
||
---
|
||
|
||
# 20. Core Notifications
|
||
|
||
Notifications sind Infrastruktur, kein aktivierbares Fachmodul.
|
||
|
||
Fachmodule rufen einen zentralen Notification-Service auf.
|
||
|
||
Konzeptionelles Modell:
|
||
|
||
```text
|
||
Notification
|
||
├── id
|
||
├── account_id
|
||
├── team_id optional
|
||
├── type
|
||
├── priority
|
||
├── title
|
||
├── text
|
||
├── action_endpoint optional
|
||
├── created_at
|
||
└── read_at optional
|
||
```
|
||
|
||
Prioritäten:
|
||
|
||
- `normal`
|
||
- `important`
|
||
- `urgent`
|
||
|
||
### Grundregel
|
||
|
||
> Dashboard zeigt Zustände. Benachrichtigungen melden Ereignisse.
|
||
|
||
Beispiel:
|
||
|
||
- Benachrichtigung: „Andrea hat ihren Einsatz abgesagt.“
|
||
- Dashboard: „1 Position ist aktuell unbesetzt.“
|
||
|
||
Wenn der Platz besetzt ist, verschwindet der Dashboardzustand; die historische Benachrichtigung darf bestehen bleiben.
|
||
|
||
v1 nutzt operative Benachrichtigungen **nur in der App**. E-Mail wird in v1 für Auth und Einladungen verwendet, nicht für normalen Teamverkehr.
|
||
|
||
---
|
||
|
||
# 21. Modul 1: Scheduling / Einsatzplanung
|
||
|
||
## 21.1 Ziel
|
||
|
||
Scheduling bleibt das umfangreichste Fachmodul. Es soll unterschiedliche Teammodelle unterstützen, ohne die Bedienung komplizierter zu machen.
|
||
|
||
Die bisherige feste Regel „2 Haupt + 1 Springer“ wird **nicht** Plattformregel. Sie wird eine Team-/Vorlagenkonfiguration.
|
||
|
||
## 21.2 Workflow
|
||
|
||
```text
|
||
Vorbereitung
|
||
→ Verfügbarkeit offen
|
||
→ Plan wird erstellt
|
||
→ Veröffentlicht
|
||
```
|
||
|
||
Statuswerte:
|
||
|
||
- `draft`
|
||
- `availability_open`
|
||
- `scheduling`
|
||
- `published`
|
||
|
||
Ein vergangener Zeitraum kann später archiviert werden, ohne dafür einen komplizierten zusätzlichen Arbeitsstatus zu benötigen.
|
||
|
||
### Semantik
|
||
|
||
**Vorbereitung:** Verwaltung legt Zeitraum, Einsätze und Positionen an.
|
||
**Verfügbarkeit offen:** Mitglieder geben Ja/Nein pro Einsatz ab und dürfen ändern.
|
||
**Plan wird erstellt:** Verfügbarkeit ist eingefroren; Verwaltung teilt ein.
|
||
**Veröffentlicht:** Plan ist für Mitglieder verbindlich sichtbar; Änderungen laufen über Absage/Nachbesetzung.
|
||
|
||
## 21.3 PlanningPeriod
|
||
|
||
```text
|
||
PlanningPeriod
|
||
├── id
|
||
├── team_id
|
||
├── name
|
||
├── start_date
|
||
├── end_date
|
||
├── status
|
||
├── availability_deadline optional
|
||
├── created_at
|
||
└── archived_at optional
|
||
```
|
||
|
||
Keine erzwungene Zweimonatsdauer.
|
||
|
||
## 21.4 PositionDefinition
|
||
|
||
```text
|
||
PositionDefinition
|
||
├── id
|
||
├── team_id
|
||
├── name
|
||
├── description optional
|
||
├── sort_order
|
||
├── active
|
||
└── replacement_for_id optional
|
||
```
|
||
|
||
Beispiele:
|
||
|
||
```text
|
||
Vor-Ort:
|
||
Haupt
|
||
Springer → Ersatz für Haupt
|
||
|
||
Café Bach:
|
||
Theke
|
||
Springer → Ersatz für Theke
|
||
```
|
||
|
||
Das Ersatzmodell bleibt absichtlich einfach. Keine komplexen Vertretungsketten in v1.
|
||
|
||
## 21.5 ShiftTemplate
|
||
|
||
```text
|
||
ShiftTemplate
|
||
├── id
|
||
├── team_id
|
||
├── name
|
||
├── default_title
|
||
├── default_start_time
|
||
├── default_end_time
|
||
├── default_location
|
||
├── active
|
||
└── Template-Positionen
|
||
```
|
||
|
||
```text
|
||
ShiftTemplatePosition
|
||
├── shift_template_id
|
||
├── position_definition_id
|
||
├── required_count
|
||
└── sort_order
|
||
```
|
||
|
||
Vorlagen sind Komfort, keine Pflicht. Ein konkreter Einsatz darf nach Erzeugung verändert werden.
|
||
|
||
## 21.6 Shift
|
||
|
||
```text
|
||
Shift
|
||
├── id
|
||
├── team_id
|
||
├── planning_period_id
|
||
├── title
|
||
├── date
|
||
├── start_time
|
||
├── end_time
|
||
├── location
|
||
├── description optional
|
||
└── created_at
|
||
```
|
||
|
||
## 21.7 ShiftPosition
|
||
|
||
```text
|
||
ShiftPosition
|
||
├── id
|
||
├── team_id
|
||
├── shift_id
|
||
├── position_definition_id
|
||
├── required_count
|
||
└── sort_order
|
||
```
|
||
|
||
Beispiel:
|
||
|
||
```text
|
||
Haupt required_count = 2
|
||
Springer required_count = 1
|
||
```
|
||
|
||
Ein offener Platz wird aus `required_count - aktive Assignments` berechnet. Es braucht keinen künstlichen „offenen Assignment-Datensatz ohne Person“.
|
||
|
||
## 21.8 Availability
|
||
|
||
Mitglieder melden nur **Ja oder Nein zum gesamten Einsatz**, keine Positionspräferenzen.
|
||
|
||
```text
|
||
Availability
|
||
├── id
|
||
├── team_id
|
||
├── shift_id
|
||
├── team_membership_id
|
||
├── status
|
||
└── updated_at
|
||
```
|
||
|
||
Status:
|
||
|
||
- `available`
|
||
- `unavailable`
|
||
|
||
Kein Datensatz = noch nicht beantwortet.
|
||
|
||
Beim Wechsel des Planungszeitraums zu `scheduling` werden Verfügbarkeiten eingefroren.
|
||
|
||
## 21.9 Assignment
|
||
|
||
```text
|
||
Assignment
|
||
├── id
|
||
├── team_id
|
||
├── shift_id
|
||
├── shift_position_id
|
||
├── team_membership_id
|
||
├── status
|
||
├── assigned_at
|
||
└── assigned_by optional/system
|
||
```
|
||
|
||
Status mindestens:
|
||
|
||
- `assigned`
|
||
- `cancelled`
|
||
- `promoted`
|
||
|
||
Die Erstverteilung bleibt **manuell durch Team-Verwaltung**.
|
||
|
||
## 21.10 Fairness-Hilfe
|
||
|
||
Bei der Auswahl einer verfügbaren Person zeigt die App mindestens:
|
||
|
||
- Anzahl bisheriger Einsätze,
|
||
- Datum des letzten Einsatzes.
|
||
|
||
Keine automatische Fairness-Verteilung und keine Gewichtungsformeln in v1.
|
||
|
||
## 21.11 Unterbesetzung
|
||
|
||
Unterbesetzung wird aus Daten berechnet:
|
||
|
||
```text
|
||
aktive Assignments < benötigte Positionsanzahl
|
||
```
|
||
|
||
Kein manuell zu pflegender Wahrheitswert, wenn er zuverlässig ableitbar ist.
|
||
|
||
---
|
||
|
||
# 22. Scheduling: Ausfall und Nachbesetzung
|
||
|
||
## 22.1 Grundregel
|
||
|
||
Nach Veröffentlichung wird eine Zuteilung nicht durch nachträgliches Ändern der ursprünglichen Availability korrigiert.
|
||
|
||
Wenn jemand ursprünglich `available` war und später absagt:
|
||
|
||
```text
|
||
Availability = available
|
||
Assignment = cancelled
|
||
```
|
||
|
||
Damit bleibt die historische Information korrekt.
|
||
|
||
## 22.2 Automatisches Nachrücken
|
||
|
||
Wenn eine Zielposition ausfällt und eine passende Ersatzposition definiert und besetzt ist:
|
||
|
||
```text
|
||
Haupt fällt aus
|
||
→ Springer rückt auf Haupt
|
||
→ Springerplatz wird offen
|
||
```
|
||
|
||
Automatisches Nachrücken findet nur statt, wenn das Ergebnis eindeutig ist. Bei mehreren möglichen Ersatzpersonen oder anderen Mehrdeutigkeiten entscheidet die Team-Verwaltung.
|
||
|
||
## 22.3 Springer fällt aus
|
||
|
||
Es wird lediglich die Springerposition offen. Niemand wird künstlich umverteilt.
|
||
|
||
## 22.4 Kein Springer vorhanden
|
||
|
||
Die ausgefallene Zielposition selbst wird offen.
|
||
|
||
## 22.5 Mehrere Ausfälle
|
||
|
||
Automatisierung endet nach den eindeutig möglichen Nachrückschritten. Die App versucht nicht, selbstständig eine komplette neue Dienstplanung zu errechnen.
|
||
|
||
## 22.6 Selbstübernahme offener Positionen
|
||
|
||
Pro Team kann konfiguriert werden, ob offene Positionen nach Veröffentlichung selbst übernommen werden dürfen.
|
||
|
||
Eine Person darf übernehmen, wenn:
|
||
|
||
- aktive TeamMembership,
|
||
- Scheduling aktiviert,
|
||
- Einsatz veröffentlicht,
|
||
- Position wirklich offen,
|
||
- Person nicht bereits in diesem Einsatz eingeteilt,
|
||
- Platz nicht im selben Moment bereits vergeben wurde.
|
||
|
||
Der letzte Punkt muss **transaktional / durch DB-Constraint bzw. atomare Logik** abgesichert werden, nicht nur durch den Buttonzustand im Browser.
|
||
|
||
Eine ursprünglich als `unavailable` gemeldete Person darf später trotzdem einen offenen Dienst übernehmen, falls sich ihre Situation geändert hat.
|
||
|
||
Auch ein übernommener Ersatzdienst kann später wieder abgesagt werden.
|
||
|
||
## 22.7 Dringlichkeit
|
||
|
||
Eine offene Position wird in v1 **24 Stunden vor Einsatzbeginn** als `urgent` behandelt.
|
||
|
||
Das verändert nicht die Fachlogik, nur Priorität und Darstellung.
|
||
|
||
Beispiel:
|
||
|
||
> Dringend: Heute 19:00 – Springer gesucht.
|
||
|
||
Der 24-Stunden-Wert ist in v1 ein fester Produktstandard und kann später teamkonfigurierbar werden.
|
||
|
||
---
|
||
|
||
# 23. Scheduling: Benachrichtigungen
|
||
|
||
### Mitglieder
|
||
|
||
| Ereignis | Empfänger | Priorität |
|
||
|---|---|---|
|
||
| Verfügbarkeit geöffnet | aktive Teammitglieder | normal |
|
||
| Rückmeldung unvollständig, ca. 48 h vor Frist | nur betroffene Mitglieder | normal |
|
||
| Plan veröffentlicht / eigene neue Einsätze | betroffene Mitglieder, sinnvoll gebündelt | wichtig |
|
||
| eigene Einteilung geändert | betroffene Person | wichtig |
|
||
| automatisches Nachrücken | nachrückende Person | wichtig |
|
||
| offener Dienst | berechtigte Teammitglieder | normal |
|
||
| offener Dienst <24 h | berechtigte Teammitglieder | dringend |
|
||
| Einsatzzeit/-ort geändert | eingeteilte Personen | wichtig |
|
||
| Einsatz abgesagt | eingeteilte Personen | wichtig |
|
||
|
||
### Team-Verwaltung
|
||
|
||
| Ereignis | Priorität |
|
||
|---|---|
|
||
| Mitglied sagt Zuteilung ab | wichtig |
|
||
| Person übernimmt offenen Platz | normal |
|
||
| kurzfristige Unterbesetzung | dringend |
|
||
|
||
Dauerhafte Zustände wie „6 Rückmeldungen fehlen“ gehören auf das Dashboard, nicht als dauernder Notification-Spam in die Glocke.
|
||
|
||
Benachrichtigungen sollen bei Veröffentlichung sinnvoll dedupliziert/gebündelt werden, z. B. „Der Dienstplan ist da – du hast 3 neue Einsätze“ statt unmittelbar mehrerer redundanter Meldungen.
|
||
|
||
---
|
||
|
||
# 24. Scheduling: Verlauf
|
||
|
||
Das Scheduling-Modul soll fachlich nachvollziehbar machen, wie eine aktuelle Besetzung entstanden ist, ohne den globalen Security-Audit mit jedem Fachereignis zu überladen.
|
||
|
||
Ein kleiner Scheduling-Verlauf darf Ereignisse wie diese dokumentieren:
|
||
|
||
```text
|
||
Andrea hat abgesagt.
|
||
Chris ist automatisch von Springer auf Haupt nachgerückt.
|
||
Springerposition wurde geöffnet.
|
||
Marco hat die offene Springerposition übernommen.
|
||
```
|
||
|
||
Konkrete Implementierung kann über ein schlankes `SchedulingEvent`-Modell erfolgen.
|
||
|
||
---
|
||
|
||
# 25. Modul 2: Documents / Dokumente
|
||
|
||
## 25.1 Ziel
|
||
|
||
Das Dokumentenmodul ist **keine Nextcloud** und kein komplexes DMS.
|
||
|
||
Die sichtbare Struktur bleibt:
|
||
|
||
```text
|
||
Aktuell
|
||
Archiv
|
||
```
|
||
|
||
## 25.2 Document
|
||
|
||
```text
|
||
Document
|
||
├── id
|
||
├── team_id
|
||
├── uploader_membership_id
|
||
├── title
|
||
├── description optional
|
||
├── original_filename
|
||
├── storage_key
|
||
├── mime_type
|
||
├── file_size
|
||
├── approval_status
|
||
├── library_status
|
||
├── reviewed_by_membership_id optional
|
||
├── rejection_reason optional
|
||
├── created_at
|
||
├── reviewed_at optional
|
||
└── archived_at optional
|
||
```
|
||
|
||
## 25.3 Freigabestatus
|
||
|
||
```text
|
||
pending
|
||
approved
|
||
rejected
|
||
```
|
||
|
||
## 25.4 Bibliotheksstatus
|
||
|
||
```text
|
||
current
|
||
archived
|
||
```
|
||
|
||
Freigabe und Archivierung sind getrennte Dimensionen:
|
||
|
||
- Freigabe = darf das Team die Datei sehen?
|
||
- Archiv = ist die Datei noch aktuell?
|
||
|
||
## 25.5 Uploadrechte
|
||
|
||
Normale Mitglieder dürfen hochladen. Diese Uploads starten mit `pending`.
|
||
|
||
Sichtbarkeit im Pending-Status:
|
||
|
||
- Uploader:in,
|
||
- Team-Verwaltung.
|
||
|
||
Team-Verwaltungsuploads sind direkt freigegeben.
|
||
|
||
Normale Mitglieder dürfen eigene noch wartende Uploads zurückziehen.
|
||
|
||
## 25.6 Dateiformate
|
||
|
||
Normale Teammitglieder dürfen in v1 mindestens hochladen:
|
||
|
||
- PDF
|
||
- Word (`.doc`, `.docx`)
|
||
- JPG / JPEG
|
||
- PNG
|
||
- HEIC / HEIF
|
||
|
||
HEIC/HEIF wird aufgenommen, damit Smartphone-Fotos — insbesondere aus verbreiteten iPhone-Konfigurationen — praktisch nutzbar bleiben.
|
||
|
||
### HTML-Sonderfall
|
||
|
||
HTML darf **ausschließlich durch Team-Verwaltungen** hochgeladen/bereitgestellt werden.
|
||
|
||
Ziel: kleine statische, gut lesbare Anleitungen, z. B. für Café-Abläufe.
|
||
|
||
HTML wird nicht als vertrauenswürdige Mini-Web-App behandelt. Darstellung erfolgt isoliert und restriktiv:
|
||
|
||
- kein JavaScript,
|
||
- keine Formulare,
|
||
- keine frei laufenden aktiven Inhalte,
|
||
- keine unkontrollierten externen Embeds,
|
||
- sandboxed iframe / restriktive CSP bzw. äquivalente Schutzmechanismen.
|
||
|
||
HTML ist Top-down-Inhalt; normale Mitglieder können es nicht einreichen.
|
||
|
||
## 25.7 Dateiprüfung
|
||
|
||
Serverseitig prüfen:
|
||
|
||
- erlaubte Erweiterung,
|
||
- plausibler MIME-/Dateityp,
|
||
- Dateigrößenlimit,
|
||
- Dateiname wird nie als Speicherpfad verwendet.
|
||
|
||
Maximale Uploadgröße ist konfigurierbar, nicht im Fachcode fest verdrahtet.
|
||
|
||
## 25.8 Storage
|
||
|
||
Dateien werden nicht als SQLite-BLOB gespeichert.
|
||
|
||
Beispielstruktur im persistenten App-Volume:
|
||
|
||
```text
|
||
/app/instance/
|
||
├── app.db
|
||
└── uploads/
|
||
├── documents/
|
||
└── profiles/
|
||
```
|
||
|
||
Physische Dateinamen/Storage Keys sind opaque (z. B. UUIDs). `original_filename` ist nur Metadatum.
|
||
|
||
Caddy darf private Uploads nicht einfach öffentlich als statische Dateien ausliefern.
|
||
|
||
Download/Preview läuft durch kontrollierten Zugriff:
|
||
|
||
```text
|
||
Login
|
||
→ TeamMembership
|
||
→ team_id prüfen
|
||
→ Dokument-/Statusberechtigung
|
||
→ Datei ausliefern
|
||
```
|
||
|
||
## 25.9 Bildvorschau
|
||
|
||
Bilder erhalten serverseitig erzeugte Thumbnails/Vorschauen in einem Browser-kompatiblen Format. Das Original bleibt erhalten.
|
||
|
||
So können auch HEIC-/HEIF-Ursprungsbilder zuverlässig in der App als Vorschau dargestellt werden.
|
||
|
||
## 25.10 Freigabe
|
||
|
||
Team-Verwaltung kann:
|
||
|
||
- freigeben,
|
||
- ablehnen,
|
||
- optional Ablehnungsgrund nennen,
|
||
- archivieren,
|
||
- wiederherstellen,
|
||
- dauerhaft löschen.
|
||
|
||
Dauerhaftes Löschen entfernt Datei und Domain-Datensatz. Im Security-/Admin-Audit kann eine minimale Löschaktion verbleiben, ohne unnötig sensible Dateinamen fortzuschreiben.
|
||
|
||
## 25.11 Foto-Hinweis
|
||
|
||
Beim Bild-Upload wird freundlich darauf hingewiesen, nur Fotos hochzuladen, für deren teaminterne Nutzung keine ungeklärten Rechte/Einwilligungen Dritter entgegenstehen.
|
||
|
||
Keine Checkbox-Orgie; der Freigabeprozess ist die zweite Kontrollstufe.
|
||
|
||
## 25.12 Benachrichtigungen
|
||
|
||
- Mitglied lädt hoch → Team-Verwaltung erhält Freigabe-Ereignis.
|
||
- Dokument freigegeben → Uploader:in erhält normale/wichtige Info.
|
||
- Dokument abgelehnt → Uploader:in erhält Info inkl. Grund, falls vorhanden.
|
||
- Nicht jedes neue freigegebene Dokument erzeugt eine Nachricht an das gesamte Team.
|
||
|
||
Wartende Freigaben erscheinen auf dem Verwaltungsdashboard.
|
||
|
||
---
|
||
|
||
# 26. Zukünftige Fotogalerie
|
||
|
||
Eine echte Galerie wird **nicht** in das Dokumentenmodul hineingebaut.
|
||
|
||
Sie kann später als eigenes optionales Modul entstehen, z. B.:
|
||
|
||
```text
|
||
gallery/
|
||
├── Fotoübersicht
|
||
├── Alben / Veranstaltungen
|
||
├── große Bildansicht
|
||
└── Downloadoptionen
|
||
```
|
||
|
||
Sie darf denselben Core-Storage-Service nutzen. Ob Galerie und Dokumente dieselben Domain-Datensätze verwenden, wird erst entschieden, wenn das Galerie-Modul wirklich gebaut wird.
|
||
|
||
---
|
||
|
||
# 27. Modul 3: Chat / Teamkommunikation
|
||
|
||
## 27.1 Ziel
|
||
|
||
Der Chat ist **Arbeitskommunikation, kein WhatsApp-Ersatz für Unterhaltung**.
|
||
|
||
Er soll Teamabsprachen erleichtern, ohne zum Meme-, GIF- oder Dateiaustauschkanal zu werden.
|
||
|
||
## 27.2 Channel
|
||
|
||
```text
|
||
Channel
|
||
├── id
|
||
├── team_id
|
||
├── name
|
||
├── description
|
||
├── type
|
||
├── sort_order
|
||
├── status
|
||
└── created_at
|
||
```
|
||
|
||
Typen:
|
||
|
||
- `announcement`
|
||
- `discussion`
|
||
|
||
Beim Aktivieren des Chat-Moduls entstehen standardmäßig:
|
||
|
||
- **Ankündigungen**
|
||
- **Team**
|
||
|
||
Team-Verwaltungen dürfen weitere **teamweite Discussion-Channels** anlegen und archivieren.
|
||
|
||
Keine privaten Untergruppen in v1.
|
||
|
||
Channel-Status:
|
||
|
||
- `active`
|
||
- `archived`
|
||
|
||
## 27.3 Ankündigungen
|
||
|
||
- Team-Verwaltung schreibt.
|
||
- Alle aktiven Teammitglieder lesen.
|
||
- Mitglieder antworten dort nicht.
|
||
- Neue Ankündigung erzeugt eine In-App-Benachrichtigung.
|
||
|
||
## 27.4 Teamchat / Discussion
|
||
|
||
Alle aktiven Teammitglieder dürfen:
|
||
|
||
- lesen,
|
||
- schreiben,
|
||
- auf einzelne Nachrichten antworten,
|
||
- eigene Nachrichten bearbeiten,
|
||
- eigene Nachrichten löschen.
|
||
|
||
Team-Verwaltungen dürfen zusätzlich moderieren/entfernen.
|
||
|
||
## 27.5 Message
|
||
|
||
```text
|
||
Message
|
||
├── id
|
||
├── team_id
|
||
├── channel_id
|
||
├── author_membership_id
|
||
├── text
|
||
├── reply_to_message_id optional
|
||
├── created_at
|
||
├── edited_at optional
|
||
└── deleted_at optional
|
||
```
|
||
|
||
Gelöschte Nachrichten zeigen in der UI einen neutralen Platzhalter. Bei Moderation darf sichtbar sein, dass die Team-Verwaltung entfernt hat.
|
||
|
||
Es gibt kein öffentliches Edit-Versionsarchiv in v1.
|
||
|
||
## 27.6 Keine Medien/Dateien
|
||
|
||
Im Chat sind ausdrücklich nicht erlaubt:
|
||
|
||
- Bilder,
|
||
- Dokumente,
|
||
- Videos,
|
||
- Audio,
|
||
- GIFs,
|
||
- Sticker,
|
||
- andere Dateianhänge.
|
||
|
||
Fotos gehören in das Dokumenten-/spätere Galerie-System, nicht in den Chat.
|
||
|
||
## 27.7 Links
|
||
|
||
Externe Links werden bewusst eingeschränkt:
|
||
|
||
- nur `https://`,
|
||
- maximal ein externer Link pro normaler Chatnachricht,
|
||
- keine reinen Link-only-Nachrichten; kurzer Kontexttext ist erforderlich,
|
||
- keine Link-Vorschau,
|
||
- kein Thumbnail,
|
||
- kein YouTube-/Social-Embed,
|
||
- bekannte URL-Shortener werden blockiert,
|
||
- externe Links werden als extern kenntlich gemacht.
|
||
|
||
Interne Links innerhalb der eigenen App dürfen normal verwendet werden.
|
||
|
||
Später kann bei echtem Bedarf eine Domain-Allowlist ergänzt werden.
|
||
|
||
## 27.8 Replies
|
||
|
||
Ein einfaches `reply_to_message_id` ist Teil von v1.
|
||
|
||
Threads oder verschachtelte Diskussionen werden nicht gebaut.
|
||
|
||
Eine direkte Antwort auf die eigene Nachricht darf eine normale In-App-Benachrichtigung auslösen.
|
||
|
||
## 27.9 Ungelesen / Zustellung / Gelesen
|
||
|
||
```text
|
||
ChannelReadState
|
||
├── team_membership_id
|
||
├── channel_id
|
||
├── last_delivered_message_id
|
||
└── last_read_message_id
|
||
```
|
||
|
||
Anzeige für eigene Nachrichten:
|
||
|
||
- `✓` grau = erfolgreich auf dem Server gespeichert,
|
||
- `✓✓` grau = von allen relevanten aktuellen Empfänger:innen technisch abgerufen,
|
||
- `✓✓` in der Brand-Akzentfarbe = von allen relevanten Empfänger:innen gelesen.
|
||
|
||
Mitglieder, die erst später einem Team beitreten, zählen nicht rückwirkend für alte Nachrichten.
|
||
|
||
Optionaler Detailhinweis darf z. B. „Gelesen von 8/10“ zeigen, aber keine detaillierte Überwachungsliste mit individuellen Lesezeitpunkten.
|
||
|
||
## 27.10 Polling
|
||
|
||
Neue Nachrichten werden mit kleinem Vanilla-JS-`fetch`-Polling nachgeladen.
|
||
|
||
- kein WebSocket-System in v1,
|
||
- Polling-Intervall konfigurierbar,
|
||
- Chat bleibt grundsätzlich auch durch normales Neuladen funktional.
|
||
|
||
## 27.11 Chat-Benachrichtigungen
|
||
|
||
Normale neue Chatnachrichten erzeugen **keine Glockenbenachrichtigung pro Nachricht**.
|
||
|
||
Stattdessen:
|
||
|
||
- Channel zeigt Ungelesen-Zähler,
|
||
- Ankündigung → Notification,
|
||
- direkte Antwort auf eigene Nachricht → Notification.
|
||
|
||
---
|
||
|
||
# 28. Core Storage Service
|
||
|
||
Da Dokumente, Profilbilder und spätere Medienmodule Dateien benötigen, gibt es einen gemeinsamen Storage-Service im Core.
|
||
|
||
Aufgaben:
|
||
|
||
- sichere opaque Storage Keys erzeugen,
|
||
- Pfade kontrollieren,
|
||
- Dateien schreiben/lesen/löschen,
|
||
- Thumbnails/Varianten verwalten,
|
||
- Dateigrößenlimits und erlaubte Speicherbereiche unterstützen,
|
||
- keine öffentliche Pfadableitung aus User-Dateinamen zulassen.
|
||
|
||
Domain-Modelle behalten ihre eigenen Metadaten und Berechtigungen; der Storage-Service entscheidet nicht selbst, wer ein Dokument sehen darf.
|
||
|
||
---
|
||
|
||
# 29. Audit Log
|
||
|
||
Der globale `AuditLog` ist für sicherheits- und administrationsrelevante Änderungen gedacht, **nicht für jeden Klick**.
|
||
|
||
```text
|
||
AuditLog
|
||
├── id
|
||
├── organization_id
|
||
├── team_id optional
|
||
├── actor_account_id
|
||
├── action
|
||
├── target_type
|
||
├── target_id
|
||
└── created_at
|
||
```
|
||
|
||
Beispiele:
|
||
|
||
- Team erstellt/archiviert,
|
||
- Brand Profile geändert,
|
||
- Modul aktiviert/deaktiviert,
|
||
- Team-Verwaltung ernannt/entfernt,
|
||
- Org-Admin-Rechte geändert,
|
||
- Account gesperrt,
|
||
- Mitgliedschaft administrativ beendet,
|
||
- sensibles Dokument dauerhaft gelöscht.
|
||
|
||
Chatnachrichten, normale Verfügbarkeitsklicks oder jeder Seitenaufruf gehören nicht in diesen Audit Log.
|
||
|
||
---
|
||
|
||
# 30. Berechtigungsmatrix v1
|
||
|
||
| Funktion | Mitglied | Team-Verwaltung | Organisationsadmin ohne TeamMembership |
|
||
|---|:---:|:---:|:---:|
|
||
| eigenes Team öffnen | ✓ | ✓ | ✗ |
|
||
| Teamverzeichnis sehen | ✓ | ✓ | ✗ |
|
||
| freiwillig geteilte Teamkontakte sehen | ✓ | ✓ | ✗ |
|
||
| Login-E-Mails normaler Mitglieder sehen | ✗ | ✓ | ✗ |
|
||
| Mitglieder einladen | ✗ | ✓ | ✗ |
|
||
| normale Mitgliedschaft verwalten | ✗ | ✓ | ✗ |
|
||
| Team-Verwaltung ernennen | ✗ | ✗ | ✓ |
|
||
| Team-Verwaltung entfernen | ✗ | ✗ | ✓ |
|
||
| Team erstellen/archivieren | ✗ | ✗ | ✓ |
|
||
| Brand Profile zuordnen | ✗ | ✗ | ✓ |
|
||
| Module aktivieren/deaktivieren | ✗ | ✗ | ✓ |
|
||
| aktivierte Module konfigurieren | ✗ | ✓ | ✗ |
|
||
| Teamchat lesen | ✓ | ✓ | ✗ |
|
||
| Dokumente sehen | ✓* | ✓ | ✗ |
|
||
| Scheduling sehen | ✓* | ✓ | ✗ |
|
||
| Teaminterne Inhalte quer über alle Teams sehen | ✗ | ✗ | ✗ |
|
||
|
||
`*` nur wenn das entsprechende Modul für das Team aktiviert ist und die konkrete Objektberechtigung erfüllt ist.
|
||
|
||
---
|
||
|
||
# 31. UI- und UX-Grundsätze
|
||
|
||
## 31.1 Mobile first
|
||
|
||
Primärer Testbereich:
|
||
|
||
- ca. 360–460 px App-Breite.
|
||
|
||
Desktop ist vollständig nutzbar, aber nicht der Ausgangspunkt für die Informationsarchitektur.
|
||
|
||
## 31.2 Barrierearmut
|
||
|
||
Pflicht:
|
||
|
||
- Touch-Ziele mindestens ca. 44 px,
|
||
- sichtbarer Tastaturfokus,
|
||
- sinnvolle Labels,
|
||
- Status nie nur über Farbe,
|
||
- ausreichender Kontrast,
|
||
- Fehlermeldungen mit konkreter Handlungsanweisung,
|
||
- semantisches HTML,
|
||
- keine zentrale Funktion ausschließlich per JavaScript.
|
||
|
||
## 31.3 Sprache
|
||
|
||
- Deutsch,
|
||
- klare aktive Verben,
|
||
- ein Begriff bleibt im Flow gleich,
|
||
- keine unnötige Verwaltungs-/IT-Sprache in der Mitgliederoberfläche.
|
||
|
||
## 31.4 Komponenten
|
||
|
||
Zentrale, wiederverwendbare Komponenten, z. B.:
|
||
|
||
- Buttons,
|
||
- Cards,
|
||
- Badges,
|
||
- Form Fields,
|
||
- Confirmation Pattern,
|
||
- Empty State,
|
||
- Tabellen,
|
||
- Listen,
|
||
- Navigation,
|
||
- Dashboard Cards.
|
||
|
||
Fachmodule dürfen fachspezifische Komponenten ergänzen, aber keine eigenen Grund-Buttons oder eigene Designwelt erfinden.
|
||
|
||
## 31.5 Visuelle Dichte und Bildsprache
|
||
|
||
Die Ehrenamt-Plattform ist **kein Kampagnenauftritt**, sondern ein Arbeitswerkzeug. Deshalb gilt für alle Brand Profiles, insbesondere für Vor-Ort:
|
||
|
||
- Illustrationen und Fotos nur dort einsetzen, wo sie inhaltlich helfen.
|
||
- Dashboards bleiben grundsätzlich bildarm.
|
||
- Motivierende Texte sind optional und selten; sie dürfen keine Arbeitsinformation verdrängen.
|
||
- Status, nächste Schritte und Handlungsbedarf stehen visuell vor Dekoration.
|
||
- Wiederkehrende Markenidentität entsteht primär durch Logo, Typografie, Farben, Navigation und Komponentenrhythmus.
|
||
|
||
Für Vor-Ort gilt der Quick Mockup aus Abschnitt 14.2 als Referenz für diese Balance.
|
||
|
||
## 31.6 Navigation
|
||
|
||
Die genaue mobile Belegung wird in der UI-Implementierungsphase anhand der aktivierten Module festgelegt, aber:
|
||
|
||
- **Start**, **Team** und **Profil** sind Core-Ziele,
|
||
- Module registrieren Navigationseinträge,
|
||
- Navigation muss auch bei mehreren aktiven Modulen übersichtlich bleiben,
|
||
- Verwaltungsbereiche bleiben klar von der Mitgliederoberfläche getrennt.
|
||
|
||
Diese Detailfrage darf bei der UI-Implementierung entschieden werden; sie ändert die Architektur nicht.
|
||
|
||
---
|
||
|
||
# 32. Sicherheit
|
||
|
||
## 32.1 Invite-only
|
||
|
||
Es gibt keine offene Selbstregistrierung. Accounts entstehen über Einladung bzw. verifizierte E-Mail im Zusammenhang mit einer bestehenden Einladung/Organisation.
|
||
|
||
## 32.2 Autorisierung
|
||
|
||
Jeder teaminterne Zugriff prüft serverseitig:
|
||
|
||
1. gültige Account-/Device-Session,
|
||
2. aktive TeamMembership,
|
||
3. Teamzugehörigkeit des Objekts,
|
||
4. Modulaktivierung,
|
||
5. konkrete Permission.
|
||
|
||
Keine UI-Ausblendung ersetzt eine serverseitige Prüfung.
|
||
|
||
## 32.3 Auth-Schutz
|
||
|
||
- Auth-Codes/-Tokens nur gehasht speichern,
|
||
- Ablaufzeiten,
|
||
- Rate Limits,
|
||
- Fehlversuchslimit,
|
||
- konsumierte Challenges nicht wiederverwenden,
|
||
- generische Antworten vermeiden unnötige E-Mail-Enumeration,
|
||
- sichere Cookies.
|
||
|
||
## 32.4 Web-Sicherheit
|
||
|
||
Mindestens berücksichtigen:
|
||
|
||
- CSRF-Schutz für zustandsändernde Requests,
|
||
- XSS-Vermeidung durch Jinja Autoescaping und gezielte Sanitization,
|
||
- Uploadpfade nie aus ungeprüften Dateinamen bauen,
|
||
- restriktive HTML-Einbettung,
|
||
- keine direkte öffentliche Auslieferung privater Uploads,
|
||
- sichere Redirects,
|
||
- Proxy-Headers nur bei aktivierter vertrauenswürdiger Proxy-Konfiguration.
|
||
|
||
## 32.5 Datenschutz
|
||
|
||
- keine Klient:innendaten,
|
||
- Datensparsamkeit,
|
||
- Teamgrenzen strikt,
|
||
- freiwillige Kontaktdaten opt-in,
|
||
- Login-E-Mail nicht als Teamkontakt missbrauchen,
|
||
- Foto-/Einwilligungsregel,
|
||
- Datenschutzinformationen und Verarbeitungsverzeichnis müssen vor echtem Betrieb organisatorisch mit der Aidshilfe abgestimmt werden.
|
||
|
||
---
|
||
|
||
# 33. Datenbankstrategie
|
||
|
||
## 33.1 SQLite bleibt v1-Entscheidung
|
||
|
||
Für die geplante Größenordnung bleibt SQLite bewusst bestehen.
|
||
|
||
Trotzdem:
|
||
|
||
- ausschließlich über SQLAlchemy arbeiten,
|
||
- Flask-Migrate/Alembic ab Beginn der neuen Architektur,
|
||
- keine unnötigen SQLite-Spezialkonstruktionen,
|
||
- Datenmodell so halten, dass ein späterer PostgreSQL-Wechsel möglich bleibt.
|
||
|
||
Ein PostgreSQL-Umstieg wird nicht vorweggebaut.
|
||
|
||
## 33.2 Persistenter Pfad
|
||
|
||
Produktion:
|
||
|
||
```text
|
||
/app/instance/app.db
|
||
```
|
||
|
||
und Uploads ebenfalls unter dem persistenten `/app/instance`-Mount.
|
||
|
||
Der konkrete Host-Volume-/Named-Volume-Name wird im Deployment-Stack dokumentiert.
|
||
|
||
---
|
||
|
||
# 34. Migrationen
|
||
|
||
Es gibt einen gemeinsamen Alembic-/Flask-Migrate-Verlauf für die gesamte App:
|
||
|
||
```text
|
||
migrations/
|
||
```
|
||
|
||
Module besitzen keine separaten, konkurrierenden Migrationssysteme.
|
||
|
||
Migrationen werden mit der jeweiligen Featureänderung erzeugt, getestet und versioniert.
|
||
|
||
Deployment führt ausstehende Migrationen bewusst aus; Schemaänderungen werden nicht durch `db.create_all()` in Produktion „magisch“ erledigt.
|
||
|
||
---
|
||
|
||
# 35. Tests
|
||
|
||
Jedes Core-Teil und jedes Fachmodul bringt Tests für seine Regeln mit.
|
||
|
||
## 35.1 Pflicht-Sicherheitstests für jedes Teammodul
|
||
|
||
Mindestens:
|
||
|
||
1. keine TeamMembership → Zugriff verboten,
|
||
2. falsches Team → Zugriff verboten,
|
||
3. Modul deaktiviert → Zugriff verboten,
|
||
4. normales Mitglied → nur Mitgliederfunktionen,
|
||
5. Team-Verwaltung → Verwaltungsfunktionen,
|
||
6. Organisationsadmin ohne TeamMembership → Team-Inhalte verboten,
|
||
7. Objekt aus anderem Team → niemals sichtbar/manipulierbar.
|
||
|
||
## 35.2 Auth-Tests
|
||
|
||
- Code nur einmal verwendbar,
|
||
- abgelaufener Code abgelehnt,
|
||
- Fehlversuchslimit,
|
||
- widerrufene Device Session ungültig,
|
||
- frische Re-Auth für sensible Aktionen.
|
||
|
||
## 35.3 Scheduling-Tests
|
||
|
||
- Statusübergänge,
|
||
- Verfügbarkeit nach Freeze nicht änderbar,
|
||
- manuelle Zuteilung,
|
||
- Unterbesetzung,
|
||
- eindeutiges Springer-Nachrücken,
|
||
- kein automatisches Raten bei Mehrdeutigkeit,
|
||
- offene Position übernehmen,
|
||
- gleichzeitige Übernahme nur einmal erfolgreich,
|
||
- ursprüngliches `unavailable` blockiert spätere Selbstübernahme nicht,
|
||
- Dringlichkeit <24 h.
|
||
|
||
## 35.4 Documents-Tests
|
||
|
||
- Pending-Sichtbarkeit,
|
||
- Freigabe/Ablehnung,
|
||
- normale Mitglieder können kein HTML hochladen,
|
||
- HTML nur isoliert dargestellt,
|
||
- unerlaubte Dateitypen abgelehnt,
|
||
- Storage Path Traversal verhindert,
|
||
- Teamgrenzen bei Download/Preview,
|
||
- Rückzug eigener Pending-Uploads.
|
||
|
||
## 35.5 Chat-Tests
|
||
|
||
- Announcement-Schreibrecht,
|
||
- Discussion-Schreibrecht,
|
||
- Teamgrenzen,
|
||
- Reply,
|
||
- Edit/Delete nur eigene Nachrichten bzw. Moderation,
|
||
- keine Dateianhänge,
|
||
- Linkregeln,
|
||
- Read/Delivered-State,
|
||
- neue Teammitglieder zählen nicht rückwirkend für alte Read Receipts.
|
||
|
||
## 35.6 UI-/A11y-Tests
|
||
|
||
Mindestens kritische Flows in mobilen Breiten prüfen:
|
||
|
||
- Login,
|
||
- Teamwechsel,
|
||
- Verfügbarkeit,
|
||
- Dienstübernahme,
|
||
- Dokumentupload,
|
||
- Chat,
|
||
- Teamverwaltung.
|
||
|
||
---
|
||
|
||
# 36. Deployment-Vertrag
|
||
|
||
Deployment ist Teil der Architektur.
|
||
|
||
## 36.1 VPS-Struktur
|
||
|
||
Bestehendes Betriebsprinzip:
|
||
|
||
```text
|
||
~/apps/<name>/ → Git-Arbeitskopie der Anwendung
|
||
~/stacks/<name>/ → Compose-/Stack-Konfiguration
|
||
Caddy → Reverse Proxy / TLS
|
||
```
|
||
|
||
Der endgültige App-/Repo-/Containername ist noch festzulegen; danach wird **derselbe Name konsistent** für Repo, App-Verzeichnis, Stack-Verzeichnis und Container verwendet.
|
||
|
||
## 36.2 Versioniert
|
||
|
||
Mindestens:
|
||
|
||
- `Dockerfile`
|
||
- `compose.yaml` bzw. versionierte Stack-Vorlage
|
||
- `.env.example`
|
||
- `requirements.txt`
|
||
- Python-Versionsfestlegung
|
||
- Migrationen
|
||
- `INSTALL.md` / Recovery-Anleitung
|
||
- `PROJECT_DESIGN.md`
|
||
|
||
## 36.3 Nicht versioniert
|
||
|
||
- echte `.env`
|
||
- `SECRET_KEY`
|
||
- SMTP-Passwort / Credentials
|
||
- Device-/Auth-Secrets
|
||
- Produktionsdatenbank
|
||
- Uploads
|
||
- Backups
|
||
|
||
## 36.4 Docker
|
||
|
||
- fester Python-Major/Minor-Stand,
|
||
- Gunicorn vorhanden,
|
||
- Prozess läuft als non-root User,
|
||
- persistentes `/app/instance`,
|
||
- keine Secrets im Image,
|
||
- Healthcheck bzw. `/health`.
|
||
|
||
## 36.5 Proxy
|
||
|
||
Caddy terminiert TLS und proxyt zur Flask/Gunicorn-App.
|
||
|
||
`ProxyFix` wird nur aktiviert, wenn eine explizite Konfiguration wie `TRUST_PROXY_HEADERS=true` dies verlangt und der Proxy vertrauenswürdig ist.
|
||
|
||
## 36.6 Health Endpoint
|
||
|
||
`/health`:
|
||
|
||
- sehr leichtgewichtig,
|
||
- ohne personenbezogene oder sensible Daten,
|
||
- geeignet für Container-/Deploymentprüfung.
|
||
|
||
---
|
||
|
||
# 37. E-Mail als Produktionsabhängigkeit
|
||
|
||
Passwordless Login macht E-Mail zu einer kritischen Infrastrukturkomponente.
|
||
|
||
SMTP-/Mailkonfiguration ausschließlich über Environment, z. B. sinngemäß:
|
||
|
||
```text
|
||
MAIL_HOST
|
||
MAIL_PORT
|
||
MAIL_USERNAME
|
||
MAIL_PASSWORD
|
||
MAIL_FROM
|
||
```
|
||
|
||
Konkrete Variablennamen können bei Implementierung finalisiert werden, müssen dann in `.env.example` dokumentiert sein.
|
||
|
||
Keine Provider-Credentials im Code.
|
||
|
||
Auth-/Einladungsmails sollen funktionieren, bevor v1 als betriebsbereit gilt.
|
||
|
||
---
|
||
|
||
# 38. Backup- und Restore-Vertrag
|
||
|
||
Ein vollständiges Backup besteht mindestens aus einem zusammengehörenden Stand von:
|
||
|
||
1. SQLite-Datenbank,
|
||
2. Upload-/Storage-Verzeichnis.
|
||
|
||
Nur `app.db` zu sichern ist **kein vollständiges Backup**.
|
||
|
||
Backups müssen außerhalb des laufenden Container-Dateisystems verfügbar sein.
|
||
|
||
Ein Restore-Prozess gilt erst als belastbar, wenn er mindestens einmal praktisch getestet wurde.
|
||
|
||
Restore-Check:
|
||
|
||
- Account/Org/Teams vorhanden,
|
||
- TeamMemberships vorhanden,
|
||
- Dokumentdateien funktionieren,
|
||
- Profilbilder funktionieren,
|
||
- Migrationen auf Zielstand laufen,
|
||
- Login-Mail funktioniert,
|
||
- Teamgrenzen weiterhin korrekt.
|
||
|
||
---
|
||
|
||
# 39. Reinstall-/Recovery-Vertrag
|
||
|
||
Eine Neuinstallation auf einem frischen Server soll reproduzierbar sein:
|
||
|
||
```text
|
||
1. Host-Voraussetzungen installieren
|
||
2. Caddy / Docker vorbereiten
|
||
3. App-Repo nach ~/apps/<name> klonen
|
||
4. Stack nach ~/stacks/<name> bereitstellen
|
||
5. .env aus .env.example erstellen und Secrets eintragen
|
||
6. persistentes Datenvolume anlegen bzw. Backup wiederherstellen
|
||
7. Image bauen / Compose starten
|
||
8. Migrationen ausführen
|
||
9. Caddy-Route aktivieren
|
||
10. /health prüfen
|
||
11. E-Mail-Login testen
|
||
12. Team-/Modulzugriff als Smoke Test prüfen
|
||
```
|
||
|
||
Die App soll nicht davon abhängen, dass eine einzelne Person „noch weiß, wie das damals eingerichtet war“.
|
||
|
||
---
|
||
|
||
# 40. Bootstrap des ersten Org-Admins
|
||
|
||
Es gibt einen CLI-Befehl bzw. äquivalenten kontrollierten Bootstrap:
|
||
|
||
```text
|
||
flask ... bootstrap-org-admin <email>
|
||
```
|
||
|
||
Der genaue Commandname wird bei Implementierung festgelegt.
|
||
|
||
Er:
|
||
|
||
- nimmt eine E-Mail entgegen,
|
||
- erzeugt keinen hardcodierten Masteraccount,
|
||
- startet einen verifizierten Org-Admin-Einladungsflow,
|
||
- verweigert standardmäßig unkontrollierte weitere Bootstrap-Admins, wenn bereits Organisationsadministration existiert.
|
||
|
||
Nach Restore kommen Org-Admin-Rechte aus der Datenbank zurück; Bootstrap ist kein normaler täglicher Verwaltungsweg.
|
||
|
||
---
|
||
|
||
# 41. Vorgeschlagene Projektstruktur
|
||
|
||
```text
|
||
<repo>/
|
||
├── PROJECT_DESIGN.md
|
||
├── CLAUDE.md # optional: kurze Agentenregeln + Verweis hierher
|
||
├── TASKS.md # neuer Build-/Migrationsplan
|
||
├── README.md
|
||
├── INSTALL.md
|
||
├── .env.example
|
||
├── .python-version # oder gleichwertige Versionierung
|
||
├── requirements.txt
|
||
├── Dockerfile
|
||
├── compose.yaml # oder Stack-Vorlage
|
||
├── migrations/
|
||
│
|
||
├── app/
|
||
│ ├── __init__.py
|
||
│ │
|
||
│ ├── core/
|
||
│ │ ├── auth/
|
||
│ │ ├── accounts/
|
||
│ │ ├── organizations/
|
||
│ │ ├── teams/
|
||
│ │ ├── memberships/
|
||
│ │ ├── invitations/
|
||
│ │ ├── permissions/
|
||
│ │ ├── branding/
|
||
│ │ ├── storage/
|
||
│ │ ├── notifications/
|
||
│ │ ├── dashboard/
|
||
│ │ ├── profiles/
|
||
│ │ └── audit/
|
||
│ │
|
||
│ ├── modules/
|
||
│ │ ├── scheduling/
|
||
│ │ ├── documents/
|
||
│ │ └── chat/
|
||
│ │
|
||
│ ├── templates/
|
||
│ │ ├── core/
|
||
│ │ └── components/
|
||
│ │
|
||
│ └── static/
|
||
│ ├── css/
|
||
│ ├── js/
|
||
│ └── avatars/
|
||
│
|
||
├── tests/
|
||
└── instance/ # lokal; nicht versioniert
|
||
```
|
||
|
||
Die genaue Unterteilung kann pragmatisch angepasst werden. Nicht verhandelbar sind die fachlichen Grenzen Core ↔ Module und Services ↔ Routes.
|
||
|
||
---
|
||
|
||
# 42. Migration vom bestehenden CheckPoint-Prototyp
|
||
|
||
Der vorhandene Stand hat bereits große Teile des alten Einzelteam-Konzepts implementiert. Deshalb soll der Umbau nicht als unkontrolliertes „weiter draufprogrammieren“ erfolgen.
|
||
|
||
## Phase 0 — Bestand sichern
|
||
|
||
- aktuellen funktionierenden Stand committen,
|
||
- Legacy-Tag/Branch anlegen,
|
||
- aktuelle SQLite-/Uploaddaten sichern, falls reale Daten existieren,
|
||
- relevante bestehende Tests/Flows dokumentieren.
|
||
|
||
## Phase 1 — Dokumente entwirren
|
||
|
||
- dieses `PROJECT_DESIGN.md` als Source of Truth einführen,
|
||
- alte Konzeptdateien als historisch markieren,
|
||
- `CLAUDE.md` auf Arbeitsregeln + Verweis reduzieren,
|
||
- `TASKS.md` nicht weiter nach altem Architekturmodell abarbeiten.
|
||
|
||
## Phase 2 — Plattform-Core
|
||
|
||
In sinnvoll kleinen Migrationen:
|
||
|
||
1. `Organization`
|
||
2. `Account`
|
||
3. `Team`
|
||
4. `OrganizationMembership`
|
||
5. `TeamMembership`
|
||
6. Einladungsmodelle
|
||
7. `BrandProfile`
|
||
8. `TeamModule`
|
||
9. `AuditLog`
|
||
10. Core Storage / Profile
|
||
|
||
Bestehende Daten nur migrieren, wenn sie erhaltenswert/real sind. Bei reinem Entwicklungsdatenbestand darf ein sauberer Neu-Seed einfacher und sicherer sein als künstliche Datenrettung.
|
||
|
||
## Phase 3 — Passwordless Auth
|
||
|
||
- alten Benutzername-/Passwort-Flow entfernen,
|
||
- Mailkonfiguration,
|
||
- AuthChallenge,
|
||
- DeviceSession,
|
||
- Invite-only Onboarding,
|
||
- Bootstrap Org Admin,
|
||
- Re-Auth für sensible Aktionen.
|
||
|
||
## Phase 4 — Team-/Org-Oberflächen
|
||
|
||
- Teamkontext in URLs,
|
||
- Teamwechsler,
|
||
- Mitgliederstartseite,
|
||
- Teamverwaltung,
|
||
- Organisationsverwaltung,
|
||
- Mitgliederverzeichnis,
|
||
- freiwillige Teamkontakte,
|
||
- Avatar/Profile.
|
||
|
||
## Phase 5 — Module Registry + Dashboard
|
||
|
||
- `ModuleDefinition`
|
||
- `TeamModule`
|
||
- Navigation Contribution
|
||
- Member-/Manager-Dashboard Provider
|
||
- standardisierte DashboardItem-Komponente.
|
||
|
||
## Phase 6 — Scheduling refactor
|
||
|
||
- flexible Planungszeiträume,
|
||
- PositionDefinitions,
|
||
- Templates,
|
||
- ShiftPositions,
|
||
- neuer Statusflow,
|
||
- Availability Freeze,
|
||
- Assignments,
|
||
- Springerlogik,
|
||
- dringende Nachbesetzung,
|
||
- Scheduling Notifications/History.
|
||
|
||
## Phase 7 — Documents refactor
|
||
|
||
- Core Storage,
|
||
- Teamscoping,
|
||
- Approval/Archive trennen,
|
||
- HEIC/HEIF,
|
||
- Thumbnails,
|
||
- HTML nur für Team-Verwaltung und isoliert,
|
||
- Backup-/Restore-Test mit echten Testdateien.
|
||
|
||
## Phase 8 — Chat refactor
|
||
|
||
- generische Channels,
|
||
- Announcement + Discussion,
|
||
- Replies,
|
||
- Moderation,
|
||
- Linkrestriktionen,
|
||
- Polling,
|
||
- delivered/read states,
|
||
- Notification-Ausnahmen.
|
||
|
||
## Phase 9 — Produktionshärtung
|
||
|
||
- CSRF / Cookies / Rate Limits,
|
||
- Permission-Invariant-Tests,
|
||
- `/health`,
|
||
- Gunicorn,
|
||
- non-root Docker,
|
||
- ProxyFix-Konfiguration,
|
||
- Logging,
|
||
- Backup-/Restore-Dokumentation,
|
||
- SMTP Smoke Test.
|
||
|
||
## Phase 10 — Deployment
|
||
|
||
Erst wenn die neue Architektur, Auth und Teamgrenzen getestet sind, erfolgt der produktive VPS-Deploy nach dem Reinstall-Vertrag.
|
||
|
||
---
|
||
|
||
# 43. Erweiterung um ein neues Modul
|
||
|
||
Ein neues Fachmodul folgt immer demselben Rezept:
|
||
|
||
```text
|
||
1. echten Bedarf beschreiben
|
||
2. stabilen module_key festlegen
|
||
3. ModuleDefinition registrieren
|
||
4. Datenmodell mit team_id anlegen
|
||
5. Migration erstellen
|
||
6. Services implementieren
|
||
7. Permissions definieren
|
||
8. Routes dünn halten
|
||
9. Templates aus zentralen UI-Komponenten bauen
|
||
10. Navigation/Dashboard-Beiträge registrieren
|
||
11. Team-Scoping- und Permission-Tests schreiben
|
||
12. erst dann für ausgewählte Teams aktivieren
|
||
```
|
||
|
||
Ein neues Modul darf nicht verlangen, den Core fachlich umzubauen, nur um sich selbst sichtbar zu machen.
|
||
|
||
---
|
||
|
||
# 44. Future Log
|
||
|
||
Bewusst vorbereitete, aber nicht vorweg implementierte Erweiterungen:
|
||
|
||
## Galerie
|
||
|
||
Eigenständiges Modul, ggf. gemeinsamer Core Storage.
|
||
|
||
## Inventory
|
||
|
||
Eigenständiges Modul für Material-/Bestandsverwaltung, falls ein Team echten Bedarf meldet.
|
||
|
||
## Qualifications
|
||
|
||
Spätere Erweiterung, z. B. erforderliche Schulungen für Positionen. Das Scheduling-Positionsmodell lässt dafür Platz, ohne Qualifikationen heute einzubauen.
|
||
|
||
## Kalenderintegration
|
||
|
||
Später denkbar, aber nicht Teil des Scheduling-Kerns von v1.
|
||
|
||
## Push / operative E-Mail
|
||
|
||
Kann später den Notification-Service als Ausgabekanal ergänzen.
|
||
|
||
## Passkeys / 2FA
|
||
|
||
Vor allem für sensible Organisationsadministration später denkbar.
|
||
|
||
## feinere Rollen / Permissions
|
||
|
||
Der Code benutzt schon Permission Helpers, damit später neue Rollen möglich sind, ohne Rollenstrings überall zu ändern.
|
||
|
||
## PostgreSQL
|
||
|
||
Falls Last, Parallelität oder Betrieb es später sinnvoll machen. Kein aktuelles Ziel.
|
||
|
||
---
|
||
|
||
# 45. Noch bewusst offene Konfigurationsdetails
|
||
|
||
Diese Punkte sind **keine offenen Architekturfragen**, sondern dürfen bei Implementierung/Deployment konkretisiert werden. Sie sollen nicht stillschweigend erfunden, sondern als kleine Produkt-/Betriebsentscheidung dokumentiert werden:
|
||
|
||
1. endgültiger Produkt-/Repo-/Containername,
|
||
2. exakte Auth-Code-Gültigkeit und Device-Session-Dauer,
|
||
3. konkrete Upload-Größenlimits,
|
||
4. konkrete Liste blockierter URL-Shortener,
|
||
5. endgültige mobile Navigation bei vielen aktivierten Modulen,
|
||
6. konkreter SMTP-Provider und Env-Variablennamen,
|
||
7. genaue Backup-Aufbewahrungsdauer,
|
||
8. ob normale Mitglieder in v1 ihre Teammitgliedschaft selbst verlassen dürfen oder ob eine Team-Verwaltung sie beendet,
|
||
9. genaue Regeln für erneutes Senden/Verlängern abgelaufener Einladungen.
|
||
|
||
Für diese Details gilt: klein entscheiden, dokumentieren, testen — keine neue Plattformarchitektur daraus machen.
|
||
|
||
---
|
||
|
||
# 46. Definition of Done für v1
|
||
|
||
v1 ist nicht fertig, nur weil die Seiten funktionieren.
|
||
|
||
Sie gilt als bereit, wenn:
|
||
|
||
- Passwordless Login zuverlässig funktioniert,
|
||
- Einladung + Onboarding funktionieren,
|
||
- mehrere Accounts und mehrere Teams getestet sind,
|
||
- dieselbe Person in mehreren Teams funktionieren kann,
|
||
- Teamwechsel klar ist,
|
||
- Org-Admin ohne TeamMembership keine Team-Inhalte lesen kann,
|
||
- Team-Verwaltungen keine weiteren Manager ernennen können,
|
||
- Brand Profiles teamweise funktionieren,
|
||
- Module pro Team aktivierbar/deaktivierbar sind,
|
||
- Scheduling komplett im neuen Positionsmodell funktioniert,
|
||
- Documents inkl. Freigabe/Storage/Preview funktionieren,
|
||
- Chat die festgelegten Arbeitskommunikationsregeln einhält,
|
||
- Dashboard-Providers modulunabhängig funktionieren,
|
||
- mobile Hauptflows auf ca. 360–460 px gut bedienbar sind,
|
||
- kritische A11y-Grundsätze eingehalten sind,
|
||
- DB-Migrationen reproduzierbar laufen,
|
||
- `/health` funktioniert,
|
||
- Docker/Gunicorn/Caddy-Setup dokumentiert ist,
|
||
- Backup von DB + Uploads funktioniert,
|
||
- ein Restore praktisch getestet wurde,
|
||
- SMTP-Loginmail im Produktionssetup getestet wurde,
|
||
- Reinstall-Anleitung von einer frischen Umgebung aus nachvollziehbar ist.
|
||
|
||
---
|
||
|
||
# 47. Kurzfassung der nicht verhandelbaren Architekturregeln
|
||
|
||
1. **Account ist teamunabhängig.**
|
||
2. **Teamzugehörigkeit läuft ausschließlich über TeamMembership.**
|
||
3. **Team-Verwaltung ist zusätzliches Recht eines normalen Teammitglieds.**
|
||
4. **Nur Organisationsadministration ernennt/entfernt Team-Verwaltungen.**
|
||
5. **Org-Admin ist kein Generalschlüssel für Teamdaten.**
|
||
6. **Teaminterne Fachobjekte tragen `team_id`.**
|
||
7. **Module dürfen den Core nutzen; der Core kennt keine Fachlogik einzelner Module.**
|
||
8. **Code definiert vorhandene Module, `TeamModule` definiert Aktivierung pro Team.**
|
||
9. **Org-Verwaltung aktiviert Module; Team-Verwaltung konfiguriert sie.**
|
||
10. **Routes bleiben dünn; Businesslogik lebt in Services.**
|
||
11. **Permission Helpers statt verstreuter Rollenchecks.**
|
||
12. **Dashboard zeigt Zustände; Notifications melden Ereignisse.**
|
||
13. **Keine Passwörter in v1 — E-Mail-Code + langlebige widerrufbare Device Session.**
|
||
14. **Private Uploads werden nie unkontrolliert statisch ausgeliefert.**
|
||
15. **SQLite + Migrationen von Anfang an; PostgreSQL ist kein v1-Projekt.**
|
||
16. **Backup bedeutet Datenbank + Uploads.**
|
||
17. **Deployment und Restore sind Teil des Produkts, nicht Nacharbeit.**
|
||
18. **Neue Funktionen werden als klar begrenzte Module ergänzt, nicht in bestehende Module hineingestopft.**
|
||
|
||
---
|
||
|
||
# 48. Nächster Schritt
|
||
|
||
Aus diesem Dokument wird als nächstes ein **neues `TASKS.md` für den Umbau/Migrationspfad** abgeleitet.
|
||
|
||
Dabei gilt:
|
||
|
||
- kleine Sessions,
|
||
- jede Session hat ein klar prüfbares Ergebnis,
|
||
- zuerst Core und Sicherheitsgrenzen,
|
||
- erst danach Fachmodule,
|
||
- bestehende funktionierende Teile werden nur übernommen, wenn sie zum neuen Modell passen,
|
||
- keine alte Architektur erhalten, nur weil bereits Code dafür existiert.
|
||
|
||
`PROJECT_DESIGN.md` bleibt während dieses Umbaus die verbindliche Grundlage.
|