- HTML 53.2%
- JavaScript 24.7%
- Vue 21.7%
- CSS 0.2%
- Dockerfile 0.1%
- Other 0.1%
Security Audit & Remediation ============================= Backend CVEs Fixed: - nodemailer 8.x -> 9.0.3: Fixed CRLF injection (GHSA-268h-hp4c-crq3), jsonTransport bypass (GHSA-wqvq-jvpq-h66f), TLS cert validation bypass (GHSA-r7g4-qg5f-qqm2), raw option SSRF (GHSA-p6gq-j5cr-w38f) - body-parser -> 1.20.6: Fixed DoS via invalid limit (GHSA-v422-hmwv-36x6) - multer 2.1.x -> latest: Fixed DoS via nested fields (GHSA-72gw-mp4g-v24j) and incomplete cleanup (GHSA-3p4h-7m6x-2hcm) - @babel/core -> 7.29.0+: Fixed arbitrary file read (GHSA-4x5r-pxfx-6jf8) - form-data -> 4.0.6+: Fixed CRLF injection (GHSA-hmw2-7cc7-3qxx) - js-yaml -> 4.x latest: Fixed quadratic DoS in merge keys (GHSA-h67p-54hq-rp68) - brace-expansion -> 5.0.8 via override: Fixed DoS via unbounded expansion (GHSA-mh99-v99m-4gvg, GHSA-3jxr-9vmj-r5cp) - jest 29.x -> 30.x (dev): Updated transitive dependency chain Frontend CVEs Fixed: - axios 1.15.x -> 1.17.x: Fixed 10 CVEs including prototype pollution (GHSA-xj6q-8x83-jv6g), DoS (GHSA-42h9-826w-cgv3, GHSA-pmv8-rq9r-6j72), SSRF bypass (GHSA-jqh4-m9w3-8hp9), proxy bypass (GHSA-f4gw-2p7v-4548) - vite 8.0.x -> 8.1.x: Fixed NTLM hash disclosure (GHSA-v6wh-96g9-6wx3) and fs.deny bypass (GHSA-fx2h-pf6j-xcff) - postcss -> 8.5.17+: Fixed path traversal via source maps (GHSA-r28c-9q8g-f849) - undici -> 7.28.x: Fixed 7 CVEs including TLS bypass (GHSA-vmh5-mc38-953g), header injection (GHSA-p88m-4jfj-68fv), response queue poisoning (GHSA-35p6-xmwp-9g52) - form-data -> 4.0.6+: Fixed CRLF injection (GHSA-hmw2-7cc7-3qxx) - js-yaml -> 4.2.x: Fixed quadratic DoS (GHSA-h67p-54hq-rp68) - brace-expansion -> 5.0.8 via override: Fixed DoS (GHSA-mh99-v99m-4gvg) - cypress 14.x -> 15.x (dev): Fixed qs DoS via @cypress/request (GHSA-q8mj-m7cp-5q26) - @playwright/test 1.58.x -> 1.62.x (dev) Test Results: - Backend: 6 test suites, 93 tests - ALL PASSED - Frontend: 4 test files, 26 tests - ALL PASSED - Frontend build: SUCCESS - E2E: 15/77 passed (62 fail due to pre-existing test user seed data issue) - Both npm audit: 0 vulnerabilities remaining |
||
|---|---|---|
| backend | ||
| frontend | ||
| .env.prod | ||
| .gitignore | ||
| app_requirements.txt | ||
| CONTEXT.md | ||
| DEPLOYMENT.md | ||
| docker-compose.override.yml | ||
| docker-compose.prod.yml | ||
| docker-compose.yml | ||
| env.example | ||
| LICENSE | ||
| nginx-upload-fix.conf | ||
| PRODUCTION_FIX.md | ||
| README.md | ||
| REVERSE_PROXY.md | ||
| SUMMARY.md | ||
| TEST_RESULTS.md | ||
Get Healthy
A clean, open-source fitness tracking application to help you track workouts, body measurements, and progress toward health goals.
Tech Stack
| Layer | Technology |
|---|---|
| Frontend | Vue.js 3 + Vite + Tailwind CSS + Vue Router + Pinia |
| Backend | Node.js + Express.js + Passport.js |
| Database | PostgreSQL |
| ORM | Prisma |
| Auth | JWT (Local) + OIDC/OAuth2 (Google, GitHub, Keycloak, Authentik, Authelia) |
| API | REST |
| Deployment | Docker Compose |
Features
Authentication
- Email/password registration and login
- OIDC/OAuth2 integration (Google, GitHub, Keycloak, Authentik, Authelia, Generic OIDC)
- Password reset flow with SMTP email support
- Email verification
- First registered user automatically becomes admin
User Profile
- Display name, avatar upload
- Height, age, current weight tracking
- Preferred weight unit (lb/kg)
- Light/Dark mode with device detection
- Push notification preferences
- Full data deletion
Body Measurements
- Weight, body fat percentage
- Body measurements: waist, hips, chest, thighs, biceps, calves, shoulders, back span, neck
- Historical tracking with interactive charts
- Progress visualization over time
Workout Tracking
- Create and manage workout routines
- Routine types: Weightlifting, Aerobic, Flexibility, Combination
- Exercise types: Strength, Cardio, Flexibility
- Quick-entry UI for sets/reps/weight (mobile optimized)
- RPE (Rate of Perceived Exertion) tracking
- Continue previous incomplete workouts
- Archive workouts individually or batch (by date range or name pattern)
- View and restore archived workouts
- Exercise name autocomplete from history
Workout Templates
- Pre-built template library (StrongLifts, PPL, Couch to 5K, Starting Strength, 5K Training, HIIT, Yoga, Pilates, etc.)
- Create custom templates with workout builder
- Save workout as template
- Browse/search templates by type, goal, difficulty
Photo Journal
- Upload progress photos with categorization
- Gallery view organized by category
- Edit photo details (category, notes, date)
- Timelapse playback for progress visualization
- Full-screen photo viewer
Exercise Limits
- Set upper/lower limits per exercise
- Visual badges for limits in profile
- Quick add/delete limits from profile
Scheduling
- Schedule workouts on specific dates
- Cadence system (every X days)
- Calendar view
- Push notification reminders (HTTPS required)
Reporting & Dashboard
- Dashboard with summary charts
- Progress tracking charts
- Time-of-day analysis
- Motivational messages
- Calorie tracking
Admin
- Admin dashboard
- User management
- System statistics
- SMTP configuration and testing
- OIDC provider configuration
AI Integration
- Backend hooks for Ollama integration
- Online AI support (OpenAI/Anthropic)
Data Management
- JSON export
- CSV export
- Full data deletion
Getting Started
Prerequisites
- Node.js 18+
- Docker and Docker Compose
Quick Start (Docker)
# Clone the repository
git clone https://github.com/your-repo/get_healthy.git
cd get_healthy
# Start all services
docker-compose up -d
# Access the app
# Frontend: http://localhost:5173
# Backend API: http://localhost:3000
Development Setup
- Install dependencies:
# Backend
cd backend && npm install
# Frontend
cd ../frontend && npm install
- Start PostgreSQL with Docker:
docker-compose up -d db
- Set up the database:
cd backend
npx prisma db push
npx prisma generate
- Start the backend:
cd backend
npm run dev
- Start the frontend (in another terminal):
cd frontend
npm run dev
- Access the app:
- Frontend: http://localhost:5173
- Backend API: http://localhost:3000
Environment Variables
Backend (.env)
DATABASE_URL=postgresql://user:pass@localhost:5432/get_healthy
JWT_SECRET=your-secret-key
NODE_ENV=development
PORT=3000
FRONTEND_URL=http://localhost:5173
# OAuth / OIDC (optional - configure any combination)
# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_CALLBACK_URL=http://localhost:3000/api/auth/oidc/google/callback
# GitHub OAuth
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/oidc/github/callback
# Keycloak OIDC
KEYCLOAK_CLIENT_ID=your-keycloak-client-id
KEYCLOAK_CLIENT_SECRET=your-keycloak-client-secret
KEYCLOAK_ISSUER=https://your-keycloak.com/realms/your-realm
KEYCLOAK_AUTHORIZATION_URL=https://your-keycloak.com/realms/your-realm/protocol/openid-connect/auth
KEYCLOAK_TOKEN_URL=https://your-keycloak.com/realms/your-realm/protocol/openid-connect/token
KEYCLOAK_USERINFO_URL=https://your-keycloak.com/realms/your-realm/protocol/openid-connect/userinfo
KEYCLOAK_CALLBACK_URL=http://localhost:3000/api/auth/oidc/keycloak/callback
# Authentik OIDC
AUTHENTIK_CLIENT_ID=your-authentik-client-id
AUTHENTIK_CLIENT_SECRET=your-authentik-client-secret
AUTHENTIK_ISSUER=https://your-authentik.com
AUTHENTIK_AUTHORIZATION_URL=https://your-authentik.com/application/o/authorize/
AUTHENTIK_TOKEN_URL=https://your-authentik.com/application/o/token/
AUTHENTIK_USERINFO_URL=https://your-authentik.com/application/o/userinfo/
AUTHENTIK_CALLBACK_URL=http://localhost:3000/api/auth/oidc/authentik/callback
# Authelia OIDC
AUTHELIA_CLIENT_ID=your-authelia-client-id
AUTHELIA_CLIENT_SECRET=your-authelia-client-secret
AUTHELIA_ISSUER=https://your-authelia.com
AUTHELIA_AUTHORIZATION_URL=https://your-authelia.com/api/oidc/authorization
AUTHELIA_TOKEN_URL=https://your-authelia.com/api/oidc/token
AUTHELIA_USERINFO_URL=https://your-authelia.com/api/oidc/userinfo
AUTHELIA_CALLBACK_URL=http://localhost:3000/api/auth/oidc/authelia/callback
# Generic OIDC Provider
OIDC_CLIENT_ID=your-oidc-client-id
OIDC_CLIENT_SECRET=your-oidc-client-secret
OIDC_ISSUER=https://your-idp.com
OIDC_AUTHORIZATION_URL=https://your-idp.com/authorize
OIDC_TOKEN_URL=https://your-idp.com/token
OIDC_USERINFO_URL=https://your-idp.com/userinfo
OIDC_CALLBACK_URL=http://localhost:3000/api/auth/oidc/generic/callback
# SMTP Configuration (optional - for password reset emails)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-smtp-user
SMTP_PASS=your-smtp-password
SMTP_FROM=your-from-email@example.com
SMTP_FROM_NAME=Get Healthy
Frontend (.env)
VITE_API_URL=http://localhost:3000/api
OIDC Setup
The application supports OAuth/OIDC authentication with both cloud providers (Google, GitHub) and self-hosted identity providers.
Supported Providers
| Provider | Type | Protocol |
|---|---|---|
| Cloud | OAuth 2.0 | |
| GitHub | Cloud | OAuth 2.0 |
| Keycloak | Self-hosted | OIDC |
| Authentik | Self-hosted | OIDC |
| Authelia | Self-hosted | OIDC |
| Generic | Self-hosted | OIDC |
Keycloak Setup
-
Create a realm in Keycloak or use an existing one
-
Create a client:
- Client ID:
get-healthy - Client Protocol:
openid-connect - Access Type:
confidential - Valid Redirect URIs:
http://localhost:3000/api/auth/oidc/keycloak/callback
- Client ID:
-
Get credentials from the Credentials tab
-
Get endpoints from: Realm Settings → Endpoints → OpenID Endpoint Configuration
-
Set environment variables (see above)
Authentik Setup
-
Create an OAuth2 Provider:
- Name:
Get Healthy - Client ID:
get-healthy(auto-generated) - Redirect URIs:
http://localhost:3000/api/auth/oidc/authentik/callback
- Name:
-
Create an Application linked to the provider
-
Set environment variables (see above)
Generic OIDC Provider
For any OIDC-compliant provider, use the generic configuration with your provider's endpoints.
SMTP Setup
SMTP settings can also be configured from the Admin panel (Email tab):
- Navigate to Admin → Email
- Enter your SMTP credentials
- Click "Test Connection" to verify
- Save settings
Common SMTP Providers:
- Gmail: smtp.gmail.com | Port 587 | Requires App Password
- Outlook: smtp-mail.outlook.com | Port 587 | TLS
- SendGrid: smtp.sendgrid.net | Port 587 | API Key
- Mailgun: smtp.mailgun.org | Port 587 | TLS
- AWS SES: email-smtp.us-east-1.amazonaws.com | Port 587 | SMTP credentials
Production Deployment
For production, update callback URLs to use your production domain:
FRONTEND_URL=https://get-healthy.example.com
GOOGLE_CALLBACK_URL=https://get-healthy.example.com/api/auth/oidc/google/callback
# etc.
Project Structure
/get_healthy
├── /frontend # Vue.js SPA
│ ├── /src
│ │ ├── /components # Reusable components (ConfirmModal, HelpIcon, etc.)
│ │ ├── /views # Page components (Dashboard, Workout, etc.)
│ │ ├── /stores # Pinia stores (auth, ui, notifications)
│ │ └── /router # Vue Router config
│ └── ...
├── /backend # Express.js API
│ ├── /src
│ │ ├── /routes # API routes
│ │ ├── /controllers # Route handlers
│ │ ├── /middleware # Express middleware
│ │ └── /utils # Utilities (passport-config, emailService, etc.)
│ └── /prisma # Database schema
├── /docker # Docker configs
├── docker-compose.yml
├── docker-compose.override.yml
└── README.md
API Endpoints
Auth
POST /api/auth/register- Register new userPOST /api/auth/login- LoginPOST /api/auth/logout- LogoutGET /api/auth/me- Get current userPOST /api/password-reset/forgot- Request password resetPOST /api/password-reset/reset- Reset password with tokenPOST /api/email/verify- Verify emailPOST /api/email/resend- Resend verification emailGET /api/auth/oidc/:provider- Initiate OIDC flowGET /api/auth/oidc/:provider/callback- OIDC callback
Profile
GET /api/profile- Get user profilePUT /api/profile- Update profile (displayName, height, age, weight, weightUnit)POST /api/profile/avatar- Upload avatarDELETE /api/profile- Delete all user data
Body Measurements
GET/POST /api/measurements- List/create measurementsGET /api/measurements/history- Measurement historyGET /api/measurements/latest- Latest measurement
Photo Journal
GET /api/photos- List all photos (optional?category=filter)GET /api/photos/categories- Get unique categoriesGET /api/photos/category/:category- Get photos by categoryGET /api/photos/:id- Get single photoPOST /api/photos- Upload photo (multipart/form-data)PUT /api/photos/:id- Update photo (category, notes, takenAt)DELETE /api/photos/:id- Delete photo
Routines
GET/POST /api/routines- List/create routinesGET/PUT/DELETE /api/routines/:id- Routine CRUDGET /api/routines/:id- Get routine with workouts
Workouts
GET/POST /api/routines/:routineId/workouts- List/create workoutsGET/PUT/DELETE /api/routines/:routineId/workouts/:id- Workout CRUDPOST /api/routines/:routineId/workouts/:id/complete- Complete workoutPOST /api/routines/:routineId/workouts/:id/archive- Archive workoutPOST /api/routines/:routineId/workouts/:id/unarchive- Restore workoutGET /api/routines/:routineId/workouts/archived- Get archived workoutsPOST /api/routines/:routineId/workouts/batch-archive- Batch archive by filter
Exercises
GET/POST /api/workouts/:workoutId/exercises- List/create exercisesPUT/DELETE /api/exercises/:id- Exercise CRUD
Sets & Logs
GET/POST /api/exercises/:exerciseId/sets- List/create setsPUT/DELETE /api/sets/:id- Set CRUDPOST /api/exercises/:exerciseId/aerobic-log- Log aerobic dataPUT/DELETE /api/cardio-logs/:id- Cardio log CRUDPOST /api/exercises/:exerciseId/flexibility-log- Log flexibility data
Templates
GET/POST /api/templates- List/create templatesGET/PUT/DELETE /api/templates/:id- Template CRUDGET /api/templates/public- Public templatesPOST /api/templates/from-workout/:workoutId- Save workout as templatePOST /api/templates/:id/start- Start workout from template
Exercise Limits
GET /api/limits- List all limitsGET /api/limits/exercise/:exerciseName- Get limits for exercisePOST /api/limits- Create/update limitPOST /api/limits/check- Check if value violates limitDELETE /api/limits/:id- Delete limit
Schedule
GET/POST /api/schedule- List/create scheduled workoutsPUT/DELETE /api/schedule/:id- Schedule CRUD
Reports
GET /api/reports/progress- Progress dataGET /api/reports/calories- Calorie dataGET /api/reports/by-time-of-day- Time analysisGET /api/reports/workout-frequency- Workout frequencyGET /api/reports/dashboard- Dashboard data
Dashboard
GET /api/dashboard- Dashboard data (alias for /api/reports/dashboard)
Export
GET /api/export/json- Export all data as JSONGET /api/export/csv- Export measurements as CSV
Admin
GET /api/admin/users- List usersPUT/DELETE /api/admin/users/:id- Update/delete userGET /api/admin/stats- System statisticsGET /api/admin/settings- Get admin settingsPUT /api/admin/settings/oidc- Update OIDC settingsPUT /api/admin/settings/smtp- Update SMTP settingsPOST /api/admin/settings/smtp/test- Test SMTP connection
AI
POST /api/ai/analyze- Analyze workout with OllamaGET /api/ai/health- Check Ollama connection
Scripts
# Backend
cd backend
npm run dev # Development server
npm start # Production server
npx prisma db push # Push schema to database
npx prisma generate # Generate Prisma client
npx prisma studio # Open Prisma Studio
npm test # Run tests (88 tests passing)
# Frontend
cd frontend
npm run dev # Development server (http://localhost:5173)
npm run build # Production build
npm test # Run tests (21 tests passing)
npm run test:e2e # Run E2E tests
npm run lint # Run ESLint
Database Schema
Core Entities
- User - id, email, password_hash, role, provider, avatar, verification
- UserProfile - user_id, display_name, height, age, weight, weight_unit, avatar_url
- BodyMeasurement - weight, body fat, waist, hips, chest, thighs, biceps, calves, etc.
- Routine - id, name, type (weightlifting/aerobic/flexibility/combination)
- Workout - id, routine_id, name, scheduled_date, completed_at, archived
- Exercise - id, workout_id, name, type (strength/cardio/flexibility)
- ExerciseSet - id, exercise_id, set_number, reps, weight, rpe, completed
- AerobicLog - duration, distance, heart_rate, calories, blood_pressure, spo2
- FlexibilityLog - duration, notes
Template Entities
- Template - id, name, description, type, goals[], difficulty, equipment[], is_public
- TemplateWorkout - id, template_id, name, order
- TemplateExercise - id, template_workout_id, name, type, default_sets, default_reps, rest_seconds
Scheduling Entities
- ScheduledWorkout - id, user_id, routine_id, scheduled_date, cadence_type, cadence_value
Limits Entities
- ExerciseLimit - id, user_id, exercise_name, limit_type (upper/lower), value, unit
Photo Journal Entities
- Photo - id, user_id, filename, original_name, category, notes, taken_at, created_at
License
GPLv3