Zum Inhalt springen

Statische Seiten über die Pull-API (Starlight, Astro, Hugo …)

Orimora kann die Inhaltsquelle für eine statische Seite sein, ohne einen Git-Mirror dazwischen. Die Seite zieht die publizierten Dokumente zur Build-Zeit aus Orimoras token-gesicherter Pull-API und rendert sie — Orimora bleibt die Single Source of Truth, und es gibt kein separates Content-Repository zu pflegen.

Das funktioniert mit jedem Generator, der Markdown + Frontmatter liest (Astro/Starlight, Hugo, Jekyll, Eleventy …). Das Beispiel nutzt Starlight.

  1. Du publizierst Dokumente (oder einen Ordner) zu einem Kanal in Orimora — nur diese werden ausgeliefert, sonst nichts (kein Draft-Leak).
  2. Ein kleines Pull-Skript läuft vor jedem Build, holt das publizierte Set aus der Pull-API und schreibt eine Markdown-Datei pro Dokument.
  3. Der Generator baut wie gewohnt aus diesen Dateien.

Dieses Rezept ist für statische Generatoren (SSG: Starlight, Hugo, Jekyll, Eleventy …) — die übernehmen Inhalte nur beim Build, deshalb braucht es ein Pull-Skript und einen Rebuild-Auslöser.

Server-gerenderte Apps (SSR/ISR — Next.js, Nuxt, SvelteKit …) brauchen dieses Rezept nicht. Sie rufen die Pull-API einfach zur Request-Zeit ab (mit Cache davor) — kein Build, kein Hook, publizieren ist sofort live. Die API unterstützt dafür ETag / If-None-Match (304-Antworten) und updatedSince für inkrementelle Abfragen.

  1. Einstellungen → Publishing → Neuer Kanal, Transport Pull, Format Markdown. Optional: die Deploy-Hook-URL deines Hosts eintragen — dann stößt Orimora den Rebuild nach jedem Publizieren automatisch an (Details).
  2. Publiziere deine Dokumente per „An Kanal senden…” an einem Dokument oder Ordner — das läuft über deine Login-Session und braucht kein Token.
  3. Öffne den Kanal und erstelle ein Token — behandle es wie ein Passwort. Das Token ist nur für den Consumer (das Pull-Skript), nicht fürs Publizieren.

Du hast nun drei Werte für das Skript: die Orimora-Basis-URL, die Kanal-ID und das Token. Basis-URL und Kanal-ID stecken in der Pull-API-URL, die auf der Kanal-Karte steht und sich dort kopieren lässt (https://<basis-url>/api/v1/published/<kanal-id>/documents).

Bevor du irgendetwas baust, prüfe die Kette mit einem curl (Pull-URL von der Kanal-Karte kopieren, Token einsetzen):

Terminal-Fenster
curl -H "Authorization: Bearer <token>" \
"https://wiki.example.com/api/v1/published/<kanal-id>/documents"

Du musst deine publizierten Dokumente als JSON sehen (data: [...]). Kommt 401, stimmt das Token nicht; kommt 403, gehört das Token zu einem anderen Kanal; ist data leer, wurde noch nichts an diesen Kanal gesendet. Danach stempelt der Kanal „Zuletzt abgerufen” — daran erkennst du später jederzeit, ob dein Build wirklich zieht.

Lege das in dein Site-Repo als scripts/pull-orimora.mjs. Es blättert per Cursor durch die Pull-API und schreibt src/content/docs/orimora/<slug>.md:

scripts/pull-orimora.mjs
import { mkdir, rm, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
const BASE = process.env.ORIMORA_URL; // z. B. https://wiki.example.com
const CHANNEL = process.env.ORIMORA_CHANNEL; // Kanal-ID
const TOKEN = process.env.ORIMORA_TOKEN; // Kanal-Token
const OUT = 'src/content/docs/orimora'; // Zielverzeichnis
if (!BASE || !CHANNEL || !TOKEN) {
throw new Error('ORIMORA_URL, ORIMORA_CHANNEL und ORIMORA_TOKEN setzen');
}
async function pull() {
const docs = [];
let cursor = null;
do {
const url = new URL(`${BASE}/api/v1/published/${CHANNEL}/documents`);
url.searchParams.set('limit', '200');
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, { headers: { Authorization: `Bearer ${TOKEN}` } });
if (!res.ok) throw new Error(`Pull fehlgeschlagen: ${res.status} ${res.statusText}`);
const { data, meta } = await res.json();
docs.push(...data);
cursor = meta?.nextCursor ?? null;
} while (cursor);
return docs;
}
function toMarkdownFile(doc) {
// `content` ist bereits Markdown, weil das Kanal-Format "markdown" ist.
// `frontmatter` enthält title + alle auf dem Kanal gemappten Felder.
const fm = { title: doc.title, ...doc.frontmatter };
const yaml = Object.entries(fm)
.filter(([, v]) => v !== null && v !== undefined)
.map(([k, v]) => `${k}: ${JSON.stringify(v)}`)
.join('\n');
return `---\n${yaml}\n---\n\n${doc.content ?? ''}`;
}
const docs = await pull();
await rm(OUT, { recursive: true, force: true });
for (const doc of docs) {
// In menschenlesbare Ordner nach Collection-Name gruppieren (statt kryptischer IDs).
const dir = join(OUT, doc.collectionSlug ?? 'uncategorised');
await mkdir(dir, { recursive: true });
await writeFile(join(dir, `${doc.slug}.md`), toMarkdownFile(doc), 'utf-8');
}
console.log(`${docs.length} Dokument(e) aus Orimora nach ${OUT} gezogen`);

Als prebuild-Schritt einbinden, damit jeder Build den Inhalt aktualisiert:

package.json
{
"scripts": {
"prebuild": "node scripts/pull-orimora.mjs",
"build": "astro build"
}
}

npm run build zieht jetzt zuerst, dann baut es. Auf einem Host wie Netlify/Vercel/Cloudflare Pages setzt du ORIMORA_URL, ORIMORA_CHANNEL, ORIMORA_TOKEN als Build-Umgebungsvariablen.

Publizieren schreibt nur in Orimora — deine Seite zeigt den Inhalt erst nach dem nächsten Build. Damit das automatisch passiert, trage am Pull-Kanal die Deploy-Hook-URL deines Hosts ein (Kanal bearbeiten → Konfiguration): Orimora POSTet dann nach jedem Publizieren dorthin. Wo die URL zu finden ist und welche Plattformen unterstützt werden, steht im Publishing-Guide. Ohne Hook bleibt es beim manuellen, zeitgesteuerten oder Git-getriggerten Rebuild — der Publish-Dialog erinnert dich daran („Löse jetzt den Build aus”).

GET /api/v1/published/{channelId}/documentsAuthorization: Bearer <token>.

QueryBedeutung
limit1–200 (Default 50)
cursorISO-Timestamp des letzten Eintrags — Cursor-Pagination
updatedSinceISO-Timestamp — nur Dokumente, die danach geändert wurden

Jeder Eintrag: id, title, emoji, slug, collectionId, collectionName, collectionSlug (direkt als Ordnersegment nutzbar), updatedAt, publishedAt, content (Markdown bei einem Markdown-Kanal), frontmatter. meta.nextCursor ist null auf der letzten Seite.

Der Bereich Praxistipps dieser Doku wird genau nach diesem Rezept live aus Orimora gezogen — er ist der Realitätstest des Features:

  • Die Tipps werden in Orimora geschrieben und an einen Pull-Kanal („Starlight Doku”) publiziert.
  • docs/scripts/pull-orimora.mjs läuft vor jedem astro build und holt das Set über die Pull-API. Env-Vars: ORIMORA_URL, ORIMORA_PRAXISTIPPS_CHANNEL, ORIMORA_PRAXISTIPPS_TOKEN — gesetzt in Coolify an der Doku-App, durchgereicht als Docker-Build-Args (siehe Hinweis oben).
  • Bewusst robust: Fehlen die Variablen oder ist Orimora nicht erreichbar, skippt das Skript mit Exit 0 — ein Orimora-Ausfall kann den Doku-Build nie brechen; dann bleibt der committete Platzhalter-Index stehen.
  • Verifikation: Im Build-Log steht [pull-orimora] Pulled N Praxistipp(s) (bzw. die Skip-Begründung), und der Kanal zeigt „Zuletzt abgerufen” mit dem Build-Zeitpunkt.