source: Klonkt/src/routes/circle.js@ 429d3b0

main
Last change on this file since 429d3b0 was 8ef8c3e, checked in by Robin Genis <roboburr@…>, 3 months ago

feat(cirkel): remove the old Cirkels pull-protocol; Cirkels is now AP-only

Phase 4 of the Cirkels-on-AP rework. Removes the legacy custom-Ed25519 pull
protocol and everything around it: /.klonkt/* (federation.js), CircleService +
CircleFederation, the 15-min sync loop, the /admin/circle UI, the remote_posts
union in /cirkel and the /cirkel/:id local-reading page. Tenancy is retired
(getTenancy always 'solo'); the Solo|Circle mode picker is gone. /cirkel is now
purely the auto-boosted (featured) feed. The circle_links/remote_* tables are
kept as dead data. A scripts/migrate-circles.mjs converts existing circle_links
to AP follows with auto-boost (run per instance).

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 2.1 KB
Line 
1/**
2 * Circle feed — the artists this site features (auto-boosts), sourced from
3 * ActivityPub. Cards link to the source post. Available whenever the site
4 * auto-boosts at least one account; otherwise next() -> postsRoutes.
5 * GET /cirkel
6 */
7
8import express from 'express';
9import { renderPage } from '../middleware/render.js';
10import { apEnabled } from '../services/SettingsService.js';
11import ActivityPubService from '../services/ActivityPubService.js';
12
13const router = express.Router();
14
15function safeUrl(u) {
16 return typeof u === 'string' && /^https?:\/\//i.test(u) ? u : null;
17}
18function safeJson(s) {
19 try { return s ? JSON.parse(s) : []; } catch { return []; }
20}
21function mediaImage(media_json) {
22 const media = safeJson(media_json).map((m) => ({ ...m, url: safeUrl(m.url) })).filter((m) => m.url);
23 return media.find((m) => /image/i.test(m.type || '')) || media[0] || null;
24}
25function htmlToText(html) {
26 return String(html || '').replace(/<[^>]+>/g, ' ').replace(/&[a-z#0-9]+;/gi, ' ').replace(/\s+/g, ' ').trim();
27}
28
29router.get('/cirkel', (req, res, next) => {
30 const site = res.locals.site;
31 if (!site || !apEnabled() || ActivityPubService.autoBoostCount(site.slug) === 0) return next();
32
33 const posts = ActivityPubService.getCirkelPosts(site.slug, 80).map((r) => {
34 const text = htmlToText(r.content);
35 const image = mediaImage(r.media_json);
36 const name = r.author_name || r.author_handle || 'Onbekend';
37 return {
38 id: 'ap-' + r.id,
39 slug: '',
40 title: text ? (text.length > 90 ? text.slice(0, 90) + '…' : text) : name,
41 excerpt: '',
42 cover_image_url: image ? image.url : null,
43 published_at: r.published,
44 created_at: r.published,
45 type: 'post',
46 tags: '',
47 pinned: 0,
48 status: 'published',
49 source_name: name,
50 external_url: safeUrl(r.url),
51 };
52 });
53
54 const sites = ActivityPubService.getCirkelMembers(site.slug)
55 .map((s) => ({ name: s.name || 'Onbekend', url: safeUrl(s.url), avatar: safeUrl(s.icon) }));
56
57 renderPage(req, res, 'pages/circle-feed', { pageTitle: 'Cirkel', bodyClass: 'on-cirkel', posts, sites });
58});
59
60export default router;
Note: See TracBrowser for help on using the repository browser.