@open-mercato/core 0.6.6-develop.6228.1.cf483c68b5 → 0.6.6-develop.6241.1.47c01ad4ed
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/modules/auth/commands/users.js +1 -0
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/communication_channels/data/enrichers.js +9 -1
- package/dist/modules/communication_channels/data/enrichers.js.map +2 -2
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +3 -5
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +2 -2
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +39 -1
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/route.js +107 -12
- package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
- package/dist/modules/customers/api/pipelines/route.js +107 -12
- package/dist/modules/customers/api/pipelines/route.js.map +2 -2
- package/dist/modules/customers/api/settings/address-format/route.js +33 -1
- package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
- package/dist/modules/customers/api/tags/assign/route.js +37 -0
- package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js +37 -0
- package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
- package/dist/modules/entities/api/encryption.js +109 -38
- package/dist/modules/entities/api/encryption.js.map +2 -2
- package/dist/modules/entities/components/EncryptionManager.js +22 -9
- package/dist/modules/entities/components/EncryptionManager.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +23 -1
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +10 -3
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/state/route.js +20 -5
- package/dist/modules/integrations/api/[id]/state/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/version/route.js +17 -1
- package/dist/modules/integrations/api/[id]/version/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +2 -0
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +36 -19
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +13 -9
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/page.js +4 -3
- package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
- package/dist/modules/integrations/data/entities.js +2 -2
- package/dist/modules/integrations/data/entities.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-service.js +32 -0
- package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
- package/dist/modules/integrations/lib/state-service.js +4 -2
- package/dist/modules/integrations/lib/state-service.js.map +2 -2
- package/dist/modules/notifications/lib/notificationService.js +42 -10
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/dist/modules/payment_gateways/api/status/route.js +91 -6
- package/dist/modules/payment_gateways/api/status/route.js.map +2 -2
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +25 -11
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +25 -7
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +25 -7
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js +52 -24
- package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
- package/dist/modules/sales/components/DocumentNumberSettings.js +26 -9
- package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
- package/dist/modules/sales/components/OrderEditingSettings.js +26 -9
- package/dist/modules/sales/components/OrderEditingSettings.js.map +2 -2
- package/dist/modules/sales/components/StatusSettings.js +66 -31
- package/dist/modules/sales/components/StatusSettings.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +25 -7
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/shipping_carriers/api/cancel/route.js +38 -0
- package/dist/modules/shipping_carriers/api/cancel/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/shipments/route.js +12 -5
- package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +7 -8
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-ui/timerErrors.js +12 -0
- package/dist/modules/staff/lib/timesheets-ui/timerErrors.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js +4 -3
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/page.js +19 -10
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/commands/users.ts +1 -0
- package/src/modules/communication_channels/data/enrichers.ts +25 -0
- package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +7 -6
- package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.ts +5 -2
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +41 -1
- package/src/modules/customers/api/pipeline-stages/route.ts +112 -13
- package/src/modules/customers/api/pipelines/route.ts +112 -13
- package/src/modules/customers/api/settings/address-format/route.ts +36 -1
- package/src/modules/customers/api/tags/assign/route.ts +39 -0
- package/src/modules/customers/api/tags/unassign/route.ts +39 -0
- package/src/modules/customers/i18n/de.json +1 -0
- package/src/modules/customers/i18n/en.json +1 -0
- package/src/modules/customers/i18n/es.json +1 -0
- package/src/modules/customers/i18n/pl.json +1 -0
- package/src/modules/entities/api/encryption.ts +122 -39
- package/src/modules/entities/components/EncryptionManager.tsx +28 -9
- package/src/modules/integrations/api/[id]/credentials/route.ts +23 -0
- package/src/modules/integrations/api/[id]/route.ts +8 -1
- package/src/modules/integrations/api/[id]/state/route.ts +20 -4
- package/src/modules/integrations/api/[id]/version/route.ts +18 -1
- package/src/modules/integrations/api/route.ts +3 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +39 -20
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +19 -11
- package/src/modules/integrations/backend/integrations/page.tsx +8 -5
- package/src/modules/integrations/data/entities.ts +2 -2
- package/src/modules/integrations/lib/credentials-service.ts +35 -0
- package/src/modules/integrations/lib/state-service.ts +3 -0
- package/src/modules/notifications/lib/notificationService.ts +74 -11
- package/src/modules/payment_gateways/api/status/route.ts +97 -5
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +27 -11
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +31 -7
- package/src/modules/sales/backend/sales/channels/page.tsx +31 -7
- package/src/modules/sales/components/AdjustmentKindSettings.tsx +60 -24
- package/src/modules/sales/components/DocumentNumberSettings.tsx +32 -10
- package/src/modules/sales/components/OrderEditingSettings.tsx +32 -10
- package/src/modules/sales/components/StatusSettings.tsx +74 -33
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +30 -6
- package/src/modules/shipping_carriers/api/cancel/route.ts +46 -0
- package/src/modules/shipping_carriers/api/shipments/route.ts +21 -6
- package/src/modules/staff/i18n/de.json +5 -5
- package/src/modules/staff/i18n/es.json +5 -5
- package/src/modules/staff/i18n/pl.json +5 -5
- package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +7 -8
- package/src/modules/staff/lib/timesheets-ui/timerErrors.ts +23 -0
- package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.tsx +4 -3
- package/src/modules/workflows/backend/definitions/page.tsx +19 -10
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/api/pipeline-stages/reorder/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { pipelineStageReorderSchema, type PipelineStageReorderInput } from '../../../data/validators'\nimport { withScopedPayload } from '../../utils'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineStageReorderInput, void>(\n 'customers.pipeline-stages.reorder',\n { input
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAEnD,SAAS,kCAAkE;AAC3E,SAAS,yBAAyB;AAClC,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { pipelineStageReorderSchema, type PipelineStageReorderInput } from '../../../data/validators'\nimport { withScopedPayload } from '../../utils'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nconst PIPELINE_STAGE_RESOURCE_KIND = 'customers.pipelineStage'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n const tenantId = auth.tenantId ?? null\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = pipelineStageReorderSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: auth.sub,\n resourceKind: PIPELINE_STAGE_RESOURCE_KIND,\n resourceId: organizationId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineStageReorderInput, void>(\n 'customers.pipeline-stages.reorder',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: auth.sub,\n resourceKind: PIPELINE_STAGE_RESOURCE_KIND,\n resourceId: organizationId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages.reorder failed', err)\n return NextResponse.json({ error: 'Failed to reorder pipeline stages' }, { status: 400 })\n }\n}\n\nconst reorderOkResponseSchema = z.object({ ok: z.boolean() })\nconst reorderErrorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Reorder pipeline stages',\n methods: {\n POST: {\n summary: 'Reorder pipeline stages',\n description: 'Updates the order of pipeline stages in bulk.',\n requestBody: { contentType: 'application/json', schema: pipelineStageReorderSchema },\n responses: [\n { status: 200, description: 'Stages reordered', schema: reorderOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: reorderErrorSchema },\n { status: 401, description: 'Unauthorized', schema: reorderErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -13,6 +13,11 @@ import { withScopedPayload } from "../utils.js";
|
|
|
13
13
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
14
14
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
15
15
|
import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
|
|
16
|
+
import {
|
|
17
|
+
runCrudMutationGuardAfterSuccess,
|
|
18
|
+
validateCrudMutationGuard
|
|
19
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
20
|
+
const PIPELINE_STAGE_RESOURCE_KIND = "customers.pipelineStage";
|
|
16
21
|
const metadata = {
|
|
17
22
|
GET: { requireAuth: true, requireFeatures: ["customers.pipelines.view"] },
|
|
18
23
|
POST: { requireAuth: true, requireFeatures: ["customers.pipelines.manage"] },
|
|
@@ -35,13 +40,13 @@ async function buildContext(req) {
|
|
|
35
40
|
};
|
|
36
41
|
const organizationId = scope?.selectedId ?? auth.orgId ?? null;
|
|
37
42
|
const tenantId = auth.tenantId ?? null;
|
|
38
|
-
return { ctx, organizationId, tenantId };
|
|
43
|
+
return { ctx, organizationId, tenantId, translate };
|
|
39
44
|
}
|
|
40
45
|
async function GET(req) {
|
|
41
46
|
try {
|
|
42
|
-
const { ctx, organizationId, tenantId } = await buildContext(req);
|
|
47
|
+
const { ctx, organizationId, tenantId, translate } = await buildContext(req);
|
|
43
48
|
if (!organizationId || !tenantId) {
|
|
44
|
-
return NextResponse.json({ error: "Organization and tenant context required" }, { status: 400 });
|
|
49
|
+
return NextResponse.json({ error: translate("customers.errors.context_required", "Organization and tenant context required") }, { status: 400 });
|
|
45
50
|
}
|
|
46
51
|
const url = new URL(req.url);
|
|
47
52
|
const pipelineId = url.searchParams.get("pipelineId");
|
|
@@ -84,15 +89,45 @@ async function GET(req) {
|
|
|
84
89
|
}
|
|
85
90
|
async function POST(req) {
|
|
86
91
|
try {
|
|
87
|
-
const { ctx } = await buildContext(req);
|
|
92
|
+
const { ctx, organizationId, tenantId, translate } = await buildContext(req);
|
|
93
|
+
if (!organizationId || !tenantId) {
|
|
94
|
+
return NextResponse.json({ error: translate("customers.errors.context_required", "Organization and tenant context required") }, { status: 400 });
|
|
95
|
+
}
|
|
88
96
|
const body = await req.json().catch(() => ({}));
|
|
89
|
-
const { translate } = await resolveTranslations();
|
|
90
97
|
const scoped = withScopedPayload(body, ctx, translate);
|
|
98
|
+
const input = pipelineStageCreateSchema.parse(scoped);
|
|
99
|
+
const guardResult = await validateCrudMutationGuard(ctx.container, {
|
|
100
|
+
tenantId,
|
|
101
|
+
organizationId,
|
|
102
|
+
userId: ctx.auth.sub,
|
|
103
|
+
resourceKind: PIPELINE_STAGE_RESOURCE_KIND,
|
|
104
|
+
resourceId: organizationId,
|
|
105
|
+
operation: "create",
|
|
106
|
+
requestMethod: req.method,
|
|
107
|
+
requestHeaders: req.headers,
|
|
108
|
+
mutationPayload: input
|
|
109
|
+
});
|
|
110
|
+
if (guardResult && !guardResult.ok) {
|
|
111
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
112
|
+
}
|
|
91
113
|
const commandBus = ctx.container.resolve("commandBus");
|
|
92
114
|
const { result, logEntry } = await commandBus.execute(
|
|
93
115
|
"customers.pipeline-stages.create",
|
|
94
|
-
{ input
|
|
116
|
+
{ input, ctx }
|
|
95
117
|
);
|
|
118
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
119
|
+
await runCrudMutationGuardAfterSuccess(ctx.container, {
|
|
120
|
+
tenantId,
|
|
121
|
+
organizationId,
|
|
122
|
+
userId: ctx.auth.sub,
|
|
123
|
+
resourceKind: PIPELINE_STAGE_RESOURCE_KIND,
|
|
124
|
+
resourceId: result?.stageId ?? organizationId,
|
|
125
|
+
operation: "create",
|
|
126
|
+
requestMethod: req.method,
|
|
127
|
+
requestHeaders: req.headers,
|
|
128
|
+
metadata: guardResult.metadata ?? null
|
|
129
|
+
});
|
|
130
|
+
}
|
|
96
131
|
const response = NextResponse.json({ id: result?.stageId ?? null }, { status: 201 });
|
|
97
132
|
if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
|
|
98
133
|
response.headers.set(
|
|
@@ -119,15 +154,45 @@ async function POST(req) {
|
|
|
119
154
|
}
|
|
120
155
|
async function PUT(req) {
|
|
121
156
|
try {
|
|
122
|
-
const { ctx } = await buildContext(req);
|
|
157
|
+
const { ctx, organizationId, tenantId, translate } = await buildContext(req);
|
|
158
|
+
if (!organizationId || !tenantId) {
|
|
159
|
+
return NextResponse.json({ error: translate("customers.errors.context_required", "Organization and tenant context required") }, { status: 400 });
|
|
160
|
+
}
|
|
123
161
|
const body = await req.json().catch(() => ({}));
|
|
124
|
-
const { translate } = await resolveTranslations();
|
|
125
162
|
const scoped = withScopedPayload(body, ctx, translate);
|
|
163
|
+
const input = pipelineStageUpdateSchema.parse(scoped);
|
|
164
|
+
const guardResult = await validateCrudMutationGuard(ctx.container, {
|
|
165
|
+
tenantId,
|
|
166
|
+
organizationId,
|
|
167
|
+
userId: ctx.auth.sub,
|
|
168
|
+
resourceKind: PIPELINE_STAGE_RESOURCE_KIND,
|
|
169
|
+
resourceId: input.id,
|
|
170
|
+
operation: "update",
|
|
171
|
+
requestMethod: req.method,
|
|
172
|
+
requestHeaders: req.headers,
|
|
173
|
+
mutationPayload: input
|
|
174
|
+
});
|
|
175
|
+
if (guardResult && !guardResult.ok) {
|
|
176
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
177
|
+
}
|
|
126
178
|
const commandBus = ctx.container.resolve("commandBus");
|
|
127
179
|
const { logEntry } = await commandBus.execute(
|
|
128
180
|
"customers.pipeline-stages.update",
|
|
129
|
-
{ input
|
|
181
|
+
{ input, ctx }
|
|
130
182
|
);
|
|
183
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
184
|
+
await runCrudMutationGuardAfterSuccess(ctx.container, {
|
|
185
|
+
tenantId,
|
|
186
|
+
organizationId,
|
|
187
|
+
userId: ctx.auth.sub,
|
|
188
|
+
resourceKind: PIPELINE_STAGE_RESOURCE_KIND,
|
|
189
|
+
resourceId: input.id,
|
|
190
|
+
operation: "update",
|
|
191
|
+
requestMethod: req.method,
|
|
192
|
+
requestHeaders: req.headers,
|
|
193
|
+
metadata: guardResult.metadata ?? null
|
|
194
|
+
});
|
|
195
|
+
}
|
|
131
196
|
const response = NextResponse.json({ ok: true });
|
|
132
197
|
if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
|
|
133
198
|
response.headers.set(
|
|
@@ -154,15 +219,45 @@ async function PUT(req) {
|
|
|
154
219
|
}
|
|
155
220
|
async function DELETE(req) {
|
|
156
221
|
try {
|
|
157
|
-
const { ctx } = await buildContext(req);
|
|
222
|
+
const { ctx, organizationId, tenantId, translate } = await buildContext(req);
|
|
223
|
+
if (!organizationId || !tenantId) {
|
|
224
|
+
return NextResponse.json({ error: translate("customers.errors.context_required", "Organization and tenant context required") }, { status: 400 });
|
|
225
|
+
}
|
|
158
226
|
const body = await req.json().catch(() => ({}));
|
|
159
|
-
const { translate } = await resolveTranslations();
|
|
160
227
|
const scoped = withScopedPayload(body, ctx, translate);
|
|
228
|
+
const input = pipelineStageDeleteSchema.parse(scoped);
|
|
229
|
+
const guardResult = await validateCrudMutationGuard(ctx.container, {
|
|
230
|
+
tenantId,
|
|
231
|
+
organizationId,
|
|
232
|
+
userId: ctx.auth.sub,
|
|
233
|
+
resourceKind: PIPELINE_STAGE_RESOURCE_KIND,
|
|
234
|
+
resourceId: input.id,
|
|
235
|
+
operation: "delete",
|
|
236
|
+
requestMethod: req.method,
|
|
237
|
+
requestHeaders: req.headers,
|
|
238
|
+
mutationPayload: input
|
|
239
|
+
});
|
|
240
|
+
if (guardResult && !guardResult.ok) {
|
|
241
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
242
|
+
}
|
|
161
243
|
const commandBus = ctx.container.resolve("commandBus");
|
|
162
244
|
await commandBus.execute(
|
|
163
245
|
"customers.pipeline-stages.delete",
|
|
164
|
-
{ input
|
|
246
|
+
{ input, ctx }
|
|
165
247
|
);
|
|
248
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
249
|
+
await runCrudMutationGuardAfterSuccess(ctx.container, {
|
|
250
|
+
tenantId,
|
|
251
|
+
organizationId,
|
|
252
|
+
userId: ctx.auth.sub,
|
|
253
|
+
resourceKind: PIPELINE_STAGE_RESOURCE_KIND,
|
|
254
|
+
resourceId: input.id,
|
|
255
|
+
operation: "delete",
|
|
256
|
+
requestMethod: req.method,
|
|
257
|
+
requestHeaders: req.headers,
|
|
258
|
+
metadata: guardResult.metadata ?? null
|
|
259
|
+
});
|
|
260
|
+
}
|
|
166
261
|
return NextResponse.json({ ok: true });
|
|
167
262
|
} catch (err) {
|
|
168
263
|
if (isCrudHttpError(err)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/api/pipeline-stages/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CustomerPipelineStage, CustomerDictionaryEntry } from '../../data/entities'\nimport {\n pipelineStageCreateSchema,\n pipelineStageUpdateSchema,\n pipelineStageDeleteSchema,\n type PipelineStageCreateInput,\n type PipelineStageUpdateInput,\n type PipelineStageDeleteInput,\n} from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.pipelines.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; organizationId: string | null; tenantId: string | null }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n const tenantId = auth.tenantId ?? null\n return { ctx, organizationId, tenantId }\n}\n\nexport async function GET(req: Request) {\n try {\n const { ctx, organizationId, tenantId } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: 'Organization and tenant context required' }, { status: 400 })\n }\n const url = new URL(req.url)\n const pipelineId = url.searchParams.get('pipelineId')\n\n const em = (ctx.container.resolve('em') as EntityManager)\n const where: Record<string, unknown> = { organizationId, tenantId }\n if (pipelineId) where.pipelineId = pipelineId\n\n const stages = await em.find(CustomerPipelineStage, where, { orderBy: { order: 'ASC' } })\n\n const stageLabels = stages.map((s) => s.label.trim().toLowerCase())\n const dictEntries = stageLabels.length\n ? await em.find(CustomerDictionaryEntry, {\n organizationId,\n tenantId,\n kind: 'pipeline_stage',\n normalizedValue: { $in: stageLabels },\n })\n : []\n const dictByNormalized = new Map<string, CustomerDictionaryEntry>()\n dictEntries.forEach((entry) => dictByNormalized.set(entry.normalizedValue, entry))\n\n const items = stages.map((stage) => {\n const dictEntry = dictByNormalized.get(stage.label.trim().toLowerCase())\n return {\n id: stage.id,\n pipelineId: stage.pipelineId,\n label: stage.label,\n order: stage.order,\n color: dictEntry?.color ?? null,\n icon: dictEntry?.icon ?? null,\n organizationId: stage.organizationId,\n tenantId: stage.tenantId,\n createdAt: stage.createdAt,\n updatedAt: stage.updatedAt,\n }\n })\n return NextResponse.json({ items, total: items.length })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages GET failed', err)\n return NextResponse.json({ error: 'Failed to load pipeline stages' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<PipelineStageCreateInput, { stageId: string }>(\n 'customers.pipeline-stages.create',\n { input: pipelineStageCreateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ id: result?.stageId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.pipelineStage',\n resourceId: logEntry.resourceId ?? result?.stageId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages POST failed', err)\n return NextResponse.json({ error: 'Failed to create pipeline stage' }, { status: 400 })\n }\n}\n\nexport async function PUT(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute<PipelineStageUpdateInput, void>(\n 'customers.pipeline-stages.update',\n { input: pipelineStageUpdateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.pipelineStage',\n resourceId: logEntry.resourceId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages PUT failed', err)\n return NextResponse.json({ error: 'Failed to update pipeline stage' }, { status: 400 })\n }\n}\n\nexport async function DELETE(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineStageDeleteInput, void>(\n 'customers.pipeline-stages.delete',\n { input: pipelineStageDeleteSchema.parse(scoped), ctx },\n )\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages DELETE failed', err)\n return NextResponse.json({ error: 'Failed to delete pipeline stage' }, { status: 400 })\n }\n}\n\nconst stageItemSchema = z.object({\n id: z.string().uuid(),\n pipelineId: z.string().uuid(),\n label: z.string(),\n order: z.number(),\n color: z.string().nullable(),\n icon: z.string().nullable(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n createdAt: z.date(),\n updatedAt: z.date(),\n})\n\nconst stageListResponseSchema = z.object({\n items: z.array(stageItemSchema),\n total: z.number(),\n})\n\nconst stageCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst stageOkResponseSchema = z.object({\n ok: z.boolean(),\n})\n\nconst stageErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Manage pipeline stages',\n methods: {\n GET: {\n summary: 'List pipeline stages',\n description: 'Returns pipeline stages for the authenticated organization, optionally filtered by pipelineId.',\n query: z.object({ pipelineId: z.string().uuid().optional() }),\n responses: [\n { status: 200, description: 'Stage list', schema: stageListResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: stageErrorSchema },\n { status: 400, description: 'Invalid request', schema: stageErrorSchema },\n ],\n },\n POST: {\n summary: 'Create pipeline stage',\n description: 'Creates a new pipeline stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageCreateSchema },\n responses: [\n { status: 201, description: 'Stage created', schema: stageCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: stageErrorSchema },\n { status: 401, description: 'Unauthorized', schema: stageErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update pipeline stage',\n description: 'Updates an existing pipeline stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageUpdateSchema },\n responses: [\n { status: 200, description: 'Stage updated', schema: stageOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: stageErrorSchema },\n { status: 404, description: 'Stage not found', schema: stageErrorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete pipeline stage',\n description: 'Deletes a pipeline stage. Returns 409 if active deals use this stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageDeleteSchema },\n responses: [\n { status: 200, description: 'Stage deleted', schema: stageOkResponseSchema },\n ],\n errors: [\n { status: 409, description: 'Stage has active deals', schema: stageErrorSchema },\n { status: 404, description: 'Stage not found', schema: stageErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAGnD,SAAS,uBAAuB,+BAA+B;AAC/D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,yBAAyB;AAClC,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;AACpC,SAAS,kCAAkC;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CustomerPipelineStage, CustomerDictionaryEntry } from '../../data/entities'\nimport {\n pipelineStageCreateSchema,\n pipelineStageUpdateSchema,\n pipelineStageDeleteSchema,\n type PipelineStageCreateInput,\n type PipelineStageUpdateInput,\n type PipelineStageDeleteInput,\n} from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nconst PIPELINE_STAGE_RESOURCE_KIND = 'customers.pipelineStage'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.pipelines.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; organizationId: string | null; tenantId: string | null; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n const tenantId = auth.tenantId ?? null\n return { ctx, organizationId, tenantId, translate }\n}\n\nexport async function GET(req: Request) {\n try {\n const { ctx, organizationId, tenantId, translate } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const url = new URL(req.url)\n const pipelineId = url.searchParams.get('pipelineId')\n\n const em = (ctx.container.resolve('em') as EntityManager)\n const where: Record<string, unknown> = { organizationId, tenantId }\n if (pipelineId) where.pipelineId = pipelineId\n\n const stages = await em.find(CustomerPipelineStage, where, { orderBy: { order: 'ASC' } })\n\n const stageLabels = stages.map((s) => s.label.trim().toLowerCase())\n const dictEntries = stageLabels.length\n ? await em.find(CustomerDictionaryEntry, {\n organizationId,\n tenantId,\n kind: 'pipeline_stage',\n normalizedValue: { $in: stageLabels },\n })\n : []\n const dictByNormalized = new Map<string, CustomerDictionaryEntry>()\n dictEntries.forEach((entry) => dictByNormalized.set(entry.normalizedValue, entry))\n\n const items = stages.map((stage) => {\n const dictEntry = dictByNormalized.get(stage.label.trim().toLowerCase())\n return {\n id: stage.id,\n pipelineId: stage.pipelineId,\n label: stage.label,\n order: stage.order,\n color: dictEntry?.color ?? null,\n icon: dictEntry?.icon ?? null,\n organizationId: stage.organizationId,\n tenantId: stage.tenantId,\n createdAt: stage.createdAt,\n updatedAt: stage.updatedAt,\n }\n })\n return NextResponse.json({ items, total: items.length })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages GET failed', err)\n return NextResponse.json({ error: 'Failed to load pipeline stages' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, organizationId, tenantId, translate } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = pipelineStageCreateSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_STAGE_RESOURCE_KIND,\n resourceId: organizationId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<PipelineStageCreateInput, { stageId: string }>(\n 'customers.pipeline-stages.create',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_STAGE_RESOURCE_KIND,\n resourceId: result?.stageId ?? organizationId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ id: result?.stageId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.pipelineStage',\n resourceId: logEntry.resourceId ?? result?.stageId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages POST failed', err)\n return NextResponse.json({ error: 'Failed to create pipeline stage' }, { status: 400 })\n }\n}\n\nexport async function PUT(req: Request) {\n try {\n const { ctx, organizationId, tenantId, translate } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = pipelineStageUpdateSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_STAGE_RESOURCE_KIND,\n resourceId: input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute<PipelineStageUpdateInput, void>(\n 'customers.pipeline-stages.update',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_STAGE_RESOURCE_KIND,\n resourceId: input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.pipelineStage',\n resourceId: logEntry.resourceId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages PUT failed', err)\n return NextResponse.json({ error: 'Failed to update pipeline stage' }, { status: 400 })\n }\n}\n\nexport async function DELETE(req: Request) {\n try {\n const { ctx, organizationId, tenantId, translate } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = pipelineStageDeleteSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_STAGE_RESOURCE_KIND,\n resourceId: input.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineStageDeleteInput, void>(\n 'customers.pipeline-stages.delete',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_STAGE_RESOURCE_KIND,\n resourceId: input.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages DELETE failed', err)\n return NextResponse.json({ error: 'Failed to delete pipeline stage' }, { status: 400 })\n }\n}\n\nconst stageItemSchema = z.object({\n id: z.string().uuid(),\n pipelineId: z.string().uuid(),\n label: z.string(),\n order: z.number(),\n color: z.string().nullable(),\n icon: z.string().nullable(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n createdAt: z.date(),\n updatedAt: z.date(),\n})\n\nconst stageListResponseSchema = z.object({\n items: z.array(stageItemSchema),\n total: z.number(),\n})\n\nconst stageCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst stageOkResponseSchema = z.object({\n ok: z.boolean(),\n})\n\nconst stageErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Manage pipeline stages',\n methods: {\n GET: {\n summary: 'List pipeline stages',\n description: 'Returns pipeline stages for the authenticated organization, optionally filtered by pipelineId.',\n query: z.object({ pipelineId: z.string().uuid().optional() }),\n responses: [\n { status: 200, description: 'Stage list', schema: stageListResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: stageErrorSchema },\n { status: 400, description: 'Invalid request', schema: stageErrorSchema },\n ],\n },\n POST: {\n summary: 'Create pipeline stage',\n description: 'Creates a new pipeline stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageCreateSchema },\n responses: [\n { status: 201, description: 'Stage created', schema: stageCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: stageErrorSchema },\n { status: 401, description: 'Unauthorized', schema: stageErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update pipeline stage',\n description: 'Updates an existing pipeline stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageUpdateSchema },\n responses: [\n { status: 200, description: 'Stage updated', schema: stageOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: stageErrorSchema },\n { status: 404, description: 'Stage not found', schema: stageErrorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete pipeline stage',\n description: 'Deletes a pipeline stage. Returns 409 if active deals use this stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageDeleteSchema },\n responses: [\n { status: 200, description: 'Stage deleted', schema: stageOkResponseSchema },\n ],\n errors: [\n { status: 409, description: 'Stage has active deals', schema: stageErrorSchema },\n { status: 404, description: 'Stage not found', schema: stageErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -13,6 +13,11 @@ import { withScopedPayload } from "../utils.js";
|
|
|
13
13
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
14
14
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
15
15
|
import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
|
|
16
|
+
import {
|
|
17
|
+
runCrudMutationGuardAfterSuccess,
|
|
18
|
+
validateCrudMutationGuard
|
|
19
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
20
|
+
const PIPELINE_RESOURCE_KIND = "customers.pipeline";
|
|
16
21
|
const metadata = {
|
|
17
22
|
GET: { requireAuth: true, requireFeatures: ["customers.pipelines.view"] },
|
|
18
23
|
POST: { requireAuth: true, requireFeatures: ["customers.pipelines.manage"] },
|
|
@@ -35,13 +40,13 @@ async function buildContext(req) {
|
|
|
35
40
|
};
|
|
36
41
|
const organizationId = scope?.selectedId ?? auth.orgId ?? null;
|
|
37
42
|
const tenantId = auth.tenantId ?? null;
|
|
38
|
-
return { ctx, organizationId, tenantId };
|
|
43
|
+
return { ctx, organizationId, tenantId, translate };
|
|
39
44
|
}
|
|
40
45
|
async function GET(req) {
|
|
41
46
|
try {
|
|
42
|
-
const { ctx, organizationId, tenantId } = await buildContext(req);
|
|
47
|
+
const { ctx, organizationId, tenantId, translate } = await buildContext(req);
|
|
43
48
|
if (!organizationId || !tenantId) {
|
|
44
|
-
return NextResponse.json({ error: "Organization and tenant context required" }, { status: 400 });
|
|
49
|
+
return NextResponse.json({ error: translate("customers.errors.context_required", "Organization and tenant context required") }, { status: 400 });
|
|
45
50
|
}
|
|
46
51
|
const url = new URL(req.url);
|
|
47
52
|
const isDefaultParam = url.searchParams.get("isDefault");
|
|
@@ -70,15 +75,45 @@ async function GET(req) {
|
|
|
70
75
|
}
|
|
71
76
|
async function POST(req) {
|
|
72
77
|
try {
|
|
73
|
-
const { ctx } = await buildContext(req);
|
|
78
|
+
const { ctx, organizationId, tenantId, translate } = await buildContext(req);
|
|
79
|
+
if (!organizationId || !tenantId) {
|
|
80
|
+
return NextResponse.json({ error: translate("customers.errors.context_required", "Organization and tenant context required") }, { status: 400 });
|
|
81
|
+
}
|
|
74
82
|
const body = await req.json().catch(() => ({}));
|
|
75
|
-
const { translate } = await resolveTranslations();
|
|
76
83
|
const scoped = withScopedPayload(body, ctx, translate);
|
|
84
|
+
const input = pipelineCreateSchema.parse(scoped);
|
|
85
|
+
const guardResult = await validateCrudMutationGuard(ctx.container, {
|
|
86
|
+
tenantId,
|
|
87
|
+
organizationId,
|
|
88
|
+
userId: ctx.auth.sub,
|
|
89
|
+
resourceKind: PIPELINE_RESOURCE_KIND,
|
|
90
|
+
resourceId: organizationId,
|
|
91
|
+
operation: "create",
|
|
92
|
+
requestMethod: req.method,
|
|
93
|
+
requestHeaders: req.headers,
|
|
94
|
+
mutationPayload: input
|
|
95
|
+
});
|
|
96
|
+
if (guardResult && !guardResult.ok) {
|
|
97
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
98
|
+
}
|
|
77
99
|
const commandBus = ctx.container.resolve("commandBus");
|
|
78
100
|
const { result, logEntry } = await commandBus.execute(
|
|
79
101
|
"customers.pipelines.create",
|
|
80
|
-
{ input
|
|
102
|
+
{ input, ctx }
|
|
81
103
|
);
|
|
104
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
105
|
+
await runCrudMutationGuardAfterSuccess(ctx.container, {
|
|
106
|
+
tenantId,
|
|
107
|
+
organizationId,
|
|
108
|
+
userId: ctx.auth.sub,
|
|
109
|
+
resourceKind: PIPELINE_RESOURCE_KIND,
|
|
110
|
+
resourceId: result?.pipelineId ?? organizationId,
|
|
111
|
+
operation: "create",
|
|
112
|
+
requestMethod: req.method,
|
|
113
|
+
requestHeaders: req.headers,
|
|
114
|
+
metadata: guardResult.metadata ?? null
|
|
115
|
+
});
|
|
116
|
+
}
|
|
82
117
|
const response = NextResponse.json({ id: result?.pipelineId ?? null }, { status: 201 });
|
|
83
118
|
if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
|
|
84
119
|
response.headers.set(
|
|
@@ -105,15 +140,45 @@ async function POST(req) {
|
|
|
105
140
|
}
|
|
106
141
|
async function PUT(req) {
|
|
107
142
|
try {
|
|
108
|
-
const { ctx } = await buildContext(req);
|
|
143
|
+
const { ctx, organizationId, tenantId, translate } = await buildContext(req);
|
|
144
|
+
if (!organizationId || !tenantId) {
|
|
145
|
+
return NextResponse.json({ error: translate("customers.errors.context_required", "Organization and tenant context required") }, { status: 400 });
|
|
146
|
+
}
|
|
109
147
|
const body = await req.json().catch(() => ({}));
|
|
110
|
-
const { translate } = await resolveTranslations();
|
|
111
148
|
const scoped = withScopedPayload(body, ctx, translate);
|
|
149
|
+
const input = pipelineUpdateSchema.parse(scoped);
|
|
150
|
+
const guardResult = await validateCrudMutationGuard(ctx.container, {
|
|
151
|
+
tenantId,
|
|
152
|
+
organizationId,
|
|
153
|
+
userId: ctx.auth.sub,
|
|
154
|
+
resourceKind: PIPELINE_RESOURCE_KIND,
|
|
155
|
+
resourceId: input.id,
|
|
156
|
+
operation: "update",
|
|
157
|
+
requestMethod: req.method,
|
|
158
|
+
requestHeaders: req.headers,
|
|
159
|
+
mutationPayload: input
|
|
160
|
+
});
|
|
161
|
+
if (guardResult && !guardResult.ok) {
|
|
162
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
163
|
+
}
|
|
112
164
|
const commandBus = ctx.container.resolve("commandBus");
|
|
113
165
|
const { logEntry } = await commandBus.execute(
|
|
114
166
|
"customers.pipelines.update",
|
|
115
|
-
{ input
|
|
167
|
+
{ input, ctx }
|
|
116
168
|
);
|
|
169
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
170
|
+
await runCrudMutationGuardAfterSuccess(ctx.container, {
|
|
171
|
+
tenantId,
|
|
172
|
+
organizationId,
|
|
173
|
+
userId: ctx.auth.sub,
|
|
174
|
+
resourceKind: PIPELINE_RESOURCE_KIND,
|
|
175
|
+
resourceId: input.id,
|
|
176
|
+
operation: "update",
|
|
177
|
+
requestMethod: req.method,
|
|
178
|
+
requestHeaders: req.headers,
|
|
179
|
+
metadata: guardResult.metadata ?? null
|
|
180
|
+
});
|
|
181
|
+
}
|
|
117
182
|
const response = NextResponse.json({ ok: true });
|
|
118
183
|
if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
|
|
119
184
|
response.headers.set(
|
|
@@ -140,15 +205,45 @@ async function PUT(req) {
|
|
|
140
205
|
}
|
|
141
206
|
async function DELETE(req) {
|
|
142
207
|
try {
|
|
143
|
-
const { ctx } = await buildContext(req);
|
|
208
|
+
const { ctx, organizationId, tenantId, translate } = await buildContext(req);
|
|
209
|
+
if (!organizationId || !tenantId) {
|
|
210
|
+
return NextResponse.json({ error: translate("customers.errors.context_required", "Organization and tenant context required") }, { status: 400 });
|
|
211
|
+
}
|
|
144
212
|
const body = await req.json().catch(() => ({}));
|
|
145
|
-
const { translate } = await resolveTranslations();
|
|
146
213
|
const scoped = withScopedPayload(body, ctx, translate);
|
|
214
|
+
const input = pipelineDeleteSchema.parse(scoped);
|
|
215
|
+
const guardResult = await validateCrudMutationGuard(ctx.container, {
|
|
216
|
+
tenantId,
|
|
217
|
+
organizationId,
|
|
218
|
+
userId: ctx.auth.sub,
|
|
219
|
+
resourceKind: PIPELINE_RESOURCE_KIND,
|
|
220
|
+
resourceId: input.id,
|
|
221
|
+
operation: "delete",
|
|
222
|
+
requestMethod: req.method,
|
|
223
|
+
requestHeaders: req.headers,
|
|
224
|
+
mutationPayload: input
|
|
225
|
+
});
|
|
226
|
+
if (guardResult && !guardResult.ok) {
|
|
227
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
228
|
+
}
|
|
147
229
|
const commandBus = ctx.container.resolve("commandBus");
|
|
148
230
|
await commandBus.execute(
|
|
149
231
|
"customers.pipelines.delete",
|
|
150
|
-
{ input
|
|
232
|
+
{ input, ctx }
|
|
151
233
|
);
|
|
234
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
235
|
+
await runCrudMutationGuardAfterSuccess(ctx.container, {
|
|
236
|
+
tenantId,
|
|
237
|
+
organizationId,
|
|
238
|
+
userId: ctx.auth.sub,
|
|
239
|
+
resourceKind: PIPELINE_RESOURCE_KIND,
|
|
240
|
+
resourceId: input.id,
|
|
241
|
+
operation: "delete",
|
|
242
|
+
requestMethod: req.method,
|
|
243
|
+
requestHeaders: req.headers,
|
|
244
|
+
metadata: guardResult.metadata ?? null
|
|
245
|
+
});
|
|
246
|
+
}
|
|
152
247
|
return NextResponse.json({ ok: true });
|
|
153
248
|
} catch (err) {
|
|
154
249
|
if (isCrudHttpError(err)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/api/pipelines/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CustomerPipeline } from '../../data/entities'\nimport {\n pipelineCreateSchema,\n pipelineUpdateSchema,\n pipelineDeleteSchema,\n type PipelineCreateInput,\n type PipelineUpdateInput,\n type PipelineDeleteInput,\n} from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.pipelines.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; organizationId: string | null; tenantId: string | null }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n const tenantId = auth.tenantId ?? null\n return { ctx, organizationId, tenantId }\n}\n\nexport async function GET(req: Request) {\n try {\n const { ctx, organizationId, tenantId } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: 'Organization and tenant context required' }, { status: 400 })\n }\n const url = new URL(req.url)\n const isDefaultParam = url.searchParams.get('isDefault')\n\n const em = (ctx.container.resolve('em') as EntityManager)\n const where: Record<string, unknown> = { organizationId, tenantId }\n if (isDefaultParam === 'true') where.isDefault = true\n if (isDefaultParam === 'false') where.isDefault = false\n\n const pipelines = await em.find(CustomerPipeline, where, { orderBy: { createdAt: 'ASC' } })\n const items = pipelines.map((pipeline) => ({\n id: pipeline.id,\n name: pipeline.name,\n isDefault: pipeline.isDefault,\n organizationId: pipeline.organizationId,\n tenantId: pipeline.tenantId,\n createdAt: pipeline.createdAt,\n updatedAt: pipeline.updatedAt,\n }))\n return NextResponse.json({ items, total: items.length })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines GET failed', err)\n return NextResponse.json({ error: 'Failed to load pipelines' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<PipelineCreateInput, { pipelineId: string }>(\n 'customers.pipelines.create',\n { input: pipelineCreateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ id: result?.pipelineId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.pipeline',\n resourceId: logEntry.resourceId ?? result?.pipelineId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines POST failed', err)\n return NextResponse.json({ error: 'Failed to create pipeline' }, { status: 400 })\n }\n}\n\nexport async function PUT(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute<PipelineUpdateInput, void>(\n 'customers.pipelines.update',\n { input: pipelineUpdateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.pipeline',\n resourceId: logEntry.resourceId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines PUT failed', err)\n return NextResponse.json({ error: 'Failed to update pipeline' }, { status: 400 })\n }\n}\n\nexport async function DELETE(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineDeleteInput, void>(\n 'customers.pipelines.delete',\n { input: pipelineDeleteSchema.parse(scoped), ctx },\n )\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines DELETE failed', err)\n return NextResponse.json({ error: 'Failed to delete pipeline' }, { status: 400 })\n }\n}\n\nconst pipelineItemSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n isDefault: z.boolean(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n createdAt: z.date(),\n updatedAt: z.date(),\n})\n\nconst pipelineListResponseSchema = z.object({\n items: z.array(pipelineItemSchema),\n total: z.number(),\n})\n\nconst pipelineCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst pipelineOkResponseSchema = z.object({\n ok: z.boolean(),\n})\n\nconst pipelineErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Manage customer pipelines',\n methods: {\n GET: {\n summary: 'List pipelines',\n description: 'Returns a list of pipelines scoped to the authenticated organization.',\n query: z.object({ isDefault: z.string().optional() }),\n responses: [\n { status: 200, description: 'Pipeline list', schema: pipelineListResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },\n { status: 400, description: 'Invalid request', schema: pipelineErrorSchema },\n ],\n },\n POST: {\n summary: 'Create pipeline',\n description: 'Creates a new pipeline within the authenticated organization.',\n requestBody: { contentType: 'application/json', schema: pipelineCreateSchema },\n responses: [\n { status: 201, description: 'Pipeline created', schema: pipelineCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: pipelineErrorSchema },\n { status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update pipeline',\n description: 'Updates an existing pipeline.',\n requestBody: { contentType: 'application/json', schema: pipelineUpdateSchema },\n responses: [\n { status: 200, description: 'Pipeline updated', schema: pipelineOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: pipelineErrorSchema },\n { status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete pipeline',\n description: 'Deletes a pipeline. Returns 409 if active deals exist.',\n requestBody: { contentType: 'application/json', schema: pipelineDeleteSchema },\n responses: [\n { status: 200, description: 'Pipeline deleted', schema: pipelineOkResponseSchema },\n ],\n errors: [\n { status: 409, description: 'Pipeline has active deals', schema: pipelineErrorSchema },\n { status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAGnD,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,yBAAyB;AAClC,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;AACpC,SAAS,kCAAkC;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CustomerPipeline } from '../../data/entities'\nimport {\n pipelineCreateSchema,\n pipelineUpdateSchema,\n pipelineDeleteSchema,\n type PipelineCreateInput,\n type PipelineUpdateInput,\n type PipelineDeleteInput,\n} from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nconst PIPELINE_RESOURCE_KIND = 'customers.pipeline'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.pipelines.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; organizationId: string | null; tenantId: string | null; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n const tenantId = auth.tenantId ?? null\n return { ctx, organizationId, tenantId, translate }\n}\n\nexport async function GET(req: Request) {\n try {\n const { ctx, organizationId, tenantId, translate } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const url = new URL(req.url)\n const isDefaultParam = url.searchParams.get('isDefault')\n\n const em = (ctx.container.resolve('em') as EntityManager)\n const where: Record<string, unknown> = { organizationId, tenantId }\n if (isDefaultParam === 'true') where.isDefault = true\n if (isDefaultParam === 'false') where.isDefault = false\n\n const pipelines = await em.find(CustomerPipeline, where, { orderBy: { createdAt: 'ASC' } })\n const items = pipelines.map((pipeline) => ({\n id: pipeline.id,\n name: pipeline.name,\n isDefault: pipeline.isDefault,\n organizationId: pipeline.organizationId,\n tenantId: pipeline.tenantId,\n createdAt: pipeline.createdAt,\n updatedAt: pipeline.updatedAt,\n }))\n return NextResponse.json({ items, total: items.length })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines GET failed', err)\n return NextResponse.json({ error: 'Failed to load pipelines' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, organizationId, tenantId, translate } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = pipelineCreateSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_RESOURCE_KIND,\n resourceId: organizationId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<PipelineCreateInput, { pipelineId: string }>(\n 'customers.pipelines.create',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_RESOURCE_KIND,\n resourceId: result?.pipelineId ?? organizationId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ id: result?.pipelineId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.pipeline',\n resourceId: logEntry.resourceId ?? result?.pipelineId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines POST failed', err)\n return NextResponse.json({ error: 'Failed to create pipeline' }, { status: 400 })\n }\n}\n\nexport async function PUT(req: Request) {\n try {\n const { ctx, organizationId, tenantId, translate } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = pipelineUpdateSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_RESOURCE_KIND,\n resourceId: input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute<PipelineUpdateInput, void>(\n 'customers.pipelines.update',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_RESOURCE_KIND,\n resourceId: input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.pipeline',\n resourceId: logEntry.resourceId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines PUT failed', err)\n return NextResponse.json({ error: 'Failed to update pipeline' }, { status: 400 })\n }\n}\n\nexport async function DELETE(req: Request) {\n try {\n const { ctx, organizationId, tenantId, translate } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = pipelineDeleteSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_RESOURCE_KIND,\n resourceId: input.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineDeleteInput, void>(\n 'customers.pipelines.delete',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: ctx.auth!.sub,\n resourceKind: PIPELINE_RESOURCE_KIND,\n resourceId: input.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines DELETE failed', err)\n return NextResponse.json({ error: 'Failed to delete pipeline' }, { status: 400 })\n }\n}\n\nconst pipelineItemSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n isDefault: z.boolean(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n createdAt: z.date(),\n updatedAt: z.date(),\n})\n\nconst pipelineListResponseSchema = z.object({\n items: z.array(pipelineItemSchema),\n total: z.number(),\n})\n\nconst pipelineCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst pipelineOkResponseSchema = z.object({\n ok: z.boolean(),\n})\n\nconst pipelineErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Manage customer pipelines',\n methods: {\n GET: {\n summary: 'List pipelines',\n description: 'Returns a list of pipelines scoped to the authenticated organization.',\n query: z.object({ isDefault: z.string().optional() }),\n responses: [\n { status: 200, description: 'Pipeline list', schema: pipelineListResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },\n { status: 400, description: 'Invalid request', schema: pipelineErrorSchema },\n ],\n },\n POST: {\n summary: 'Create pipeline',\n description: 'Creates a new pipeline within the authenticated organization.',\n requestBody: { contentType: 'application/json', schema: pipelineCreateSchema },\n responses: [\n { status: 201, description: 'Pipeline created', schema: pipelineCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: pipelineErrorSchema },\n { status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update pipeline',\n description: 'Updates an existing pipeline.',\n requestBody: { contentType: 'application/json', schema: pipelineUpdateSchema },\n responses: [\n { status: 200, description: 'Pipeline updated', schema: pipelineOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: pipelineErrorSchema },\n { status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete pipeline',\n description: 'Deletes a pipeline. Returns 409 if active deals exist.',\n requestBody: { contentType: 'application/json', schema: pipelineDeleteSchema },\n responses: [\n { status: 200, description: 'Pipeline deleted', schema: pipelineOkResponseSchema },\n ],\n errors: [\n { status: 409, description: 'Pipeline has active deals', schema: pipelineErrorSchema },\n { status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|