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):

  1. FastAPI router in backend/app/activities/activity/public_router.py → handler in backend/app/activities/activity/crud.py (get_all_activities).
  2. Handler calls backend/app/core/database.py to obtain a SQLAlchemy session, then queries the activity model.
  3. Result passes through backend/app/activities/activity/utils.py::serialize_activity (calls print_to_log for diagnostics) and is returned as JSON.

The most‑connected internal modules are:

ModuleCaCeInstability
backend/app/core/logger.py061
backend/app/activities/activity/crud.py051

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_secret reads 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.test and 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: false in docs.yml.
  • MEDIUM – Run container as non‑root (docker/Dockerfile).
  • LOW – Set timeout-minutes on 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.