@rmdes/indiekit-endpoint-activitypub 3.10.3 → 3.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +62 -53
  2. package/package.json +2 -1
package/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import express from "express";
2
+ import { waitForReady } from "@rmdes/indiekit-startup-gate";
2
3
 
3
4
  import { setupFederation, buildPersonActor } from "./lib/federation-setup.js";
4
5
  import { createMastodonRouter } from "./lib/mastodon/router.js";
@@ -1096,19 +1097,6 @@ export default class ActivityPubEndpoint {
1096
1097
  // Register syndicator (appears in post editing UI)
1097
1098
  Indiekit.addSyndicator(this.syndicator);
1098
1099
 
1099
- // Start batch re-follow processor after federation settles
1100
- const refollowOptions = {
1101
- federation: this._federation,
1102
- collections: this._collections,
1103
- handle: this.options.actor.handle,
1104
- publicationUrl: this._publicationUrl,
1105
- };
1106
- setTimeout(() => {
1107
- startBatchRefollow(refollowOptions).catch((error) => {
1108
- console.error("[ActivityPub] Batch refollow start failed:", error.message);
1109
- });
1110
- }, 10_000);
1111
-
1112
1100
  // Run one-time migrations (idempotent — safe to run on every startup)
1113
1101
  console.info("[ActivityPub] Init: starting post-refollow setup");
1114
1102
  runSeparateMentionsMigration(this._collections).then(({ skipped, updated }) => {
@@ -1119,52 +1107,66 @@ export default class ActivityPubEndpoint {
1119
1107
  console.error("[ActivityPub] Migration separate-mentions failed:", error.message);
1120
1108
  });
1121
1109
 
1122
- // Schedule timeline retention cleanup (runs on startup + every 24h)
1123
- if (this.options.timelineRetention > 0) {
1124
- scheduleCleanup(this._collections, this.options.timelineRetention);
1125
- }
1126
-
1127
- // Load server blocks into Redis for fast inbox checks
1128
- loadBlockedServersToRedis(this._collections).catch((error) => {
1129
- console.warn("[ActivityPub] Failed to load blocked servers to Redis:", error.message);
1130
- });
1131
-
1132
- // Schedule proactive key refresh for stale follower keys (runs on startup + every 24h)
1110
+ // Defer background workers until host is ready
1111
+ const refollowOptions = {
1112
+ federation: this._federation,
1113
+ collections: this._collections,
1114
+ handle: this.options.actor.handle,
1115
+ publicationUrl: this._publicationUrl,
1116
+ };
1133
1117
  const keyRefreshHandle = this.options.actor.handle;
1134
1118
  const keyRefreshFederation = this._federation;
1135
1119
  const keyRefreshPubUrl = this._publicationUrl;
1136
- scheduleKeyRefresh(
1137
- this._collections,
1138
- () => keyRefreshFederation?.createContext(new URL(keyRefreshPubUrl), {
1139
- handle: keyRefreshHandle,
1140
- publicationUrl: keyRefreshPubUrl,
1141
- }),
1142
- keyRefreshHandle,
1143
- );
1120
+ this._stopGate = waitForReady(
1121
+ () => {
1122
+ // Start batch re-follow processor
1123
+ startBatchRefollow(refollowOptions).catch((error) => {
1124
+ console.error("[ActivityPub] Batch refollow start failed:", error.message);
1125
+ });
1144
1126
 
1145
- // Backfill ap_timeline from posts collection (idempotent, runs on every startup)
1146
- import("./lib/mastodon/backfill-timeline.js").then(({ backfillTimeline }) => {
1147
- // Delay to let MongoDB connections settle
1148
- setTimeout(() => {
1149
- backfillTimeline(this._collections).then(({ total, inserted, skipped }) => {
1150
- if (inserted > 0) {
1151
- console.log(`[Mastodon API] Timeline backfill: ${inserted} posts added (${skipped} already existed, ${total} total)`);
1152
- }
1153
- }).catch((error) => {
1154
- console.warn("[Mastodon API] Timeline backfill failed:", error.message);
1127
+ // Schedule timeline retention cleanup (runs on startup + every 24h)
1128
+ if (this.options.timelineRetention > 0) {
1129
+ scheduleCleanup(this._collections, this.options.timelineRetention);
1130
+ }
1131
+
1132
+ // Load server blocks into Redis for fast inbox checks
1133
+ loadBlockedServersToRedis(this._collections).catch((error) => {
1134
+ console.warn("[ActivityPub] Failed to load blocked servers to Redis:", error.message);
1155
1135
  });
1156
- }, 5000);
1157
- });
1158
1136
 
1159
- // Start async inbox queue processor (processes one item every 3s)
1160
- console.info("[ActivityPub] Init: starting inbox queue processor");
1161
- this._inboxProcessorInterval = startInboxProcessor(
1162
- this._collections,
1163
- () => this._federation?.createContext(new URL(this._publicationUrl), {
1164
- handle: this.options.actor.handle,
1165
- publicationUrl: this._publicationUrl,
1166
- }),
1167
- this.options.actor.handle,
1137
+ // Schedule proactive key refresh for stale follower keys (runs on startup + every 24h)
1138
+ scheduleKeyRefresh(
1139
+ this._collections,
1140
+ () => keyRefreshFederation?.createContext(new URL(keyRefreshPubUrl), {
1141
+ handle: keyRefreshHandle,
1142
+ publicationUrl: keyRefreshPubUrl,
1143
+ }),
1144
+ keyRefreshHandle,
1145
+ );
1146
+
1147
+ // Backfill ap_timeline from posts collection (idempotent, runs on every startup)
1148
+ import("./lib/mastodon/backfill-timeline.js").then(({ backfillTimeline }) => {
1149
+ backfillTimeline(this._collections).then(({ total, inserted, skipped }) => {
1150
+ if (inserted > 0) {
1151
+ console.log(`[Mastodon API] Timeline backfill: ${inserted} posts added (${skipped} already existed, ${total} total)`);
1152
+ }
1153
+ }).catch((error) => {
1154
+ console.warn("[Mastodon API] Timeline backfill failed:", error.message);
1155
+ });
1156
+ });
1157
+
1158
+ // Start async inbox queue processor (processes one item every 3s)
1159
+ console.info("[ActivityPub] Init: starting inbox queue processor");
1160
+ this._inboxProcessorInterval = startInboxProcessor(
1161
+ this._collections,
1162
+ () => this._federation?.createContext(new URL(this._publicationUrl), {
1163
+ handle: this.options.actor.handle,
1164
+ publicationUrl: this._publicationUrl,
1165
+ }),
1166
+ this.options.actor.handle,
1167
+ );
1168
+ },
1169
+ { label: "ActivityPub" },
1168
1170
  );
1169
1171
  }
1170
1172
 
@@ -1198,4 +1200,11 @@ export default class ActivityPubEndpoint {
1198
1200
 
1199
1201
  await ap_profile.insertOne(profile);
1200
1202
  }
1203
+
1204
+ destroy() {
1205
+ this._stopGate?.();
1206
+ if (this._inboxProcessorInterval) {
1207
+ clearInterval(this._inboxProcessorInterval);
1208
+ }
1209
+ }
1201
1210
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-activitypub",
3
- "version": "3.10.3",
3
+ "version": "3.10.5",
4
4
  "description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
5
5
  "keywords": [
6
6
  "indiekit",
@@ -38,6 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@fedify/debugger": "^2.1.0",
41
+ "@rmdes/indiekit-startup-gate": "^1.0.0",
41
42
  "@fedify/fedify": "^2.1.0",
42
43
  "@fedify/redis": "^2.1.0",
43
44
  "@js-temporal/polyfill": "^0.5.0",