Files
Materialschein/README.md
T
Fidelis HuberandClaude Sonnet 5 97c4aa1c68
Build and Deploy / version (push) Successful in 1s
Build and Deploy / build (push) Successful in 21s
Build and Deploy / deploy (push) Successful in 1s
Add local username/password login as fallback when Microsoft login isn't available
Neues "users"-Table (bcrypt-Hash, nie Klartext), POST /api/auth/login gibt ein
selbst-signiertes JWT (HS256) aus. requireAuth unterscheidet MSAL- (RS256) und
lokale Tokens (HS256) anhand des alg-Headers. Server legt beim Start optional
ein erstes lokales Konto an, wenn SEED_ADMIN_USERNAME/SEED_ADMIN_PASSWORD als
Stack-Env gesetzt sind (idempotent, Klartext-Passwort landet nie im Repo).

Frontend: Login-Screen hat jetzt einen Alternativ-Link zu Benutzername/Passwort,
App.tsx kombiniert MSAL- und lokalen Auth-Status.

Temporärer test-build-only.yml Workflow zur Docker-Build-Verifikation ohne
Registry-Push (wird nach dem Test wieder entfernt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 17:56:40 +02:00

120 lines
5.7 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 (idempotent
läuft bei jedem Neustart mit, legt aber nur an, was noch nicht existiert). 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.
Optional danach `SEED_ADMIN_PASSWORD` aus dem Stack wieder entfernen (Härtung), sobald
das Konto einmal erfolgreich angelegt wurde `SEED_ADMIN_USERNAME` kann stehen bleiben,
ohne Passwort passiert dann nichts mehr. 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).