dialog: Menü-Eintrag DIALOG + Übersicht aller Dialoge; Breite an andere Seiten angeglichen
- Menü: FORUM (extern) → DIALOG → /dialog/ - /dialog/ ohne ?thread zeigt Übersicht aller begonnenen Dialoge (GET /api/threads, Titel aus Inhaltsdateien, sortiert nach Aktivität) - .dialog-page füllt jetzt die normale Inhaltsspalte (kein eigenes max-width/ Padding mehr) → gleiche Breite wie andere Seiten - Threads entstehen erst mit der ersten Wortmeldung Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import publish from './routes/publish.js';
|
||||
import upload from './routes/upload.js';
|
||||
import profile from './routes/profile.js';
|
||||
import users from './routes/users.js';
|
||||
import { listComments, createComment, deleteComment, login } from './routes/comments.js';
|
||||
import { listComments, listThreads, createComment, deleteComment, login } from './routes/comments.js';
|
||||
import { requireAuth } from './auth.js';
|
||||
|
||||
const SITE_DIR = process.env.SITE_DIR || '/site';
|
||||
@@ -19,8 +19,9 @@ const app = new Hono();
|
||||
|
||||
// --- API ---
|
||||
app.get('/api/health', (c) => c.json({ ok: true, hugo: '0.161.1+extended' }));
|
||||
// Öffentlich (ohne Login): Dialog lesen + Login fürs Dialog-Widget.
|
||||
// Öffentlich (ohne Login): Dialog lesen, Übersicht, Login fürs Dialog-Widget.
|
||||
app.get('/api/comments', listComments);
|
||||
app.get('/api/threads', listThreads);
|
||||
app.post('/api/auth/login', login);
|
||||
// Alles weitere unter /api/* braucht ein gültiges Supabase-Token.
|
||||
app.use('/api/*', requireAuth);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { supabase } from '../supabase.js';
|
||||
import { listEntries } from '../files.js';
|
||||
|
||||
// Dialog: flache Wortmeldungen pro Thread (= Beitrags-Pfad), optionaler Bezug.
|
||||
const SITE_DIR = process.env.SITE_DIR || '/site';
|
||||
@@ -26,6 +27,25 @@ export async function listComments(c) {
|
||||
return c.json(out);
|
||||
}
|
||||
|
||||
// ÖFFENTLICH: Übersicht aller begonnenen Dialoge (Threads mit Wortmeldungen).
|
||||
export async function listThreads(c) {
|
||||
const { data, error } = await supabase.from('comments').select('thread,created_at,deleted');
|
||||
if (error) return c.json({ error: error.message }, 500);
|
||||
const map = {};
|
||||
for (const r of data || []) {
|
||||
if (r.deleted) continue;
|
||||
const t = map[r.thread] || (map[r.thread] = { thread: r.thread, count: 0, last: r.created_at });
|
||||
t.count += 1;
|
||||
if (r.created_at > t.last) t.last = r.created_at;
|
||||
}
|
||||
let titles = {};
|
||||
try { (await listEntries()).forEach((e) => { titles[e.url] = e.title; }); } catch { /* egal */ }
|
||||
const out = Object.values(map)
|
||||
.map((t) => ({ ...t, title: titles[t.thread] || t.thread }))
|
||||
.sort((a, b) => (b.last || '').localeCompare(a.last || ''));
|
||||
return c.json(out);
|
||||
}
|
||||
|
||||
// EINGELOGGT: Wortmeldung schreiben.
|
||||
export async function createComment(c) {
|
||||
const user = c.get('user');
|
||||
|
||||
Reference in New Issue
Block a user