Devlog-Seite: Bildergalerie, Projekt-Chips, Monats-Trenner

- Bild-Attachments werden beim Archivieren lokal gespeichert (Discord-CDN-URLs
  laufen ab) und unter /devlog-assets/ ausgeliefert; DB-Migration: images-Spalte
- Frontend: Bildergalerie im Grid (responsive nach Anzahl), Embed-Titel als
  Projekt-Chip, Commit-Fußzeile abgesetzt, Monats-Trenner in der Timeline
- Backfill lädt Bilder alter Devlogs nach (Dedupe verhindert Doppel-Downloads)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 00:56:53 +02:00
co-authored by Claude Opus 4.8
parent 4b7a93ddb1
commit ed69d859ca
8 changed files with 199 additions and 24 deletions
+6 -1
View File
@@ -21,7 +21,12 @@ export function registerApiRoutes(app) {
app.get('/api/devlogs', async (request) => {
const { limit, offset, page } = paging(request);
const { items, total } = listDevlogs(limit, offset);
return { items, total, page, pageSize: PAGE_SIZE };
// images: JSON-Spalte → fertige URLs fürs Frontend
const mapped = items.map(({ images, ...rest }) => ({
...rest,
images: JSON.parse(images || '[]').map((f) => `/devlog-assets/${f}`),
}));
return { items: mapped, total, page, pageSize: PAGE_SIZE };
});
// Commit-Feed — nur für den Admin (spiegelt den privaten #-gitea-Kanal)
+10
View File
@@ -11,6 +11,7 @@ import { saveCommits } from '../db.js';
import { postPushEmbed } from '../bot/commit-feed.js';
import { registerAuthRoutes } from './auth.js';
import { registerApiRoutes } from './api.js';
import { imagesDir } from '../bot/devlog-archive.js';
// Gebautes React-Frontend (frontend/dist) — im Container immer vorhanden,
// lokal nur nach `npm run build` im frontend/-Ordner
@@ -59,6 +60,15 @@ export async function startWebServer(client) {
// Healthcheck (für Portainer/NPM)
app.get('/health', async () => ({ status: 'ok' }));
// Lokal gespeicherte Devlog-Bilder (Discord-CDN-Links laufen ab)
await app.register(fastifyStatic, {
root: imagesDir,
prefix: '/devlog-assets/',
decorateReply: false,
maxAge: '30d',
immutable: true,
});
if (existsSync(frontendDist)) {
// React-Frontend ausliefern; unbekannte GET-Pfade → index.html (SPA-Routing)
await app.register(fastifyStatic, { root: frontendDist });