init
release-tag / release-image (push) Has been cancelled

This commit is contained in:
2026-08-14 17:42:54 +02:00
parent 18a48d2285
commit 222b5d2413
22 changed files with 3013 additions and 1 deletions
+204 -1
View File
@@ -1,2 +1,205 @@
# flancer
# Pocketwatch Go
Ein schlanker, self-hosted Zeiterfasser in Go – funktional an `winnicodes/pocketwatch` angelehnt, aber ohne React, Node.js, PHP oder Nginx im Anwendungs-Stack.
Die Anwendung besteht aus einem Go-Binary, eingebettetem HTML/CSS/Vanilla-JavaScript und SQLite über `modernc.org/sqlite` (pure Go, kein CGO notwendig).
## Enthalten
- Start-/Stop-Timer mit Live-Anzeige
- Laufender Timer überlebt Reloads und Gerätewechsel
- Kunde + Tätigkeit, Kunden-Autocomplete nach letzter Nutzung
- Tages- und Wochensumme
- Verlauf gruppiert nach Tagen
- Suche über Kunde und Tätigkeit
- Zeitraumfilter: Tag, Woche, Monat, Jahr, alle
- Zeitraum vor/zurück schalten
- Einträge nachtragen, bearbeiten und löschen
- Rundung 1–60 Minuten, normal oder immer aufwärts
- CSV-Export (Semikolon + UTF-8 BOM für Excel)
- PDF-Export ohne PDF-Framework
- Export wahlweise für aktuelle Ansicht oder freien Datumsbereich, auf-/absteigend und kompakt
- Responsive Desktop-/Mobile-Oberfläche
- Mehrbenutzerbetrieb mit strikt getrennten Daten
- Admin-/Benutzerrollen
- Benutzer anlegen, deaktivieren und Passwörter zurücksetzen
- Eigenes Passwort ändern
- Keine öffentliche Registrierung nach der Ersteinrichtung
- Sessions in SQLite, HttpOnly-Cookie, CSRF-Token
- Login-Rate-Limit
- CSP und weitere Security-Header
- SQLite WAL, Foreign Keys, Busy Timeout
- Healthcheck unter `/healthz`
- Docker/Compose, unprivilegierter Runtime-Benutzer, alle Linux-Capabilities entfernt
## Abhängigkeiten
Zur Laufzeit gibt es nur eine externe Go-Abhängigkeit:
```text
modernc.org/sqlite
```
Alles andere verwendet die Go-Standardbibliothek. Das Frontend hat **keine** npm-/Node-Abhängigkeiten und keinen separaten Build-Schritt.
## Schnellstart mit Docker Compose
```bash
docker compose up -d --build
```
Danach:
```text
http://localhost:8080
```
Beim ersten Aufruf erscheint die Ersteinrichtung. Der erste Account wird Administrator.
Die Daten liegen im Docker-Volume `pocketwatch-data` in `/data/pocketwatch.db`. Das Image enthält außerdem einen Docker-Healthcheck gegen `/healthz`.
Bei einem Host-Bind-Mount statt eines Named Volumes muss das Zielverzeichnis für UID/GID `10001` schreibbar sein.
## Lokal ohne Docker
Voraussetzungen:
- Go 1.26+
Dann:
```bash
go mod tidy
DATA_DIR=./data APP_ADDR=:8080 go run ./cmd/pocketwatch
```
Oder:
```bash
make run
```
## Konfiguration
| Variable | Default | Bedeutung |
|---|---:|---|
| `APP_ADDR` | `:8080` | Listen-Adresse des HTTP-Servers |
| `DATA_DIR` | `/data` | Verzeichnis für `pocketwatch.db` |
| `COOKIE_SECURE` | `false` | Auf `true` setzen, wenn die App ausschließlich über HTTPS erreichbar ist |
### Hinter Reverse Proxy / HTTPS
Wenn z. B. Caddy, Traefik oder nginx TLS terminiert:
```yaml
environment:
COOKIE_SECURE: "true"
```
Die App setzt selbst keine CORS-Header. API und UI sind als Same-Origin-Anwendung gedacht.
## Datenmodell
SQLite enthält vier Kernbereiche:
- `users` – Accounts, Rollen, Aktivstatus
- `sessions` – gehashte Session-Tokens + CSRF-Token
- `entries` – Zeiteinträge, immer mit `user_id`
- `user_settings` – persönliche Rundungs-, Export- und Anzeigeeinstellungen
Ein partieller Unique-Index stellt sicher, dass pro Benutzer höchstens ein laufender Timer existiert.
Zeitpunkte werden als Unix-Millisekunden gespeichert. Die Web-Oberfläche verwendet die lokale Browser-Zeitzone; Exporte verwenden die persönliche IANA-Zeitzone, z. B. `Europe/Berlin`.
## Sicherheit
### Passwörter
Passwörter werden mit PBKDF2-HMAC-SHA256, zufälligem Salt und 310.000 Iterationen gespeichert. Die Implementierung nutzt `crypto/pbkdf2`, `crypto/sha256` und `crypto/rand` aus der Go-Standardbibliothek von Go 1.26.
### Sessions
- 256-Bit zufällige Session-Tokens
- nur SHA-256-Digest des Session-Tokens in SQLite
- HttpOnly-Cookie
- SameSite=Lax
- optional `Secure`
- serverseitiges Ablaufdatum
- Sessions werden beim Deaktivieren eines Benutzers oder Passwort-Reset invalidiert
### CSRF
Schreibende API-Aufrufe benötigen zusätzlich ein zufälliges, sitzungsgebundenes `X-CSRF-Token`.
### Mandantentrennung
Jede SQL-Operation auf Zeiten enthält die `user_id` aus der authentifizierten Session. IDs aus einem anderen Benutzerkonto reichen daher nicht aus, um fremde Einträge zu lesen oder zu verändern.
## Backup
Wegen WAL sollte die Datenbank nicht blind während Schreibzugriffen als einzelne Datei kopiert werden. Der einfachste konsistente Weg bei Docker Compose:
```bash
docker compose stop pocketwatch
docker run --rm \
-v pocketwatch-go_pocketwatch-data:/data:ro \
-v "$PWD:/backup" \
alpine:3.22 \
tar czf /backup/pocketwatch-backup.tgz -C /data .
docker compose start pocketwatch
```
Der konkrete Volume-Name kann je nach Compose-Projektname abweichen (`docker volume ls`).
## Projektstruktur
```text
pocketwatch-go/
├── cmd/pocketwatch/main.go
├── internal/app/
│ ├── auth.go
│ ├── db.go
│ ├── export.go
│ ├── server.go
│ ├── *_test.go
│ └── web/
│ ├── index.html
│ ├── login.html
│ ├── app.css
│ ├── app.js
│ └── login.js
├── Dockerfile
├── docker-compose.yml
├── Makefile
└── go.mod
```
## Tests
```bash
go test ./...
```
Enthalten sind u. a. Tests für PBKDF2, Rundungslogik und den minimalen PDF-Writer.
## Bewusste Unterschiede zum ursprünglichen Pocketwatch
Diese Implementierung übernimmt das Produktkonzept und die wichtigsten Bedienabläufe, ist aber technisch ein Neuaufbau:
- SQLite statt JSON-Dateien
- Go statt PHP
- Vanilla JS statt React/Vite/Tailwind
- kein Node.js im Build oder Betrieb
- Login und Mehrbenutzerbetrieb
- Adminverwaltung
- serverseitige Datenisolation
- CSRF- und Session-Schutz
- Healthcheck und Security-Header
Die Oberfläche orientiert sich am dunklen, kompakten Amber-Design des Originals, ist aber kein 1:1 kopierter Frontend-Quellcode. Sie ist derzeit bewusst deutschsprachig; das Datenmodell hält die Spracheinstellung bereits für eine spätere vollständige Lokalisierung vor.
## Inspiration
Inspiriert von [`winnicodes/pocketwatch`](https://github.com/winnicodes/pocketwatch), das als minimalistischer self-hosted Zeiterfasser unter MIT veröffentlicht ist.