Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

47 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

kk-sentinel

Überwacht die Zusatzbeiträge der gesetzlichen Krankenkassen und warnt abonnierte Nutzer, solange ihr Sonderkündigungsrecht noch gilt (Ende des Monats der Erhöhung). Hintergrund: Seit dem GKV-Beitragssatzstabilisierungs­gesetz (10.07.2026) sind die Kassen nicht mehr verpflichtet, ihre Mitglieder über eine Erhöhung des Zusatzbeitrags zu informieren.

Stack

Schicht Technologie
Dev-Umgebung DDEV (PHP 8.4, nginx-fpm)
Backend Symfony 7.4 + API Platform 4, JWT (lexik)
Datenbank MariaDB 11.8
Frontend React 19 + Vite + TypeScript, TanStack Query

Entkoppelt: api/ (JSON-API) und frontend/ (SPA). In der Entwicklung proxyt Vite /api an nginx im selben Container — kein CORS im Browser.

Code-Konvention: englische Bezeichner. Domäne übersetzt: HealthInsurer (Krankenkasse), ContributionRate (Zusatzbeitrag-Historie), Feld currentRate; Routen /api/insurers, /api/rates. Sichtbare UI-Texte und E-Mails bleiben deutsch.

Start

ddev start          # Container hoch (einmalig sudo für /etc/hosts)
Zweck URL
Frontend (Vite) https://kk-sentinel.ddev.site:5173
API-Entrypoint https://kk-sentinel.ddev.site/api
API-Doku (OpenAPI) https://kk-sentinel.ddev.site/api/docs
Mailpit https://kk-sentinel.ddev.site:8026

Der Vite-Dev-Server läuft als DDEV-Daemon (.ddev/config.vite.yaml) und startet automatisch mit ddev start.

Setup nach frischem Clone

vendor/, node_modules/ und die JWT-Keys sind bewusst nicht im Repo. Die JWT-Passphrase liegt in .env, daher lassen sich die Keys reproduzierbar neu erzeugen:

ddev start
ddev exec 'cd api && composer install'
ddev exec 'cd api && php bin/console lexik:jwt:generate-keypair'
ddev exec 'cd api && php bin/console doctrine:migrations:migrate --no-interaction'
ddev exec 'cd api && php bin/console app:crawl:gkv'   # 93 Kassen + Beiträge laden
ddev exec 'cd api && php bin/console app:create-admin admin@kk-sentinel.ddev.site "ChangeMe123!"'
ddev exec 'cd frontend && npm install'

Nützliche Befehle

# Backend (im Container)
ddev exec 'cd api && php bin/console make:migration'
ddev exec 'cd api && php bin/console doctrine:migrations:migrate'
ddev exec 'cd api && php bin/console app:crawl:gkv'            # GKV-Zentralliste crawlen
ddev exec 'cd api && php bin/console app:alerts:dispatch'      # Warn-Mails versenden (idempotent)
ddev exec 'cd api && php bin/console app:create-admin <mail> <pw>'
ddev exec 'cd api && php bin/console app:seed-insurers'        # nur Offline-Fallback (Platzhalter)

# Messenger-Worker (async Crawl-Jobs + täglicher Scheduler).
# Läuft in DDEV automatisch als Daemon (.ddev/config.vite.yaml); manuell:
ddev exec 'cd api && php bin/console messenger:consume async scheduler_main -v'

# Frontend
ddev exec 'cd frontend && npm run build'

Datenfluss

  1. Crawler (app:crawl:gkv) lädt die GKV-Spitzenverband-Zentralliste (alle 93 Kassen, § 242 Abs. 5 SGB V), gleicht sie per Slug ab und legt bei jeder Beitragsänderung eine neue ContributionRate an. Ersatzkassen (BARMER/TK/DAK/KKH/HEK/hkk) werden als Typ EK erkannt; die SVLFG erhebt keinen Zusatzbeitrag (currentRate = null).
  2. Der Scheduler (MainSchedule) stößt den Crawl täglich an. Sowohl der Scheduler als auch der Admin-Button „Jetzt crawlen" dispatchen RunGkvCrawl an den async-Transport (Doctrine); der Messenger-Worker führt Crawl + AlertDispatcher aus (HTTP-Request blockiert nicht).
  3. Der AlertDispatcher (app:alerts:dispatch) schickt für jede alertfähige Erhöhung eine Warn-Mail an aktive Abonnenten inkl. Sonderkündigungs-Frist — idempotent (unique Notification je Abo+Rate, keine Warnung für Abos, die nach der Änderung entstanden).

Manuelle Rate vs. Crawler: Der Crawler überschreibt einen manuell gesetzten Beitrag beim nächsten Lauf (GKV ist maßgeblich). Alerts feuern nur bei echten GKV→GKV-Anstiegen (ContributionRate.alertable); ein manueller Vorwert oder ein manueller Wert selbst löst nie eine Warnung aus.

Öffentliche Ansicht (/)

  • Kassen-Liste mit Kennzahlen (Anzahl, Ø, günstigste/teuerste), Name-Suche, Bundesland-Filter, „nur allgemein wählbare"-Toggle (blendet betriebs-/ branchenbezogene BKKs aus), Typ-Filter und Sortierung (inkl. „zuletzt geändert").
  • Beitrag als Farbbalken (grün→rot) mit Ø-Abweichung; Trend-Badge (↑/↓ + Datum) bei erkannten Änderungen; aufklappbare Detailzeile (Historie-Sparkline, Regionen, Website).
  • „beobachten" übernimmt eine Kasse ins Abo-Formular; dort Mehrfachauswahl per Suche + Chips (eine Bestätigungsmail für alle).

Öffentliche Endpunkte

Methode Pfad Zweck
GET /api/insurers Alle Kassen: Beitrag, Typ, Regionen, letzte Änderung
GET /api/rates?insurer=/api/insurers/{id} Beitrags-Historie
POST /api/subscribe Abo anlegen ({email, insurers:[…]}) → Double-Opt-In
GET /api/subscribe/confirm/{token} Abo bestätigen
GET /api/subscribe/unsubscribe/{token} Abmelden
POST /api/login_check Admin-JWT

Admin

Geschützter Bereich unter /admin in der React-App (JWT im Authorization-Header, Login unter /admin/login):

  • Dashboard — Kennzahlen, letzter Crawl, letzte Erhöhungen
  • Crawl-Läufe — Verlauf + „Jetzt crawlen" (async; löst danach Alerts aus)
  • Kassen — bearbeiten, aktiv/inaktiv, manuelle Beitragskorrektur, Historie
  • Abonnenten — Filter (Status, Kasse) + E-Mail-Suche; einzeln/Bulk an-/abmelden (mit E-Mail-Benachrichtigung), löschen (DSGVO, mit Bestätigungs- und Lösch-Mail), einzelnes Kassen-Abo per × entfernen; Versand-Log
    • Resend. Löschungen sind rückfrage-bestätigt.

API dahinter unter /api/admin/* (ROLE_ADMIN).

Auth

  • Publikum: E-Mail Double-Opt-In (Phase 4) — keine Passwörter.
  • Admin: JWT via POST /api/login_check ({ "email", "password" }). Dev-Admin: admin@kk-sentinel.ddev.site / ChangeMe123! (bitte ändern).

Sicherheit / bekannte Punkte

  • Rate-Limiting auf POST /api/subscribe: 5/15 min pro IP, 3/h pro Ziel-Adresse (Anti-Mail-Bombing) → 429 mit Retry-After.
  • Admin-JWT im localStorage: bewusster SPA-Tradeoff (XSS-exponiert). Gemildert durch kurze Token-Laufzeit (1 h), Auto-Logout bei 401 und die kleine Admin-Angriffsfläche. Sauberer Fix (später): httpOnly-Cookie-Auth inkl. CSRF — bewusst zurückgestellt, da kein Kleinkram.

Status

  • Phase 1 DDEV-Gerüst
  • Phase 2 Symfony-API-Basis (API Platform, JWT-Admin, MariaDB)
  • Phase 3 React-19-Frontend (Kassen-Liste, Abo-Formular, Admin-Login)
  • Phase 4 Domäne: Beitrags-Historie, GKV-Crawler + Scheduler, Double-Opt-In-Abos, E-Mail-Warnungen, Such-/Überblick-Ansicht
  • Admin-Bereich (/admin): Dashboard, Crawl-Läufe + async Trigger, Kassen-Pflege, Abonnenten-Verwaltung (Filter, Bulk, An-/Abmelden mit Mail, DSGVO-Löschmail), Versand-Log + Resend
  • Öffentliche Ansicht ausgebaut: Filter, Trend, Farbbalken, „beobachten", Detailzeile, Mehrfach-Abo
  • Feinschliff: Ersatzkassen-Typ (EK), SVLFG (93 Kassen), Rate-Limiting, async Crawl-Worker, einheitliches Button-/Select-Styling
  • Phase 4 (Ausbau, optional) Einzel-Crawler je Kasse (frühere Vorwarnung)
  • Phase 5 Hetzner-Hosting (Ubuntu 24.04, nginx + PHP-FPM 8.4 + MariaDB, Scheduler-Worker als systemd-Service) — bewusst ganz am Ende

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages