diff --git a/README.md b/README.md index dda87cb..e88fb82 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,97 @@ # joerg-lohrer.de -Persönliche Webseite. In Transition von einer Hugo-basierten, statischen Seite -hin zu einer SvelteKit-SPA, die Blog-Posts live aus signierten Nostr-Events -(NIP-23, `kind:30023`) rendert. +Persönliche Webseite. Nach einer Transition von einer Hugo-basierten, +statischen Seite läuft `joerg-lohrer.de` jetzt als SvelteKit-SPA, die +Blog-Posts live aus signierten Nostr-Events (NIP-23, `kind:30023`) rendert. ## Aktueller Stand -- **`https://joerg-lohrer.de/`** — Hugo-Seite, läuft noch. -- **`https://spa.joerg-lohrer.de/`** — Vanilla-HTML-Mini-Spike (Proof of Concept). -- **`https://svelte.joerg-lohrer.de/`** — produktive SvelteKit-SPA (Ziel). +- **`https://joerg-lohrer.de/`** — SvelteKit-SPA, Cutover am 2026-04-18 erfolgt. +- **`https://staging.joerg-lohrer.de/`** — Staging (gleicher Build, ein Schritt vor Prod). +- **`https://svelte.joerg-lohrer.de/`** — Entwicklungs-Deploy-Target der Pipeline. +- **`https://spa.joerg-lohrer.de/`** — Vanilla-HTML-Mini-Spike (Proof of Concept, historisch). Detailliert in [`docs/STATUS.md`](docs/STATUS.md). +## Wie die Seite funktioniert + +1. **Inhalte** liegen als Markdown in `content/posts//index.md` mit + strukturierten Bild-Metadaten im Frontmatter (Alt-Text, Lizenz, Autor:innen). +2. **Publish-Pipeline** (`publish/`, Deno) lädt Bilder auf Blossom-Server + (content-addressed) und publiziert signierte `kind:30023`-Events via + NIP-46-Bunker (Amber) auf 5 Relays. +3. **SvelteKit-SPA** (`app/`) lädt diese Events zur Laufzeit und rendert + Post-Liste + Detailseiten. Keine Server-Komponente, Static-Hosting reicht. +4. **CI**: GitHub Actions triggert die Publish-Pipeline bei Push auf `main` + (via Forgejo→GitHub Push-Mirror). + +Identität und Assets: +- **Pubkey:** `npub1f7jar3qnu269uyx5p0e4v24hqxjnxysxudvujza2ur5ehltvdeqsly2fx9` +- **NIP-05:** `joerglohrer@joerg-lohrer.de` (statisches `.well-known/nostr.json`) +- **Blossom-Server:** `blossom.edufeed.org`, `blossom.primal.net` +- **Relays:** `relay.damus.io`, `nos.lol`, `relay.primal.net`, `relay.tchncs.de`, `relay.edufeed.org` + ## Navigation - 📍 **Stand und Live-URLs:** [`docs/STATUS.md`](docs/STATUS.md) - 🔜 **Wie es weitergeht:** [`docs/HANDOFF.md`](docs/HANDOFF.md) - 📐 **SPA-Spec:** [`docs/superpowers/specs/2026-04-15-nostr-page-design.md`](docs/superpowers/specs/2026-04-15-nostr-page-design.md) - 📐 **Publish-Pipeline-Spec:** [`docs/superpowers/specs/2026-04-15-publish-pipeline-design.md`](docs/superpowers/specs/2026-04-15-publish-pipeline-design.md) +- 📐 **Bild-Metadaten-Konvention:** [`docs/superpowers/specs/2026-04-16-image-metadata-convention.md`](docs/superpowers/specs/2026-04-16-image-metadata-convention.md) - 🛠 **SvelteKit-SPA-Plan:** [`docs/superpowers/plans/2026-04-15-spa-sveltekit.md`](docs/superpowers/plans/2026-04-15-spa-sveltekit.md) (35 Tasks, abgeschlossen) +- 🛠 **Publish-Pipeline-Plan:** [`docs/superpowers/plans/2026-04-16-publish-pipeline.md`](docs/superpowers/plans/2026-04-16-publish-pipeline.md) (24 Tasks, abgeschlossen) - 🤖 **Claude-Workflow-Skill:** [`.claude/skills/joerglohrerde-workflow.md`](.claude/skills/joerglohrerde-workflow.md) ## Branches -- **`main`** — kanonisch (Content, Specs, Pläne, Deploy-Scripts, Skill). -- **`spa`** — aktueller Arbeitszweig mit allen SvelteKit-Commits. Wird beim - Cutover nach `main` gemerged. +- **`main`** — kanonisch. Seit Cutover (2026-04-18) Produktions-Quelle. +- **`spa`** — historischer SvelteKit-Arbeitszweig, inzwischen gemerged. - **`hugo-archive`** — eingefrorener Hugo-Zustand als Orphan-Branch. - Rollback über `git checkout hugo-archive && hugo build`. + Rollback-Option über `git checkout hugo-archive && hugo build`. ## Repo-Struktur ``` -content/posts/ Markdown-Posts (Quelle für Nostr-Events) -app/ SvelteKit-SPA (Ziel-Implementation) -preview/spa-mini/ Vanilla-HTML-Mini-Spike (Referenz) -scripts/deploy-svelte.sh FTPS-Deploy nach svelte.joerg-lohrer.de -static/ Site-Assets (Favicons, Profilbild) -docs/ Specs, Pläne, Status, Handoff -.claude/ Claude-Code-Sessions (transparenz) + Skills +content/posts/ Markdown-Posts (Quelle für Nostr-Events, 18 Stück) +content/impressum.md Statisches Impressum (wird von SPA geladen) +app/ SvelteKit-SPA (Laufzeit-Renderer) +publish/ Deno-Publish-Pipeline (Blossom + Nostr) +preview/spa-mini/ Vanilla-HTML-Mini-Spike (historische Referenz) +scripts/deploy-svelte.sh FTPS-Deploy, Targets: svelte/staging/prod +static/ Site-Assets (Favicons, Profilbild, .well-known/) +docs/ Specs, Pläne, Status, Handoff, Wiki-Entwürfe +.github/workflows/ GitHub-Actions CI (Publish-Pipeline-Trigger) +.claude/ Claude-Code-Sessions (Transparenz) + Skills +``` + +## Entwicklung + +```sh +# SPA lokal +cd app && npm run dev + +# SPA testen +cd app && npm run test:unit +cd app && npm run test:e2e +cd app && npm run check + +# Publish-Pipeline +cd publish && deno task check # pre-flight +cd publish && deno task publish --dry-run # Simulation +cd publish && deno task publish # diff-modus echt +cd publish && deno task publish --post # ein Post +cd publish && deno task test # Tests + +# Deploy +DEPLOY_TARGET=staging ./scripts/deploy-svelte.sh +DEPLOY_TARGET=prod ./scripts/deploy-svelte.sh ``` ## Lizenz -Siehe [LICENSE](LICENSE). +Inhalte: [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/deed.de) +(Namensnennung erwünscht, aber rechtlich nicht erforderlich), sofern nicht +anders vermerkt. Drittinhalte sind beim jeweiligen Bild mit Autor:innen und +Lizenz gekennzeichnet. + +Code: siehe [LICENSE](LICENSE). diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index cf9beef..51f0202 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -5,87 +5,87 @@ Dieses Dokument sagt: was ist der Zustand, was wartet, wo liegen die Fäden. ## Zustand (Details in `STATUS.md`) -**Die Nostr-Publish-Pipeline ist live.** Alle 18 Posts sind publiziert als -`kind:30023`-Events auf 5 Relays, 91 Bilder auf 2 Blossom-Servern. Die -SvelteKit-SPA unter `svelte.joerg-lohrer.de` rendert alles ordentlich. +**Cutover am 2026-04-18 abgeschlossen.** `joerg-lohrer.de` läuft als +SvelteKit-SPA, rendert 18 Nostr-Langform-Posts live aus 5 Relays, Bilder +auf 2 Blossom-Servern. Hugo-Altbestand liegt als `hugo-archive`-Branch +eingefroren. -**Das inhaltliche Kernziel des Gesamtprojekts ist damit erreicht.** +Der Rest sind Feinschliff-Aufgaben. -Der Rest sind Feinschliff- und Cutover-Aufgaben. +## Was als Nächstes ansteht (priorisiert) -## Was als Nächstes ansteht +### Option A — Repo/Nostr-Konflikt-Management (priorisiert) -### Option 1 — CI-Push-Auto-Trigger verifizieren (optional) +**Warum jetzt:** Es gibt **9 Langform-Events auf Nostr, die keine +Markdown-Entsprechung im Repo haben** — alle via Client (Habla / Yakihonne) +direkt auf Nostr erstellt, zum Teil mit problematischen d-tags (Emojis, +Doppelpunkte, Umlaute, Trailing-Dashes, oder leer). -**Status:** Workflow-Manual-Trigger ist grün (Run #1 am 18.04.2026). -Automatischer Auto-Trigger bei Content-Push noch nicht ausprobiert — -kann jederzeit beiläufig mitgenommen werden: +**Liste der verwaisten Nostr-Events** (d-tag → event-id-Prefix): -1. Minimaler Edit in einem Post (z. B. Typo) → commit → push `main` -2. Forgejo synct automatisch zu GitHub → Workflow triggert → 1 Post als - `update` publiziert -3. Log-Artefakt in GitHub Actions prüfen +| d-tag | event-id | Probleme | +|---|---|---| +| `richter-oder-rcher-banksy-als-moderner-prophet-vor-dem-high-court` | `bb2c2cea…` | Umlaut-Abdecker (`Rächer` → `rcher`) | +| `die-kraft-der-gemeinschaft-wahre-strke-liegt-nicht-in-strukturen-sondern-in-prozessen` | `27d7fbee…` | Umlaut-Abdecker (`Stärke` → `strke`) | +| `nostr-und-open-educational-practices-oep-` | `0baa3615…` | Trailing-Dash, unschön | +| `religionsbezogene-bildung-mit-rollenkarten:-ki-bilder-als-impulsgeber` | `3ac719ca…` | `:` ungültig für URL-Slug | +| `📢-empowering-learners-for-the-age-of-ai-–-der-neue-review-draft-des-ai-literacy-frameworks-für-schule-ist-da!` | `3c005996…` | Emoji + `!` + Umlaute + extrem lang | +| `🟠-prompts-für-die-religionsbezogene-bildung-posten-und-diskutieren-auf-nostr` | `f726fcd5…` | Emoji + Umlaute | +| `ki-mitmachen` | `a1368d2e…` | sauber, aber fehlt im Repo | +| `bibel-selfies` | `00cbe5f3…` | Langform-Version; Duplikat mit Unix-Timestamp wurde bereits gelöscht | +| `""` (leer!) | `d75857dc…` | leerer d-tag — SPA-kritisch, Event hat keinen Slug | -Kein Ziel mehr, nur Bestätigung. Pipeline ist funktional vollständig. +**Zielbild:** Repo ist die Quelle der Wahrheit. Jeder Post existiert als +Markdown mit sauberem Slug. d-tags sind URL-freundlich (ASCII, keine +Sonderzeichen außer `-`). -### Option 2 — Cutover auf `joerg-lohrer.de` +**Empfohlener Flow (Reihenfolge nicht tauschen!):** -**Voraussetzung:** Option 1 optional, aber nicht blockierend. Die Pipeline -läuft ja schon, ob manuell oder via CI ist für den Cutover egal. +1. **Content exportieren.** Pro Event: `nak req -i ` → + JSON mit Content + Tags. Manuell in neue Markdown-Datei umwandeln + (`content/posts/-/index.md`). Titel, + published_at, ggf. summary und bestehende image-Tags übernehmen. + Bilder nach `images/` kopieren (falls noch erreichbar), sonst + Blossom-URLs im Markdown belassen und beim Publish neu hashen. +2. **Slugs bereinigen.** Neue, saubere, ASCII-only d-tags wählen. Doku + in `docs/redaktion-bild-metadaten.md` oder einem neuen + `docs/nostr-reimport-mapping.md` festhalten (alter d-tag → neuer slug). +3. **Neu publizieren.** `deno task publish --post ` pro Datei. + Pipeline hasht Bilder zu Blossom, signiert mit stabiler Identität. +4. **Alte Events löschen** via NIP-09 (`kind:5`). Heute noch manuell per + `nak event -k 5 -t e=`, siehe Option D. Oder: erst + Option D bauen, dann diesen Schritt per Pipeline-Subcommand. +5. **Verifikation.** Post-Count pro Relay checken, SPA-Post-Liste + visuell prüfen. -**Schritte:** -1. In All-Inkl KAS die Domain `joerg-lohrer.de` auf den SvelteKit-Webroot - umhängen (aktuell: `svelte.joerg-lohrer.de` → `/www/htdocs/v109928/joerglohrer28/` - oder welcher Ordner auch immer). -2. SvelteKit-SPA deployen, sofern sie nicht schon dort liegt. -3. Live-Check: `curl -sI https://joerg-lohrer.de/` → sollte die neue SPA - liefern, nicht mehr Hugo. +**Edge Cases:** +- Das leere-d-tag-Event (`d75857dc…`): wenn es noch sinnvoller Content + ist, als neuer Post re-importieren. Sonst einfach löschen. +- Bilder in alten Events zeigen auf externe Server (nicht Blossom). Beim + Re-Publish lädt die Pipeline sie herunter und hasht sie neu. Wenn die + Quelle tot ist, muss das Bild manuell beschafft oder der Post mit + Platzhalter markiert werden. +- `relay.damus.io` liefert mehr Events als andere Relays — bei + Nicht-Auffindbarkeit auf anderen Relays trotzdem löschen, damus.io + respektiert den NIP-09. -Hugo-Altbestand bleibt als Archiv im `hugo-archive`-Branch. - -### Option 3 — Startseite + Menü-Navigation + Impressum in der SPA - -**Unabhängig von Cutover**, aber Voraussetzung für diesen: ohne Design wäre -die Hauptdomain eine rohe Post-Liste. - -- **Startseite** bekommt ein eigenes Design (nicht nur Post-Liste dump) -- **Menü-Navigation** in `app/src/routes/+layout.svelte` (Home, Archiv, - Impressum, Mastodon-Link) -- **Impressum** als Static-Page (SvelteKit-Route `/impressum/`), **nicht** - als Nostr-Event — soll nicht als Blog-Beitrag in Listen erscheinen. - Text ist bereits im `content/`-Ordner (Repo-Quelle); die einzige - rechtlich relevante HTML-Datei, die auf dem Server liegt. - -### Option 4 — SPA respektiert `kind:5`-Deletion-Events +### Option B — SPA respektiert NIP-09-Deletion-Events **Status:** aktuell filtert die SPA nicht nach NIP-09. Wenn ein Event per -`kind:5`-Referenz gelöscht wurde (z. B. `7f5d08b8…` deletet `89609df5…` -für `d=1744905463975` am 18.04.), zeigen Relays es meist nicht mehr aus — +`kind:5`-Referenz gelöscht wurde, zeigen Relays es meist nicht mehr aus — aber die SPA würde es trotzdem rendern, falls ein Relay es doch liefert. -**Zu tun:** im `kind:30023`-Loader (`app/src/lib/nostr/...`) einen +**Zu tun:** im `kind:30023`-Loader (`app/src/lib/nostr/…`) einen Cross-Check auf `kind:5`-Events einbauen. Events, deren Addressable-Pointer (`30023:pubkey:d-tag`) in einem `kind:5` referenziert ist, werden gefiltert. Defensive Maßnahme für zukünftige Duplikate / Soft-Deletes. -### Option 5 — Repo/Nostr-Konflikt-Management +### Option C — Postfach `webmaster@joerg-lohrer.de` -**Warum:** aktuell ist die Pipeline eine einseitige Straße — Repo → Nostr. -Wenn du via Client (Habla, Yakihonne, Amber) auf Nostr editierst, -überschreibt der nächste Pipeline-Lauf deine Client-Edits mit dem (alten) -Repo-State. Das ist ein echter Datenverlust-Risikofaktor. +User-Task: im All-Inkl KAS als Weiterleitung anlegen. Der Link im +Footer und in den Social-Icons zeigt bereits darauf. -**Zu tun:** -- **Defensiv (`created_at`-Check):** Pipeline liest vor Publish das - aktuelle Event vom Relay und vergleicht `created_at`. Wenn das - Remote-Event neuer ist: Abbruch mit Warnung. -- **Reverse-Sync (`pull-from-nostr`-Subcommand):** liest Events, vergleicht - mit Repo, zeigt Diffs. Manuelle Konfliktauflösung. - -Keine Eile, solange du nicht parallel editierst. Erst relevant, wenn du -dich an Habla & Co. gewöhnst. - -### Option 6 — NIP-09-Delete als Pipeline-Subcommand +### Option D — NIP-09-Delete als Pipeline-Subcommand **Status:** heute einmalig per `nak event -k 5 …` mit neu erzeugter Bunker- URL erledigt (Duplikat `1744905463975`). Das war ein Workaround um das @@ -100,9 +100,10 @@ deno task publish-delete --slug deno task publish-delete --event-id ``` -Jetzt nicht dringend — nur bauen, wenn der Fall öfter eintritt. +**Sinnvollerweise mit Option A kombinieren** — in der Re-Import-Kampagne +werden 9 NIP-09 hintereinander gebraucht. -### Option 7 — Pipeline weg von GitHub (self-hosted CI) +### Option E — Pipeline weg von GitHub (self-hosted CI) **Wann:** Wenn der Optiplex-Server steht und ein zentraler Ort für Dienste existiert. @@ -116,6 +117,17 @@ existiert. Der Pipeline-Code selbst (`publish/src/**`) ist CI-agnostisch — nur die Trigger-Konfiguration ändert sich. +### Option F — Design-Refinements + +**Wann:** irgendwann, wenn Lust drauf ist. + +- Parallax-Effekte, Animations +- Dark-Mode-Feinschliff (aktuell `prefers-color-scheme`, könnte Toggle bekommen) +- Typografie-Experimente (Variable Fonts?) +- Bildergalerie-Komponente für Posts mit vielen Bildern + +Alles nicht-blockierend, die SPA funktioniert solide. + ## Schnell-Orientierung für die nächste Claude-Session Lies in dieser Reihenfolge: @@ -134,7 +146,8 @@ cd app && npm run check cd app && npm run dev # SPA-Build + Deploy -cd app && npm run build && cd .. && ./scripts/deploy-svelte.sh +DEPLOY_TARGET=staging ./scripts/deploy-svelte.sh +DEPLOY_TARGET=prod ./scripts/deploy-svelte.sh # Publish-Pipeline cd publish && deno task check # pre-flight @@ -142,7 +155,7 @@ cd publish && deno task publish --dry-run # diff-modus simulation cd publish && deno task publish # diff-modus echt cd publish && deno task publish --force-all # alle posts cd publish && deno task publish --post # einen post -cd publish && deno task test # 59 tests +cd publish && deno task test # tests ``` ## Bekannte Stolperfallen @@ -161,6 +174,9 @@ cd publish && deno task test # 59 tests - **Hugo-quotierte Dates:** `date: "2023-02-26"` ist ein YAML-String, nicht ein Date-Objekt. `validatePost` coerced das automatisch; in neuen Posts am besten ohne Quotes schreiben. +- **Deploy-Targets:** `svelte` → Entwicklung, `staging` → Pre-Prod, + `prod` → `joerglohrer26/` (Produktion seit Cutover). Script parst + `.env.local` per awk (wegen Sonderzeichen in FTP-Passwörtern). ## Offene UNKNOWN-Einträge zur späteren Recherche @@ -177,10 +193,14 @@ Pipeline loggt Warnungen, publisht aber trotzdem. Recherche-Notizen in ## Session-Kontext Hilfreich beim Wiedereinstieg mit Claude: -- Branch-Check: `git log --oneline -10 spa main` -- Live-Check SPA: `curl -sI https://svelte.joerg-lohrer.de/` -- Event-Count: `nak req -k 30023 -a 4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41 wss://relay.primal.net 2>/dev/null | jq -s 'length'` → 18 -- Pipeline-Tests: `cd publish && deno task test` → 59 grün +- Branch-Check: `git log --oneline -10 main` +- Live-Check: `curl -sI https://joerg-lohrer.de/` +- Event-Count Repo vs. Relays: + ```sh + ls content/posts/ | wc -l + nak req -k 30023 -a 4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41 wss://relay.edufeed.org 2>/dev/null | jq -s 'length' + ``` +- Pipeline-Tests: `cd publish && deno task test` ## Community-Wiki-Entwürfe diff --git a/docs/STATUS.md b/docs/STATUS.md index c2dc0d2..a9e6c31 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,63 +1,63 @@ # Projekt-Status: joerg-lohrer.de → Nostr-basierte SPA -**Stand:** 2026-04-18 +**Stand:** 2026-04-18 (Cutover abgeschlossen) ## Kurzfassung -Jörg Lohrers persönliche Webseite wird von einem Hugo-basierten statischen -Site-Generator zu einer dezentralen Nostr-basierten SPA überführt. Posts -existieren als signierte Events (NIP-23, `kind:30023`) auf Public-Relays und -werden zur Laufzeit im Browser gerendert. +`joerg-lohrer.de` läuft als SvelteKit-SPA, die Blog-Posts live aus +signierten Nostr-Events (NIP-23, `kind:30023`) auf 5 Public-Relays rendert. +Bilder liegen content-addressed auf 2 Blossom-Servern. Die Hugo-basierte +Altseite ist als `hugo-archive`-Branch eingefroren. -## Vier parallele Webseiten +**Das inhaltliche Kernziel des Gesamtprojekts ist erreicht.** + +## Live-URLs | URL | Status | Rolle | |---|---|---| -| `https://joerg-lohrer.de/` | live, unverändert | **Hugo-Altbestand** (bleibt bis Cutover) | -| `https://spa.joerg-lohrer.de/` | live | **Vanilla-HTML-Mini-Spike** (Proof of Concept) | -| `https://svelte.joerg-lohrer.de/` | live | **SvelteKit-SPA** (35-Task-Plan komplett) | -| `https://staging.joerg-lohrer.de/` | live, leer | **Staging** (Webroot `joerglohrer26/` für Pipeline-Entwicklung; FTP-Creds in `.env.local`) | - -Die SvelteKit-SPA unter `svelte.joerg-lohrer.de` ist die Ziel-Implementierung. -`spa.joerg-lohrer.de` bleibt als schlanke Referenz erhalten. Hugo läuft weiter, -bis die Publish-Pipeline steht und der Cutover auf die Hauptdomain erfolgt. +| `https://joerg-lohrer.de/` | live | **Produktion**, SvelteKit-SPA (Cutover 2026-04-18) | +| `https://staging.joerg-lohrer.de/` | live | **Staging**, letzter Pre-Prod-Build | +| `https://svelte.joerg-lohrer.de/` | live | **Entwicklung**, Deploy-Target der Pipeline | +| `https://spa.joerg-lohrer.de/` | live | **Historisch**, Vanilla-HTML-Mini-Spike | ## Was auf Nostr liegt - **Autoren-Pubkey:** `npub1f7jar3qnu269uyx5p0e4v24hqxjnxysxudvujza2ur5ehltvdeqsly2fx9` (hex: `4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41`) -- **Publizierte Events:** **18 Langform-Posts** (`kind:30023`) — alle Altposts - via Publish-Pipeline migriert (Commit `0c6fdd1`, Log in - `docs/publish-logs/2026-04-18-force-all-migration.json`). +- **NIP-05:** `joerglohrer@joerg-lohrer.de` (via `/.well-known/nostr.json`) +- **Publizierte Events:** **18 Langform-Posts** (`kind:30023`) aus dem Repo, + plus **9 nicht-Repo-Events** (via Client wie Habla/Yakihonne erstellt, + siehe HANDOFF → Option 5 für Konflikt-Management-Plan). - **Relay-Liste** (`kind:10002`): `relay.damus.io`, `nos.lol`, `relay.primal.net`, `relay.tchncs.de`, `relay.edufeed.org` - **Blossom-Server** (`kind:10063`): `blossom.edufeed.org`, `blossom.primal.net` - -**91 Bilder** auf beiden Blossom-Servern. Alle Events enthalten hash-basierte -Blossom-URLs. SPA rendert alle Posts einheitlich — kein Legacy-Pfad, keine -rsync-Artefakte. +- **91 Bilder** auf beiden Blossom-Servern, alle Events enthalten + hash-basierte Blossom-URLs. ## Repo-Struktur ``` joerglohrerde/ -├── content/posts/ # 18 Markdown-Posts, alle mit structured images: im Frontmatter -├── app/ # SvelteKit-SPA (Ziel-Implementation) -├── preview/spa-mini/ # Vanilla-HTML-Mini-Spike (Referenz) -├── publish/ # NOCH NICHT ANGELEGT — Publish-Pipeline (Task 1 aus Plan) +├── content/posts/ # 18 Markdown-Posts, alle mit strukturierten images: +├── content/impressum.md # Statisches Impressum (wird von SPA geladen) +├── app/ # SvelteKit-SPA (Laufzeit-Renderer) +├── publish/ # Deno-Publish-Pipeline (Blossom + Nostr) +├── preview/spa-mini/ # Vanilla-HTML-Mini-Spike (historisch) ├── scripts/ -│ └── deploy-svelte.sh # FTPS-Deploy nach svelte.joerg-lohrer.de +│ └── deploy-svelte.sh # FTPS-Deploy, Targets: svelte/staging/prod ├── docs/ │ ├── STATUS.md # Dieses Dokument │ ├── HANDOFF.md # Wie man hier weitermacht -│ ├── redaktion-bild-metadaten.md # Checkliste, Bild-Durchgang (abgearbeitet) -│ ├── wiki-entwurf-nostr-bild-metadaten.md # Wiki-Konvention deutsch -│ ├── wiki-draft-nostr-image-metadata.md # Wiki-Konvention englisch +│ ├── redaktion-bild-metadaten.md +│ ├── wiki-entwurf-nostr-bild-metadaten.md +│ ├── wiki-draft-nostr-image-metadata.md +│ ├── github-ci-setup.md │ └── superpowers/ │ ├── specs/ # SPA + Publish-Pipeline + Bild-Metadaten-Konvention │ └── plans/ -│ ├── 2026-04-15-spa-sveltekit.md # erledigt -│ └── 2026-04-16-publish-pipeline.md # ⬅ als nächstes +│ ├── 2026-04-15-spa-sveltekit.md # erledigt +│ └── 2026-04-16-publish-pipeline.md # erledigt +├── .github/workflows/ # publish.yml (Forgejo→GitHub Push-Mirror-Trigger) ├── .claude/ │ ├── skills/ # Repo-spezifischer Claude-Skill │ └── settings.local.json # Claude-Session-State (gitignored) @@ -66,80 +66,78 @@ joerglohrerde/ ## Branch-Layout (Git) -- **`main`** — kanonischer Zweig. -- **`spa`** — aktueller Arbeits-Branch, vor `main`. SvelteKit-SPA live, - Content-Migration (Bild-Metadaten) abgeschlossen, Publish-Pipeline geplant. +- **`main`** — kanonischer Zweig, Produktions-Quelle seit Cutover. +- **`spa`** — historischer SvelteKit-Arbeitszweig, gemerged. - **`hugo-archive`** — Orphan-Branch mit Hugo-Zustand, eingefroren. ## Setup-Zustand -Einmalig manuell erledigt: -- ✅ Amber-Bunker-URL in `.env.local` als `BUNKER_URL` -- ✅ FTP-Creds für alle Subdomains (SPA, SVELTE, STAGING) in `.env.local` -- ✅ `AUTHOR_PUBKEY_HEX` und `BOOTSTRAP_RELAY=wss://relay.primal.net` in `.env.local` +Einmalig manuell erledigt (gitignored in `.env.local`): +- ✅ Amber-Bunker-URL als `BUNKER_URL` +- ✅ FTP-Creds für alle Targets (SVELTE/STAGING/PROD) +- ✅ `AUTHOR_PUBKEY_HEX` und `BOOTSTRAP_RELAY=wss://relay.primal.net` +- ✅ `CLIENT_SECRET_HEX` (identisch mit GitHub-Secret für stabile App-ID in Amber) - ✅ `kind:10002`-Event publiziert (Relay-Liste) - ✅ `kind:10063`-Event publiziert (Blossom-Server) - ✅ Subdomains mit TLS + HSTS -- ✅ Staging-Subdomain `staging.joerg-lohrer.de` → Webroot `joerglohrer26/` +- ✅ Staging → Webroot `joerglohrer26/` +- ✅ Prod → Webroot `joerglohrer26/` (Cutover 2026-04-18) +- ✅ NIP-05-JSON mit CORS-Header via `.htaccess` -Alles in `.env.local` — gitignored, nicht committet. +## Offene Punkte (Details in HANDOFF.md) -## Offene Punkte +Nach Priorität: +1. **Repo/Nostr-Konflikt-Management** — 9 verwaiste Nostr-Events haben + keine Markdown-Entsprechung. Geplanter Flow: Markdown nachziehen → + d-tags bereinigen → neu publizieren → alte löschen (NIP-09). +2. **Postfach `webmaster@joerg-lohrer.de`** als Weiterleitung in KAS anlegen. +3. **SPA respektiert NIP-09-Deletion-Events** (defensiver kind:5-Filter). +4. **NIP-09-Delete als Pipeline-Subcommand** (heute noch `nak event -k 5`). +5. **Self-hosted CI** (Woodpecker / Cron auf Optiplex), weg von GitHub. +6. **5 UNKNOWN-Einträge** im VR-Post zur späteren Recherche. -- **End-to-End-Test CI mit echtem Content-Push** (Workflow-Manual-Trigger - ist grün; automatischer Auto-Trigger via Push-Mirror noch nicht real - ausprobiert). -- **Menü-Navigation** in der SPA (Home / Archiv / Impressum / Kontakt) -- **Impressum-Seite** (braucht rechtlichen Text) -- **Cutover auf `joerg-lohrer.de`** (Pipeline läuft, Voraussetzung erfüllt; - Hauptdomain kann auf SvelteKit-SPA umgestellt werden) -- **5 UNKNOWN-Einträge** im `virtual-reality`-Post zur späteren Recherche - (Wikipedia-Screenshot, Sketchfab-Fotograf, Ready-Player-Me, EyeMeasure-App) -- **CI-Migration** (später): weg von GitHub-Actions zu Woodpecker oder - Cron auf Optiplex — siehe `docs/github-ci-setup.md`. - -## Erledigt seit 2026-04-15 +## Erledigt (chronologisch seit 2026-04-15) - ✅ Content-Migration: alle 18 Posts haben strukturierte `images:`-Liste - im Frontmatter (91 Bilder, mit Alt-Text, Lizenz, Autor:innen, ggf. Caption - und Modifications). Commit `c023b59`. -- ✅ Erlebnispädagogik-Post: tote Amazon-Hotlinks entfernt. + im Frontmatter (91 Bilder, mit Alt-Text, Lizenz, Autor:innen, ggf. + Caption und Modifications). - ✅ Spec, Plan und Bild-Metadaten-Konvention geschrieben. - ✅ Community-Wiki-Entwürfe (DE + EN) für Nostr-Bildattribution. -- ✅ **Publish-Pipeline komplett implementiert**, 22 Tasks aus dem Plan: - - 18 Code-Tasks (Phase 1–6), 59 Unit-Tests grün - - Stabile NIP-46-Anbindung via `CLIENT_SECRET_HEX` für wiederverwendbare - App-Identität in Amber - - `validatePost` akzeptiert auch string-dates (für Hugo-Kompatibilität) -- ✅ **Alle 18 Altposts publiziert** als `kind:30023`-Events (Commit `0c6fdd1`, - Log in `docs/publish-logs/2026-04-18-force-all-migration.json`). +- ✅ **Publish-Pipeline komplett implementiert** (24 Tasks, 59 Tests grün). +- ✅ **Alle 18 Altposts publiziert** als `kind:30023`-Events. - ✅ **91 Bilder** auf beiden Blossom-Servern. -- ✅ SPA rendert alle Posts mit Bildern von Blossom (visuell verifiziert). -- ✅ **GitHub-Actions-Workflow** angelegt (`.github/workflows/publish.yml`). -- ✅ Forgejo → GitHub Push-Mirror eingerichtet, GitHub-Secrets gesetzt. -- ✅ **`spa` → `main` gemergt**, GitHub-Actions-Workflow manuell verifiziert - (Run #1: signer ok, outbox ok, blossom-liste ok, mode=diff posts=0). - **Alle 24 Tasks des Publish-Pipeline-Plans abgeschlossen.** -- ✅ **Staging-Deploy-Infrastruktur:** `scripts/deploy-svelte.sh` mit - drei Targets (`svelte`/`staging`/`prod`), dynamisches `og:url` und - `canonical` via `__SITE_URL__`-Platzhalter. Staging-Subdomain - `staging.joerg-lohrer.de` bedient jetzt `joerglohrer26/` (Cutover-Ziel). -- ✅ **Duplikat-Event `1744905463975` via NIP-09 gelöscht** (Delete-Event - `7f5d08b8…`, auf alle 5 Relays). Nach Migration nicht mehr auffindbar. +- ✅ **GitHub-Actions-Workflow** + Forgejo→GitHub Push-Mirror + Secrets. +- ✅ **Duplikat-Event via NIP-09 gelöscht** (`bibel-selfies` Unix-Timestamp-dup). +- ✅ **Staging-Deploy-Infrastruktur** mit `__SITE_URL__`-Templating. +- ✅ **Homepage** mit Hero, Profilbild, Social-Icons (Nostr/Mastodon/ + Bluesky/LinkedIn/ORCID/Mail), Latest-Posts. +- ✅ **Archiv-Seite**, **Impressum-Seite**, Menü-Navigation im Layout. +- ✅ **CC0-Footer-Badge** (Heart+Zero inline SVG, monochrom). +- ✅ **Impressum auf CC0 umgestellt** (mit freundlichem Namensnennungs-Hinweis). +- ✅ **Cutover 2026-04-18** — `joerg-lohrer.de` von Hugo (`joerglohrer24/`) + auf SvelteKit-SPA (`joerglohrer26/`) umgehängt. ## Live-Verifikation ```sh -curl -sI https://svelte.joerg-lohrer.de/ | head -3 +curl -sI https://joerg-lohrer.de/ | head -3 curl -sI https://staging.joerg-lohrer.de/ | head -3 +curl -s https://joerg-lohrer.de/.well-known/nostr.json | jq . ``` -## Kontakt zur Implementierung +## Pipeline-Quick-Check + +```sh +# Event-Count pro Relay +for r in wss://relay.damus.io wss://nos.lol wss://relay.primal.net wss://relay.tchncs.de wss://relay.edufeed.org; do + echo -n "$r: "; nak req -k 30023 -a 4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41 $r 2>/dev/null | wc -l +done +``` + +## Design-Referenzen -Alle Design-Entscheidungen in: - `docs/superpowers/specs/2026-04-15-nostr-page-design.md` (SPA) - `docs/superpowers/specs/2026-04-15-publish-pipeline-design.md` (Publish, Blossom-only) - `docs/superpowers/specs/2026-04-16-image-metadata-convention.md` (Bild-Metadaten-YAML) -- `docs/superpowers/plans/2026-04-16-publish-pipeline.md` (24 Tasks, als nächstes) Für die nächste Session: **`docs/HANDOFF.md`** lesen.