@open-mercato/core 0.7.1-develop.7130.1.fef2396fd8 → 0.7.1-develop.7135.1.19bf969756
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/.turbo/turbo-build.log +1 -1
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/auth/api/profile/route.js +5 -0
- package/dist/modules/auth/api/profile/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +40 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/lib/access-control.js +6 -1
- package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
- package/dist/modules/communication_channels/lib/system-user.js +8 -3
- package/dist/modules/communication_channels/lib/system-user.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +13 -0
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +9 -0
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js +5 -0
- package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js +5 -0
- package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
- package/dist/modules/customers/api/labels/assign/route.js +5 -0
- package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
- package/dist/modules/customers/api/labels/route.js +5 -0
- package/dist/modules/customers/api/labels/route.js.map +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js +5 -0
- package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +9 -0
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/route.js +5 -0
- package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +5 -0
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/route.js +13 -0
- package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
- package/dist/modules/customers/api/pipelines/route.js +13 -0
- package/dist/modules/customers/api/pipelines/route.js.map +2 -2
- package/dist/modules/customers/api/settings/address-format/route.js +5 -0
- package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +5 -0
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
- package/dist/modules/customers/api/settings/stuck-threshold/route.js +5 -0
- package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
- package/dist/modules/customers/api/tags/assign/route.js +5 -0
- package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js +5 -0
- package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +13 -0
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +9 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +5 -0
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
- package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +5 -0
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +5 -0
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/archive/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/read/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/read/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/forward/route.js +5 -0
- package/dist/modules/messages/api/[id]/forward/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/reply/route.js +5 -0
- package/dist/modules/messages/api/[id]/reply/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +9 -0
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/token/[token]/route.js +5 -0
- package/dist/modules/messages/api/token/[token]/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js +5 -0
- package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js +5 -0
- package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js +5 -0
- package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/settings/order-editing/route.js +5 -0
- package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/self/route.js +5 -0
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +5 -0
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +9 -0
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js +25 -18
- package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/assign/route.js +5 -0
- package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js +16 -12
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +6 -1
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js +24 -27
- package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js +8 -10
- package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js +4 -9
- package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +8 -3
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +8 -3
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +9 -9
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js +17 -13
- package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +19 -15
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/registrations/route.js +9 -4
- package/dist/modules/warranty_claims/api/registrations/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +16 -12
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +25 -19
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js +2 -1
- package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +16 -12
- package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/settings-general/route.js +5 -0
- package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/submit/route.js +5 -0
- package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/transition/route.js +5 -0
- package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js +22 -1
- package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js +5 -0
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -1
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
- package/dist/modules/warranty_claims/commands/shared.js +2 -2
- package/dist/modules/warranty_claims/commands/shared.js.map +2 -2
- package/dist/modules/warranty_claims/lib/portalAuthGuard.js +31 -0
- package/dist/modules/warranty_claims/lib/portalAuthGuard.js.map +7 -0
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +5 -0
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
- package/package.json +7 -7
- package/src/helpers/integration/communicationChannelsFixtures.ts +13 -0
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +5 -0
- package/src/modules/auth/api/profile/route.ts +5 -0
- package/src/modules/communication_channels/api/post/test-seed/route.ts +47 -2
- package/src/modules/communication_channels/lib/access-control.ts +28 -0
- package/src/modules/communication_channels/lib/system-user.ts +40 -22
- package/src/modules/customers/api/activities/route.ts +13 -0
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -0
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +5 -0
- package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -0
- package/src/modules/customers/api/interactions/cancel/route.ts +5 -0
- package/src/modules/customers/api/interactions/complete/route.ts +5 -0
- package/src/modules/customers/api/labels/assign/route.ts +5 -0
- package/src/modules/customers/api/labels/route.ts +5 -0
- package/src/modules/customers/api/labels/unassign/route.ts +5 -0
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +9 -0
- package/src/modules/customers/api/people/[id]/companies/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/route.ts +13 -0
- package/src/modules/customers/api/pipelines/route.ts +13 -0
- package/src/modules/customers/api/settings/address-format/route.ts +5 -0
- package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -0
- package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -0
- package/src/modules/customers/api/tags/assign/route.ts +5 -0
- package/src/modules/customers/api/tags/unassign/route.ts +5 -0
- package/src/modules/customers/api/todos/route.ts +13 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +9 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +5 -0
- package/src/modules/directory/api/organization-branding/route.ts +5 -0
- package/src/modules/eudr/api/plots/import/route.ts +7 -0
- package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +7 -0
- package/src/modules/feature_toggles/api/overrides/route.ts +5 -0
- package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/archive/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/read/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/route.ts +5 -0
- package/src/modules/messages/api/[id]/forward/route.ts +5 -0
- package/src/modules/messages/api/[id]/reply/route.ts +5 -0
- package/src/modules/messages/api/[id]/route.ts +9 -0
- package/src/modules/messages/api/token/[token]/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/assign/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/unassign/route.ts +5 -0
- package/src/modules/sales/api/quotes/accept/route.ts +5 -0
- package/src/modules/sales/api/quotes/convert/route.ts +5 -0
- package/src/modules/sales/api/settings/document-numbers/route.ts +5 -0
- package/src/modules/sales/api/settings/order-editing/route.ts +5 -0
- package/src/modules/staff/api/leave-requests/accept/route.ts +5 -0
- package/src/modules/staff/api/leave-requests/reject/route.ts +5 -0
- package/src/modules/staff/api/team-members/self/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/assign/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +5 -0
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +5 -0
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +9 -0
- package/src/modules/warranty_claims/api/ai/assess/route.ts +28 -17
- package/src/modules/warranty_claims/api/assign/route.ts +5 -0
- package/src/modules/warranty_claims/api/credit-memo/route.ts +19 -13
- package/src/modules/warranty_claims/api/events/route.ts +6 -1
- package/src/modules/warranty_claims/api/portal/attachments/route.ts +29 -27
- package/src/modules/warranty_claims/api/portal/claims/[id]/route.ts +9 -10
- package/src/modules/warranty_claims/api/portal/claims/[id]/shared.ts +5 -9
- package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +8 -3
- package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +8 -3
- package/src/modules/warranty_claims/api/portal/claims/route.ts +10 -9
- package/src/modules/warranty_claims/api/portal/events/route.ts +18 -13
- package/src/modules/warranty_claims/api/receiving/route.ts +22 -15
- package/src/modules/warranty_claims/api/registrations/route.ts +9 -3
- package/src/modules/warranty_claims/api/replacement-order/route.ts +19 -13
- package/src/modules/warranty_claims/api/return-label/route.ts +26 -17
- package/src/modules/warranty_claims/api/risk/route.ts +3 -1
- package/src/modules/warranty_claims/api/sales-return/route.ts +19 -13
- package/src/modules/warranty_claims/api/settings-general/route.ts +5 -0
- package/src/modules/warranty_claims/api/submit/route.ts +5 -0
- package/src/modules/warranty_claims/api/transition/route.ts +5 -0
- package/src/modules/warranty_claims/api/troubleshooting-guides/route.ts +23 -1
- package/src/modules/warranty_claims/api/vendor-recovery/route.ts +5 -0
- package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +3 -1
- package/src/modules/warranty_claims/commands/shared.ts +7 -1
- package/src/modules/warranty_claims/lib/portalAuthGuard.ts +30 -0
- package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +5 -0
|
@@ -43,7 +43,9 @@ import {
|
|
|
43
43
|
* (delegates to the real connect-credential command so the channel persists
|
|
44
44
|
* credentials + lands in `status='connected'`). Enables the outbound
|
|
45
45
|
* compose → deliver → `.sent` chain to complete in CI. `providerFlavor: 'chat'`
|
|
46
|
-
* connects the non-email stub instead, for tests about sender identity
|
|
46
|
+
* connects the non-email stub instead, for tests about sender identity, and
|
|
47
|
+
* `labelAsProviderKey` relabels the connected row so a provider-scoped
|
|
48
|
+
* listing has something to list without a live credential probe.
|
|
47
49
|
* - `ingest-inbound`: run the REAL `ingest_inbound_message` command over an
|
|
48
50
|
* adapter-normalized chat frame, so the platform compose path — and every
|
|
49
51
|
* validation rule on it — actually executes. Use this for anything that
|
|
@@ -88,6 +90,27 @@ const connectChannelSchema = z.object({
|
|
|
88
90
|
* without inventing one (#4975).
|
|
89
91
|
*/
|
|
90
92
|
providerFlavor: z.enum(['email', 'chat']).optional(),
|
|
93
|
+
/**
|
|
94
|
+
* TEST-ONLY: rewrite the connected stub channel's `provider_key` to this value.
|
|
95
|
+
*
|
|
96
|
+
* Routes that a provider package owns filter on their own `provider_key`, and
|
|
97
|
+
* a real channel for those providers cannot be connected in CI — the Discord
|
|
98
|
+
* adapter's `validateCredentials` performs a live API call against a bot token
|
|
99
|
+
* no test environment has. Without this, a provider-scoped listing has nothing
|
|
100
|
+
* to list and can only be asserted against an empty result, which is exactly
|
|
101
|
+
* the assertion that would have stayed green through #5602.
|
|
102
|
+
*
|
|
103
|
+
* It relabels a row; it registers nothing. The stub adapter is still the only
|
|
104
|
+
* adapter present, so the channel remains network-free — but no test should
|
|
105
|
+
* drive an outbound send through a relabelled channel, because delivery would
|
|
106
|
+
* then resolve the real provider's adapter.
|
|
107
|
+
*/
|
|
108
|
+
labelAsProviderKey: z
|
|
109
|
+
.string()
|
|
110
|
+
.min(1)
|
|
111
|
+
.max(64)
|
|
112
|
+
.regex(/^[a-z0-9_-]+$/)
|
|
113
|
+
.optional(),
|
|
91
114
|
})
|
|
92
115
|
|
|
93
116
|
/**
|
|
@@ -224,8 +247,30 @@ export async function POST(req: Request): Promise<Response> {
|
|
|
224
247
|
{ status: 500 },
|
|
225
248
|
)
|
|
226
249
|
}
|
|
250
|
+
if (body.labelAsProviderKey && body.labelAsProviderKey !== input.providerKey) {
|
|
251
|
+
const seedEm = (container.resolve('em') as EntityManager).fork()
|
|
252
|
+
const connected = await findOneWithDecryption(
|
|
253
|
+
seedEm,
|
|
254
|
+
CommunicationChannel,
|
|
255
|
+
{ id: result.channelId, tenantId },
|
|
256
|
+
undefined,
|
|
257
|
+
{ tenantId, organizationId },
|
|
258
|
+
)
|
|
259
|
+
if (!connected) {
|
|
260
|
+
return NextResponse.json(
|
|
261
|
+
{ error: '[internal] test-seed could not reload the channel it just connected' },
|
|
262
|
+
{ status: 500 },
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
connected.providerKey = body.labelAsProviderKey
|
|
266
|
+
await seedEm.flush()
|
|
267
|
+
}
|
|
227
268
|
return NextResponse.json(
|
|
228
|
-
{
|
|
269
|
+
{
|
|
270
|
+
channelId: result.channelId,
|
|
271
|
+
externalIdentifier: result.externalIdentifier,
|
|
272
|
+
providerKey: body.labelAsProviderKey ?? input.providerKey,
|
|
273
|
+
},
|
|
229
274
|
{ status: 201 },
|
|
230
275
|
)
|
|
231
276
|
}
|
|
@@ -59,6 +59,34 @@ export function channelOrgScopeWhereFromFilter(
|
|
|
59
59
|
return { $or: [{ organizationId: { $in: organizationIds } }, { organizationId: null }] }
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Ownership fragment for LISTING channels — the SQL-layer twin of
|
|
64
|
+
* {@link assertCanAccessChannel}.
|
|
65
|
+
*
|
|
66
|
+
* Routes have historically hardcoded one of the two halves of the rule: the
|
|
67
|
+
* admin channel list narrows to `user_id IS NULL`, the profile list narrows to
|
|
68
|
+
* `user_id = <caller>`. A provider-scoped listing needs both, because which half
|
|
69
|
+
* a channel falls into is decided by the connect flow the provider offers, not
|
|
70
|
+
* by the listing. Hardcoding `userId: null` is what made the Discord AI
|
|
71
|
+
* auto-reply panel structurally empty (#5602): every route the product exposes
|
|
72
|
+
* for connecting a Discord bot writes `user_id = auth.sub`, so the filter
|
|
73
|
+
* excluded the only channels that can exist.
|
|
74
|
+
*
|
|
75
|
+
* The clause admits exactly what the assert allows and nothing more — shared
|
|
76
|
+
* channels, plus the caller's own personal ones. It grants no admin bypass, in
|
|
77
|
+
* line with v1 strict owner-only: a personal mailbox stays invisible to everyone
|
|
78
|
+
* but its owner. An anonymous caller (no user id) sees shared channels only.
|
|
79
|
+
*
|
|
80
|
+
* Spread into a MikroORM `where`, or nest under `$and` when another fragment in
|
|
81
|
+
* the same query already claims `$or` (`channelOrgScopeWhereFromFilter` does).
|
|
82
|
+
*/
|
|
83
|
+
export function channelOwnerScopeWhere(
|
|
84
|
+
currentUserId: string | null | undefined,
|
|
85
|
+
): Record<string, unknown> {
|
|
86
|
+
if (!currentUserId) return { userId: null }
|
|
87
|
+
return { $or: [{ userId: null }, { userId: currentUserId }] }
|
|
88
|
+
}
|
|
89
|
+
|
|
62
90
|
/**
|
|
63
91
|
* Throws when the caller may not access a specific channel. Returns silently
|
|
64
92
|
* when access is allowed.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
+
import { lookupHashCandidates } from '@open-mercato/shared/lib/encryption/aes'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Sentinel UUID — used as a last-resort `senderUserId` for inbound channel
|
|
@@ -31,6 +32,12 @@ export function systemUserEmail(tenantId: string): string {
|
|
|
31
32
|
* sentinel. The inbound-processor must never refuse to ingest a message
|
|
32
33
|
* because the channel-bot user doesn't exist.
|
|
33
34
|
*
|
|
35
|
+
* That fail-soft design is also what hid #5599 for as long as it did — a broken
|
|
36
|
+
* lookup and an absent user produce the identical outcome — so the two
|
|
37
|
+
* properties the lookup depends on (the column it matches and the table it
|
|
38
|
+
* names) are pinned by `__tests__/system-user.test.ts` rather than left to the
|
|
39
|
+
* next reader to notice.
|
|
40
|
+
*
|
|
34
41
|
* @param em EntityManager scoped to the tenant.
|
|
35
42
|
* @param tenantId Tenant id for which to resolve the system user.
|
|
36
43
|
* @param fallbackId Optional caller-supplied fallback (e.g., the channel's
|
|
@@ -43,28 +50,39 @@ export async function resolveCommunicationChannelsSystemUserId(
|
|
|
43
50
|
): Promise<string> {
|
|
44
51
|
try {
|
|
45
52
|
const expectedEmail = systemUserEmail(tenantId)
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
53
|
+
// Match on `email_hash`, never on `email` (#5599). `users.email` is
|
|
54
|
+
// encrypted at rest with a per-row IV, so its ciphertext is
|
|
55
|
+
// non-deterministic and an equality filter against the plaintext can never
|
|
56
|
+
// hit — which is exactly why the tenant uniqueness index keys on
|
|
57
|
+
// `email_hash` instead (see `auth/data/entities.ts`). The candidate list
|
|
58
|
+
// covers both the keyed `v2:` digest and the legacy unkeyed one, so a
|
|
59
|
+
// deployment that has not yet backfilled still matches.
|
|
60
|
+
//
|
|
61
|
+
// The digests must be computed the same way `auth` writes them
|
|
62
|
+
// (`auth/lib/emailHash.ts` → `hashForLookup(email)`), i.e. with NO hash
|
|
63
|
+
// context. Reusing the shared primitive rather than importing the auth
|
|
64
|
+
// helper keeps this module free of a cross-module code dependency; the two
|
|
65
|
+
// must stay aligned if `auth` ever adopts a context.
|
|
66
|
+
const emailHashes = lookupHashCandidates(expectedEmail)
|
|
67
|
+
|
|
68
|
+
// Raw SQL against the table, not `createQueryBuilder('auth.users')`. That
|
|
69
|
+
// call names no registered entity — entities are discovered under their
|
|
70
|
+
// class names — so MikroORM reads the dot as a schema qualifier and looks
|
|
71
|
+
// for `users` in a schema named `auth`, which this project never creates
|
|
72
|
+
// (`Migration20251030150038` puts `users` in the default schema). The helper
|
|
73
|
+
// swallows every error by design, so the miss was indistinguishable from
|
|
74
|
+
// "no channel-bot user exists" and hid behind the same fallback as the
|
|
75
|
+
// plaintext-vs-ciphertext defect above. A parameterized statement names the
|
|
76
|
+
// table unambiguously and still pulls in no cross-module entity class,
|
|
77
|
+
// keeping the decoupling this helper was written for.
|
|
78
|
+
const placeholders = emailHashes.map(() => '?').join(', ')
|
|
79
|
+
const rows = await em
|
|
80
|
+
.getConnection()
|
|
81
|
+
.execute<Array<{ id?: string }>>(
|
|
82
|
+
`SELECT id FROM users WHERE email_hash IN (${placeholders}) AND tenant_id = ? AND deleted_at IS NULL LIMIT 1`,
|
|
83
|
+
[...emailHashes, tenantId],
|
|
84
|
+
)
|
|
85
|
+
const id = Array.isArray(rows) ? rows[0]?.id : undefined
|
|
68
86
|
if (typeof id === 'string' && id.length > 0) return id
|
|
69
87
|
} catch {
|
|
70
88
|
// ignore — fall through to fallback
|
|
@@ -31,6 +31,7 @@ import { hydrateCanonicalInteractions } from '../../lib/interactionReadModel'
|
|
|
31
31
|
import { resolveCanonicalActivityTargetId } from '../../lib/legacyActivityBridge'
|
|
32
32
|
import { buildEmailVisibilityMikroFilter } from '../../lib/visibilityFilter'
|
|
33
33
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
34
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
34
35
|
|
|
35
36
|
const logger = createLogger('customers')
|
|
36
37
|
|
|
@@ -620,6 +621,10 @@ export async function POST(request: Request): Promise<Response> {
|
|
|
620
621
|
if (isCrudHttpError(err)) {
|
|
621
622
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))
|
|
622
623
|
}
|
|
624
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
625
|
+
if (interceptorRejection) {
|
|
626
|
+
return withAdapterHeaders(NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status }))
|
|
627
|
+
}
|
|
623
628
|
if (err instanceof z.ZodError) {
|
|
624
629
|
return withAdapterHeaders(
|
|
625
630
|
NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),
|
|
@@ -703,6 +708,10 @@ export async function PUT(request: Request): Promise<Response> {
|
|
|
703
708
|
if (isCrudHttpError(err)) {
|
|
704
709
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))
|
|
705
710
|
}
|
|
711
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
712
|
+
if (interceptorRejection) {
|
|
713
|
+
return withAdapterHeaders(NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status }))
|
|
714
|
+
}
|
|
706
715
|
if (err instanceof z.ZodError) {
|
|
707
716
|
return withAdapterHeaders(
|
|
708
717
|
NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),
|
|
@@ -771,6 +780,10 @@ export async function DELETE(request: Request): Promise<Response> {
|
|
|
771
780
|
if (isCrudHttpError(err)) {
|
|
772
781
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))
|
|
773
782
|
}
|
|
783
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
784
|
+
if (interceptorRejection) {
|
|
785
|
+
return withAdapterHeaders(NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status }))
|
|
786
|
+
}
|
|
774
787
|
if (err instanceof z.ZodError) {
|
|
775
788
|
return withAdapterHeaders(
|
|
776
789
|
NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
validateCrudMutationGuard,
|
|
17
17
|
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
18
18
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
19
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
19
20
|
|
|
20
21
|
const logger = createLogger('customers')
|
|
21
22
|
|
|
@@ -175,6 +176,10 @@ export async function PATCH(req: Request, ctx: { params?: { kind?: string; id?:
|
|
|
175
176
|
if (isCrudHttpError(err)) {
|
|
176
177
|
return NextResponse.json(err.body, { status: err.status })
|
|
177
178
|
}
|
|
179
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
180
|
+
if (interceptorRejection) {
|
|
181
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
182
|
+
}
|
|
178
183
|
const { translate } = await resolveTranslations()
|
|
179
184
|
logger.error('customers.dictionaries.update failed', { err })
|
|
180
185
|
return NextResponse.json({ error: translate('customers.errors.lookup_failed', 'Failed to save dictionary entry') }, { status: 400 })
|
|
@@ -276,6 +281,10 @@ export async function DELETE(req: Request, ctx: { params?: { kind?: string; id?:
|
|
|
276
281
|
if (isCrudHttpError(err)) {
|
|
277
282
|
return NextResponse.json(err.body, { status: err.status })
|
|
278
283
|
}
|
|
284
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
285
|
+
if (interceptorRejection) {
|
|
286
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
287
|
+
}
|
|
279
288
|
const { translate } = await resolveTranslations()
|
|
280
289
|
logger.error('customers.dictionaries.delete failed', { err })
|
|
281
290
|
return NextResponse.json({ error: translate('customers.errors.lookup_failed', 'Failed to delete dictionary entry') }, { status: 400 })
|
|
@@ -23,6 +23,7 @@ import { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/
|
|
|
23
23
|
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
24
24
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
25
25
|
import { CUSTOMER_DICTIONARY_ORGANIZATION_REQUIRED_CODE } from '../../../lib/dictionaries'
|
|
26
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
26
27
|
|
|
27
28
|
const logger = createLogger('customers')
|
|
28
29
|
|
|
@@ -271,6 +272,10 @@ export async function POST(req: Request, ctx: { params?: { kind?: string } }) {
|
|
|
271
272
|
if (isCrudHttpError(err)) {
|
|
272
273
|
return NextResponse.json(err.body, { status: err.status })
|
|
273
274
|
}
|
|
275
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
276
|
+
if (interceptorRejection) {
|
|
277
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
278
|
+
}
|
|
274
279
|
const { translate } = await resolveTranslations()
|
|
275
280
|
logger.error('customers.dictionaries.create failed', { err })
|
|
276
281
|
return NextResponse.json({ error: translate('customers.errors.lookup_failed', 'Failed to save dictionary entry') }, { status: 400 })
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type CustomerKindSettingsUpsertInput,
|
|
21
21
|
} from '../../../data/validators'
|
|
22
22
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
23
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
23
24
|
|
|
24
25
|
const logger = createLogger('customers')
|
|
25
26
|
|
|
@@ -186,6 +187,10 @@ export async function PATCH(req: Request) {
|
|
|
186
187
|
if (isCrudHttpError(err)) {
|
|
187
188
|
return NextResponse.json(err.body, { status: err.status })
|
|
188
189
|
}
|
|
190
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
191
|
+
if (interceptorRejection) {
|
|
192
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
193
|
+
}
|
|
189
194
|
logger.error('customers/dictionaries/kind-settings.PATCH', { err })
|
|
190
195
|
return NextResponse.json({ error: 'Failed to update kind setting' }, { status: 500 })
|
|
191
196
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { resolveAuthActorId } from '../../../lib/interactionRequestContext'
|
|
17
17
|
import { withOperationMetadata } from '../../../lib/operationMetadata'
|
|
18
18
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
19
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
19
20
|
|
|
20
21
|
const logger = createLogger('customers')
|
|
21
22
|
|
|
@@ -86,6 +87,10 @@ export async function POST(req: Request) {
|
|
|
86
87
|
if (isCrudHttpError(err)) {
|
|
87
88
|
return NextResponse.json(err.body, { status: err.status })
|
|
88
89
|
}
|
|
90
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
91
|
+
if (interceptorRejection) {
|
|
92
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
93
|
+
}
|
|
89
94
|
if (err instanceof z.ZodError) {
|
|
90
95
|
return NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 })
|
|
91
96
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { resolveAuthActorId } from '../../../lib/interactionRequestContext'
|
|
17
17
|
import { withOperationMetadata } from '../../../lib/operationMetadata'
|
|
18
18
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
19
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
19
20
|
|
|
20
21
|
const logger = createLogger('customers')
|
|
21
22
|
|
|
@@ -86,6 +87,10 @@ export async function POST(req: Request) {
|
|
|
86
87
|
if (isCrudHttpError(err)) {
|
|
87
88
|
return NextResponse.json(err.body, { status: err.status })
|
|
88
89
|
}
|
|
90
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
91
|
+
if (interceptorRejection) {
|
|
92
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
93
|
+
}
|
|
89
94
|
if (err instanceof z.ZodError) {
|
|
90
95
|
return NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 })
|
|
91
96
|
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
25
25
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
26
26
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
27
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
27
28
|
|
|
28
29
|
const logger = createLogger('customers')
|
|
29
30
|
|
|
@@ -174,6 +175,10 @@ export async function POST(req: Request) {
|
|
|
174
175
|
if (isCrudHttpError(err)) {
|
|
175
176
|
return NextResponse.json(err.body, { status: err.status })
|
|
176
177
|
}
|
|
178
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
179
|
+
if (interceptorRejection) {
|
|
180
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
181
|
+
}
|
|
177
182
|
logger.error('customers/labels/assign.POST', { err })
|
|
178
183
|
const { translate: fallbackTranslate } = await resolveTranslations()
|
|
179
184
|
return NextResponse.json(
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
28
28
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
29
29
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
30
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
30
31
|
|
|
31
32
|
const logger = createLogger('customers')
|
|
32
33
|
|
|
@@ -287,6 +288,10 @@ export async function POST(req: Request) {
|
|
|
287
288
|
if (isCrudHttpError(err)) {
|
|
288
289
|
return NextResponse.json(err.body, { status: err.status })
|
|
289
290
|
}
|
|
291
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
292
|
+
if (interceptorRejection) {
|
|
293
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
294
|
+
}
|
|
290
295
|
logger.error('customers/labels.POST', { err })
|
|
291
296
|
return NextResponse.json({ error: 'Failed to create label' }, { status: 500 })
|
|
292
297
|
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
25
25
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
26
26
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
27
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
27
28
|
|
|
28
29
|
const logger = createLogger('customers')
|
|
29
30
|
|
|
@@ -174,6 +175,10 @@ export async function POST(req: Request) {
|
|
|
174
175
|
if (isCrudHttpError(err)) {
|
|
175
176
|
return NextResponse.json(err.body, { status: err.status })
|
|
176
177
|
}
|
|
178
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
179
|
+
if (interceptorRejection) {
|
|
180
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
181
|
+
}
|
|
177
182
|
logger.error('customers/labels/unassign.POST', { err })
|
|
178
183
|
return NextResponse.json({ error: 'Failed to unassign label' }, { status: 500 })
|
|
179
184
|
}
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
import { loadPersonContext } from '../context'
|
|
22
22
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
23
23
|
import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'
|
|
24
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
24
25
|
|
|
25
26
|
const paramsSchema = z.object({
|
|
26
27
|
id: z.string().uuid(),
|
|
@@ -226,6 +227,10 @@ export async function PATCH(req: Request, ctx: { params?: { id?: string; linkId?
|
|
|
226
227
|
if (isCrudHttpError(err)) {
|
|
227
228
|
return NextResponse.json(err.body, { status: err.status })
|
|
228
229
|
}
|
|
230
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
231
|
+
if (interceptorRejection) {
|
|
232
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
233
|
+
}
|
|
229
234
|
return NextResponse.json({ error: translate('customers.errors.internal', 'Internal server error') }, { status: 500 })
|
|
230
235
|
}
|
|
231
236
|
}
|
|
@@ -330,6 +335,10 @@ export async function DELETE(req: Request, ctx: { params?: { id?: string; linkId
|
|
|
330
335
|
if (isCrudHttpError(err)) {
|
|
331
336
|
return NextResponse.json(err.body, { status: err.status })
|
|
332
337
|
}
|
|
338
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
339
|
+
if (interceptorRejection) {
|
|
340
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
341
|
+
}
|
|
333
342
|
return NextResponse.json({ error: translate('customers.errors.internal', 'Internal server error') }, { status: 500 })
|
|
334
343
|
}
|
|
335
344
|
}
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import { loadPersonContext } from './context'
|
|
27
27
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
28
28
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
29
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
29
30
|
|
|
30
31
|
const paramsSchema = z.object({
|
|
31
32
|
id: z.string().uuid(),
|
|
@@ -210,6 +211,10 @@ export async function POST(req: Request, ctx: { params?: { id?: string } }) {
|
|
|
210
211
|
if (isCrudHttpError(err)) {
|
|
211
212
|
return NextResponse.json(err.body, { status: err.status })
|
|
212
213
|
}
|
|
214
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
215
|
+
if (interceptorRejection) {
|
|
216
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
217
|
+
}
|
|
213
218
|
return NextResponse.json({ error: translate('customers.errors.internal', 'Internal server error') }, { status: 500 })
|
|
214
219
|
}
|
|
215
220
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
15
15
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
16
16
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
17
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
17
18
|
|
|
18
19
|
const logger = createLogger('customers')
|
|
19
20
|
|
|
@@ -86,6 +87,10 @@ export async function POST(req: Request) {
|
|
|
86
87
|
if (isCrudHttpError(err)) {
|
|
87
88
|
return NextResponse.json(err.body, { status: err.status })
|
|
88
89
|
}
|
|
90
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
91
|
+
if (interceptorRejection) {
|
|
92
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
93
|
+
}
|
|
89
94
|
logger.error('customers.pipeline-stages.reorder failed', { err })
|
|
90
95
|
return NextResponse.json({ error: 'Failed to reorder pipeline stages' }, { status: 400 })
|
|
91
96
|
}
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
26
26
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
27
27
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
28
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
28
29
|
|
|
29
30
|
const logger = createLogger('customers')
|
|
30
31
|
|
|
@@ -174,6 +175,10 @@ export async function POST(req: Request) {
|
|
|
174
175
|
if (isCrudHttpError(err)) {
|
|
175
176
|
return NextResponse.json(err.body, { status: err.status })
|
|
176
177
|
}
|
|
178
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
179
|
+
if (interceptorRejection) {
|
|
180
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
181
|
+
}
|
|
177
182
|
logger.error('customers.pipeline-stages POST failed', { err })
|
|
178
183
|
return NextResponse.json({ error: 'Failed to create pipeline stage' }, { status: 400 })
|
|
179
184
|
}
|
|
@@ -242,6 +247,10 @@ export async function PUT(req: Request) {
|
|
|
242
247
|
if (isCrudHttpError(err)) {
|
|
243
248
|
return NextResponse.json(err.body, { status: err.status })
|
|
244
249
|
}
|
|
250
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
251
|
+
if (interceptorRejection) {
|
|
252
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
253
|
+
}
|
|
245
254
|
logger.error('customers.pipeline-stages PUT failed', { err })
|
|
246
255
|
return NextResponse.json({ error: 'Failed to update pipeline stage' }, { status: 400 })
|
|
247
256
|
}
|
|
@@ -295,6 +304,10 @@ export async function DELETE(req: Request) {
|
|
|
295
304
|
if (isCrudHttpError(err)) {
|
|
296
305
|
return NextResponse.json(err.body, { status: err.status })
|
|
297
306
|
}
|
|
307
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
308
|
+
if (interceptorRejection) {
|
|
309
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
310
|
+
}
|
|
298
311
|
logger.error('customers.pipeline-stages DELETE failed', { err })
|
|
299
312
|
return NextResponse.json({ error: 'Failed to delete pipeline stage' }, { status: 400 })
|
|
300
313
|
}
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
26
26
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
27
27
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
28
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
28
29
|
|
|
29
30
|
const logger = createLogger('customers')
|
|
30
31
|
|
|
@@ -156,6 +157,10 @@ export async function POST(req: Request) {
|
|
|
156
157
|
if (isCrudHttpError(err)) {
|
|
157
158
|
return NextResponse.json(err.body, { status: err.status })
|
|
158
159
|
}
|
|
160
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
161
|
+
if (interceptorRejection) {
|
|
162
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
163
|
+
}
|
|
159
164
|
logger.error('customers.pipelines POST failed', { err })
|
|
160
165
|
return NextResponse.json({ error: 'Failed to create pipeline' }, { status: 400 })
|
|
161
166
|
}
|
|
@@ -224,6 +229,10 @@ export async function PUT(req: Request) {
|
|
|
224
229
|
if (isCrudHttpError(err)) {
|
|
225
230
|
return NextResponse.json(err.body, { status: err.status })
|
|
226
231
|
}
|
|
232
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
233
|
+
if (interceptorRejection) {
|
|
234
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
235
|
+
}
|
|
227
236
|
logger.error('customers.pipelines PUT failed', { err })
|
|
228
237
|
return NextResponse.json({ error: 'Failed to update pipeline' }, { status: 400 })
|
|
229
238
|
}
|
|
@@ -277,6 +286,10 @@ export async function DELETE(req: Request) {
|
|
|
277
286
|
if (isCrudHttpError(err)) {
|
|
278
287
|
return NextResponse.json(err.body, { status: err.status })
|
|
279
288
|
}
|
|
289
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
290
|
+
if (interceptorRejection) {
|
|
291
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
292
|
+
}
|
|
280
293
|
logger.error('customers.pipelines DELETE failed', { err })
|
|
281
294
|
return NextResponse.json({ error: 'Failed to delete pipeline' }, { status: 400 })
|
|
282
295
|
}
|
|
@@ -17,6 +17,7 @@ import type { CustomerAddressFormat } from '../../../data/entities'
|
|
|
17
17
|
import { withScopedPayload } from '../../utils'
|
|
18
18
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
19
19
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
20
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
20
21
|
|
|
21
22
|
const logger = createLogger('customers')
|
|
22
23
|
|
|
@@ -134,6 +135,10 @@ export async function PUT(req: Request) {
|
|
|
134
135
|
if (isCrudHttpError(err)) {
|
|
135
136
|
return NextResponse.json(err.body, { status: err.status })
|
|
136
137
|
}
|
|
138
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
139
|
+
if (interceptorRejection) {
|
|
140
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
141
|
+
}
|
|
137
142
|
const { translate } = await resolveTranslations()
|
|
138
143
|
logger.error('customers.settings.address-format.put failed', { err })
|
|
139
144
|
return NextResponse.json({ error: translate('customers.errors.save_failed', 'Failed to save settings') }, { status: 400 })
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import { invalidateDictionaryCache } from '../../dictionaries/cache'
|
|
32
32
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
33
33
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
34
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
34
35
|
|
|
35
36
|
const logger = createLogger('customers')
|
|
36
37
|
|
|
@@ -222,6 +223,10 @@ export async function PATCH(req: Request) {
|
|
|
222
223
|
if (isCrudHttpError(err)) {
|
|
223
224
|
return NextResponse.json(err.body, { status: err.status })
|
|
224
225
|
}
|
|
226
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
227
|
+
if (interceptorRejection) {
|
|
228
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
229
|
+
}
|
|
225
230
|
const { translate } = await resolveTranslations()
|
|
226
231
|
logger.error('customers.settings.dictionary-sort-modes.patch failed', { err })
|
|
227
232
|
return NextResponse.json(
|
|
@@ -20,6 +20,7 @@ import { loadCustomerSettings } from '../../../commands/settings'
|
|
|
20
20
|
import { withScopedPayload } from '../../utils'
|
|
21
21
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
22
22
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
23
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
23
24
|
|
|
24
25
|
const logger = createLogger('customers')
|
|
25
26
|
|
|
@@ -151,6 +152,10 @@ export async function PUT(req: Request) {
|
|
|
151
152
|
if (isCrudHttpError(err)) {
|
|
152
153
|
return NextResponse.json(err.body, { status: err.status })
|
|
153
154
|
}
|
|
155
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
156
|
+
if (interceptorRejection) {
|
|
157
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
158
|
+
}
|
|
154
159
|
const { translate } = await resolveTranslations()
|
|
155
160
|
logger.error('customers.settings.stuck-threshold.put failed', { err })
|
|
156
161
|
return NextResponse.json(
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
16
16
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
17
17
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
18
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
18
19
|
|
|
19
20
|
const logger = createLogger('customers')
|
|
20
21
|
|
|
@@ -108,6 +109,10 @@ export async function POST(req: Request) {
|
|
|
108
109
|
if (isCrudHttpError(err)) {
|
|
109
110
|
return NextResponse.json(err.body, { status: err.status })
|
|
110
111
|
}
|
|
112
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err)
|
|
113
|
+
if (interceptorRejection) {
|
|
114
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
115
|
+
}
|
|
111
116
|
const { translate } = await resolveTranslations()
|
|
112
117
|
logger.error('customers.tags.assign failed', { err })
|
|
113
118
|
return NextResponse.json({ error: translate('customers.errors.assign_failed', 'Failed to assign tag') }, { status: 400 })
|