kivirnz/OpenLyfta: An easy, Caddy & ExpressJS based web app for importing and sharing data from the API of the Lyfta fi

github.com · 2026-06-29 · by Long Nguyen ·github
An easy, Caddy & ExpressJS based web app for importing and sharing data from the API of the Lyfta fitness app - kivirnz/OpenLyfta

kivirnz/OpenLyfta

An easy, Caddy & ExpressJS based web app for importing and sharing data from the API of the Lyfta fitness app - kivirnz/OpenLyfta

0 stars · 0 forks · 0 watchers · JavaScript

README

Self-hosted mirror, share-card generator, shareable workout pages & Telegram auto-poster for the Lyfta workout tracker.

Features

Mirror your Lyfta data locally - syncs all workout history, exercises, and sets into a local SQLite database Share-card generation - composites body-map muscle overlays (primary in red, secondary in lighter red), workout stats (weight lifted, duration, total sets), and the OpenLyfta logo onto your workout pictures using sharp. Gender-aware: male/female body maps with automatic fallback Exercise collage cards - for workouts without a picture, automatically generates a 3×3 grid of exercise images with set counts and names, with multi-page support for 9+ exercises Shareable workout pages - generates unauthenticated /share/:id links anyone can open, showing your share card, workout stats, exercises with type badges (Weight × Reps, 2 Dumbbells, Bodyweight Assisted, etc.), decoded personal records (Est. 1RM, Max Weight, Max Set Volume, Max Reps), and set-by-set details Telegram auto-posting - automatically sends share cards to your Telegram chat when new workouts sync, with configurable caption templates supporting HTML formatting (<b>, <i>, <u>, <s>) and template tokens Rate-limit aware bulk send - bulk import sends all workouts to Telegram chronologically (oldest first) with 1.5s delay between each and automatic 429 retry handling Weight unit toggle - switch between kg and lbs everywhere (share cards, dashboard, Telegram captions) Web dashboard - browse workouts, view exercise/set details with record badges and exercise type badges, regenerate cards, send to Telegram, view logs, configure settings CloudFront exercise catalog - auto-discovers and caches Lyfta's exercise catalog for muscle-ID mapping User profile - fetches your Lyfta profile (gender, weight, name) via viewProfileGraph API Unicode & emoji decoding - handles \uXXXX escape sequences from Lyfta's API including emoji surrogate pairs Built-in log viewer - timestamped application logs viewable in the web UI settings

Quick Start Docker Compose / Portainer (recommended)

Create the config directory and Caddyfile on your server:

mkdir -p /opt/docker/openlyfta/data

Create /opt/docker/openlyfta/Caddyfile:

:80 { reverse_proxy localhost:3000 }

Deploy the stack using Portainer or docker compose:

services: openlyfta: image: wolfgirl/openlyfta:latest container_name: openlyfta restart: unless-stopped pull_policy: always ports: - "80:80" volumes: - /opt/docker/openlyfta/data:/data - /opt/docker/openlyfta/Caddyfile:/etc/caddy/Caddyfile:ro

Open http://<server-ip> in your browser. On first visit, set your admin password.

Go to Settings, enter your Lyfta email and password, then click Sync now.

Docker Run mkdir -p /opt/docker/openlyfta/data docker run -d \ --name openlyfta \ -p 80:80 \ -v /opt/docker/openlyfta/data:/data \ -v /opt/docker/openlyfta/Caddyfile:/etc/caddy/Caddyfile:ro \ --restart unless-stopped \ --pull always \ wolfgirl/openlyfta:latest Local Development npm install npm start Server runs on http://localhost:3000. Configuration All settings can be configured via the web UI under Settings, or via environment variables:

Setting Env Variable Description Default

Lyfta email LYFTA_EMAIL Your Lyfta account email -

Lyfta password LYFTA_PASSWORD Your Lyfta account password -

Lyfta device ID LYFTA_DEVICE_ID Device ID string OpenLyfta, Server, 14

Admin password OPENLYFTA_ADMIN_PASSWORD Password for web UI login Set on first login

Weight unit - kg or lbs (toggle in Settings) kg

Public URL - Base URL for shareable links (e.g. https://gym.example.com) -

Sync schedule - Cron expression for auto-sync /10 *

Telegram bot token - From @BotFather -

Telegram chat ID - Target chat/channel ID -

Telegram auto-send - Send new workouts automatically Off

Telegram Setup

Create a bot via @BotFather and get the bot token Get your chat ID (send a message to the bot, then visit https://api.telegram.org/bot<TOKEN>/getUpdates) Enter both in the web UI under Settings → Telegram

Caption Template Tokens Use these tokens in your Telegram caption template. HTML formatting is supported (<b>, <i>, <u>, <s>):

Token Description

<date> Workout date

<title> Workout title (decoded)

<workoutname> Same as <title>

<duration> Workout duration

<volume> Total volume (raw number)

<volumeformatted> Total volume formatted (e.g. 5,150kg)

<totalsets> Total number of sets

<exercises> Number of exercises

<workoutnumber> Workout number

<calories> Calories burned

<bodyweight> Body weight

<sharelink> Shareable workout page URL (requires Public URL setting)

Share Card Design Picture workouts

Original workout photo as background Body-map muscle overlay at bottom-left (front + back views, 23.4% of image height)

Primary muscles highlighted in red (#EB445A) Secondary/synergist muscles in lighter red (#C87887)

Stats stacked vertically above body model (Weight Lifted, Duration, Total Sets) - centered, bold white OpenLyfta logo between the two body figures at the bottom

No-picture workouts

Auto-generated 3×3 exercise image collage (cover fit, no gray bars) N × ExerciseName labels under each image (truncated with ellipsis if too long) Right-aligned stats overlay at bottom-right corner OpenLyfta logo at bottom-left Multi-page support for 9+ exercises

Shareable Workout Pages Each workout has a public page at /share/<id> that shows:

Share card image Workout title, date, user name Stats bar (Weight Lifted, Duration, Total Sets, Exercises) Exercise list with:

Colored type badges (Weight × Reps, 2 Dumbbells, Bodyweight Assisted, Distance · Duration, etc.) Clean weight formatting (30kg not 30.000kg) Set numbers (#1, #2, etc.) Decoded personal records (🏆 Est. 1RM: 84, 🏆 Max Weight: 60) Exercise notes with emoji support

Dashboard Features

Workout grid with card thumbnails, volume chips, and Telegram sent badges View dialog with full exercise/set breakdown, type badges, record badges, shareable link button Regenerate all cards - rebuild every share card with current settings Send all to Telegram - bulk upload oldest-first with rate limiting Reset sent badges - clear all Telegram sent flags Settings dialog with Lyfta credentials, Telegram config, caption template editor, weight unit toggle, public URL, cron schedule, and log viewer First-run onboarding prompt for history sync

Architecture ┌─────────────┐ ┌──────────────────┐ ┌──────────────┐ │ Caddy │───▶│ Express (Node) │───▶│ SQLite │ │ (port 80) │ │ supervisord │ │ (better- │ │ reverse │ │ ┌─────────────┐ │ │ sqlite3) │ │ proxy │ │ │ Sync engine │ │ └──────────────┘ └─────────────┘ │ │ (Lyfta API) │ │ │ └─────────────┘ │ ┌──────────────┐ │ ┌─────────────┐ │───▶│ Telegram │ │ │ Card gen │ │ │ Bot API │ │ │ (sharp) │ │ └──────────────┘ │ └─────────────┘ │ │ ┌─────────────┐ │ ┌──────────────┐ │ │ Share pages │ │───▶│ Public web │ │ │ (/share/:id)│ │ │ (no auth) │ │ └─────────────┘ │ └──────────────┘ └──────────────────┘

Caddy - reverse proxy, handles HTTP on port 80 Express - web UI + REST API + public share pages Sync engine - mirrors Lyfta API data, discovers CloudFront exercise catalog Card generator - composites body-map overlays + stats onto workout pictures, or generates exercise collages Share pages - server-rendered unauthenticated HTML pages for public workout sharing Telegram bot - sends share cards with configurable HTML captions, rate-limit aware

Project Structure app/ ├── src/ │ ├── index.js # Entrypoint: Express server, routes, cron, auto-sync │ ├── store.js # SQLite schema + CRUD (workouts, exercises, profiles, logs) │ ├── auth/session.js # Session-based cookie auth │ ├── lyfta/ │ │ ├── client.js # Lyfta API client (login, feeds, exercises, profile, catalog) │ │ └── sync.js # Sync orchestrator (feed walk, backfill, catalog, profile) │ ├── image/ │ │ ├── card.js # Share-card + collage generator (sharp composite + SVG) │ │ └── muscles.js # Auto-generated muscle→drawable mapping (from APK) │ ├── telegram/bot.js # Telegram sendPhoto multipart sender + caption templates │ ├── share/page.js # Server-rendered public share page HTML generator │ ├── lib/ │ │ ├── pipeline.js # Orchestrates: sync → card gen → Telegram send │ │ └── logger.js # Logger wrapper (console + SQLite logs table) │ └── routes/api.js # REST API routes ├── public/ │ ├── index.html # Dashboard GUI │ ├── stickfigure.png # OpenLyfta icon │ ├── transparent.png # OpenLyfta logo (transparent background) │ └── favicon-32.png # Favicon ├── assets/ │ ├── img/ # Logo PNGs │ ├── font/ # Google Sans TTF for card text rendering │ └── bodymaps/ # Male + female muscle overlay webps (from Lyfta APK) ├── Dockerfile # Single-image build (Node + Caddy + supervisord) ├── docker-compose.yml # Docker Compose / Portainer deployment ├── Caddyfile # Caddy reverse proxy config (HTTP port 80) ├── supervisord.conf # Process manager config (Caddy + Node) └── .gitignore

License This project is for personal use. Lyfta is a trademark of its respective owners. Neither the developer Riley Kivimäki nor OpenLyfta is affiliated with nor endorsed by Lyfta.

source

github.com · 2 images · 110 links

Comments (0)

No comments yet