@kyro-cms/core 0.1.5 → 0.1.7

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 (170) hide show
  1. package/dist/WebhookService-BCgL1bLF.d.cts +112 -0
  2. package/dist/WebhookService-BPVJUgTl.d.ts +112 -0
  3. package/dist/{base-DlhVlwnN.d.cts → base-B0Y6isUJ.d.cts} +1 -1
  4. package/dist/{base-CQkFzqQl.d.ts → base-DaP-5PPG.d.ts} +1 -1
  5. package/dist/bootstrap-BMWVB2T6.cjs +31 -0
  6. package/dist/{bootstrap-X6TP3NKX.cjs.map → bootstrap-BMWVB2T6.cjs.map} +1 -1
  7. package/dist/bootstrap-LL6O7PWO.js +6 -0
  8. package/dist/{bootstrap-BDTTUGY2.js.map → bootstrap-LL6O7PWO.js.map} +1 -1
  9. package/dist/{chunk-3VZCX4DF.cjs → chunk-42JPONZU.cjs} +77 -14
  10. package/dist/chunk-42JPONZU.cjs.map +1 -0
  11. package/dist/{chunk-MHS6CPO5.cjs → chunk-4M5PHMUE.cjs} +66 -346
  12. package/dist/chunk-4M5PHMUE.cjs.map +1 -0
  13. package/dist/chunk-4PWRCMTQ.cjs +15 -0
  14. package/dist/chunk-4PWRCMTQ.cjs.map +1 -0
  15. package/dist/chunk-6COM32WF.js +47 -0
  16. package/dist/chunk-6COM32WF.js.map +1 -0
  17. package/dist/chunk-6MSSF46R.js +941 -0
  18. package/dist/chunk-6MSSF46R.js.map +1 -0
  19. package/dist/{chunk-TZFJMPCH.cjs → chunk-7YITG2US.cjs} +9 -18
  20. package/dist/chunk-7YITG2US.cjs.map +1 -0
  21. package/dist/{chunk-A3RQWHKD.cjs → chunk-BLMFBDBG.cjs} +56 -6
  22. package/dist/chunk-BLMFBDBG.cjs.map +1 -0
  23. package/dist/{chunk-EINVJPFM.js → chunk-BTOE3VUK.js} +65 -3
  24. package/dist/chunk-BTOE3VUK.js.map +1 -0
  25. package/dist/chunk-E5X75WNB.js +497 -0
  26. package/dist/chunk-E5X75WNB.js.map +1 -0
  27. package/dist/chunk-E63IF3MD.cjs +951 -0
  28. package/dist/chunk-E63IF3MD.cjs.map +1 -0
  29. package/dist/{chunk-K7QF2QCM.cjs → chunk-FTSSDDZQ.cjs} +7 -3
  30. package/dist/chunk-FTSSDDZQ.cjs.map +1 -0
  31. package/dist/chunk-G7VZBCD6.cjs +35 -0
  32. package/dist/{chunk-5BLDMQED.cjs.map → chunk-G7VZBCD6.cjs.map} +1 -1
  33. package/dist/{chunk-VMSRTAH7.js → chunk-GLCPGZPM.js} +56 -6
  34. package/dist/chunk-GLCPGZPM.js.map +1 -0
  35. package/dist/{chunk-V3B25QOK.cjs → chunk-GVFB5C6O.cjs} +74 -2
  36. package/dist/chunk-GVFB5C6O.cjs.map +1 -0
  37. package/dist/chunk-HVSQDZZJ.cjs +765 -0
  38. package/dist/chunk-HVSQDZZJ.cjs.map +1 -0
  39. package/dist/chunk-HYC4GNHX.js +758 -0
  40. package/dist/chunk-HYC4GNHX.js.map +1 -0
  41. package/dist/chunk-KDVDIZ4Y.cjs +3479 -0
  42. package/dist/chunk-KDVDIZ4Y.cjs.map +1 -0
  43. package/dist/{chunk-OG3KX56O.js → chunk-KWGNR4HM.js} +7 -3
  44. package/dist/chunk-KWGNR4HM.js.map +1 -0
  45. package/dist/chunk-LIJVWQKU.cjs +256 -0
  46. package/dist/chunk-LIJVWQKU.cjs.map +1 -0
  47. package/dist/{chunk-XTZSUDSI.js → chunk-LTRCYJAG.js} +3 -18
  48. package/dist/chunk-LTRCYJAG.js.map +1 -0
  49. package/dist/{chunk-UEYC46RL.js → chunk-OUGKLCYF.js} +71 -8
  50. package/dist/chunk-OUGKLCYF.js.map +1 -0
  51. package/dist/chunk-RONAX6UU.js +3456 -0
  52. package/dist/chunk-RONAX6UU.js.map +1 -0
  53. package/dist/{chunk-C74MQIRL.js → chunk-RRYXQMZG.js} +66 -344
  54. package/dist/chunk-RRYXQMZG.js.map +1 -0
  55. package/dist/{chunk-QUJ4OLSC.js → chunk-U74F3YZU.js} +87 -7
  56. package/dist/chunk-U74F3YZU.js.map +1 -0
  57. package/dist/chunk-VIONYQ2K.cjs +517 -0
  58. package/dist/chunk-VIONYQ2K.cjs.map +1 -0
  59. package/dist/chunk-VSTRLXMQ.cjs +50 -0
  60. package/dist/chunk-VSTRLXMQ.cjs.map +1 -0
  61. package/dist/chunk-YT7HXXVN.js +13 -0
  62. package/dist/chunk-YT7HXXVN.js.map +1 -0
  63. package/dist/chunk-Z6ZWNWWR.js +30 -0
  64. package/dist/{chunk-NSBPE2FW.js.map → chunk-Z6ZWNWWR.js.map} +1 -1
  65. package/dist/cli/index.cjs +11 -7
  66. package/dist/cli/index.cjs.map +1 -1
  67. package/dist/cli/index.js +11 -7
  68. package/dist/cli/index.js.map +1 -1
  69. package/dist/drizzle/index.cjs +20 -17
  70. package/dist/drizzle/index.d.cts +4 -4
  71. package/dist/drizzle/index.d.ts +4 -4
  72. package/dist/drizzle/index.js +4 -5
  73. package/dist/graphql/index.cjs +4 -4
  74. package/dist/graphql/index.d.cts +3 -2
  75. package/dist/graphql/index.d.ts +3 -2
  76. package/dist/graphql/index.js +2 -2
  77. package/dist/{index-DI0DRPNv.d.cts → index-BwE4NueJ.d.cts} +1 -1
  78. package/dist/{index-CMUNCIWQ.d.ts → index-DUKmDSeC.d.cts} +96 -24
  79. package/dist/{index-BMySjW6o.d.cts → index-DtBi3zP0.d.ts} +96 -24
  80. package/dist/{index-4fJKLFK2.d.ts → index-DupWTmW6.d.ts} +1 -1
  81. package/dist/index.cjs +3317 -352
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.d.cts +379 -105
  84. package/dist/index.d.ts +379 -105
  85. package/dist/index.js +3211 -310
  86. package/dist/index.js.map +1 -1
  87. package/dist/media-HOT3O7RW.js +4 -0
  88. package/dist/media-HOT3O7RW.js.map +1 -0
  89. package/dist/media-WKP5AOX2.cjs +17 -0
  90. package/dist/media-WKP5AOX2.cjs.map +1 -0
  91. package/dist/mongodb/index.cjs +1 -1
  92. package/dist/mongodb/index.d.cts +2 -2
  93. package/dist/mongodb/index.d.ts +2 -2
  94. package/dist/mongodb/index.js +1 -1
  95. package/dist/mysql-media-AI6YK767.cjs +48 -0
  96. package/dist/mysql-media-AI6YK767.cjs.map +1 -0
  97. package/dist/mysql-media-CDZUS7YX.js +45 -0
  98. package/dist/mysql-media-CDZUS7YX.js.map +1 -0
  99. package/dist/postgres-auth-adapter-EVRPO7BQ.cjs +14 -0
  100. package/dist/{postgres-auth-adapter-VK6GY7LX.cjs.map → postgres-auth-adapter-EVRPO7BQ.cjs.map} +1 -1
  101. package/dist/postgres-auth-adapter-OTRWSTT5.js +5 -0
  102. package/dist/{postgres-auth-adapter-REJFUMP7.js.map → postgres-auth-adapter-OTRWSTT5.js.map} +1 -1
  103. package/dist/redis-adapter-2N6VA7BI.cjs +13 -0
  104. package/dist/{redis-adapter-LBLNKGNS.cjs.map → redis-adapter-2N6VA7BI.cjs.map} +1 -1
  105. package/dist/redis-adapter-RA24FNCX.js +4 -0
  106. package/dist/{redis-adapter-4YDY4LWE.js.map → redis-adapter-RA24FNCX.js.map} +1 -1
  107. package/dist/rest/index.cjs +7 -5
  108. package/dist/rest/index.d.cts +29 -3
  109. package/dist/rest/index.d.ts +29 -3
  110. package/dist/rest/index.js +5 -3
  111. package/dist/schema-CNB2DDTX.js +6 -0
  112. package/dist/schema-CNB2DDTX.js.map +1 -0
  113. package/dist/schema-Y777CQQS.cjs +67 -0
  114. package/dist/schema-Y777CQQS.cjs.map +1 -0
  115. package/dist/templates/index.cjs +24 -28
  116. package/dist/templates/index.d.cts +2 -4
  117. package/dist/templates/index.d.ts +2 -4
  118. package/dist/templates/index.js +2 -2
  119. package/dist/trpc/index.cjs +12 -12
  120. package/dist/trpc/index.d.cts +19 -14
  121. package/dist/trpc/index.d.ts +19 -14
  122. package/dist/trpc/index.js +3 -3
  123. package/dist/{types-BGM5MV_K.d.cts → types-BM0s_YOy.d.cts} +67 -35
  124. package/dist/{types-BGM5MV_K.d.ts → types-BM0s_YOy.d.ts} +67 -35
  125. package/dist/ws/index.cjs +1 -1
  126. package/dist/ws/index.js +1 -1
  127. package/package.json +11 -1
  128. package/dist/bootstrap-BDTTUGY2.js +0 -4
  129. package/dist/bootstrap-X6TP3NKX.cjs +0 -29
  130. package/dist/chunk-3QX6KG2S.js +0 -2125
  131. package/dist/chunk-3QX6KG2S.js.map +0 -1
  132. package/dist/chunk-3VZCX4DF.cjs.map +0 -1
  133. package/dist/chunk-5BLDMQED.cjs +0 -18
  134. package/dist/chunk-7G6EVYCU.cjs +0 -94
  135. package/dist/chunk-7G6EVYCU.cjs.map +0 -1
  136. package/dist/chunk-A3RQWHKD.cjs.map +0 -1
  137. package/dist/chunk-C74MQIRL.js.map +0 -1
  138. package/dist/chunk-EINVJPFM.js.map +0 -1
  139. package/dist/chunk-F5B64H5S.cjs +0 -2149
  140. package/dist/chunk-F5B64H5S.cjs.map +0 -1
  141. package/dist/chunk-K7QF2QCM.cjs.map +0 -1
  142. package/dist/chunk-LRTZJJPD.js +0 -86
  143. package/dist/chunk-LRTZJJPD.js.map +0 -1
  144. package/dist/chunk-MHS6CPO5.cjs.map +0 -1
  145. package/dist/chunk-NSBPE2FW.js +0 -15
  146. package/dist/chunk-OG3KX56O.js.map +0 -1
  147. package/dist/chunk-QUJ4OLSC.js.map +0 -1
  148. package/dist/chunk-R3XIBBAW.cjs +0 -34
  149. package/dist/chunk-R3XIBBAW.cjs.map +0 -1
  150. package/dist/chunk-SDMNUYVU.js +0 -30
  151. package/dist/chunk-SDMNUYVU.js.map +0 -1
  152. package/dist/chunk-TZFJMPCH.cjs.map +0 -1
  153. package/dist/chunk-UEG7KMKC.cjs +0 -228
  154. package/dist/chunk-UEG7KMKC.cjs.map +0 -1
  155. package/dist/chunk-UEYC46RL.js.map +0 -1
  156. package/dist/chunk-V3B25QOK.cjs.map +0 -1
  157. package/dist/chunk-VMSRTAH7.js.map +0 -1
  158. package/dist/chunk-XTZSUDSI.js.map +0 -1
  159. package/dist/chunk-YD7Y25W7.cjs +0 -176
  160. package/dist/chunk-YD7Y25W7.cjs.map +0 -1
  161. package/dist/chunk-YPAFJ7EV.js +0 -225
  162. package/dist/chunk-YPAFJ7EV.js.map +0 -1
  163. package/dist/database-7CJOXEZR.js +0 -5
  164. package/dist/database-7CJOXEZR.js.map +0 -1
  165. package/dist/database-QOIV44GT.cjs +0 -22
  166. package/dist/database-QOIV44GT.cjs.map +0 -1
  167. package/dist/postgres-auth-adapter-REJFUMP7.js +0 -5
  168. package/dist/postgres-auth-adapter-VK6GY7LX.cjs +0 -14
  169. package/dist/redis-adapter-4YDY4LWE.js +0 -4
  170. package/dist/redis-adapter-LBLNKGNS.cjs +0 -13
@@ -0,0 +1,497 @@
1
+ import { createHmac, randomBytes, randomUUID, timingSafeEqual } from 'crypto';
2
+
3
+ // src/access/types.ts
4
+ async function evaluateAccess(access, args) {
5
+ if (typeof access === "boolean") {
6
+ return access;
7
+ }
8
+ if (typeof access === "function") {
9
+ return await access(args);
10
+ }
11
+ return true;
12
+ }
13
+ function mergeWhereClauses(...whereClauses) {
14
+ const result = {};
15
+ for (const clause of whereClauses) {
16
+ if (clause && typeof clause === "object") {
17
+ Object.assign(result, clause);
18
+ }
19
+ }
20
+ return result;
21
+ }
22
+ function getWhereClause(access, args) {
23
+ return evaluateAccess(access, args).then((result) => {
24
+ if (result === true) return void 0;
25
+ if (result === false) return { _id: { $eq: null } };
26
+ return result;
27
+ });
28
+ }
29
+
30
+ // src/webhooks/types.ts
31
+ var WEBHOOK_EVENTS = {
32
+ COLLECTION_CREATE: "collection.create",
33
+ COLLECTION_UPDATE: "collection.update",
34
+ COLLECTION_DELETE: "collection.delete",
35
+ MEDIA_UPLOAD: "media.upload",
36
+ MEDIA_DELETE: "media.delete",
37
+ AUTH_LOGIN: "auth.login",
38
+ AUTH_REGISTER: "auth.register",
39
+ AUTH_LOGOUT: "auth.logout",
40
+ ORDER_CREATED: "order.created",
41
+ ORDER_PAID: "order.paid",
42
+ ORDER_SHIPPED: "order.shipped",
43
+ ORDER_DELIVERED: "order.delivered"
44
+ };
45
+ var ALL_WEBHOOK_EVENTS = Object.values(WEBHOOK_EVENTS);
46
+ var WEBHOOK_COLLECTION = "_webhooks";
47
+ var WEBHOOK_DELIVERY_COLLECTION = "_webhook_deliveries";
48
+ function signPayload(payload, secret) {
49
+ return `sha256=${createHmac("sha256", secret).update(payload).digest("hex")}`;
50
+ }
51
+ function generateWebhookSecret() {
52
+ return randomBytes(32).toString("hex");
53
+ }
54
+ async function deliverWebhook(webhook, payload, options = {}) {
55
+ const timeout = options.timeout || 3e4;
56
+ const startTime = Date.now();
57
+ const body = JSON.stringify(payload);
58
+ const headers = {
59
+ "Content-Type": "application/json",
60
+ "User-Agent": "Kyro-CMS-Webhook/1.0",
61
+ "X-Webhook-Event": payload.event,
62
+ "X-Webhook-Delivery": payload.id,
63
+ "X-Webhook-Timestamp": payload.timestamp,
64
+ ...webhook.headers || {}
65
+ };
66
+ if (webhook.secret) {
67
+ const signature = signPayload(body, webhook.secret);
68
+ headers["X-Webhook-Signature"] = signature;
69
+ }
70
+ const controller = new AbortController();
71
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
72
+ try {
73
+ const response = await fetch(webhook.url, {
74
+ method: "POST",
75
+ headers,
76
+ body,
77
+ signal: controller.signal
78
+ });
79
+ clearTimeout(timeoutId);
80
+ const duration = Date.now() - startTime;
81
+ let responseBody;
82
+ try {
83
+ const text = await response.text();
84
+ responseBody = text.slice(0, 1e3);
85
+ } catch {
86
+ }
87
+ const result = {
88
+ success: response.ok,
89
+ status: response.status,
90
+ statusText: response.statusText,
91
+ body: responseBody,
92
+ duration
93
+ };
94
+ if (result.success && options.onSuccess) {
95
+ options.onSuccess(result);
96
+ } else if (!result.success && options.onFailure) {
97
+ options.onFailure(`HTTP ${response.status}: ${response.statusText}`);
98
+ }
99
+ return result;
100
+ } catch (error) {
101
+ clearTimeout(timeoutId);
102
+ const duration = Date.now() - startTime;
103
+ const errorMessage = error.name === "AbortError" ? `Request timed out after ${timeout}ms` : error.message || "Unknown error";
104
+ if (options.onFailure) {
105
+ options.onFailure(errorMessage);
106
+ }
107
+ return {
108
+ success: false,
109
+ status: 0,
110
+ duration,
111
+ error: errorMessage
112
+ };
113
+ }
114
+ }
115
+ async function deliverWithRetry(webhook, payload, deliveryId, options = {}) {
116
+ const maxRetries = options.maxRetries ?? 5;
117
+ const baseDelay = options.retryDelay ?? 1e3;
118
+ let lastResult = null;
119
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
120
+ if (attempt > 0) {
121
+ const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), 3e4);
122
+ if (options.onRetry) {
123
+ options.onRetry(attempt, `Retrying in ${delay}ms...`);
124
+ }
125
+ await sleep(delay);
126
+ }
127
+ if (options.onRetry && attempt > 0) {
128
+ options.onRetry(attempt, `Attempt ${attempt + 1}/${maxRetries + 1}`);
129
+ }
130
+ lastResult = await deliverWebhook(webhook, payload, {
131
+ ...options,
132
+ onRetry: void 0,
133
+ onSuccess: void 0,
134
+ onFailure: void 0
135
+ });
136
+ if (lastResult.success) {
137
+ return lastResult;
138
+ }
139
+ if (lastResult.error?.includes("timed out") && attempt < maxRetries) {
140
+ continue;
141
+ }
142
+ if (lastResult.status >= 400 && lastResult.status < 500) {
143
+ return lastResult;
144
+ }
145
+ }
146
+ return lastResult || {
147
+ success: false,
148
+ status: 0,
149
+ duration: 0,
150
+ error: "All delivery attempts failed"
151
+ };
152
+ }
153
+ function buildDeliveryRecord(deliveryId, webhookId, event, payload, attempt, result) {
154
+ return {
155
+ id: deliveryId,
156
+ webhookId,
157
+ event,
158
+ payload,
159
+ attempt,
160
+ status: result.success ? "success" : "failed",
161
+ responseStatus: result.status || void 0,
162
+ responseBody: result.body,
163
+ duration: result.duration,
164
+ error: result.error,
165
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
166
+ deliveredAt: result.success ? (/* @__PURE__ */ new Date()).toISOString() : void 0
167
+ };
168
+ }
169
+ function sleep(ms) {
170
+ return new Promise((resolve) => setTimeout(resolve, ms));
171
+ }
172
+ function createTestPayload() {
173
+ return {
174
+ id: `test_${Date.now()}`,
175
+ event: "collection.create",
176
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
177
+ collection: "test",
178
+ operation: "create",
179
+ data: { message: "This is a test webhook delivery" },
180
+ user: { id: "system", email: "system@kyro.dev", role: "super_admin" }
181
+ };
182
+ }
183
+ var WebhookService = class {
184
+ db;
185
+ constructor(db) {
186
+ this.db = db;
187
+ }
188
+ async getWebhooks(filters) {
189
+ const result = await this.db.find({
190
+ collection: WEBHOOK_COLLECTION,
191
+ where: filters?.status ? { status: { equals: filters.status } } : {},
192
+ limit: 100,
193
+ page: 1
194
+ });
195
+ const webhooks = result.docs;
196
+ if (filters?.event) {
197
+ return webhooks.filter(
198
+ (w) => w.events.includes(filters.event)
199
+ );
200
+ }
201
+ return webhooks;
202
+ }
203
+ async getWebhookById(id) {
204
+ return this.db.findByID({
205
+ collection: WEBHOOK_COLLECTION,
206
+ id
207
+ });
208
+ }
209
+ async createWebhook(data) {
210
+ const now = (/* @__PURE__ */ new Date()).toISOString();
211
+ const secret = data.secret || generateWebhookSecret();
212
+ const webhook = {
213
+ id: randomUUID(),
214
+ name: data.name,
215
+ url: data.url,
216
+ events: data.events,
217
+ status: data.status || "active",
218
+ secret,
219
+ headers: data.headers || {},
220
+ createdAt: now,
221
+ updatedAt: now
222
+ };
223
+ await this.db.create({
224
+ collection: WEBHOOK_COLLECTION,
225
+ data: webhook
226
+ });
227
+ return webhook;
228
+ }
229
+ async updateWebhook(id, data) {
230
+ const existing = await this.getWebhookById(id);
231
+ if (!existing) return null;
232
+ const updated = {
233
+ ...existing,
234
+ ...data,
235
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
236
+ };
237
+ if (data.secret === "" && "secret" in data) {
238
+ delete updated.secret;
239
+ }
240
+ await this.db.update({
241
+ collection: WEBHOOK_COLLECTION,
242
+ id,
243
+ data: updated
244
+ });
245
+ return updated;
246
+ }
247
+ async deleteWebhook(id) {
248
+ await this.db.delete({
249
+ collection: WEBHOOK_COLLECTION,
250
+ id
251
+ });
252
+ }
253
+ async trigger(event, payloadData) {
254
+ const webhooks = await this.getWebhooks();
255
+ const activeWebhooks = webhooks.filter((w) => w.status === "active");
256
+ const matchingWebhooks = activeWebhooks.filter(
257
+ (w) => w.events.includes(event)
258
+ );
259
+ if (matchingWebhooks.length === 0) {
260
+ return [];
261
+ }
262
+ const payload = {
263
+ id: `wh_${Date.now()}_${randomUUID().slice(0, 8)}`,
264
+ event,
265
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
266
+ ...payloadData
267
+ };
268
+ const results = [];
269
+ for (const webhook of matchingWebhooks) {
270
+ const result = await this.triggerWebhook(webhook, payload);
271
+ results.push(result);
272
+ }
273
+ return results;
274
+ }
275
+ async triggerWebhook(webhook, payload) {
276
+ const deliveryId = `dlv_${Date.now()}_${randomUUID().slice(0, 8)}`;
277
+ try {
278
+ const result = await deliverWithRetry(webhook, payload, deliveryId, {
279
+ maxRetries: 5,
280
+ retryDelay: 1e3
281
+ });
282
+ const deliveryRecord = buildDeliveryRecord(
283
+ deliveryId,
284
+ webhook.id,
285
+ webhook.events[0],
286
+ payload,
287
+ 1,
288
+ result
289
+ );
290
+ try {
291
+ await this.db.create({
292
+ collection: WEBHOOK_DELIVERY_COLLECTION,
293
+ data: deliveryRecord
294
+ });
295
+ } catch {
296
+ console.warn(
297
+ "[WebhookService] Failed to save delivery record:",
298
+ deliveryId
299
+ );
300
+ }
301
+ if (!result.success) {
302
+ await this.updateWebhook(webhook.id, {
303
+ status: "error",
304
+ lastError: result.error
305
+ }).catch(() => {
306
+ });
307
+ } else {
308
+ await this.updateWebhook(webhook.id, {
309
+ status: "active",
310
+ lastTriggered: (/* @__PURE__ */ new Date()).toISOString()
311
+ }).catch(() => {
312
+ });
313
+ }
314
+ return {
315
+ deliveryId,
316
+ webhookId: webhook.id,
317
+ event: webhook.events[0],
318
+ status: result.success ? "success" : "failed",
319
+ responseStatus: result.status,
320
+ duration: result.duration,
321
+ error: result.error
322
+ };
323
+ } catch (error) {
324
+ return {
325
+ deliveryId,
326
+ webhookId: webhook.id,
327
+ event: webhook.events[0],
328
+ status: "failed",
329
+ error: error.message
330
+ };
331
+ }
332
+ }
333
+ async testWebhook(webhookId) {
334
+ const webhook = await this.getWebhookById(webhookId);
335
+ if (!webhook) return null;
336
+ const payload = {
337
+ id: `test_${Date.now()}`,
338
+ event: webhook.events[0] || "collection.create",
339
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
340
+ collection: "test",
341
+ operation: "create",
342
+ data: { message: "This is a test webhook delivery from Kyro CMS" },
343
+ user: {
344
+ id: "system",
345
+ email: "system@kyro.dev",
346
+ role: "super_admin"
347
+ }
348
+ };
349
+ return this.triggerWebhook(webhook, payload);
350
+ }
351
+ async getDeliveryHistory(webhookId, limit = 50) {
352
+ const result = await this.db.find({
353
+ collection: WEBHOOK_DELIVERY_COLLECTION,
354
+ where: { webhookId: { equals: webhookId } },
355
+ sort: "-createdAt",
356
+ limit,
357
+ page: 1
358
+ });
359
+ return result.docs;
360
+ }
361
+ async retryDelivery(deliveryId) {
362
+ const delivery = await this.db.findByID({
363
+ collection: WEBHOOK_DELIVERY_COLLECTION,
364
+ id: deliveryId
365
+ });
366
+ if (!delivery) return null;
367
+ const webhook = await this.getWebhookById(delivery.webhookId);
368
+ if (!webhook) return null;
369
+ return this.triggerWebhook(webhook, delivery.payload);
370
+ }
371
+ };
372
+ function createWebhookService(db) {
373
+ return new WebhookService(db);
374
+ }
375
+ var API_KEY_COLLECTION = "_api_keys";
376
+ function generateKeyPrefix(key) {
377
+ return key.substring(0, 8);
378
+ }
379
+ function constantTimeCompare(a, b) {
380
+ if (a.length !== b.length) {
381
+ return false;
382
+ }
383
+ try {
384
+ return timingSafeEqual(Buffer.from(a), Buffer.from(b));
385
+ } catch {
386
+ return false;
387
+ }
388
+ }
389
+ async function validateApiKey(rawKey, db, userLookup) {
390
+ if (!rawKey || typeof rawKey !== "string") {
391
+ return { valid: false, error: "No API key provided" };
392
+ }
393
+ if (!rawKey.startsWith("kyro_")) {
394
+ return { valid: false, error: "Invalid API key format" };
395
+ }
396
+ const keyPrefix = generateKeyPrefix(rawKey);
397
+ try {
398
+ const result = await db.find({
399
+ collection: API_KEY_COLLECTION,
400
+ where: { keyPrefix: { equals: keyPrefix } },
401
+ limit: 100,
402
+ page: 1
403
+ });
404
+ if (!result.docs || result.docs.length === 0) {
405
+ return { valid: false, error: "Invalid API key" };
406
+ }
407
+ let matchedKey = null;
408
+ for (const doc of result.docs) {
409
+ const record = doc;
410
+ if (constantTimeCompare(record.key, rawKey)) {
411
+ matchedKey = record;
412
+ break;
413
+ }
414
+ }
415
+ if (!matchedKey) {
416
+ return { valid: false, error: "Invalid API key" };
417
+ }
418
+ if (matchedKey.expiresAt) {
419
+ const expiresAt = new Date(matchedKey.expiresAt);
420
+ if (expiresAt < /* @__PURE__ */ new Date()) {
421
+ return { valid: false, error: "API key has expired" };
422
+ }
423
+ }
424
+ try {
425
+ await db.update({
426
+ collection: API_KEY_COLLECTION,
427
+ id: matchedKey.id,
428
+ data: { lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }
429
+ });
430
+ } catch {
431
+ }
432
+ const user = {
433
+ id: matchedKey.userId,
434
+ role: matchedKey.role || "author",
435
+ tenantId: matchedKey.tenantId
436
+ };
437
+ if (userLookup) {
438
+ const dbUser = await userLookup(matchedKey.userId);
439
+ if (dbUser) {
440
+ Object.assign(user, dbUser);
441
+ }
442
+ }
443
+ return {
444
+ valid: true,
445
+ userId: matchedKey.userId,
446
+ user,
447
+ permissions: matchedKey.permissions || [],
448
+ apiKeyId: matchedKey.id,
449
+ tenantId: user.tenantId,
450
+ role: user.role
451
+ };
452
+ } catch (error) {
453
+ console.error("[ApiKey] Validation error:", error);
454
+ return { valid: false, error: "Failed to validate API key" };
455
+ }
456
+ }
457
+ function extractApiKeyFromRequest(request) {
458
+ const authHeader = request.headers.get("Authorization");
459
+ if (authHeader) {
460
+ if (authHeader.startsWith("ApiKey ")) {
461
+ return authHeader.slice(7).trim();
462
+ }
463
+ if (authHeader.startsWith("Bearer ")) {
464
+ return null;
465
+ }
466
+ }
467
+ const xApiKey = request.headers.get("X-API-Key");
468
+ if (xApiKey) {
469
+ return xApiKey.trim();
470
+ }
471
+ return null;
472
+ }
473
+ function createApiKeyContext(result) {
474
+ if (!result.valid || !result.userId) {
475
+ return null;
476
+ }
477
+ return {
478
+ userId: result.userId,
479
+ user: result.user || {},
480
+ permissions: result.permissions || [],
481
+ apiKeyId: result.apiKeyId || "",
482
+ tenantId: result.tenantId,
483
+ role: result.role
484
+ };
485
+ }
486
+ function hasApiKeyPermission(permissions, required) {
487
+ if (permissions.length === 0) return false;
488
+ if (permissions.includes("*")) return true;
489
+ if (permissions.includes(required)) return true;
490
+ const [resource, action] = required.split(":");
491
+ if (permissions.includes(`${resource}:*`)) return true;
492
+ return false;
493
+ }
494
+
495
+ export { ALL_WEBHOOK_EVENTS, WEBHOOK_COLLECTION, WEBHOOK_DELIVERY_COLLECTION, WEBHOOK_EVENTS, WebhookService, buildDeliveryRecord, createApiKeyContext, createTestPayload, createWebhookService, deliverWebhook, deliverWithRetry, evaluateAccess, extractApiKeyFromRequest, generateWebhookSecret, getWhereClause, hasApiKeyPermission, mergeWhereClauses, signPayload, validateApiKey };
496
+ //# sourceMappingURL=chunk-E5X75WNB.js.map
497
+ //# sourceMappingURL=chunk-E5X75WNB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/access/types.ts","../src/webhooks/types.ts","../src/webhooks/delivery.ts","../src/webhooks/WebhookService.ts","../src/auth/api-key.ts"],"names":[],"mappings":"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","file":"chunk-E5X75WNB.js","sourcesContent":["import type { User, Request } from '../hooks/types.js';\n\n// ============================================================================\n// Access Control Types\n// ============================================================================\n\nexport interface WhereClause {\n [field: string]: any;\n}\n\nexport interface AccessArgs {\n req: Request;\n user?: User;\n data?: any;\n doc?: any;\n id?: string;\n tenantID?: string;\n context?: Record<string, any>;\n}\n\nexport type AccessControl = boolean | ((args: AccessArgs) => Promise<boolean | WhereClause> | boolean | WhereClause);\n\nexport interface CollectionAccess {\n create?: AccessControl;\n read?: AccessControl;\n update?: AccessControl;\n delete?: AccessControl;\n admin?: AccessControl;\n unlock?: AccessControl;\n readVersions?: AccessControl;\n}\n\nexport interface GlobalAccess {\n read?: AccessControl;\n update?: AccessControl;\n}\n\nexport interface FieldAccess {\n create?: AccessControl;\n read?: AccessControl;\n update?: AccessControl;\n}\n\n// ============================================================================\n// Access Control Evaluation\n// ============================================================================\n\nexport async function evaluateAccess(\n access: AccessControl,\n args: AccessArgs\n): Promise<boolean | WhereClause> {\n if (typeof access === 'boolean') {\n return access;\n }\n if (typeof access === 'function') {\n return await access(args);\n }\n return true;\n}\n\nexport function mergeWhereClauses(\n ...whereClauses: (WhereClause | boolean | undefined)[]\n): WhereClause {\n const result: WhereClause = {};\n for (const clause of whereClauses) {\n if (clause && typeof clause === 'object') {\n Object.assign(result, clause);\n }\n }\n return result;\n}\n\nexport function getWhereClause(\n access: AccessControl,\n args: AccessArgs\n): Promise<WhereClause | undefined> {\n return evaluateAccess(access, args).then(result => {\n if (result === true) return undefined;\n if (result === false) return { _id: { $eq: null } };\n return result;\n });\n}\n","export const WEBHOOK_EVENTS = {\n COLLECTION_CREATE: \"collection.create\",\n COLLECTION_UPDATE: \"collection.update\",\n COLLECTION_DELETE: \"collection.delete\",\n MEDIA_UPLOAD: \"media.upload\",\n MEDIA_DELETE: \"media.delete\",\n AUTH_LOGIN: \"auth.login\",\n AUTH_REGISTER: \"auth.register\",\n AUTH_LOGOUT: \"auth.logout\",\n ORDER_CREATED: \"order.created\",\n ORDER_PAID: \"order.paid\",\n ORDER_SHIPPED: \"order.shipped\",\n ORDER_DELIVERED: \"order.delivered\",\n} as const;\n\nexport type WebhookEvent = (typeof WEBHOOK_EVENTS)[keyof typeof WEBHOOK_EVENTS];\n\nexport const ALL_WEBHOOK_EVENTS: WebhookEvent[] = Object.values(WEBHOOK_EVENTS);\n\nexport interface WebhookConfig {\n id: string;\n name: string;\n url: string;\n events: WebhookEvent[];\n status: \"active\" | \"inactive\" | \"error\";\n secret?: string;\n headers?: Record<string, string>;\n lastTriggered?: string;\n lastError?: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateWebhookData {\n name: string;\n url: string;\n events: WebhookEvent[];\n status?: \"active\" | \"inactive\";\n secret?: string;\n headers?: Record<string, string>;\n}\n\nexport interface UpdateWebhookData {\n name?: string;\n url?: string;\n events?: WebhookEvent[];\n status?: \"active\" | \"inactive\" | \"error\";\n secret?: string;\n headers?: Record<string, string>;\n lastTriggered?: string | null;\n lastError?: string | null;\n}\n\nexport interface WebhookPayload {\n id: string;\n event: WebhookEvent;\n timestamp: string;\n collection?: string;\n operation?: \"create\" | \"update\" | \"delete\";\n data?: unknown;\n previousData?: unknown;\n user?: {\n id: string;\n email?: string;\n role?: string;\n };\n tenantId?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface WebhookDelivery {\n id: string;\n webhookId: string;\n event: WebhookEvent;\n payload: WebhookPayload;\n attempt: number;\n status: \"pending\" | \"success\" | \"failed\" | \"retrying\";\n responseStatus?: number;\n responseBody?: string;\n error?: string;\n duration?: number;\n createdAt: string;\n deliveredAt?: string;\n nextRetryAt?: string;\n}\n\nexport interface WebhookTriggerResult {\n deliveryId: string;\n webhookId: string;\n event: WebhookEvent;\n status: \"queued\" | \"success\" | \"failed\";\n responseStatus?: number;\n duration?: number;\n error?: string;\n}\n\nexport const WEBHOOK_COLLECTION = \"_webhooks\";\nexport const WEBHOOK_DELIVERY_COLLECTION = \"_webhook_deliveries\";\n","import { createHmac, randomBytes } from \"crypto\";\nimport type {\n WebhookConfig,\n WebhookPayload,\n WebhookDelivery,\n} from \"./types.js\";\n\nexport interface DeliveryResult {\n success: boolean;\n status: number;\n statusText?: string;\n body?: string;\n duration: number;\n error?: string;\n}\n\nexport interface DeliveryOptions {\n timeout?: number;\n maxRetries?: number;\n retryDelay?: number;\n onRetry?: (attempt: number, error: string) => void;\n onSuccess?: (result: DeliveryResult) => void;\n onFailure?: (error: string) => void;\n}\n\nexport function signPayload(payload: string, secret: string): string {\n return `sha256=${createHmac(\"sha256\", secret).update(payload).digest(\"hex\")}`;\n}\n\nexport function generateWebhookSecret(): string {\n return randomBytes(32).toString(\"hex\");\n}\n\nexport async function deliverWebhook(\n webhook: WebhookConfig,\n payload: WebhookPayload,\n options: DeliveryOptions = {},\n): Promise<DeliveryResult> {\n const timeout = options.timeout || 30000;\n const startTime = Date.now();\n\n const body = JSON.stringify(payload);\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"Kyro-CMS-Webhook/1.0\",\n \"X-Webhook-Event\": payload.event,\n \"X-Webhook-Delivery\": payload.id,\n \"X-Webhook-Timestamp\": payload.timestamp,\n ...(webhook.headers || {}),\n };\n\n if (webhook.secret) {\n const signature = signPayload(body, webhook.secret);\n headers[\"X-Webhook-Signature\"] = signature;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(webhook.url, {\n method: \"POST\",\n headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n const duration = Date.now() - startTime;\n let responseBody: string | undefined;\n\n try {\n const text = await response.text();\n responseBody = text.slice(0, 1000);\n } catch {}\n\n const result: DeliveryResult = {\n success: response.ok,\n status: response.status,\n statusText: response.statusText,\n body: responseBody,\n duration,\n };\n\n if (result.success && options.onSuccess) {\n options.onSuccess(result);\n } else if (!result.success && options.onFailure) {\n options.onFailure(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return result;\n } catch (error: any) {\n clearTimeout(timeoutId);\n const duration = Date.now() - startTime;\n const errorMessage =\n error.name === \"AbortError\"\n ? `Request timed out after ${timeout}ms`\n : error.message || \"Unknown error\";\n\n if (options.onFailure) {\n options.onFailure(errorMessage);\n }\n\n return {\n success: false,\n status: 0,\n duration,\n error: errorMessage,\n };\n }\n}\n\nexport async function deliverWithRetry(\n webhook: WebhookConfig,\n payload: WebhookPayload,\n deliveryId: string,\n options: DeliveryOptions = {},\n): Promise<DeliveryResult> {\n const maxRetries = options.maxRetries ?? 5;\n const baseDelay = options.retryDelay ?? 1000;\n let lastResult: DeliveryResult | null = null;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), 30000);\n if (options.onRetry) {\n options.onRetry(attempt, `Retrying in ${delay}ms...`);\n }\n await sleep(delay);\n }\n\n if (options.onRetry && attempt > 0) {\n options.onRetry(attempt, `Attempt ${attempt + 1}/${maxRetries + 1}`);\n }\n\n lastResult = await deliverWebhook(webhook, payload, {\n ...options,\n onRetry: undefined,\n onSuccess: undefined,\n onFailure: undefined,\n });\n\n if (lastResult.success) {\n return lastResult;\n }\n\n if (lastResult.error?.includes(\"timed out\") && attempt < maxRetries) {\n continue;\n }\n\n if (lastResult.status >= 400 && lastResult.status < 500) {\n return lastResult;\n }\n }\n\n return (\n lastResult || {\n success: false,\n status: 0,\n duration: 0,\n error: \"All delivery attempts failed\",\n }\n );\n}\n\nexport function buildDeliveryRecord(\n deliveryId: string,\n webhookId: string,\n event: string,\n payload: WebhookPayload,\n attempt: number,\n result: DeliveryResult,\n): WebhookDelivery {\n return {\n id: deliveryId,\n webhookId,\n event: event as any,\n payload,\n attempt,\n status: result.success ? \"success\" : \"failed\",\n responseStatus: result.status || undefined,\n responseBody: result.body,\n duration: result.duration,\n error: result.error,\n createdAt: new Date().toISOString(),\n deliveredAt: result.success ? new Date().toISOString() : undefined,\n };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function createTestPayload(): WebhookPayload {\n return {\n id: `test_${Date.now()}`,\n event: \"collection.create\",\n timestamp: new Date().toISOString(),\n collection: \"test\",\n operation: \"create\",\n data: { message: \"This is a test webhook delivery\" },\n user: { id: \"system\", email: \"system@kyro.dev\", role: \"super_admin\" },\n };\n}\n","import { randomUUID } from \"crypto\";\nimport type { BaseAdapter } from \"../registry/types.js\";\nimport {\n type WebhookConfig,\n type CreateWebhookData,\n type UpdateWebhookData,\n type WebhookPayload,\n type WebhookDelivery,\n type WebhookEvent,\n type WebhookTriggerResult,\n WEBHOOK_COLLECTION,\n WEBHOOK_DELIVERY_COLLECTION,\n} from \"./types.js\";\nimport {\n deliverWithRetry,\n buildDeliveryRecord,\n generateWebhookSecret,\n} from \"./delivery.js\";\n\nexport class WebhookService {\n private db: BaseAdapter;\n\n constructor(db: BaseAdapter) {\n this.db = db;\n }\n\n async getWebhooks(filters?: {\n status?: string;\n event?: WebhookEvent;\n }): Promise<WebhookConfig[]> {\n const result = await this.db.find({\n collection: WEBHOOK_COLLECTION,\n where: filters?.status ? { status: { equals: filters.status } } : {},\n limit: 100,\n page: 1,\n });\n\n const webhooks = result.docs as unknown as WebhookConfig[];\n\n if (filters?.event) {\n return webhooks.filter((w) =>\n w.events.includes(filters.event as WebhookEvent),\n );\n }\n\n return webhooks;\n }\n\n async getWebhookById(id: string): Promise<WebhookConfig | null> {\n return this.db.findByID({\n collection: WEBHOOK_COLLECTION,\n id,\n }) as Promise<WebhookConfig | null>;\n }\n\n async createWebhook(data: CreateWebhookData): Promise<WebhookConfig> {\n const now = new Date().toISOString();\n const secret = data.secret || generateWebhookSecret();\n\n const webhook = {\n id: randomUUID(),\n name: data.name,\n url: data.url,\n events: data.events,\n status: data.status || \"active\",\n secret,\n headers: data.headers || {},\n createdAt: now,\n updatedAt: now,\n };\n\n await this.db.create({\n collection: WEBHOOK_COLLECTION,\n data: webhook,\n });\n\n return webhook as WebhookConfig;\n }\n\n async updateWebhook(\n id: string,\n data: UpdateWebhookData,\n ): Promise<WebhookConfig | null> {\n const existing = await this.getWebhookById(id);\n if (!existing) return null;\n\n const updated = {\n ...existing,\n ...data,\n updatedAt: new Date().toISOString(),\n };\n\n if (data.secret === \"\" && \"secret\" in data) {\n delete updated.secret;\n }\n\n await this.db.update({\n collection: WEBHOOK_COLLECTION,\n id,\n data: updated,\n });\n\n return updated as WebhookConfig;\n }\n\n async deleteWebhook(id: string): Promise<void> {\n await this.db.delete({\n collection: WEBHOOK_COLLECTION,\n id,\n });\n }\n\n async trigger(\n event: WebhookEvent,\n payloadData: Omit<WebhookPayload, \"id\" | \"event\" | \"timestamp\">,\n ): Promise<WebhookTriggerResult[]> {\n const webhooks = await this.getWebhooks();\n const activeWebhooks = webhooks.filter((w) => w.status === \"active\");\n\n const matchingWebhooks = activeWebhooks.filter((w) =>\n w.events.includes(event),\n );\n\n if (matchingWebhooks.length === 0) {\n return [];\n }\n\n const payload: WebhookPayload = {\n id: `wh_${Date.now()}_${randomUUID().slice(0, 8)}`,\n event,\n timestamp: new Date().toISOString(),\n ...payloadData,\n };\n\n const results: WebhookTriggerResult[] = [];\n\n for (const webhook of matchingWebhooks) {\n const result = await this.triggerWebhook(webhook, payload);\n results.push(result);\n }\n\n return results;\n }\n\n async triggerWebhook(\n webhook: WebhookConfig,\n payload: WebhookPayload,\n ): Promise<WebhookTriggerResult> {\n const deliveryId = `dlv_${Date.now()}_${randomUUID().slice(0, 8)}`;\n\n try {\n const result = await deliverWithRetry(webhook, payload, deliveryId, {\n maxRetries: 5,\n retryDelay: 1000,\n });\n\n const deliveryRecord = buildDeliveryRecord(\n deliveryId,\n webhook.id,\n webhook.events[0],\n payload,\n 1,\n result,\n );\n\n try {\n await this.db.create({\n collection: WEBHOOK_DELIVERY_COLLECTION,\n data: deliveryRecord,\n });\n } catch {\n console.warn(\n \"[WebhookService] Failed to save delivery record:\",\n deliveryId,\n );\n }\n\n if (!result.success) {\n await this.updateWebhook(webhook.id, {\n status: \"error\",\n lastError: result.error,\n }).catch(() => {});\n } else {\n await this.updateWebhook(webhook.id, {\n status: \"active\",\n lastTriggered: new Date().toISOString(),\n }).catch(() => {});\n }\n\n return {\n deliveryId,\n webhookId: webhook.id,\n event: webhook.events[0],\n status: result.success ? \"success\" : \"failed\",\n responseStatus: result.status,\n duration: result.duration,\n error: result.error,\n };\n } catch (error: any) {\n return {\n deliveryId,\n webhookId: webhook.id,\n event: webhook.events[0],\n status: \"failed\",\n error: error.message,\n };\n }\n }\n\n async testWebhook(webhookId: string): Promise<WebhookTriggerResult | null> {\n const webhook = await this.getWebhookById(webhookId);\n if (!webhook) return null;\n\n const payload: WebhookPayload = {\n id: `test_${Date.now()}`,\n event: webhook.events[0] || \"collection.create\",\n timestamp: new Date().toISOString(),\n collection: \"test\",\n operation: \"create\",\n data: { message: \"This is a test webhook delivery from Kyro CMS\" },\n user: {\n id: \"system\",\n email: \"system@kyro.dev\",\n role: \"super_admin\",\n },\n };\n\n return this.triggerWebhook(webhook, payload);\n }\n\n async getDeliveryHistory(\n webhookId: string,\n limit: number = 50,\n ): Promise<WebhookDelivery[]> {\n const result = await this.db.find({\n collection: WEBHOOK_DELIVERY_COLLECTION,\n where: { webhookId: { equals: webhookId } },\n sort: \"-createdAt\",\n limit,\n page: 1,\n });\n\n return result.docs as unknown as WebhookDelivery[];\n }\n\n async retryDelivery(\n deliveryId: string,\n ): Promise<WebhookTriggerResult | null> {\n const delivery = (await this.db.findByID({\n collection: WEBHOOK_DELIVERY_COLLECTION,\n id: deliveryId,\n })) as WebhookDelivery | null;\n\n if (!delivery) return null;\n\n const webhook = await this.getWebhookById(delivery.webhookId);\n if (!webhook) return null;\n\n return this.triggerWebhook(webhook, delivery.payload);\n }\n}\n\nexport function createWebhookService(db: BaseAdapter): WebhookService {\n return new WebhookService(db);\n}\n","import { timingSafeEqual } from \"crypto\";\nimport type { BaseAdapter } from \"../registry/types.js\";\nimport type { AuthUser, UserRole } from \"./types.js\";\n\nexport interface ApiKeyRecord {\n id: string;\n userId: string;\n name: string;\n key: string;\n keyPrefix: string;\n permissions: string[];\n lastUsedAt?: string;\n expiresAt?: string;\n createdAt: string;\n}\n\nexport interface ApiKeyValidationResult {\n valid: boolean;\n userId?: string;\n user?: Partial<AuthUser>;\n permissions?: string[];\n apiKeyId?: string;\n error?: string;\n tenantId?: string;\n role?: UserRole;\n}\n\nexport interface ApiKeyContext {\n userId: string;\n user: Partial<AuthUser>;\n permissions: string[];\n apiKeyId: string;\n tenantId?: string;\n role?: UserRole;\n}\n\nexport const API_KEY_COLLECTION = \"_api_keys\";\n\nfunction generateKeyPrefix(key: string): string {\n return key.substring(0, 8);\n}\n\nfunction constantTimeCompare(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n try {\n return timingSafeEqual(Buffer.from(a), Buffer.from(b));\n } catch {\n return false;\n }\n}\n\nexport async function validateApiKey(\n rawKey: string,\n db: BaseAdapter,\n userLookup?: (userId: string) => Promise<Partial<AuthUser> | null>,\n): Promise<ApiKeyValidationResult> {\n if (!rawKey || typeof rawKey !== \"string\") {\n return { valid: false, error: \"No API key provided\" };\n }\n\n if (!rawKey.startsWith(\"kyro_\")) {\n return { valid: false, error: \"Invalid API key format\" };\n }\n\n const keyPrefix = generateKeyPrefix(rawKey);\n\n try {\n const result = await db.find({\n collection: API_KEY_COLLECTION,\n where: { keyPrefix: { equals: keyPrefix } },\n limit: 100,\n page: 1,\n });\n\n if (!result.docs || result.docs.length === 0) {\n return { valid: false, error: \"Invalid API key\" };\n }\n\n let matchedKey: ApiKeyRecord | null = null;\n for (const doc of result.docs) {\n const record = doc as unknown as ApiKeyRecord;\n if (constantTimeCompare(record.key, rawKey)) {\n matchedKey = record;\n break;\n }\n }\n\n if (!matchedKey) {\n return { valid: false, error: \"Invalid API key\" };\n }\n\n if (matchedKey.expiresAt) {\n const expiresAt = new Date(matchedKey.expiresAt);\n if (expiresAt < new Date()) {\n return { valid: false, error: \"API key has expired\" };\n }\n }\n\n try {\n await db.update({\n collection: API_KEY_COLLECTION,\n id: matchedKey.id,\n data: { lastUsedAt: new Date().toISOString() },\n });\n } catch {\n // Non-critical: don't fail if lastUsedAt update fails\n }\n\n const user: Partial<AuthUser> = {\n id: matchedKey.userId,\n role: (matchedKey as any).role || \"author\",\n tenantId: (matchedKey as any).tenantId,\n };\n\n if (userLookup) {\n const dbUser = await userLookup(matchedKey.userId);\n if (dbUser) {\n Object.assign(user, dbUser);\n }\n }\n\n return {\n valid: true,\n userId: matchedKey.userId,\n user,\n permissions: matchedKey.permissions || [],\n apiKeyId: matchedKey.id,\n tenantId: user.tenantId,\n role: user.role,\n };\n } catch (error) {\n console.error(\"[ApiKey] Validation error:\", error);\n return { valid: false, error: \"Failed to validate API key\" };\n }\n}\n\nexport function extractApiKeyFromRequest(request: Request): string | null {\n const authHeader = request.headers.get(\"Authorization\");\n if (authHeader) {\n if (authHeader.startsWith(\"ApiKey \")) {\n return authHeader.slice(7).trim();\n }\n if (authHeader.startsWith(\"Bearer \")) {\n return null;\n }\n }\n\n const xApiKey = request.headers.get(\"X-API-Key\");\n if (xApiKey) {\n return xApiKey.trim();\n }\n\n return null;\n}\n\nexport function createApiKeyContext(\n result: ApiKeyValidationResult,\n): ApiKeyContext | null {\n if (!result.valid || !result.userId) {\n return null;\n }\n return {\n userId: result.userId,\n user: result.user || {},\n permissions: result.permissions || [],\n apiKeyId: result.apiKeyId || \"\",\n tenantId: result.tenantId,\n role: result.role,\n };\n}\n\nexport function hasApiKeyPermission(\n permissions: string[],\n required: string,\n): boolean {\n if (permissions.length === 0) return false;\n if (permissions.includes(\"*\")) return true;\n if (permissions.includes(required)) return true;\n\n const [resource, action] = required.split(\":\");\n if (permissions.includes(`${resource}:*`)) return true;\n\n return false;\n}\n\nexport function generateApiKey(): string {\n const chars = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n let suffix = \"\";\n for (let i = 0; i < 28; i++) {\n suffix += chars[Math.floor(Math.random() * chars.length)];\n }\n return `kyro_${suffix}`;\n}\n\nexport function generateApiKeyPrefix(key: string): string {\n return key.substring(0, 8);\n}\n"]}