The Problem
Organizations that want a Strava‑like activity tracker often have to rely on cloud SaaS platforms, which means the data lives outside their control and compliance requirements become difficult to meet.
What This Does
endurain delivers a self‑hosted fitness‑tracking stack. The backend (backend/) is a FastAPI service that stores activities, gear, followers, and authentication data in PostgreSQL. Core endpoints live in files such as backend/app/activities/activity/crud.py, backend/app/users/users/crud.py, and backend/app/auth/router.py.
The frontend (frontend/app/) is a Vue.js SPA built from frontend/app/src/main.js, router/index.js, and component files under src/components/. It talks to the API via the generated OpenAPI client and renders activity data, charts, and settings.
Deployment is containerised: a Dockerfile builds the backend image (Python 3.13‑alpine) and the Vue app, while docker-compose.yml.example wires the service together with PostgreSQL, Jaeger, and a reverse‑proxy.
How It Is Wired
Entry point – execution begins in backend/app/main.py. The startup_event function (line 39) runs on server start, calling upgrade (Alembic migration) which executes a DB connection (connection.execute). It also calls print_to_log (via many paths) – a hub function defined in backend/app/core/logger.py, referenced from 79 other files.
create_app (line 131) builds the FastAPI instance, registers routers from modules such as backend/app/activities/activity/router.py and backend/app/auth/router.py, and finally invokes read_secret (via backend/app/core/config.py) to load environment values from the filesystem (.env.example or .env.test).
Typical request flow (e.g., GET /api/v1/activities):
- FastAPI router in
backend/app/activities/activity/public_router.py→ handler inbackend/app/activities/activity/crud.py(get_all_activities). - Handler calls
backend/app/core/database.pyto obtain a SQLAlchemy session, then queries theactivitymodel. - Result passes through
backend/app/activities/activity/utils.py::serialize_activity(callsprint_to_logfor diagnostics) and is returned as JSON.
The most‑connected internal modules are:
| Module | Ca | Ce | Instability |
|---|---|---|---|
backend/app/core/logger.py | 0 | 6 | 1 |
backend/app/activities/activity/crud.py | 0 | 5 | 1 |
These modules have the widest “blast radius”: changes to logging or activity CRUD affect many callers. No circular imports were detected, simplifying refactoring.
Outbound effects:
- Database writes – 223 functions (e.g.,
backend/app/users/users/crud.py::create_user). - Filesystem reads –
read_secretreads env files; several utils handle uploaded GPX/TCX/FIT files. - Network calls – identity‑provider service (
backend/app/auth/identity_providers/service.py::_fetch_jwks) and Garmin/Strava APIs. - Cryptography – password hashing (
backend/app/auth/password_hasher.py::hash_password) and token encryption (backend/app/auth/token_manager.py).
How To Use It
# Clone the repo
git clone https://github.com/moses-y/endurain
cd endurain
# Backend dependencies (Poetry)
cd backend
poetry install # uses pyproject.toml
# Frontend dependencies (npm)
cd ../frontend/app
npm ci # installs from package-lock.json
# Create a .env file (copy the example and fill secrets)
cp ../../.env.example .env
# IMPORTANT: remove the committed .env.test (see Code Health)
# Build and run with Docker (recommended)
cd ../../docker
docker build -t endurain-backend -f Dockerfile .
docker compose -f ../docker-compose.yml.example up -d
The API starts on the port defined in .env.example (default 8000). The Vue SPA is served by the same container or can be run locally with npm run dev from frontend/app.
Real‑World Use
A corporate wellness portal can deploy endurain behind its internal VPN. Employees upload GPX files via the Vue UI; the backend stores them in PostgreSQL, runs backend/app/fit/utils.py to parse the file, and a nightly scheduler (backend/app/core/scheduler.py) generates aggregate reports. All data never leaves the corporate network.
Code Health & Issues
Measured findings (static analysis) – 9 high, 50 medium, 1 low. Key patterns: deep nesting, duplicated blocks, oversized files, broad exception handling, and TODO markers.
Code‑Health audit (11 concrete items)
- HIGH – Pin GitHub Actions to commit SHAs (
.github/workflows/*). - HIGH – Remove committed
.env.testand rotate credentials (backend/.env.test). - HIGH – Add test execution to CI (no test step in workflows).
- MEDIUM – Enable Dependabot/Renovate (
.github/dependabot.yml). - MEDIUM – Pin Docker base images by digest (
docker/Dockerfile). - MEDIUM – Add a dependency‑vulnerability scan gate (
.github/workflows). - MEDIUM – Install a pre‑commit secret‑scan hook.
- MEDIUM – Set
persist-credentials: falseindocs.yml. - MEDIUM – Run container as non‑root (
docker/Dockerfile). - LOW – Set
timeout-minuteson workflow jobs (docker-publish.yml).
Additional observations: a committed secret‑shaped file (docker-compose.yml.secrets.example) is present; tests exist (115 files) but are never run in CI; the repository includes a full license (LICENSE) and contribution guide, but the presence of a live .env file is a serious leak risk.
The Bottom Line
endurain provides a complete, Docker‑ready self‑hosted fitness tracker with a modern Vue front‑end and a FastAPI back‑end, but the codebase suffers from maintainability hotspots (deep nesting, duplicated logic) and several security‑hygiene gaps (pinned actions, exposed env file). It is suitable for teams willing to invest in refactoring and tightening CI/CD security before production deployment.