2f302b26af
feat: add v9c subscription system database schema
...
Deploy Development / deploy (push) Successful in 53s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
Phase 1: Database Migration Complete
Created migration infrastructure:
- backend/migrations/v9c_subscription_system.sql (11 new tables)
- backend/apply_v9c_migration.py (auto-migration runner)
- Updated main.py startup event to apply migration
New tables (Feature-Registry Pattern):
1. app_settings - Global configuration
2. tiers - Subscription tiers (free/basic/premium/selfhosted)
3. features - Feature registry (11 limitable features)
4. tier_limits - Tier x Feature matrix (44 initial limits)
5. user_feature_restrictions - Individual user overrides
6. user_feature_usage - Usage tracking with reset periods
7. coupons - Coupon management (single-use, period, Wellpass)
8. coupon_redemptions - Redemption history
9. access_grants - Time-limited access with pause/resume logic
10. user_activity_log - Activity tracking (JSONB details)
11. user_stats - Aggregated statistics
Extended profiles table:
- tier, trial_ends_at, email_verified, email_verify_token
- invited_by, invitation_token
Initial data inserted:
- 4 tiers (free/basic/premium/selfhosted)
- 11 features (weight, circumference, caliper, nutrition, activity, photos, ai_calls, ai_pipeline, export_*)
- 44 tier_limits (complete Tier x Feature matrix)
- App settings (trial duration, self-registration config)
Migration auto-runs on container startup (similar to SQLite→PostgreSQL).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 12:42:43 +01:00
b4a1856f79
refactor: modular backend architecture with 14 router modules
...
Deploy Development / deploy (push) Successful in 58s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Phase 2 Complete - Backend Refactoring:
- Extracted all endpoints to dedicated router modules
- main.py: 1878 → 75 lines (-96% reduction)
- Created modular structure for maintainability
Router Structure (60 endpoints total):
├── auth.py - 7 endpoints (login, logout, password reset)
├── profiles.py - 7 endpoints (CRUD + current user)
├── weight.py - 5 endpoints (tracking + stats)
├── circumference.py - 4 endpoints (body measurements)
├── caliper.py - 4 endpoints (skinfold tracking)
├── activity.py - 6 endpoints (workouts + Apple Health import)
├── nutrition.py - 4 endpoints (diet + FDDB import)
├── photos.py - 3 endpoints (progress photos)
├── insights.py - 8 endpoints (AI analysis + pipeline)
├── prompts.py - 2 endpoints (AI prompt management)
├── admin.py - 7 endpoints (user management)
├── stats.py - 1 endpoint (dashboard stats)
├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export)
└── importdata.py - 1 endpoint (ZIP import)
Core modules maintained:
- db.py: PostgreSQL connection + helpers
- auth.py: Auth functions (hash, verify, sessions)
- models.py: 11 Pydantic models
Benefits:
- Self-contained modules with clear responsibilities
- Easier to navigate and modify specific features
- Improved code organization and readability
- 100% functional compatibility maintained
- All syntax checks passed
Updated CLAUDE.md with new architecture documentation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
9e6a542289
fix: change password endpoint method from POST to PUT to match frontend
Deploy Development / deploy (push) Successful in 55s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
2026-03-19 10:13:07 +01:00
c7d283c0c9
refactor: extract Pydantic models to models.py
...
Deploy Development / deploy (push) Successful in 56s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Phase 1.3 - Data Models isolieren
NEUE DATEI:
- backend/models.py: Alle Pydantic Models (122 Zeilen)
* ProfileCreate, ProfileUpdate
* WeightEntry, CircumferenceEntry, CaliperEntry
* ActivityEntry, NutritionDay
* LoginRequest, PasswordResetRequest, PasswordResetConfirm
* AdminProfileUpdate
ÄNDERUNGEN:
- backend/main.py:
* Import models from models.py
* Entfernt: ~60 Zeilen Model-Definitionen
* Von 2025 → 1878 Zeilen (-147 Zeilen / -7%)
PROGRESS:
✅ db.py: Database + init_db
✅ auth.py: Auth functions + dependencies
✅ models.py: Pydantic schemas
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 09:53:51 +01:00
d826524789
refactor: extract auth functions to auth.py
...
Deploy Development / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Phase 1.2 - Authentication-Logik isolieren
NEUE DATEI:
- backend/auth.py: Auth-Funktionen mit Dokumentation
* hash_pin() - bcrypt + SHA256 legacy support
* verify_pin() - Password verification
* make_token() - Session token generation
* get_session() - Token validation
* require_auth() - FastAPI dependency
* require_auth_flexible() - Auth via header OR query
* require_admin() - Admin-only dependency
ÄNDERUNGEN:
- backend/main.py:
* Import from auth.py
* Removed 48 lines of auth code
* hashlib, secrets nicht mehr benötigt
KEINE funktionalen Änderungen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 09:51:25 +01:00
548d733048
refactor: move init_db() to db.py
...
Deploy Development / deploy (push) Successful in 56s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Phase 1.1 - Database-Logik konsolidieren
ÄNDERUNGEN:
- init_db() von main.py nach db.py verschoben
- main.py importiert init_db von db
- startup_event() ruft db.init_db() auf
- Keine funktionalen Änderungen
DATEIEN:
- backend/db.py: +60 Zeilen (init_db Funktion)
- backend/main.py: -48 Zeilen (init_db entfernt, import hinzugefügt)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 09:49:46 +01:00
85f48907a4
fix: startup crash - init_db() jetzt mit Error-Handling
...
Deploy Development / deploy (push) Successful in 55s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
PROBLEM:
- Backend crasht beim Start auf Prod
- Login zeigt HTML statt JSON (Backend nicht erreichbar)
- Ursache: init_db() wirft Exception beim Startup
FIX:
1. startup_event() wrapped in try-except (non-fatal)
2. init_db() prüft ob ai_prompts Tabelle existiert
3. init_db() hat eigenen try-except
4. Bessere Fehlermeldungen in stdout
ERGEBNIS:
- Backend startet auch wenn init_db() fehlschlägt
- Pipeline-Prompt kann manuell angelegt werden falls nötig
- Login funktioniert wieder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 08:26:09 +01:00
c40b30737a
feat: zentraler Schalter für Pipeline-Deaktivierung
...
Deploy Development / deploy (push) Successful in 55s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
VORHER:
- Pipeline-Deaktivierung war nicht sichtbar im UI
- Deaktivierung sollte über Sub-Prompts erfolgen (nicht intuitiv)
JETZT:
- Zentraler Toggle-Button direkt unter "Mehrstufige Pipeline"
- Button-Text: "Gesamte Pipeline aktivieren/deaktivieren"
- Visuelles Feedback: Warning-Box wird rot wenn deaktiviert
IMPLEMENTIERUNG:
Backend (main.py):
- Neuer "pipeline" Master-Prompt wird automatisch angelegt
- startup_event() ruft init_db() auf
- Prompt: slug='pipeline', sort_order=-10 (ganz oben)
- Template: 'PIPELINE_MASTER' (nur Steuerung, kein echtes Template)
Frontend (Analysis.jsx):
- Toggle-Button unter Sektionsüberschrift
- Prüft: prompts.find(p=>p.slug==='pipeline')?.active
- pipelineAvailable basiert auf diesem Prompt (nicht Sub-Prompts)
- Warning-Box wechselt Farbe + Text:
* Aktiv: Orange + JSON-Hinweis
* Inaktiv: Rot + "Pipeline deaktiviert"
VERHALTEN:
✅ Button im Prompts-Tab unter "Mehrstufige Pipeline"
✅ Klar sichtbar: "Gesamte Pipeline deaktivieren"
✅ Pipeline verschwindet von Analyse-Seite wenn deaktiviert
✅ Sub-Prompts bleiben unabhängig editierbar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 07:56:36 +01:00
3f4ef75463
fix: Prompt-Deaktivierung jetzt voll funktionsfähig
...
Deploy Development / deploy (push) Successful in 57s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
PROBLEME behoben:
1. Falscher Datentyp: Frontend sendete 0/1 statt boolean
2. Button-Text: "Aktiv." → "Aktivieren"
3. Keine visuelle Markierung für deaktivierte Prompts
FIXES:
Backend (main.py):
- active-Wert wird explizit zu boolean konvertiert
- Akzeptiert jetzt sowohl true/false als auch 1/0
Frontend (Analysis.jsx):
- Sendet jetzt !p.active (boolean) statt p.active?0:1
- Button-Text: "Deaktivieren" / "Aktivieren" (klar lesbar)
- Visuelle Markierung für inaktive Prompts:
* Opacity 0.6 (ausgegraut)
* Rotes Badge "⏸ Deaktiviert"
- Gilt für Einzel- UND Pipeline-Prompts
RESULTAT:
✅ Deaktivierte Prompts werden nicht mehr auf "Analysen starten" gezeigt
✅ Klare visuelle Unterscheidung im Prompts-Tab
✅ Button-Text eindeutig ("Aktivieren" vs "Deaktivieren")
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 07:27:56 +01:00
518e417b1d
fix: admins können jetzt alle Prompts sehen und bearbeiten
...
Deploy Development / deploy (push) Successful in 57s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
- /api/prompts checkt nun ob User admin ist
- Admins sehen ALLE Prompts (inkl. pipeline_ und inaktive)
- Normale User sehen nur aktive Einzelanalysen (wie bisher)
- Frontend (Analysis.jsx) zeigt Pipeline-Prompts bereits korrekt:
* Gruppiert nach "Einzelanalysen" und "Mehrstufige Pipeline"
* JSON-Prompts (Stage 1) mit oranger Border und Badge
* Warnung über JSON-Format bereits vorhanden
- CSS-Variablen --warn, --warn-bg, --warn-text bereits definiert
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 06:28:48 +01:00
115d975335
feat: add ZIP import functionality
...
Deploy Development / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 11s
- Backend: POST /api/import/zip endpoint with validation and rollback
- CSV import with ON CONFLICT DO NOTHING for duplicate detection
- Photo import with existence check
- AI insights import
- Frontend: file upload UI in SettingsPage
- Import summary showing count per category
- Full transaction rollback on error
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 22:52:35 +01:00
e10e9d7eb9
fix: photos now display in History with token auth
...
Deploy Development / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Problem: Photo endpoint requires auth header, but <img src> can't send headers.
Solution:
- Backend: Added require_auth_flexible() that accepts token via header OR query param
- Backend: Photo endpoint uses flexible auth
- Frontend: photoUrl() now appends ?token=xxx to URL
Photos in History/Verlauf now display correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 22:48:40 +01:00
b6f8b11685
fix: handle datetime.date object for birth_year in ZIP export
...
Deploy Development / deploy (push) Successful in 58s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
PostgreSQL returns dob as datetime.date object, not string.
Changed from prof['dob'][:4] to prof['dob'].year
Error was: TypeError: 'datetime.date' object is not subscriptable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 22:41:51 +01:00
64d1b9bf7b
feat: implement comprehensive ZIP export per v9c specification
...
Deploy Development / deploy (push) Successful in 1m15s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Complete rewrite of ZIP export to match CLAUDE.md specification.
**Structure:**
- README.txt (format explanation)
- profile.json (no password hash, includes stats)
- data/*.csv (5 separate CSV files)
- insights/ai_insights.json
- photos/*.jpg
**CSV Format:**
- Delimiter: semicolon (;)
- Encoding: UTF-8 with BOM (Excel compatible)
- Date format: YYYY-MM-DD
- Decimal separator: dot (.)
- NULL values: empty string
- First row: header
**Files:**
- data/weight.csv (id, date, weight, note, source, created)
- data/circumferences.csv (8 measurement points)
- data/caliper.csv (skinfold measurements + bf%)
- data/nutrition.csv (kcal, protein, fat, carbs)
- data/activity.csv (type, duration, kcal, HR, distance)
**Filename:** mitai-export-{name}-{YYYY-MM-DD}.zip
Ready for import functionality (v9c).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 22:15:31 +01:00
47a268f426
fix: comprehensive PostgreSQL Decimal handling across all endpoints
...
Deploy Development / deploy (push) Successful in 55s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Fixed all remaining Decimal → float conversion issues found by audit.
**Fixed Endpoints:**
1. Weight Stats: min/max/avg calculations
2. Activity Stats: kcal/duration accumulation
3. Nutrition Weekly: average calculations
4. Template Variables: all f-string Decimal formatting
5. CSV Export: all numeric value formatting
6. JSON Export: added Decimal handler
7. ZIP Export: added Decimal handler
8. Correlations: weight, nutrition, caliper values
**Changes:**
- Added `from decimal import Decimal` import
- Weight stats: convert to float for min/max/avg
- Activity: float() in sum() and accumulation
- Nutrition: float() in averages
- Template vars: float() for weight_aktuell, kf_aktuell, goals
- CSV: float() in all f-strings (weight, circ, caliper, nutrition, activity)
- JSON/ZIP: custom decimal_handler for json.dumps()
- Correlations: float() for all numeric DB values
Prevents:
- TypeError in math operations
- "Decimal('X')" strings in exports
- JSON serialization failures
All numeric values from PostgreSQL now properly converted to float.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 21:52:57 +01:00
f7f7f745b1
fix: convert PostgreSQL Decimal to float for math operations
...
Deploy Development / deploy (push) Successful in 59s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Fixed TypeError when preparing AI prompt template variables.
PostgreSQL returns NUMERIC columns as decimal.Decimal, not float.
**Fixed in _prepare_template_vars:**
- Weight calculations (protein targets, delta)
- Nutrition averages (kcal, protein, fat, carbs)
- Activity totals (kcal_active)
All Decimal values now converted to float before math operations.
Error was: "TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float'"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 21:44:10 +01:00
1db780858b
fix: align all API endpoints between frontend and backend
...
Fixed 11 critical endpoint mismatches found during codebase audit.
**Renamed Endpoints (consistency):**
- /api/ai/analyze/{slug} → /api/insights/run/{slug}
- /api/ai/analyze-pipeline → /api/insights/pipeline
- /api/auth/password-reset-request → /api/auth/forgot-password
- /api/auth/password-reset-confirm → /api/auth/reset-password
- /api/admin/test-email → /api/admin/email/test
**Added Missing Endpoints:**
- POST /api/auth/pin (change PIN/password for current user)
- PUT /api/admin/profiles/{id}/permissions (set permissions)
- PUT /api/admin/profiles/{id}/email (set email)
- PUT /api/admin/profiles/{id}/pin (admin set PIN)
- GET /api/admin/email/status (check SMTP config)
- PUT /api/prompts/{id} (edit prompt templates, admin only)
- GET /api/export/json (export all data as JSON)
- GET /api/export/zip (export data + photos as ZIP)
**Updated:**
- Added imports: json, zipfile, Response
- Fixed admin email test endpoint to accept dict body
All frontend API calls now have matching backend implementations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 17:07:41 +01:00
3d58a2db8e
fix: add missing /api/insights endpoints
...
Deploy Development / deploy (push) Successful in 56s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 13s
- Add GET /api/insights (returns all insights for profile)
- Add DELETE /api/insights/{id} (delete by ID, not scope)
- Frontend Analysis.jsx needs these endpoints to load/delete insights
Fixes 404 error preventing prompts from displaying.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 15:26:57 +01:00
36f334aba7
fix: PostgreSQL boolean syntax in prompts queries
...
Deploy Development / deploy (push) Successful in 56s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
- Change WHERE active=1 to WHERE active=true (PostgreSQL uses boolean)
- Change endpoint from /api/ai/prompts to /api/prompts (simpler path)
- Fixed 5 occurrences across prompt-related queries
This fixes the issue where no prompts were returned, causing empty
prompt list in Admin and no AI analysis options.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 13:55:14 +01:00
8390c7f510
feat: add missing API endpoints
...
Deploy Development / deploy (push) Successful in 53s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
- Add GET /api/insights/latest (returns latest 10 insights)
- Add GET /api/auth/status (health check endpoint)
These endpoints were called by frontend but returned 404,
causing uncaught promise errors that blocked page loading.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 12:54:25 +01:00
79a951ce92
fix: use column names for COUNT queries with RealDictCursor
...
Deploy Development / deploy (push) Successful in 58s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
RealDictCursor returns dicts, not tuples. Cannot use [0] for index access.
Changed all COUNT(*) to COUNT(*) as count and access via ['count'].
Fixes: KeyError: 0 on cur.fetchone()[0]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 12:47:01 +01:00
9fbedb6c4b
fix: use RealDictCursor for PostgreSQL row access
...
Deploy Development / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
All conn.cursor() calls replaced with get_cursor(conn) to enable
dict-like row access (prof['pin_hash'] instead of prof[column_index]).
This fixes KeyError when accessing PostgreSQL query results.
Fixes: 'tuple' object has no attribute '__getitem__' with string keys
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 12:42:46 +01:00
0a871fea22
9b
Deploy Development / deploy (push) Failing after 1s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 1m6s
2026-03-18 08:27:33 +01:00
89b6c0b072
feat: initial commit – Mitai Jinkendo v9a
Deploy to Raspberry Pi / deploy (push) Waiting to run
Build Test / build-frontend (push) Waiting to run
Build Test / lint-backend (push) Waiting to run
2026-03-16 13:35:11 +01:00