source: Klonkt/src/routes/circle.js@ 62b899d

main
Last change on this file since 62b899d was 682d504, checked in by roboburr <roboburr@…>, 3 months ago

Circle: remove cached posts on outdated + exclude outdated sites from header

An outdated source (proto mismatch) kept its previously cached remote_posts
→ they remained (buggy) visible in the feed. markOutdated now removes those
posts. The header only shows active circle links (outdated/error are
excluded).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@…>

  • Property mode set to 100644
File size: 3.7 KB
Line 
1/**
2 * Cirkel-feed + lokale lees-pagina.
3 * GET /cirkel -> overzicht (zelfde timeline/grid-view als de home)
4 * GET /cirkel/:id -> losse remote-post in eigen chrome (blijf op je site)
5 * Alleen actief als tenancy === 'circle' (anders next() -> postsRoutes/404).
6 * Zie docs/cirkels-v1-spec.md §5c.
7 */
8
9import express from 'express';
10import { renderPage } from '../middleware/render.js';
11import db from '../config/database.js';
12import { getTenancy } from '../services/SettingsService.js';
13
14const router = express.Router();
15
16function safeUrl(u) {
17 return typeof u === 'string' && /^https?:\/\//i.test(u) ? u : null;
18}
19function safeJson(s) {
20 try { return s ? JSON.parse(s) : []; } catch { return []; }
21}
22function mediaImage(media_json) {
23 const media = safeJson(media_json).map((m) => ({ ...m, url: safeUrl(m.url) })).filter((m) => m.url);
24 return media.find((m) => m.type === 'image') || null;
25}
26
27// ── Overzicht ────────────────────────────────────────────────
28router.get('/cirkel', (req, res, next) => {
29 if (getTenancy() !== 'circle') return next();
30
31 const rows = db.prepare(`
32 SELECT p.id, p.published, p.title, p.summary, p.media_json,
33 a.name AS actor_name
34 FROM remote_posts p
35 JOIN remote_actors a ON a.id = p.actor_id
36 ORDER BY COALESCE(p.published, p.fetched_at) DESC
37 LIMIT 100
38 `).all();
39
40 const posts = rows.map((r) => {
41 const image = mediaImage(r.media_json);
42 return {
43 id: r.id,
44 // Lokale lees-pagina -> de kaart blijft op de eigen site (post-card linkt
45 // lokaal + htmx, GEEN external_url).
46 slug: 'cirkel/' + encodeURIComponent(r.id),
47 title: r.title || '(zonder titel)',
48 excerpt: r.summary || '',
49 cover_image_url: image ? image.url : null,
50 published_at: r.published,
51 created_at: r.published,
52 type: 'post',
53 tags: '',
54 pinned: 0,
55 status: 'published',
56 source_name: r.actor_name || 'Onbekend',
57 };
58 });
59
60 // Sites in de cirkel — alleen actieve links (outdated/error vallen weg, net als
61 // hun posts). Voor de grafische header met avatars.
62 const sites = db.prepare(`
63 SELECT a.name, a.url, a.avatar
64 FROM remote_actors a
65 JOIN circle_links l ON l.remote_actor_id = a.id
66 WHERE l.status = 'active'
67 ORDER BY a.name
68 `).all()
69 .map((s) => ({
70 name: s.name || 'Onbekend',
71 url: safeUrl(s.url),
72 avatar: safeUrl(s.avatar),
73 }));
74
75 renderPage(req, res, 'pages/circle-feed', { pageTitle: 'Cirkel', bodyClass: 'on-cirkel', posts, sites });
76});
77
78// ── Losse remote-post (lokaal lezen) ─────────────────────────
79router.get('/cirkel/:id', (req, res, next) => {
80 if (getTenancy() !== 'circle') return next();
81
82 const row = db.prepare(`
83 SELECT p.id, p.published, p.title, p.summary, p.url, p.media_json,
84 a.name AS actor_name, a.url AS actor_url, a.avatar AS actor_avatar
85 FROM remote_posts p
86 JOIN remote_actors a ON a.id = p.actor_id
87 WHERE p.id = ?
88 `).get(req.params.id);
89
90 if (!row) return next();
91
92 const image = mediaImage(row.media_json);
93 const post = {
94 title: row.title || '(zonder titel)',
95 body: row.summary || '', // platte tekst (gesanitized bij ingest)
96 published: row.published,
97 image: image ? image.url : null,
98 sourceName: row.actor_name || 'Onbekend',
99 sourceUrl: safeUrl(row.actor_url),
100 sourceAvatar: safeUrl(row.actor_avatar),
101 originalUrl: safeUrl(row.url),
102 };
103
104 renderPage(req, res, 'pages/circle-post', {
105 pageTitle: post.title,
106 bodyClass: 'on-cirkel-post',
107 post,
108 });
109});
110
111export default router;
Note: See TracBrowser for help on using the repository browser.