@voyantjs/products 0.52.2 → 0.52.4
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/action-ledger-drift.d.ts +29 -0
- package/dist/action-ledger-drift.d.ts.map +1 -0
- package/dist/action-ledger-drift.js +335 -0
- package/dist/action-ledger.d.ts +104 -0
- package/dist/action-ledger.d.ts.map +1 -0
- package/dist/action-ledger.js +100 -0
- package/dist/booking-extension.d.ts +3 -3
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/route-env.d.ts +22 -0
- package/dist/route-env.d.ts.map +1 -0
- package/dist/route-env.js +1 -0
- package/dist/routes-associations.d.ts +164 -0
- package/dist/routes-associations.d.ts.map +1 -0
- package/dist/routes-associations.js +100 -0
- package/dist/routes-catalog.d.ts +436 -0
- package/dist/routes-catalog.d.ts.map +1 -0
- package/dist/routes-catalog.js +104 -0
- package/dist/routes-configuration.d.ts +773 -0
- package/dist/routes-configuration.d.ts.map +1 -0
- package/dist/routes-configuration.js +364 -0
- package/dist/routes-core.d.ts +302 -0
- package/dist/routes-core.d.ts.map +1 -0
- package/dist/routes-core.js +79 -0
- package/dist/routes-itinerary.d.ts +614 -0
- package/dist/routes-itinerary.d.ts.map +1 -0
- package/dist/routes-itinerary.js +309 -0
- package/dist/routes-maintenance.d.ts +32 -0
- package/dist/routes-maintenance.d.ts.map +1 -0
- package/dist/routes-maintenance.js +14 -0
- package/dist/routes-media.d.ts +634 -0
- package/dist/routes-media.d.ts.map +1 -0
- package/dist/routes-media.js +245 -0
- package/dist/routes-merchandising.d.ts +1108 -0
- package/dist/routes-merchandising.d.ts.map +1 -0
- package/dist/routes-merchandising.js +376 -0
- package/dist/routes-options.d.ts +363 -0
- package/dist/routes-options.d.ts.map +1 -0
- package/dist/routes-options.js +173 -0
- package/dist/routes-public.d.ts +4 -4
- package/dist/routes-translations.d.ts +477 -0
- package/dist/routes-translations.d.ts.map +1 -0
- package/dist/routes-translations.js +258 -0
- package/dist/routes.d.ts +417 -355
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +21 -1133
- package/dist/schema-core.d.ts +3 -3
- package/dist/schema-itinerary.d.ts +1 -1
- package/dist/schema-settings.d.ts +4 -4
- package/dist/service-catalog.d.ts +2 -2
- package/dist/service-public.d.ts +4 -4
- package/dist/service.d.ts +225 -97
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +91 -0
- package/dist/tasks/brochures.d.ts +1 -1
- package/dist/validation-catalog.d.ts +10 -10
- package/dist/validation-config.d.ts +17 -17
- package/dist/validation-content.d.ts +26 -26
- package/dist/validation-core.d.ts +21 -21
- package/dist/validation-public.d.ts +25 -25
- package/dist/validation-shared.d.ts +11 -11
- package/package.json +13 -7
package/dist/routes.js
CHANGED
|
@@ -1,1135 +1,23 @@
|
|
|
1
|
-
import { parseJsonBody, parseQuery, RequestValidationError, requireUserId } from "@voyantjs/hono";
|
|
2
1
|
import { Hono } from "hono";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
import { productAssociationRoutes } from "./routes-associations.js";
|
|
3
|
+
import { productCatalogRoutes } from "./routes-catalog.js";
|
|
4
|
+
import { productConfigurationRoutes } from "./routes-configuration.js";
|
|
5
|
+
import { productCoreRoutes } from "./routes-core.js";
|
|
6
|
+
import { productItineraryRoutes } from "./routes-itinerary.js";
|
|
7
|
+
import { productMaintenanceRoutes } from "./routes-maintenance.js";
|
|
8
|
+
import { productMediaRoutes } from "./routes-media.js";
|
|
9
|
+
import { productMerchandisingRoutes } from "./routes-merchandising.js";
|
|
10
|
+
import { productOptionRoutes } from "./routes-options.js";
|
|
11
|
+
import { productTranslationRoutes } from "./routes-translations.js";
|
|
12
|
+
// Product route groups stay split by domain area; mount at root to preserve public paths.
|
|
10
13
|
export const productRoutes = new Hono()
|
|
11
|
-
|
|
12
|
-
.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// POST / — Create product
|
|
22
|
-
.post("/", async (c) => {
|
|
23
|
-
const row = await productsService.createProduct(c.get("db"), await parseJsonBody(c, insertProductSchema));
|
|
24
|
-
await c.get("eventBus")?.emit("product.created", { id: row.id });
|
|
25
|
-
return c.json({ data: row }, 201);
|
|
26
|
-
})
|
|
27
|
-
// ==========================================================================
|
|
28
|
-
// Product operating configuration
|
|
29
|
-
// ==========================================================================
|
|
30
|
-
.get("/activation-settings", async (c) => {
|
|
31
|
-
const query = parseQuery(c, productActivationSettingListQuerySchema);
|
|
32
|
-
return c.json(await productsService.listActivationSettings(c.get("db"), query));
|
|
33
|
-
})
|
|
34
|
-
.get("/activation-settings/:id", async (c) => {
|
|
35
|
-
const row = await productsService.getActivationSettingById(c.get("db"), c.req.param("id"));
|
|
36
|
-
if (!row) {
|
|
37
|
-
return c.json({ error: "Product activation setting not found" }, 404);
|
|
38
|
-
}
|
|
39
|
-
return c.json({ data: row });
|
|
40
|
-
})
|
|
41
|
-
.post("/:id/activation-settings", async (c) => {
|
|
42
|
-
const row = await productsService.upsertActivationSetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductActivationSettingSchema));
|
|
43
|
-
if (!row) {
|
|
44
|
-
return c.json({ error: "Product not found" }, 404);
|
|
45
|
-
}
|
|
46
|
-
return c.json({ data: row }, 201);
|
|
47
|
-
})
|
|
48
|
-
.patch("/activation-settings/:id", async (c) => {
|
|
49
|
-
const row = await productsService.updateActivationSetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductActivationSettingSchema));
|
|
50
|
-
if (!row) {
|
|
51
|
-
return c.json({ error: "Product activation setting not found" }, 404);
|
|
52
|
-
}
|
|
53
|
-
return c.json({ data: row });
|
|
54
|
-
})
|
|
55
|
-
.delete("/activation-settings/:id", async (c) => {
|
|
56
|
-
const row = await productsService.deleteActivationSetting(c.get("db"), c.req.param("id"));
|
|
57
|
-
if (!row) {
|
|
58
|
-
return c.json({ error: "Product activation setting not found" }, 404);
|
|
59
|
-
}
|
|
60
|
-
return c.json({ success: true }, 200);
|
|
61
|
-
})
|
|
62
|
-
.get("/ticket-settings", async (c) => {
|
|
63
|
-
const query = parseQuery(c, productTicketSettingListQuerySchema);
|
|
64
|
-
return c.json(await productsService.listTicketSettings(c.get("db"), query));
|
|
65
|
-
})
|
|
66
|
-
.get("/ticket-settings/:id", async (c) => {
|
|
67
|
-
const row = await productsService.getTicketSettingById(c.get("db"), c.req.param("id"));
|
|
68
|
-
if (!row) {
|
|
69
|
-
return c.json({ error: "Product ticket setting not found" }, 404);
|
|
70
|
-
}
|
|
71
|
-
return c.json({ data: row });
|
|
72
|
-
})
|
|
73
|
-
.post("/:id/ticket-settings", async (c) => {
|
|
74
|
-
const row = await productsService.upsertTicketSetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductTicketSettingSchema));
|
|
75
|
-
if (!row) {
|
|
76
|
-
return c.json({ error: "Product not found" }, 404);
|
|
77
|
-
}
|
|
78
|
-
return c.json({ data: row }, 201);
|
|
79
|
-
})
|
|
80
|
-
.patch("/ticket-settings/:id", async (c) => {
|
|
81
|
-
const row = await productsService.updateTicketSetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductTicketSettingSchema));
|
|
82
|
-
if (!row) {
|
|
83
|
-
return c.json({ error: "Product ticket setting not found" }, 404);
|
|
84
|
-
}
|
|
85
|
-
return c.json({ data: row });
|
|
86
|
-
})
|
|
87
|
-
.delete("/ticket-settings/:id", async (c) => {
|
|
88
|
-
const row = await productsService.deleteTicketSetting(c.get("db"), c.req.param("id"));
|
|
89
|
-
if (!row) {
|
|
90
|
-
return c.json({ error: "Product ticket setting not found" }, 404);
|
|
91
|
-
}
|
|
92
|
-
return c.json({ success: true }, 200);
|
|
93
|
-
})
|
|
94
|
-
.get("/visibility-settings", async (c) => {
|
|
95
|
-
const query = parseQuery(c, productVisibilitySettingListQuerySchema);
|
|
96
|
-
return c.json(await productsService.listVisibilitySettings(c.get("db"), query));
|
|
97
|
-
})
|
|
98
|
-
.get("/visibility-settings/:id", async (c) => {
|
|
99
|
-
const row = await productsService.getVisibilitySettingById(c.get("db"), c.req.param("id"));
|
|
100
|
-
if (!row) {
|
|
101
|
-
return c.json({ error: "Product visibility setting not found" }, 404);
|
|
102
|
-
}
|
|
103
|
-
return c.json({ data: row });
|
|
104
|
-
})
|
|
105
|
-
.post("/:id/visibility-settings", async (c) => {
|
|
106
|
-
const row = await productsService.upsertVisibilitySetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductVisibilitySettingSchema));
|
|
107
|
-
if (!row) {
|
|
108
|
-
return c.json({ error: "Product not found" }, 404);
|
|
109
|
-
}
|
|
110
|
-
return c.json({ data: row }, 201);
|
|
111
|
-
})
|
|
112
|
-
.patch("/visibility-settings/:id", async (c) => {
|
|
113
|
-
const row = await productsService.updateVisibilitySetting(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductVisibilitySettingSchema));
|
|
114
|
-
if (!row) {
|
|
115
|
-
return c.json({ error: "Product visibility setting not found" }, 404);
|
|
116
|
-
}
|
|
117
|
-
return c.json({ data: row });
|
|
118
|
-
})
|
|
119
|
-
.delete("/visibility-settings/:id", async (c) => {
|
|
120
|
-
const row = await productsService.deleteVisibilitySetting(c.get("db"), c.req.param("id"));
|
|
121
|
-
if (!row) {
|
|
122
|
-
return c.json({ error: "Product visibility setting not found" }, 404);
|
|
123
|
-
}
|
|
124
|
-
return c.json({ success: true }, 200);
|
|
125
|
-
})
|
|
126
|
-
.get("/capabilities", async (c) => {
|
|
127
|
-
const query = parseQuery(c, productCapabilityListQuerySchema);
|
|
128
|
-
return c.json(await productsService.listCapabilities(c.get("db"), query));
|
|
129
|
-
})
|
|
130
|
-
.get("/capabilities/:id", async (c) => {
|
|
131
|
-
const row = await productsService.getCapabilityById(c.get("db"), c.req.param("id"));
|
|
132
|
-
if (!row) {
|
|
133
|
-
return c.json({ error: "Product capability not found" }, 404);
|
|
134
|
-
}
|
|
135
|
-
return c.json({ data: row });
|
|
136
|
-
})
|
|
137
|
-
.post("/:id/capabilities", async (c) => {
|
|
138
|
-
const row = await productsService.createCapability(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductCapabilitySchema));
|
|
139
|
-
if (!row) {
|
|
140
|
-
return c.json({ error: "Product not found" }, 404);
|
|
141
|
-
}
|
|
142
|
-
return c.json({ data: row }, 201);
|
|
143
|
-
})
|
|
144
|
-
.patch("/capabilities/:id", async (c) => {
|
|
145
|
-
const row = await productsService.updateCapability(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductCapabilitySchema));
|
|
146
|
-
if (!row) {
|
|
147
|
-
return c.json({ error: "Product capability not found" }, 404);
|
|
148
|
-
}
|
|
149
|
-
return c.json({ data: row });
|
|
150
|
-
})
|
|
151
|
-
.delete("/capabilities/:id", async (c) => {
|
|
152
|
-
const row = await productsService.deleteCapability(c.get("db"), c.req.param("id"));
|
|
153
|
-
if (!row) {
|
|
154
|
-
return c.json({ error: "Product capability not found" }, 404);
|
|
155
|
-
}
|
|
156
|
-
return c.json({ success: true }, 200);
|
|
157
|
-
})
|
|
158
|
-
.get("/delivery-formats", async (c) => {
|
|
159
|
-
const query = parseQuery(c, productDeliveryFormatListQuerySchema);
|
|
160
|
-
return c.json(await productsService.listDeliveryFormats(c.get("db"), query));
|
|
161
|
-
})
|
|
162
|
-
.get("/delivery-formats/:id", async (c) => {
|
|
163
|
-
const row = await productsService.getDeliveryFormatById(c.get("db"), c.req.param("id"));
|
|
164
|
-
if (!row) {
|
|
165
|
-
return c.json({ error: "Product delivery format not found" }, 404);
|
|
166
|
-
}
|
|
167
|
-
return c.json({ data: row });
|
|
168
|
-
})
|
|
169
|
-
.post("/:id/delivery-formats", async (c) => {
|
|
170
|
-
const row = await productsService.createDeliveryFormat(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductDeliveryFormatSchema));
|
|
171
|
-
if (!row) {
|
|
172
|
-
return c.json({ error: "Product not found" }, 404);
|
|
173
|
-
}
|
|
174
|
-
return c.json({ data: row }, 201);
|
|
175
|
-
})
|
|
176
|
-
.patch("/delivery-formats/:id", async (c) => {
|
|
177
|
-
const row = await productsService.updateDeliveryFormat(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductDeliveryFormatSchema));
|
|
178
|
-
if (!row) {
|
|
179
|
-
return c.json({ error: "Product delivery format not found" }, 404);
|
|
180
|
-
}
|
|
181
|
-
return c.json({ data: row });
|
|
182
|
-
})
|
|
183
|
-
.delete("/delivery-formats/:id", async (c) => {
|
|
184
|
-
const row = await productsService.deleteDeliveryFormat(c.get("db"), c.req.param("id"));
|
|
185
|
-
if (!row) {
|
|
186
|
-
return c.json({ error: "Product delivery format not found" }, 404);
|
|
187
|
-
}
|
|
188
|
-
return c.json({ success: true }, 200);
|
|
189
|
-
})
|
|
190
|
-
.get("/features", async (c) => {
|
|
191
|
-
const query = parseQuery(c, productFeatureListQuerySchema);
|
|
192
|
-
return c.json(await productsService.listFeatures(c.get("db"), query));
|
|
193
|
-
})
|
|
194
|
-
.get("/features/:id", async (c) => {
|
|
195
|
-
const row = await productsService.getFeatureById(c.get("db"), c.req.param("id"));
|
|
196
|
-
if (!row) {
|
|
197
|
-
return c.json({ error: "Product feature not found" }, 404);
|
|
198
|
-
}
|
|
199
|
-
return c.json({ data: row });
|
|
200
|
-
})
|
|
201
|
-
.post("/:id/features", async (c) => {
|
|
202
|
-
const productId = c.req.param("id");
|
|
203
|
-
const row = await productsService.createFeature(c.get("db"), productId, await parseJsonBody(c, insertProductFeatureSchema));
|
|
204
|
-
if (!row) {
|
|
205
|
-
return c.json({ error: "Product not found" }, 404);
|
|
206
|
-
}
|
|
207
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "feature" });
|
|
208
|
-
return c.json({ data: row }, 201);
|
|
209
|
-
})
|
|
210
|
-
.patch("/features/:id", async (c) => {
|
|
211
|
-
const row = await productsService.updateFeature(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductFeatureSchema));
|
|
212
|
-
if (!row) {
|
|
213
|
-
return c.json({ error: "Product feature not found" }, 404);
|
|
214
|
-
}
|
|
215
|
-
if (row.productId) {
|
|
216
|
-
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "feature" });
|
|
217
|
-
}
|
|
218
|
-
return c.json({ data: row });
|
|
219
|
-
})
|
|
220
|
-
.delete("/features/:id", async (c) => {
|
|
221
|
-
const row = await productsService.deleteFeature(c.get("db"), c.req.param("id"));
|
|
222
|
-
if (!row) {
|
|
223
|
-
return c.json({ error: "Product feature not found" }, 404);
|
|
224
|
-
}
|
|
225
|
-
if ("productId" in row && typeof row.productId === "string") {
|
|
226
|
-
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "feature" });
|
|
227
|
-
}
|
|
228
|
-
return c.json({ success: true }, 200);
|
|
229
|
-
})
|
|
230
|
-
.get("/faqs", async (c) => {
|
|
231
|
-
const query = parseQuery(c, productFaqListQuerySchema);
|
|
232
|
-
return c.json(await productsService.listFaqs(c.get("db"), query));
|
|
233
|
-
})
|
|
234
|
-
.get("/faqs/:id", async (c) => {
|
|
235
|
-
const row = await productsService.getFaqById(c.get("db"), c.req.param("id"));
|
|
236
|
-
if (!row) {
|
|
237
|
-
return c.json({ error: "Product FAQ not found" }, 404);
|
|
238
|
-
}
|
|
239
|
-
return c.json({ data: row });
|
|
240
|
-
})
|
|
241
|
-
.post("/:id/faqs", async (c) => {
|
|
242
|
-
const productId = c.req.param("id");
|
|
243
|
-
const row = await productsService.createFaq(c.get("db"), productId, await parseJsonBody(c, insertProductFaqSchema));
|
|
244
|
-
if (!row) {
|
|
245
|
-
return c.json({ error: "Product not found" }, 404);
|
|
246
|
-
}
|
|
247
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "faq" });
|
|
248
|
-
return c.json({ data: row }, 201);
|
|
249
|
-
})
|
|
250
|
-
.patch("/faqs/:id", async (c) => {
|
|
251
|
-
const row = await productsService.updateFaq(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductFaqSchema));
|
|
252
|
-
if (!row) {
|
|
253
|
-
return c.json({ error: "Product FAQ not found" }, 404);
|
|
254
|
-
}
|
|
255
|
-
if (row.productId) {
|
|
256
|
-
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "faq" });
|
|
257
|
-
}
|
|
258
|
-
return c.json({ data: row });
|
|
259
|
-
})
|
|
260
|
-
.delete("/faqs/:id", async (c) => {
|
|
261
|
-
const row = await productsService.deleteFaq(c.get("db"), c.req.param("id"));
|
|
262
|
-
if (!row) {
|
|
263
|
-
return c.json({ error: "Product FAQ not found" }, 404);
|
|
264
|
-
}
|
|
265
|
-
if ("productId" in row && typeof row.productId === "string") {
|
|
266
|
-
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "faq" });
|
|
267
|
-
}
|
|
268
|
-
return c.json({ success: true }, 200);
|
|
269
|
-
})
|
|
270
|
-
.get("/locations", async (c) => {
|
|
271
|
-
const query = parseQuery(c, productLocationListQuerySchema);
|
|
272
|
-
return c.json(await productsService.listLocations(c.get("db"), query));
|
|
273
|
-
})
|
|
274
|
-
.get("/locations/:id", async (c) => {
|
|
275
|
-
const row = await productsService.getLocationById(c.get("db"), c.req.param("id"));
|
|
276
|
-
if (!row) {
|
|
277
|
-
return c.json({ error: "Product location not found" }, 404);
|
|
278
|
-
}
|
|
279
|
-
return c.json({ data: row });
|
|
280
|
-
})
|
|
281
|
-
.post("/:id/locations", async (c) => {
|
|
282
|
-
const productId = c.req.param("id");
|
|
283
|
-
const row = await productsService.createLocation(c.get("db"), productId, await parseJsonBody(c, insertProductLocationSchema));
|
|
284
|
-
if (!row) {
|
|
285
|
-
return c.json({ error: "Product not found" }, 404);
|
|
286
|
-
}
|
|
287
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "location" });
|
|
288
|
-
return c.json({ data: row }, 201);
|
|
289
|
-
})
|
|
290
|
-
.patch("/locations/:id", async (c) => {
|
|
291
|
-
const row = await productsService.updateLocation(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductLocationSchema));
|
|
292
|
-
if (!row) {
|
|
293
|
-
return c.json({ error: "Product location not found" }, 404);
|
|
294
|
-
}
|
|
295
|
-
if (row.productId) {
|
|
296
|
-
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "location" });
|
|
297
|
-
}
|
|
298
|
-
return c.json({ data: row });
|
|
299
|
-
})
|
|
300
|
-
.delete("/locations/:id", async (c) => {
|
|
301
|
-
const row = await productsService.deleteLocation(c.get("db"), c.req.param("id"));
|
|
302
|
-
if (!row) {
|
|
303
|
-
return c.json({ error: "Product location not found" }, 404);
|
|
304
|
-
}
|
|
305
|
-
if ("productId" in row && typeof row.productId === "string") {
|
|
306
|
-
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "location" });
|
|
307
|
-
}
|
|
308
|
-
return c.json({ success: true }, 200);
|
|
309
|
-
})
|
|
310
|
-
.get("/destinations", async (c) => {
|
|
311
|
-
const query = parseQuery(c, destinationListQuerySchema);
|
|
312
|
-
return c.json(await productsService.listDestinations(c.get("db"), query));
|
|
313
|
-
})
|
|
314
|
-
.get("/destinations/:id", async (c) => {
|
|
315
|
-
const row = await productsService.getDestinationById(c.get("db"), c.req.param("id"));
|
|
316
|
-
if (!row) {
|
|
317
|
-
return c.json({ error: "Destination not found" }, 404);
|
|
318
|
-
}
|
|
319
|
-
return c.json({ data: row });
|
|
320
|
-
})
|
|
321
|
-
.post("/destinations", async (c) => {
|
|
322
|
-
const row = await productsService.createDestination(c.get("db"), await parseJsonBody(c, insertDestinationSchema));
|
|
323
|
-
return c.json({ data: row }, 201);
|
|
324
|
-
})
|
|
325
|
-
.patch("/destinations/:id", async (c) => {
|
|
326
|
-
const row = await productsService.updateDestination(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateDestinationSchema));
|
|
327
|
-
if (!row) {
|
|
328
|
-
return c.json({ error: "Destination not found" }, 404);
|
|
329
|
-
}
|
|
330
|
-
return c.json({ data: row });
|
|
331
|
-
})
|
|
332
|
-
.delete("/destinations/:id", async (c) => {
|
|
333
|
-
const row = await productsService.deleteDestination(c.get("db"), c.req.param("id"));
|
|
334
|
-
if (!row) {
|
|
335
|
-
return c.json({ error: "Destination not found" }, 404);
|
|
336
|
-
}
|
|
337
|
-
return c.json({ success: true }, 200);
|
|
338
|
-
})
|
|
339
|
-
.get("/destination-translations", async (c) => {
|
|
340
|
-
const query = parseQuery(c, destinationTranslationListQuerySchema);
|
|
341
|
-
return c.json(await productsService.listDestinationTranslations(c.get("db"), query));
|
|
342
|
-
})
|
|
343
|
-
.post("/destinations/:id/translations", async (c) => {
|
|
344
|
-
const row = await productsService.upsertDestinationTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertDestinationTranslationSchema));
|
|
345
|
-
if (!row) {
|
|
346
|
-
return c.json({ error: "Destination not found" }, 404);
|
|
347
|
-
}
|
|
348
|
-
return c.json({ data: row }, 201);
|
|
349
|
-
})
|
|
350
|
-
.patch("/destination-translations/:id", async (c) => {
|
|
351
|
-
const row = await productsService.updateDestinationTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateDestinationTranslationSchema));
|
|
352
|
-
if (!row) {
|
|
353
|
-
return c.json({ error: "Destination translation not found" }, 404);
|
|
354
|
-
}
|
|
355
|
-
return c.json({ data: row });
|
|
356
|
-
})
|
|
357
|
-
.delete("/destination-translations/:id", async (c) => {
|
|
358
|
-
const row = await productsService.deleteDestinationTranslation(c.get("db"), c.req.param("id"));
|
|
359
|
-
if (!row) {
|
|
360
|
-
return c.json({ error: "Destination translation not found" }, 404);
|
|
361
|
-
}
|
|
362
|
-
return c.json({ success: true }, 200);
|
|
363
|
-
})
|
|
364
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
365
|
-
// Product category translations (locale-aware names + descriptions).
|
|
366
|
-
// Mirrors the destinations translation surface above. The catalog plane's
|
|
367
|
-
// taxonomy projection reads these per-slice locale and falls back to
|
|
368
|
-
// `productCategories.name` when no row exists for a given locale.
|
|
369
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
370
|
-
.get("/product-category-translations", async (c) => {
|
|
371
|
-
const query = parseQuery(c, productCategoryTranslationListQuerySchema);
|
|
372
|
-
return c.json(await productsService.listProductCategoryTranslations(c.get("db"), query));
|
|
373
|
-
})
|
|
374
|
-
.post("/product-categories/:id/translations", async (c) => {
|
|
375
|
-
const row = await productsService.upsertProductCategoryTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductCategoryTranslationSchema));
|
|
376
|
-
if (!row) {
|
|
377
|
-
return c.json({ error: "Product category not found" }, 404);
|
|
378
|
-
}
|
|
379
|
-
return c.json({ data: row }, 201);
|
|
380
|
-
})
|
|
381
|
-
.patch("/product-category-translations/:id", async (c) => {
|
|
382
|
-
const row = await productsService.updateProductCategoryTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductCategoryTranslationSchema));
|
|
383
|
-
if (!row) {
|
|
384
|
-
return c.json({ error: "Product category translation not found" }, 404);
|
|
385
|
-
}
|
|
386
|
-
return c.json({ data: row });
|
|
387
|
-
})
|
|
388
|
-
.delete("/product-category-translations/:id", async (c) => {
|
|
389
|
-
const row = await productsService.deleteProductCategoryTranslation(c.get("db"), c.req.param("id"));
|
|
390
|
-
if (!row) {
|
|
391
|
-
return c.json({ error: "Product category translation not found" }, 404);
|
|
392
|
-
}
|
|
393
|
-
return c.json({ success: true }, 200);
|
|
394
|
-
})
|
|
395
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
396
|
-
// Product tag translations. Slimmer shape — tags are short labels with no
|
|
397
|
-
// description / SEO blurbs (per #502 non-goals).
|
|
398
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
399
|
-
.get("/product-tag-translations", async (c) => {
|
|
400
|
-
const query = parseQuery(c, productTagTranslationListQuerySchema);
|
|
401
|
-
return c.json(await productsService.listProductTagTranslations(c.get("db"), query));
|
|
402
|
-
})
|
|
403
|
-
.post("/product-tags/:id/translations", async (c) => {
|
|
404
|
-
const row = await productsService.upsertProductTagTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertProductTagTranslationSchema));
|
|
405
|
-
if (!row) {
|
|
406
|
-
return c.json({ error: "Product tag not found" }, 404);
|
|
407
|
-
}
|
|
408
|
-
return c.json({ data: row }, 201);
|
|
409
|
-
})
|
|
410
|
-
.patch("/product-tag-translations/:id", async (c) => {
|
|
411
|
-
const row = await productsService.updateProductTagTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductTagTranslationSchema));
|
|
412
|
-
if (!row) {
|
|
413
|
-
return c.json({ error: "Product tag translation not found" }, 404);
|
|
414
|
-
}
|
|
415
|
-
return c.json({ data: row });
|
|
416
|
-
})
|
|
417
|
-
.delete("/product-tag-translations/:id", async (c) => {
|
|
418
|
-
const row = await productsService.deleteProductTagTranslation(c.get("db"), c.req.param("id"));
|
|
419
|
-
if (!row) {
|
|
420
|
-
return c.json({ error: "Product tag translation not found" }, 404);
|
|
421
|
-
}
|
|
422
|
-
return c.json({ success: true }, 200);
|
|
423
|
-
})
|
|
424
|
-
.get("/destination-links", async (c) => {
|
|
425
|
-
const query = parseQuery(c, productDestinationListQuerySchema);
|
|
426
|
-
return c.json(await productsService.listProductDestinations(c.get("db"), query));
|
|
427
|
-
})
|
|
428
|
-
.post("/:id/destinations", async (c) => {
|
|
429
|
-
const productId = c.req.param("id");
|
|
430
|
-
const row = await productsService.assignProductDestination(c.get("db"), productId, await parseJsonBody(c, insertProductDestinationSchema));
|
|
431
|
-
if (!row) {
|
|
432
|
-
return c.json({ error: "Product or destination not found" }, 404);
|
|
433
|
-
}
|
|
434
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "destination" });
|
|
435
|
-
return c.json({ data: row }, 201);
|
|
436
|
-
})
|
|
437
|
-
.delete("/:id/destinations/:destinationId", async (c) => {
|
|
438
|
-
const productId = c.req.param("id");
|
|
439
|
-
const row = await productsService.removeProductDestination(c.get("db"), productId, c.req.param("destinationId"));
|
|
440
|
-
if (!row) {
|
|
441
|
-
return c.json({ error: "Product destination link not found" }, 404);
|
|
442
|
-
}
|
|
443
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "destination" });
|
|
444
|
-
return c.json({ success: true }, 200);
|
|
445
|
-
})
|
|
446
|
-
// ==========================================================================
|
|
447
|
-
// Options
|
|
448
|
-
// ==========================================================================
|
|
449
|
-
// GET /options — List options
|
|
450
|
-
.get("/options", async (c) => {
|
|
451
|
-
const query = parseQuery(c, productOptionListQuerySchema);
|
|
452
|
-
return c.json(await productsService.listOptions(c.get("db"), query));
|
|
453
|
-
})
|
|
454
|
-
// GET /options/:optionId — Get single option
|
|
455
|
-
.get("/options/:optionId", async (c) => {
|
|
456
|
-
const row = await productsService.getOptionById(c.get("db"), c.req.param("optionId"));
|
|
457
|
-
if (!row) {
|
|
458
|
-
return c.json({ error: "Product option not found" }, 404);
|
|
459
|
-
}
|
|
460
|
-
return c.json({ data: row });
|
|
461
|
-
})
|
|
462
|
-
// POST /:id/options — Create option for product
|
|
463
|
-
.post("/:id/options", async (c) => {
|
|
464
|
-
const productId = c.req.param("id");
|
|
465
|
-
const row = await productsService.createOption(c.get("db"), productId, await parseJsonBody(c, insertProductOptionSchema));
|
|
466
|
-
if (!row) {
|
|
467
|
-
return c.json({ error: "Product not found" }, 404);
|
|
468
|
-
}
|
|
469
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "option" });
|
|
470
|
-
return c.json({ data: row }, 201);
|
|
471
|
-
})
|
|
472
|
-
// PATCH /options/:optionId — Update option
|
|
473
|
-
.patch("/options/:optionId", async (c) => {
|
|
474
|
-
const row = await productsService.updateOption(c.get("db"), c.req.param("optionId"), await parseJsonBody(c, updateProductOptionSchema));
|
|
475
|
-
if (!row) {
|
|
476
|
-
return c.json({ error: "Product option not found" }, 404);
|
|
477
|
-
}
|
|
478
|
-
if (row.productId) {
|
|
479
|
-
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "option" });
|
|
480
|
-
}
|
|
481
|
-
return c.json({ data: row });
|
|
482
|
-
})
|
|
483
|
-
// DELETE /options/:optionId — Delete option
|
|
484
|
-
.delete("/options/:optionId", async (c) => {
|
|
485
|
-
const row = await productsService.deleteOption(c.get("db"), c.req.param("optionId"));
|
|
486
|
-
if (!row) {
|
|
487
|
-
return c.json({ error: "Product option not found" }, 404);
|
|
488
|
-
}
|
|
489
|
-
if ("productId" in row && typeof row.productId === "string") {
|
|
490
|
-
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "option" });
|
|
491
|
-
}
|
|
492
|
-
return c.json({ success: true }, 200);
|
|
493
|
-
})
|
|
494
|
-
// ==========================================================================
|
|
495
|
-
// Option Units
|
|
496
|
-
// ==========================================================================
|
|
497
|
-
// GET /units — List units
|
|
498
|
-
.get("/units", async (c) => {
|
|
499
|
-
const query = parseQuery(c, optionUnitListQuerySchema);
|
|
500
|
-
return c.json(await productsService.listUnits(c.get("db"), query));
|
|
501
|
-
})
|
|
502
|
-
// GET /units/:unitId — Get single unit
|
|
503
|
-
.get("/units/:unitId", async (c) => {
|
|
504
|
-
const row = await productsService.getUnitById(c.get("db"), c.req.param("unitId"));
|
|
505
|
-
if (!row) {
|
|
506
|
-
return c.json({ error: "Option unit not found" }, 404);
|
|
507
|
-
}
|
|
508
|
-
return c.json({ data: row });
|
|
509
|
-
})
|
|
510
|
-
// POST /options/:optionId/units — Create unit for option
|
|
511
|
-
.post("/options/:optionId/units", async (c) => {
|
|
512
|
-
const row = await productsService.createUnit(c.get("db"), c.req.param("optionId"), await parseJsonBody(c, insertOptionUnitSchema));
|
|
513
|
-
if (!row) {
|
|
514
|
-
return c.json({ error: "Product option not found" }, 404);
|
|
515
|
-
}
|
|
516
|
-
return c.json({ data: row }, 201);
|
|
517
|
-
})
|
|
518
|
-
// PATCH /units/:unitId — Update unit
|
|
519
|
-
.patch("/units/:unitId", async (c) => {
|
|
520
|
-
const row = await productsService.updateUnit(c.get("db"), c.req.param("unitId"), await parseJsonBody(c, updateOptionUnitSchema));
|
|
521
|
-
if (!row) {
|
|
522
|
-
return c.json({ error: "Option unit not found" }, 404);
|
|
523
|
-
}
|
|
524
|
-
return c.json({ data: row });
|
|
525
|
-
})
|
|
526
|
-
// DELETE /units/:unitId — Delete unit
|
|
527
|
-
.delete("/units/:unitId", async (c) => {
|
|
528
|
-
const row = await productsService.deleteUnit(c.get("db"), c.req.param("unitId"));
|
|
529
|
-
if (!row) {
|
|
530
|
-
return c.json({ error: "Option unit not found" }, 404);
|
|
531
|
-
}
|
|
532
|
-
return c.json({ success: true }, 200);
|
|
533
|
-
})
|
|
534
|
-
// ==========================================================================
|
|
535
|
-
// Translations
|
|
536
|
-
// ==========================================================================
|
|
537
|
-
.get("/translations", async (c) => {
|
|
538
|
-
const query = parseQuery(c, productTranslationListQuerySchema);
|
|
539
|
-
return c.json(await productsService.listProductTranslations(c.get("db"), query));
|
|
540
|
-
})
|
|
541
|
-
.get("/translations/:translationId", async (c) => {
|
|
542
|
-
const row = await productsService.getProductTranslationById(c.get("db"), c.req.param("translationId"));
|
|
543
|
-
if (!row) {
|
|
544
|
-
return c.json({ error: "Product translation not found" }, 404);
|
|
545
|
-
}
|
|
546
|
-
return c.json({ data: row });
|
|
547
|
-
})
|
|
548
|
-
.post("/:id/translations", async (c) => {
|
|
549
|
-
const productId = c.req.param("id");
|
|
550
|
-
const row = await productsService.createProductTranslation(c.get("db"), productId, await parseJsonBody(c, insertProductTranslationSchema));
|
|
551
|
-
if (!row) {
|
|
552
|
-
return c.json({ error: "Product not found" }, 404);
|
|
553
|
-
}
|
|
554
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "translation" });
|
|
555
|
-
return c.json({ data: row }, 201);
|
|
556
|
-
})
|
|
557
|
-
.patch("/translations/:translationId", async (c) => {
|
|
558
|
-
const row = await productsService.updateProductTranslation(c.get("db"), c.req.param("translationId"), await parseJsonBody(c, updateProductTranslationSchema));
|
|
559
|
-
if (!row) {
|
|
560
|
-
return c.json({ error: "Product translation not found" }, 404);
|
|
561
|
-
}
|
|
562
|
-
if (row.productId) {
|
|
563
|
-
await emitProductContentChanged(c.get("eventBus"), {
|
|
564
|
-
id: row.productId,
|
|
565
|
-
axis: "translation",
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
return c.json({ data: row });
|
|
569
|
-
})
|
|
570
|
-
.delete("/translations/:translationId", async (c) => {
|
|
571
|
-
const row = await productsService.deleteProductTranslation(c.get("db"), c.req.param("translationId"));
|
|
572
|
-
if (!row) {
|
|
573
|
-
return c.json({ error: "Product translation not found" }, 404);
|
|
574
|
-
}
|
|
575
|
-
if ("productId" in row && typeof row.productId === "string") {
|
|
576
|
-
await emitProductContentChanged(c.get("eventBus"), {
|
|
577
|
-
id: row.productId,
|
|
578
|
-
axis: "translation",
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
return c.json({ success: true }, 200);
|
|
582
|
-
})
|
|
583
|
-
.get("/option-translations", async (c) => {
|
|
584
|
-
const query = parseQuery(c, productOptionTranslationListQuerySchema);
|
|
585
|
-
return c.json(await productsService.listOptionTranslations(c.get("db"), query));
|
|
586
|
-
})
|
|
587
|
-
.get("/option-translations/:translationId", async (c) => {
|
|
588
|
-
const row = await productsService.getOptionTranslationById(c.get("db"), c.req.param("translationId"));
|
|
589
|
-
if (!row) {
|
|
590
|
-
return c.json({ error: "Option translation not found" }, 404);
|
|
591
|
-
}
|
|
592
|
-
return c.json({ data: row });
|
|
593
|
-
})
|
|
594
|
-
.post("/options/:optionId/translations", async (c) => {
|
|
595
|
-
const row = await productsService.createOptionTranslation(c.get("db"), c.req.param("optionId"), await parseJsonBody(c, insertProductOptionTranslationSchema));
|
|
596
|
-
if (!row) {
|
|
597
|
-
return c.json({ error: "Product option not found" }, 404);
|
|
598
|
-
}
|
|
599
|
-
return c.json({ data: row }, 201);
|
|
600
|
-
})
|
|
601
|
-
.patch("/option-translations/:translationId", async (c) => {
|
|
602
|
-
const row = await productsService.updateOptionTranslation(c.get("db"), c.req.param("translationId"), await parseJsonBody(c, updateProductOptionTranslationSchema));
|
|
603
|
-
if (!row) {
|
|
604
|
-
return c.json({ error: "Option translation not found" }, 404);
|
|
605
|
-
}
|
|
606
|
-
return c.json({ data: row });
|
|
607
|
-
})
|
|
608
|
-
.delete("/option-translations/:translationId", async (c) => {
|
|
609
|
-
const row = await productsService.deleteOptionTranslation(c.get("db"), c.req.param("translationId"));
|
|
610
|
-
if (!row) {
|
|
611
|
-
return c.json({ error: "Option translation not found" }, 404);
|
|
612
|
-
}
|
|
613
|
-
return c.json({ success: true }, 200);
|
|
614
|
-
})
|
|
615
|
-
.get("/unit-translations", async (c) => {
|
|
616
|
-
const query = parseQuery(c, optionUnitTranslationListQuerySchema);
|
|
617
|
-
return c.json(await productsService.listUnitTranslations(c.get("db"), query));
|
|
618
|
-
})
|
|
619
|
-
.get("/unit-translations/:translationId", async (c) => {
|
|
620
|
-
const row = await productsService.getUnitTranslationById(c.get("db"), c.req.param("translationId"));
|
|
621
|
-
if (!row) {
|
|
622
|
-
return c.json({ error: "Unit translation not found" }, 404);
|
|
623
|
-
}
|
|
624
|
-
return c.json({ data: row });
|
|
625
|
-
})
|
|
626
|
-
.post("/units/:unitId/translations", async (c) => {
|
|
627
|
-
const row = await productsService.createUnitTranslation(c.get("db"), c.req.param("unitId"), await parseJsonBody(c, insertOptionUnitTranslationSchema));
|
|
628
|
-
if (!row) {
|
|
629
|
-
return c.json({ error: "Option unit not found" }, 404);
|
|
630
|
-
}
|
|
631
|
-
return c.json({ data: row }, 201);
|
|
632
|
-
})
|
|
633
|
-
.patch("/unit-translations/:translationId", async (c) => {
|
|
634
|
-
const row = await productsService.updateUnitTranslation(c.get("db"), c.req.param("translationId"), await parseJsonBody(c, updateOptionUnitTranslationSchema));
|
|
635
|
-
if (!row) {
|
|
636
|
-
return c.json({ error: "Unit translation not found" }, 404);
|
|
637
|
-
}
|
|
638
|
-
return c.json({ data: row });
|
|
639
|
-
})
|
|
640
|
-
.delete("/unit-translations/:translationId", async (c) => {
|
|
641
|
-
const row = await productsService.deleteUnitTranslation(c.get("db"), c.req.param("translationId"));
|
|
642
|
-
if (!row) {
|
|
643
|
-
return c.json({ error: "Unit translation not found" }, 404);
|
|
644
|
-
}
|
|
645
|
-
return c.json({ success: true }, 200);
|
|
646
|
-
})
|
|
647
|
-
// ==========================================================================
|
|
648
|
-
// Product Types
|
|
649
|
-
// ==========================================================================
|
|
650
|
-
.get("/product-types", async (c) => {
|
|
651
|
-
const query = parseQuery(c, productTypeListQuerySchema);
|
|
652
|
-
return c.json(await productsService.listProductTypes(c.get("db"), query));
|
|
653
|
-
})
|
|
654
|
-
.get("/product-types/:typeId", async (c) => {
|
|
655
|
-
const row = await productsService.getProductTypeById(c.get("db"), c.req.param("typeId"));
|
|
656
|
-
if (!row) {
|
|
657
|
-
return c.json({ error: "Product type not found" }, 404);
|
|
658
|
-
}
|
|
659
|
-
return c.json({ data: row });
|
|
660
|
-
})
|
|
661
|
-
.post("/product-types", async (c) => {
|
|
662
|
-
return c.json({
|
|
663
|
-
data: await productsService.createProductType(c.get("db"), await parseJsonBody(c, insertProductTypeSchema)),
|
|
664
|
-
}, 201);
|
|
665
|
-
})
|
|
666
|
-
.patch("/product-types/:typeId", async (c) => {
|
|
667
|
-
const row = await productsService.updateProductType(c.get("db"), c.req.param("typeId"), await parseJsonBody(c, updateProductTypeSchema));
|
|
668
|
-
if (!row) {
|
|
669
|
-
return c.json({ error: "Product type not found" }, 404);
|
|
670
|
-
}
|
|
671
|
-
return c.json({ data: row });
|
|
672
|
-
})
|
|
673
|
-
.delete("/product-types/:typeId", async (c) => {
|
|
674
|
-
const row = await productsService.deleteProductType(c.get("db"), c.req.param("typeId"));
|
|
675
|
-
if (!row) {
|
|
676
|
-
return c.json({ error: "Product type not found" }, 404);
|
|
677
|
-
}
|
|
678
|
-
return c.json({ success: true }, 200);
|
|
679
|
-
})
|
|
680
|
-
// ==========================================================================
|
|
681
|
-
// Product Categories
|
|
682
|
-
// ==========================================================================
|
|
683
|
-
.get("/product-categories", async (c) => {
|
|
684
|
-
const query = parseQuery(c, productCategoryListQuerySchema);
|
|
685
|
-
return c.json(await productsService.listProductCategories(c.get("db"), query));
|
|
686
|
-
})
|
|
687
|
-
.get("/product-categories/:categoryId", async (c) => {
|
|
688
|
-
const row = await productsService.getProductCategoryById(c.get("db"), c.req.param("categoryId"));
|
|
689
|
-
if (!row) {
|
|
690
|
-
return c.json({ error: "Product category not found" }, 404);
|
|
691
|
-
}
|
|
692
|
-
return c.json({ data: row });
|
|
693
|
-
})
|
|
694
|
-
.post("/product-categories", async (c) => {
|
|
695
|
-
return c.json({
|
|
696
|
-
data: await productsService.createProductCategory(c.get("db"), await parseJsonBody(c, insertProductCategorySchema)),
|
|
697
|
-
}, 201);
|
|
698
|
-
})
|
|
699
|
-
.patch("/product-categories/:categoryId", async (c) => {
|
|
700
|
-
const row = await productsService.updateProductCategory(c.get("db"), c.req.param("categoryId"), await parseJsonBody(c, updateProductCategorySchema));
|
|
701
|
-
if (!row) {
|
|
702
|
-
return c.json({ error: "Product category not found" }, 404);
|
|
703
|
-
}
|
|
704
|
-
return c.json({ data: row });
|
|
705
|
-
})
|
|
706
|
-
.delete("/product-categories/:categoryId", async (c) => {
|
|
707
|
-
const row = await productsService.deleteProductCategory(c.get("db"), c.req.param("categoryId"));
|
|
708
|
-
if (!row) {
|
|
709
|
-
return c.json({ error: "Product category not found" }, 404);
|
|
710
|
-
}
|
|
711
|
-
return c.json({ success: true }, 200);
|
|
712
|
-
})
|
|
713
|
-
// ==========================================================================
|
|
714
|
-
// Product Tags
|
|
715
|
-
// ==========================================================================
|
|
716
|
-
.get("/product-tags", async (c) => {
|
|
717
|
-
const query = parseQuery(c, productTagListQuerySchema);
|
|
718
|
-
return c.json(await productsService.listProductTags(c.get("db"), query));
|
|
719
|
-
})
|
|
720
|
-
.get("/product-tags/:tagId", async (c) => {
|
|
721
|
-
const row = await productsService.getProductTagById(c.get("db"), c.req.param("tagId"));
|
|
722
|
-
if (!row) {
|
|
723
|
-
return c.json({ error: "Product tag not found" }, 404);
|
|
724
|
-
}
|
|
725
|
-
return c.json({ data: row });
|
|
726
|
-
})
|
|
727
|
-
.post("/product-tags", async (c) => {
|
|
728
|
-
return c.json({
|
|
729
|
-
data: await productsService.createProductTag(c.get("db"), await parseJsonBody(c, insertProductTagSchema)),
|
|
730
|
-
}, 201);
|
|
731
|
-
})
|
|
732
|
-
.patch("/product-tags/:tagId", async (c) => {
|
|
733
|
-
const row = await productsService.updateProductTag(c.get("db"), c.req.param("tagId"), await parseJsonBody(c, updateProductTagSchema));
|
|
734
|
-
if (!row) {
|
|
735
|
-
return c.json({ error: "Product tag not found" }, 404);
|
|
736
|
-
}
|
|
737
|
-
return c.json({ data: row });
|
|
738
|
-
})
|
|
739
|
-
.delete("/product-tags/:tagId", async (c) => {
|
|
740
|
-
const row = await productsService.deleteProductTag(c.get("db"), c.req.param("tagId"));
|
|
741
|
-
if (!row) {
|
|
742
|
-
return c.json({ error: "Product tag not found" }, 404);
|
|
743
|
-
}
|
|
744
|
-
return c.json({ success: true }, 200);
|
|
745
|
-
})
|
|
746
|
-
// ==========================================================================
|
|
747
|
-
// Media
|
|
748
|
-
// ==========================================================================
|
|
749
|
-
// GET /media/:mediaId — Get single media item
|
|
750
|
-
.get("/media/:mediaId", async (c) => {
|
|
751
|
-
const row = await productsService.getMediaById(c.get("db"), c.req.param("mediaId"));
|
|
752
|
-
if (!row) {
|
|
753
|
-
return c.json({ error: "Media not found" }, 404);
|
|
754
|
-
}
|
|
755
|
-
return c.json({ data: row });
|
|
756
|
-
})
|
|
757
|
-
// PATCH /media/:mediaId — Update media metadata
|
|
758
|
-
.patch("/media/:mediaId", async (c) => {
|
|
759
|
-
const row = await productsService.updateMedia(c.get("db"), c.req.param("mediaId"), await parseJsonBody(c, updateProductMediaSchema));
|
|
760
|
-
if (!row) {
|
|
761
|
-
return c.json({ error: "Media not found" }, 404);
|
|
762
|
-
}
|
|
763
|
-
return c.json({ data: row });
|
|
764
|
-
})
|
|
765
|
-
// PATCH /media/:mediaId/set-cover — Set as cover image
|
|
766
|
-
.patch("/media/:mediaId/set-cover", async (c) => {
|
|
767
|
-
const media = await productsService.getMediaById(c.get("db"), c.req.param("mediaId"));
|
|
768
|
-
if (!media) {
|
|
769
|
-
return c.json({ error: "Media not found" }, 404);
|
|
770
|
-
}
|
|
771
|
-
const row = await productsService.setCoverMedia(c.get("db"), media.productId, media.id, media.dayId);
|
|
772
|
-
if (!row) {
|
|
773
|
-
return c.json({ error: "Failed to set cover" }, 500);
|
|
774
|
-
}
|
|
775
|
-
return c.json({ data: row });
|
|
776
|
-
})
|
|
777
|
-
// DELETE /media/:mediaId — Delete media
|
|
778
|
-
.delete("/media/:mediaId", async (c) => {
|
|
779
|
-
const row = await productsService.deleteMedia(c.get("db"), c.req.param("mediaId"));
|
|
780
|
-
if (!row) {
|
|
781
|
-
return c.json({ error: "Media not found" }, 404);
|
|
782
|
-
}
|
|
783
|
-
return c.json({ data: row });
|
|
784
|
-
})
|
|
785
|
-
// GET /:id/brochure — Get canonical brochure for product
|
|
786
|
-
.get("/:id/brochure", async (c) => {
|
|
787
|
-
const row = await productsService.getBrochure(c.get("db"), c.req.param("id"));
|
|
788
|
-
if (!row) {
|
|
789
|
-
return c.json({ error: "Product brochure not found" }, 404);
|
|
790
|
-
}
|
|
791
|
-
return c.json({ data: row });
|
|
792
|
-
})
|
|
793
|
-
// GET /:id/brochure/versions — List brochure history for product
|
|
794
|
-
.get("/:id/brochure/versions", async (c) => {
|
|
795
|
-
return c.json({ data: await productsService.listBrochures(c.get("db"), c.req.param("id")) });
|
|
796
|
-
})
|
|
797
|
-
// PUT /:id/brochure — Upsert canonical brochure for product
|
|
798
|
-
.put("/:id/brochure", async (c) => {
|
|
799
|
-
const row = await productsService.upsertBrochure(c.get("db"), c.req.param("id"), await parseJsonBody(c, upsertProductBrochureSchema));
|
|
800
|
-
if (!row) {
|
|
801
|
-
return c.json({ error: "Product not found" }, 404);
|
|
802
|
-
}
|
|
803
|
-
return c.json({ data: row }, 201);
|
|
804
|
-
})
|
|
805
|
-
// DELETE /:id/brochure — Delete canonical brochure for product
|
|
806
|
-
.delete("/:id/brochure", async (c) => {
|
|
807
|
-
const row = await productsService.deleteBrochure(c.get("db"), c.req.param("id"));
|
|
808
|
-
if (!row) {
|
|
809
|
-
return c.json({ error: "Product brochure not found" }, 404);
|
|
810
|
-
}
|
|
811
|
-
return c.json({ data: row });
|
|
812
|
-
})
|
|
813
|
-
// POST /:id/brochure/versions/:brochureId/set-current — Promote brochure version
|
|
814
|
-
.post("/:id/brochure/versions/:brochureId/set-current", async (c) => {
|
|
815
|
-
const row = await productsService.setCurrentBrochure(c.get("db"), c.req.param("id"), c.req.param("brochureId"));
|
|
816
|
-
if (!row) {
|
|
817
|
-
return c.json({ error: "Product brochure version not found" }, 404);
|
|
818
|
-
}
|
|
819
|
-
return c.json({ data: row });
|
|
820
|
-
})
|
|
821
|
-
// DELETE /:id/brochure/versions/:brochureId — Delete brochure version
|
|
822
|
-
.delete("/:id/brochure/versions/:brochureId", async (c) => {
|
|
823
|
-
const row = await productsService.deleteBrochureVersion(c.get("db"), c.req.param("id"), c.req.param("brochureId"));
|
|
824
|
-
if (!row) {
|
|
825
|
-
return c.json({ error: "Product brochure version not found" }, 404);
|
|
826
|
-
}
|
|
827
|
-
return c.json({ data: row });
|
|
828
|
-
})
|
|
829
|
-
// GET /:id — Get single product
|
|
830
|
-
.get("/:id", async (c) => {
|
|
831
|
-
const row = await productsService.getProductById(c.get("db"), c.req.param("id"));
|
|
832
|
-
if (!row) {
|
|
833
|
-
return c.json({ error: "Product not found" }, 404);
|
|
834
|
-
}
|
|
835
|
-
return c.json({ data: row });
|
|
836
|
-
})
|
|
837
|
-
// PATCH /:id — Update product
|
|
838
|
-
.patch("/:id", async (c) => {
|
|
839
|
-
const row = await productsService.updateProduct(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateProductSchema));
|
|
840
|
-
if (!row) {
|
|
841
|
-
return c.json({ error: "Product not found" }, 404);
|
|
842
|
-
}
|
|
843
|
-
await c.get("eventBus")?.emit("product.updated", { id: row.id });
|
|
844
|
-
await emitProductContentChanged(c.get("eventBus"), { id: row.id, axis: "product" });
|
|
845
|
-
return c.json({ data: row });
|
|
846
|
-
})
|
|
847
|
-
// DELETE /:id — Delete product
|
|
848
|
-
.delete("/:id", async (c) => {
|
|
849
|
-
const row = await productsService.deleteProduct(c.get("db"), c.req.param("id"));
|
|
850
|
-
if (!row) {
|
|
851
|
-
return c.json({ error: "Product not found" }, 404);
|
|
852
|
-
}
|
|
853
|
-
await c.get("eventBus")?.emit("product.deleted", { id: row.id });
|
|
854
|
-
return c.json({ success: true }, 200);
|
|
855
|
-
})
|
|
856
|
-
// ==========================================================================
|
|
857
|
-
// Itineraries
|
|
858
|
-
// ==========================================================================
|
|
859
|
-
.get("/:id/itineraries", async (c) => {
|
|
860
|
-
return c.json({ data: await productsService.listItineraries(c.get("db"), c.req.param("id")) });
|
|
861
|
-
})
|
|
862
|
-
.post("/:id/itineraries", async (c) => {
|
|
863
|
-
const row = await productsService.createItinerary(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertItinerarySchema));
|
|
864
|
-
if (!row) {
|
|
865
|
-
return c.json({ error: "Product not found" }, 404);
|
|
866
|
-
}
|
|
867
|
-
return c.json({ data: row }, 201);
|
|
868
|
-
})
|
|
869
|
-
.patch("/itineraries/:itineraryId", async (c) => {
|
|
870
|
-
const row = await productsService.updateItinerary(c.get("db"), c.req.param("itineraryId"), await parseJsonBody(c, updateItinerarySchema));
|
|
871
|
-
if (!row) {
|
|
872
|
-
return c.json({ error: "Itinerary not found" }, 404);
|
|
873
|
-
}
|
|
874
|
-
return c.json({ data: row });
|
|
875
|
-
})
|
|
876
|
-
.delete("/itineraries/:itineraryId", async (c) => {
|
|
877
|
-
const row = await productsService.deleteItinerary(c.get("db"), c.req.param("itineraryId"));
|
|
878
|
-
if (!row) {
|
|
879
|
-
return c.json({ error: "Itinerary not found" }, 404);
|
|
880
|
-
}
|
|
881
|
-
return c.json({ success: true }, 200);
|
|
882
|
-
})
|
|
883
|
-
.post("/itineraries/:itineraryId/duplicate", async (c) => {
|
|
884
|
-
const body = await parseJsonBody(c, duplicateItinerarySchema);
|
|
885
|
-
const row = await productsService.duplicateItinerary(c.get("db"), c.req.param("itineraryId"), body);
|
|
886
|
-
if (!row) {
|
|
887
|
-
return c.json({ error: "Itinerary not found" }, 404);
|
|
888
|
-
}
|
|
889
|
-
return c.json({ data: row }, 201);
|
|
890
|
-
})
|
|
891
|
-
// ==========================================================================
|
|
892
|
-
// Days
|
|
893
|
-
// ==========================================================================
|
|
894
|
-
.get("/:id/itineraries/:itineraryId/days", async (c) => {
|
|
895
|
-
return c.json({
|
|
896
|
-
data: await productsService.listItineraryDays(c.get("db"), c.req.param("itineraryId")),
|
|
897
|
-
});
|
|
898
|
-
})
|
|
899
|
-
.post("/:id/itineraries/:itineraryId/days", async (c) => {
|
|
900
|
-
const row = await productsService.createItineraryDay(c.get("db"), c.req.param("id"), c.req.param("itineraryId"), await parseJsonBody(c, insertDaySchema));
|
|
901
|
-
if (!row) {
|
|
902
|
-
return c.json({ error: "Itinerary not found" }, 404);
|
|
903
|
-
}
|
|
904
|
-
return c.json({ data: row }, 201);
|
|
905
|
-
})
|
|
906
|
-
// GET /:id/days — List days for product
|
|
907
|
-
.get("/:id/days", async (c) => {
|
|
908
|
-
return c.json({ data: await productsService.listDays(c.get("db"), c.req.param("id")) });
|
|
909
|
-
})
|
|
910
|
-
// POST /:id/days — Add day to product
|
|
911
|
-
.post("/:id/days", async (c) => {
|
|
912
|
-
const productId = c.req.param("id");
|
|
913
|
-
const row = await productsService.createDay(c.get("db"), productId, await parseJsonBody(c, insertDaySchema));
|
|
914
|
-
if (!row) {
|
|
915
|
-
return c.json({ error: "Product not found" }, 404);
|
|
916
|
-
}
|
|
917
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "day" });
|
|
918
|
-
return c.json({ data: row }, 201);
|
|
919
|
-
})
|
|
920
|
-
// PATCH /:id/days/:dayId — Update day
|
|
921
|
-
.patch("/:id/days/:dayId", async (c) => {
|
|
922
|
-
const productId = c.req.param("id");
|
|
923
|
-
const row = await productsService.updateDay(c.get("db"), c.req.param("dayId"), await parseJsonBody(c, updateDaySchema));
|
|
924
|
-
if (!row) {
|
|
925
|
-
return c.json({ error: "Day not found" }, 404);
|
|
926
|
-
}
|
|
927
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "day" });
|
|
928
|
-
return c.json({ data: row });
|
|
929
|
-
})
|
|
930
|
-
// DELETE /:id/days/:dayId — Delete day
|
|
931
|
-
.delete("/:id/days/:dayId", async (c) => {
|
|
932
|
-
const productId = c.req.param("id");
|
|
933
|
-
const row = await productsService.deleteDay(c.get("db"), c.req.param("dayId"));
|
|
934
|
-
if (!row) {
|
|
935
|
-
return c.json({ error: "Day not found" }, 404);
|
|
936
|
-
}
|
|
937
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "day" });
|
|
938
|
-
return c.json({ success: true }, 200);
|
|
939
|
-
})
|
|
940
|
-
// ==========================================================================
|
|
941
|
-
// Day Services
|
|
942
|
-
// ==========================================================================
|
|
943
|
-
// GET /:id/days/:dayId/services — List services for a day
|
|
944
|
-
.get("/:id/days/:dayId/services", async (c) => {
|
|
945
|
-
return c.json({
|
|
946
|
-
data: await productsService.listDayServices(c.get("db"), c.req.param("dayId")),
|
|
947
|
-
});
|
|
948
|
-
})
|
|
949
|
-
// POST /:id/days/:dayId/services — Add service to day
|
|
950
|
-
.post("/:id/days/:dayId/services", async (c) => {
|
|
951
|
-
const productId = c.req.param("id");
|
|
952
|
-
const row = await productsService.createDayService(c.get("db"), productId, c.req.param("dayId"), await parseJsonBody(c, insertDayServiceSchema));
|
|
953
|
-
if (!row) {
|
|
954
|
-
return c.json({ error: "Day not found" }, 404);
|
|
955
|
-
}
|
|
956
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "day" });
|
|
957
|
-
return c.json({ data: row }, 201);
|
|
958
|
-
})
|
|
959
|
-
// PATCH /:id/days/:dayId/services/:serviceId — Update service
|
|
960
|
-
.patch("/:id/days/:dayId/services/:serviceId", async (c) => {
|
|
961
|
-
const productId = c.req.param("id");
|
|
962
|
-
const row = await productsService.updateDayService(c.get("db"), productId, c.req.param("serviceId"), await parseJsonBody(c, updateDayServiceSchema));
|
|
963
|
-
if (!row) {
|
|
964
|
-
return c.json({ error: "Service not found" }, 404);
|
|
965
|
-
}
|
|
966
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "day" });
|
|
967
|
-
return c.json({ data: row });
|
|
968
|
-
})
|
|
969
|
-
// DELETE /:id/days/:dayId/services/:serviceId — Delete service
|
|
970
|
-
.delete("/:id/days/:dayId/services/:serviceId", async (c) => {
|
|
971
|
-
const productId = c.req.param("id");
|
|
972
|
-
const row = await productsService.deleteDayService(c.get("db"), productId, c.req.param("serviceId"));
|
|
973
|
-
if (!row) {
|
|
974
|
-
return c.json({ error: "Service not found" }, 404);
|
|
975
|
-
}
|
|
976
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "day" });
|
|
977
|
-
return c.json({ success: true }, 200);
|
|
978
|
-
})
|
|
979
|
-
// ==========================================================================
|
|
980
|
-
// Versions
|
|
981
|
-
// ==========================================================================
|
|
982
|
-
// GET /:id/versions — List versions for product
|
|
983
|
-
.get("/:id/versions", async (c) => {
|
|
984
|
-
return c.json({ data: await productsService.listVersions(c.get("db"), c.req.param("id")) });
|
|
985
|
-
})
|
|
986
|
-
// POST /:id/versions — Create version snapshot
|
|
987
|
-
.post("/:id/versions", async (c) => {
|
|
988
|
-
const userId = requireUserId(c);
|
|
989
|
-
const row = await productsService.createVersion(c.get("db"), c.req.param("id"), userId, await parseJsonBody(c, insertVersionSchema, {
|
|
990
|
-
invalidJsonMessage: "Invalid JSON body",
|
|
991
|
-
}).catch((error) => {
|
|
992
|
-
if (error instanceof RequestValidationError && error.message === "Invalid JSON body") {
|
|
993
|
-
return {};
|
|
994
|
-
}
|
|
995
|
-
throw error;
|
|
996
|
-
}));
|
|
997
|
-
if (!row) {
|
|
998
|
-
return c.json({ error: "Product not found" }, 404);
|
|
999
|
-
}
|
|
1000
|
-
return c.json({ data: row }, 201);
|
|
1001
|
-
})
|
|
1002
|
-
// ==========================================================================
|
|
1003
|
-
// Notes
|
|
1004
|
-
// ==========================================================================
|
|
1005
|
-
// GET /:id/notes — List notes for product
|
|
1006
|
-
.get("/:id/notes", async (c) => {
|
|
1007
|
-
return c.json({ data: await productsService.listNotes(c.get("db"), c.req.param("id")) });
|
|
1008
|
-
})
|
|
1009
|
-
// POST /:id/notes — Add note to product
|
|
1010
|
-
.post("/:id/notes", async (c) => {
|
|
1011
|
-
const userId = requireUserId(c);
|
|
1012
|
-
const row = await productsService.createNote(c.get("db"), c.req.param("id"), userId, await parseJsonBody(c, insertProductNoteSchema));
|
|
1013
|
-
if (!row) {
|
|
1014
|
-
return c.json({ error: "Product not found" }, 404);
|
|
1015
|
-
}
|
|
1016
|
-
return c.json({ data: row }, 201);
|
|
1017
|
-
})
|
|
1018
|
-
// ==========================================================================
|
|
1019
|
-
// Product <-> Category associations
|
|
1020
|
-
// ==========================================================================
|
|
1021
|
-
.get("/:id/categories", async (c) => {
|
|
1022
|
-
return c.json({
|
|
1023
|
-
data: await productsService.listProductCategories_(c.get("db"), c.req.param("id")),
|
|
1024
|
-
});
|
|
1025
|
-
})
|
|
1026
|
-
.post("/:id/categories", async (c) => {
|
|
1027
|
-
const productId = c.req.param("id");
|
|
1028
|
-
const { categoryId, sortOrder } = await parseJsonBody(c, z.object({
|
|
1029
|
-
categoryId: z.string(),
|
|
1030
|
-
sortOrder: z.number().optional(),
|
|
1031
|
-
}));
|
|
1032
|
-
const row = await productsService.addProductToCategory(c.get("db"), productId, categoryId, sortOrder);
|
|
1033
|
-
if (!row) {
|
|
1034
|
-
return c.json({ error: "Already assigned or not found" }, 409);
|
|
1035
|
-
}
|
|
1036
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "category" });
|
|
1037
|
-
return c.json({ success: true }, 201);
|
|
1038
|
-
})
|
|
1039
|
-
.delete("/:id/categories/:categoryId", async (c) => {
|
|
1040
|
-
const productId = c.req.param("id");
|
|
1041
|
-
const row = await productsService.removeProductFromCategory(c.get("db"), productId, c.req.param("categoryId"));
|
|
1042
|
-
if (!row) {
|
|
1043
|
-
return c.json({ error: "Association not found" }, 404);
|
|
1044
|
-
}
|
|
1045
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "category" });
|
|
1046
|
-
return c.json({ success: true }, 200);
|
|
1047
|
-
})
|
|
1048
|
-
// ==========================================================================
|
|
1049
|
-
// Product <-> Tag associations
|
|
1050
|
-
// ==========================================================================
|
|
1051
|
-
.get("/:id/tags", async (c) => {
|
|
1052
|
-
return c.json({
|
|
1053
|
-
data: await productsService.listProductTags_(c.get("db"), c.req.param("id")),
|
|
1054
|
-
});
|
|
1055
|
-
})
|
|
1056
|
-
.post("/:id/tags", async (c) => {
|
|
1057
|
-
const productId = c.req.param("id");
|
|
1058
|
-
const { tagId } = await parseJsonBody(c, z.object({
|
|
1059
|
-
tagId: z.string(),
|
|
1060
|
-
}));
|
|
1061
|
-
const row = await productsService.addProductTag(c.get("db"), productId, tagId);
|
|
1062
|
-
if (!row) {
|
|
1063
|
-
return c.json({ error: "Already assigned or not found" }, 409);
|
|
1064
|
-
}
|
|
1065
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "tag" });
|
|
1066
|
-
return c.json({ success: true }, 201);
|
|
1067
|
-
})
|
|
1068
|
-
.delete("/:id/tags/:tagId", async (c) => {
|
|
1069
|
-
const productId = c.req.param("id");
|
|
1070
|
-
const row = await productsService.removeProductTag(c.get("db"), productId, c.req.param("tagId"));
|
|
1071
|
-
if (!row) {
|
|
1072
|
-
return c.json({ error: "Association not found" }, 404);
|
|
1073
|
-
}
|
|
1074
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "tag" });
|
|
1075
|
-
return c.json({ success: true }, 200);
|
|
1076
|
-
})
|
|
1077
|
-
// ==========================================================================
|
|
1078
|
-
// Product Media (nested under product)
|
|
1079
|
-
// ==========================================================================
|
|
1080
|
-
// GET /:id/media — List product-level media
|
|
1081
|
-
.get("/:id/media", async (c) => {
|
|
1082
|
-
const query = parseQuery(c, productMediaListQuerySchema);
|
|
1083
|
-
return c.json(await productsService.listProductLevelMedia(c.get("db"), c.req.param("id"), query));
|
|
1084
|
-
})
|
|
1085
|
-
// POST /:id/media — Create media for product
|
|
1086
|
-
.post("/:id/media", async (c) => {
|
|
1087
|
-
const productId = c.req.param("id");
|
|
1088
|
-
const row = await productsService.createMedia(c.get("db"), productId, await parseJsonBody(c, insertProductMediaSchema));
|
|
1089
|
-
if (!row) {
|
|
1090
|
-
return c.json({ error: "Product not found or invalid dayId" }, 404);
|
|
1091
|
-
}
|
|
1092
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
1093
|
-
return c.json({ data: row }, 201);
|
|
1094
|
-
})
|
|
1095
|
-
// POST /:id/media/reorder — Batch reorder media
|
|
1096
|
-
.post("/:id/media/reorder", async (c) => {
|
|
1097
|
-
const productId = c.req.param("id");
|
|
1098
|
-
const data = await parseJsonBody(c, reorderProductMediaSchema);
|
|
1099
|
-
const results = await productsService.reorderMedia(c.get("db"), data);
|
|
1100
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
1101
|
-
return c.json({ data: results });
|
|
1102
|
-
})
|
|
1103
|
-
// GET /:id/days/:dayId/media — List day media
|
|
1104
|
-
.get("/:id/days/:dayId/media", async (c) => {
|
|
1105
|
-
const query = parseQuery(c, productMediaListQuerySchema);
|
|
1106
|
-
return c.json(await productsService.listMedia(c.get("db"), c.req.param("id"), {
|
|
1107
|
-
...query,
|
|
1108
|
-
dayId: c.req.param("dayId"),
|
|
1109
|
-
}));
|
|
1110
|
-
})
|
|
1111
|
-
// POST /:id/days/:dayId/media — Create day media
|
|
1112
|
-
.post("/:id/days/:dayId/media", async (c) => {
|
|
1113
|
-
const productId = c.req.param("id");
|
|
1114
|
-
const body = await parseJsonBody(c, insertProductMediaSchema);
|
|
1115
|
-
const row = await productsService.createMedia(c.get("db"), productId, {
|
|
1116
|
-
...body,
|
|
1117
|
-
dayId: c.req.param("dayId"),
|
|
1118
|
-
});
|
|
1119
|
-
if (!row) {
|
|
1120
|
-
return c.json({ error: "Product or day not found" }, 404);
|
|
1121
|
-
}
|
|
1122
|
-
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
1123
|
-
return c.json({ data: row }, 201);
|
|
1124
|
-
})
|
|
1125
|
-
// ==========================================================================
|
|
1126
|
-
// Recalculate
|
|
1127
|
-
// ==========================================================================
|
|
1128
|
-
// POST /:id/recalculate — Recalculate product cost and margin
|
|
1129
|
-
.post("/:id/recalculate", async (c) => {
|
|
1130
|
-
const result = await productsService.recalculate(c.get("db"), c.req.param("id"));
|
|
1131
|
-
if (!result) {
|
|
1132
|
-
return c.json({ error: "Product not found" }, 404);
|
|
1133
|
-
}
|
|
1134
|
-
return c.json({ data: result });
|
|
1135
|
-
});
|
|
14
|
+
.route("/", productConfigurationRoutes)
|
|
15
|
+
.route("/", productMerchandisingRoutes)
|
|
16
|
+
.route("/", productOptionRoutes)
|
|
17
|
+
.route("/", productTranslationRoutes)
|
|
18
|
+
.route("/", productCatalogRoutes)
|
|
19
|
+
.route("/", productMediaRoutes)
|
|
20
|
+
.route("/", productItineraryRoutes)
|
|
21
|
+
.route("/", productAssociationRoutes)
|
|
22
|
+
.route("/", productMaintenanceRoutes)
|
|
23
|
+
.route("/", productCoreRoutes);
|