@kidecms/core 0.1.4 → 0.1.6
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/admin/components/DocumentActions.tsx +13 -5
- package/dist/api.js +6 -0
- package/dist/integration.js +5 -1
- package/dist/webhooks.js +71 -0
- package/package.json +1 -1
|
@@ -149,11 +149,19 @@ export default function DocumentActions({
|
|
|
149
149
|
const form = document.createElement("form");
|
|
150
150
|
form.method = "post";
|
|
151
151
|
form.action = restoreEndpoint;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
152
|
+
|
|
153
|
+
const addInput = (name: string, value: string) => {
|
|
154
|
+
const input = document.createElement("input");
|
|
155
|
+
input.type = "hidden";
|
|
156
|
+
input.name = name;
|
|
157
|
+
input.value = value;
|
|
158
|
+
form.appendChild(input);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
addInput("_action", "restore");
|
|
162
|
+
addInput("version", String(version));
|
|
163
|
+
addInput("redirectTo", redirectTo ?? window.location.pathname + window.location.search);
|
|
164
|
+
|
|
157
165
|
document.body.appendChild(form);
|
|
158
166
|
form.submit();
|
|
159
167
|
};
|
package/dist/api.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getCollectionMap, getTranslatableFieldNames, isStructuralField } from "
|
|
|
5
5
|
import { getDb } from "./runtime";
|
|
6
6
|
import { getSchema } from "./schema";
|
|
7
7
|
import { cloneValue, createRichTextFromPlainText, slugify } from "./values";
|
|
8
|
+
import { dispatchWebhooks } from "./webhooks";
|
|
8
9
|
const now = () => new Date().toISOString();
|
|
9
10
|
const pick = (input, keys) => Object.fromEntries(keys.filter((key) => key in input).map((key) => [key, input[key]]));
|
|
10
11
|
const isJsonField = (field) => field.type === "richText" ||
|
|
@@ -405,6 +406,7 @@ export const createCms = (config) => {
|
|
|
405
406
|
}
|
|
406
407
|
const result = await this.findById(docId, {}, context);
|
|
407
408
|
await collection.hooks?.afterCreate?.(result, hookContext);
|
|
409
|
+
dispatchWebhooks(config, "create", slug, result, context.user);
|
|
408
410
|
return result;
|
|
409
411
|
},
|
|
410
412
|
async update(id, data, context = {}) {
|
|
@@ -479,6 +481,7 @@ export const createCms = (config) => {
|
|
|
479
481
|
}
|
|
480
482
|
const result = await this.findById(id, { status: "any" }, context);
|
|
481
483
|
await collection.hooks?.afterUpdate?.(result, hookContext);
|
|
484
|
+
dispatchWebhooks(config, "update", slug, result, context.user);
|
|
482
485
|
return result;
|
|
483
486
|
},
|
|
484
487
|
async delete(id, context = {}) {
|
|
@@ -499,6 +502,7 @@ export const createCms = (config) => {
|
|
|
499
502
|
await db.delete(tables.versions).where(eq(tables.versions._docId, id));
|
|
500
503
|
await db.delete(tables.main).where(eq(tables.main._id, id));
|
|
501
504
|
await collection.hooks?.afterDelete?.(existing, hookContext);
|
|
505
|
+
dispatchWebhooks(config, "delete", slug, existing, context.user);
|
|
502
506
|
},
|
|
503
507
|
async publish(id, context = {}) {
|
|
504
508
|
if (!collection.drafts)
|
|
@@ -529,6 +533,7 @@ export const createCms = (config) => {
|
|
|
529
533
|
await db.update(tables.main).set(updateValues).where(eq(tables.main._id, id));
|
|
530
534
|
const result = await this.findById(id, { status: "any" }, context);
|
|
531
535
|
await collection.hooks?.afterPublish?.(result, hookContext);
|
|
536
|
+
dispatchWebhooks(config, "publish", slug, result, context.user);
|
|
532
537
|
return result;
|
|
533
538
|
},
|
|
534
539
|
async unpublish(id, context = {}) {
|
|
@@ -559,6 +564,7 @@ export const createCms = (config) => {
|
|
|
559
564
|
await db.update(tables.main).set(updateValues).where(eq(tables.main._id, id));
|
|
560
565
|
const result = (await this.findById(id, { status: "any" }, context));
|
|
561
566
|
await collection.hooks?.afterUnpublish?.(result, hookContext);
|
|
567
|
+
dispatchWebhooks(config, "unpublish", slug, result, context.user);
|
|
562
568
|
return result;
|
|
563
569
|
},
|
|
564
570
|
async schedule(id, publishAt, unpublishAt, context = {}) {
|
package/dist/integration.js
CHANGED
|
@@ -175,7 +175,11 @@ export default function cmsIntegration(options) {
|
|
|
175
175
|
pattern: "/api/cms/locks/[...path]",
|
|
176
176
|
entrypoint: "@kidecms/core/routes/api/cms/locks/[...path].ts",
|
|
177
177
|
});
|
|
178
|
-
|
|
178
|
+
// Preview render route uses Astro Container API which depends on Vite internals.
|
|
179
|
+
// Only inject in dev mode — production builds (especially Cloudflare Workers) can't bundle it.
|
|
180
|
+
if (command === "dev") {
|
|
181
|
+
injectRoute({ pattern: "/api/cms/preview/render", entrypoint: "@kidecms/core/routes/api/cms/preview/render.ts" });
|
|
182
|
+
}
|
|
179
183
|
injectRoute({
|
|
180
184
|
pattern: "/api/cms/references/[collection]/[id]",
|
|
181
185
|
entrypoint: "@kidecms/core/routes/api/cms/references/[collection]/[id].ts",
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const MAX_RETRIES = 3;
|
|
2
|
+
const TIMEOUT_MS = 5000;
|
|
3
|
+
const RETRY_DELAYS = [1000, 3000, 9000];
|
|
4
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
5
|
+
async function deliverOnce(webhook, body) {
|
|
6
|
+
const controller = new AbortController();
|
|
7
|
+
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
8
|
+
try {
|
|
9
|
+
const response = await fetch(webhook.url, {
|
|
10
|
+
method: webhook.method ?? "POST",
|
|
11
|
+
headers: { "Content-Type": "application/json", ...webhook.headers },
|
|
12
|
+
body,
|
|
13
|
+
signal: controller.signal,
|
|
14
|
+
});
|
|
15
|
+
return { ok: response.ok, status: response.status };
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
clearTimeout(timeout);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function deliverWebhook(webhook, body) {
|
|
25
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
26
|
+
const result = await deliverOnce(webhook, body);
|
|
27
|
+
if (result.ok) {
|
|
28
|
+
if (attempt > 0) {
|
|
29
|
+
console.log(` [webhook] ${webhook.name} delivered after ${attempt + 1} attempts`);
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const detail = result.status ? `HTTP ${result.status}` : result.error;
|
|
34
|
+
if (attempt < MAX_RETRIES) {
|
|
35
|
+
console.warn(` [webhook] ${webhook.name} failed (${detail}) — retrying in ${RETRY_DELAYS[attempt]}ms`);
|
|
36
|
+
await sleep(RETRY_DELAYS[attempt]);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.error(` [webhook] ${webhook.name} failed permanently after ${MAX_RETRIES + 1} attempts: ${detail}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function dispatchWebhooks(config, event, collectionSlug, doc, user) {
|
|
44
|
+
const webhooks = config.admin?.webhooks;
|
|
45
|
+
if (!webhooks || webhooks.length === 0)
|
|
46
|
+
return;
|
|
47
|
+
const matching = webhooks.filter((webhook) => {
|
|
48
|
+
if (!webhook.events.includes(event))
|
|
49
|
+
return false;
|
|
50
|
+
if (webhook.collections && !webhook.collections.includes(collectionSlug))
|
|
51
|
+
return false;
|
|
52
|
+
return true;
|
|
53
|
+
});
|
|
54
|
+
if (matching.length === 0)
|
|
55
|
+
return;
|
|
56
|
+
const context = {
|
|
57
|
+
user: user ?? null,
|
|
58
|
+
event,
|
|
59
|
+
collection: collectionSlug,
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
};
|
|
62
|
+
// Fire all matching webhooks in parallel — don't block the operation
|
|
63
|
+
for (const webhook of matching) {
|
|
64
|
+
const payload = webhook.payload ? webhook.payload(doc, context) : { event, collection: collectionSlug, doc, user, timestamp: context.timestamp };
|
|
65
|
+
const body = JSON.stringify(payload);
|
|
66
|
+
// Fire and forget — failures are logged but don't bubble up
|
|
67
|
+
deliverWebhook(webhook, body).catch((err) => {
|
|
68
|
+
console.error(` [webhook] ${webhook.name} dispatcher error:`, err);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|