Der erste echte Lauf des internen Deploy-Blocks (28.08.2026) förderte zwei harmlose, aber verunsichernde Punkte zutage: - npm ci warnt "allow-scripts ... better-sqlite3": Die allowScripts-Sperre blockiert den nativen Build, aber prebuild-install liefert ein fertiges Binary. Dienst lädt danach die DB einwandfrei -> in Ordnung. Als Kontrolle dokumentiert. - sleep 2 war zu kurz: Der health-Check lief, bevor der Dienst auf 4200 hörte -> kurzzeitig 502 / "activating", obwohl gleich darauf alles läuft. Auf sleep 6 erhöht, mit Hinweis, wann ein 502 wirklich ein Problem ist. Deploy selbst war erfolgreich: Bremse greift live (20 durch, 21. -> 429), multer 2.2.0 + node-cron 4.6.0 installiert, better-sqlite3 lädt. Co-Authored-By: Claude Opus 5 <[email protected]>
347 lines
18 KiB
Markdown
347 lines
18 KiB
Markdown
# 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)
|
||
|
||
```bash
|
||
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):
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
sudo -u dogiintern git -C /home/dogiintern/dogfather-universe pull --ff-only origin main && \
|
||
sudo -u dogiintern bash -c 'cd /home/dogiintern/dogfather-universe/server-internal && npm 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 6 && 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
|
||
```
|
||
|
||
**Wichtig — kein führendes `cd`.** `/home/dogiintern/` steht auf 700; selbst
|
||
`dogi` darf per `cd` nicht hinein, nur der Benutzer `dogiintern` über `sudo -u`.
|
||
Deshalb `git -C <pfad>` (braucht kein cd) und das `npm ci` in einer
|
||
`sudo -u dogiintern bash -c 'cd … && npm ci'`-Shell, die den Wechsel als
|
||
dogiintern ausführt. Ein `cd` als erste Zeile scheitert an „Keine Berechtigung".
|
||
|
||
**Zwei Warnungen von `npm ci`, die HARMLOS sind (am 28.08.2026 verifiziert):**
|
||
|
||
1. `npm warn allow-scripts … better-sqlite3 … (install: prebuild-install || node-gyp rebuild)`
|
||
— better-sqlite3 baut sich normalerweise über ein Install-Skript. Die
|
||
allowScripts-Sperre auf dem Konto blockiert das, aber `prebuild-install`
|
||
lädt ein fertig kompiliertes Binary, das ohne den Build-Schritt funktioniert.
|
||
**Kontrolle:** Läuft der Dienst danach (`active`) und steht in den Logs
|
||
„… Einstellungen aus der Datenbank geladen", ist better-sqlite3 in Ordnung.
|
||
Der `native Module laden: ok`-Schritt im Block prüft genau das vorab.
|
||
2. `npm warn deprecated [email protected]` — nur ein Hinweis, kein Fehler.
|
||
|
||
**`sleep 6`, nicht 2.** Der Dienst braucht nach dem Neustart ein paar Sekunden,
|
||
bis er auf Port 4200 hört. Ein zu früher health-Check meldet sonst kurzzeitig
|
||
`502` (Caddy erreicht den Dienst noch nicht) und `Dienst: activating`, obwohl
|
||
gleich darauf alles läuft. Erst bei anhaltendem 502/activating ist wirklich
|
||
etwas kaputt — dann `sudo systemctl status dogiintern.service --no-pager` ansehen.
|
||
|
||
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.
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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**:
|
||
|
||
```bash
|
||
# 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`,
|
||
Zweig `main` (auf dem eigenen Server, kostenlos, in eigener Hand).
|
||
- Lokal heißt das Fernziel `gitea`, der Zweig `master`.
|
||
- Auf dem Server heißt das Fernziel `origin`, der Zweig `main`.
|
||
- `gitea/master` ist ein **alter, abgehängter Zweig** (Stand 44086b0) — nicht benutzen.
|
||
- Sicherungszweig `server-stand-vor-abgleich-22-08-2026` auf 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-upgrades` hat
|
||
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-required` steht. 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.json` auf
|
||
multer 2.2.0 / node-cron 4.6.0). Ausrollen mit dem **vollen** Deploy-Block oben
|
||
(der mit `npm 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 Ordner
|
||
`assets/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 `*`** — in `server-internal/index.js` (`app.use(cors())`) und im
|
||
Altbestand `cloudflare-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 über `Authorization: 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) und `server/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 und `npm audit` null 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.
|