cms: headless CMS vor Hugo (Supabase + Node-API + React-Admin)

All-in-One docker-compose-Stack (Muster von RAPPORT-SERVER gespiegelt):
db/auth/rest/kong + cms-Service (Node-API + Hugo-Binary 0.161.1 + Admin-SPA).

- DB-backed: posts-Tabelle kanonisch, MD ist generiertes Artefakt
- echte Hugo-Vorschau via draft:true + --buildDrafts → /_preview
- Publish: DB → content/library/<section>/<slug>.md → hugo build → live
- Bild-Upload nach static/images/, Supabase-Auth schützt /api/*
- Proxmox-LXC-Script: legt Container an, generiert Secrets, startet Stack

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-05-31 00:21:04 +02:00
parent 7a5be9250a
commit 60e5ef6844
31 changed files with 3616 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
import { Hono } from 'hono';
import { mkdir, writeFile } from 'node:fs/promises';
import path from 'node:path';
const SITE_DIR = process.env.SITE_DIR || '/site';
// Bild-Upload → static/images/<name>. Hugo kopiert das beim Build nach
// public/images/, cover_image referenziert es als /images/<name>.
const upload = new Hono();
upload.post('/', async (c) => {
const body = await c.req.parseBody();
const file = body['file'];
if (!file || typeof file === 'string') return c.json({ error: 'Keine Datei' }, 400);
const name = safeName(file.name);
const dir = path.join(SITE_DIR, 'static', 'images');
await mkdir(dir, { recursive: true });
await writeFile(path.join(dir, name), Buffer.from(await file.arrayBuffer()));
return c.json({ url: `/images/${name}` });
});
// Sicherer Dateiname: nur basename, kleingeschrieben, ohne Pfad/Sonderzeichen.
function safeName(raw) {
const base = path.basename(String(raw || 'bild'));
const cleaned = base
.toLowerCase()
.replace(/[^a-z0-9._-]+/g, '-')
.replace(/^-+|-+$/g, '');
return cleaned || 'bild';
}
export default upload;