No description
  • TypeScript 99%
  • CSS 0.8%
  • Dockerfile 0.1%
Find a file
Claude 9930531de3
All checks were successful
build / images (server, server) (push) Successful in 18s
build / images (web, web) (push) Successful in 19s
check / verify (push) Successful in 1m37s
v0.1.116: Befangenheit in Rangfolge-Runden, Aufräum-Jobs, Stripe-Betrag aus Session, SVG-Regeln, CI-Retry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HM4hgT1s3Nj29F2LS9jW6
2026-10-02 08:19:35 +02:00
.forgejo/workflows v0.1.116: Befangenheit in Rangfolge-Runden, Aufräum-Jobs, Stripe-Betrag aus Session, SVG-Regeln, CI-Retry 2026-10-02 08:19:35 +02:00
docs v0.1.116: Befangenheit in Rangfolge-Runden, Aufräum-Jobs, Stripe-Betrag aus Session, SVG-Regeln, CI-Retry 2026-10-02 08:19:35 +02:00
infra/proxy v0.1.114: Mehrere Instanzen (Redis), Upload-Härtung, Weiterleiten-Transaktion, DSGVO-Co-Autoren, Spracheinstellung 2026-10-02 07:05:52 +02:00
server v0.1.116: Befangenheit in Rangfolge-Runden, Aufräum-Jobs, Stripe-Betrag aus Session, SVG-Regeln, CI-Retry 2026-10-02 08:19:35 +02:00
web v0.1.116: Befangenheit in Rangfolge-Runden, Aufräum-Jobs, Stripe-Betrag aus Session, SVG-Regeln, CI-Retry 2026-10-02 08:19:35 +02:00
.env.example v0.1.115: Befunde der unabhängigen Code-Review und letzte offene Punkte 2026-10-02 07:55:33 +02:00
.gitignore v0.1.113: Fehlerbehebungen aus Doku-Durchsicht und Staging 2026-10-02 00:06:24 +02:00
CHANGELOG.md v0.1.116: Befangenheit in Rangfolge-Runden, Aufräum-Jobs, Stripe-Betrag aus Session, SVG-Regeln, CI-Retry 2026-10-02 08:19:35 +02:00
docker-compose.portainer.yml v0.1.114: Mehrere Instanzen (Redis), Upload-Härtung, Weiterleiten-Transaktion, DSGVO-Co-Autoren, Spracheinstellung 2026-10-02 07:05:52 +02:00
docker-compose.yml v0.1.115: Befunde der unabhängigen Code-Review und letzte offene Punkte 2026-10-02 07:55:33 +02:00
README.md v0.1.113: Fehlerbehebungen aus Doku-Durchsicht und Staging 2026-10-02 00:06:24 +02:00
stack.env v0.1.116: Befangenheit in Rangfolge-Runden, Aufräum-Jobs, Stripe-Betrag aus Session, SVG-Regeln, CI-Retry 2026-10-02 08:19:35 +02:00

Juro

Selbstgehostete Awards-/Jurierungs-Plattform — angelehnt an Evalato, aber bewusst schlank: ein Stack pro Event, keine Mandanten. Einreichungen sammeln, Dateien nach S3, Jurierung mit Normalisierung, Ergebnisse exportieren. Komplett über Docker deploybar, Worker horizontal skalierbar.

Architektur

            ┌─────────┐         ┌──────────────┐
  Browser ──┤  proxy  ├── /api ─┤  api (NestJS)│── Postgres
            │ (nginx) │         │  Session=Redis│── Redis (Sessions + Queues)
            └────┬────┘         └──────┬────────┘── S3 (Presigned Uploads)
                 │ /                   │ enqueue
            ┌────┴────┐          ┌─────┴───────────────┐
            │   web   │          │ worker × N (BullMQ) │── S3 / SMTP
            │ (React) │          │ email·attachment·   │
            └─────────┘          │ export              │
                                 └─────────────────────┘
  • api und worker teilen sich ein Docker-Image (juro-server), nur der Startbefehl unterscheidet sich (dist/main.js vs. dist/worker.js). Mehr Worker = mehr Container desselben Images.
  • Uploads gehen per Presigned-URL direkt Browser → S3. Die API überträgt keine Dateibytes; ein Worker bestätigt anschließend den Upload und verarbeitet ihn nach (Platz für Thumbnails/Transcoding via ffmpeg).
  • Single-Event: Es gibt kein Tenant-Feld. Settings ist eine Singleton-Zeile, die Branding und Event-weite Schalter hält und im Admin-Bereich editierbar ist.

Stack

NestJS · Prisma · PostgreSQL · Redis · BullMQ · AWS SDK v3 (S3/RustFS) · React · Vite · Mantine · AG Grid · nginx · Forgejo Actions.

Funktionsumfang

Einreichung & Formulare

  • Konfigurierbarer Formular-Builder (Drag & Drop, Feldtypen, Pflicht/öffentlich/identifizierend), Live-Vorschau
  • Geführter Einreichungs-Wizard, Datei-Uploads per Presigned-URL, Einsendeschluss mit Auto-Schließung
  • Einreicher-Portal (Magic-Link): eigene Beiträge einsehen, bearbeiten, zurückziehen; Deadline-Erinnerungs-Mails

Jurierung

  • Runden mit Bewertungsarten (Punkte, Sterne, Ja/Nein, Bestanden/Nicht, Ranking, nur Kommentar)
  • Gewichtete Kriterien-Rubrics, blinde Jurierung, Per-Kategorie-Juror-Scope, Interessenkonflikt-Flag
  • Z-Score-Normalisierung je Juror, gewichtetes Gesamtergebnis, CSV-Export
  • Insights-Dashboard: Status-Verteilung, Score-Histogramme, Juror-Strenge, höchste Uneinigkeit

Ergebnisse & Öffentlichkeit

  • Veröffentlichte Ergebnisseite, öffentliche Beitrags-Detailseiten, Gewinner-Zertifikate (PDF)
  • Ergebnis-Benachrichtigung per E-Mail, öffentliches Voting (E-Mail-verifiziert, optional Turnstile-Captcha / Allowlist)
  • Bild-Thumbnails (sharp) für Galerie/Voting/Ergebnisse

Verwaltung & Betrieb

  • Rollen: Admin, Chair (Koordinator), Juror, Einreicher; Nutzer-Einladungen, 2FA (TOTP + Recovery-Codes)
  • Audit-Log, DSGVO-Werkzeuge (Auskunft-Export & Löschung), anpassbare E-Mail-Vorlagen
  • Optionale Einreichgebühren via Stripe (Feature-Flag), Live-Kommentare/Presence/Spotlight (Socket.IO)
  • Reiches Erscheinungsbild (Farben, Typo, Dark/Light/Auto, CSS-Variablen, eigenes CSS, Mantine-Theme-JSON)
  • Mehrsprachig (DE/EN, Deutsch als Standard), Health/Readiness-Checks, optionales Error-Tracking (Sentry)

Go-Live

Vor dem ersten echten Event: docs/GO-LIVE.md durchgehen (Checkliste für Infrastruktur, Konfiguration, Funktionsdurchlauf und Betrieb).

Dokumentation

Ausführliche Handbücher unter docs/: Anwenderhandbuch (PDF) · Admin-/Betriebshandbuch · Entwickler-Dokumentation.

Alle URLs (Teilnehmer, Jury, Öffentlich, Verwaltung) sind in docs/LINKS.md dokumentiert. Dieselbe Liste mit Kopier-Buttons und einer „Alle als Text kopieren"-Funktion findet das Admin-/Chair-Team in der App unter Admin → Links (/admin/links).

  • Anleitung & typischer Ablauf: direkt in der App unter Admin → Anleitung (/admin/help), zweisprachig (DE/EN) und in Abschnitte gegliedert – inkl. „Beispiele: 3 typische Anwendungen". Einzige Quelle: die Markdown-Abschnitte unter web/src/content/guide/ (Dateien NN-slug.de.md / NN-slug.en.md).
  • Sicherheitsmaßnahmen: docs/SECURITY.md

Schnellstart (lokal)

cp .env.example .env          # ggf. anpassen
# SESSION_SECRET muss gesetzt sein (die API startet sonst nicht):
sed -i "s/^SESSION_SECRET=.*/SESSION_SECRET=$(openssl rand -hex 32)/" .env
docker compose up --build

Dann:

Beim ersten Start migriert der API-Container die DB und seedet Admin + Default-Form.

Worker skalieren

docker compose up -d --scale worker=4

Jeder Worker zieht Jobs aus denselben Redis-Queues (email, attachment, export). Failover und Retries (3×, exponentielles Backoff) sind in jobs.service.ts gesetzt.

Deploy & Releases (Forgejo)

Tags werden per Action erzeugt, nicht von Hand:

  1. In Forgejo Actions → release → Run workflow ausführen, Bump (patch/minor/major) wählen. Die Action legt das nächste vX.Y.Z-Tag an und pusht es.
  2. Der Tag-Push triggert build.yml: baut & pusht juro-server und juro-web in die Registry, getaggt mit der Version und latest.
  3. Auf dem Zielhost TAG=vX.Y.Z docker compose pull && docker compose up -d.

Vorab in Forgejo als Secrets hinterlegen: REGISTRY_USER, REGISTRY_TOKEN.

Erster Release: build.yml läuft nur bei einem Tag-Push. Solange noch kein vX.Y.Z-Tag existiert, gibt es keine Images in der Registry. Einmal release ausführen (oder einen Tag pushen) erzeugt sie.

Mit Portainer deployen (fertige Images ziehen)

docker-compose.portainer.yml ist eine eigenständige Variante, die die in der Registry liegenden Images zieht statt sie zu bauen, und keine gemounteten Host-Dateien braucht (der Proxy-Config wird inline injiziert). Damit ist sie copy-paste-tauglich für den Portainer-Web-Editor.

  1. In Portainer Registries → forgejo.thiel.tools mit Benutzer + Token hinzufügen, das thiel/juro-* ziehen darf (private Registry).
  2. Stacks → Add stack → entweder das Repo als Git-Quelle angeben (Compose-Pfad docker-compose.portainer.yml) oder den Inhalt in den Web-Editor einfügen.
  3. Environment bereitstellen. api und worker laden ihre Konfiguration per env_file: stack.env — stack.env enthält alle Variablen mit Defaults.
    • Git-Quelle: unter Environment die Environment file/Env-Datei auf stack.env setzen (liegt im Repo neben der Compose).
    • Web-Editor: die Variablen aus stack.env in den Environment variables-Bereich übernehmen — Portainer schreibt daraus die stack.env, die die Compose dann referenziert. In beiden Fällen die mit >>> CHANGE markierten Werte anpassen — mindestens SESSION_SECRET, ADMIN_PASSWORD und PUBLIC_BASE_URL; ggf. S3_PUBLIC_ENDPOINT (die URL, über die der Browser den Objekt­speicher erreicht — auf einem entfernten Host nicht localhost). TAG ist in stack.env auf eine feste Version gepinnt (vX.Y.Z) – bei Updates hochsetzen; für „immer neueste" auf latest setzen (ohne TAG zieht die Compose ebenfalls latest). Hinweis: S3_PUBLIC_ENDPOINT bleibt im Normalfall leer – dann nutzen die Presigned-URLs PUBLIC_BASE_URL, und der mitgelieferte Proxy reicht den Bucket-Pfad same-origin durch. Nur für einen externen S3-Host setzen.
  4. Deploy. Der API-Container migriert + seedet beim ersten Start automatisch.

Produktion

  • SESSION_SECRET zwingend setzen – mit NODE_ENV=production startet die API bei leerem Wert oder Platzhalter (change-me-in-prod, dev-secret) nicht. S3_* ggf. auf echtes S3 zeigen lassen (S3_FORCE_PATH_STYLE=false bei AWS), dann S3_PUBLIC_ENDPOINT auf die öffentlich erreichbare S3-URL.
  • TRUST_PROXY legt fest, welchen Proxys X-Forwarded-For geglaubt wird. Der Standard (loopback, linklocal, uniquelocal = private Netze) passt zum mitgelieferten nginx, auch mit Traefik/Caddy im selben Docker-Netz davor; alternativ Hop-Anzahl oder Adressliste.
  • TLS terminiert sinnvollerweise eine Ebene über dem proxy (Traefik/Caddy) — dann PUBLIC_BASE_URL=https://…, womit das Session-Cookie automatisch secure wird.

Datenmodell (Kern)

User (ADMIN/CHAIR/JUDGE/ENTRANT) · Settings (Singleton, Branding) · Category → Form → FormField · Entry → Attachment · Round (6 Voting-Typen) · Assignment (Juror↔Entry, mit Conflict-of-Interest) · Score (Z-Score-normalisiert) · Tag.

Was drin ist / Roadmap

Implementiert: Infrastruktur-Stack, Auth/Sessions (inkl. TOTP-2FA), Rollen (Admin/Chair/Judge), Branding-Admin, Kategorien, Formular-Builder, öffentliche Einreichung mit S3-Direct-Upload (inkl. Bild-Downscaling, Cover, Kollaborateure), Einreicher-Portal (Magic-Link), Entries-Grid mit Tagging und Audit-Trail, Jurierungsrunden + Scoring (blind, Kategorien-Scope, Deadlines/Erinnerungen), öffentliches Voting (E-Mail-Verifikation + Turnstile), Galerie und Ergebnisseite (Zertifikate, Badges), PDF-Reports, CSV-/XLSX-Exporte, Video-Transcoding und Thumbnails im Attachment-Worker (ffmpeg/sharp), Partner, Webhooks/API-Keys, Stripe-Einreichgebühren (optional), DSGVO-Export/-Löschung, Live-Chat, i18n (DE/EN), CI mit Tag-Action.

Noch offen: CONCEPT.md ins Repo aufnehmen (Spezifikation aktuell nicht versioniert), DTO-Validierung auf Admin-Endpunkte ausweiten, E2E-/API-Tests gegen eine echte Datenbank.