149 lines
6.8 KiB
Markdown
149 lines
6.8 KiB
Markdown
# Wotra — Working Time Tracker
|
|
|
|
A self-hosted working time tracker with a Go backend and a Svelte PWA frontend. Offline-capable, single-binary deploy.
|
|
|
|
## Features
|
|
|
|
- **Start / Stop** time tracking with an optional note.
|
|
- **Close days**: merges all entries (worked_ms = sum of durations, breaks excluded).
|
|
- **Mark days** as holiday, vacation, or sick (auto-credits expected daily hours).
|
|
- **Close weeks**: computes overtime/undertime against a frozen snapshot of your expected hours.
|
|
- **Settings history**: change hours/week with an `effective_from` date — past closed weeks are unaffected.
|
|
- **PWA + offline**: works without a network connection; syncs via IndexedDB outbox when back online.
|
|
- **CSV export**: entries, days, weeks.
|
|
- **Midnight guard**: running entries are automatically stopped at 23:59:59 if they cross midnight.
|
|
|
|
## Quick Start
|
|
|
|
Tool versions are declared in `mise.toml`. Install [mise](https://mise.jdx.dev) and run `mise install` once to get the correct Go and Node versions.
|
|
|
|
### Development
|
|
|
|
```bash
|
|
# Start both servers (Go API + Vite dev server)
|
|
mise run dev
|
|
# Go API: http://localhost:8080
|
|
# Vite UI: http://localhost:5173 (with /api proxy)
|
|
```
|
|
|
|
**Note**: Dependencies are automatically installed when needed. If you want to manually trigger dependency installation, run `mise deps`.
|
|
|
|
`AUTH_TOKEN` defaults to `devtoken` in `mise.toml`. Override it by setting the variable in your environment before running.
|
|
|
|
Open `http://localhost:5173`, go to **Settings**, enter your token.
|
|
|
|
### Production (single binary)
|
|
|
|
```bash
|
|
mise run build # builds web/ then embeds it in the Go binary
|
|
|
|
AUTH_TOKEN=mysecrettoken ./wotra
|
|
# open http://localhost:8080
|
|
```
|
|
|
|
### All tasks
|
|
|
|
| Task | Description |
|
|
|---------------|--------------------------------------------------|
|
|
| `mise run build` | Build production binary (web + Go) |
|
|
| `mise run build:web` | Build Svelte frontend only |
|
|
| `mise run build:go` | Build Go binary only |
|
|
| `mise run dev` | Start API + UI dev servers concurrently |
|
|
| `mise run dev:api` | Start Go API server only |
|
|
| `mise run dev:ui` | Start Vite dev server only |
|
|
| `mise run test` | Run all Go tests |
|
|
| `mise deps` | Install/update project dependencies |
|
|
| `mise run clean` | Remove build artifacts |
|
|
|
|
## Configuration (environment variables)
|
|
|
|
| Variable | Default | Description |
|
|
|-------------|-------------|----------------------------------------------|
|
|
| `AUTH_TOKEN` | **required** | Bearer token for API access |
|
|
| `PORT` | `8080` | HTTP port |
|
|
| `DB_PATH` | `wotra.db` | SQLite database file path |
|
|
| `TZ` | `UTC` | Timezone for day/week key calculation |
|
|
|
|
## API Reference
|
|
|
|
All API endpoints require `Authorization: Bearer <token>` except `/healthz`.
|
|
|
|
### Entries
|
|
|
|
| Method | Path | Description |
|
|
|--------|-----------------------------|--------------------------|
|
|
| POST | `/api/entries/start` | Start tracking |
|
|
| POST | `/api/entries/{id}/stop` | Stop a specific entry |
|
|
| GET | `/api/entries?from=&to=` | List entries by date |
|
|
| PUT | `/api/entries/{id}` | Edit start/end/note |
|
|
| DELETE | `/api/entries/{id}` | Soft-delete an entry |
|
|
|
|
### Days
|
|
|
|
| Method | Path | Description |
|
|
|--------|-------------------------------|--------------------------------------|
|
|
| GET | `/api/days?from=&to=` | List closed days |
|
|
| POST | `/api/days/{day}/close` | Close a day (merge entries) |
|
|
| POST | `/api/days/{day}/mark` | Mark as holiday/vacation/sick |
|
|
| DELETE | `/api/days/{day}/close` | Reopen a closed day |
|
|
|
|
### Weeks
|
|
|
|
| Method | Path | Description |
|
|
|--------|-------------------------------|--------------------------------------|
|
|
| GET | `/api/weeks?from=&to=` | List closed weeks |
|
|
| POST | `/api/weeks/{week}/close` | Close a week (compute overtime) |
|
|
| DELETE | `/api/weeks/{week}/close` | Reopen a closed week |
|
|
|
|
Week key format: `YYYY-Www` (e.g. `2024-W03`).
|
|
|
|
### Settings
|
|
|
|
| Method | Path | Description |
|
|
|--------|-------------------------|------------------------------------|
|
|
| GET | `/api/settings` | Current effective settings |
|
|
| PUT | `/api/settings` | Add a new settings version |
|
|
| GET | `/api/settings/history` | All settings history |
|
|
|
|
### Export
|
|
|
|
| Method | Path | Description |
|
|
|--------|------------------------------|--------------------------|
|
|
| GET | `/api/export/entries.csv` | Export entries as CSV |
|
|
| GET | `/api/export/days.csv` | Export days as CSV |
|
|
| GET | `/api/export/weeks.csv` | Export weeks as CSV |
|
|
|
|
### Sync
|
|
|
|
| Method | Path | Description |
|
|
|--------|-------------------|-------------------------------------|
|
|
| POST | `/api/sync/pull` | Pull changes since a version |
|
|
| POST | `/api/sync/push` | Push local changes (advisory) |
|
|
|
|
### Health
|
|
|
|
| Method | Path | Description |
|
|
|--------|-------------|--------------------------|
|
|
| GET | `/healthz` | Unauthenticated health |
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────────────┐ ┌──────────────────────────┐
|
|
│ Svelte PWA (client) │ ◄─────► │ Go service (API) │
|
|
│ - IndexedDB (Dexie) │ HTTPS │ - REST/JSON endpoints │
|
|
│ - Service Worker │ JSON │ - Business logic │
|
|
│ - Sync outbox │ │ - SQLite (modernc) │
|
|
└─────────────────────────┘ └──────────────────────────┘
|
|
```
|
|
|
|
See [PLAN.md](PLAN.md) for the full design document.
|
|
|
|
## Development Notes
|
|
|
|
- **Go module**: `github.com/wotra/wotra`
|
|
- **SQLite driver**: `modernc.org/sqlite` (pure Go, no CGO)
|
|
- **Day boundary**: entries must not cross midnight; the server auto-stops any running entry at 23:59:59 local time.
|
|
- **Settings history**: `effective_from` lets you change hours/week without rewriting past closed weeks.
|
|
- **Overtime**: stored as a signed `delta_ms` on each `closed_weeks` row. Frozen at close time.
|