Changeset 8455dac in Klonkt
- Timestamp:
- 08/13/2026 01:05:47 PM (4 weeks ago)
- Branches:
- main
- Children:
- a117862
- Parents:
- 783b9ff
- Files:
-
- 1 added
- 4 edited
-
src/routes/activitypub.js (modified) (1 diff)
-
src/services/ActivityPubService.js (modified) (5 diffs)
-
src/services/ap-core.js (modified) (1 diff)
-
src/services/music/index.js (modified) (1 diff)
-
test/outbox-diepte.test.js (added)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/activitypub.js
r783b9ff r8455dac 197 197 return AP.sendAP(res, AP.buildOutbox(baseUrl(req), site, [], [], { page: paginaNr(req) }), 'private, no-store'); 198 198 } 199 const fanClause = audience === 'friend' ? '' : "AND (fan_only IS NULL OR fan_only = 0)";200 const posts = db.prepare(201 `SELECT id, slug, title, content, cover_image_url, cover_video_url, nsfw, content_warning, c2s_attachments, quote_json, embed_json, published_at, created_at202 FROM posts WHERE site_id = ? AND status = 'published' ${fanClause}203 ORDER BY COALESCE(published_at, created_at) DESC LIMIT 20`204 ).all(site.id);199 // ECHT DOORBLADEREN (shaer-sk4). Hier stonden twintig posts uit SQL met een 200 // tweede kap van twintig eroverheen: alles daarvoor was niet op een volgende 201 // pagina maar helemaal onbereikbaar. outboxSlice pagineert over de UNION van 202 // posts en tracks, want die vlechten op datum en zijn met twee losse queries 203 // niet te offsetten. 204 // 205 205 // De tracks gaan mee voor iedereen die de deur door mag; de blocked-tak 206 206 // hierboven levert bewust een outbox ZONDER posts en zonder tracks. 207 const ob = AP.buildOutbox(baseUrl(req), site, posts, AP.siteOpenTracks(site.id), { page: paginaNr(req) }); 207 const nr = paginaNr(req); 208 const { posts, tracks, totaal } = AP.outboxSlice(site.id, { 209 fanOnly: audience === 'friend', 210 offset: (Math.max(1, nr || 1) - 1) * AP.PAGINA_GROOTTE, 211 limit: AP.PAGINA_GROOTTE, 212 }); 213 const ob = AP.buildOutbox(baseUrl(req), site, posts, tracks, { page: nr, totalItems: totaal, alGesneden: true }); 208 214 if (audience === 'friend') { 209 215 // The owner's app builds its feed from this leg, and every note here is -
src/services/ActivityPubService.js
r783b9ff r8455dac 29 29 import Blocklist from './BlocklistService.js'; 30 30 import * as Guardianship from './guardianship/index.js'; 31 import { PUBLIC, AP_CONTEXT, safeUrl, actorId, noteId, guessMediaType, normalizeTags, tagParts, hashtagTags, buildHashtagList, pagedCollection } from './ap-core.js';31 import { PUBLIC, AP_CONTEXT, safeUrl, actorId, noteId, guessMediaType, normalizeTags, tagParts, hashtagTags, buildHashtagList, pagedCollection, PAGINA_GROOTTE } from './ap-core.js'; 32 32 // Doorgeven wat hier altijd vandaan kwam, zodat elke bestaande aanroep blijft werken. 33 33 export { AP_CONTEXT, actorId, noteId, guessMediaType }; 34 34 // De muziekkant woont in music/ (shaer-drc). Doorgeven wat hier altijd 35 35 // vandaan kwam, zodat elke bestaande aanroep blijft werken. 36 import { 36 import { TRACK_KOLOMMEN, 37 37 playlistOpenTracks, siteOpenTracks, openTrack, trackHostPosts, 38 38 buildTrackAudio, buildTrackCollection, buildTrackCreate, … … 1019 1019 * zijn eigen database bevraagt zou dwars door die deur heen leveren. 1020 1020 */ 1021 export function buildOutbox(base, site, posts, tracks = [], { page = false } = {}) { 1021 /** 1022 * Een PAGINA van de outbox, in SQL (shaer-sk4). 1023 * 1024 * De outbox mengt twee bronnen: posts en open tracks, gevlochten op datum. Een 1025 * offset over die twee kan niet met twee losse queries -- je weet niet hoeveel 1026 * van elk er in pagina drie horen. Vandaar een UNION met de datum als sleutel, 1027 * daar de LIMIT/OFFSET overheen, en pas dan de rijen zelf ophalen. 1028 * 1029 * Wat er stond was geen paginering maar een KAP: de route haalde twintig posts 1030 * en hield daarvan twintig items over. Alles daarvoor was niet op een volgende 1031 * pagina maar helemaal onbereikbaar. 1032 * 1033 * @param {boolean} fanOnly mag de lezer ook de fans-only posts zien? 1034 */ 1035 export function outboxSlice(siteId, { fanOnly = false, offset = 0, limit = MAX_OUTBOX } = {}) { 1036 const fanClause = fanOnly ? '' : 'AND (p.fan_only IS NULL OR p.fan_only = 0)'; 1037 const unie = ` 1038 SELECT 'post' AS soort, p.id AS id, COALESCE(p.published_at, p.created_at) AS wanneer 1039 FROM posts p WHERE p.site_id = ? AND p.status = 'published' ${fanClause} 1040 UNION ALL 1041 SELECT 'track', t.id, t.created_at 1042 FROM audio_tracks t WHERE t.site_id = ? AND t.fedi_open = 1`; 1043 let rijen = [], totaal = 0; 1044 try { 1045 totaal = db.prepare(`SELECT COUNT(*) n FROM (${unie})`).get(siteId, siteId).n; 1046 rijen = db.prepare(`SELECT soort, id FROM (${unie}) ORDER BY wanneer DESC LIMIT ? OFFSET ?`) 1047 .all(siteId, siteId, limit, Math.max(0, offset)); 1048 } catch { return { posts: [], tracks: [], totaal: 0 }; } 1049 1050 const postIds = rijen.filter((r) => r.soort === 'post').map((r) => r.id); 1051 const trackIds = rijen.filter((r) => r.soort === 'track').map((r) => r.id); 1052 const gaten = (n) => Array.from({ length: n }, () => '?').join(','); 1053 const posts = postIds.length ? db.prepare( 1054 `SELECT id, slug, title, content, cover_image_url, cover_video_url, nsfw, content_warning, 1055 c2s_attachments, quote_json, embed_json, published_at, created_at 1056 FROM posts WHERE id IN (${gaten(postIds.length)})`).all(...postIds) : []; 1057 const tracks = trackIds.length ? db.prepare( 1058 `SELECT ${TRACK_KOLOMMEN} 1059 FROM audio_tracks t JOIN media m ON m.id = t.media_id 1060 WHERE t.id IN (${gaten(trackIds.length)})`).all(...trackIds) : []; 1061 return { posts, tracks, totaal }; 1062 } 1063 1064 export function buildOutbox(base, site, posts, tracks = [], { page = false, totalItems, alGesneden = false } = {}) { 1022 1065 const id = `${actorId(base, site.slug)}/outbox`; 1023 1066 const wanneer = (x) => Date.parse(x && x.published ? x.published : 0) || 0; … … 1031 1074 ] 1032 1075 .sort((a, b) => wanneer(b) - wanneer(a)) 1033 .slice(0, MAX_OUTBOX);1076 .slice(0, alGesneden ? Infinity : MAX_OUTBOX); 1034 1077 // WAT HIER NOG NIET GEPAGINEERD IS, en dat hoort genoemd (shaer-sk4): deze 1035 1078 // lijst is al door de route op twintig rijen afgekapt, dus pagina 2 is leeg. … … 1050 1093 // wie hem vandaag leest hoort er morgen niet voor te hoeven pagineren. Er is 1051 1094 // precies een pagina, dus first en last wijzen naar dezelfde. 1052 return pagedCollection(id, items, { page });1095 return pagedCollection(id, items, { page, totalItems, alGesneden }); 1053 1096 } 1054 1097 … … 6528 6571 export default { 6529 6572 AP_CONTEXT, getOrCreateKeys, apWants, sendAP, actorId, noteId, stripLeadingMentions, pagedCollection, 6530 deriveHandle, localSlugOf, 6573 deriveHandle, localSlugOf, outboxSlice, PAGINA_GROOTTE, 6531 6574 buildActor, buildNote, buildCreate, buildOutbox, buildFollowers, buildFollowing, buildFeatured, 6532 6575 channelUrls, channelCategory, timelineFields, guessMediaType, -
src/services/ap-core.js
r783b9ff r8455dac 232 232 * antwoord zijn dat over zichzelf liegt. 233 233 */ 234 export function pagedCollection(id, items, { totalItems, page = false, perPage = PAGINA_GROOTTE, extra = {} } = {}) {234 export function pagedCollection(id, items, { totalItems, page = false, perPage = PAGINA_GROOTTE, alGesneden = false, extra = {} } = {}) { 235 235 const lijst = items || []; 236 236 const telling = totalItems === undefined ? lijst.length : totalItems; 237 237 const grootte = Math.max(1, Number(perPage) || PAGINA_GROOTTE); 238 const paginas = Math.max(1, Math.ceil(lijst.length / grootte)); 238 // `alGesneden` voor wie in SQL al gepagineerd heeft (de outbox): dan is `lijst` 239 // een PAGINA en zegt hij niets over het geheel, dus telt het aantal pagina's 240 // uit `totalItems`. Zonder dat zou een volle pagina zichzelf als de enige zien 241 // en nooit een `next` aanbieden. 242 const paginas = Math.max(1, Math.ceil((alGesneden ? telling : lijst.length) / grootte)); 239 243 const url = (n) => `${id}?page=${n}`; 240 244 241 245 if (page) { 242 246 const n = Math.max(1, Math.floor(Number(page)) || 1); 243 const deel = lijst.slice((n - 1) * grootte, n * grootte);247 const deel = alGesneden ? lijst : lijst.slice((n - 1) * grootte, n * grootte); 244 248 return { 245 249 '@context': AP_CONTEXT, -
src/services/music/index.js
r783b9ff r8455dac 18 18 19 19 // m.size hoort erbij voor de RSS-enclosure: die eist een lengte in bytes. 20 const TRACK_KOLOMMEN = `t.id, t.title, t.artist, t.duration, t.cover_url, t.created_at,20 export const TRACK_KOLOMMEN = `t.id, t.title, t.artist, t.duration, t.cover_url, t.created_at, 21 21 t.position, t.license, 22 22 m.filename, m.storage_path, m.mime_type, m.size`;
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)