Files
workh/README.md
T

98 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WorkH — working hours tracker
Log your daily hours, see how much you are over/under your weekly target,
and keep a running balance of extra/minus hours.
- Enter each day as a **total** (`7.5`, `7:15`, `7h15`; 0–24 h) — the default — or as
**start–end + break**, or mark it **off** (vacation, sick, holiday → no target).
- Full-time target defaults to **36 h 15 min/week** (7:15 per weekday).
- **Workload periods** (Settings) scale the target from a given date, e.g. 50% or 25%.
- Anyone can **create an account** and **delete it** again under _Account_. Each
account sees only its own hours.
- Several tabs or devices can be open at once: a save never overwrites newer changes
made elsewhere — edits are merged day by day.
## Project layout
| Path | |
| ----------- | ----------------------------------- |
| `frontend/` | React app (Vite) |
| `backend/` | Express API, accounts, file storage |
| `shared/` | Types used by both |
| `tests/` | Unit and integration tests (Vitest) |
| `e2e/` | End-to-end tests (Playwright) |
| `k8s/` | Kubernetes manifests |
## Configuration
| Variable | Default | |
| ------------------- | -------- | ------------------------------------------------------------------------------------------ |
| `PORT` | `3001` | |
| `DATA_DIR` | `./data` | `users.json` + `users/<id>.json` |
| `SESSION_SECRET` | random | Signs login cookies; without it everyone is logged out on restart. `npm run -s gen-secret` |
| `REGISTRATION_CODE` | — | If set, sign-up requires this invite code |
Everything else is fixed in `backend/config.ts`: at most 100 accounts, 5 sign-ups per
hour per IP, 10 failed logins per 15 min per IP, 30-day sessions. Client IPs are taken
from `X-Forwarded-For` only when the connection comes from a private network (the
ingress proxy).
Session cookies are always `Secure`; browsers accept that on `http://localhost`, but
anywhere else the app must be served over HTTPS.
## Run locally
```sh
npm install
npm run dev # http://localhost:5173 (Vite + API on :3001) — create an account there
```
`npm run dev` loads `.env` if present (e.g. `npm run -s gen-secret > .env` to stay
logged in across restarts). If you used the single-user version before, the first
account created takes over the hours in `data.json`.
## Checks
```sh
npm run lint # oxlint
npm run format:check # oxfmt (npm run format to fix)
npm run typecheck
npm test # unit + API integration tests
npx playwright install chromium # once
npm run test:e2e # builds, starts two test servers, runs Playwright
```
## CI/CD
`.github/workflows/ci.yml` runs lint, format, type, unit and e2e checks on every push and
pull request. When they pass it builds the Docker image; pushes to `main` and `v*` tags
also publish it to `ghcr.io/<owner>/<repo>` (tags: `latest`, branch, version, commit SHA).
GitHub packages are private by default: make the package public, or give the cluster an
image pull secret for ghcr.io.
## Docker
```sh
docker build -t workh . # also runs the unit tests
npm run -s gen-secret > .env.docker
docker run -p 3001:3001 --env-file .env.docker -v workh-data:/app/data workh # http://localhost:3001
```
## Kubernetes
Manifests are in `k8s/`: Deployment (1 replica, PVC-backed), Service, and a Traefik
Ingress at `workh.pumppi.dev` with the shared fail2ban middleware.
1. Set `image:` in `k8s/deployment.yaml` to the image CI published.
2. Create the sealed secret (session secret, plus an optional invite code):
```sh
npm run -s gen-secret > .env.secret
echo "REGISTRATION_CODE=<invite code>" >> .env.secret # optional
kubectl create secret generic workh-secrets -n default \
--from-env-file=.env.secret --dry-run=client -o yaml \
| kubeseal --format yaml > k8s/sealed-secret.yaml
rm .env.secret
```
3. Apply: `kubectl apply -f k8s/`