Files
Materialschein/README.md
T
Fidelis HuberandClaude Sonnet 5 ca5d240cf6
Build and Deploy / version (push) Successful in 1s
Build and Deploy / build (push) Successful in 6s
Build and Deploy / deploy (push) Successful in 1s
Make admin seed idempotent-upsert so a corrupted password can be fixed by restart
Grund: $ in SEED_ADMIN_PASSWORD wird von Docker-Compose/Portainer als
Variablen-Referenz interpretiert und kann dadurch verstümmelt beim Container
ankommen. Seed synct das Passwort jetzt bei jedem Start neu, solange die
Variable gesetzt ist, statt nur einmalig anzulegen. README um Warnhinweis
ergänzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 18:33:36 +02:00

127 lines
6.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.
# BergVOLT Materialschein
Materialschein & Kabelrechner für BergVOLT Elektrotechnik. React + Vite + TypeScript
(PWA) im Client, Express + TypeScript + Prisma im Server, Postgres als Datenbank.
Anmeldung über Microsoft Entra ID (gleiche App-Registrierung wie das Zeiterfassungs-Tool).
## Lokale Entwicklung
```bash
# 1. Postgres lokal starten
docker compose -f docker-compose.dev.yml up -d
# 2. Server
cd server
npm install
echo 'DATABASE_URL="postgresql://materialschein:dev@localhost:5433/materialschein"' > .env
echo 'MSAL_CLIENT_ID="b1cd974c-77ce-4d93-b6e8-00e41a31adc8"' >> .env
echo 'MSAL_TENANT_ID="d9adeaaf-853b-4c4e-a822-8f1bedbb84f6"' >> .env
echo 'PORT=8787' >> .env
npx prisma migrate deploy
npm run dev # läuft auf :8787
# 3. Client (neues Terminal)
cd client
npm install
npm run dev # läuft auf :5173, proxied /api auf :8787
```
## Architektur
- `client/` React 18 + Vite + TypeScript, `vite-plugin-pwa` für Manifest/Service Worker.
MSAL.js (`@azure/msal-browser` + `@azure/msal-react`) für den Microsoft-Login.
- `server/` Express + TypeScript, validiert das MSAL-ID-Token serverseitig (JWKS von
`login.microsoftonline.com`) und liefert zusätzlich die gebaute Client-App aus
(`public/` = `client/dist`, SPA-Fallback für React-Router).
- `server/prisma/schema.prisma` zwei Tabellen: `projekte` (Baustellen) und
`material` (Positionen je Projekt, per `projektId` verknüpft).
- Ein Docker-Image (`Dockerfile`, Multi-Stage) enthält Client-Build + Server, Port 80.
## Deploy (git.prox.ws → Portainer)
Folgt [deploy-standard](https://git.prox.ws/PROX/deploy-standard) 1:1 `gitops/stack.yml`
und `.gitea/workflows/build.yml` sind bereits fertig im Repo. Zum Scharfschalten:
### 1. Azure AD zweite Redirect-URI eintragen
Bestehende App-Registrierung "BergVolt Zeiterfassung" im Azure Portal öffnen →
Authentifizierung → bei "Single-Page-Anwendung" ergänzen:
`https://materialschein.bergvolt.at` (oder die tatsächlich vorgesehene Domain/Pfad).
**Redirect-URI in `client/src/lib/authConfig.ts` steht auf `window.location.origin`
d.h. die exakte Domain, unter der die App später erreichbar ist, muss hier eingetragen werden.**
### 2. Gitea Repo-Secrets setzen
Repo → Settings → Actions → Secrets:
| Secret | Wert |
|---|---|
| `GITEAUSER` | Gitea-Username des Package-Namespace-Owners (siehe deploy-standard: Package-Push geht nur als Namespace-Owner) |
| `GITEAPASSWORD` | PAT dieses Users, Scope **nur `write:package`** (Settings → Applications → Generate New Token) |
| `PORTAINER_WEBHOOK_URL` | kommt aus Schritt 4 |
Repo → Settings → Actions: Default-Permission auf **Read and Write**, damit der
Commit-back (`gitops/stack.yml` auf `:<version>` pinnen) funktioniert.
### 3. Erster Push
```bash
git push origin main
```
`build` wird grün, `deploy` schlägt fehl (Webhook-Secret fehlt noch) erwartet.
### 4. Portainer Stack anlegen
Auf `https://10.1.10.111:9443` (Zugang laut deploy-standard bei "dominic"):
- Stacks → Add stack → **Repository**
- Repository URL: `https://git.prox.ws/BERGVOLT/Materialschein`
- Reference: `refs/heads/main`
- Compose path: `gitops/stack.yml`
- Authentication: `portainer-deploy` + dessen PAT (Org-Repos sind über das
`deploy-read`-Team bereits abgedeckt, nichts zusätzlich einzutragen)
- Environment-Variablen setzen:
- `POSTGRES_PASSWORD` = ein zufälliges, sicheres Passwort
- `JWT_SECRET` = ein zufälliger, langer String (signiert die lokalen Login-Tokens
niemals ins Repo, nur hier als Stack-Env). Beispiel zum Generieren: `openssl rand -base64 48`
- `SEED_ADMIN_USERNAME` / `SEED_ADMIN_PASSWORD` = legt beim ersten Start automatisch
ein lokales Fallback-Konto an (siehe "Lokales Konto" unten)
- **Webhook aktivieren**, **ForceUpdate an**, `ForcePullImage` bleibt aus
- Die angezeigte Webhook-URL (`.../api/stacks/webhooks/<uuid>`) als Gitea-Secret
`PORTAINER_WEBHOOK_URL` eintragen (Schritt 2).
- nginx-proxy-manager: Proxy Host auf Container `materialschein`, Port `80`.
### 5. Verifizieren
- Webhook von Hand feuern (`curl -sk -X POST <url>`) → `204`, Container laufen
(`db` + `web`).
- Push oder „Run workflow" auslösen → beide Jobs grün, `gitops/stack.yml` zeigt danach
`image: …:1.0.<N>` (kein `:latest` mehr), Commit `deploy: v1.0.<N> [skip ci]` im Log.
- Cleanup-Rule für den Package-Owner einmalig setzen (Org/User → Settings → Packages →
Cleanup Rules): keep most recent 10 + keep matching `^latest$`.
## Lokales Konto (Fallback ohne Microsoft-Login)
Auf dem Login-Screen gibt es unter dem Microsoft-Button den Link "Alternativ mit
Benutzername anmelden". Damit das funktioniert, muss im Portainer-Stack `JWT_SECRET`
gesetzt sein (Schritt 4).
Ein erstes Konto legt der Server **automatisch beim Start** an, wenn
`SEED_ADMIN_USERNAME` und `SEED_ADMIN_PASSWORD` als Stack-Env gesetzt sind (Upsert
läuft bei jedem Neustart mit und synchronisiert das Passwort neu, solange die Variable
gesetzt ist ein falsch übernommenes Passwort lässt sich also einfach per Neustart
korrigieren). Das Klartext-Passwort steht dabei nur in der Portainer-Stack-Konfiguration,
nie im Git-Repo der Server speichert ausschließlich einen bcrypt-Hash in der Datenbank.
**Vorsicht bei `$` im Passwort:** Docker-Compose/Portainer interpretieren `$` in
Env-Werten als Beginn einer Variablen-Referenz (`$xyz` wird als `${xyz}` gelesen und bei
fehlender Definition oft still zu leer aufgelöst) ein Passwort mit `$` kann dadurch
unbemerkt anders ankommen, als eingetippt. Entweder jedes `$` im Passwortwert als `$$`
escapen, oder einfacher ein Passwort ohne `$` verwenden.
Optional danach `SEED_ADMIN_PASSWORD` aus dem Stack wieder entfernen (Härtung), sobald
das Konto wie gewünscht funktioniert `SEED_ADMIN_USERNAME` kann stehen bleiben, ohne
Passwort-Variable wird nichts mehr verändert. Weitere lokale Konten aktuell nur direkt in der
Datenbank anlegbar (`INSERT INTO users …` mit einem bcrypt-Hash) ein Verwaltungs-UI
dafür gibt es noch nicht.
### Danach: zum Homescreen hinzufügen
Seite im Handy-Browser öffnen, anmelden, "Zum Home-Bildschirm hinzufügen" (iOS Safari)
bzw. "App installieren" (Android Chrome) die App ist als PWA installierbar
(`vite-plugin-pwa` generiert Manifest + Service Worker automatisch beim Build).