admin: Übersicht-Dashboard, aufgewertete Nutzerverwaltung, Wiki-Autoren
- Neue /api/stats (Admin, read-only): Inhalte/Nutzer/Dialog-Kennzahlen - Übersicht-View als Admin-Dashboard: Stat-Karten (klickbar) + Schnellzugriff - Nutzerverwaltung: Avatar-Initiale, angelegt/zuletzt-aktiv, Rolle beim Anlegen, Inline-Passwort (statt prompt), Filter, Rollen-Badge; API liefert last_sign_in_at - Wiki im Editor anlegbar: Typ 'Wiki-Seite' + Gruppe-Feld → content/wiki/<slug>.md; files.js klassifiziert wiki als eigene 'kind' (eigene Sidebar-Gruppe) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import { Hono } from 'hono';
|
||||
import { supabase } from '../supabase.js';
|
||||
import { listEntries } from '../files.js';
|
||||
import { requireAdmin, roleOf } from '../auth.js';
|
||||
|
||||
// Kennzahlen für die Admin-Übersicht. Nur Admins; rein lesend.
|
||||
const stats = new Hono();
|
||||
stats.use('*', requireAdmin);
|
||||
|
||||
stats.get('/', async (c) => {
|
||||
// Inhalte aus dem Dateisystem zählen.
|
||||
const content = { beitraege: 0, entwuerfe: 0, wiki: 0, seiten: 0, rubriken: 0 };
|
||||
try {
|
||||
for (const e of await listEntries()) {
|
||||
if (e.kind === 'beitrag') { content.beitraege++; if (e.draft) content.entwuerfe++; }
|
||||
else if (e.kind === 'wiki') content.wiki++;
|
||||
else if (e.kind === 'rubrik') content.rubriken++;
|
||||
else content.seiten++;
|
||||
}
|
||||
} catch { /* Filesystem nicht lesbar → 0 */ }
|
||||
|
||||
// Nutzer nach Rolle.
|
||||
const users = { total: 0, admin: 0, editor: 0, user: 0 };
|
||||
try {
|
||||
const { data } = await supabase.auth.admin.listUsers();
|
||||
for (const u of data?.users || []) { users.total++; users[roleOf(u)] = (users[roleOf(u)] || 0) + 1; }
|
||||
} catch { /* GoTrue nicht erreichbar */ }
|
||||
|
||||
// Dialog-Zähler (effizient: head + count, keine Zeilen laden).
|
||||
const count = async (table, filter) => {
|
||||
try {
|
||||
let q = supabase.from(table).select('*', { count: 'exact', head: true });
|
||||
if (filter) q = filter(q);
|
||||
const { count: n } = await q;
|
||||
return n || 0;
|
||||
} catch { return 0; }
|
||||
};
|
||||
const [forums, threads, comments] = await Promise.all([
|
||||
count('forums'),
|
||||
count('threads', (q) => q.eq('deleted', false)),
|
||||
count('comments', (q) => q.eq('deleted', false)),
|
||||
]);
|
||||
|
||||
return c.json({ content, users, dialog: { forums, threads, comments } });
|
||||
});
|
||||
|
||||
export default stats;
|
||||
Reference in New Issue
Block a user