@rmdes/indiekit-endpoint-syndicate 1.0.0-beta.38 → 1.0.0-beta.39
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.
- package/lib/controllers/syndicate.js +91 -62
- package/package.json +4 -1
|
@@ -15,6 +15,24 @@ import {
|
|
|
15
15
|
*/
|
|
16
16
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Guards batch mode against overlapping runs.
|
|
20
|
+
*
|
|
21
|
+
* A post stays selectable until its Micropub update lands at the end of a
|
|
22
|
+
* batch, so a second trigger arriving mid-batch sees `mp-syndicate-to` still
|
|
23
|
+
* set and syndicates it again. Both triggers — the poller and the Eleventy
|
|
24
|
+
* post-build hook — reach the same process, so a module-level flag is enough.
|
|
25
|
+
* Anything skipped is picked up by the next poll.
|
|
26
|
+
*
|
|
27
|
+
* Batch mode only: the single-post force path (source_url) is deliberately
|
|
28
|
+
* unguarded so manual re-syndication always runs.
|
|
29
|
+
*
|
|
30
|
+
* ponytail: single-process guard. If Indiekit is ever run multi-process or
|
|
31
|
+
* multi-container, replace with an atomic per-post claim (findOneAndUpdate
|
|
32
|
+
* setting a claim timestamp, with a stale-claim timeout).
|
|
33
|
+
*/
|
|
34
|
+
let batchRunning = false;
|
|
35
|
+
|
|
18
36
|
/**
|
|
19
37
|
* Syndicate a single post and update it via Micropub
|
|
20
38
|
* @param {object} options - Options
|
|
@@ -153,87 +171,98 @@ export const syndicateController = {
|
|
|
153
171
|
}
|
|
154
172
|
|
|
155
173
|
// --- Batch mode (no source_url — process ALL pending posts) ---
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (!allPostData || allPostData.length === 0) {
|
|
174
|
+
if (batchRunning) {
|
|
175
|
+
console.log("[syndication] Batch already in progress, skipping");
|
|
159
176
|
return response.json({
|
|
160
177
|
success: "OK",
|
|
161
|
-
success_description: "
|
|
178
|
+
success_description: "Batch already in progress",
|
|
162
179
|
});
|
|
163
180
|
}
|
|
164
181
|
|
|
165
|
-
|
|
166
|
-
`[syndication] Batch processing ${allPostData.length} post(s)`,
|
|
167
|
-
);
|
|
182
|
+
batchRunning = true;
|
|
168
183
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const result = await syndicatePost({
|
|
177
|
-
application,
|
|
178
|
-
publication,
|
|
179
|
-
postData,
|
|
180
|
-
bearerToken,
|
|
184
|
+
try {
|
|
185
|
+
const allPostData = await getAllPostData(postsCollection);
|
|
186
|
+
|
|
187
|
+
if (!allPostData || allPostData.length === 0) {
|
|
188
|
+
return response.json({
|
|
189
|
+
success: "OK",
|
|
190
|
+
success_description: "No posts awaiting syndication",
|
|
181
191
|
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(
|
|
195
|
+
`[syndication] Batch processing ${allPostData.length} post(s)`,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const results = [];
|
|
199
|
+
|
|
200
|
+
for (const [index, postData] of allPostData.entries()) {
|
|
201
|
+
try {
|
|
202
|
+
const result = await syndicatePost({
|
|
203
|
+
application,
|
|
204
|
+
publication,
|
|
205
|
+
postData,
|
|
206
|
+
bearerToken,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Post was skipped (not yet built)
|
|
210
|
+
if (result.skipped) {
|
|
211
|
+
results.push({
|
|
212
|
+
url: result.url,
|
|
213
|
+
success: true,
|
|
214
|
+
skipped: true,
|
|
215
|
+
reason: `Not yet built (post: ${result.readiness.postStatus}, og: ${result.readiness.ogStatus})`,
|
|
216
|
+
});
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
182
219
|
|
|
183
|
-
// Post was skipped (not yet built)
|
|
184
|
-
if (result.skipped) {
|
|
185
220
|
results.push({
|
|
186
221
|
url: result.url,
|
|
187
222
|
success: true,
|
|
188
|
-
|
|
189
|
-
|
|
223
|
+
syndicatedUrls: result.syndicatedUrls,
|
|
224
|
+
...(result.failedTargets && {
|
|
225
|
+
failedTargets: result.failedTargets,
|
|
226
|
+
}),
|
|
190
227
|
});
|
|
191
|
-
skippedCount++;
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
results.push({
|
|
196
|
-
url: result.url,
|
|
197
|
-
success: true,
|
|
198
|
-
syndicatedUrls: result.syndicatedUrls,
|
|
199
|
-
...(result.failedTargets && {
|
|
200
|
-
failedTargets: result.failedTargets,
|
|
201
|
-
}),
|
|
202
|
-
});
|
|
203
228
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
});
|
|
229
|
+
console.log(
|
|
230
|
+
`[syndication] Syndicated: ${result.url} (${result.syndicatedUrls.length} target(s))`,
|
|
231
|
+
);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
results.push({
|
|
234
|
+
url: postData.properties?.url,
|
|
235
|
+
success: false,
|
|
236
|
+
error: error.message,
|
|
237
|
+
});
|
|
214
238
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
239
|
+
console.error(
|
|
240
|
+
`[syndication] Failed: ${postData.properties?.url} - ${error.message}`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
220
243
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
244
|
+
// Rate limit delay between posts (2 seconds)
|
|
245
|
+
if (index < allPostData.length - 1) {
|
|
246
|
+
await delay(2000);
|
|
247
|
+
}
|
|
224
248
|
}
|
|
225
|
-
}
|
|
226
249
|
|
|
227
|
-
|
|
228
|
-
|
|
250
|
+
const failed = results.filter((result) => !result.success).length;
|
|
251
|
+
const skipped = results.filter((result) => result.skipped).length;
|
|
252
|
+
const succeeded = results.length - failed - skipped;
|
|
253
|
+
const description =
|
|
254
|
+
`Processed ${allPostData.length} post(s): ${succeeded} succeeded, ${failed} failed, ${skipped} not yet built`;
|
|
229
255
|
|
|
230
|
-
|
|
256
|
+
console.log(`[syndication] ${description}`);
|
|
231
257
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
258
|
+
return response.json({
|
|
259
|
+
success: "OK",
|
|
260
|
+
success_description: description,
|
|
261
|
+
results,
|
|
262
|
+
});
|
|
263
|
+
} finally {
|
|
264
|
+
batchRunning = false;
|
|
265
|
+
}
|
|
237
266
|
} catch (error) {
|
|
238
267
|
next(error);
|
|
239
268
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmdes/indiekit-endpoint-syndicate",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.39",
|
|
4
4
|
"description": "Syndication endpoint for Indiekit. Fork of @indiekit/endpoint-syndicate with batch syndication support and bug fixes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"indiekit",
|
|
@@ -37,5 +37,8 @@
|
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "node --test test/*.js"
|
|
40
43
|
}
|
|
41
44
|
}
|