Devlog-Suche (FTS5) + öffentlicher RSS-Feed
- FTS5-Volltextindex mit Trigger-Sync und einmaligem Backfill bestehender Devlogs - /api/devlogs?q= — Präfix-Suche, injection-sicher (Phrasen-Quoting) - Suchfeld mit Entprellung und Treffer-Zähler auf der Devlog-Seite - /feed.xml — RSS 2.0 der letzten 20 Devlogs (escaped, deutsche Titel) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+39
-3
@@ -1,6 +1,7 @@
|
||||
// REST-API fürs Webinterface: Devlogs öffentlich, Commits + Settings nur für den Admin
|
||||
import { EmbedBuilder } from 'discord.js';
|
||||
import { listDevlogs, listCommits, archiveStats, getSetting, setSetting } from '../db.js';
|
||||
import { listDevlogs, searchDevlogs, listCommits, archiveStats, getSetting, setSetting } from '../db.js';
|
||||
import { config } from '../config.js';
|
||||
import { removeDevlog } from '../bot/devlog-archive.js';
|
||||
import { commitChannelId, devlogChannelId } from '../runtime-settings.js';
|
||||
import { getSessionUser, isAdmin } from './auth.js';
|
||||
@@ -28,10 +29,11 @@ export function registerApiRoutes(app, client) {
|
||||
return user ? { user, admin: isAdmin(user) } : { user: null, admin: false };
|
||||
});
|
||||
|
||||
// Devlog-Archiv — öffentlich (wie der Discord-Kanal)
|
||||
// Devlog-Archiv — öffentlich (wie der Discord-Kanal); ?q= für Volltextsuche
|
||||
app.get('/api/devlogs', async (request) => {
|
||||
const { limit, offset, page } = paging(request);
|
||||
const { items, total } = listDevlogs(limit, offset);
|
||||
const q = (request.query.q ?? '').trim();
|
||||
const { items, total } = q ? searchDevlogs(q, limit, offset) : listDevlogs(limit, offset);
|
||||
// images: JSON-Spalte → fertige URLs fürs Frontend
|
||||
const mapped = items.map(({ images, ...rest }) => ({
|
||||
...rest,
|
||||
@@ -40,6 +42,40 @@ export function registerApiRoutes(app, client) {
|
||||
return { items: mapped, total, page, pageSize: PAGE_SIZE };
|
||||
});
|
||||
|
||||
// RSS-Feed der Devlogs — öffentlich abonnierbar
|
||||
app.get('/feed.xml', async (request, reply) => {
|
||||
const { items } = listDevlogs(20, 0);
|
||||
const esc = (s) =>
|
||||
String(s).replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
||||
const dateFmt = new Intl.DateTimeFormat('de-DE', { day: '2-digit', month: 'long', year: 'numeric' });
|
||||
|
||||
const rssItems = items
|
||||
.map((d) => {
|
||||
const project = d.content.match(/^Devlog\s*[—–-]\s*(.+)$/im)?.[1]?.trim();
|
||||
const title = `Devlog ${dateFmt.format(new Date(d.posted_at))}${project ? ` — ${project}` : ''}`;
|
||||
return ` <item>
|
||||
<title>${esc(title)}</title>
|
||||
<link>${config.publicUrl}/devlogs</link>
|
||||
<guid isPermaLink="false">${esc(d.message_id)}</guid>
|
||||
<pubDate>${new Date(d.posted_at).toUTCString()}</pubDate>
|
||||
<description>${esc(d.content)}</description>
|
||||
</item>`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>D4RKST3R // DEVLOG</title>
|
||||
<link>${config.publicUrl}/devlogs</link>
|
||||
<description>Entwicklungs-Updates, automatisch archiviert.</description>
|
||||
<language>de</language>
|
||||
${rssItems}
|
||||
</channel>
|
||||
</rss>`;
|
||||
return reply.type('application/rss+xml; charset=utf-8').send(xml);
|
||||
});
|
||||
|
||||
// Devlog aus dem Archiv löschen — nur Admin (z. B. alte Test-Posts)
|
||||
app.delete('/api/devlogs/:id', async (request, reply) => {
|
||||
if (requireAdmin(request, reply)) return;
|
||||
|
||||
Reference in New Issue
Block a user