- TypeScript 95%
- JavaScript 2.7%
- PLpgSQL 1%
- CSS 1%
- Shell 0.3%
| .github/workflows | ||
| db | ||
| public | ||
| scripts | ||
| src | ||
| supabase/functions/send-email | ||
| tests | ||
| .env.example | ||
| .gitignore | ||
| CHANGELOG.md | ||
| next.config.mjs | ||
| package.json | ||
| playwright.config.ts | ||
| pnpm-lock.yaml | ||
| postcss.config.mjs | ||
| README.md | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
| vercel.json | ||
Bündnis Ost – Event-Plattform
Eine offene, gemeinschaftlich betriebene Web-Plattform für Veranstaltungen, Bündnisse und Initiativen in Ostdeutschland. Veranstalter:innen können Events anlegen, sich gegenseitig als Mitwirkende verknüpfen, und die Community kann sich bei MachBar-Veranstaltungen als Helfer:innen eintragen.
„Ostsicht schärfen, Netzwerke stärken. Finde Veranstaltungen, Bündnisse und Initiativen in deiner Region – oder trage dein eigenes Event ein."
Inhaltsverzeichnis
- Was ist Bündnis Ost?
- Hauptfunktionen
- Tech-Stack (Übersicht)
- Tech-Stack (detailliert)
- Architektur
- Datenbank
- Authentifizierung & Rollen
- Projektstruktur
- Lokale Entwicklung
- Datenbank-Migrationen
- Deployment
- API-Übersicht
- Sicherheit
- Mitwirken
- Lizenz
Was ist Bündnis Ost?
Bündnis Ost ist eine offene Event-Plattform, die sich auf demokratische Veranstaltungen, zivilgesellschaftliche Bündnisse und lokale Initiativen in Ostdeutschland konzentriert. Sie wurde konzipiert als:
- Zentrale Anlaufstelle für politische und kulturelle Veranstaltungen in der Region
- Mitmach-Plattform – jede:r kann eigene Events anlegen
- Netzwerk-Tool für Bündnisse und Initiativen
- MachBar-Plattform – ein spezieller Veranstaltungsmodus, bei dem Helfer:innen für Aufgaben gesucht werden
Die Plattform ist seit Juni 2026 in Betrieb und wird kontinuierlich weiterentwickelt.
Hauptfunktionen
Für Besucher:innen
- Veranstaltungen entdecken mit Filter nach Modus (Standard / MachBar) und Sortierung nach Datum
- Detail-Ansicht mit Karte, Beschreibung, Veranstalter, Teilnahme-Optionen
- MachBar-Anmeldung: Bei MachBar-Veranstaltungen kann man sich als Helfer:in eintragen
- Geschichten lesen – kurze Artikel, Berichte und persönliche Beiträge
- Netzwerk durchsuchen – Übersicht von Bündnissen, Vereinen und Initiativen
- Dark Mode – umschaltbarer Theme-Toggle (Sonne/Mond) als iOS-Style Schalter
- Mobile-First – optimiert für Smartphones, eigener Bottom-Nav mit Aktions-Button
Für Veranstalter:innen
- 10-Schritt-Wizard zum Erstellen einer Veranstaltung mit:
- Modus-Auswahl (Standard / MachBar)
- Standort mit interaktiver Leaflet-Karte + Adress-Suche via Nominatim
- TipTap-basierter Rich-Text-Editor für die Beschreibung
- Datum & Uhrzeit mit „Ende offen"-Option
- Kontaktdaten, Mitveranstalter (Co-Organisatoren), Social-Media-Links
- Cover-Bild (Upload in Supabase Storage oder URL)
- Mitveranstalter hinzufügen – verknüpfbar mit bestehenden Plattform-Konten via User-Autocomplete
- Eigene Veranstaltungen bearbeiten/löschen unter
/my-events - Story-Editor für eigene Artikel/Beiträge
Für Mitwirkende (Co-Organisatoren)
- Mit Konto verknüpfte Mitveranstalter können die Veranstaltung selbst bearbeiten
- Eigene Rolle wird in der Edit-Ansicht angezeigt
Für Admins/Moderatoren
- Admin-Panel mit Sidebar-Navigation (öffnet sich auf Mobile als Slide-in-Sheet)
- Dashboard mit Statistiken, Schnellzugriff, letzter Aktivität
- Moderations-Bereich: Reports prüfen, Kommentare ausblenden
- User-Verwaltung: Rollen ändern, Konten sperren/löschen
- Story-Verwaltung, Widget-Konfiguration, Keyword-Filter (Wortfilter für Inhalts-Moderation)
- Audit-Log der Admin-Aktionen
- Changelog – die Plattform dokumentiert ihre eigene Entwicklung mit
Plattform-Features
- Öffentlicher Changelog unter
/changelogmit Versions-Historie - Barrierefreiheits-Erklärung (Barrierefreiheits-Page)
- Impressum und Datenschutz-Erklärung
- Hilfe-Sektion für User-Guidance
- Multi-Step-Wizard mit Validierung, Auto-Save im LocalStorage, Progress-Tracking
Tech-Stack (Übersicht)
Hier eine kurze Erklärung für alle, die nicht aus der Webentwicklung kommen:
Was ist eine Web-App?
Eine moderne Webanwendung läuft im Browser (Chrome, Firefox, Safari…), aber große Teile der Logik passieren auf einem Server. Das Frontend ist das, was der User sieht und anklickt; das Backend verwaltet Daten, Logik und Sicherheit.
Was wurde hier gebaut?
| Bereich | Technologie | Was es macht (einfach erklärt) |
|---|---|---|
| Frontend | Next.js + React | Das sichtbare Interface – Buttons, Formulare, Seiten |
| Programmiersprache | TypeScript | JavaScript mit Typen-Checking – verhindert Tippfehler |
| Styling | Tailwind CSS | Werkzeug für saubere, konsistente Optik |
| Datenbank | Supabase (= PostgreSQL) | Speichert alle Daten (Events, User, Geschichten…) |
| Authentifizierung | Supabase Auth (Magic-Link / OTP) | User können sich ohne Passwort per E-Mail anmelden |
| Storage | Supabase Storage | Speichert Cover-Bilder für Events und Stories |
| Karten | Leaflet + OpenStreetMap | Interaktive Karte im Veranstaltungs-Wizard |
| Reich-Text-Editor | TipTap | WYSIWYG-Editor für Beschreibungen |
| Animationen | Framer Motion | Sanfte Übergänge und Bewegungen |
| Icons | Lucide | Über 1000 moderne Icons (React) |
| Validierung | Zod | Prüft Formular-Eingaben auf Korrektheit |
| Geocoding | Nominatim (OpenStreetMap) | Wandelt Adressen in Karten-Koordinaten um |
| Sprache | Deutsch (User-Interface) |
Tech-Stack (detailliert)
Frontend Framework
- Next.js 15.5.19 – React-Framework mit Server-Side-Rendering, App-Router, File-Based-Routing, optimierten Bundles
- React 19.2.7 – Library für deklarative UI-Komponenten
- TypeScript 5.6.3 – Strenge Typisierung, strict mode
Styling
- Tailwind CSS 3.4.14 – Utility-First CSS-Framework
- PostCSS 8.4.49 + Autoprefixer 10.4.20 – CSS-Transformation
- @tailwindcss/typography 0.5.20 –
prose-Klassen für Rich-Text - tailwind-merge 2.5.4 – Konfliktfreie Tailwind-Klassen
- clsx 2.1.1 – Conditional
className-Builder - class-variance-authority 0.7.1 – Variant-basiertes Component-API
UI & Interaktion
- Lucide React 0.460.0 – Icon-Bibliothek
- Framer Motion 12.40.0 – Animationen & Gesten
- @formkit/auto-animate 0.9.0 – Auto-Animations für Listen
- react-hook-form 7.53.2 + @hookform/resolvers 3.9.1 – Performante Formular-Verwaltung
Karten & Geocoding
- Leaflet 1.9.4 + react-leaflet 5.0.0 – Interaktive Karte
- OpenStreetMap Tiles – Kartendaten (kein API-Key nötig)
- Nominatim API – Adress→Koordinaten-Suche (eigener Server-Endpoint:
/api/geocode)
Rich-Text-Editor
- TipTap 2.27.2 + Erweiterungen:
@tiptap/starter-kit– Basis-Editor mit Paragraphen, Headings, Listen@tiptap/extension-link– Links einfügen@tiptap/extension-image– Bilder im Text
Backend & Datenbank
- Supabase v2 – Self-Hosted oder Cloud PostgreSQL mit:
- PostgREST – Automatische REST-API aus der DB
- Supabase Auth – Magic-Link, OTP, JWT
- Supabase Storage – S3-kompatibler Object-Storage
- Supabase SSR 0.5.2 – Server-Side-Rendering-Adapter
- pg 8.21.0 – Direkter Postgres-Treiber (für Migrations)
- PL/pgSQL – Trigger, Functions, RLS-Policies in der DB
Validierung
- Zod 3.23.8 – TypeScript-first Schema-Validierung
- Eigene Validatoren für URLs, E-Mails, deutsche PLZ
Daten-Management
- @tanstack/react-query 5.59.20 – Server-State-Caching, wird strategisch genutzt
Entwicklung & Build
- Node.js (LTS) – JavaScript-Runtime
- pnpm – Package-Manager (schneller, effizienter als npm)
- ESLint 9.15.0 + eslint-config-next – Linting
- dotenv 16.4.5 –
.env.local-Loader
Authentifizierung – Details
Die Plattform nutzt passwortlose Anmeldung:
- User gibt E-Mail-Adresse ein
- System sendet einen 6-stelligen OTP-Code (One-Time-Password) per Mail
- Alternativ: Magic-Link (direkter Login-Link)
- Bei Bedarf: Passwort-Reset per Mail
Implementiert über die profiles-Tabelle, die per Postgres-Trigger bei jeder neuen Auth-User-Erstellung automatisch mit Rollen-Eintrag angelegt wird.
Architektur
Server-Architektur
┌──────────────────────────────────────────────────────────────┐
│ Browser (Client) │
│ React 19 + Next.js Client Components + Tailwind │
└──────────────────────────────────────────────────────────────┘
↓ HTTPS
┌──────────────────────────────────────────────────────────────┐
│ Next.js 15 Server (Node.js) │
│ ┌────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
│ │ Server │ │ Server Actions │ │ API Routes │ │
│ │ Components │ │ (Form-Mut.) │ │ (REST) │ │
│ └────────────────┘ └─────────────────┘ └──────────────┘ │
│ ↓ ↓ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Supabase JS Client (anon + cookie auth) │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
↓ SQL over TLS
┌──────────────────────────────────────────────────────────────┐
│ Supabase (PostgreSQL 15+) │
│ ┌──────────────┐ ┌─────────────┐ ┌────────────────────┐ │
│ │ Tables │ │ RLS │ │ PL/pgSQL │ │
│ │ + Indices │ │ Policies │ │ Functions / │ │
│ │ │ │ │ │ Triggers │ │
│ └──────────────┘ └─────────────┘ └────────────────────┘ │
│ ┌──────────────┐ ┌─────────────┐ │
│ │ PostgREST │ │ Storage │ (Cover-Bilder, │
│ │ (Auto-API) │ │ (S3-API) │ Avatare, Story-Media) │
│ └──────────────┘ └─────────────┘ │
└──────────────────────────────────────────────────────────────┘
Wichtige Architektur-Entscheidungen
- Next.js App-Router mit Server Components für Datenladen, Client Components für Interaktivität
- Row-Level-Security (RLS) auf jeder Tabelle – keine Business-Logik im Middleware-Layer
- SECURITY DEFINER-Funktionen für komplexe Berechtigungsprüfungen (vermeidet RLS-Rekursionen)
- Storage-URLs statt Buckets in DB: Cover-Bilder werden in
event-coversBucket gespeichert, URL in Tabelle referenziert - Magic-Link-Auth statt klassischer Username/Passwort – niedrigere Hürde für neue User
- Multi-Step-Wizard statt langer Formulare – bessere UX, schrittweise Validierung
- Changelog im UI (
/changelog) und im Code (CHANGELOG.md) – dokumentiert jede Änderung
Datenbank
Schema-Übersicht
Die DB besteht aus 18 Kern-Tabellen und mehreren Helper-Tabellen:
| Tabelle | Zweck |
|---|---|
profiles |
User-Profile (1:1 mit Supabase Auth) mit Rolle (user/moderator/admin/editor) |
events |
Veranstaltungen mit Modus (standard/machbar), Adresse, Datum, Veranstalter |
event_registrations |
Anmeldungen zu Events (interne/externe) |
event_comments |
Kommentare unter Events |
event_updates |
Veranstalter-Updates / Änderungs-Historie |
machbar_tasks |
Aufgaben innerhalb einer MachBar-Veranstaltung |
machbar_signups |
User-Anmeldungen für konkrete Aufgaben |
stories |
Geschichten / Blog-Posts mit Cover-Bild und Rich-Text-Body |
story_media |
Bilder/Videos pro Story |
story_social_links |
Social-Media-Verknüpfungen pro Story |
co_organizers |
Mitveranstalter (verknüpfbar mit User-Konto via user_id) |
social_links |
Social-Media-Links pro Event (Instagram, Mastodon…) |
network_entries |
Netzwerk-Einträge (Bündnisse, Initiativen) |
event_reports |
Missbrauchsmeldungen |
filtered_keywords |
Wort-Filter für Auto-Moderation |
widgets |
Dashboard-Widgets (Admin) |
changelog_entries |
Versions-Einträge für die öffentliche Changelog-Page |
event_attendees |
MachBar-Anmeldungen „Ich bin dabei" |
Migrationen
Das Projekt nutzt ein nummeriertes Migrations-System: db/migrations/0001_*.sql bis db/migrations/0032_*.sql. Jede Migration ist idempotent (verwendet if exists / create or replace), sodass sie mehrfach ausgeführt werden kann.
pnpm db:push läuft alle SQL-Files alphabetisch gegen die Supabase-DB.
Row-Level-Security (RLS) Policies
Jede Tabelle hat mehrere Policies für SELECT/INSERT/UPDATE/DELETE. Beispiele:
- Events: User sehen veröffentlichte Events, Ersteller sehen alle eigenen, Admins/Moderatoren sehen alles
- Stories: Ähnlich – veröffentlichte sichtbar, Autoren + Editors sehen eigene Drafts
- Co-Organizers: Veranstalter verwalten ihre Co-Organisatoren, Admins/Moderatoren alles
- Event-Attendees: User sehen nur eigene, Veranstalter + Co-Organisatoren sehen alle
Rekursions-Schutz: Wo Policies sich gegenseitig referenzieren würden, kommen SECURITY DEFINER-Funktionen zum Einsatz (siehe Migration 0032).
Authentifizierung & Rollen
Rollenmodell
| Rolle | Berechtigungen |
|---|---|
| user (default) | Eigene Events anlegen, Events ansehen, Stories lesen, sich für MachBar-Veranstaltungen eintragen |
| editor | Wie User + Stories verfassen und veröffentlichen |
| moderator | Wie Editor + Missbrauchsmeldungen prüfen, Kommentare ausblenden, Events prüfen |
| admin | Wie Moderator + User-Verwaltung, alle Events/Stories bearbeiten, Plattform-Konfiguration |
Auth-Flow
- User gibt E-Mail ein → System sendet OTP-Code (6-stellig) per Mail
- Alternativ: Magic-Link direkt im Postfach
- Bei Supabase wird ein
auth.users-Eintrag erzeugt - Postgres-Trigger legt automatisch einen Eintrag in
profilesmit Rolleuseran - JWT-Token wird in HTTP-only-Cookies gespeichert
- Bei jedem Request wird der User per Cookie identifiziert
Sicherheits-Layer
- RLS = Source-of-Truth für Berechtigungen (nicht Middleware!)
- JWT mit kurzer Lebensdauer, automatische Refresh
- HTTP-only + SameSite=Lax Cookies verhindern XSS-Cookie-Stealing
- CORS strikt konfiguriert
- Rate-Limiting (geplant für nächste Version)
Projektstruktur
app/
├── db/
│ └── migrations/ # 32 SQL-Migrations-Dateien
├── scripts/
│ └── db-push.mjs # Migration-Runner
├── src/
│ ├── app/ # Next.js App-Router
│ │ ├── (public routes)
│ │ │ ├── page.tsx # Home / Landing
│ │ │ ├── events/ # Event-Listing + Detail + Create
│ │ │ ├── stories/ # Stories-Listing + Detail + Create/Edit
│ │ │ ├── network/ # Netzwerk-Listing
│ │ │ ├── about/ hilfe/ impressum/ datenschutz/ barrierefreiheit/
│ │ │ ├── changelog/ # Öffentlicher Changelog
│ │ │ └── login/ # Auth-Pages
│ │ ├── (authenticated routes)
│ │ │ ├── my-events/ # Eigene Events verwalten
│ │ │ ├── profile/ # Eigenes Profil
│ │ │ └── admin/ # Admin-Panel
│ │ ├── api/ # REST-API-Endpoints
│ │ │ ├── auth/ # signin, signup, otp, reset
│ │ │ ├── events/ # CRUD + co-organizers, social-links, attendees
│ │ │ ├── stories/ # CRUD + media, social-links
│ │ │ ├── admin/ # Admin-spezifische Endpoints
│ │ │ ├── geocode/ # Nominatim-Proxy
│ │ │ └── profiles/ # User-Suche
│ │ ├── layout.tsx # Root-Layout mit Theme-Provider
│ │ ├── globals.css # Globale Styles, CSS-Variablen
│ │ ├── icon.svg # Favicon (auto-generiert)
│ │ └── apple-icon.svg # Apple Touch-Icon
│ ├── components/
│ │ ├── layout/ # Header, Footer, BottomNav, ThemeToggle
│ │ ├── event/ # EventCard, EventWizard, EventShareBar
│ │ ├── story/ # StoryCard, StoryEditor
│ │ ├── network/ # NetworkCard
│ │ ├── editor/ # RichTextEditor (TipTap)
│ │ ├── map/ # MapPicker (Leaflet)
│ │ ├── admin/ # AdminShell, AdminMobileNav
│ │ ├── auth/ # Auth-spezifische Komponenten
│ │ ├── profile/ # Profil-Komponenten
│ │ ├── shared/ # Geteilte Komponenten
│ │ └── ui/ # Generische UI-Bausteine (Button, Input, Card, Badge…)
│ ├── lib/
│ │ ├── auth/ # Session-Helper, Organizer-Context
│ │ ├── logging/ # Strukturiertes Logging (pino)
│ │ ├── supabase/ # Client-Setups (server, client, service-role)
│ │ ├── validation/ # Zod-Schemas + Wizard-Validator
│ │ ├── api/ # safeFetch-Helper für API-Calls
│ │ ├── motion.ts # Framer-Motion Easings
│ │ └── utils/ # cn(), date-format, etc.
│ ├── types/ # TypeScript-Types (Database-Types)
│ └── middleware.ts # Auth-Middleware für geschützte Routen
├── package.json
├── next.config.mjs
├── tailwind.config.ts
├── tsconfig.json
└── CHANGELOG.md
Konventionen
- Jede Page in
app/src/app/...= eine URL-Route page.tsx= die Page selbstlayout.tsx= Wrapper, der alle Child-Pages umschließtloading.tsx= Suspense-Fallbackerror.tsx= Error-Boundarynot-found.tsx= 404-Handler- API-Routes =
route.tsmit exportierten HTTP-Method-Handlern
Lokale Entwicklung
Voraussetzungen
- Node.js 18.17+ (empfohlen: 20 LTS)
- pnpm 10+ (
npm install -g pnpm) - Git
- Supabase-Konto (Free Tier reicht) ODER lokale Postgres-Instanz
Schritt 1: Repository klonen
git clone <repository-url> buendnis-ost
cd buendnis-ost/app
Schritt 2: Dependencies installieren
pnpm install
Schritt 3: Supabase-Projekt erstellen
- Gehe zu supabase.com und erstelle ein neues Projekt
- Notiere dir:
- Project URL (
https://xyz.supabase.co) - Anon Key (öffentlich, im Browser sichtbar)
- Service Role Key (geheim, nur serverseitig)
- Database Password (für direkten DB-Zugriff)
- Project URL (
Schritt 4: Environment-Variablen konfigurieren
Erstelle app/.env.local:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xyz.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
SUPABASE_PROJECT_REF=xyz
SUPABASE_DB_PASSWORD=...
# Optional
NEXT_PUBLIC_SITE_URL=http://localhost:3000
Erstelle auch app/.mavis/secrets/buendnis-ost.env (für pnpm db:push):
SUPABASE_PROJECT_REF=xyz
SUPABASE_DB_PASSWORD=...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
Schritt 5: Datenbank-Migrationen ausführen
pnpm db:push
Dies führt alle 32 SQL-Migrations-Dateien gegen deine Supabase-DB aus.
Schritt 6: Dev-Server starten
pnpm dev
Die App läuft jetzt auf http://localhost:3000.
Schritt 7: Type-Check & Build
# TypeScript-Validierung
pnpm typecheck
# Production-Build
pnpm build
# Linting
pnpm lint
Häufige Probleme
| Problem | Lösung |
|---|---|
relation does not exist |
pnpm db:push ausführen |
permission denied for table |
RLS-Policies prüfen, ggf. als service_role anmelden |
Cookie ... has been rejected for invalid domain |
.env.local prüfen, NEXT_PUBLIC_SITE_URL setzen |
404 auf /icon.svg |
Browser-Cache leeren, Dev-Server neu starten |
infinite recursion detected in policy |
Sicherstellen, dass Migration 0032 ausgeführt wurde |
Datenbank-Migrationen
Eine neue Migration hinzufügen
- Erstelle
app/db/migrations/00XX_beschreibung.sql(nächste freie Nummer) - Schreibe idempotentes SQL:
create table if not exists,drop policy if exists,create or replace function - Teste mit
pnpm db:push - Trage den Changelog-Eintrag in
app/CHANGELOG.mdund ggf. alschangelog_entries-Eintrag in der Migration ein - Committe mit aussagekräftiger Message
Konventionen
- Niemals eine bestehende Migration ändern, nachdem sie gepusht wurde
- Wenn ein Fix nötig ist: neue Migration erstellen
- Eine thematische Änderung pro Migration (z.B. „neue Tabelle", „neue RLS-Policy", „neue Spalte")
NOTIFY pgrst, 'reload schema'am Ende, damit PostgREST das Schema neu lädt
Testing (E2E)
Die Plattform nutzt Playwright für End-to-End-Tests mit echtem Browser-Rendering, plus einen strukturierten winston-Logger für detaillierte Test-Reports.
Test-Suiten
| Datei | Zweck | Anzahl Tests |
|---|---|---|
tests/e2e/01-public-pages.spec.ts |
Smoke-Tests: Home, Events, Stories, Network, Changelog, Impressum, Datenschutz, Barrierefreiheit, Hilfe | ~15 |
tests/e2e/02-auth.spec.ts |
Login-Page, Magic-Link-Flow, Session-Persistenz, geschützte Routen, Admin-Zugriff | ~7 |
tests/e2e/03-event-wizard.spec.ts |
10-Schritte-Event-Erstellung, Hybrid-Standort, max_participants | ~4 |
tests/e2e/04-features.spec.ts |
Co-Org-E-Mail-Verknüpfung, MachBar-Anmeldung, Theme-Toggle | ~7 |
tests/e2e/05-mobile.spec.ts |
Mobile Bottom-Nav, Sticky-Header, kein horizontaler Scroll | ~3 |
tests/e2e/06-api.spec.ts |
Direkte API-Tests (ohne Browser): 401/400-Checks, Geocoding | ~5 |
Schnellstart
cd app
# 1. Test-User anlegen (idempotent, einmalig)
node -e "
import('./scripts/_setup_test_users.mjs').then(m => m.default());
" || true
# 2. Alle Browser installieren
pnpm exec playwright install chromium firefox webkit
# 3. Tests starten (Dev-Server läuft automatisch)
pnpm test
# 4. Nur Chromium (schnell)
pnpm test:chromium
# 5. Mit UI
pnpm test:ui
# 6. Mit sichtbarem Browser
pnpm test:headed
# 7. HTML-Report öffnen
pnpm test:report
Test-Output
Während eines Test-Runs werden drei Arten von Output erzeugt:
1. Log-Dateien (tests/log/)
test-runner-w{0..N}.log– pro Worker, mit allen Events (Start, Schritte, Browser-Console, Network)test-run-<timestamp>.log– persistent pro Run, mit vollständigem Debug-Output- Format:
[Timestamp] [LEVEL] Message {"meta": "json"}
Beispiel:
[2026-06-10 23:28:45.426] [INFO ] ▶ STEP START: API: profile-search unauthorized
[2026-06-10 23:28:45.592] [INFO ] ◀ STEP END: API: profile-search unauthorized {"duration":"167ms","status":401}
[2026-06-10 23:28:45.601] [INFO ] ✓ 01-public-pages > Public Page lädt: /events {"duration":"1189ms"}
2. HTML-Report (tests/reports/html/index.html)
- Interaktiver Report mit allen Tests, Traces, Videos, Screenshots
pnpm test:reportöffnet ihn im Browser
3. Screenshots & Traces
tests/screenshots/– Screenshot pro Test (zur visuellen Verifikation)tests/traces/– Playwright-Trace (für Debugging fehlgeschlagener Tests)
Test-Architektur
tests/helpers/logger.ts – Strukturierter Logger (winston)
logger.info(),.warn(),.error(),.debug()– Standard-APItestStep('Name')– bündelt mehrere Logs zu einem benannten Test-Schritt mit Timingsummary– sammelt Test-Ergebnisse, gibt finale Zusammenfassung ausTestSummary.printSummary()– Tabelle mit Pass/Fail/Skip-Counts am Test-Run-Ende
tests/helpers/auth.ts – Auth-Helper
magicLinkSignIn(email)– generiert Magic-Link via service-role, loggt User ein, gibt Storage-State zurückensureTestUsers()– legttest-admin@,test-moderator@,test-user@buendnisost.localangetAdminClient()– Service-Role-Client (Bypass RLS, für Test-Setup)
tests/helpers/fixtures.ts – Custom Fixtures
basePage/baseContext– frischer Browser ohne LoginadminPage/moderatorPage/userPage– bereits eingeloggte Pages- Automatisches Console- und Network-Logging
- Test-Summary-Tracking via
test.afterEach() - Cleanup: löscht Test-Daten mit Prefix
[E2E]intest.afterAll()
playwright.config.ts – Konfiguration
- 5 Projekte: chromium, firefox, webkit, mobile-chrome (Pixel 7), mobile-safari (iPhone 15)
- Parallel-Tests lokal (default), 1 Worker in CI für Stabilität
- Auto-Server:
pnpm devstartet vor Tests - Trace + Video nur bei Failures
- HTML-, JSON-, JUnit-Reporter
Was wird getestet?
Kern-Funktionalität (jeder Commit sollte diese Tests bestehen):
- ✅ Alle Public-Pages laden (200, korrekter Title, kein Console-Error)
- ✅ Auth: Magic-Link-Request, Session-Persistenz, geschützte Routen
- ✅ Event-Wizard: kompletter 10-Schritte-Flow, Hybrid-Standort, max_participants
- ✅ Co-Org-E-Mail: Auto-Verknüpfung existierender Konten, Magic-Link-Einladung für externe
- ✅ MachBar: "Ich bin dabei"-Button, Anmeldung, Liste
- ✅ Theme-Toggle: Light/Dark-Mode, localStorage-Persistenz
- ✅ Mobile: Sticky-Header, Bottom-Nav, kein horizontaler Scroll
- ✅ API: 401/400/500-Checks, Geocoding, Profile-Lookup
Cleanup: Test-Daten mit Prefix [E2E] werden nach jedem Run aus der DB entfernt (Events soft-deleted, Stories status=deleted).
CI-Integration
.github/workflows/e2e.yml läuft die Tests automatisch bei jedem Push/PR auf main oder develop:
- Setup Node 20 + pnpm 10
- Install Dependencies
- Install Playwright-Browser (chromium, firefox, webkit + OS-Dependencies)
- Build (
pnpm build) - Run E2E Tests (chromium only für Geschwindigkeit)
- Upload Artifacts (HTML-Report, Logs, Traces)
Reports werden als GitHub-Artifacts für 14 Tage gespeichert.
Eigene Tests schreiben
import { test, expect } from '../helpers/fixtures';
import { testStep } from '../helpers/logger';
test('Mein neuer Test', async ({ adminPage }) => {
const step = testStep('Beschreibung');
await adminPage.goto('/admin');
// ... Test-Logik
step.end({ status: 'PASS' });
});
Tipps:
- Nutze
adminPage/userPagefür authentifizierte Tests - Nutze
testStep()für aussagekräftige Step-Logs - Cleanup: Daten mit Prefix
[E2E]werden automatisch entfernt - Mobile-Tests: nutze
testInfo.project.name.includes('mobile')für konditionale Logik
Deployment
Empfohlene Plattformen
- Vercel – First-Class-Support für Next.js
- Netlify – Ebenfalls gute Next.js-Unterstützung
- Eigener Node.js-Server – via
pnpm build && pnpm start
Vercel-Deployment
- Vercel-Konto mit GitHub verbinden
- Repository importieren
- Build-Settings:
- Framework: Next.js
- Build Command:
cd app && pnpm install && pnpm build - Output Directory:
app/.next - Install Command:
cd app && pnpm install
- Environment-Variablen aus
.env.localin Vercel-Dashboard übertragen - Erstes Deployment: Migrations manuell via Supabase-Studio oder
pnpm db:pushausführen - Custom Domain (optional): in Vercel-Settings hinzufügen
Pre-Deployment-Checklist
pnpm buildläuft ohne Fehler durchpnpm typecheckist sauber- Alle Migrations sind in der Supabase-DB angewendet
NEXT_PUBLIC_SITE_URLzeigt auf die Production-Domain- Supabase Email-Templates sind für die eigene Domain konfiguriert
- CORS in Supabase erlaubt die Production-Domain
- Redirect-URLs in Supabase Auth sind gesetzt
API-Übersicht
Alle API-Endpoints leben unter /api/... und nutzen den Next.js Route Handler. Vollständige OpenAPI-Dokumentation ist geplant.
Auth
| Method | Path | Zweck |
|---|---|---|
| POST | /api/auth/signin |
OTP-Code anfordern |
| POST | /api/auth/otp-verify |
OTP-Code prüfen, Session erstellen |
| POST | /api/auth/magic-link |
Magic-Link per Mail senden |
| POST | /api/auth/signup |
Neuen Account anlegen |
| POST | /api/auth/signout |
Session beenden |
| POST | /api/auth/reset-password |
Passwort-Reset anfordern |
Events
| Method | Path | Zweck |
|---|---|---|
| GET | /api/events |
Events auflisten (mit Filtern) |
| POST | /api/events |
Event erstellen |
| GET | /api/events/[id]/co-organizers |
Mitveranstalter auflisten |
| POST | /api/events/[id]/co-organizers |
Mitveranstalter hinzufügen |
| PATCH/DELETE | /api/events/[id]/co-organizers/[coId] |
Mitveranstalter bearbeiten/löschen |
| GET | /api/events/[id]/attendees |
MachBar-Anmeldungen (nur Veranstalter) |
| POST | /api/events/[id]/attendees |
Selbst anmelden (MachBar) |
| GET/PATCH/DELETE | /api/events/[id]/attendees/me |
Eigene Anmeldung verwalten |
| POST | /api/events/upload |
Cover-Bild hochladen |
| GET | /api/geocode?q=... |
Adresse → Koordinaten (Nominatim-Proxy) |
Stories
| Method | Path | Zweck |
|---|---|---|
| GET/POST | /api/stories |
Stories listen/erstellen |
| GET/PATCH/DELETE | /api/stories/[id] |
Einzelne Story verwalten |
| POST | /api/stories/upload |
Story-Media hochladen |
| GET/POST/DELETE | /api/stories/media/[id] |
Story-Media verwalten |
| GET/POST/DELETE | /api/stories/social-links/[id] |
Story-Social-Links verwalten |
Profile & Suche
| Method | Path | Zweck |
|---|---|---|
| GET | /api/profile |
Eigenes Profil abrufen/aktualisieren |
| GET | /api/profiles/search?q=... |
User-Autocomplete (für Mitveranstalter-Auswahl) |
Admin
| Method | Path | Zweck |
|---|---|---|
| GET/PATCH/DELETE | /api/admin/users[/{id}] |
User-Verwaltung |
| GET/PATCH/DELETE | /api/admin/events/{id} |
Event-Verwaltung |
| GET/PATCH/DELETE | /api/admin/keywords[/{id}] |
Wort-Filter |
| GET/PATCH/DELETE | /api/admin/widgets[/{id}] |
Dashboard-Widgets |
| GET/POST/PATCH/DELETE | /api/admin/network[/{id}] |
Netzwerk-Einträge |
| POST | /api/admin/network/{id}/approve / reject |
Netzwerk-Freigabe |
| POST | /api/admin/reports/{id}/resolve |
Report abschließen |
| POST | /api/admin/reports/{id}/event/{eventId}/hide |
Event verbergen |
Alle geschützten Endpoints erfordern ein gültiges JWT-Cookie. Fehler werden im JSON-Format zurückgegeben:
{ "error": "Beschreibung", "requestId": "req_abc123" }
Sicherheit
Verteidigungs-Linien
- JWT-Auth (Supabase Auth, automatische Rotation)
- HTTP-only Cookies mit
SameSite=Lax(kein XSS-Cookie-Stealing) - Row-Level-Security auf Datenbank-Ebene (Defense in Depth)
- Zod-Validierung aller API-Inputs (verhindert Injection & Bad-Data)
- CORS strikt konfiguriert
- Rate-Limiting (geplant)
- Content-Security-Policy (geplant)
- Storage-Policies auf Supabase-Buckets (User können nur eigene Bilder hochladen)
Bekannte Limitierungen (geplant zu beheben)
- Kein Captcha auf Auth-Endpoints (Anfälligkeit für Spam)
- Kein explizites Rate-Limiting (Supabase hat built-in, aber fein-granular fehlt)
- CSP-Header sind nicht gesetzt
- Audit-Log ist nur für Admin-Aktionen, nicht für User-Aktionen
Daten-Backup
- Supabase Cloud: automatische tägliche Backups (7 Tage Retention im Free Tier)
- Für kritische Daten: wöchentlicher Export via
pg_dumpempfohlen
Mitwirken
Code-Stil
- TypeScript strict mode – keine
any, keine@ts-ignoreohne Kommentar - Tailwind only – kein separates CSS, außer in
globals.cssfür Base-Layer - Komponenten klein und fokussiert – eine Komponente, eine Verantwortlichkeit
- Server Components first – Client Components nur wenn nötig (Interaktivität, Browser-APIs)
- i18n-ready – alle User-Strings in deutscher Sprache; Architektur erlaubt spätere Lokalisierung
Commit-Konventionen
- Deutsch oder Englisch – egal, aber konsistent pro Projekt
- Präfixe für Kategorien:
feat:,fix:,chore:,docs:,refactor:,style: - Beispiele:
feat: MachBar-Anmeldung implementierenfix: RLS-Rekursion in events-Policies auflösenchore: Next.js auf 15.5.19 updaten
Pull-Request-Workflow
- Branch erstellen:
git checkout -b feat/mein-feature - Änderungen committen mit aussagekräftigen Messages
- Sicherstellen, dass
pnpm typecheckundpnpm builddurchlaufen - Falls DB-Änderungen: Migration hinzufügen + in PR-Beschreibung dokumentieren
- PR erstellen mit Beschreibung (Was? Warum? Wie getestet?)
- Review abwarten
Issue-Triagen
- Bugs: bitte mit Reproduktions-Schritten, Browser, OS, Screenshot
- Feature-Wünsche: gerne mit Use-Case und Mockups
- Sicherheitslücken: bitte nicht öffentlich, sondern direkt per Mail an die Maintainer
Lizenz
MIT License – siehe LICENSE-Datei (zu erstellen).
Kurzfassung: Du darfst den Code nutzen, verändern, weitergeben – solange der Copyright-Hinweis und die Lizenz mitgegeben werden. Keine Haftung, keine Garantie.
Anhänge
A. Datenbank-Schema visualisiert
profiles ─┐
├─< events ─< co_organizers >─ profiles
│ │ (mit user_id-Verknüpfung)
│ ├─< event_registrations >─ profiles
│ ├─< event_comments >─ profiles
│ ├─< event_updates
│ ├─< event_reports
│ ├─< event_attendees >─ profiles
│ ├─< machbar_tasks ─< machbar_signups >─ profiles
│ ├─< social_links
│ └─< cover_image_url (Storage)
│
├─< stories ─< story_media
│ └─< story_social_links
│
└─< network_entries
(mit approval-status)
changelog_entries (public read, admin/mod write)
filtered_keywords (admin manage)
widgets (admin manage)
B. Rollen-Hierarchie
┌──────────┐
│ admin │ (alle Rechte)
└─────┬────┘
┌───────┴───────┐
┌───┴────┐ ┌─────┴────┐
│moderator│ │ editor │ (Stories verwalten)
└────┬────┘ └────┬────┘
│ │
└───────┬───────┘
│
┌────┴────┐
│ user │ (Default)
└─────────┘
C. Farbschema
Die Plattform nutzt drei Brand-Farben (HSL-definiert in globals.css):
- Primary Blue (
217 91% 60%) – Hauptaktionen, Links - Magenta/Pink (
338 65% 38%) – MachBar-Veranstaltungen, sekundäre Aktionen - Red (
0 84% 60%) – Fehler, destruktive Aktionen
Plus Dark Mode mit angepassten Kontrasten für Lesbarkeit.
D. Performance-Optimierungen
- Server Components für datenintensive Pages (kein JS-Bundle für nicht-interaktive Inhalte)
- React Query für Client-Side-Caching wo nötig
- Image-Optimierung via Next.js
<Image>-Komponente - Code-Splitting automatisch durch Next.js
- Font-Loading via
next/font(kein FOUT) - Critical CSS automatisch inline durch Next.js
- Bundle-Größen werden im Build-Report überwacht (siehe
pnpm build-Output)
Kontakt
- Issues: GitHub-Issues
- Diskussionen: GitHub-Discussions
- Sicherheit: siehe
SECURITY.md(geplant) - E-Mail: siehe Plattform-Impressum unter
/impressum
Letzte Aktualisierung: Juni 2026
Version: 0.1.0 (siehe CHANGELOG.md für Details)