feat: defer background tasks until host readiness signal
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
|
import { waitForReady } from "@rmdes/indiekit-startup-gate";
|
||||||
|
|
||||||
import { setupFederation, buildPersonActor } from "./lib/federation-setup.js";
|
import { setupFederation, buildPersonActor } from "./lib/federation-setup.js";
|
||||||
import { createMastodonRouter } from "./lib/mastodon/router.js";
|
import { createMastodonRouter } from "./lib/mastodon/router.js";
|
||||||
@@ -1096,19 +1097,6 @@ export default class ActivityPubEndpoint {
|
|||||||
// Register syndicator (appears in post editing UI)
|
// Register syndicator (appears in post editing UI)
|
||||||
Indiekit.addSyndicator(this.syndicator);
|
Indiekit.addSyndicator(this.syndicator);
|
||||||
|
|
||||||
// Start batch re-follow processor after federation settles
|
|
||||||
const refollowOptions = {
|
|
||||||
federation: this._federation,
|
|
||||||
collections: this._collections,
|
|
||||||
handle: this.options.actor.handle,
|
|
||||||
publicationUrl: this._publicationUrl,
|
|
||||||
};
|
|
||||||
setTimeout(() => {
|
|
||||||
startBatchRefollow(refollowOptions).catch((error) => {
|
|
||||||
console.error("[ActivityPub] Batch refollow start failed:", error.message);
|
|
||||||
});
|
|
||||||
}, 10_000);
|
|
||||||
|
|
||||||
// Run one-time migrations (idempotent — safe to run on every startup)
|
// Run one-time migrations (idempotent — safe to run on every startup)
|
||||||
console.info("[ActivityPub] Init: starting post-refollow setup");
|
console.info("[ActivityPub] Init: starting post-refollow setup");
|
||||||
runSeparateMentionsMigration(this._collections).then(({ skipped, updated }) => {
|
runSeparateMentionsMigration(this._collections).then(({ skipped, updated }) => {
|
||||||
@@ -1119,52 +1107,66 @@ export default class ActivityPubEndpoint {
|
|||||||
console.error("[ActivityPub] Migration separate-mentions failed:", error.message);
|
console.error("[ActivityPub] Migration separate-mentions failed:", error.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Schedule timeline retention cleanup (runs on startup + every 24h)
|
// Defer background workers until host is ready
|
||||||
if (this.options.timelineRetention > 0) {
|
const refollowOptions = {
|
||||||
scheduleCleanup(this._collections, this.options.timelineRetention);
|
federation: this._federation,
|
||||||
}
|
collections: this._collections,
|
||||||
|
handle: this.options.actor.handle,
|
||||||
// Load server blocks into Redis for fast inbox checks
|
publicationUrl: this._publicationUrl,
|
||||||
loadBlockedServersToRedis(this._collections).catch((error) => {
|
};
|
||||||
console.warn("[ActivityPub] Failed to load blocked servers to Redis:", error.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Schedule proactive key refresh for stale follower keys (runs on startup + every 24h)
|
|
||||||
const keyRefreshHandle = this.options.actor.handle;
|
const keyRefreshHandle = this.options.actor.handle;
|
||||||
const keyRefreshFederation = this._federation;
|
const keyRefreshFederation = this._federation;
|
||||||
const keyRefreshPubUrl = this._publicationUrl;
|
const keyRefreshPubUrl = this._publicationUrl;
|
||||||
scheduleKeyRefresh(
|
this._stopGate = waitForReady(
|
||||||
this._collections,
|
() => {
|
||||||
() => keyRefreshFederation?.createContext(new URL(keyRefreshPubUrl), {
|
// Start batch re-follow processor
|
||||||
handle: keyRefreshHandle,
|
startBatchRefollow(refollowOptions).catch((error) => {
|
||||||
publicationUrl: keyRefreshPubUrl,
|
console.error("[ActivityPub] Batch refollow start failed:", error.message);
|
||||||
}),
|
|
||||||
keyRefreshHandle,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Backfill ap_timeline from posts collection (idempotent, runs on every startup)
|
|
||||||
import("./lib/mastodon/backfill-timeline.js").then(({ backfillTimeline }) => {
|
|
||||||
// Delay to let MongoDB connections settle
|
|
||||||
setTimeout(() => {
|
|
||||||
backfillTimeline(this._collections).then(({ total, inserted, skipped }) => {
|
|
||||||
if (inserted > 0) {
|
|
||||||
console.log(`[Mastodon API] Timeline backfill: ${inserted} posts added (${skipped} already existed, ${total} total)`);
|
|
||||||
}
|
|
||||||
}).catch((error) => {
|
|
||||||
console.warn("[Mastodon API] Timeline backfill failed:", error.message);
|
|
||||||
});
|
});
|
||||||
}, 5000);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start async inbox queue processor (processes one item every 3s)
|
// Schedule timeline retention cleanup (runs on startup + every 24h)
|
||||||
console.info("[ActivityPub] Init: starting inbox queue processor");
|
if (this.options.timelineRetention > 0) {
|
||||||
this._inboxProcessorInterval = startInboxProcessor(
|
scheduleCleanup(this._collections, this.options.timelineRetention);
|
||||||
this._collections,
|
}
|
||||||
() => this._federation?.createContext(new URL(this._publicationUrl), {
|
|
||||||
handle: this.options.actor.handle,
|
// Load server blocks into Redis for fast inbox checks
|
||||||
publicationUrl: this._publicationUrl,
|
loadBlockedServersToRedis(this._collections).catch((error) => {
|
||||||
}),
|
console.warn("[ActivityPub] Failed to load blocked servers to Redis:", error.message);
|
||||||
this.options.actor.handle,
|
});
|
||||||
|
|
||||||
|
// Schedule proactive key refresh for stale follower keys (runs on startup + every 24h)
|
||||||
|
scheduleKeyRefresh(
|
||||||
|
this._collections,
|
||||||
|
() => keyRefreshFederation?.createContext(new URL(keyRefreshPubUrl), {
|
||||||
|
handle: keyRefreshHandle,
|
||||||
|
publicationUrl: keyRefreshPubUrl,
|
||||||
|
}),
|
||||||
|
keyRefreshHandle,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Backfill ap_timeline from posts collection (idempotent, runs on every startup)
|
||||||
|
import("./lib/mastodon/backfill-timeline.js").then(({ backfillTimeline }) => {
|
||||||
|
backfillTimeline(this._collections).then(({ total, inserted, skipped }) => {
|
||||||
|
if (inserted > 0) {
|
||||||
|
console.log(`[Mastodon API] Timeline backfill: ${inserted} posts added (${skipped} already existed, ${total} total)`);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.warn("[Mastodon API] Timeline backfill failed:", error.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start async inbox queue processor (processes one item every 3s)
|
||||||
|
console.info("[ActivityPub] Init: starting inbox queue processor");
|
||||||
|
this._inboxProcessorInterval = startInboxProcessor(
|
||||||
|
this._collections,
|
||||||
|
() => this._federation?.createContext(new URL(this._publicationUrl), {
|
||||||
|
handle: this.options.actor.handle,
|
||||||
|
publicationUrl: this._publicationUrl,
|
||||||
|
}),
|
||||||
|
this.options.actor.handle,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ label: "ActivityPub" },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1198,4 +1200,8 @@ export default class ActivityPubEndpoint {
|
|||||||
|
|
||||||
await ap_profile.insertOne(profile);
|
await ap_profile.insertOne(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this._stopGate?.();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rmdes/indiekit-endpoint-activitypub",
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
||||||
"version": "3.10.3",
|
"version": "3.10.4",
|
||||||
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"indiekit",
|
"indiekit",
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fedify/debugger": "^2.1.0",
|
"@fedify/debugger": "^2.1.0",
|
||||||
|
"@rmdes/indiekit-startup-gate": "^1.0.0",
|
||||||
"@fedify/fedify": "^2.1.0",
|
"@fedify/fedify": "^2.1.0",
|
||||||
"@fedify/redis": "^2.1.0",
|
"@fedify/redis": "^2.1.0",
|
||||||
"@js-temporal/polyfill": "^0.5.0",
|
"@js-temporal/polyfill": "^0.5.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user