@voyantjs/products 0.52.1 → 0.52.3
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/catalog-policy.d.ts.map +1 -1
- package/dist/catalog-policy.js +18 -0
- package/dist/content-shape.d.ts +2 -0
- package/dist/content-shape.d.ts.map +1 -1
- package/dist/content-shape.js +2 -0
- 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-plane.d.ts.map +1 -1
- package/dist/service-catalog-plane.js +48 -1
- package/dist/service-catalog.d.ts +2 -2
- package/dist/service-content-owned.d.ts.map +1 -1
- package/dist/service-content-owned.js +98 -4
- 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 +46 -46
- package/dist/validation-core.d.ts.map +1 -1
- package/dist/validation-core.js +17 -1
- package/dist/validation-public.d.ts +25 -25
- package/dist/validation-shared.d.ts +11 -11
- package/package.json +13 -7
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { parseJsonBody, parseQuery } from "@voyantjs/hono";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { appendProductMutationLedgerEntry, changedMutationFields } from "./action-ledger.js";
|
|
4
|
+
import { emitProductContentChanged } from "./events.js";
|
|
5
|
+
import { productsService } from "./service.js";
|
|
6
|
+
import * as validation from "./validation.js";
|
|
7
|
+
export const productMediaRoutes = new Hono()
|
|
8
|
+
// ==========================================================================
|
|
9
|
+
// Media
|
|
10
|
+
// ==========================================================================
|
|
11
|
+
// GET /media/:mediaId — Get single media item
|
|
12
|
+
.get("/media/:mediaId", async (c) => {
|
|
13
|
+
const row = await productsService.getMediaById(c.get("db"), c.req.param("mediaId"));
|
|
14
|
+
if (!row) {
|
|
15
|
+
return c.json({ error: "Media not found" }, 404);
|
|
16
|
+
}
|
|
17
|
+
return c.json({ data: row });
|
|
18
|
+
})
|
|
19
|
+
// PATCH /media/:mediaId — Update media metadata
|
|
20
|
+
.patch("/media/:mediaId", async (c) => {
|
|
21
|
+
const mediaId = c.req.param("mediaId");
|
|
22
|
+
const body = await parseJsonBody(c, validation.updateProductMediaSchema);
|
|
23
|
+
const before = await productsService.getMediaById(c.get("db"), mediaId);
|
|
24
|
+
if (!before) {
|
|
25
|
+
return c.json({ error: "Media not found" }, 404);
|
|
26
|
+
}
|
|
27
|
+
const row = await productsService.updateMedia(c.get("db"), mediaId, body);
|
|
28
|
+
if (!row) {
|
|
29
|
+
return c.json({ error: "Media not found" }, 404);
|
|
30
|
+
}
|
|
31
|
+
await appendProductMutationLedgerEntry(c, {
|
|
32
|
+
action: "update",
|
|
33
|
+
productId: row.productId,
|
|
34
|
+
changedFields: changedMutationFields(body, before, row),
|
|
35
|
+
subject: before.dayId ? "product day media" : "product media",
|
|
36
|
+
actionName: before.dayId ? "product.day_media.update" : "product.media.update",
|
|
37
|
+
routeOrToolName: before.dayId ? "products.day_media.update" : "products.media.update",
|
|
38
|
+
});
|
|
39
|
+
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "media" });
|
|
40
|
+
return c.json({ data: row });
|
|
41
|
+
})
|
|
42
|
+
// PATCH /media/:mediaId/set-cover — Set as cover image
|
|
43
|
+
.patch("/media/:mediaId/set-cover", async (c) => {
|
|
44
|
+
const media = await productsService.getMediaById(c.get("db"), c.req.param("mediaId"));
|
|
45
|
+
if (!media) {
|
|
46
|
+
return c.json({ error: "Media not found" }, 404);
|
|
47
|
+
}
|
|
48
|
+
const row = await productsService.setCoverMedia(c.get("db"), media.productId, media.id, media.dayId);
|
|
49
|
+
if (!row) {
|
|
50
|
+
return c.json({ error: "Failed to set cover" }, 500);
|
|
51
|
+
}
|
|
52
|
+
await appendProductMutationLedgerEntry(c, {
|
|
53
|
+
action: "update",
|
|
54
|
+
productId: row.productId,
|
|
55
|
+
changedFields: ["isCover"],
|
|
56
|
+
subject: media.dayId ? "product day media" : "product media",
|
|
57
|
+
actionName: media.dayId ? "product.day_media.set_cover" : "product.media.set_cover",
|
|
58
|
+
routeOrToolName: media.dayId ? "products.day_media.set_cover" : "products.media.set_cover",
|
|
59
|
+
summary: "Set product media as cover",
|
|
60
|
+
});
|
|
61
|
+
await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "media" });
|
|
62
|
+
return c.json({ data: row });
|
|
63
|
+
})
|
|
64
|
+
// DELETE /media/:mediaId — Delete media
|
|
65
|
+
.delete("/media/:mediaId", async (c) => {
|
|
66
|
+
const mediaId = c.req.param("mediaId");
|
|
67
|
+
const before = await productsService.getMediaById(c.get("db"), mediaId);
|
|
68
|
+
if (!before) {
|
|
69
|
+
return c.json({ error: "Media not found" }, 404);
|
|
70
|
+
}
|
|
71
|
+
const row = await productsService.deleteMedia(c.get("db"), mediaId);
|
|
72
|
+
if (!row) {
|
|
73
|
+
return c.json({ error: "Media not found" }, 404);
|
|
74
|
+
}
|
|
75
|
+
await appendProductMutationLedgerEntry(c, {
|
|
76
|
+
action: "delete",
|
|
77
|
+
productId: before.productId,
|
|
78
|
+
changedFields: [],
|
|
79
|
+
subject: before.dayId ? "product day media" : "product media",
|
|
80
|
+
actionName: before.dayId ? "product.day_media.delete" : "product.media.delete",
|
|
81
|
+
routeOrToolName: before.dayId ? "products.day_media.delete" : "products.media.delete",
|
|
82
|
+
});
|
|
83
|
+
await emitProductContentChanged(c.get("eventBus"), { id: before.productId, axis: "media" });
|
|
84
|
+
return c.json({ data: row });
|
|
85
|
+
})
|
|
86
|
+
// GET /:id/brochure — Get canonical brochure for product
|
|
87
|
+
.get("/:id/brochure", async (c) => {
|
|
88
|
+
const row = await productsService.getBrochure(c.get("db"), c.req.param("id"));
|
|
89
|
+
if (!row) {
|
|
90
|
+
return c.json({ error: "Product brochure not found" }, 404);
|
|
91
|
+
}
|
|
92
|
+
return c.json({ data: row });
|
|
93
|
+
})
|
|
94
|
+
// GET /:id/brochure/versions — List brochure history for product
|
|
95
|
+
.get("/:id/brochure/versions", async (c) => {
|
|
96
|
+
return c.json({ data: await productsService.listBrochures(c.get("db"), c.req.param("id")) });
|
|
97
|
+
})
|
|
98
|
+
// PUT /:id/brochure — Upsert canonical brochure for product
|
|
99
|
+
.put("/:id/brochure", async (c) => {
|
|
100
|
+
const productId = c.req.param("id");
|
|
101
|
+
const body = await parseJsonBody(c, validation.upsertProductBrochureSchema);
|
|
102
|
+
const row = await productsService.upsertBrochure(c.get("db"), productId, body);
|
|
103
|
+
if (!row) {
|
|
104
|
+
return c.json({ error: "Product not found" }, 404);
|
|
105
|
+
}
|
|
106
|
+
await appendProductMutationLedgerEntry(c, {
|
|
107
|
+
action: "create",
|
|
108
|
+
productId,
|
|
109
|
+
changedFields: changedMutationFields(body, null, row),
|
|
110
|
+
subject: "product brochure version",
|
|
111
|
+
actionName: "product.brochure.create",
|
|
112
|
+
routeOrToolName: "products.brochure.create",
|
|
113
|
+
});
|
|
114
|
+
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
115
|
+
return c.json({ data: row }, 201);
|
|
116
|
+
})
|
|
117
|
+
// DELETE /:id/brochure — Delete canonical brochure for product
|
|
118
|
+
.delete("/:id/brochure", async (c) => {
|
|
119
|
+
const productId = c.req.param("id");
|
|
120
|
+
const row = await productsService.deleteBrochure(c.get("db"), productId);
|
|
121
|
+
if (!row) {
|
|
122
|
+
return c.json({ error: "Product brochure not found" }, 404);
|
|
123
|
+
}
|
|
124
|
+
await appendProductMutationLedgerEntry(c, {
|
|
125
|
+
action: "delete",
|
|
126
|
+
productId,
|
|
127
|
+
changedFields: [],
|
|
128
|
+
subject: "product brochure",
|
|
129
|
+
actionName: "product.brochure.delete_current",
|
|
130
|
+
routeOrToolName: "products.brochure.delete_current",
|
|
131
|
+
});
|
|
132
|
+
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
133
|
+
return c.json({ data: row });
|
|
134
|
+
})
|
|
135
|
+
// POST /:id/brochure/versions/:brochureId/set-current — Promote brochure version
|
|
136
|
+
.post("/:id/brochure/versions/:brochureId/set-current", async (c) => {
|
|
137
|
+
const productId = c.req.param("id");
|
|
138
|
+
const row = await productsService.setCurrentBrochure(c.get("db"), productId, c.req.param("brochureId"));
|
|
139
|
+
if (!row) {
|
|
140
|
+
return c.json({ error: "Product brochure version not found" }, 404);
|
|
141
|
+
}
|
|
142
|
+
await appendProductMutationLedgerEntry(c, {
|
|
143
|
+
action: "update",
|
|
144
|
+
productId,
|
|
145
|
+
changedFields: ["isBrochureCurrent"],
|
|
146
|
+
subject: "product brochure version",
|
|
147
|
+
actionName: "product.brochure.set_current",
|
|
148
|
+
routeOrToolName: "products.brochure.set_current",
|
|
149
|
+
summary: "Set current product brochure version",
|
|
150
|
+
});
|
|
151
|
+
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
152
|
+
return c.json({ data: row });
|
|
153
|
+
})
|
|
154
|
+
// DELETE /:id/brochure/versions/:brochureId — Delete brochure version
|
|
155
|
+
.delete("/:id/brochure/versions/:brochureId", async (c) => {
|
|
156
|
+
const productId = c.req.param("id");
|
|
157
|
+
const row = await productsService.deleteBrochureVersion(c.get("db"), productId, c.req.param("brochureId"));
|
|
158
|
+
if (!row) {
|
|
159
|
+
return c.json({ error: "Product brochure version not found" }, 404);
|
|
160
|
+
}
|
|
161
|
+
await appendProductMutationLedgerEntry(c, {
|
|
162
|
+
action: "delete",
|
|
163
|
+
productId,
|
|
164
|
+
changedFields: [],
|
|
165
|
+
subject: "product brochure version",
|
|
166
|
+
actionName: "product.brochure.delete_version",
|
|
167
|
+
routeOrToolName: "products.brochure.delete_version",
|
|
168
|
+
});
|
|
169
|
+
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
170
|
+
return c.json({ data: row });
|
|
171
|
+
})
|
|
172
|
+
// ==========================================================================
|
|
173
|
+
// Product Media (nested under product)
|
|
174
|
+
// ==========================================================================
|
|
175
|
+
// GET /:id/media — List product-level media
|
|
176
|
+
.get("/:id/media", async (c) => {
|
|
177
|
+
const query = parseQuery(c, validation.productMediaListQuerySchema);
|
|
178
|
+
return c.json(await productsService.listProductLevelMedia(c.get("db"), c.req.param("id"), query));
|
|
179
|
+
})
|
|
180
|
+
// POST /:id/media — Create media for product
|
|
181
|
+
.post("/:id/media", async (c) => {
|
|
182
|
+
const productId = c.req.param("id");
|
|
183
|
+
const body = await parseJsonBody(c, validation.insertProductMediaSchema);
|
|
184
|
+
const row = await productsService.createMedia(c.get("db"), productId, body);
|
|
185
|
+
if (!row) {
|
|
186
|
+
return c.json({ error: "Product not found or invalid dayId" }, 404);
|
|
187
|
+
}
|
|
188
|
+
await appendProductMutationLedgerEntry(c, {
|
|
189
|
+
action: "create",
|
|
190
|
+
productId,
|
|
191
|
+
changedFields: changedMutationFields(body, null, row),
|
|
192
|
+
subject: "product media",
|
|
193
|
+
actionName: "product.media.create",
|
|
194
|
+
routeOrToolName: "products.media.create",
|
|
195
|
+
});
|
|
196
|
+
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
197
|
+
return c.json({ data: row }, 201);
|
|
198
|
+
})
|
|
199
|
+
// POST /:id/media/reorder — Batch reorder media
|
|
200
|
+
.post("/:id/media/reorder", async (c) => {
|
|
201
|
+
const productId = c.req.param("id");
|
|
202
|
+
const data = await parseJsonBody(c, validation.reorderProductMediaSchema);
|
|
203
|
+
const results = await productsService.reorderMedia(c.get("db"), data);
|
|
204
|
+
await appendProductMutationLedgerEntry(c, {
|
|
205
|
+
action: "update",
|
|
206
|
+
productId,
|
|
207
|
+
changedFields: ["sortOrder"],
|
|
208
|
+
subject: "product media order",
|
|
209
|
+
actionName: "product.media.reorder",
|
|
210
|
+
routeOrToolName: "products.media.reorder",
|
|
211
|
+
summary: `Reordered ${results.length} product media items`,
|
|
212
|
+
});
|
|
213
|
+
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
214
|
+
return c.json({ data: results });
|
|
215
|
+
})
|
|
216
|
+
// GET /:id/days/:dayId/media — List day media
|
|
217
|
+
.get("/:id/days/:dayId/media", async (c) => {
|
|
218
|
+
const query = parseQuery(c, validation.productMediaListQuerySchema);
|
|
219
|
+
return c.json(await productsService.listMedia(c.get("db"), c.req.param("id"), {
|
|
220
|
+
...query,
|
|
221
|
+
dayId: c.req.param("dayId"),
|
|
222
|
+
}));
|
|
223
|
+
})
|
|
224
|
+
// POST /:id/days/:dayId/media — Create day media
|
|
225
|
+
.post("/:id/days/:dayId/media", async (c) => {
|
|
226
|
+
const productId = c.req.param("id");
|
|
227
|
+
const body = await parseJsonBody(c, validation.insertProductMediaSchema);
|
|
228
|
+
const row = await productsService.createMedia(c.get("db"), productId, {
|
|
229
|
+
...body,
|
|
230
|
+
dayId: c.req.param("dayId"),
|
|
231
|
+
});
|
|
232
|
+
if (!row) {
|
|
233
|
+
return c.json({ error: "Product or day not found" }, 404);
|
|
234
|
+
}
|
|
235
|
+
await appendProductMutationLedgerEntry(c, {
|
|
236
|
+
action: "create",
|
|
237
|
+
productId,
|
|
238
|
+
changedFields: changedMutationFields(body, null, row),
|
|
239
|
+
subject: "product day media",
|
|
240
|
+
actionName: "product.day_media.create",
|
|
241
|
+
routeOrToolName: "products.day_media.create",
|
|
242
|
+
});
|
|
243
|
+
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
|
|
244
|
+
return c.json({ data: row }, 201);
|
|
245
|
+
});
|