@kyro-cms/core 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{WebhookService-BCpW2dyL.d.ts → WebhookService-118ZTFis.d.ts} +1 -1
- package/dist/{WebhookService-DxYSFvNg.d.cts → WebhookService-AefJfqX0.d.cts} +1 -1
- package/dist/api-handler.cjs +5 -5
- package/dist/api-handler.js +4 -4
- package/dist/{chunk-VEI5KQVC.cjs → chunk-2UOI5MUC.cjs} +45 -15
- package/dist/chunk-2UOI5MUC.cjs.map +1 -0
- package/dist/{chunk-2SJATAN4.js → chunk-4UD44U4Z.js} +320 -16
- package/dist/chunk-4UD44U4Z.js.map +1 -0
- package/dist/{chunk-XIXGJGQW.js → chunk-5FTY2DLG.js} +44 -14
- package/dist/chunk-5FTY2DLG.js.map +1 -0
- package/dist/{chunk-MMYAIYHJ.cjs → chunk-DE7OQOMD.cjs} +319 -15
- package/dist/chunk-DE7OQOMD.cjs.map +1 -0
- package/dist/{chunk-CZ3HWX2X.cjs → chunk-R4C4O4SE.cjs} +8 -8
- package/dist/chunk-R4C4O4SE.cjs.map +1 -0
- package/dist/{chunk-B76I67F3.js → chunk-WSCJQI2B.js} +61 -13
- package/dist/chunk-WSCJQI2B.js.map +1 -0
- package/dist/{chunk-6LPNEC6D.js → chunk-Y3TM7WH7.js} +9 -9
- package/dist/chunk-Y3TM7WH7.js.map +1 -0
- package/dist/{chunk-DAIBBBOL.cjs → chunk-Z2OVHWHB.cjs} +61 -13
- package/dist/chunk-Z2OVHWHB.cjs.map +1 -0
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/drizzle/index.cjs +5 -5
- package/dist/drizzle/index.d.cts +1 -1
- package/dist/drizzle/index.d.ts +1 -1
- package/dist/drizzle/index.js +1 -1
- package/dist/index.cjs +133 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +75 -35
- package/dist/index.js.map +1 -1
- package/dist/rest/index.cjs +4 -3
- package/dist/rest/index.d.cts +1 -1
- package/dist/rest/index.d.ts +1 -1
- package/dist/rest/index.js +2 -1
- package/dist/templates/index.cjs +24 -24
- package/dist/templates/index.js +1 -1
- package/dist/trpc/index.d.cts +1 -1
- package/dist/trpc/index.d.ts +1 -1
- package/dist/{types-Da83JLDk.d.cts → types-BnTm7oJG.d.cts} +1 -1
- package/dist/{types-Da83JLDk.d.ts → types-BnTm7oJG.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-2SJATAN4.js.map +0 -1
- package/dist/chunk-6LPNEC6D.js.map +0 -1
- package/dist/chunk-B76I67F3.js.map +0 -1
- package/dist/chunk-CZ3HWX2X.cjs.map +0 -1
- package/dist/chunk-DAIBBBOL.cjs.map +0 -1
- package/dist/chunk-MMYAIYHJ.cjs.map +0 -1
- package/dist/chunk-VEI5KQVC.cjs.map +0 -1
- package/dist/chunk-XIXGJGQW.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PasswordPolicy, ConfigService, SQLiteAuthAdapter, EmailTransport } from './chunk-RYDGMBIG.js';
|
|
2
2
|
import { createAuditContext } from './chunk-P2YW545G.js';
|
|
3
|
-
import { WEBHOOK_EVENTS,
|
|
3
|
+
import { WEBHOOK_EVENTS, extractApiKeyFromRequest, validateApiKey, createApiKeyContext, API_KEY_COLLECTION, generateApiKey, generateApiKeyPrefix, hasApiKeyPermission } from './chunk-QXIQWPAP.js';
|
|
4
|
+
import { buildGraphQLSchema } from './chunk-REK7AYOC.js';
|
|
4
5
|
import { evaluateAccess } from './chunk-SDMNUYVU.js';
|
|
5
6
|
import { genId } from './chunk-RGIQKTZ7.js';
|
|
6
7
|
import { __esm, __export, __toCommonJS, __require } from './chunk-Z6ZWNWWR.js';
|
|
@@ -11,6 +12,7 @@ import { Hono } from 'hono';
|
|
|
11
12
|
import path, { join, basename, extname, resolve } from 'path';
|
|
12
13
|
import { existsSync, readFileSync } from 'fs';
|
|
13
14
|
import sharp from 'sharp';
|
|
15
|
+
import { parse, execute } from 'graphql';
|
|
14
16
|
import { mkdir, readdir, stat, rename, unlink, writeFile } from 'fs/promises';
|
|
15
17
|
import { S3Client, ListObjectsV2Command, DeleteObjectsCommand, DeleteObjectCommand, PutObjectCommand, HeadObjectCommand, CopyObjectCommand } from '@aws-sdk/client-s3';
|
|
16
18
|
import { Readable } from 'stream';
|
|
@@ -1351,7 +1353,7 @@ var AuthRoutes = class {
|
|
|
1351
1353
|
}
|
|
1352
1354
|
try {
|
|
1353
1355
|
const sessions = await getUserSessions(session.userId, getSessionIdFromRequest(req) ?? void 0);
|
|
1354
|
-
return this.jsonResponse({
|
|
1356
|
+
return this.jsonResponse({ sessions });
|
|
1355
1357
|
} catch (error) {
|
|
1356
1358
|
console.error("[AuthRoutes.listSessions] Error:", error);
|
|
1357
1359
|
return this.errorResponse("Failed to list sessions", 500);
|
|
@@ -3373,6 +3375,27 @@ function getWebhookEvent(collection, operation) {
|
|
|
3373
3375
|
if (mapped) return mapped[operation];
|
|
3374
3376
|
return `collection.${operation}`;
|
|
3375
3377
|
}
|
|
3378
|
+
function extractIp(req) {
|
|
3379
|
+
const forwarded = req.headers.get("x-forwarded-for");
|
|
3380
|
+
if (forwarded) return forwarded.split(",")[0].trim();
|
|
3381
|
+
return req.headers.get("x-real-ip") || "unknown";
|
|
3382
|
+
}
|
|
3383
|
+
function auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, endpoint, method, req) {
|
|
3384
|
+
if (apiKeyContext?.apiKeyId && sessionAuthAdapter) {
|
|
3385
|
+
sessionAuthAdapter.createAuditLog({
|
|
3386
|
+
action: "api_request",
|
|
3387
|
+
userId: apiKeyContext.userId || "",
|
|
3388
|
+
resource: "api_key",
|
|
3389
|
+
resourceId: apiKeyContext.apiKeyId,
|
|
3390
|
+
success: true,
|
|
3391
|
+
metadata: {
|
|
3392
|
+
endpoint,
|
|
3393
|
+
method,
|
|
3394
|
+
ip: extractIp(req)
|
|
3395
|
+
}
|
|
3396
|
+
});
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3376
3399
|
function readBaseUpdatedAt(body) {
|
|
3377
3400
|
return body.baseUpdatedAt ?? body._baseUpdatedAt;
|
|
3378
3401
|
}
|
|
@@ -3515,7 +3538,8 @@ async function resolveAuthContext(req, authMw, staticUser, staticTenantID) {
|
|
|
3515
3538
|
return {
|
|
3516
3539
|
user: result.user || staticUser,
|
|
3517
3540
|
tenantID: result.tenantContext?.tenantId || staticTenantID,
|
|
3518
|
-
apiKeyContext: result.apiKeyContext
|
|
3541
|
+
apiKeyContext: result.apiKeyContext,
|
|
3542
|
+
authType: result.authType
|
|
3519
3543
|
};
|
|
3520
3544
|
}
|
|
3521
3545
|
function createHonoApp(options) {
|
|
@@ -3630,6 +3654,69 @@ function createHonoApp(options) {
|
|
|
3630
3654
|
app.delete("/api/auth/sessions", async (c) => authRoutes.revokeOtherSessions(c.req.raw));
|
|
3631
3655
|
app.delete("/api/auth/sessions/:id", async (c) => authRoutes.revokeSession(c.req.raw, c.req.param("id")));
|
|
3632
3656
|
app.put("/api/auth/sessions/:id/name", async (c) => authRoutes.renameSession(c.req.raw, c.req.param("id")));
|
|
3657
|
+
app.post("/api/graphql", async (c) => {
|
|
3658
|
+
try {
|
|
3659
|
+
const req = c.req.raw;
|
|
3660
|
+
const apiKeyRaw = extractApiKeyFromRequest(req);
|
|
3661
|
+
if (apiKeyRaw && db) {
|
|
3662
|
+
const apiKeyResult = await validateApiKey(apiKeyRaw, db);
|
|
3663
|
+
if (!apiKeyResult.valid) {
|
|
3664
|
+
return c.json({ errors: [{ message: apiKeyResult.error || "Invalid API key" }] }, 401);
|
|
3665
|
+
}
|
|
3666
|
+
const apiKeyId = apiKeyResult.apiKeyId || "";
|
|
3667
|
+
await sessionAuthAdapter?.createAuditLog({
|
|
3668
|
+
action: "api_key_request",
|
|
3669
|
+
userId: apiKeyResult.userId || "",
|
|
3670
|
+
resource: "api_key",
|
|
3671
|
+
resourceId: apiKeyId,
|
|
3672
|
+
success: true,
|
|
3673
|
+
metadata: {
|
|
3674
|
+
endpoint: "/api/graphql",
|
|
3675
|
+
method: "POST",
|
|
3676
|
+
ip: extractIp(req)
|
|
3677
|
+
}
|
|
3678
|
+
});
|
|
3679
|
+
}
|
|
3680
|
+
const body = await req.json().catch(() => ({}));
|
|
3681
|
+
const { query, variables } = body;
|
|
3682
|
+
if (!query) {
|
|
3683
|
+
return c.json({ errors: [{ message: "No query provided" }] }, 400);
|
|
3684
|
+
}
|
|
3685
|
+
let gqlUser;
|
|
3686
|
+
let apiKeyCtx;
|
|
3687
|
+
if (apiKeyRaw && db) {
|
|
3688
|
+
const apiKeyResult = await validateApiKey(apiKeyRaw, db);
|
|
3689
|
+
if (apiKeyResult.valid && apiKeyResult.user) {
|
|
3690
|
+
gqlUser = apiKeyResult.user;
|
|
3691
|
+
apiKeyCtx = createApiKeyContext(apiKeyResult);
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
const schema = buildGraphQLSchema({
|
|
3695
|
+
registry,
|
|
3696
|
+
db,
|
|
3697
|
+
user: gqlUser,
|
|
3698
|
+
req,
|
|
3699
|
+
settings
|
|
3700
|
+
});
|
|
3701
|
+
const document = parse(query);
|
|
3702
|
+
const result = await execute({
|
|
3703
|
+
schema,
|
|
3704
|
+
document,
|
|
3705
|
+
variableValues: variables,
|
|
3706
|
+
contextValue: { user: gqlUser, apiKeyContext: apiKeyCtx, req, db }
|
|
3707
|
+
});
|
|
3708
|
+
return c.json(result);
|
|
3709
|
+
} catch (error) {
|
|
3710
|
+
if (error.message?.includes("GraphQL is disabled")) {
|
|
3711
|
+
return c.json({ errors: [{ message: "GraphQL API is disabled" }] }, 403);
|
|
3712
|
+
}
|
|
3713
|
+
if (error instanceof SyntaxError) {
|
|
3714
|
+
return c.json({ errors: [{ message: "Invalid request body" }] }, 400);
|
|
3715
|
+
}
|
|
3716
|
+
console.error("[GraphQL] execution error:", error);
|
|
3717
|
+
return c.json({ errors: [{ message: error.message || "GraphQL execution failed" }] }, 500);
|
|
3718
|
+
}
|
|
3719
|
+
});
|
|
3633
3720
|
app.get("/api/auth/access", async (c) => {
|
|
3634
3721
|
try {
|
|
3635
3722
|
const { user: ctxUser, tenantID: ctxTenantID } = await resolveAuthContext(
|
|
@@ -4344,6 +4431,7 @@ function createHonoApp(options) {
|
|
|
4344
4431
|
key: rawKey,
|
|
4345
4432
|
keyPrefix: generateApiKeyPrefix(rawKey),
|
|
4346
4433
|
permissions: Array.isArray(body.permissions) ? body.permissions : ["*"],
|
|
4434
|
+
expiresAt: body.expiresAt || null,
|
|
4347
4435
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4348
4436
|
}
|
|
4349
4437
|
});
|
|
@@ -4389,6 +4477,201 @@ function createHonoApp(options) {
|
|
|
4389
4477
|
return c.json({ error: error.message }, 500);
|
|
4390
4478
|
}
|
|
4391
4479
|
});
|
|
4480
|
+
app.patch("/api/keys/:id", async (c) => {
|
|
4481
|
+
try {
|
|
4482
|
+
const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
|
|
4483
|
+
if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
|
|
4484
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
4485
|
+
}
|
|
4486
|
+
const id = c.req.param("id");
|
|
4487
|
+
const body = await c.req.json();
|
|
4488
|
+
const existing = await db.findByID({ collection: API_KEY_COLLECTION, id });
|
|
4489
|
+
if (!existing) return c.json({ error: "API key not found" }, 404);
|
|
4490
|
+
const updateData = {};
|
|
4491
|
+
if (typeof body.name === "string" && body.name.trim()) updateData.name = body.name.trim();
|
|
4492
|
+
if (Array.isArray(body.permissions)) updateData.permissions = body.permissions;
|
|
4493
|
+
if (body.expiresAt !== void 0) updateData.expiresAt = body.expiresAt || null;
|
|
4494
|
+
if (Object.keys(updateData).length === 0) return c.json({ error: "Nothing to update" }, 400);
|
|
4495
|
+
const updated = await db.update({ collection: API_KEY_COLLECTION, id, data: updateData });
|
|
4496
|
+
return c.json({ ...updated, keyPrefix: existing.keyPrefix });
|
|
4497
|
+
} catch (error) {
|
|
4498
|
+
console.error("[ApiKeys] PATCH error:", error);
|
|
4499
|
+
return c.json({ error: error.message }, 500);
|
|
4500
|
+
}
|
|
4501
|
+
});
|
|
4502
|
+
app.post("/api/keys/:id/rotate", async (c) => {
|
|
4503
|
+
try {
|
|
4504
|
+
const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
|
|
4505
|
+
if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
|
|
4506
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
4507
|
+
}
|
|
4508
|
+
const id = c.req.param("id");
|
|
4509
|
+
const existing = await db.findByID({ collection: API_KEY_COLLECTION, id });
|
|
4510
|
+
if (!existing) return c.json({ error: "API key not found" }, 404);
|
|
4511
|
+
const rawKey = generateApiKey();
|
|
4512
|
+
const updated = await db.update({
|
|
4513
|
+
collection: API_KEY_COLLECTION,
|
|
4514
|
+
id,
|
|
4515
|
+
data: {
|
|
4516
|
+
key: rawKey,
|
|
4517
|
+
keyPrefix: generateApiKeyPrefix(rawKey),
|
|
4518
|
+
lastUsedAt: null
|
|
4519
|
+
}
|
|
4520
|
+
});
|
|
4521
|
+
await sessionAuthAdapter?.createAuditLog({
|
|
4522
|
+
action: "api_key_rotate",
|
|
4523
|
+
userId: ctxUser.id,
|
|
4524
|
+
resource: "api_key",
|
|
4525
|
+
resourceId: id,
|
|
4526
|
+
success: true,
|
|
4527
|
+
metadata: { keyName: existing.name }
|
|
4528
|
+
});
|
|
4529
|
+
return c.json({
|
|
4530
|
+
...updated,
|
|
4531
|
+
key: rawKey,
|
|
4532
|
+
permissions: existing.permissions,
|
|
4533
|
+
expiresAt: existing.expiresAt
|
|
4534
|
+
});
|
|
4535
|
+
} catch (error) {
|
|
4536
|
+
console.error("[ApiKeys] rotate error:", error);
|
|
4537
|
+
return c.json({ error: error.message }, 500);
|
|
4538
|
+
}
|
|
4539
|
+
});
|
|
4540
|
+
app.get("/api/webhooks", async (c) => {
|
|
4541
|
+
try {
|
|
4542
|
+
const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
|
|
4543
|
+
if (!ctxUser || !hasPermission(ctxUser, "users:read")) {
|
|
4544
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
4545
|
+
}
|
|
4546
|
+
if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
|
|
4547
|
+
const webhooks = await webhookService.getWebhooks();
|
|
4548
|
+
return c.json({ docs: webhooks });
|
|
4549
|
+
} catch (error) {
|
|
4550
|
+
console.error("[Webhooks] GET error:", error);
|
|
4551
|
+
return c.json({ error: error.message }, 500);
|
|
4552
|
+
}
|
|
4553
|
+
});
|
|
4554
|
+
app.post("/api/webhooks", async (c) => {
|
|
4555
|
+
try {
|
|
4556
|
+
const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
|
|
4557
|
+
if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
|
|
4558
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
4559
|
+
}
|
|
4560
|
+
if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
|
|
4561
|
+
const body = await c.req.json();
|
|
4562
|
+
const webhook = await webhookService.createWebhook(body);
|
|
4563
|
+
await sessionAuthAdapter?.createAuditLog({
|
|
4564
|
+
action: "webhook_create",
|
|
4565
|
+
userId: ctxUser.id,
|
|
4566
|
+
resource: "webhook",
|
|
4567
|
+
resourceId: webhook.id,
|
|
4568
|
+
success: true,
|
|
4569
|
+
metadata: { name: webhook.name, url: webhook.url }
|
|
4570
|
+
});
|
|
4571
|
+
return c.json(webhook, 201);
|
|
4572
|
+
} catch (error) {
|
|
4573
|
+
console.error("[Webhooks] POST error:", error);
|
|
4574
|
+
return c.json({ error: error.message }, 500);
|
|
4575
|
+
}
|
|
4576
|
+
});
|
|
4577
|
+
app.get("/api/webhooks/:id", async (c) => {
|
|
4578
|
+
try {
|
|
4579
|
+
const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
|
|
4580
|
+
if (!ctxUser || !hasPermission(ctxUser, "users:read")) {
|
|
4581
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
4582
|
+
}
|
|
4583
|
+
if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
|
|
4584
|
+
const id = c.req.param("id");
|
|
4585
|
+
const webhook = await webhookService.getWebhookById(id);
|
|
4586
|
+
if (!webhook) return c.json({ error: "Webhook not found" }, 404);
|
|
4587
|
+
return c.json(webhook);
|
|
4588
|
+
} catch (error) {
|
|
4589
|
+
console.error("[Webhooks] GET :id error:", error);
|
|
4590
|
+
return c.json({ error: error.message }, 500);
|
|
4591
|
+
}
|
|
4592
|
+
});
|
|
4593
|
+
app.patch("/api/webhooks/:id", async (c) => {
|
|
4594
|
+
try {
|
|
4595
|
+
const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
|
|
4596
|
+
if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
|
|
4597
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
4598
|
+
}
|
|
4599
|
+
if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
|
|
4600
|
+
const id = c.req.param("id");
|
|
4601
|
+
const body = await c.req.json();
|
|
4602
|
+
const updated = await webhookService.updateWebhook(id, body);
|
|
4603
|
+
if (!updated) return c.json({ error: "Webhook not found" }, 404);
|
|
4604
|
+
await sessionAuthAdapter?.createAuditLog({
|
|
4605
|
+
action: "webhook_update",
|
|
4606
|
+
userId: ctxUser.id,
|
|
4607
|
+
resource: "webhook",
|
|
4608
|
+
resourceId: id,
|
|
4609
|
+
success: true,
|
|
4610
|
+
metadata: { name: updated.name }
|
|
4611
|
+
});
|
|
4612
|
+
return c.json(updated);
|
|
4613
|
+
} catch (error) {
|
|
4614
|
+
console.error("[Webhooks] PATCH error:", error);
|
|
4615
|
+
return c.json({ error: error.message }, 500);
|
|
4616
|
+
}
|
|
4617
|
+
});
|
|
4618
|
+
app.delete("/api/webhooks/:id", async (c) => {
|
|
4619
|
+
try {
|
|
4620
|
+
const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
|
|
4621
|
+
if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
|
|
4622
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
4623
|
+
}
|
|
4624
|
+
if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
|
|
4625
|
+
const id = c.req.param("id");
|
|
4626
|
+
const existing = await webhookService.getWebhookById(id);
|
|
4627
|
+
if (!existing) return c.json({ error: "Webhook not found" }, 404);
|
|
4628
|
+
await webhookService.deleteWebhook(id);
|
|
4629
|
+
await sessionAuthAdapter?.createAuditLog({
|
|
4630
|
+
action: "webhook_delete",
|
|
4631
|
+
userId: ctxUser.id,
|
|
4632
|
+
resource: "webhook",
|
|
4633
|
+
resourceId: id,
|
|
4634
|
+
success: true,
|
|
4635
|
+
metadata: { name: existing.name }
|
|
4636
|
+
});
|
|
4637
|
+
return c.json({ message: "Webhook deleted" });
|
|
4638
|
+
} catch (error) {
|
|
4639
|
+
console.error("[Webhooks] DELETE error:", error);
|
|
4640
|
+
return c.json({ error: error.message }, 500);
|
|
4641
|
+
}
|
|
4642
|
+
});
|
|
4643
|
+
app.post("/api/webhooks/:id/test", async (c) => {
|
|
4644
|
+
try {
|
|
4645
|
+
const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
|
|
4646
|
+
if (!ctxUser || !hasPermission(ctxUser, "users:admin")) {
|
|
4647
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
4648
|
+
}
|
|
4649
|
+
if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
|
|
4650
|
+
const id = c.req.param("id");
|
|
4651
|
+
const result = await webhookService.testWebhook(id);
|
|
4652
|
+
if (!result) return c.json({ error: "Webhook not found" }, 404);
|
|
4653
|
+
return c.json(result);
|
|
4654
|
+
} catch (error) {
|
|
4655
|
+
console.error("[Webhooks] test error:", error);
|
|
4656
|
+
return c.json({ error: error.message }, 500);
|
|
4657
|
+
}
|
|
4658
|
+
});
|
|
4659
|
+
app.get("/api/webhooks/:id/history", async (c) => {
|
|
4660
|
+
try {
|
|
4661
|
+
const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
|
|
4662
|
+
if (!ctxUser || !hasPermission(ctxUser, "users:read")) {
|
|
4663
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
4664
|
+
}
|
|
4665
|
+
if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
|
|
4666
|
+
const id = c.req.param("id");
|
|
4667
|
+
const limit = Math.min(parseInt(c.req.query("limit") || "50"), 100);
|
|
4668
|
+
const history = await webhookService.getDeliveryHistory(id, limit);
|
|
4669
|
+
return c.json({ docs: history });
|
|
4670
|
+
} catch (error) {
|
|
4671
|
+
console.error("[Webhooks] history error:", error);
|
|
4672
|
+
return c.json({ error: error.message }, 500);
|
|
4673
|
+
}
|
|
4674
|
+
});
|
|
4392
4675
|
const collections = registry.getCollections();
|
|
4393
4676
|
for (const collection of collections) {
|
|
4394
4677
|
let computeDiff2 = function(a, b) {
|
|
@@ -4425,6 +4708,7 @@ function createHonoApp(options) {
|
|
|
4425
4708
|
if (!access.allowed) {
|
|
4426
4709
|
return c.json({ error: access.error }, access.status || 403);
|
|
4427
4710
|
}
|
|
4711
|
+
auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, basePath, "GET", c.req.raw);
|
|
4428
4712
|
const url = new URL(c.req.url);
|
|
4429
4713
|
const page = parseInt(url.searchParams.get("page") || "1");
|
|
4430
4714
|
const limit = Math.min(
|
|
@@ -4482,6 +4766,7 @@ function createHonoApp(options) {
|
|
|
4482
4766
|
const url = new URL(c.req.url);
|
|
4483
4767
|
const compareA = url.searchParams.get("compareA");
|
|
4484
4768
|
const compareB = url.searchParams.get("compareB");
|
|
4769
|
+
auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, `${basePath}/${id}/versions`, "GET", c.req.raw);
|
|
4485
4770
|
if (compareA && compareB) {
|
|
4486
4771
|
const [versionA, versionB] = await Promise.all([
|
|
4487
4772
|
db.findVersionByID({ collection: slug, versionId: compareA, tenantID: ctxTenantID }),
|
|
@@ -4494,7 +4779,7 @@ function createHonoApp(options) {
|
|
|
4494
4779
|
return c.json({ diffs });
|
|
4495
4780
|
}
|
|
4496
4781
|
const page = parseInt(url.searchParams.get("page") || "1");
|
|
4497
|
-
const limit = parseInt(url.searchParams.get("limit") || "10");
|
|
4782
|
+
const limit = Math.min(parseInt(url.searchParams.get("limit") || "10"), 100);
|
|
4498
4783
|
const result = await db.findVersions({
|
|
4499
4784
|
collection: slug,
|
|
4500
4785
|
documentId: id,
|
|
@@ -4681,6 +4966,7 @@ function createHonoApp(options) {
|
|
|
4681
4966
|
if (!access.allowed) {
|
|
4682
4967
|
return c.json({ error: access.error }, access.status || 403);
|
|
4683
4968
|
}
|
|
4969
|
+
auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, basePath, "POST", c.req.raw);
|
|
4684
4970
|
const body = await c.req.json();
|
|
4685
4971
|
const schema = registry.getCreateZodSchema(slug);
|
|
4686
4972
|
let validated;
|
|
@@ -4740,13 +5026,19 @@ function createHonoApp(options) {
|
|
|
4740
5026
|
const id = c.req.param("id");
|
|
4741
5027
|
const body = await c.req.json();
|
|
4742
5028
|
const baseUpdatedAt = readBaseUpdatedAt(body);
|
|
5029
|
+
console.log(`[PATCH] ${slug}/${id}`, {
|
|
5030
|
+
baseUpdatedAt,
|
|
5031
|
+
bodyKeys: Object.keys(body),
|
|
5032
|
+
tenantID: ctxTenantID
|
|
5033
|
+
});
|
|
4743
5034
|
const cleaned = Object.fromEntries(
|
|
4744
5035
|
Object.entries(omitRevisionFields(body)).filter(
|
|
4745
|
-
([_, v]) => v !==
|
|
5036
|
+
([_, v]) => v !== "null" && v !== void 0
|
|
4746
5037
|
)
|
|
4747
5038
|
);
|
|
4748
5039
|
const schema = registry.getUpdateZodSchema(slug);
|
|
4749
5040
|
const validated = schema.parse(cleaned);
|
|
5041
|
+
console.log(`[PATCH] Validated data:`, Object.keys(validated));
|
|
4750
5042
|
const originalDoc = await db.findByID({
|
|
4751
5043
|
collection: slug,
|
|
4752
5044
|
id,
|
|
@@ -4754,6 +5046,9 @@ function createHonoApp(options) {
|
|
|
4754
5046
|
draft: true
|
|
4755
5047
|
// Always fetch current doc regardless of status
|
|
4756
5048
|
});
|
|
5049
|
+
if (originalDoc) {
|
|
5050
|
+
console.log(`[PATCH] Original doc updatedAt:`, originalDoc.updatedAt);
|
|
5051
|
+
}
|
|
4757
5052
|
if (!originalDoc) {
|
|
4758
5053
|
return c.json({ error: "Document not found" }, 404);
|
|
4759
5054
|
}
|
|
@@ -4773,7 +5068,7 @@ function createHonoApp(options) {
|
|
|
4773
5068
|
changeDescription: "Manual save (Draft)",
|
|
4774
5069
|
tenantID: ctxTenantID
|
|
4775
5070
|
});
|
|
4776
|
-
|
|
5071
|
+
await db.update({
|
|
4777
5072
|
collection: slug,
|
|
4778
5073
|
id,
|
|
4779
5074
|
data: { _has_draft: true },
|
|
@@ -4782,13 +5077,21 @@ function createHonoApp(options) {
|
|
|
4782
5077
|
});
|
|
4783
5078
|
} else {
|
|
4784
5079
|
const saveData = isDraftEnabled ? { ...validated, _status: "draft", _has_draft: false } : validated;
|
|
4785
|
-
|
|
5080
|
+
await db.update({
|
|
4786
5081
|
collection: slug,
|
|
4787
5082
|
id,
|
|
4788
5083
|
data: saveData,
|
|
4789
5084
|
tenantID: ctxTenantID
|
|
4790
5085
|
});
|
|
4791
|
-
|
|
5086
|
+
}
|
|
5087
|
+
doc = await db.findByID({
|
|
5088
|
+
collection: slug,
|
|
5089
|
+
id,
|
|
5090
|
+
tenantID: ctxTenantID,
|
|
5091
|
+
draft: true
|
|
5092
|
+
});
|
|
5093
|
+
if (isDraftEnabled) {
|
|
5094
|
+
if (!isAlreadyPublished) {
|
|
4792
5095
|
await db.createVersion({
|
|
4793
5096
|
collection: slug,
|
|
4794
5097
|
documentId: id,
|
|
@@ -4798,13 +5101,13 @@ function createHonoApp(options) {
|
|
|
4798
5101
|
changeDescription: "Manual save",
|
|
4799
5102
|
tenantID: ctxTenantID
|
|
4800
5103
|
});
|
|
4801
|
-
} else {
|
|
4802
|
-
await db.deleteDraft({
|
|
4803
|
-
collection: slug,
|
|
4804
|
-
documentId: id,
|
|
4805
|
-
tenantID: ctxTenantID
|
|
4806
|
-
});
|
|
4807
5104
|
}
|
|
5105
|
+
} else {
|
|
5106
|
+
await db.deleteDraft({
|
|
5107
|
+
collection: slug,
|
|
5108
|
+
documentId: id,
|
|
5109
|
+
tenantID: ctxTenantID
|
|
5110
|
+
});
|
|
4808
5111
|
}
|
|
4809
5112
|
if (webhookService) {
|
|
4810
5113
|
webhookService.trigger(getWebhookEvent(slug, "update"), {
|
|
@@ -4816,6 +5119,7 @@ function createHonoApp(options) {
|
|
|
4816
5119
|
tenantId: ctxTenantID
|
|
4817
5120
|
}).catch((err) => console.error(`[Webhook] Failed to trigger:`, err));
|
|
4818
5121
|
}
|
|
5122
|
+
console.log(`[PATCH] Result doc updatedAt:`, doc?.updatedAt);
|
|
4819
5123
|
return c.json({ data: doc, message: isDraftEnabled ? "Draft saved" : "Updated successfully" });
|
|
4820
5124
|
} catch (error) {
|
|
4821
5125
|
if (error.name === "ZodError") {
|
|
@@ -5510,5 +5814,5 @@ function createRESTAPI(registry, db, options) {
|
|
|
5510
5814
|
}
|
|
5511
5815
|
|
|
5512
5816
|
export { AuditLogger, AuthRoutes, InMemoryAuditLogger, InMemoryRateLimiter, MediaService, createAuditContext2 as createAuditContext, createHonoApp, createLocalStorage, createRESTAPI, getAppSecret, getEncryptionKey, getSessionConfig, init_secret, loadSecrets, resolveProvider, setDbAdapter };
|
|
5513
|
-
//# sourceMappingURL=chunk-
|
|
5514
|
-
//# sourceMappingURL=chunk-
|
|
5817
|
+
//# sourceMappingURL=chunk-4UD44U4Z.js.map
|
|
5818
|
+
//# sourceMappingURL=chunk-4UD44U4Z.js.map
|