2026-06-10 10:03:35 +02:00
2026-06-09 21:21:52 +02:00
2026-06-10 09:43:50 +02:00
2026-06-10 10:03:35 +02:00
2026-06-10 10:03:35 +02:00
2026-06-09 21:21:52 +02:00
2026-06-09 21:21:52 +02:00
2026-06-09 21:21:52 +02:00
2026-06-09 21:21:52 +02:00
2026-06-09 21:21:52 +02:00
2026-06-09 21:21:52 +02:00
2026-06-09 21:21:52 +02:00
2026-06-09 21:21:52 +02:00
2026-06-10 09:43:50 +02:00
2026-06-09 21:21:52 +02:00
2026-06-09 21:21:52 +02:00

TakeYourMeds

Persönliche Medikamenten-PWA mit dezenten Push-Erinnerungen, Einnahme-Logging und sarkastischem Humor.

Domain: medis.schwenk.online

App Icon


Features

Kern

  • Push-Erinnerungen um 8:00 und 12:00 (Europe/Berlin) — dezent (silent), kein Alarm
  • Einnahme loggen: genommen / verpasst / snooze
  • PIN-Login mit langer JWT-Session (90 Tage)
  • PWA: installierbar, offline-fähige App-Shell, Service Worker

Spaß

  • Dopamin-Drop — Animation + Erfolgsspruch beim Loggen
  • Roast-of-the-Day — täglicher sarkastischer Spruch (OpenRouter, gecacht)
  • KI-Orakel — wöchentlicher Compliance-Report (OpenRouter, gecacht)
  • Easter Eggs — Meilenstein-Badges (50/100 Logs, Streak 7/30/100)
  • ~95 Humor-Texte in messages.yaml (Reminder, Success, Missed, …)

Nützlich

  • Snooze-Kette — +15 / +30 Min (UI + Notification-Actions)
  • 90-Tage Compliance-Heatmap
  • Manifest Shortcuts — „Genommen (Morgens/Mittags)" vom Homescreen
  • Badge API — App-Icon zeigt Streak-Zahl
  • Offline-Queue — Logs in IndexedDB, Sync bei Verbindung

Stack

Komponente Technologie
Backend FastAPI + uvicorn
DB SQLite (data/medis.sqlite)
Frontend Vanilla JS (kein Build-Step)
Push Web Push (pywebpush + VAPID)
Scheduler APScheduler (8:00, 12:00, 21:00)
KI (optional) OpenRouter
Deployment Docker Compose + Traefik

KISS-Entscheidung: Ein Container statt nginx+api — FastAPI serviert API und statische Dateien. Traefik-Labels aus compose.example.yml übernommen.


Schnellstart (lokal)

# 1. venv + Dependencies
python3 -m venv .venv
source .venv/bin/activate
pip install -e .

# 2. Konfiguration
cp .env.example .env
# APP_PIN und JWT_SECRET anpassen

# 3. VAPID keys (für Push)
python tools/gen_vapid.py
# Output in .env eintragen

# 4. Starten
uvicorn server.app:app --reload --host 0.0.0.0 --port 8000

App: http://localhost:8000


Deployment (Docker + Traefik)

cp .env.example .env
# .env ausfüllen (PIN, JWT_SECRET, VAPID keys, optional OPENROUTER_API_KEY)

docker compose up -d --build

Traefik routet medis.schwenk.online → Container Port 8000 (TLS via myresolver).

Volumes:

  • ./data — SQLite-Datenbank
  • ./meds.yaml — Medikamenten-Zeitplan (read-only)
  • ./messages.yaml — Humor-Texte (read-only)

Konfiguration

.env

Variable Beschreibung
APP_PIN Login-PIN (46 Ziffern)
JWT_SECRET Geheimer Key für JWT (min. 32 Zeichen empfohlen: openssl rand -hex 32)
JWT_EXPIRE_DAYS Session-Laufzeit (Default: 90)
VAPID_PRIVATE_KEY Web-Push Private Key (PEM)
VAPID_PUBLIC_KEY Web-Push Public Key
VAPID_CLAIMS_EMAIL mailto:-Adresse für VAPID
OPENROUTER_API_KEY Optional — für Roast + Orakel
OPENROUTER_MODEL Default: google/gemini-2.0-flash-001

meds.yaml

timezone: Europe/Berlin
slots:
  - id: morning
    time: "08:00"
    label: Morgens
    meds:
      - name: Elvanse
        dose: "30mg"
    reminder_window_minutes: 90   # Karenzzeit — siehe unten

reminder_window_minutes: Zeit nach der Soll-Uhrzeit, in der eine Dosis noch als „offen" gilt. Beispiel: 08:00 + 90 Min → bis 09:30 Status pending/overdue, erst danach wird sie abends (~21:00) automatisch als missed markiert.

messages.yaml

Kategorien: reminder, success, missed, streak, snooze, easter_egg, roast_fallback. Texte frei editierbar — kein Neustart nötig (wird beim ersten Request geladen; Container-Neustart lädt neu).


API

Alle Endpunkte unter /api/*. Auth via Authorization: Bearer <token> (außer /api/auth/pin und /api/vapid-public-key).

Methode Pfad Beschreibung
POST /api/auth/pin { "pin": "1234" }{ "token": "..." }
GET /api/vapid-public-key Public Key für Push-Subscription
GET /api/config Meds-Config
GET /api/today Heutige Slots + Status
POST /api/log { "slot_id", "status", "day?", "source?" }
POST /api/snooze { "slot_id", "minutes": 15|30 }
GET /api/history?days=90 Heatmap-Daten + Stats
GET /api/stats Streak, Compliance, Meilensteine
GET /api/roast Roast-of-the-Day
GET /api/oracle Wöchentliches KI-Orakel
POST /api/push/subscribe Web-Push Subscription speichern

PWA / Push einrichten

  1. App im Browser öffnen (HTTPS erforderlich — lokal ohne Push)
  2. Einloggen → Tab EinstellungenPush aktivieren
  3. Optional: Auf Homescreen installieren
  4. Android-Shortcuts: Long-Press App-Icon → „Genommen (Morgens/Mittags)"

Notification-Actions: Genommen ✓, +15 Min, +30 Min


Icons

  • icon.png / icon.svg — Quelldateien im Repo-Root
  • Werden nach public/ kopiert für PWA (192, 512, 72 px)
  • Manifest theme_color: #F12F12

Entscheidungsprotokoll

Entscheidung Begründung
Ein FastAPI-Container statt nginx+api KISS — weniger Moving Parts, StaticFiles reicht für Single-User-PWA
Vanilla JS statt React/Vue Kein Build-Step, schnelle Iteration, passt zu persönlicher App
SQLite statt Postgres Single-User, eine Datei, Backup = data/medis.sqlite kopieren
Web Push vom Server statt Client-Timer Zuverlässig auch bei geschlossener App; APScheduler im Container
OpenRouter nur für Roast + Orakel Kosten/Latenz — Notifications nutzen statische messages.yaml
Model: gemini-2.0-flash Günstig, schnell, gut genug für kurze deutsche Roasts
PIN plain in .env statt Hash Single-User, unkritische Daten; hmac.compare_digest gegen Timing-Leaks
JWT 90 Tage Lange Session auf persönlichem Gerät, kein ständiges PIN-Eingeben
reminder_window_minutes: 90 ADHS-realistisch — nicht sofort „verpasst" um 08:01
Badge = Streak, nicht offene Dosen Streak ist motivierender Dopamin-Hook
Icons: PNG für alle Größen 556×556 Quelle — Browser skaliert; kein ImageMagick nötig
Abend-Check 21:00 Markiert überfällige Slots als missed, optional Push
Offline-Queue via IndexedDB Einfacher als SW-only; Flush bei App-Start + Background-Sync

Projektstruktur

takeyourmeds/
├── compose.yml          # Docker + Traefik
├── Dockerfile
├── meds.yaml            # Medikamenten-Zeitplan
├── messages.yaml        # Humor-Pool
├── icon.png / icon.svg  # App-Icons (Quelle)
├── server/              # FastAPI Backend
├── public/              # PWA Frontend
├── tools/               # schema.sql, gen_vapid.py
└── data/                # SQLite (gitignored)

Entwicklung

source .venv/bin/activate
uvicorn server.app:app --reload --port 8000

Service Worker cached aggressiv — für SW-Änderungen: DevTools → Application → Clear storage, oder Inkognito.


Troubleshooting

Problem Lösung
Push kommt nicht VAPID keys in .env? HTTPS? Push in Einstellungen aktiviert?
„VAPID keys nicht konfiguriert" python tools/gen_vapid.py ausführen
KI-Roast zeigt Fallback OPENROUTER_API_KEY fehlt oder API-Fehler — Fallback aus messages.yaml
Streak = 0 trotz Log Streak zählt nur Tage wo alle Slots genommen wurden
SW zeigt alte Version version.json bumpen + Cache leeren

Lizenz

Privates Projekt — persönliche Nutzung.

S
Description
No description provided
Readme 454 KiB
Languages
Python 53.4%
JavaScript 26.5%
CSS 10.4%
HTML 9.2%
Dockerfile 0.5%