167 lines
6.1 KiB
Markdown
167 lines
6.1 KiB
Markdown
# ToolCTRL — Website
|
||
|
||
Mehrseitige Website im Visitenkarten-Stil: Landing Page mit Lead-Formular,
|
||
TechDay-2026-Seite, AI-GameJam-Seite mit Datei-Upload sowie Impressum und
|
||
Datenschutz. Monochrom (weiß / schwarz), an die ToolCTRL Brand Identity
|
||
angelehnt. Der Server läuft ohne npm-Pakete (nur Node-Standardbibliothek).
|
||
|
||
## Starten
|
||
|
||
```bash
|
||
node server.js
|
||
```
|
||
|
||
Der Server sucht automatisch einen freien Port (zuerst `4317`) und gibt lokale
|
||
und Netzwerk-URL aus. Stoppen mit `Strg + C`.
|
||
|
||
Voraussetzung: Node.js (getestet mit v26).
|
||
|
||
Qualitätschecks:
|
||
|
||
```bash
|
||
npm run check
|
||
npm test
|
||
```
|
||
|
||
## Aufbau
|
||
|
||
```
|
||
public/ statische Website
|
||
index.html Landing Page + Kontaktformular
|
||
techday.html TechDay Bruchsal 2026
|
||
gamejam.html AI GameJam (eigenes Styling, Spiel-Upload)
|
||
impressum.html Impressum
|
||
datenschutz.html Datenschutzerklärung
|
||
styles.css Designsystem
|
||
script.js Formular-Logik & Mobile-Navigation
|
||
assets/ Logos, Emblem, Favicon, Icons, Schriften, Mockups
|
||
server.js abhängigkeitsfreier Node-HTTP-Server + API
|
||
leads/ historische NDJSON/CSV-Leads (nur noch Backfill-Quelle)
|
||
gamejam/ GameJam-Einreichungen, Metadaten (wird automatisch angelegt)
|
||
```
|
||
|
||
`leads/`, `gamejam/` und hochgeladene Spiele sind per `.gitignore` vom Repo
|
||
ausgeschlossen.
|
||
|
||
## Plattform-Leads
|
||
|
||
`POST /api/lead` validiert das Kontaktformular serverseitig und sendet die
|
||
Anfrage an HiveGrids nativen Endpoint `POST /v1/platform/leads`. Pflichtfelder
|
||
sind `name`, `email`, `nachricht` und die vom Browser erzeugte `submissionId`.
|
||
Ein Honeypot-Feld fängt einfache Bots ab.
|
||
|
||
Der Browser behält die Submission-ID nach einem fehlgeschlagenen Versuch mit
|
||
semantisch gleichem Payload bei. Wird das Formular inhaltlich geändert, bindet er
|
||
vor dem nächsten Senden eine neue ID. Der Server verwendet dieselbe ID als
|
||
`externalId`, `metadata.submissionId` und `Idempotency-Key`; auch der einmalige
|
||
Netzwerk-/5xx-Retry verwendet exakt denselben Key und Payload. Dem Formular wird
|
||
nur dann Erfolg gemeldet, wenn die Plattform den Lead mit `201` angenommen hat.
|
||
API-, Netzwerk- und Konfigurationsfehler bleiben Fehler.
|
||
|
||
Die bisherige Zuordnung `Messe — Landing Page` bleibt im nativen Vertrag als
|
||
`sourceDetail` und `utmCampaign=messe-landing-page` erhalten.
|
||
|
||
Erforderliche Runtime-Variablen:
|
||
|
||
```bash
|
||
PLATFORM_LEADS_API_URL=https://api.hivegrid.de
|
||
PLATFORM_LEADS_API_KEY=<source-spezifischer Key für toolctrl-website, 32–512 Zeichen>
|
||
```
|
||
|
||
Der Bearer-Key gehört ausschließlich in die Server-Runtime (Coolify), nie ins
|
||
Repository oder nach `public/`. Ohne gültige Konfiguration liefert die Route
|
||
`503` und `/readyz` bleibt nicht bereit.
|
||
|
||
Die bestehenden Dateien `leads/leads.ndjson` und `leads/leads.csv` sind
|
||
Legacy-Bestände für den kontrollierten R-LEADS-Backfill. Der laufende Producer
|
||
erstellt oder verändert sie nicht mehr und meldet bei Plattformfehlern keinen
|
||
lokalen Ersatz-Erfolg.
|
||
|
||
## GameJam-Upload & Virenscan (ClamAV)
|
||
|
||
`POST /api/gamejam/upload` nimmt eine `.html`-Datei entgegen (max. 5 MB).
|
||
**Jede hochgeladene Datei wird vor dem Speichern per ClamAV auf Viren
|
||
geprüft.** Befund → Ablehnung; ist der Scanner nicht erreichbar, wird der
|
||
Upload ebenfalls abgelehnt (fail-closed, kein ungeprüftes Speichern).
|
||
|
||
Der Server spricht den ClamAV-Daemon `clamd` direkt über sein Socket-Protokoll
|
||
an — keine zusätzliche Abhängigkeit. Voraussetzung: ClamAV ist installiert und
|
||
`clamd` läuft. Beim Start meldet der Server `Virenscan: aktiv` bzw. eine
|
||
Warnung, falls `clamd` nicht erreichbar ist.
|
||
|
||
### ClamAV einrichten — macOS
|
||
|
||
```bash
|
||
brew install clamav
|
||
cd "$(brew --prefix)/etc/clamav"
|
||
cp freshclam.conf.sample freshclam.conf
|
||
cp clamd.conf.sample clamd.conf
|
||
# In BEIDEN Dateien die Zeile "Example" entfernen oder auskommentieren.
|
||
# In clamd.conf zusätzlich diese zwei Zeilen aktivieren (Raute entfernen):
|
||
# TCPSocket 3310
|
||
# TCPAddr 127.0.0.1
|
||
freshclam # Virendefinitionen laden (einmalig)
|
||
clamd # Daemon starten
|
||
```
|
||
|
||
### ClamAV einrichten — Linux (Debian/Ubuntu)
|
||
|
||
```bash
|
||
sudo apt install clamav clamav-daemon
|
||
# In /etc/clamav/clamd.conf: TCPSocket 3310 und TCPAddr 127.0.0.1 setzen
|
||
sudo systemctl restart clamav-daemon
|
||
```
|
||
|
||
### Konfiguration
|
||
|
||
Host/Port und An/Aus stehen in `server.js` im Objekt `CLAMAV`. Mit
|
||
`enabled: false` läuft der Upload ohne Virenscan (nur für lokale Tests
|
||
ohne ClamAV gedacht).
|
||
|
||
Per Environment kann der Runtime-Betrieb konfiguriert werden:
|
||
|
||
```bash
|
||
PORT=3000
|
||
HOST=0.0.0.0
|
||
DATA_DIR=/app/data
|
||
CLAMAV_HOST=127.0.0.1
|
||
CLAMAV_PORT=3310
|
||
CLAMAV_ENABLED=true
|
||
APP_REVISION=<git-sha>
|
||
PLATFORM_LEADS_API_URL=https://api.hivegrid.de
|
||
PLATFORM_LEADS_API_KEY=<Runtime-Secret>
|
||
```
|
||
|
||
`/healthz` ist die leichte Liveness-Probe für Container und Coolify.
|
||
`/readyz` prüft zusätzlich die Plattform-Lead-Konfiguration, Schreibzugriff auf
|
||
die GameJam-Datenverzeichnisse und, falls aktiv, die Erreichbarkeit von ClamAV.
|
||
Der Container-Healthcheck verwendet `/readyz`, damit eine fehlende
|
||
Plattform-Lead-Konfiguration einen Deploy nicht gesund erscheinen lässt.
|
||
|
||
### Funktion testen
|
||
|
||
Eine Datei mit der EICAR-Testsignatur hochladen — `clamd` meldet dann
|
||
`Eicar-Test-Signature FOUND`, der Upload wird abgelehnt.
|
||
|
||
## Hinweis für die Messe
|
||
|
||
Für den QR-Code am Stand muss der Rechner im selben WLAN erreichbar sein.
|
||
Soll die Seite öffentlich laufen, hinter einen Reverse-Proxy mit HTTPS setzen
|
||
oder bei einem Hoster bereitstellen — `tool-ctrl.de`.
|
||
|
||
## Deployment
|
||
|
||
Deployment folgt dem Gitea-Infra-Standard aus `toolctrl-labs/infra`:
|
||
|
||
1. Push auf `main` triggert `.gitea/workflows/deploy.yml`.
|
||
2. Gitea Actions baut das Docker-Image und pusht es nach
|
||
`git.tool-ctrl.de/toolctrl-labs/toolctrl-website`.
|
||
3. Tags: vollständiger, unveränderlicher Commit-SHA und `main`.
|
||
4. Coolify-App `toolctrl-website` (`ocwtt7rd2zijtlszgua005k9`) zieht
|
||
`:main` per erzwungenem Pull-Deploy auf der Marketing-VM.
|
||
5. Der Workflow wartet anschließend, bis `/readyz` erfolgreich ist und exakt
|
||
den ausgelieferten Commit-SHA als `revision` meldet.
|
||
|
||
Die Coolify-App läuft im Projekt `Marketing`, Environment `production`, Server
|
||
`marketing-vm`, Port `3000`, mit persistentem Storage auf `/app/data`.
|