@porulle/core 0.8.0 → 0.10.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.
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +26 -7
- package/dist/auth/setup.d.ts.map +1 -1
- package/dist/auth/setup.js +7 -5
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +3 -1
- package/dist/config/types.d.ts +13 -3
- package/dist/config/types.d.ts.map +1 -1
- package/dist/generated/plugin-manifest.d.ts +3 -0
- package/dist/generated/plugin-manifest.d.ts.map +1 -1
- package/dist/generated/plugin-manifest.js +1 -0
- package/dist/hooks/checkout-completion.d.ts.map +1 -1
- package/dist/hooks/checkout-completion.js +2 -2
- package/dist/hooks/checkout.d.ts.map +1 -1
- package/dist/hooks/checkout.js +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/interfaces/rest/router.d.ts.map +1 -1
- package/dist/interfaces/rest/router.js +2 -0
- package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/admin/staff.js +37 -3
- package/dist/interfaces/rest/routes/checkout.d.ts +9 -0
- package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/checkout.js +82 -22
- package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/inventory.js +13 -0
- package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/orders.js +13 -0
- package/dist/interfaces/rest/schemas/catalog.d.ts +391 -0
- package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/orders.js +2 -2
- package/dist/interfaces/rest/schemas/responses.d.ts +85 -0
- package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/search.d.ts +17 -0
- package/dist/interfaces/rest/schemas/search.d.ts.map +1 -1
- package/dist/kernel/database/scoped-db.d.ts +2 -1
- package/dist/kernel/database/scoped-db.d.ts.map +1 -1
- package/dist/kernel/database/scoped-db.js +74 -15
- package/dist/kernel/errors.d.ts +12 -0
- package/dist/kernel/errors.d.ts.map +1 -1
- package/dist/kernel/errors.js +16 -0
- package/dist/kernel/jobs/adapter.d.ts +27 -8
- package/dist/kernel/jobs/adapter.d.ts.map +1 -1
- package/dist/kernel/jobs/adapter.js +1 -2
- package/dist/kernel/jobs/drizzle-adapter.d.ts +13 -3
- package/dist/kernel/jobs/drizzle-adapter.d.ts.map +1 -1
- package/dist/kernel/jobs/drizzle-adapter.js +46 -5
- package/dist/kernel/jobs/runner.d.ts +2 -1
- package/dist/kernel/jobs/runner.d.ts.map +1 -1
- package/dist/kernel/jobs/runner.js +100 -15
- package/dist/kernel/jobs/types.d.ts +13 -0
- package/dist/kernel/jobs/types.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.d.ts +5 -0
- package/dist/kernel/plugin/manifest.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.js +9 -0
- package/dist/modules/analytics/drizzle-adapter.d.ts.map +1 -1
- package/dist/modules/analytics/drizzle-adapter.js +16 -3
- package/dist/modules/cart/service.d.ts.map +1 -1
- package/dist/modules/cart/service.js +3 -2
- package/dist/modules/catalog/entity-service.d.ts +1 -0
- package/dist/modules/catalog/entity-service.d.ts.map +1 -1
- package/dist/modules/catalog/entity-service.js +49 -3
- package/dist/modules/catalog/repository/index.d.ts +2 -2
- package/dist/modules/catalog/repository/index.d.ts.map +1 -1
- package/dist/modules/catalog/repository/index.js +9 -1
- package/dist/modules/catalog/schema.d.ts +51 -0
- package/dist/modules/catalog/schema.d.ts.map +1 -1
- package/dist/modules/catalog/schema.js +12 -1
- package/dist/modules/catalog/schemas.d.ts +3 -1
- package/dist/modules/catalog/schemas.d.ts.map +1 -1
- package/dist/modules/channels/adapter.d.ts +117 -0
- package/dist/modules/channels/adapter.d.ts.map +1 -0
- package/dist/modules/channels/adapter.js +3 -0
- package/dist/modules/customers/service.d.ts.map +1 -1
- package/dist/modules/customers/service.js +6 -0
- package/dist/modules/inventory/repository/index.js +4 -4
- package/dist/modules/inventory/service.d.ts.map +1 -1
- package/dist/modules/inventory/service.js +7 -3
- package/dist/modules/orders/schema.d.ts +17 -0
- package/dist/modules/orders/schema.d.ts.map +1 -1
- package/dist/modules/orders/schema.js +4 -0
- package/dist/modules/orders/service.d.ts +16 -2
- package/dist/modules/orders/service.d.ts.map +1 -1
- package/dist/modules/orders/service.js +226 -26
- package/dist/modules/orders/stale-order-cleanup.d.ts.map +1 -1
- package/dist/modules/orders/stale-order-cleanup.js +37 -16
- package/dist/modules/pricing/service.d.ts.map +1 -1
- package/dist/modules/pricing/service.js +5 -5
- package/dist/modules/promotions/repository/index.d.ts +1 -1
- package/dist/modules/promotions/repository/index.d.ts.map +1 -1
- package/dist/modules/promotions/repository/index.js +6 -3
- package/dist/modules/promotions/service.d.ts +1 -0
- package/dist/modules/promotions/service.d.ts.map +1 -1
- package/dist/modules/promotions/service.js +1 -1
- package/dist/modules/webhooks/repository/index.d.ts +1 -1
- package/dist/modules/webhooks/repository/index.d.ts.map +1 -1
- package/dist/modules/webhooks/repository/index.js +16 -10
- package/dist/runtime/kernel.d.ts.map +1 -1
- package/dist/runtime/kernel.js +14 -2
- package/dist/runtime/server.d.ts +1 -4
- package/dist/runtime/server.d.ts.map +1 -1
- package/dist/runtime/server.js +16 -34
- package/dist/test-utils/create-test-config.d.ts.map +1 -1
- package/dist/test-utils/create-test-config.js +2 -0
- package/dist/test-utils/order-test-helpers.d.ts +12 -0
- package/dist/test-utils/order-test-helpers.d.ts.map +1 -0
- package/dist/test-utils/order-test-helpers.js +18 -0
- package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
- package/dist/test-utils/rest-api-test-utils.js +1 -0
- package/dist/test-utils/test-actors.js +1 -1
- package/dist/testing.d.ts +1 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1 -0
- package/package.json +1 -1
- package/src/auth/middleware.ts +26 -7
- package/src/auth/setup.ts +10 -4
- package/src/config/defaults.ts +3 -1
- package/src/config/types.ts +16 -3
- package/src/generated/plugin-capabilities.d.ts +1 -0
- package/src/generated/plugin-manifest.ts +1 -0
- package/src/generated/plugin-repositories.d.ts +1 -0
- package/src/hooks/checkout-completion.ts +22 -16
- package/src/hooks/checkout.ts +11 -8
- package/src/index.ts +31 -1
- package/src/interfaces/rest/router.ts +2 -0
- package/src/interfaces/rest/routes/admin/staff.ts +45 -3
- package/src/interfaces/rest/routes/checkout.ts +94 -28
- package/src/interfaces/rest/routes/inventory.ts +14 -0
- package/src/interfaces/rest/routes/orders.ts +11 -0
- package/src/interfaces/rest/schemas/orders.ts +2 -2
- package/src/kernel/database/scoped-db.ts +83 -15
- package/src/kernel/errors.ts +20 -0
- package/src/kernel/jobs/adapter.ts +39 -8
- package/src/kernel/jobs/drizzle-adapter.ts +72 -9
- package/src/kernel/jobs/runner.ts +194 -72
- package/src/kernel/jobs/types.ts +25 -0
- package/src/kernel/plugin/manifest.ts +15 -0
- package/src/modules/analytics/drizzle-adapter.ts +19 -3
- package/src/modules/cart/service.ts +3 -2
- package/src/modules/catalog/entity-service.ts +37 -2
- package/src/modules/catalog/repository/index.ts +13 -3
- package/src/modules/catalog/schema.ts +33 -18
- package/src/modules/catalog/schemas.ts +3 -1
- package/src/modules/channels/adapter.ts +139 -0
- package/src/modules/customers/service.ts +6 -0
- package/src/modules/inventory/repository/index.ts +4 -4
- package/src/modules/inventory/service.ts +12 -2
- package/src/modules/orders/schema.ts +4 -0
- package/src/modules/orders/service.ts +404 -64
- package/src/modules/orders/stale-order-cleanup.ts +52 -27
- package/src/modules/pricing/service.ts +5 -6
- package/src/modules/promotions/repository/index.ts +6 -2
- package/src/modules/promotions/service.ts +2 -0
- package/src/modules/webhooks/repository/index.ts +16 -14
- package/src/runtime/kernel.ts +16 -2
- package/src/runtime/server.ts +22 -42
- package/src/test-utils/create-test-config.ts +2 -0
- package/src/test-utils/order-test-helpers.ts +26 -0
- package/src/test-utils/rest-api-test-utils.ts +1 -0
- package/src/test-utils/test-actors.ts +1 -1
- package/src/testing.ts +1 -0
|
@@ -5,7 +5,52 @@ import { runAfterHooks, runBeforeHooks } from "../../../kernel/hooks/executor.js
|
|
|
5
5
|
import { createHookContext } from "../../../kernel/hooks/create-context.js";
|
|
6
6
|
import { mapErrorToResponse, mapErrorToStatus } from "../utils.js";
|
|
7
7
|
import { isCommerceError } from "../../../kernel/errors.js";
|
|
8
|
+
import { assertPermission } from "../../../auth/permissions.js";
|
|
8
9
|
import { makeId } from "../../../utils/id.js";
|
|
10
|
+
/**
|
|
11
|
+
* SEC-07 — resolve the customer profile a checkout order is attributed to.
|
|
12
|
+
* A self-service actor may ONLY attribute the order to its own customer
|
|
13
|
+
* profile; only actors with org-level `customers:read` (staff/clienteling) may
|
|
14
|
+
* name an arbitrary `customerId`. Returns undefined for guests / unresolved
|
|
15
|
+
* (guest checkout). Exported for unit testing.
|
|
16
|
+
*/
|
|
17
|
+
export async function resolveCheckoutCustomerUuid(customers, actor, customerId) {
|
|
18
|
+
const actorUserId = actor?.userId;
|
|
19
|
+
let canActForOthers = false;
|
|
20
|
+
if (actorUserId) {
|
|
21
|
+
try {
|
|
22
|
+
assertPermission(actor, "customers:read");
|
|
23
|
+
canActForOthers = true;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
canActForOthers = false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (customerId) {
|
|
30
|
+
const uuidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
31
|
+
if (uuidRe.test(customerId)) {
|
|
32
|
+
const byId = await customers.getById(customerId, actor);
|
|
33
|
+
// Honor a profile UUID only if it is the actor's own, or the actor may
|
|
34
|
+
// act for other customers.
|
|
35
|
+
if (byId.ok && (canActForOthers || byId.value.userId === actorUserId)) {
|
|
36
|
+
return byId.value.id;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (canActForOthers) {
|
|
40
|
+
// Staff may resolve/create a customer by a supplied user_id.
|
|
41
|
+
const byUser = await customers.getByUserId(customerId, actor);
|
|
42
|
+
if (byUser.ok)
|
|
43
|
+
return byUser.value.id;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Self-service default: the authenticated actor's own customer profile.
|
|
47
|
+
if (actorUserId && !canActForOthers) {
|
|
48
|
+
const own = await customers.getByUserId(actorUserId, actor);
|
|
49
|
+
if (own.ok)
|
|
50
|
+
return own.value.id;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
9
54
|
export function checkoutRoutes(kernel) {
|
|
10
55
|
const router = new OpenAPIHono();
|
|
11
56
|
// @ts-expect-error -- openapi() enforces strict response typing but our handler
|
|
@@ -20,7 +65,32 @@ export function checkoutRoutes(kernel) {
|
|
|
20
65
|
if (body.idempotencyKey) {
|
|
21
66
|
const replay = await kernel.services.orders.getByIdempotencyKey(body.idempotencyKey, actor);
|
|
22
67
|
if (replay.ok && replay.value) {
|
|
23
|
-
|
|
68
|
+
// IDOR guard: an idempotency key must only replay the requester's OWN
|
|
69
|
+
// order. Without this, a same-org customer who supplies/guesses another
|
|
70
|
+
// customer's key would receive that customer's order (PII, totals, lines).
|
|
71
|
+
let canActForOthers = false;
|
|
72
|
+
try {
|
|
73
|
+
assertPermission(actor, "customers:read");
|
|
74
|
+
canActForOthers = true;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
canActForOthers = false;
|
|
78
|
+
}
|
|
79
|
+
const ownCustomer = actor
|
|
80
|
+
? await resolveCheckoutCustomerUuid(kernel.services.customers, actor, undefined)
|
|
81
|
+
: undefined;
|
|
82
|
+
const replayCustomer = replay.value.customerId ?? null;
|
|
83
|
+
const ownsOrder = (replayCustomer !== null && replayCustomer === ownCustomer) ||
|
|
84
|
+
(replayCustomer === null && !ownCustomer);
|
|
85
|
+
if (canActForOthers || ownsOrder) {
|
|
86
|
+
return c.json({ data: replay.value }, 201);
|
|
87
|
+
}
|
|
88
|
+
return c.json({
|
|
89
|
+
error: {
|
|
90
|
+
code: "IDEMPOTENCY_CONFLICT",
|
|
91
|
+
message: "Idempotency key does not belong to this account.",
|
|
92
|
+
},
|
|
93
|
+
}, 409);
|
|
24
94
|
}
|
|
25
95
|
}
|
|
26
96
|
const checkoutData = {
|
|
@@ -93,26 +163,10 @@ export function checkoutRoutes(kernel) {
|
|
|
93
163
|
// If Stripe takes 5s, the DB connection pool is not affected.
|
|
94
164
|
context.tx = null;
|
|
95
165
|
const processed = await runBeforeHooks(paymentHooks, validated, "create", context);
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (uuidRe.test(processed.customerId)) {
|
|
101
|
-
// Looks like a profile UUID — try direct lookup (no auto-create)
|
|
102
|
-
const byIdResult = await kernel.services.customers.getById(processed.customerId, actor);
|
|
103
|
-
if (byIdResult.ok) {
|
|
104
|
-
customerUuid = byIdResult.value.id;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (!customerUuid) {
|
|
108
|
-
// Fall back to user_id lookup (auto-creates customer profile if needed)
|
|
109
|
-
const byUserIdResult = await kernel.services.customers.getByUserId(processed.customerId, actor);
|
|
110
|
-
if (byUserIdResult.ok) {
|
|
111
|
-
customerUuid = byUserIdResult.value.id;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// If both lookups fail, we still allow guest checkout (customerUuid remains undefined)
|
|
115
|
-
}
|
|
166
|
+
// SEC-07: resolve the order's customer server-side. A self-service actor
|
|
167
|
+
// can only attribute the order to its own profile; a client-supplied
|
|
168
|
+
// foreign customerId is ignored unless the actor is staff.
|
|
169
|
+
const customerUuid = await resolveCheckoutCustomerUuid(kernel.services.customers, actor, processed.customerId);
|
|
116
170
|
const orderPayload = {
|
|
117
171
|
...(body.idempotencyKey !== undefined
|
|
118
172
|
? { idempotencyKey: body.idempotencyKey }
|
|
@@ -159,13 +213,19 @@ export function checkoutRoutes(kernel) {
|
|
|
159
213
|
? { customerId: customerUuid }
|
|
160
214
|
: {}),
|
|
161
215
|
};
|
|
162
|
-
|
|
216
|
+
// Checkout is a trusted, already-server-priced pipeline (resolveCurrentPrices
|
|
217
|
+
// + promotions/tax) and reserves stock in its own after-hooks — so it hands
|
|
218
|
+
// the order primitive precomputed totals rather than re-deriving them.
|
|
219
|
+
const order = await kernel.services.orders.create(orderPayload, actor, undefined, {
|
|
220
|
+
trustedPricing: true,
|
|
221
|
+
});
|
|
163
222
|
if (!order.ok) {
|
|
164
223
|
return c.json(mapErrorToResponse(order.error), mapErrorToStatus(order.error));
|
|
165
224
|
}
|
|
166
225
|
if (order.ok && (processed.appliedPromotions?.length ?? 0) > 0) {
|
|
167
226
|
await kernel.services.promotions.recordUsage({
|
|
168
227
|
promotions: processed.appliedPromotions ?? [],
|
|
228
|
+
organizationId: order.value.organizationId,
|
|
169
229
|
orderId: order.value.id,
|
|
170
230
|
...(customerUuid !== undefined
|
|
171
231
|
? { customerId: customerUuid }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inventory.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/inventory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAUzD,OAAO,EAAE,KAAK,MAAM,EAAqD,MAAM,aAAa,CAAC;AAE7F,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"inventory.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/inventory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAUzD,OAAO,EAAE,KAAK,MAAM,EAAqD,MAAM,aAAa,CAAC;AAE7F,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,gCA+F7C"}
|
|
@@ -5,6 +5,13 @@ export function inventoryRoutes(kernel) {
|
|
|
5
5
|
const router = new OpenAPIHono();
|
|
6
6
|
// Inventory levels listing requires inventory:read
|
|
7
7
|
router.use("/levels", requirePerm("inventory:read"));
|
|
8
|
+
// Warehouse config + reservation are staff operations, never public. Gate them
|
|
9
|
+
// at the route (reserve/release are also called internally by checkout with the
|
|
10
|
+
// customer's actor, so the service itself must stay callable — the boundary is
|
|
11
|
+
// the HTTP route). Blocks anonymous (401) and customers lacking the perm (403).
|
|
12
|
+
router.use("/warehouses", requirePerm("inventory:read"));
|
|
13
|
+
router.use("/reserve", requirePerm("inventory:adjust"));
|
|
14
|
+
router.use("/release", requirePerm("inventory:adjust"));
|
|
8
15
|
// @ts-expect-error -- openapi handler union return type
|
|
9
16
|
router.openapi(listInventoryLevelsRoute, async (c) => {
|
|
10
17
|
const actor = c.get("actor");
|
|
@@ -59,6 +66,12 @@ export function inventoryRoutes(kernel) {
|
|
|
59
66
|
router.openapi(createWarehouseRoute, async (c) => {
|
|
60
67
|
const body = c.req.valid("json");
|
|
61
68
|
const actor = c.get("actor");
|
|
69
|
+
// Creating a warehouse (write) needs the higher inventory:adjust perm; the
|
|
70
|
+
// router.use above already requires inventory:read for the /warehouses path.
|
|
71
|
+
const perms = actor?.permissions ?? [];
|
|
72
|
+
if (!(perms.includes("inventory:adjust") || perms.includes("inventory:*") || perms.includes("*:*"))) {
|
|
73
|
+
return c.json({ error: { code: "FORBIDDEN", message: "Permission 'inventory:adjust' is required." } }, 403);
|
|
74
|
+
}
|
|
62
75
|
const result = await kernel.services.inventory.createWarehouse(body, actor);
|
|
63
76
|
if (!result.ok)
|
|
64
77
|
return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,KAAK,MAAM,EAAiE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,KAAK,MAAM,EAAiE,MAAM,aAAa,CAAC;AAIzG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,gCAgRzC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
2
2
|
import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
|
|
3
3
|
import { isUUID, mapErrorToResponse, mapErrorToStatus, parsePagination } from "../utils.js";
|
|
4
|
+
import { assertPermission } from "../../../auth/permissions.js";
|
|
4
5
|
export function orderRoutes(kernel) {
|
|
5
6
|
const router = new OpenAPIHono();
|
|
6
7
|
// @ts-expect-error -- openapi handler union return type
|
|
@@ -55,6 +56,12 @@ export function orderRoutes(kernel) {
|
|
|
55
56
|
});
|
|
56
57
|
// @ts-expect-error -- openapi handler union return type
|
|
57
58
|
router.openapi(createOrderRoute, async (c) => {
|
|
59
|
+
try {
|
|
60
|
+
assertPermission(c.get("actor"), "orders:manage");
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
|
|
64
|
+
}
|
|
58
65
|
const body = c.req.valid("json");
|
|
59
66
|
const result = await kernel.services.orders.create(body, c.get("actor"));
|
|
60
67
|
if (!result.ok)
|
|
@@ -139,6 +146,12 @@ export function orderRoutes(kernel) {
|
|
|
139
146
|
});
|
|
140
147
|
// @ts-expect-error -- openapi handler union return type
|
|
141
148
|
router.openapi(addOrderLineItemRoute, async (c) => {
|
|
149
|
+
try {
|
|
150
|
+
assertPermission(c.get("actor"), "orders:manage");
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
|
|
154
|
+
}
|
|
142
155
|
const body = c.req.valid("json");
|
|
143
156
|
const result = await kernel.services.orders.addLineItem(c.req.param("id"), body, c.get("actor"));
|
|
144
157
|
if (!result.ok)
|