@voyantjs/products 0.5.0 → 0.6.2

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/routes.js CHANGED
@@ -1,4 +1,6 @@
1
+ import { parseJsonBody, parseQuery, RequestValidationError, requireUserId } from "@voyantjs/hono";
1
2
  import { Hono } from "hono";
3
+ import { z } from "zod";
2
4
  import { productsService } from "./service.js";
3
5
  import { destinationListQuerySchema, destinationTranslationListQuerySchema, insertDaySchema, insertDayServiceSchema, insertDestinationSchema, insertDestinationTranslationSchema, insertOptionUnitSchema, insertOptionUnitTranslationSchema, insertProductActivationSettingSchema, insertProductCapabilitySchema, insertProductCategorySchema, insertProductDeliveryFormatSchema, insertProductDestinationSchema, insertProductFaqSchema, insertProductFeatureSchema, insertProductLocationSchema, insertProductMediaSchema, insertProductNoteSchema, insertProductOptionSchema, insertProductOptionTranslationSchema, insertProductSchema, insertProductTagSchema, insertProductTicketSettingSchema, insertProductTranslationSchema, insertProductTypeSchema, insertProductVisibilitySettingSchema, insertVersionSchema, optionUnitListQuerySchema, optionUnitTranslationListQuerySchema, productActivationSettingListQuerySchema, productCapabilityListQuerySchema, productCategoryListQuerySchema, productDeliveryFormatListQuerySchema, productDestinationListQuerySchema, productFaqListQuerySchema, productFeatureListQuerySchema, productListQuerySchema, productLocationListQuerySchema, productMediaListQuerySchema, productOptionListQuerySchema, productOptionTranslationListQuerySchema, productTagListQuerySchema, productTicketSettingListQuerySchema, productTranslationListQuerySchema, productTypeListQuerySchema, productVisibilitySettingListQuerySchema, reorderProductMediaSchema, updateDaySchema, updateDayServiceSchema, updateDestinationSchema, updateDestinationTranslationSchema, updateOptionUnitSchema, updateOptionUnitTranslationSchema, updateProductActivationSettingSchema, updateProductCapabilitySchema, updateProductCategorySchema, updateProductDeliveryFormatSchema, updateProductFaqSchema, updateProductFeatureSchema, updateProductLocationSchema, updateProductMediaSchema, updateProductOptionSchema, updateProductOptionTranslationSchema, updateProductSchema, updateProductTagSchema, updateProductTicketSettingSchema, updateProductTranslationSchema, updateProductTypeSchema, updateProductVisibilitySettingSchema, upsertProductBrochureSchema, } from "./validation.js";
4
6
  // ==========================================================================
@@ -7,20 +9,20 @@ import { destinationListQuerySchema, destinationTranslationListQuerySchema, inse
7
9
  export const productRoutes = new Hono()
8
10
  // GET / — List products
9
11
  .get("/", async (c) => {
10
- const query = productListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
12
+ const query = parseQuery(c, productListQuerySchema);
11
13
  return c.json(await productsService.listProducts(c.get("db"), query));
12
14
  })
13
15
  // POST / — Create product
14
16
  .post("/", async (c) => {
15
17
  return c.json({
16
- data: await productsService.createProduct(c.get("db"), insertProductSchema.parse(await c.req.json())),
18
+ data: await productsService.createProduct(c.get("db"), await parseJsonBody(c, insertProductSchema)),
17
19
  }, 201);
18
20
  })
19
21
  // ==========================================================================
20
22
  // Product operating configuration
21
23
  // ==========================================================================
22
24
  .get("/activation-settings", async (c) => {
23
- const query = productActivationSettingListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
25
+ const query = parseQuery(c, productActivationSettingListQuerySchema);
24
26
  return c.json(await productsService.listActivationSettings(c.get("db"), query));
25
27
  })
26
28
  .get("/activation-settings/:id", async (c) => {
@@ -31,14 +33,14 @@ export const productRoutes = new Hono()
31
33
  return c.json({ data: row });
32
34
  })
33
35
  .post("/:id/activation-settings", async (c) => {
34
- const row = await productsService.upsertActivationSetting(c.get("db"), c.req.param("id"), insertProductActivationSettingSchema.parse(await c.req.json()));
36
+ const row = await productsService.upsertActivationSetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductActivationSettingSchema));
35
37
  if (!row) {
36
38
  return c.json({ error: "Product not found" }, 404);
37
39
  }
38
40
  return c.json({ data: row }, 201);
39
41
  })
40
42
  .patch("/activation-settings/:id", async (c) => {
41
- const row = await productsService.updateActivationSetting(c.get("db"), c.req.param("id"), updateProductActivationSettingSchema.parse(await c.req.json()));
43
+ const row = await productsService.updateActivationSetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductActivationSettingSchema));
42
44
  if (!row) {
43
45
  return c.json({ error: "Product activation setting not found" }, 404);
44
46
  }
@@ -52,7 +54,7 @@ export const productRoutes = new Hono()
52
54
  return c.json({ success: true }, 200);
53
55
  })
54
56
  .get("/ticket-settings", async (c) => {
55
- const query = productTicketSettingListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
57
+ const query = parseQuery(c, productTicketSettingListQuerySchema);
56
58
  return c.json(await productsService.listTicketSettings(c.get("db"), query));
57
59
  })
58
60
  .get("/ticket-settings/:id", async (c) => {
@@ -63,14 +65,14 @@ export const productRoutes = new Hono()
63
65
  return c.json({ data: row });
64
66
  })
65
67
  .post("/:id/ticket-settings", async (c) => {
66
- const row = await productsService.upsertTicketSetting(c.get("db"), c.req.param("id"), insertProductTicketSettingSchema.parse(await c.req.json()));
68
+ const row = await productsService.upsertTicketSetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductTicketSettingSchema));
67
69
  if (!row) {
68
70
  return c.json({ error: "Product not found" }, 404);
69
71
  }
70
72
  return c.json({ data: row }, 201);
71
73
  })
72
74
  .patch("/ticket-settings/:id", async (c) => {
73
- const row = await productsService.updateTicketSetting(c.get("db"), c.req.param("id"), updateProductTicketSettingSchema.parse(await c.req.json()));
75
+ const row = await productsService.updateTicketSetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductTicketSettingSchema));
74
76
  if (!row) {
75
77
  return c.json({ error: "Product ticket setting not found" }, 404);
76
78
  }
@@ -84,7 +86,7 @@ export const productRoutes = new Hono()
84
86
  return c.json({ success: true }, 200);
85
87
  })
86
88
  .get("/visibility-settings", async (c) => {
87
- const query = productVisibilitySettingListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
89
+ const query = parseQuery(c, productVisibilitySettingListQuerySchema);
88
90
  return c.json(await productsService.listVisibilitySettings(c.get("db"), query));
89
91
  })
90
92
  .get("/visibility-settings/:id", async (c) => {
@@ -95,14 +97,14 @@ export const productRoutes = new Hono()
95
97
  return c.json({ data: row });
96
98
  })
97
99
  .post("/:id/visibility-settings", async (c) => {
98
- const row = await productsService.upsertVisibilitySetting(c.get("db"), c.req.param("id"), insertProductVisibilitySettingSchema.parse(await c.req.json()));
100
+ const row = await productsService.upsertVisibilitySetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductVisibilitySettingSchema));
99
101
  if (!row) {
100
102
  return c.json({ error: "Product not found" }, 404);
101
103
  }
102
104
  return c.json({ data: row }, 201);
103
105
  })
104
106
  .patch("/visibility-settings/:id", async (c) => {
105
- const row = await productsService.updateVisibilitySetting(c.get("db"), c.req.param("id"), updateProductVisibilitySettingSchema.parse(await c.req.json()));
107
+ const row = await productsService.updateVisibilitySetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductVisibilitySettingSchema));
106
108
  if (!row) {
107
109
  return c.json({ error: "Product visibility setting not found" }, 404);
108
110
  }
@@ -116,7 +118,7 @@ export const productRoutes = new Hono()
116
118
  return c.json({ success: true }, 200);
117
119
  })
118
120
  .get("/capabilities", async (c) => {
119
- const query = productCapabilityListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
121
+ const query = parseQuery(c, productCapabilityListQuerySchema);
120
122
  return c.json(await productsService.listCapabilities(c.get("db"), query));
121
123
  })
122
124
  .get("/capabilities/:id", async (c) => {
@@ -127,14 +129,14 @@ export const productRoutes = new Hono()
127
129
  return c.json({ data: row });
128
130
  })
129
131
  .post("/:id/capabilities", async (c) => {
130
- const row = await productsService.createCapability(c.get("db"), c.req.param("id"), insertProductCapabilitySchema.parse(await c.req.json()));
132
+ const row = await productsService.createCapability(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductCapabilitySchema));
131
133
  if (!row) {
132
134
  return c.json({ error: "Product not found" }, 404);
133
135
  }
134
136
  return c.json({ data: row }, 201);
135
137
  })
136
138
  .patch("/capabilities/:id", async (c) => {
137
- const row = await productsService.updateCapability(c.get("db"), c.req.param("id"), updateProductCapabilitySchema.parse(await c.req.json()));
139
+ const row = await productsService.updateCapability(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductCapabilitySchema));
138
140
  if (!row) {
139
141
  return c.json({ error: "Product capability not found" }, 404);
140
142
  }
@@ -148,7 +150,7 @@ export const productRoutes = new Hono()
148
150
  return c.json({ success: true }, 200);
149
151
  })
150
152
  .get("/delivery-formats", async (c) => {
151
- const query = productDeliveryFormatListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
153
+ const query = parseQuery(c, productDeliveryFormatListQuerySchema);
152
154
  return c.json(await productsService.listDeliveryFormats(c.get("db"), query));
153
155
  })
154
156
  .get("/delivery-formats/:id", async (c) => {
@@ -159,14 +161,14 @@ export const productRoutes = new Hono()
159
161
  return c.json({ data: row });
160
162
  })
161
163
  .post("/:id/delivery-formats", async (c) => {
162
- const row = await productsService.createDeliveryFormat(c.get("db"), c.req.param("id"), insertProductDeliveryFormatSchema.parse(await c.req.json()));
164
+ const row = await productsService.createDeliveryFormat(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductDeliveryFormatSchema));
163
165
  if (!row) {
164
166
  return c.json({ error: "Product not found" }, 404);
165
167
  }
166
168
  return c.json({ data: row }, 201);
167
169
  })
168
170
  .patch("/delivery-formats/:id", async (c) => {
169
- const row = await productsService.updateDeliveryFormat(c.get("db"), c.req.param("id"), updateProductDeliveryFormatSchema.parse(await c.req.json()));
171
+ const row = await productsService.updateDeliveryFormat(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductDeliveryFormatSchema));
170
172
  if (!row) {
171
173
  return c.json({ error: "Product delivery format not found" }, 404);
172
174
  }
@@ -180,7 +182,7 @@ export const productRoutes = new Hono()
180
182
  return c.json({ success: true }, 200);
181
183
  })
182
184
  .get("/features", async (c) => {
183
- const query = productFeatureListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
185
+ const query = parseQuery(c, productFeatureListQuerySchema);
184
186
  return c.json(await productsService.listFeatures(c.get("db"), query));
185
187
  })
186
188
  .get("/features/:id", async (c) => {
@@ -191,14 +193,14 @@ export const productRoutes = new Hono()
191
193
  return c.json({ data: row });
192
194
  })
193
195
  .post("/:id/features", async (c) => {
194
- const row = await productsService.createFeature(c.get("db"), c.req.param("id"), insertProductFeatureSchema.parse(await c.req.json()));
196
+ const row = await productsService.createFeature(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductFeatureSchema));
195
197
  if (!row) {
196
198
  return c.json({ error: "Product not found" }, 404);
197
199
  }
198
200
  return c.json({ data: row }, 201);
199
201
  })
200
202
  .patch("/features/:id", async (c) => {
201
- const row = await productsService.updateFeature(c.get("db"), c.req.param("id"), updateProductFeatureSchema.parse(await c.req.json()));
203
+ const row = await productsService.updateFeature(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductFeatureSchema));
202
204
  if (!row) {
203
205
  return c.json({ error: "Product feature not found" }, 404);
204
206
  }
@@ -212,7 +214,7 @@ export const productRoutes = new Hono()
212
214
  return c.json({ success: true }, 200);
213
215
  })
214
216
  .get("/faqs", async (c) => {
215
- const query = productFaqListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
217
+ const query = parseQuery(c, productFaqListQuerySchema);
216
218
  return c.json(await productsService.listFaqs(c.get("db"), query));
217
219
  })
218
220
  .get("/faqs/:id", async (c) => {
@@ -223,14 +225,14 @@ export const productRoutes = new Hono()
223
225
  return c.json({ data: row });
224
226
  })
225
227
  .post("/:id/faqs", async (c) => {
226
- const row = await productsService.createFaq(c.get("db"), c.req.param("id"), insertProductFaqSchema.parse(await c.req.json()));
228
+ const row = await productsService.createFaq(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductFaqSchema));
227
229
  if (!row) {
228
230
  return c.json({ error: "Product not found" }, 404);
229
231
  }
230
232
  return c.json({ data: row }, 201);
231
233
  })
232
234
  .patch("/faqs/:id", async (c) => {
233
- const row = await productsService.updateFaq(c.get("db"), c.req.param("id"), updateProductFaqSchema.parse(await c.req.json()));
235
+ const row = await productsService.updateFaq(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductFaqSchema));
234
236
  if (!row) {
235
237
  return c.json({ error: "Product FAQ not found" }, 404);
236
238
  }
@@ -244,7 +246,7 @@ export const productRoutes = new Hono()
244
246
  return c.json({ success: true }, 200);
245
247
  })
246
248
  .get("/locations", async (c) => {
247
- const query = productLocationListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
249
+ const query = parseQuery(c, productLocationListQuerySchema);
248
250
  return c.json(await productsService.listLocations(c.get("db"), query));
249
251
  })
250
252
  .get("/locations/:id", async (c) => {
@@ -255,14 +257,14 @@ export const productRoutes = new Hono()
255
257
  return c.json({ data: row });
256
258
  })
257
259
  .post("/:id/locations", async (c) => {
258
- const row = await productsService.createLocation(c.get("db"), c.req.param("id"), insertProductLocationSchema.parse(await c.req.json()));
260
+ const row = await productsService.createLocation(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductLocationSchema));
259
261
  if (!row) {
260
262
  return c.json({ error: "Product not found" }, 404);
261
263
  }
262
264
  return c.json({ data: row }, 201);
263
265
  })
264
266
  .patch("/locations/:id", async (c) => {
265
- const row = await productsService.updateLocation(c.get("db"), c.req.param("id"), updateProductLocationSchema.parse(await c.req.json()));
267
+ const row = await productsService.updateLocation(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductLocationSchema));
266
268
  if (!row) {
267
269
  return c.json({ error: "Product location not found" }, 404);
268
270
  }
@@ -276,7 +278,7 @@ export const productRoutes = new Hono()
276
278
  return c.json({ success: true }, 200);
277
279
  })
278
280
  .get("/destinations", async (c) => {
279
- const query = destinationListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
281
+ const query = parseQuery(c, destinationListQuerySchema);
280
282
  return c.json(await productsService.listDestinations(c.get("db"), query));
281
283
  })
282
284
  .get("/destinations/:id", async (c) => {
@@ -287,11 +289,11 @@ export const productRoutes = new Hono()
287
289
  return c.json({ data: row });
288
290
  })
289
291
  .post("/destinations", async (c) => {
290
- const row = await productsService.createDestination(c.get("db"), insertDestinationSchema.parse(await c.req.json()));
292
+ const row = await productsService.createDestination(c.get("db"), await parseJsonBody(c, insertDestinationSchema));
291
293
  return c.json({ data: row }, 201);
292
294
  })
293
295
  .patch("/destinations/:id", async (c) => {
294
- const row = await productsService.updateDestination(c.get("db"), c.req.param("id"), updateDestinationSchema.parse(await c.req.json()));
296
+ const row = await productsService.updateDestination(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateDestinationSchema));
295
297
  if (!row) {
296
298
  return c.json({ error: "Destination not found" }, 404);
297
299
  }
@@ -305,18 +307,18 @@ export const productRoutes = new Hono()
305
307
  return c.json({ success: true }, 200);
306
308
  })
307
309
  .get("/destination-translations", async (c) => {
308
- const query = destinationTranslationListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
310
+ const query = parseQuery(c, destinationTranslationListQuerySchema);
309
311
  return c.json(await productsService.listDestinationTranslations(c.get("db"), query));
310
312
  })
311
313
  .post("/destinations/:id/translations", async (c) => {
312
- const row = await productsService.upsertDestinationTranslation(c.get("db"), c.req.param("id"), insertDestinationTranslationSchema.parse(await c.req.json()));
314
+ const row = await productsService.upsertDestinationTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertDestinationTranslationSchema));
313
315
  if (!row) {
314
316
  return c.json({ error: "Destination not found" }, 404);
315
317
  }
316
318
  return c.json({ data: row }, 201);
317
319
  })
318
320
  .patch("/destination-translations/:id", async (c) => {
319
- const row = await productsService.updateDestinationTranslation(c.get("db"), c.req.param("id"), updateDestinationTranslationSchema.parse(await c.req.json()));
321
+ const row = await productsService.updateDestinationTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateDestinationTranslationSchema));
320
322
  if (!row) {
321
323
  return c.json({ error: "Destination translation not found" }, 404);
322
324
  }
@@ -330,11 +332,11 @@ export const productRoutes = new Hono()
330
332
  return c.json({ success: true }, 200);
331
333
  })
332
334
  .get("/destination-links", async (c) => {
333
- const query = productDestinationListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
335
+ const query = parseQuery(c, productDestinationListQuerySchema);
334
336
  return c.json(await productsService.listProductDestinations(c.get("db"), query));
335
337
  })
336
338
  .post("/:id/destinations", async (c) => {
337
- const row = await productsService.assignProductDestination(c.get("db"), c.req.param("id"), insertProductDestinationSchema.parse(await c.req.json()));
339
+ const row = await productsService.assignProductDestination(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductDestinationSchema));
338
340
  if (!row) {
339
341
  return c.json({ error: "Product or destination not found" }, 404);
340
342
  }
@@ -352,7 +354,7 @@ export const productRoutes = new Hono()
352
354
  // ==========================================================================
353
355
  // GET /options — List options
354
356
  .get("/options", async (c) => {
355
- const query = productOptionListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
357
+ const query = parseQuery(c, productOptionListQuerySchema);
356
358
  return c.json(await productsService.listOptions(c.get("db"), query));
357
359
  })
358
360
  // GET /options/:optionId — Get single option
@@ -365,7 +367,7 @@ export const productRoutes = new Hono()
365
367
  })
366
368
  // POST /:id/options — Create option for product
367
369
  .post("/:id/options", async (c) => {
368
- const row = await productsService.createOption(c.get("db"), c.req.param("id"), insertProductOptionSchema.parse(await c.req.json()));
370
+ const row = await productsService.createOption(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductOptionSchema));
369
371
  if (!row) {
370
372
  return c.json({ error: "Product not found" }, 404);
371
373
  }
@@ -373,7 +375,7 @@ export const productRoutes = new Hono()
373
375
  })
374
376
  // PATCH /options/:optionId — Update option
375
377
  .patch("/options/:optionId", async (c) => {
376
- const row = await productsService.updateOption(c.get("db"), c.req.param("optionId"), updateProductOptionSchema.parse(await c.req.json()));
378
+ const row = await productsService.updateOption(c.get("db"), c.req.param("optionId"), await parseJsonBody(c, updateProductOptionSchema));
377
379
  if (!row) {
378
380
  return c.json({ error: "Product option not found" }, 404);
379
381
  }
@@ -392,7 +394,7 @@ export const productRoutes = new Hono()
392
394
  // ==========================================================================
393
395
  // GET /units — List units
394
396
  .get("/units", async (c) => {
395
- const query = optionUnitListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
397
+ const query = parseQuery(c, optionUnitListQuerySchema);
396
398
  return c.json(await productsService.listUnits(c.get("db"), query));
397
399
  })
398
400
  // GET /units/:unitId — Get single unit
@@ -405,7 +407,7 @@ export const productRoutes = new Hono()
405
407
  })
406
408
  // POST /options/:optionId/units — Create unit for option
407
409
  .post("/options/:optionId/units", async (c) => {
408
- const row = await productsService.createUnit(c.get("db"), c.req.param("optionId"), insertOptionUnitSchema.parse(await c.req.json()));
410
+ const row = await productsService.createUnit(c.get("db"), c.req.param("optionId"), await parseJsonBody(c, insertOptionUnitSchema));
409
411
  if (!row) {
410
412
  return c.json({ error: "Product option not found" }, 404);
411
413
  }
@@ -413,7 +415,7 @@ export const productRoutes = new Hono()
413
415
  })
414
416
  // PATCH /units/:unitId — Update unit
415
417
  .patch("/units/:unitId", async (c) => {
416
- const row = await productsService.updateUnit(c.get("db"), c.req.param("unitId"), updateOptionUnitSchema.parse(await c.req.json()));
418
+ const row = await productsService.updateUnit(c.get("db"), c.req.param("unitId"), await parseJsonBody(c, updateOptionUnitSchema));
417
419
  if (!row) {
418
420
  return c.json({ error: "Option unit not found" }, 404);
419
421
  }
@@ -431,7 +433,7 @@ export const productRoutes = new Hono()
431
433
  // Translations
432
434
  // ==========================================================================
433
435
  .get("/translations", async (c) => {
434
- const query = productTranslationListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
436
+ const query = parseQuery(c, productTranslationListQuerySchema);
435
437
  return c.json(await productsService.listProductTranslations(c.get("db"), query));
436
438
  })
437
439
  .get("/translations/:translationId", async (c) => {
@@ -442,14 +444,14 @@ export const productRoutes = new Hono()
442
444
  return c.json({ data: row });
443
445
  })
444
446
  .post("/:id/translations", async (c) => {
445
- const row = await productsService.createProductTranslation(c.get("db"), c.req.param("id"), insertProductTranslationSchema.parse(await c.req.json()));
447
+ const row = await productsService.createProductTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductTranslationSchema));
446
448
  if (!row) {
447
449
  return c.json({ error: "Product not found" }, 404);
448
450
  }
449
451
  return c.json({ data: row }, 201);
450
452
  })
451
453
  .patch("/translations/:translationId", async (c) => {
452
- const row = await productsService.updateProductTranslation(c.get("db"), c.req.param("translationId"), updateProductTranslationSchema.parse(await c.req.json()));
454
+ const row = await productsService.updateProductTranslation(c.get("db"), c.req.param("translationId"), await parseJsonBody(c, updateProductTranslationSchema));
453
455
  if (!row) {
454
456
  return c.json({ error: "Product translation not found" }, 404);
455
457
  }
@@ -463,7 +465,7 @@ export const productRoutes = new Hono()
463
465
  return c.json({ success: true }, 200);
464
466
  })
465
467
  .get("/option-translations", async (c) => {
466
- const query = productOptionTranslationListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
468
+ const query = parseQuery(c, productOptionTranslationListQuerySchema);
467
469
  return c.json(await productsService.listOptionTranslations(c.get("db"), query));
468
470
  })
469
471
  .get("/option-translations/:translationId", async (c) => {
@@ -474,14 +476,14 @@ export const productRoutes = new Hono()
474
476
  return c.json({ data: row });
475
477
  })
476
478
  .post("/options/:optionId/translations", async (c) => {
477
- const row = await productsService.createOptionTranslation(c.get("db"), c.req.param("optionId"), insertProductOptionTranslationSchema.parse(await c.req.json()));
479
+ const row = await productsService.createOptionTranslation(c.get("db"), c.req.param("optionId"), await parseJsonBody(c, insertProductOptionTranslationSchema));
478
480
  if (!row) {
479
481
  return c.json({ error: "Product option not found" }, 404);
480
482
  }
481
483
  return c.json({ data: row }, 201);
482
484
  })
483
485
  .patch("/option-translations/:translationId", async (c) => {
484
- const row = await productsService.updateOptionTranslation(c.get("db"), c.req.param("translationId"), updateProductOptionTranslationSchema.parse(await c.req.json()));
486
+ const row = await productsService.updateOptionTranslation(c.get("db"), c.req.param("translationId"), await parseJsonBody(c, updateProductOptionTranslationSchema));
485
487
  if (!row) {
486
488
  return c.json({ error: "Option translation not found" }, 404);
487
489
  }
@@ -495,7 +497,7 @@ export const productRoutes = new Hono()
495
497
  return c.json({ success: true }, 200);
496
498
  })
497
499
  .get("/unit-translations", async (c) => {
498
- const query = optionUnitTranslationListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
500
+ const query = parseQuery(c, optionUnitTranslationListQuerySchema);
499
501
  return c.json(await productsService.listUnitTranslations(c.get("db"), query));
500
502
  })
501
503
  .get("/unit-translations/:translationId", async (c) => {
@@ -506,14 +508,14 @@ export const productRoutes = new Hono()
506
508
  return c.json({ data: row });
507
509
  })
508
510
  .post("/units/:unitId/translations", async (c) => {
509
- const row = await productsService.createUnitTranslation(c.get("db"), c.req.param("unitId"), insertOptionUnitTranslationSchema.parse(await c.req.json()));
511
+ const row = await productsService.createUnitTranslation(c.get("db"), c.req.param("unitId"), await parseJsonBody(c, insertOptionUnitTranslationSchema));
510
512
  if (!row) {
511
513
  return c.json({ error: "Option unit not found" }, 404);
512
514
  }
513
515
  return c.json({ data: row }, 201);
514
516
  })
515
517
  .patch("/unit-translations/:translationId", async (c) => {
516
- const row = await productsService.updateUnitTranslation(c.get("db"), c.req.param("translationId"), updateOptionUnitTranslationSchema.parse(await c.req.json()));
518
+ const row = await productsService.updateUnitTranslation(c.get("db"), c.req.param("translationId"), await parseJsonBody(c, updateOptionUnitTranslationSchema));
517
519
  if (!row) {
518
520
  return c.json({ error: "Unit translation not found" }, 404);
519
521
  }
@@ -530,7 +532,7 @@ export const productRoutes = new Hono()
530
532
  // Product Types
531
533
  // ==========================================================================
532
534
  .get("/product-types", async (c) => {
533
- const query = productTypeListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
535
+ const query = parseQuery(c, productTypeListQuerySchema);
534
536
  return c.json(await productsService.listProductTypes(c.get("db"), query));
535
537
  })
536
538
  .get("/product-types/:typeId", async (c) => {
@@ -542,11 +544,11 @@ export const productRoutes = new Hono()
542
544
  })
543
545
  .post("/product-types", async (c) => {
544
546
  return c.json({
545
- data: await productsService.createProductType(c.get("db"), insertProductTypeSchema.parse(await c.req.json())),
547
+ data: await productsService.createProductType(c.get("db"), await parseJsonBody(c, insertProductTypeSchema)),
546
548
  }, 201);
547
549
  })
548
550
  .patch("/product-types/:typeId", async (c) => {
549
- const row = await productsService.updateProductType(c.get("db"), c.req.param("typeId"), updateProductTypeSchema.parse(await c.req.json()));
551
+ const row = await productsService.updateProductType(c.get("db"), c.req.param("typeId"), await parseJsonBody(c, updateProductTypeSchema));
550
552
  if (!row) {
551
553
  return c.json({ error: "Product type not found" }, 404);
552
554
  }
@@ -563,7 +565,7 @@ export const productRoutes = new Hono()
563
565
  // Product Categories
564
566
  // ==========================================================================
565
567
  .get("/product-categories", async (c) => {
566
- const query = productCategoryListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
568
+ const query = parseQuery(c, productCategoryListQuerySchema);
567
569
  return c.json(await productsService.listProductCategories(c.get("db"), query));
568
570
  })
569
571
  .get("/product-categories/:categoryId", async (c) => {
@@ -575,11 +577,11 @@ export const productRoutes = new Hono()
575
577
  })
576
578
  .post("/product-categories", async (c) => {
577
579
  return c.json({
578
- data: await productsService.createProductCategory(c.get("db"), insertProductCategorySchema.parse(await c.req.json())),
580
+ data: await productsService.createProductCategory(c.get("db"), await parseJsonBody(c, insertProductCategorySchema)),
579
581
  }, 201);
580
582
  })
581
583
  .patch("/product-categories/:categoryId", async (c) => {
582
- const row = await productsService.updateProductCategory(c.get("db"), c.req.param("categoryId"), updateProductCategorySchema.parse(await c.req.json()));
584
+ const row = await productsService.updateProductCategory(c.get("db"), c.req.param("categoryId"), await parseJsonBody(c, updateProductCategorySchema));
583
585
  if (!row) {
584
586
  return c.json({ error: "Product category not found" }, 404);
585
587
  }
@@ -596,7 +598,7 @@ export const productRoutes = new Hono()
596
598
  // Product Tags
597
599
  // ==========================================================================
598
600
  .get("/product-tags", async (c) => {
599
- const query = productTagListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
601
+ const query = parseQuery(c, productTagListQuerySchema);
600
602
  return c.json(await productsService.listProductTags(c.get("db"), query));
601
603
  })
602
604
  .get("/product-tags/:tagId", async (c) => {
@@ -608,11 +610,11 @@ export const productRoutes = new Hono()
608
610
  })
609
611
  .post("/product-tags", async (c) => {
610
612
  return c.json({
611
- data: await productsService.createProductTag(c.get("db"), insertProductTagSchema.parse(await c.req.json())),
613
+ data: await productsService.createProductTag(c.get("db"), await parseJsonBody(c, insertProductTagSchema)),
612
614
  }, 201);
613
615
  })
614
616
  .patch("/product-tags/:tagId", async (c) => {
615
- const row = await productsService.updateProductTag(c.get("db"), c.req.param("tagId"), updateProductTagSchema.parse(await c.req.json()));
617
+ const row = await productsService.updateProductTag(c.get("db"), c.req.param("tagId"), await parseJsonBody(c, updateProductTagSchema));
616
618
  if (!row) {
617
619
  return c.json({ error: "Product tag not found" }, 404);
618
620
  }
@@ -638,7 +640,7 @@ export const productRoutes = new Hono()
638
640
  })
639
641
  // PATCH /media/:mediaId — Update media metadata
640
642
  .patch("/media/:mediaId", async (c) => {
641
- const row = await productsService.updateMedia(c.get("db"), c.req.param("mediaId"), updateProductMediaSchema.parse(await c.req.json()));
643
+ const row = await productsService.updateMedia(c.get("db"), c.req.param("mediaId"), await parseJsonBody(c, updateProductMediaSchema));
642
644
  if (!row) {
643
645
  return c.json({ error: "Media not found" }, 404);
644
646
  }
@@ -678,7 +680,7 @@ export const productRoutes = new Hono()
678
680
  })
679
681
  // PUT /:id/brochure — Upsert canonical brochure for product
680
682
  .put("/:id/brochure", async (c) => {
681
- const row = await productsService.upsertBrochure(c.get("db"), c.req.param("id"), upsertProductBrochureSchema.parse(await c.req.json()));
683
+ const row = await productsService.upsertBrochure(c.get("db"), c.req.param("id"), await parseJsonBody(c, upsertProductBrochureSchema));
682
684
  if (!row) {
683
685
  return c.json({ error: "Product not found" }, 404);
684
686
  }
@@ -718,7 +720,7 @@ export const productRoutes = new Hono()
718
720
  })
719
721
  // PATCH /:id — Update product
720
722
  .patch("/:id", async (c) => {
721
- const row = await productsService.updateProduct(c.get("db"), c.req.param("id"), updateProductSchema.parse(await c.req.json()));
723
+ const row = await productsService.updateProduct(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductSchema));
722
724
  if (!row) {
723
725
  return c.json({ error: "Product not found" }, 404);
724
726
  }
@@ -741,7 +743,7 @@ export const productRoutes = new Hono()
741
743
  })
742
744
  // POST /:id/days — Add day to product
743
745
  .post("/:id/days", async (c) => {
744
- const row = await productsService.createDay(c.get("db"), c.req.param("id"), insertDaySchema.parse(await c.req.json()));
746
+ const row = await productsService.createDay(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertDaySchema));
745
747
  if (!row) {
746
748
  return c.json({ error: "Product not found" }, 404);
747
749
  }
@@ -749,7 +751,7 @@ export const productRoutes = new Hono()
749
751
  })
750
752
  // PATCH /:id/days/:dayId — Update day
751
753
  .patch("/:id/days/:dayId", async (c) => {
752
- const row = await productsService.updateDay(c.get("db"), c.req.param("dayId"), updateDaySchema.parse(await c.req.json()));
754
+ const row = await productsService.updateDay(c.get("db"), c.req.param("dayId"), await parseJsonBody(c, updateDaySchema));
753
755
  if (!row) {
754
756
  return c.json({ error: "Day not found" }, 404);
755
757
  }
@@ -774,7 +776,7 @@ export const productRoutes = new Hono()
774
776
  })
775
777
  // POST /:id/days/:dayId/services — Add service to day
776
778
  .post("/:id/days/:dayId/services", async (c) => {
777
- const row = await productsService.createDayService(c.get("db"), c.req.param("id"), c.req.param("dayId"), insertDayServiceSchema.parse(await c.req.json()));
779
+ const row = await productsService.createDayService(c.get("db"), c.req.param("id"), c.req.param("dayId"), await parseJsonBody(c, insertDayServiceSchema));
778
780
  if (!row) {
779
781
  return c.json({ error: "Day not found" }, 404);
780
782
  }
@@ -782,7 +784,7 @@ export const productRoutes = new Hono()
782
784
  })
783
785
  // PATCH /:id/days/:dayId/services/:serviceId — Update service
784
786
  .patch("/:id/days/:dayId/services/:serviceId", async (c) => {
785
- const row = await productsService.updateDayService(c.get("db"), c.req.param("id"), c.req.param("serviceId"), updateDayServiceSchema.parse(await c.req.json()));
787
+ const row = await productsService.updateDayService(c.get("db"), c.req.param("id"), c.req.param("serviceId"), await parseJsonBody(c, updateDayServiceSchema));
786
788
  if (!row) {
787
789
  return c.json({ error: "Service not found" }, 404);
788
790
  }
@@ -805,11 +807,15 @@ export const productRoutes = new Hono()
805
807
  })
806
808
  // POST /:id/versions — Create version snapshot
807
809
  .post("/:id/versions", async (c) => {
808
- const userId = c.get("userId");
809
- if (!userId) {
810
- return c.json({ error: "User ID required to create versions" }, 400);
811
- }
812
- const row = await productsService.createVersion(c.get("db"), c.req.param("id"), userId, insertVersionSchema.parse(await c.req.json().catch(() => ({}))));
810
+ const userId = requireUserId(c);
811
+ const row = await productsService.createVersion(c.get("db"), c.req.param("id"), userId, await parseJsonBody(c, insertVersionSchema, {
812
+ invalidJsonMessage: "Invalid JSON body",
813
+ }).catch((error) => {
814
+ if (error instanceof RequestValidationError && error.message === "Invalid JSON body") {
815
+ return {};
816
+ }
817
+ throw error;
818
+ }));
813
819
  if (!row) {
814
820
  return c.json({ error: "Product not found" }, 404);
815
821
  }
@@ -824,11 +830,8 @@ export const productRoutes = new Hono()
824
830
  })
825
831
  // POST /:id/notes — Add note to product
826
832
  .post("/:id/notes", async (c) => {
827
- const userId = c.get("userId");
828
- if (!userId) {
829
- return c.json({ error: "User ID required to create notes" }, 400);
830
- }
831
- const row = await productsService.createNote(c.get("db"), c.req.param("id"), userId, insertProductNoteSchema.parse(await c.req.json()));
833
+ const userId = requireUserId(c);
834
+ const row = await productsService.createNote(c.get("db"), c.req.param("id"), userId, await parseJsonBody(c, insertProductNoteSchema));
832
835
  if (!row) {
833
836
  return c.json({ error: "Product not found" }, 404);
834
837
  }
@@ -843,7 +846,10 @@ export const productRoutes = new Hono()
843
846
  });
844
847
  })
845
848
  .post("/:id/categories", async (c) => {
846
- const { categoryId, sortOrder } = (await c.req.json());
849
+ const { categoryId, sortOrder } = await parseJsonBody(c, z.object({
850
+ categoryId: z.string(),
851
+ sortOrder: z.number().optional(),
852
+ }));
847
853
  const row = await productsService.addProductToCategory(c.get("db"), c.req.param("id"), categoryId, sortOrder);
848
854
  if (!row) {
849
855
  return c.json({ error: "Already assigned or not found" }, 409);
@@ -866,7 +872,9 @@ export const productRoutes = new Hono()
866
872
  });
867
873
  })
868
874
  .post("/:id/tags", async (c) => {
869
- const { tagId } = (await c.req.json());
875
+ const { tagId } = await parseJsonBody(c, z.object({
876
+ tagId: z.string(),
877
+ }));
870
878
  const row = await productsService.addProductTag(c.get("db"), c.req.param("id"), tagId);
871
879
  if (!row) {
872
880
  return c.json({ error: "Already assigned or not found" }, 409);
@@ -885,12 +893,12 @@ export const productRoutes = new Hono()
885
893
  // ==========================================================================
886
894
  // GET /:id/media — List product-level media
887
895
  .get("/:id/media", async (c) => {
888
- const query = productMediaListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
896
+ const query = parseQuery(c, productMediaListQuerySchema);
889
897
  return c.json(await productsService.listProductLevelMedia(c.get("db"), c.req.param("id"), query));
890
898
  })
891
899
  // POST /:id/media — Create media for product
892
900
  .post("/:id/media", async (c) => {
893
- const row = await productsService.createMedia(c.get("db"), c.req.param("id"), insertProductMediaSchema.parse(await c.req.json()));
901
+ const row = await productsService.createMedia(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductMediaSchema));
894
902
  if (!row) {
895
903
  return c.json({ error: "Product not found or invalid dayId" }, 404);
896
904
  }
@@ -898,13 +906,13 @@ export const productRoutes = new Hono()
898
906
  })
899
907
  // POST /:id/media/reorder — Batch reorder media
900
908
  .post("/:id/media/reorder", async (c) => {
901
- const data = reorderProductMediaSchema.parse(await c.req.json());
909
+ const data = await parseJsonBody(c, reorderProductMediaSchema);
902
910
  const results = await productsService.reorderMedia(c.get("db"), data);
903
911
  return c.json({ data: results });
904
912
  })
905
913
  // GET /:id/days/:dayId/media — List day media
906
914
  .get("/:id/days/:dayId/media", async (c) => {
907
- const query = productMediaListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
915
+ const query = parseQuery(c, productMediaListQuerySchema);
908
916
  return c.json(await productsService.listMedia(c.get("db"), c.req.param("id"), {
909
917
  ...query,
910
918
  dayId: c.req.param("dayId"),
@@ -912,7 +920,7 @@ export const productRoutes = new Hono()
912
920
  })
913
921
  // POST /:id/days/:dayId/media — Create day media
914
922
  .post("/:id/days/:dayId/media", async (c) => {
915
- const body = insertProductMediaSchema.parse(await c.req.json());
923
+ const body = await parseJsonBody(c, insertProductMediaSchema);
916
924
  const row = await productsService.createMedia(c.get("db"), c.req.param("id"), {
917
925
  ...body,
918
926
  dayId: c.req.param("dayId"),