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>
SettingsPage was still calling window.open() directly,
bypassing the auth-enabled fetch methods in api.js.
Changed buttons to use api.exportZip() and api.exportJson()
which properly include authentication headers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Changed from window.open() to fetch() + Blob download.
window.open() cannot send custom headers, causing 401 errors.
**Changed:**
- exportZip: fetch with auth, download blob as .zip
- exportJson: fetch with auth, download blob as .json
- exportCsv: fetch with auth, download blob as .csv
All exports now work with authenticated sessions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
- 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>
- 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>
- 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>
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>
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>
PostgreSQL DATE type doesn't accept empty strings ('').
Convert empty/whitespace date values to NULL during migration.
Fixes: invalid input syntax for type date: ""
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SQLite schema (v9a) has meas_id in photos table, but PostgreSQL
schema (v9b) was missing it. This caused migration to fail.
Added meas_id as nullable UUID column for backward compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bug: db_init.py was importing migrate_to_postgres but not calling main().
Result: Migration appeared successful but no data was migrated (0 users).
Fix: Import and call migrate_to_postgres.main()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prepares production for SQLite → PostgreSQL migration:
- Add postgres service (mitai-db, port 5432)
- Add DB environment variables to backend
- Backend depends on postgres health check
- Uses startup.sh for automatic migration
Migration strategy:
1. SQLite data in /app/data/bodytrack.db is preserved (volume mounted)
2. On first start with empty PostgreSQL: automatic migration
3. Migration is safe: checks if profiles table is empty before migrating
4. After migration: all new data goes to PostgreSQL
IMPORTANT: Set DB_PASSWORD in .env before deploying!
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The data volume was missing in dev environment, preventing automatic
SQLite → PostgreSQL migration. The SQLite database (bodytrack.db) was
not accessible to the container, so migration was skipped.
This fixes the "No SQLite database found" message when data exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Frontend was sending {email, pin} but backend expects {email, password}.
This caused 422 Unprocessable Entity errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PostgreSQL does not support IF NOT EXISTS for CREATE TRIGGER.
Use DROP TRIGGER IF EXISTS before CREATE TRIGGER instead.
Fixes: Backend crash loop due to schema.sql syntax error on line 231
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove postgresql-client installation (causes 150s+ hangs due to network)
- Add db_init.py: Pure Python PostgreSQL checks using psycopg2-binary
- Simplify startup.sh: Call Python script instead of psql commands
- Build should now complete in <30s instead of hanging
This fixes the deployment timeout issue by avoiding APT network problems entirely.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Force IPv4 (IPv6 shows 33% packet loss)
- Increase retries to 5
- Add 10s timeouts to fail fast and retry
- Previous fix improved from 1630s to 78s but still hangs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Switch from deb.debian.org to ftp.de.debian.org (33% packet loss observed)
- Add APT retry logic (3 attempts) for flaky connections
- Fixes deployment timeout on backend build (postgresql-client install)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added detailed documentation for:
- Color system (CSS variables)
- CSS class conventions
- Spacing & sizing standards
- Component patterns (loading, error, empty states)
- Jinkendo logo system
- Available custom commands
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>