@kyro-cms/core 0.3.4 → 0.4.0

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 (88) hide show
  1. package/README.md +3 -3
  2. package/dist/api-handler.cjs +6 -6
  3. package/dist/api-handler.js +5 -5
  4. package/dist/{chunk-X3CU27OO.cjs → chunk-3FW6WVVP.cjs} +2 -17
  5. package/dist/chunk-3FW6WVVP.cjs.map +1 -0
  6. package/dist/{chunk-CZ3HWX2X.cjs → chunk-3ZZPZYCM.cjs} +42 -67
  7. package/dist/chunk-3ZZPZYCM.cjs.map +1 -0
  8. package/dist/{chunk-6LPNEC6D.js → chunk-C4JJEE42.js} +43 -68
  9. package/dist/chunk-C4JJEE42.js.map +1 -0
  10. package/dist/{chunk-VEI5KQVC.cjs → chunk-FWGHXRRI.cjs} +45 -15
  11. package/dist/chunk-FWGHXRRI.cjs.map +1 -0
  12. package/dist/{chunk-MMYAIYHJ.cjs → chunk-M4GFA2UQ.cjs} +336 -32
  13. package/dist/chunk-M4GFA2UQ.cjs.map +1 -0
  14. package/dist/{chunk-2SJATAN4.js → chunk-OJBK3JYF.js} +336 -32
  15. package/dist/chunk-OJBK3JYF.js.map +1 -0
  16. package/dist/{chunk-XIXGJGQW.js → chunk-SAMZQVC2.js} +44 -14
  17. package/dist/chunk-SAMZQVC2.js.map +1 -0
  18. package/dist/{chunk-B76I67F3.js → chunk-WSCJQI2B.js} +61 -13
  19. package/dist/chunk-WSCJQI2B.js.map +1 -0
  20. package/dist/{chunk-RGIQKTZ7.js → chunk-YMG55RSX.js} +4 -18
  21. package/dist/chunk-YMG55RSX.js.map +1 -0
  22. package/dist/{chunk-DAIBBBOL.cjs → chunk-Z2OVHWHB.cjs} +61 -13
  23. package/dist/chunk-Z2OVHWHB.cjs.map +1 -0
  24. package/dist/drizzle/index.cjs +10 -10
  25. package/dist/drizzle/index.js +2 -2
  26. package/dist/index.cjs +138 -105
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.js +77 -44
  29. package/dist/index.js.map +1 -1
  30. package/dist/rest/index.cjs +5 -4
  31. package/dist/rest/index.js +3 -2
  32. package/dist/templates/index.cjs +24 -24
  33. package/dist/templates/index.js +1 -1
  34. package/package.json +2 -14
  35. package/dist/WebhookService-BCpW2dyL.d.ts +0 -112
  36. package/dist/WebhookService-DxYSFvNg.d.cts +0 -112
  37. package/dist/api-handler.d.cts +0 -9
  38. package/dist/api-handler.d.ts +0 -9
  39. package/dist/base-DvvNqnM-.d.cts +0 -73
  40. package/dist/base-eVegJ_Pr.d.ts +0 -73
  41. package/dist/chunk-2SJATAN4.js.map +0 -1
  42. package/dist/chunk-6LPNEC6D.js.map +0 -1
  43. package/dist/chunk-B76I67F3.js.map +0 -1
  44. package/dist/chunk-CZ3HWX2X.cjs.map +0 -1
  45. package/dist/chunk-DAIBBBOL.cjs.map +0 -1
  46. package/dist/chunk-MMYAIYHJ.cjs.map +0 -1
  47. package/dist/chunk-RGIQKTZ7.js.map +0 -1
  48. package/dist/chunk-VEI5KQVC.cjs.map +0 -1
  49. package/dist/chunk-X3CU27OO.cjs.map +0 -1
  50. package/dist/chunk-XIXGJGQW.js.map +0 -1
  51. package/dist/cli/index.d.cts +0 -1
  52. package/dist/cli/index.d.ts +0 -1
  53. package/dist/client.d.cts +0 -12
  54. package/dist/client.d.ts +0 -12
  55. package/dist/drizzle/index.d.cts +0 -135
  56. package/dist/drizzle/index.d.ts +0 -135
  57. package/dist/fields/index.d.cts +0 -27
  58. package/dist/fields/index.d.ts +0 -27
  59. package/dist/graphql/index.d.cts +0 -22
  60. package/dist/graphql/index.d.ts +0 -22
  61. package/dist/index-Bz9JqRGI.d.cts +0 -86
  62. package/dist/index-Bz9JqRGI.d.ts +0 -86
  63. package/dist/index-CLp-DRKA.d.ts +0 -64
  64. package/dist/index-DfO7G4kN.d.cts +0 -64
  65. package/dist/index.d.cts +0 -1361
  66. package/dist/index.d.ts +0 -1361
  67. package/dist/integration.d.cts +0 -27
  68. package/dist/integration.d.ts +0 -27
  69. package/dist/mongodb/index.d.cts +0 -63
  70. package/dist/mongodb/index.d.ts +0 -63
  71. package/dist/mysql-media-AI6YK767.cjs +0 -48
  72. package/dist/mysql-media-AI6YK767.cjs.map +0 -1
  73. package/dist/mysql-media-CDZUS7YX.js +0 -45
  74. package/dist/mysql-media-CDZUS7YX.js.map +0 -1
  75. package/dist/rest/index.d.cts +0 -57
  76. package/dist/rest/index.d.ts +0 -57
  77. package/dist/templates/index.d.cts +0 -59
  78. package/dist/templates/index.d.ts +0 -59
  79. package/dist/trpc/index.d.cts +0 -136
  80. package/dist/trpc/index.d.ts +0 -136
  81. package/dist/types-Bs1up4yP.d.ts +0 -461
  82. package/dist/types-Da83JLDk.d.cts +0 -130
  83. package/dist/types-Da83JLDk.d.ts +0 -130
  84. package/dist/types-J3R9nVsZ.d.cts +0 -461
  85. package/dist/types-VtjUxIMp.d.cts +0 -246
  86. package/dist/types-VtjUxIMp.d.ts +0 -246
  87. package/dist/ws/index.d.cts +0 -88
  88. package/dist/ws/index.d.ts +0 -88
@@ -3,8 +3,9 @@
3
3
  var chunkDLHUQO25_cjs = require('./chunk-DLHUQO25.cjs');
4
4
  var chunkADLJSJSN_cjs = require('./chunk-ADLJSJSN.cjs');
5
5
  var chunkIBG6V56E_cjs = require('./chunk-IBG6V56E.cjs');
6
+ var chunkVJT6P4N6_cjs = require('./chunk-VJT6P4N6.cjs');
6
7
  var chunkR3XIBBAW_cjs = require('./chunk-R3XIBBAW.cjs');
7
- var chunkX3CU27OO_cjs = require('./chunk-X3CU27OO.cjs');
8
+ var chunk3FW6WVVP_cjs = require('./chunk-3FW6WVVP.cjs');
8
9
  var chunkG7VZBCD6_cjs = require('./chunk-G7VZBCD6.cjs');
9
10
  var crypto = require('crypto');
10
11
  var unstorage = require('unstorage');
@@ -13,6 +14,7 @@ var hono = require('hono');
13
14
  var path = require('path');
14
15
  var fs = require('fs');
15
16
  var sharp = require('sharp');
17
+ var graphql = require('graphql');
16
18
  var promises = require('fs/promises');
17
19
  var clientS3 = require('@aws-sdk/client-s3');
18
20
  var stream = require('stream');
@@ -1360,7 +1362,7 @@ var AuthRoutes = class {
1360
1362
  }
1361
1363
  try {
1362
1364
  const sessions = await getUserSessions(session.userId, getSessionIdFromRequest(req) ?? void 0);
1363
- return this.jsonResponse({ success: true, sessions });
1365
+ return this.jsonResponse({ sessions });
1364
1366
  } catch (error) {
1365
1367
  console.error("[AuthRoutes.listSessions] Error:", error);
1366
1368
  return this.errorResponse("Failed to list sessions", 500);
@@ -2677,7 +2679,7 @@ var MediaService = class _MediaService {
2677
2679
  this.db = db;
2678
2680
  this.storage = storage2;
2679
2681
  this.dialect = options?.dialect || "sqlite";
2680
- this.genId = options?.genId || chunkX3CU27OO_cjs.genId;
2682
+ this.genId = options?.genId || chunk3FW6WVVP_cjs.genId;
2681
2683
  }
2682
2684
  static async init(db, options) {
2683
2685
  let storage2;
@@ -2851,7 +2853,7 @@ var MediaService = class _MediaService {
2851
2853
  ]
2852
2854
  );
2853
2855
  } else {
2854
- const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
2856
+ const { media: mediaSchema } = await import('./media-WKP5AOX2.cjs');
2855
2857
  await this.db.insert(mediaSchema).values({
2856
2858
  id,
2857
2859
  filename: storageResult.filename,
@@ -2907,7 +2909,7 @@ var MediaService = class _MediaService {
2907
2909
  id
2908
2910
  ]);
2909
2911
  } else {
2910
- const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
2912
+ const { media: mediaSchema } = await import('./media-WKP5AOX2.cjs');
2911
2913
  const [row] = await this.db.select().from(mediaSchema).where(mediaSchema.id.equals(id));
2912
2914
  if (row) item = this.rowToMedia(row);
2913
2915
  }
@@ -2922,7 +2924,7 @@ var MediaService = class _MediaService {
2922
2924
  if (this.dialect === "sqlite") {
2923
2925
  await this.sqliteRun(`DELETE FROM ${this.mediaTable} WHERE id = ?`, [id]);
2924
2926
  } else {
2925
- const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
2927
+ const { media: mediaSchema } = await import('./media-WKP5AOX2.cjs');
2926
2928
  await this.db.delete(mediaSchema).where(mediaSchema.id.equals(id));
2927
2929
  }
2928
2930
  }
@@ -2936,7 +2938,7 @@ var MediaService = class _MediaService {
2936
2938
  id
2937
2939
  ]);
2938
2940
  } else {
2939
- const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
2941
+ const { media: mediaSchema } = await import('./media-WKP5AOX2.cjs');
2940
2942
  const [row] = await this.db.select().from(mediaSchema).where(mediaSchema.id.equals(id));
2941
2943
  if (row) item = this.rowToMedia(row);
2942
2944
  }
@@ -2970,7 +2972,7 @@ var MediaService = class _MediaService {
2970
2972
  [...vals, this.now(), id]
2971
2973
  );
2972
2974
  } else {
2973
- const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
2975
+ const { media: mediaSchema } = await import('./media-WKP5AOX2.cjs');
2974
2976
  await this.db.update(mediaSchema).set({ ...updateData, updatedAt: this.now() }).where(mediaSchema.id.equals(id));
2975
2977
  }
2976
2978
  return {
@@ -2988,7 +2990,7 @@ var MediaService = class _MediaService {
2988
2990
  );
2989
2991
  return row2 ? this.rowToMedia(row2) : null;
2990
2992
  }
2991
- const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
2993
+ const { media: mediaSchema } = await import('./media-WKP5AOX2.cjs');
2992
2994
  const [row] = await this.db.select().from(mediaSchema).where(mediaSchema.id.equals(id));
2993
2995
  return row ? this.rowToMedia(row) : null;
2994
2996
  }
@@ -3035,8 +3037,8 @@ var MediaService = class _MediaService {
3035
3037
  totalPages: Math.ceil(totalDocs2 / limit)
3036
3038
  };
3037
3039
  }
3038
- const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
3039
- const { like, or, and, asc, desc, eq, sql } = await (this.dialect === "mysql" ? import('drizzle-orm/mysql-core') : import('drizzle-orm/pg-core'));
3040
+ const { media: mediaSchema } = await import('./media-WKP5AOX2.cjs');
3041
+ const { like, or, and, asc, desc, eq, sql } = await import('drizzle-orm/pg-core');
3040
3042
  const conditions = [];
3041
3043
  if (search) {
3042
3044
  conditions.push(
@@ -3104,7 +3106,7 @@ var MediaService = class _MediaService {
3104
3106
  );
3105
3107
  return row ? this.rowToMedia(row) : null;
3106
3108
  }
3107
- const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
3109
+ const { media: mediaSchema } = await import('./media-WKP5AOX2.cjs');
3108
3110
  const [updated] = await this.db.update(mediaSchema).set({ ...data, updatedAt: /* @__PURE__ */ new Date() }).where(mediaSchema.id.equals(id)).returning();
3109
3111
  return updated ? this.rowToMedia(updated) : null;
3110
3112
  }
@@ -3125,7 +3127,7 @@ var MediaService = class _MediaService {
3125
3127
  );
3126
3128
  }
3127
3129
  } else {
3128
- const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
3130
+ const { media: mediaSchema } = await import('./media-WKP5AOX2.cjs');
3129
3131
  for (const id of ids) {
3130
3132
  await this.db.update(mediaSchema).set({ ...data, updatedAt: /* @__PURE__ */ new Date() }).where(mediaSchema.id.equals(id));
3131
3133
  }
@@ -3139,8 +3141,8 @@ var MediaService = class _MediaService {
3139
3141
  );
3140
3142
  return rows.map((r) => r.path).filter((f) => f && f !== "").sort();
3141
3143
  }
3142
- const { media: mediaSchema, mediaFolders: folderSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
3143
- const { eq, sql } = await (this.dialect === "mysql" ? import('drizzle-orm/mysql-core') : import('drizzle-orm/pg-core'));
3144
+ const { media: mediaSchema, mediaFolders: folderSchema } = await import('./media-WKP5AOX2.cjs');
3145
+ const { eq, sql } = await import('drizzle-orm/pg-core');
3144
3146
  const fromMedia = await this.db.select({ folder: mediaSchema.folder }).from(mediaSchema).groupBy(mediaSchema.folder);
3145
3147
  const fromFolders = await this.db.select({ path: folderSchema.path }).from(folderSchema);
3146
3148
  const allPaths = /* @__PURE__ */ new Set([
@@ -3159,7 +3161,7 @@ var MediaService = class _MediaService {
3159
3161
  [fullPath, name, parentPath || null, now]
3160
3162
  );
3161
3163
  } else {
3162
- const { mediaFolders: folderSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
3164
+ const { mediaFolders: folderSchema } = await import('./media-WKP5AOX2.cjs');
3163
3165
  await this.db.insert(folderSchema).values({
3164
3166
  path: fullPath,
3165
3167
  name,
@@ -3180,8 +3182,8 @@ var MediaService = class _MediaService {
3180
3182
  [folder, `${folder}/%`]
3181
3183
  );
3182
3184
  } else {
3183
- const { mediaFolders: folderSchema } = await (this.dialect === "mysql" ? import('./mysql-media-AI6YK767.cjs') : import('./media-WKP5AOX2.cjs'));
3184
- const { like, or, eq } = await (this.dialect === "mysql" ? import('drizzle-orm/mysql-core') : import('drizzle-orm/pg-core'));
3185
+ const { mediaFolders: folderSchema } = await import('./media-WKP5AOX2.cjs');
3186
+ const { like, or, eq } = await import('./media-WKP5AOX2.cjs');
3185
3187
  await this.db.delete(folderSchema).where(
3186
3188
  or(
3187
3189
  eq(folderSchema.path, folder),
@@ -3382,6 +3384,27 @@ function getWebhookEvent(collection, operation) {
3382
3384
  if (mapped) return mapped[operation];
3383
3385
  return `collection.${operation}`;
3384
3386
  }
3387
+ function extractIp(req) {
3388
+ const forwarded = req.headers.get("x-forwarded-for");
3389
+ if (forwarded) return forwarded.split(",")[0].trim();
3390
+ return req.headers.get("x-real-ip") || "unknown";
3391
+ }
3392
+ function auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, endpoint, method, req) {
3393
+ if (apiKeyContext?.apiKeyId && sessionAuthAdapter) {
3394
+ sessionAuthAdapter.createAuditLog({
3395
+ action: "api_request",
3396
+ userId: apiKeyContext.userId || "",
3397
+ resource: "api_key",
3398
+ resourceId: apiKeyContext.apiKeyId,
3399
+ success: true,
3400
+ metadata: {
3401
+ endpoint,
3402
+ method,
3403
+ ip: extractIp(req)
3404
+ }
3405
+ });
3406
+ }
3407
+ }
3385
3408
  function readBaseUpdatedAt(body) {
3386
3409
  return body.baseUpdatedAt ?? body._baseUpdatedAt;
3387
3410
  }
@@ -3524,7 +3547,8 @@ async function resolveAuthContext(req, authMw, staticUser, staticTenantID) {
3524
3547
  return {
3525
3548
  user: result.user || staticUser,
3526
3549
  tenantID: result.tenantContext?.tenantId || staticTenantID,
3527
- apiKeyContext: result.apiKeyContext
3550
+ apiKeyContext: result.apiKeyContext,
3551
+ authType: result.authType
3528
3552
  };
3529
3553
  }
3530
3554
  function createHonoApp(options) {
@@ -3639,6 +3663,69 @@ function createHonoApp(options) {
3639
3663
  app.delete("/api/auth/sessions", async (c) => authRoutes.revokeOtherSessions(c.req.raw));
3640
3664
  app.delete("/api/auth/sessions/:id", async (c) => authRoutes.revokeSession(c.req.raw, c.req.param("id")));
3641
3665
  app.put("/api/auth/sessions/:id/name", async (c) => authRoutes.renameSession(c.req.raw, c.req.param("id")));
3666
+ app.post("/api/graphql", async (c) => {
3667
+ try {
3668
+ const req = c.req.raw;
3669
+ const apiKeyRaw = chunkIBG6V56E_cjs.extractApiKeyFromRequest(req);
3670
+ if (apiKeyRaw && db) {
3671
+ const apiKeyResult = await chunkIBG6V56E_cjs.validateApiKey(apiKeyRaw, db);
3672
+ if (!apiKeyResult.valid) {
3673
+ return c.json({ errors: [{ message: apiKeyResult.error || "Invalid API key" }] }, 401);
3674
+ }
3675
+ const apiKeyId = apiKeyResult.apiKeyId || "";
3676
+ await sessionAuthAdapter?.createAuditLog({
3677
+ action: "api_key_request",
3678
+ userId: apiKeyResult.userId || "",
3679
+ resource: "api_key",
3680
+ resourceId: apiKeyId,
3681
+ success: true,
3682
+ metadata: {
3683
+ endpoint: "/api/graphql",
3684
+ method: "POST",
3685
+ ip: extractIp(req)
3686
+ }
3687
+ });
3688
+ }
3689
+ const body = await req.json().catch(() => ({}));
3690
+ const { query, variables } = body;
3691
+ if (!query) {
3692
+ return c.json({ errors: [{ message: "No query provided" }] }, 400);
3693
+ }
3694
+ let gqlUser;
3695
+ let apiKeyCtx;
3696
+ if (apiKeyRaw && db) {
3697
+ const apiKeyResult = await chunkIBG6V56E_cjs.validateApiKey(apiKeyRaw, db);
3698
+ if (apiKeyResult.valid && apiKeyResult.user) {
3699
+ gqlUser = apiKeyResult.user;
3700
+ apiKeyCtx = chunkIBG6V56E_cjs.createApiKeyContext(apiKeyResult);
3701
+ }
3702
+ }
3703
+ const schema = chunkVJT6P4N6_cjs.buildGraphQLSchema({
3704
+ registry,
3705
+ db,
3706
+ user: gqlUser,
3707
+ req,
3708
+ settings
3709
+ });
3710
+ const document = graphql.parse(query);
3711
+ const result = await graphql.execute({
3712
+ schema,
3713
+ document,
3714
+ variableValues: variables,
3715
+ contextValue: { user: gqlUser, apiKeyContext: apiKeyCtx, req, db }
3716
+ });
3717
+ return c.json(result);
3718
+ } catch (error) {
3719
+ if (error.message?.includes("GraphQL is disabled")) {
3720
+ return c.json({ errors: [{ message: "GraphQL API is disabled" }] }, 403);
3721
+ }
3722
+ if (error instanceof SyntaxError) {
3723
+ return c.json({ errors: [{ message: "Invalid request body" }] }, 400);
3724
+ }
3725
+ console.error("[GraphQL] execution error:", error);
3726
+ return c.json({ errors: [{ message: error.message || "GraphQL execution failed" }] }, 500);
3727
+ }
3728
+ });
3642
3729
  app.get("/api/auth/access", async (c) => {
3643
3730
  try {
3644
3731
  const { user: ctxUser, tenantID: ctxTenantID } = await resolveAuthContext(
@@ -4353,6 +4440,7 @@ function createHonoApp(options) {
4353
4440
  key: rawKey,
4354
4441
  keyPrefix: chunkIBG6V56E_cjs.generateApiKeyPrefix(rawKey),
4355
4442
  permissions: Array.isArray(body.permissions) ? body.permissions : ["*"],
4443
+ expiresAt: body.expiresAt || null,
4356
4444
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
4357
4445
  }
4358
4446
  });
@@ -4398,6 +4486,201 @@ function createHonoApp(options) {
4398
4486
  return c.json({ error: error.message }, 500);
4399
4487
  }
4400
4488
  });
4489
+ app.patch("/api/keys/:id", async (c) => {
4490
+ try {
4491
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4492
+ if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
4493
+ return c.json({ error: "Forbidden" }, 403);
4494
+ }
4495
+ const id = c.req.param("id");
4496
+ const body = await c.req.json();
4497
+ const existing = await db.findByID({ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION, id });
4498
+ if (!existing) return c.json({ error: "API key not found" }, 404);
4499
+ const updateData = {};
4500
+ if (typeof body.name === "string" && body.name.trim()) updateData.name = body.name.trim();
4501
+ if (Array.isArray(body.permissions)) updateData.permissions = body.permissions;
4502
+ if (body.expiresAt !== void 0) updateData.expiresAt = body.expiresAt || null;
4503
+ if (Object.keys(updateData).length === 0) return c.json({ error: "Nothing to update" }, 400);
4504
+ const updated = await db.update({ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION, id, data: updateData });
4505
+ return c.json({ ...updated, keyPrefix: existing.keyPrefix });
4506
+ } catch (error) {
4507
+ console.error("[ApiKeys] PATCH error:", error);
4508
+ return c.json({ error: error.message }, 500);
4509
+ }
4510
+ });
4511
+ app.post("/api/keys/:id/rotate", async (c) => {
4512
+ try {
4513
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4514
+ if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
4515
+ return c.json({ error: "Forbidden" }, 403);
4516
+ }
4517
+ const id = c.req.param("id");
4518
+ const existing = await db.findByID({ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION, id });
4519
+ if (!existing) return c.json({ error: "API key not found" }, 404);
4520
+ const rawKey = chunkIBG6V56E_cjs.generateApiKey();
4521
+ const updated = await db.update({
4522
+ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION,
4523
+ id,
4524
+ data: {
4525
+ key: rawKey,
4526
+ keyPrefix: chunkIBG6V56E_cjs.generateApiKeyPrefix(rawKey),
4527
+ lastUsedAt: null
4528
+ }
4529
+ });
4530
+ await sessionAuthAdapter?.createAuditLog({
4531
+ action: "api_key_rotate",
4532
+ userId: ctxUser.id,
4533
+ resource: "api_key",
4534
+ resourceId: id,
4535
+ success: true,
4536
+ metadata: { keyName: existing.name }
4537
+ });
4538
+ return c.json({
4539
+ ...updated,
4540
+ key: rawKey,
4541
+ permissions: existing.permissions,
4542
+ expiresAt: existing.expiresAt
4543
+ });
4544
+ } catch (error) {
4545
+ console.error("[ApiKeys] rotate error:", error);
4546
+ return c.json({ error: error.message }, 500);
4547
+ }
4548
+ });
4549
+ app.get("/api/webhooks", async (c) => {
4550
+ try {
4551
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4552
+ if (!ctxUser || !hasPermission(ctxUser, "users:read")) {
4553
+ return c.json({ error: "Forbidden" }, 403);
4554
+ }
4555
+ if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
4556
+ const webhooks = await webhookService.getWebhooks();
4557
+ return c.json({ docs: webhooks });
4558
+ } catch (error) {
4559
+ console.error("[Webhooks] GET error:", error);
4560
+ return c.json({ error: error.message }, 500);
4561
+ }
4562
+ });
4563
+ app.post("/api/webhooks", async (c) => {
4564
+ try {
4565
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4566
+ if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
4567
+ return c.json({ error: "Forbidden" }, 403);
4568
+ }
4569
+ if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
4570
+ const body = await c.req.json();
4571
+ const webhook = await webhookService.createWebhook(body);
4572
+ await sessionAuthAdapter?.createAuditLog({
4573
+ action: "webhook_create",
4574
+ userId: ctxUser.id,
4575
+ resource: "webhook",
4576
+ resourceId: webhook.id,
4577
+ success: true,
4578
+ metadata: { name: webhook.name, url: webhook.url }
4579
+ });
4580
+ return c.json(webhook, 201);
4581
+ } catch (error) {
4582
+ console.error("[Webhooks] POST error:", error);
4583
+ return c.json({ error: error.message }, 500);
4584
+ }
4585
+ });
4586
+ app.get("/api/webhooks/:id", async (c) => {
4587
+ try {
4588
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4589
+ if (!ctxUser || !hasPermission(ctxUser, "users:read")) {
4590
+ return c.json({ error: "Forbidden" }, 403);
4591
+ }
4592
+ if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
4593
+ const id = c.req.param("id");
4594
+ const webhook = await webhookService.getWebhookById(id);
4595
+ if (!webhook) return c.json({ error: "Webhook not found" }, 404);
4596
+ return c.json(webhook);
4597
+ } catch (error) {
4598
+ console.error("[Webhooks] GET :id error:", error);
4599
+ return c.json({ error: error.message }, 500);
4600
+ }
4601
+ });
4602
+ app.patch("/api/webhooks/:id", async (c) => {
4603
+ try {
4604
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4605
+ if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
4606
+ return c.json({ error: "Forbidden" }, 403);
4607
+ }
4608
+ if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
4609
+ const id = c.req.param("id");
4610
+ const body = await c.req.json();
4611
+ const updated = await webhookService.updateWebhook(id, body);
4612
+ if (!updated) return c.json({ error: "Webhook not found" }, 404);
4613
+ await sessionAuthAdapter?.createAuditLog({
4614
+ action: "webhook_update",
4615
+ userId: ctxUser.id,
4616
+ resource: "webhook",
4617
+ resourceId: id,
4618
+ success: true,
4619
+ metadata: { name: updated.name }
4620
+ });
4621
+ return c.json(updated);
4622
+ } catch (error) {
4623
+ console.error("[Webhooks] PATCH error:", error);
4624
+ return c.json({ error: error.message }, 500);
4625
+ }
4626
+ });
4627
+ app.delete("/api/webhooks/:id", async (c) => {
4628
+ try {
4629
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4630
+ if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
4631
+ return c.json({ error: "Forbidden" }, 403);
4632
+ }
4633
+ if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
4634
+ const id = c.req.param("id");
4635
+ const existing = await webhookService.getWebhookById(id);
4636
+ if (!existing) return c.json({ error: "Webhook not found" }, 404);
4637
+ await webhookService.deleteWebhook(id);
4638
+ await sessionAuthAdapter?.createAuditLog({
4639
+ action: "webhook_delete",
4640
+ userId: ctxUser.id,
4641
+ resource: "webhook",
4642
+ resourceId: id,
4643
+ success: true,
4644
+ metadata: { name: existing.name }
4645
+ });
4646
+ return c.json({ message: "Webhook deleted" });
4647
+ } catch (error) {
4648
+ console.error("[Webhooks] DELETE error:", error);
4649
+ return c.json({ error: error.message }, 500);
4650
+ }
4651
+ });
4652
+ app.post("/api/webhooks/:id/test", async (c) => {
4653
+ try {
4654
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4655
+ if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
4656
+ return c.json({ error: "Forbidden" }, 403);
4657
+ }
4658
+ if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
4659
+ const id = c.req.param("id");
4660
+ const result = await webhookService.testWebhook(id);
4661
+ if (!result) return c.json({ error: "Webhook not found" }, 404);
4662
+ return c.json(result);
4663
+ } catch (error) {
4664
+ console.error("[Webhooks] test error:", error);
4665
+ return c.json({ error: error.message }, 500);
4666
+ }
4667
+ });
4668
+ app.get("/api/webhooks/:id/history", async (c) => {
4669
+ try {
4670
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4671
+ if (!ctxUser || !hasPermission(ctxUser, "users:read")) {
4672
+ return c.json({ error: "Forbidden" }, 403);
4673
+ }
4674
+ if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
4675
+ const id = c.req.param("id");
4676
+ const limit = Math.min(parseInt(c.req.query("limit") || "50"), 100);
4677
+ const history = await webhookService.getDeliveryHistory(id, limit);
4678
+ return c.json({ docs: history });
4679
+ } catch (error) {
4680
+ console.error("[Webhooks] history error:", error);
4681
+ return c.json({ error: error.message }, 500);
4682
+ }
4683
+ });
4401
4684
  const collections = registry.getCollections();
4402
4685
  for (const collection of collections) {
4403
4686
  let computeDiff2 = function(a, b) {
@@ -4434,6 +4717,7 @@ function createHonoApp(options) {
4434
4717
  if (!access.allowed) {
4435
4718
  return c.json({ error: access.error }, access.status || 403);
4436
4719
  }
4720
+ auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, basePath, "GET", c.req.raw);
4437
4721
  const url = new URL(c.req.url);
4438
4722
  const page = parseInt(url.searchParams.get("page") || "1");
4439
4723
  const limit = Math.min(
@@ -4491,6 +4775,7 @@ function createHonoApp(options) {
4491
4775
  const url = new URL(c.req.url);
4492
4776
  const compareA = url.searchParams.get("compareA");
4493
4777
  const compareB = url.searchParams.get("compareB");
4778
+ auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, `${basePath}/${id}/versions`, "GET", c.req.raw);
4494
4779
  if (compareA && compareB) {
4495
4780
  const [versionA, versionB] = await Promise.all([
4496
4781
  db.findVersionByID({ collection: slug, versionId: compareA, tenantID: ctxTenantID }),
@@ -4503,7 +4788,7 @@ function createHonoApp(options) {
4503
4788
  return c.json({ diffs });
4504
4789
  }
4505
4790
  const page = parseInt(url.searchParams.get("page") || "1");
4506
- const limit = parseInt(url.searchParams.get("limit") || "10");
4791
+ const limit = Math.min(parseInt(url.searchParams.get("limit") || "10"), 100);
4507
4792
  const result = await db.findVersions({
4508
4793
  collection: slug,
4509
4794
  documentId: id,
@@ -4690,6 +4975,7 @@ function createHonoApp(options) {
4690
4975
  if (!access.allowed) {
4691
4976
  return c.json({ error: access.error }, access.status || 403);
4692
4977
  }
4978
+ auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, basePath, "POST", c.req.raw);
4693
4979
  const body = await c.req.json();
4694
4980
  const schema = registry.getCreateZodSchema(slug);
4695
4981
  let validated;
@@ -4749,13 +5035,19 @@ function createHonoApp(options) {
4749
5035
  const id = c.req.param("id");
4750
5036
  const body = await c.req.json();
4751
5037
  const baseUpdatedAt = readBaseUpdatedAt(body);
5038
+ console.log(`[PATCH] ${slug}/${id}`, {
5039
+ baseUpdatedAt,
5040
+ bodyKeys: Object.keys(body),
5041
+ tenantID: ctxTenantID
5042
+ });
4752
5043
  const cleaned = Object.fromEntries(
4753
5044
  Object.entries(omitRevisionFields(body)).filter(
4754
- ([_, v]) => v !== null && v !== "null" && v !== void 0
5045
+ ([_, v]) => v !== "null" && v !== void 0
4755
5046
  )
4756
5047
  );
4757
5048
  const schema = registry.getUpdateZodSchema(slug);
4758
5049
  const validated = schema.parse(cleaned);
5050
+ console.log(`[PATCH] Validated data:`, Object.keys(validated));
4759
5051
  const originalDoc = await db.findByID({
4760
5052
  collection: slug,
4761
5053
  id,
@@ -4763,6 +5055,9 @@ function createHonoApp(options) {
4763
5055
  draft: true
4764
5056
  // Always fetch current doc regardless of status
4765
5057
  });
5058
+ if (originalDoc) {
5059
+ console.log(`[PATCH] Original doc updatedAt:`, originalDoc.updatedAt);
5060
+ }
4766
5061
  if (!originalDoc) {
4767
5062
  return c.json({ error: "Document not found" }, 404);
4768
5063
  }
@@ -4782,7 +5077,7 @@ function createHonoApp(options) {
4782
5077
  changeDescription: "Manual save (Draft)",
4783
5078
  tenantID: ctxTenantID
4784
5079
  });
4785
- doc = await db.update({
5080
+ await db.update({
4786
5081
  collection: slug,
4787
5082
  id,
4788
5083
  data: { _has_draft: true },
@@ -4791,13 +5086,21 @@ function createHonoApp(options) {
4791
5086
  });
4792
5087
  } else {
4793
5088
  const saveData = isDraftEnabled ? { ...validated, _status: "draft", _has_draft: false } : validated;
4794
- doc = await db.update({
5089
+ await db.update({
4795
5090
  collection: slug,
4796
5091
  id,
4797
5092
  data: saveData,
4798
5093
  tenantID: ctxTenantID
4799
5094
  });
4800
- if (isDraftEnabled) {
5095
+ }
5096
+ doc = await db.findByID({
5097
+ collection: slug,
5098
+ id,
5099
+ tenantID: ctxTenantID,
5100
+ draft: true
5101
+ });
5102
+ if (isDraftEnabled) {
5103
+ if (!isAlreadyPublished) {
4801
5104
  await db.createVersion({
4802
5105
  collection: slug,
4803
5106
  documentId: id,
@@ -4807,13 +5110,13 @@ function createHonoApp(options) {
4807
5110
  changeDescription: "Manual save",
4808
5111
  tenantID: ctxTenantID
4809
5112
  });
4810
- } else {
4811
- await db.deleteDraft({
4812
- collection: slug,
4813
- documentId: id,
4814
- tenantID: ctxTenantID
4815
- });
4816
5113
  }
5114
+ } else {
5115
+ await db.deleteDraft({
5116
+ collection: slug,
5117
+ documentId: id,
5118
+ tenantID: ctxTenantID
5119
+ });
4817
5120
  }
4818
5121
  if (webhookService) {
4819
5122
  webhookService.trigger(getWebhookEvent(slug, "update"), {
@@ -4825,6 +5128,7 @@ function createHonoApp(options) {
4825
5128
  tenantId: ctxTenantID
4826
5129
  }).catch((err) => console.error(`[Webhook] Failed to trigger:`, err));
4827
5130
  }
5131
+ console.log(`[PATCH] Result doc updatedAt:`, doc?.updatedAt);
4828
5132
  return c.json({ data: doc, message: isDraftEnabled ? "Draft saved" : "Updated successfully" });
4829
5133
  } catch (error) {
4830
5134
  if (error.name === "ZodError") {
@@ -5534,5 +5838,5 @@ exports.init_secret = init_secret;
5534
5838
  exports.loadSecrets = loadSecrets;
5535
5839
  exports.resolveProvider = resolveProvider;
5536
5840
  exports.setDbAdapter = setDbAdapter;
5537
- //# sourceMappingURL=chunk-MMYAIYHJ.cjs.map
5538
- //# sourceMappingURL=chunk-MMYAIYHJ.cjs.map
5841
+ //# sourceMappingURL=chunk-M4GFA2UQ.cjs.map
5842
+ //# sourceMappingURL=chunk-M4GFA2UQ.cjs.map