- Vollständiger kopierfertiger Deploy-Block für server-internal MIT npm ci: pull -> npm ci -> Lade-Test der nativen Module -> restart -> Selbsttest, als eine &&-Kette, die bei jedem Fehler VOR dem Neustart abbricht (alter Dienst läuft dann unberührt weiter). Bisher stand dort nur pull + restart ohne npm ci -- genau die Lücke, durch die Code und Pakete auseinanderliefen. - Wächter: 18 -> 19 Punkte (Sicherungsprüfung dazugekommen). - Zwei offene Punkte ergänzt: ausstehender Server-Neustart (Kernel/OpenSSL liegen bereit) und der ausstehende server-internal-Deploy. - Veralteten Datenschutz-404-Eintrag abgehakt (heute live 401 verifiziert). Co-Authored-By: Claude Opus 5 <[email protected]>
16 KiB
DOGFATHER UNIVERSE — Go-Live-Anleitung
⚠️ ZUERST LESEN: Die echte Seite läuft auf dem NETCUP-SERVER, nicht auf Cloudflare
Seit dem öffentlichen Start (21.08.2026) wird dogfather-universe.com vom eigenen
Netcup-Server ausgeliefert. Der alte Cloudflare-Worker existiert zwar noch und lässt sich
auch weiterhin deployen — er erreicht die echte Domain aber nicht mehr.
Das ist die gefährlichste Stelle im ganzen Projekt: npx wrangler deploy läuft ohne Fehler
durch und meldet „Deployed", die Änderung ist danach auf …workers.dev sichtbar — und auf
dogfather-universe.com passiert nichts. Genau so ist es am 22.08.2026 beim Sprachfenster-
Fix passiert (siehe unten). Wer nur die Erfolgsmeldung von Wrangler liest, meldet „ist live",
obwohl Filipe auf dem Handy weiterhin den kaputten Stand sieht.
| Adresse | Läuft wo | Wird von wo beliefert |
|---|---|---|
dogfather-universe.com ← die echte Seite |
Netcup, Caddy → localhost:4100, systemd-Dienst dogiweb.service, Verzeichnis /home/dogiweb/dogfather-universe/ |
Gitea git.dogfather-universe.com/DogFatherGit/dogfather-universe, Zweig main |
www.dogfather-universe.com |
dasselbe (Caddy nimmt beide Namen) | dasselbe |
dogfather-universe.dogfather1608.workers.dev |
Cloudflare Worker (Altbestand) | npx wrangler deploy |
Erkennungsmerkmal im Zweifel: curl -sI https://dogfather-universe.com/ | grep -i via
→ zeigt via: 1.1 Caddy, also Netcup. Käme die Seite von Cloudflare, stünde da kein Caddy.
So wird eine Änderung wirklich live (der einzige gültige Weg)
cd ~/Documents/Obelix/DogiHompage
# 1. Änderung committen
git add <dateien> && git commit
# 2. In die Ablage schieben (Gitea ist die Quelle der Wahrheit)
git push gitea master:main
# 3. Auf dem Server holen
ssh dogfather-server "cd /home/dogiweb/dogfather-universe && git pull --ff-only origin main"
Statische Dateien (HTML/CSS/JS/Bilder) sind damit sofort live — der Express-Dienst liefert das Verzeichnis direkt aus, ein Neustart ist dafür nicht nötig.
⚠️ ES GIBT ZWEI CHECKOUTS AUF DEM SERVER, NICHT EINEN
Am 22.08.2026 beim Ausrollen des Webdesign-Bereichs gefunden — diese Datei beschrieb vorher nur den ersten und war damit unvollständig:
| Dienst | Checkout | Liefert |
|---|---|---|
dogiweb.service (Port 4100) |
/home/dogiweb/dogfather-universe/ |
die öffentliche Website + server/ |
dogiintern.service (Port 4200) |
/home/dogiintern/dogfather-universe/ |
die API unter postfach.dogfather-universe.com + server-internal/ |
Ein git pull in /home/dogiweb ändert an server-internal/ also gar nichts.
Wer nur dort zieht und danach dogiintern.service neu startet, startet den
Dienst mit unverändertem Code neu und wundert sich, warum nichts passiert.
claudian hat auf /home/dogiintern/ keinen Zugriff (weder lesend noch
über den begrenzten sudo). Änderungen an server-internal/ muss deshalb
Filipe selbst ausrollen.
Der Standard-Fall (nur Code geändert, keine neuen/geänderten Pakete):
sudo -u dogiintern git -C /home/dogiintern/dogfather-universe pull --ff-only origin main
sudo systemctl restart dogiintern.service
Der volle Fall (auch package.json/package-lock.json geändert — dann MUSS
npm ci mit, sonst laufen Code und Pakete auseinander). Ein Block, bricht bei
jedem Fehler ab, und startet den Dienst erst neu, wenn Pull, Installation und
ein Lade-Test der nativen Module (better-sqlite3) durch sind — schlägt etwas
fehl, läuft der alte Dienst unberührt weiter:
cd /home/dogiintern/dogfather-universe && \
sudo -u dogiintern git pull --ff-only origin main && \
sudo -u dogiintern npm --prefix server-internal ci && \
sudo -u dogiintern node -e "require('/home/dogiintern/dogfather-universe/server-internal/node_modules/better-sqlite3'); console.log('native Module laden: ok')" && \
sudo systemctl restart dogiintern.service && \
sleep 2 && echo "Dienst:" $(systemctl is-active dogiintern.service) && \
curl -s -o /dev/null -w "health nach Neustart: %{http_code}\n" https://postfach.dogfather-universe.com/health
Erwartete letzte Zeilen: native Module laden: ok, Dienst: active,
health nach Neustart: 200. Kommt stattdessen ein Abbruch VOR dem Neustart,
ist nichts passiert — der alte Dienst läuft weiter, und der Fehler (meist ein
fehlgeschlagener npm ci) lässt sich in Ruhe ansehen.
Server-Code geändert? Dann ist ein Neustart PFLICHT
Statische Dateien sind nach dem Pull sofort live. Server-Code nicht — der läuft weiter mit dem alten Stand, bis der Dienst neu startet.
ssh dogfather-server "sudo systemctl restart dogiweb.service" # nach Änderungen in server/
ssh dogfather-server "sudo systemctl restart dogiintern.service" # nach Änderungen in server-internal/
Warum das hier besonders steht (Vorfall 22.08.2026): Nach dem Pull des
Webdesign-Bereichs war /webdesign/ für einige Minuten ohne Zugangsschutz
öffentlich erreichbar (HTTP 200 statt der Umleitung zur Zugangswand). Die
HTML-Dateien waren durch den Pull sofort da — die Schranke in
server/webdesign-gate.js lief aber erst nach dem Neustart von
dogiweb.service. Bei einem Bereich, der ausdrücklich nicht öffentlich sein
soll, ist genau dieses Zeitfenster der gefährliche Teil eines Deploys.
Merksatz: Erst neu starten, dann „ist live" melden — und danach mit
curl -I prüfen, dass die Schranke wirklich greift:
curl -sI https://dogfather-universe.com/webdesign/ | grep -iE "^location|x-robots-tag"
# erwartet: location: /webdesign/zugang.html?next=... und x-robots-tag: noindex, ...
Achtung bei Zweig-Namen: lokal heißt der Zweig master, auf dem Server und in Gitea main.
Deshalb master:main beim Push. Ein blankes git push schiebt sonst nach gitea/master —
einen alten, abgehängten Zweig, den niemand ausliefert.
Abhängigkeiten: npm ci, nicht npm install
Seit dem 26.08.2026 ist package-lock.json versioniert. Auf dem Server gilt
deshalb:
npm ci # richtig: installiert exakt das, was in der Lock-Datei steht
npm install # falsch: darf neuere Fassungen ziehen und schreibt die Datei um
Warum der Unterschied zählt. In package.json steht "express": "^4.21.2" —
das erlaubt alles unter 5.0. Vor dieser Umstellung lief auf dem Server deshalb
express 4.22.2, während hier 4.21.2 stand. Geprüft wurde also nie ganz das,
was ausgeliefert wurde. Solche Unterschiede fallen nicht beim Deploy auf,
sondern im Betrieb — und dann sucht man den Fehler im eigenen Code.
npm ci löscht node_modules vorher vollständig und baut streng nach der
Lock-Datei neu auf. Es schreibt sie nie um; passt sie nicht zur package.json,
bricht es ab, statt still etwas anderes zu installieren.
Die Lock-Datei war früher ausgeschlossen (.gitignore), weil ein git pull
daran scheiterte: Git überschreibt keine unverfolgte Datei, auch wenn ihr Inhalt
derselbe ist. Das war ein formaler Konflikt, kein inhaltlicher — nachgemessen
waren beide Fassungen Byte für Byte identisch. Wenn dieser Fall irgendwo erneut
auftritt, ist die Lösung, die Datei auf dem Server einmal zu entfernen und
danach aus dem Repo zu holen:
ssh dogfather-server "rm -f /home/dogiweb/dogfather-universe/server/package-lock.json"
ssh dogfather-server "cd /home/dogiweb/dogfather-universe && git pull --ff-only origin main"
⚠️ Am Webdesign-Bereich geändert? Dann ZWEI Zahlen hochzählen
Die App speichert Dateien zwischen. Nach einer Änderung an webdesign/
oder assets/ müssen beide Stellen hoch, sonst bekommen Geräte, die
die Seite schon einmal geöffnet haben, weiterhin den alten Stand:
webdesign/sw.js const CACHE_NAME = "dogfather-webdesign-vNN";
assets/js/wd-core.js .register("/webdesign/sw.js?v=NN", …)
Warum zwei und nicht eine. CACHE_NAME wirft den Zwischenspeicher
weg, sobald der Service Worker startet. Die Nummer in der Adresse sorgt
dafür, dass er überhaupt neu geladen wird — und das ist hier nicht
selbstverständlich:
Gemessen am 26.08.2026: Der Server liefert sw.js mit
Cache-Control: no-cache aus. Cloudflare ersetzt das durch
max-age=14400 — vier Stunden, auch bei cf-cache-status: MISS.
Ursache ist eine feste „Browser Cache TTL" in den Cloudflare-Einstellungen.
Ohne die Nummer in der Adresse erreicht jede Änderung am Service Worker
die Geräte also bis zu vier Stunden zu spät. Solange alles läuft, fällt
das nie auf; ist der Stand fehlerhaft, sind es vier Stunden ohne
Reparaturmöglichkeit.
Was auf dem Server automatisch läuft
Beides über /etc/cron.d/, beides als root. Die Skripte liegen im Repo
und werden über den normalen git pull aktualisiert — ein eigener
Deploy-Schritt ist nicht nötig.
| Wann | Was | Skript |
|---|---|---|
| täglich 03:15 | Sicherung von Datenbank und Uploads | server-internal/sicherung.sh |
| alle 5 Minuten | Wächter über 19 Punkte (inkl. „läuft die Sicherung?") | server-internal/waechter.mjs |
Sicherung. Nutzt SQLites eigenen .backup-Befehl, keine Dateikopie —
die Datenbank ist 778 KB groß, ihr WAL 4,1 MB, eine Kopie der .db
allein wäre also weitgehend leer. 14 tägliche Stände, sonntags zusätzlich
ein Wochenstand (8 davon). Jeder Stand wird sofort nach dem Anlegen
geprüft. Wiederherstellung: server-internal/wiederherstellen.sh, zeigt
ohne Argument die verfügbaren Stände.
Wächter. Prüft sechs Dienste, neun Adressen, Plattenplatz und zwei Zertifikatslaufzeiten. Meldet per Push, und zwar nur bei Zustandswechsel — nicht alle fünf Minuten dasselbe. Läuft als root und verschickt selbst; ein Wächter, der über den internen Dienst meldet, wäre ausgerechnet dann still, wenn dieser das Problem ist.
Meldeweg prüfen, ohne auf eine Störung zu warten:
sudo /usr/local/bin/node /home/dogiweb/dogfather-universe/server-internal/waechter.mjs --probe
⚠️ Grenze: Ist der Server als Ganzes weg — Netz, Strom, Hardware —, meldet auch der Wächter nichts. Dagegen hilft nur eine Überwachung außerhalb der Maschine.
Die Verwaltung ist eine eigene App
Seit dem 26.08.2026 hat webdesign/verwaltung.html ein eigenes
Manifest (verwaltung.webmanifest) mit eigener id und eigenem Symbol.
Ohne die unterschiedliche id hielten Browser beide für dieselbe App,
und die zweite Installation überschriebe die erste.
Das Manifest steht in der Ausnahmeliste von server/webdesign-gate.js —
ohne diesen Eintrag antwortet es mit 302 auf die Zugangswand, und der
Browser bietet „App installieren" gar nicht erst an.
Pflicht-Prüfung nach JEDEM Deploy
Nicht auf die Erfolgsmeldung des Deploy-Befehls verlassen, sondern die echte Domain fragen:
# Kommt meine Änderung wirklich auf der Seite an, die Filipe benutzt?
curl -s https://dogfather-universe.com/assets/css/main.css | grep "<mein Merkmal>"
Erst wenn das anschlägt, gilt eine Änderung als erledigt. Bei optischen Änderungen zusätzlich mit einem echten Browser im Handy-Format nachsehen (Playwright, Viewport 390×844) und einen Screenshot machen — Text im Quelltext beweist noch nicht, dass es auch richtig aussieht.
Der Cloudflare-Worker (Altbestand)
Bleibt vorerst bestehen, liefert aber nur noch …workers.dev aus. Ein Deploy dorthin ändert
an der echten Seite nichts. Der Versuch, die Domain per wrangler wieder anzubinden, schlägt
bewusst fehl:
Hostname 'dogfather-universe.com' already has externally managed DNS records
Das ist kein Fehler, den man beheben sollte — es ist die Schutzwirkung davon, dass die
Domain jetzt auf Netcup zeigt. routes mit custom_domain = true steht deshalb nur noch aus
historischen Gründen in wrangler.toml.
Der Bewerbungs-Postfach-Worker ist davon unabhängig und läuft weiter auf Cloudflare:
https://dogfather-universe-postfach.dogfather1608.workers.dev (API_BASE_URL in
assets/js/forms.js, index.html, postfach.html).
Git-Ablage
- Quelle der Wahrheit: Gitea,
git.dogfather-universe.com/DogFatherGit/dogfather-universe, Zweigmain(auf dem eigenen Server, kostenlos, in eigener Hand). - Lokal heißt das Fernziel
gitea, der Zweigmaster. - Auf dem Server heißt das Fernziel
origin, der Zweigmain. gitea/masterist ein alter, abgehängter Zweig (Stand44086b0) — nicht benutzen.- Sicherungszweig
server-stand-vor-abgleich-22-08-2026auf dem Server: der Stand, bevor die beiden Fassungen am 22.08.2026 zusammengeführt wurden. Kann irgendwann weg, kostet nichts.
Nie direkt auf dem Server Dateien bearbeiten, ohne sie danach in Gitea nachzutragen. Genau
dadurch waren am 22.08.2026 zwei Reparaturen (Autofokus in gate.html, abgeschnittene
Öffnen-Knöpfe in verwaltung.html) nur auf dem Server vorhanden und wären beim nächsten
Deploy vom Rechner aus überschrieben worden.
Offene Punkte
-
Server-Neustart steht aus (seit 28.08.2026).
unattended-upgradeshat Kernel (6.12.105) und OpenSSL (3.5.7) eingespielt, aber es läuft noch der alte Kernel (6.12.100) und die Dienste haben die alte libssl im Speicher./var/run/reboot-requiredsteht. Kein Notfall (die OpenSSL-CVEs betreffen PKCS7/CMS/QUIC-Pfade, die hier kaum aktiv sind — Caddys QUIC läuft über Go), aber der Neustart gehört auf eine ruhige Zeit gelegt, während Filipe erreichbar ist (Netcup-Konsole griffbereit). Danach prüfen: laufen alle sechs Dienste? -
Server-internal-Deploy ausstehend (28.08.2026): In Gitea liegen fertige, getestete Änderungen an
server-internal/, die noch nicht live sind (Anfragebremse für/submit+/testimonials/submit;package-lock.jsonauf multer 2.2.0 / node-cron 4.6.0). Ausrollen mit dem vollen Deploy-Block oben (der mitnpm ci), weil sich die Paketstände geändert haben. Getestet: multer 2.2.0 fängt den DoS-Fall sauber ab, node-cron 4.x akzeptiert den bestehenden Aufruf, alle Selbsttests grün. -
Hintergrundbilder liegen nur auf dem Server— erledigt, nachgeprüft am 26.08.2026. Alle genannten Dateien (bg-bewerben-modi.jpg,bg-links-seite.jpg,bg-medien-casper.jpg,bg-supporter.jpg,bg-modis.jpg) und der Ordnerassets/img/stimmen-avatare/(8 Dateien) sind inzwischen versioniert.git status --untracked-files=all -- assets/auf dem Server meldet null unverfolgte Dateien.Der Eintrag bleibt hier stehen, statt gelöscht zu werden: Eine Liste offener Punkte, in der Erledigtes ungekennzeichnet steht, wird beim nächsten Mal gar nicht mehr gelesen. Wer prüft, will sehen, dass geprüft wurde.
-
CORS steht auf
*— inserver-internal/index.js(app.use(cors())) und im Altbestandcloudflare-worker/src/lib/http.js.Eingeordnet am 26.08.2026, weniger dringend als der alte Hinweis klang: Die Antwort enthält kein
Access-Control-Allow-Credentials, und die Verwaltung weist sich überAuthorization: Bearer …aus, nicht über ein Cookie. Ein Browser schickt bei einer fremden Seite deshalb weder Cookies noch das Token mit — eine fremde Seite erreicht damit nur die ohnehin öffentlichen Endpunkte (Team, Events, Stimmen). An Kundendaten kommt sie nicht.Sauberer wäre es trotzdem: eine feste Liste erlaubter Herkünfte (
dogfather-universe.com,vans-diy-bastelbedarf.com) statt*. Das ist Härtung, keine Reparatur — und ein Eingriff in einen laufenden Dienst, der bei zu enger Einstellung die Seite lahmlegt. Also als eigener Vorgang, mit Live-Test danach. -
express 5 vorbereitet, nicht umgestellt.
pruef-express5.mjs(Bestand durchsuchen) undserver/test-express5.mjs(echter Server gegen 5.2.1) sind grün — der Umstieg wäre ohne Codeänderung möglich. Nicht durchgeführt, weil express 4.22.2 gepflegt wird undnpm auditnull meldet. Ebenfalls offen: dotenv 16→17, better-sqlite3 11→13 (dort muss die ABI zur Node-Fassung passen; ein Fehlgriff legt den internen Dienst still). -
Datenschutz-Endpunkte warten auf einen Pull— erledigt, live geprüft am 28.08.2026. Alle drei (/webdesign/admin/datenschutz/{auskunft,vorschau, loeschen}) antworten auf der echten Domain mit 401 (vorhanden und geschützt), nicht mehr 404. Steht abgehakt hier, nicht gelöscht — wer prüft, will sehen, dass geprüft wurde.
Alternative Hosting-Optionen (nur als Notfall-Plan)
Die Seite ist reines statisches HTML/CSS/JS und läuft überall — netlify.toml und .nojekyll
liegen bereit. Relevant nur, falls der Netcup-Server einmal ausfällt.