- Timestamp:
- 07/20/2026 09:38:41 PM (7 weeks ago)
- Branches:
- main
- Children:
- 024f4f8
- Parents:
- 0cea12b
- git-author:
- Robin <roboburr@…> (07/20/2026 09:38:40 PM)
- git-committer:
- Robin <roboburr@…> (07/20/2026 09:38:41 PM)
- Location:
- src
- Files:
-
- 2 edited
-
config/database.js (modified) (1 diff)
-
services/ActivityPubService.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/config/database.js
r0cea12b r81b2e1e 440 440 // Rich replies: JSON array [{url, mediaType, name}] → `attachment` on the Note. 441 441 ensureColumn('ap_outbox', 'attachments', 'TEXT'); 442 ensureColumn('posts', 'ap_visibility', 'TEXT'); // public|quiet|friends|direct (C2S addressing, shaer-60b) 442 443 } 443 444 -
src/services/ActivityPubService.js
r0cea12b r81b2e1e 429 429 // fan_only = "fans only" → followers-only visibility (delivered to your followers 430 430 // but not addressed to Public, so Mastodon shows it only to them and can't boost it). 431 to: post.fan_only? [`${aId}/followers`] : [PUBLIC],431 to: (post.fan_only || post.ap_visibility === 'quiet') ? [`${aId}/followers`] : [PUBLIC], 432 432 // Mentioned actors (from inline @user@host links the caller resolved) are addressed in cc 433 433 // so Mastodon notifies them; empty unless the content was mention-linked (delivery time). 434 cc: [...new Set([...(post.fan_only ? [] : [`${aId}/followers`]), ..._mentionCc])], 434 cc: [...new Set([ 435 ...(post.ap_visibility === 'quiet' ? [PUBLIC] : []), // quiet public: Public in cc, not to 436 ...((post.fan_only || post.ap_visibility === 'quiet') ? [] : [`${aId}/followers`]), 437 ..._mentionCc])], 435 438 tag: [...buildHashtagList(base, post.tags, body), ..._mentionTags], 436 439 replies: `${id}/replies`, … … 1887 1890 const slug = 'n-' + postId.slice(0, 8); 1888 1891 const now = new Date().toISOString(); 1889 db.prepare(`INSERT INTO posts (id, site_id, slug, author_id, title, content, excerpt, status, type, language, created_at, updated_at, published_at) 1890 VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)`) 1891 .run(postId, site.id, slug, user.id, '', html, '', 'published', 'post', object.language || 'nl', now, now, now); 1892 // Visibility from the note's addressing (shaer-60b): Public in `to` = loud 1893 // public, Public in `cc` = quiet public (unlisted), followers-only = friends 1894 // (rides the existing fan_only pipeline: followers-only AP delivery + web 1895 // gating), neither = participants-only (kept local until mention addressing 1896 // lands; still followers-gated on the web). 1897 const vis = c2sVisibility(object); 1898 const fanOnly = (vis === 'friends' || vis === 'direct') ? 1 : 0; 1899 db.prepare(`INSERT INTO posts (id, site_id, slug, author_id, title, content, excerpt, status, type, language, fan_only, ap_visibility, created_at, updated_at, published_at) 1900 VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`) 1901 .run(postId, site.id, slug, user.id, '', html, '', 'published', 'post', object.language || 'nl', fanOnly, vis, now, now, now); 1892 1902 try { db.prepare('UPDATE posts SET content_rendered = ? WHERE id = ?').run(bakePostContent(html), postId); } catch { /* render fallback covers it */ } 1893 1903 bakePostContentWithMentions(html).then((h) => { try { db.prepare('UPDATE posts SET content_rendered = ? WHERE id = ?').run(h, postId); } catch { /* keep sync bake */ } }).catch(() => {}); 1894 1904 try { db.prepare('INSERT INTO posts_fts(content, title, author, post_id) VALUES (?,?,?,?)').run(HtmlSanitizerService.toPlainText(html), '', user.username || '', postId); } catch { /* FTS non-fatal */ } 1895 deliverCreate(site, { id: postId, slug, title: '', content: html, published_at: now, created_at: now }).catch(() => { /* best-effort */ }); 1905 if (vis !== 'direct') { 1906 deliverCreate(site, { id: postId, slug, title: '', content: html, published_at: now, created_at: now, fan_only: fanOnly, ap_visibility: vis }).catch(() => { /* best-effort */ }); 1907 } 1896 1908 return { status: 201, id: postId, url: `${base}/ap/notes/${postId}` }; 1909 } 1910 1911 // Addressing → visibility. Arrays or bare strings; unknown shapes read as the 1912 // safest bucket they match. 1913 export function c2sVisibility(object) { 1914 const arr = (v) => (Array.isArray(v) ? v : (v ? [v] : [])).filter((x) => typeof x === 'string'); 1915 const to = arr(object.to), cc = arr(object.cc); 1916 const isPublic = (x) => x === PUBLIC || x === 'as:Public' || x === 'Public'; 1917 const isFollowers = (x) => /\/followers\/?$/.test(x); 1918 if (to.some(isPublic)) return 'public'; 1919 if (cc.some(isPublic)) return 'quiet'; 1920 if (to.some(isFollowers) || cc.some(isFollowers)) return 'friends'; 1921 if (!to.length && !cc.length) return 'public'; // no addressing at all: legacy client, keep old behavior 1922 return 'direct'; 1897 1923 } 1898 1924 … … 2904 2930 linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower, listConnections, 2905 2931 noteVisibility, isRejectedObject, rejectInteraction, interactionReportTarget, 2906 getMessages, notificationsSeenAt, ingestOutboxActivity, 2932 getMessages, notificationsSeenAt, ingestOutboxActivity, c2sVisibility, 2907 2933 };
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)