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
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:
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:
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
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)
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:
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:
- Push auf
maintriggert.gitea/workflows/deploy.yml. - Gitea Actions baut das Docker-Image und pusht es nach
git.tool-ctrl.de/toolctrl-labs/toolctrl-website. - Tags: vollständiger, unveränderlicher Commit-SHA und
main. - Coolify-App
toolctrl-website(ocwtt7rd2zijtlszgua005k9) zieht:mainper erzwungenem Pull-Deploy auf der Marketing-VM. - Der Workflow wartet anschließend, bis
/readyzerfolgreich ist und exakt den ausgelieferten Commit-SHA alsrevisionmeldet.
Die Coolify-App läuft im Projekt Marketing, Environment production, Server
marketing-vm, Port 3000, mit persistentem Storage auf /app/data.