- JavaScript 96.8%
- CSS 2.6%
- Dockerfile 0.3%
- HTML 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .github/workflows | ||
| migrations | ||
| public | ||
| server | ||
| src | ||
| test | ||
| .dockerignore | ||
| .gitignore | ||
| context.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| eslint.config.js | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.js | ||
| README.md | ||
| tailwind.config.js | ||
| vite.config.js | ||
TimeTracker
Time tracking and invoicing app for 1099 contractors. Log hours, manage clients, generate PDF invoices, and track income analytics.
Features
- Time Entries — Log hours by client and task with rate snapshotting
- Invoicing — Generate, email, and track PDF invoices (draft → sent → paid)
- Dashboard — Revenue analytics, charts, and income breakdowns
- Clients — Manage clients, contacts, and billing addresses
- Auto-Invoicing — Scheduled automatic invoice generation (weekly/biweekly/monthly)
- Customizable Invoices — Logo, accent color, font size, footer text
- Email — Send invoices directly via Gmail SMTP
- Dark Mode — Full light/dark theme support
- Mobile Friendly — Responsive card layouts on small screens
Tech Stack
- Backend: Node.js, Express, SQLite (WAL mode)
- Frontend: React, Vite, Tailwind CSS
- PDF: PDFKit
- Charts: Recharts
- Email: Nodemailer (Gmail SMTP)
Quick Start
export ENCRYPTION_KEY=$(openssl rand -hex 32) # generate once, then keep it
docker compose up -d
Access at http://localhost:3002
Keep
ENCRYPTION_KEYsafe and stable. It encrypts the Gmail App Password at rest; if it changes, that password can no longer be decrypted and must be re-entered. A key shorter than 16 characters logs a warning at startup but still runs, so an existing install is never forced into a rotation it did not ask for.There is no authentication. Anyone who can reach the port can read and change all financial data and send mail through the configured Gmail account. Run it only on a trusted network, or put a reverse proxy with authentication in front of it.
Docker Compose
Images are published to this Forgejo instance's own container registry and can be pulled without authenticating:
services:
timetracker:
image: git.serversatho.me/serversathome/timetracker:latest
ports:
- "3002:3000"
volumes:
- timetracker-data:/data
environment:
- NODE_ENV=production
- ENCRYPTION_KEY=${ENCRYPTION_KEY:?ENCRYPTION_KEY must be set}
- TZ=America/New_York
restart: unless-stopped
volumes:
timetracker-data:
Update with docker compose pull && docker compose up -d.
Every push is tagged with its short commit SHA alongside latest, so pinning — or
rolling back — is just:
image: git.serversatho.me/serversathome/timetracker:ad6493a
Building from source instead
Skips the registry entirely — useful when testing an unmerged change:
build:
context: https://git.serversatho.me/ServersatHome/timetracker.git#main
Then docker compose build --no-cache && docker compose up -d.
Development
git clone https://git.serversatho.me/ServersatHome/timetracker.git
cd timetracker
npm install
ENCRYPTION_KEY=dev-only-key npm run dev
Runs the Express backend (with node --watch) and the Vite dev server concurrently.
npm test # unit + HTTP integration tests, against a scratch database
npm run lint # eslint
Environment Variables
| Variable | Required | Description |
|---|---|---|
ENCRYPTION_KEY |
Yes | Key for encrypting the Gmail App Password at rest. Missing is fatal at startup; shorter than 16 characters only warns |
NODE_ENV |
No | Set to production for Docker deployment |
BASE_URL |
No | Base URL for the app (used in emails) |
TZ |
Recommended | Server timezone, e.g. America/New_York. Drives "today", invoice due dates and the auto-invoice schedule. Defaults to UTC, which dates evening entries to the following day for anyone west of it |
DB_PATH |
No | Override the SQLite file location (used by tests) |
License
Private