Team-Bereich aufgehuebscht: Avatare, Rechte-Chips, Protokoll mit relativer Zeit
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Der Team-Reiter war funktional, aber roh: Rechte standen als "content,community"
da, es gab keine Gesichter, und das Protokoll war eine Wand aus Zeitstempeln.

- /api/webadmins und /api/auditlog liefern jetzt Avatar und Anzeigename aus dem
  Member-Cache — kostet keinen zusaetzlichen Discord-Aufruf
- Owner-Zeile mit eigenem Chip "Alle Bereiche", damit klar ist, wer alles darf
- Bereiche als lesbare Chips statt roher IDs; die Auswahl beim Anlegen sind
  klickbare Karten mit Beschreibung
- Protokoll zeigt relative Zeit ("vor 37 Min"), erst 12 Zeilen, Rest auf Klick
- Toenungen ueber color-mix aus --neon: stimmt im Hub (gold) wie auf der
  Bot-Seite (orange)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 22:25:51 +02:00
co-authored by Claude Fable 5
parent c026b2a603
commit 570e45fe60
3 changed files with 286 additions and 68 deletions
+23 -2
View File
@@ -1688,7 +1688,21 @@ ${rssItems}
app.get('/api/webadmins', async (request, reply) => {
if (requireAdmin(request, reply)) return;
return { admins: listWebAdmins(), scopes: TEAM_SCOPES };
// Avatare aus dem Member-Cache — kostet keinen Discord-Aufruf,
// dieselbe Quelle wie bei der Bestenliste
const bild = (id) => {
for (const guild of client.guilds.cache.values()) {
const m = guild.members?.cache?.get(id);
if (m) return { avatar: m.displayAvatarURL?.({ size: 64 }) ?? null, name: m.displayName };
}
return { avatar: null, name: null };
};
const owner = bild(config.adminDiscordId);
return {
admins: listWebAdmins().map((a) => ({ ...a, ...bild(a.user_id) })),
scopes: TEAM_SCOPES,
owner: { user_id: config.adminDiscordId, ...owner },
};
});
app.put('/api/webadmins', async (request, reply) => {
@@ -1724,7 +1738,14 @@ ${rssItems}
// Audit-Log: die letzten Team-/Owner-Aktionen im Webinterface (nur Owner)
app.get('/api/auditlog', async (request, reply) => {
if (requireAdmin(request, reply)) return;
return { entries: listAudit(100) };
const bild = (id) => {
for (const guild of client.guilds.cache.values()) {
const m = guild.members?.cache?.get(id);
if (m) return m.displayAvatarURL?.({ size: 48 }) ?? null;
}
return null;
};
return { entries: listAudit(100).map((e) => ({ ...e, avatar: bild(e.user_id) })) };
});
// --- SSO-Apps (nur Owner): Fremd-Dienste, die den Discord-Login mitnutzen ---