A full-stack web application for tracking training activities, setting targets, visualizing progress, and managing events.
This application was inspired by Jason Gilmore's article "10,000 Pushups And Other Silly Exercise Quests That Changed My Life", available at https://wjgilmore.com/articles/10000-pushups.
- User Authentication: Secure registration and login with email and password.
- Activity Tracking: Register various training activities (e.g., Push-ups, Sit-ups).
- Event Logging: Log repetitions for activities with timestamps.
- Target Counts: Set and track target repetition counts for each activity.
- Progress Visualization: Interactive charts (volume over time, time of day patterns) for selected activities.
- Event Management: View, edit, and delete past training events.
- Data Export: Export all training data to CSV format.
- Multi-language Support: English, Norwegian, and Spanish.
- Theme Switching: Dark and Light mode.
- Responsive Design: Works well on both mobile and desktop.
The dashboard view of the Fremgang application, showcasing the user interface for tracking activities, logging events, and visualizing progress with interactive charts.
- Frontend: Svelte, SvelteKit, TypeScript, Chart.js, i18next
- Backend: Node.js, Express.js, SQLite3, bcrypt, jsonwebtoken
Follow these steps to set up and run the project locally.
- Node.js (LTS version recommended)
- npm (Node Package Manager)
JWT_SECRET(required in production): Secret used to sign authentication tokens. Generate a strong random string, e.g.node -e "console.log(require('crypto').randomBytes(32).toString('hex'))". In development, if unset, an ephemeral random secret is generated automatically (logins will not survive restarts).DB_PATH(optional): Path to the SQLite database file. Defaults todb.sqlite.PORT(optional): Server port. Defaults to3010.
-
Clone the repository:
git clone <repository-url> cd fremgang-wip-maal
-
Install Dependencies:
npm install
This will install the dependencies for the root, server, and client-svelte projects.
To create a production-ready build of the application:
- Navigate to the root of the project.
- Run the build script:
This command will:
npm run build
- Create an optimized static build of the Svelte frontend in the
client-svelte/builddirectory. - Compile the TypeScript backend code into JavaScript in the
server/distdirectory.
- Create an optimized static build of the Svelte frontend in the
For a development environment with live reloading for both the frontend and backend:
- Navigate to the root of the project.
- Run the development start script:
This will start the Svelte development server and the Node.js server simultaneously. The application will be accessible at
npm run start:dev
http://localhost:5173.
After building the application (see "Building for Production"), you can run the production version:
- Navigate to the root of the project.
- Start the server:
This will start the Node.js server, which will serve the optimized frontend build. The application will be accessible at
npm start
http://localhost:3010(or the port specified inserver/src/index.ts).
The application can be run in a container using Podman. The SQLite database is stored in a persistent volume, so data survives container restarts.
Prerequisites:
- Podman installed
- podman-compose installed
Build and run:
# Build the image
podman build -t fremgang .
# Set a strong JWT secret first
export JWT_SECRET=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")
# Run using podman-compose (recommended, persists DB data)
podman-compose -f podman-compose.yml up -dOr run manually:
podman run -d --name fremgang -p 3010:3010 \
-v fremgang-data:/app/data \
-e DB_PATH=/app/data/db.sqlite \
-e JWT_SECRET=your-secret-here \
fremgangThe application will be accessible at http://localhost:3010.
Stop and remove:
podman-compose -f podman-compose.yml downPodman Quadlets are a simple, declarative way to define containers as systemd services. The Quadlet files are translated into proper systemd units automatically. This approach uses rootless podman — no sudo required.
1. Build the image:
podman build -t fremgang .2. Create the Quadlet directory:
mkdir -p ~/.config/containers/systemd3. Create the container Quadlet:
cat > ~/.config/containers/systemd/fremgang.container << 'EOF'
[Unit]
Description=Fremgang Training Tracker
After=network-online.target
[Container]
Image=localhost/fremgang:latest
PublishPort=3010:3010
Volume=fremgang-data:/app/data
Environment=DB_PATH=/app/data/db.sqlite
EnvironmentFile=/path/to/fremgang/.env
[Install]
WantedBy=default.target
EOFReplace /path/to/fremgang/.env with the absolute path to your project's .env file. The JWT_SECRET from that file will be passed to the container automatically. The fremgang-data volume will be created on first run.
4. Enable lingering (so it starts at boot, not just at login):
loginctl enable-linger $USER5. Start the service:
systemctl --user daemon-reexec
systemctl --user start fremgang.serviceNote: The service name is fremgang.service (not fremgang.container.service). Generated Quadlet units cannot be enabled — the [Install] section in the Quadlet handles automatic starting.
6. Verify:
systemctl --user status fremgang.serviceThe container starts at boot and restarts on failure. The SQLite database persists in the fremgang-data volume.
Manage the service:
systemctl --user stop fremgang.service
systemctl --user start fremgang.service
systemctl --user restart fremgang.service
journalctl --user -u fremgang.service -fTo change a user's password via the command line:
- Navigate to the
serverdirectory. - Run:
(e.g.,
npm run change-password -- <user-email> <new-password>
npm run change-password -- user@example.com newSecurePass123)
