Changeset 5a6a457 in Klonkt for src/services/Scheduler.js


Ignore:
Timestamp:
06/25/2026 05:15:48 AM (3 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
d7526bd
Parents:
99234c3
Message:

feat(fediverse): federate on save+scheduler, delivery retry-queue, music as listen-link

#1 Posts now federate to followers not only on create, but also when a draft/

scheduled post becomes published (editor save) and when the Scheduler flips a
scheduled post live — previously those silently didn't reach followers.

#2 Delivery retry-queue (ap_delivery): a failed delivery (down server/timeout) is

queued and retried with backoff (1/5/15/60/180/360 min, 6 tries) by a worker,
instead of fire-and-forget. Signing key re-derived from the slug, never stored.

#3 Music posts: audio shortcodes federate as a '🎵 listen on the site' link to the

post (protected player) instead of the raw mp3 — keeps Klonkt's audio friction
intact (no downloadable file handed to Mastodon).

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/Scheduler.js

    r99234c3 r5a6a457  
    1010import db from '../config/database.js';
    1111import HtmlSanitizerService from './HtmlSanitizerService.js';
     12import ActivityPubService from './ActivityPubService.js';
    1213
    1314export function flipScheduledPosts() {
    1415  try {
    1516    const due = db.prepare(`
    16       SELECT p.id, p.title, p.content, u.username
     17      SELECT p.id, p.site_id, p.slug, p.title, p.content, p.cover_image_url, p.fan_only,
     18             p.published_at, p.publish_at, p.created_at, u.username
    1719      FROM posts p JOIN users u ON u.id = p.author_id
    1820      WHERE p.status = 'scheduled' AND p.publish_at IS NOT NULL AND datetime(p.publish_at) <= datetime('now')
     
    2325    );
    2426    const fts = db.prepare('INSERT INTO posts_fts(content, title, author, post_id) VALUES (?, ?, ?, ?)');
     27    const siteStmt = db.prepare('SELECT * FROM sites WHERE id = ?');
    2528    for (const p of due) {
    2629      upd.run(p.id);
    2730      try { fts.run(HtmlSanitizerService.toPlainText(p.content || ''), p.title || '', p.username || '', p.id); } catch { /* FTS failure is non-fatal */ }
     31      // ActivityPub: federate the now-published post to followers.
     32      if (!p.fan_only) {
     33        try {
     34          const site = siteStmt.get(p.site_id);
     35          if (site) {
     36            ActivityPubService.deliverCreate(site, {
     37              id: p.id, slug: p.slug, title: p.title || p.slug,
     38              content: p.content, cover_image_url: p.cover_image_url || null,
     39              published_at: p.published_at || p.publish_at, created_at: p.created_at,
     40            }).catch(() => { /* best-effort */ });
     41          }
     42        } catch { /* non-fatal */ }
     43      }
    2844    }
    2945    return due.length;
Note: See TracChangeset for help on using the changeset viewer.