From bd8557025924dd9c33bc43c14ad2c6f1cdcf4bea Mon Sep 17 00:00:00 2001 From: karim Date: Sun, 31 May 2026 16:09:19 +0200 Subject: [PATCH] dialog: Diskussionsplattform mit Foren, Rollen & Moderation + RLS-Fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auth/RLS-Fix (Schreiben gab 400): - supabase.js: eigener supabaseAuth-Client für Login/Token-Check, damit signInWithPassword den Service-Daten-Client nicht prozessweit aufs User-Token umstellt (sonst lief insert als role=authenticated → RLS-Block). Rollen (admin > editor > user): - auth.js: roleOf() aus app_metadata.role + ADMIN_EMAILS, requireModerator. - users.js: Rolle anzeigen/setzen über GoTrue app_metadata; .env-Admins fix. Datenmodell (schema.sql): - forums (Kategorien) + threads; Seed Allgemein/Projekte/Technik/Off-Topic und Sonder-Kategorie Beiträge. Library-Beiträge werden als Threads gespiegelt (dialog-store.syncLibrary). API (routes/dialog.js, dialog-store.js): - öffentlich: /api/forums, /api/forums/:slug, /api/recent, /api/thread - eingeloggt: POST /api/threads (Thread starten, nur in Foren) - Moderation: /api/mod/* (sperren/ausblenden), Admin: /api/admin/forums CRUD - comments: Lock-Prüfung beim Schreiben, Moderation darf jede löschen. Frontend: - static/dialog.js: Router (Übersicht-Split-View | Forum | Thread), neuer Thread, Mod-Leiste, subtiles Login (dezente Zeile statt Formular). - Admin-UI: Tabs Foren + Moderation, Rollen-Dropdown bei Autor:innen. Co-Authored-By: Claude Opus 4.8 --- assets/css/custom.css | 51 +++++ cms/admin/src/App.jsx | 160 ++++++++++++- cms/admin/src/api.js | 13 ++ cms/admin/src/styles.css | 26 +++ cms/api/src/auth.js | 33 ++- cms/api/src/dialog-store.js | 178 +++++++++++++++ cms/api/src/index.js | 18 +- cms/api/src/routes/comments.js | 47 +--- cms/api/src/routes/dialog.js | 93 ++++++++ cms/api/src/routes/users.js | 32 ++- cms/api/src/supabase.js | 15 +- cms/db/schema.sql | 49 ++++ layouts/_default/dialog.html | 12 - static/dialog.js | 400 ++++++++++++++++++++++----------- 14 files changed, 916 insertions(+), 211 deletions(-) create mode 100644 cms/api/src/dialog-store.js create mode 100644 cms/api/src/routes/dialog.js diff --git a/assets/css/custom.css b/assets/css/custom.css index 58a4ba7..1dd9fe3 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -510,6 +510,57 @@ a.byline-author:hover, a.journal-author:hover { color: var(--accent); } .dialog-composer { display: flex; flex-direction: column; gap: 0.6em; } .dialog-loginhint { color: var(--color-text-muted); margin: 0; } +/* Subtiles Login: dezenter Text-Link statt dominantem Formular. */ +.dialog-loginlink { + align-self: flex-start; font: inherit; font-size: var(--font-size-small); + background: none; border: none; padding: 0.2em 0; cursor: pointer; + color: var(--color-text-muted); text-decoration: underline; text-underline-offset: 3px; +} +.dialog-loginlink:hover { color: var(--accent); } +.dialog-loginform { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5em; } +.dialog-loginform .dialog-input { width: auto; flex: 1 1 9em; min-width: 8em; padding: 0.45em 0.7em; } +.dialog-loginform .dialog-send { padding: 0.45em 1em; } +.dialog-logincancel { align-self: center; } + +/* ── Übersicht: Split-View (links letzte Wortmeldungen, rechts Foren) ── */ +.dialog-split { display: grid; grid-template-columns: 1.2fr 1fr; gap: 2.4em; align-items: start; } +@media (max-width: 720px) { .dialog-split { grid-template-columns: 1fr; gap: 1.6em; } } +.dialog-recent-list, .dialog-forum-list, .dialog-thread-list { display: flex; flex-direction: column; gap: 0.7em; } +.dialog-recent-item, .dialog-forum-item, .dialog-thread-item { + display: block; text-decoration: none; color: inherit; + border: 1px solid var(--color-border); border-radius: 12px; padding: 0.85em 1em; background: var(--color-bg-primary); + transition: border-color .12s, transform .12s; +} +.dialog-recent-item:hover, .dialog-forum-item:hover, .dialog-thread-item:hover { border-color: var(--accent); } +.dialog-recent-top { display: flex; justify-content: space-between; gap: 1em; align-items: baseline; } +.dialog-recent-author { font-weight: 600; } +.dialog-recent-meta, .dialog-thread-meta, .dialog-forum-meta { color: var(--color-text-muted); font-size: var(--font-size-small); } +.dialog-recent-thread { font-family: var(--font-family-serif); font-style: italic; color: var(--accent); margin: 0.15em 0; } +.dialog-recent-body { color: var(--color-text-muted); font-size: var(--font-size-small); + display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } + +/* Foren-Karten — linke Akzentkante aus der Forum-Farbe */ +.dialog-forum-item { border-left: 3px solid var(--forum-accent, var(--accent)); } +.dialog-forum-name { display: block; font-weight: 600; font-size: 1.05em; } +.dialog-forum-desc { display: block; color: var(--color-text-muted); font-size: var(--font-size-small); margin-top: 0.2em; } + +/* Forum-Ansicht */ +.dialog-forum-head { border-left: 4px solid var(--forum-accent, var(--accent)); padding-left: 0.6em; } +.dialog-thread-title { display: block; font-weight: 600; } +.dialog-lock { color: var(--color-text-muted); } +.dialog-newthread { margin: 0.4em 0 1.4em; display: flex; flex-direction: column; gap: 0.6em; } +.dialog-newbtn { align-self: flex-start; font: inherit; cursor: pointer; padding: 0.5em 1.2em; border-radius: 999px; + background: var(--accent); color: #fff; border: 1px solid var(--accent); } +.dialog-newbtn:hover { background: #a23f23; } + +/* Thread-Ansicht: Moderationsleiste */ +.dialog-modbar { display: flex; align-items: center; gap: 0.6em; margin: 0.2em 0 1em; flex-wrap: wrap; } +.dialog-modbar:empty { display: none; } +.dialog-modlabel { color: var(--color-text-muted); font-size: var(--font-size-small); } +.dialog-modbtn { font: inherit; font-size: var(--font-size-small); cursor: pointer; padding: 0.3em 0.9em; border-radius: 999px; + background: none; border: 1px solid var(--color-border); color: var(--color-text-muted); } +.dialog-modbtn:hover { border-color: var(--accent); color: var(--accent); } +.dialog-locked { color: var(--color-text-muted); font-style: italic; margin: 0.4em 0; } .dialog-textarea, .dialog-input { width: 100%; font: inherit; padding: 0.7em 0.9em; border: 1px solid var(--color-border); border-radius: 10px; background: var(--color-bg-primary); diff --git a/cms/admin/src/App.jsx b/cms/admin/src/App.jsx index c2469d3..027fcf2 100644 --- a/cms/admin/src/App.jsx +++ b/cms/admin/src/App.jsx @@ -100,6 +100,8 @@ function Dashboard({ email }) { @@ -112,6 +114,10 @@ function Dashboard({ email }) { ) : view === 'users' ? ( + ) : view === 'forums' ? ( + + ) : view === 'moderation' ? ( + ) : ( <>