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

# 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 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

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).

S
Description
No description provided
Readme
204 KiB
Languages
TypeScript 73.6%
CSS 24.2%
Dockerfile 1.2%
HTML 1%