Fitness app for older athletes. Joints first. Ego second.
| Layer | Technology |
|---|---|
| Frontend | React 18 + Vite (mobile-first) |
| Backend | FastAPI (Python 3.12) |
| Database | PostgreSQL 16 |
| MCP Server | Python MCP SDK (stdio) |
| Reverse Proxy | Nginx |
| Container | Docker Compose |
cp .env.example .env
# Edit .env — set passwords and SECRET_KEY
# Use the dev nginx config (no SSL required locally)
cp nginx/nginx.dev.conf nginx/nginx.conf
docker compose up --buildApp at http://localhost API docs at http://localhost/api/docs
- Copy project to your server
- Obtain SSL certs (e.g. Let's Encrypt) and place in
nginx/ssl/:nginx/ssl/fullchain.pemnginx/ssl/privkey.pem
- Set up
.envwith strong secrets docker compose up -d --build
The MCP server lets an admin interact with Claude via natural language to populate and manage the exercise database.
Add to your claude_desktop_config.json:
{
"mcpServers": {
"old-fits": {
"command": "docker",
"args": ["exec", "-i", "oldfits_mcp", "python", "server.py"],
"env": {}
}
}
}| Tool | Description |
|---|---|
add_exercise |
Add a new exercise (Claude fills most fields automatically) |
search_exercises |
Search by name, muscle, equipment, type |
update_exercise |
Update any field on an exercise |
get_exercises_by_category |
Filter/audit exercises by category or missing fields |
tag_exercise |
Add or replace tags on an exercise |
Add 20 upper body exercises suitable for men over 55 with shoulder limitations.
Find all exercises where video_url is missing.
Update exercise 42 — set ego_check_rating to 9 and add the tag "ego-trap".
Add all major hip mobility exercises with zero impact, and tag them "hip-friendly".
| Method | Path | Description |
|---|---|---|
| POST | /auth/register |
Create account |
| POST | /auth/login |
Get JWT token |
| GET | /auth/me |
Current user |
| GET | /exercises/ |
List/filter exercises |
| GET | /exercises/search?q= |
Full-text search |
| GET | /exercises/{id} |
Exercise detail |
| POST | /exercises/ |
Create exercise (admin) |
| PATCH | /exercises/{id} |
Update exercise (admin) |
| GET | /workouts/ |
User's workouts |
| POST | /workouts/ |
Create workout |
| GET | /admin/stats |
Platform stats (admin) |
| GET | /admin/users |
User list (admin) |
old-fits/
├── docker-compose.yml
├── .env.example
├── nginx/
│ ├── Dockerfile
│ ├── nginx.conf ← production (SSL)
│ └── nginx.dev.conf ← development (no SSL)
├── backend/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── app/
│ ├── main.py
│ ├── config.py
│ ├── database.py
│ ├── models.py
│ ├── schemas.py
│ ├── auth.py
│ └── routers/
│ ├── auth.py
│ ├── exercises.py
│ ├── workouts.py
│ └── admin.py
├── mcp-server/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── server.py
├── frontend/
│ ├── Dockerfile
│ ├── package.json
│ ├── vite.config.js
│ ├── index.html
│ └── src/
│ ├── index.jsx
│ ├── App.jsx
│ ├── context/AuthContext.jsx
│ ├── hooks/useApi.js
│ ├── components/
│ │ ├── Navbar.jsx
│ │ └── ExerciseCard.jsx
│ └── pages/
│ ├── Login.jsx
│ ├── Register.jsx
│ ├── Dashboard.jsx
│ ├── Exercises.jsx
│ ├── ExerciseDetail.jsx
│ └── Workouts.jsx
└── db/
└── init.sql
Register normally via the app, then run:
UPDATE users SET is_admin = TRUE WHERE email = 'your@email.com';Or via docker:
docker exec -it oldfits_db psql -U oldfits_user -d oldfits \
-c "UPDATE users SET is_admin = TRUE WHERE email = 'your@email.com';"