@open-mercato/core 0.6.6-develop.6153.1.b4c555b820 → 0.6.6-develop.6156.1.5ac693a69a
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/messages/api/[id]/actions/[actionId]/route.js +32 -0
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/archive/route.js +63 -0
- package/dist/modules/messages/api/[id]/archive/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/attachments/route.js +63 -0
- package/dist/modules/messages/api/[id]/attachments/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/archive/route.js +32 -0
- package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/read/route.js +32 -0
- package/dist/modules/messages/api/[id]/conversation/read/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/route.js +32 -0
- package/dist/modules/messages/api/[id]/conversation/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/forward/route.js +32 -0
- package/dist/modules/messages/api/[id]/forward/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/read/route.js +63 -0
- package/dist/modules/messages/api/[id]/read/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/reply/route.js +32 -0
- package/dist/modules/messages/api/[id]/reply/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +63 -0
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/guards.js +31 -0
- package/dist/modules/messages/api/guards.js.map +7 -0
- package/dist/modules/messages/api/route.js +32 -0
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +107 -0
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-entries.js +142 -1
- package/dist/modules/staff/commands/timesheets-entries.js.map +3 -3
- package/dist/modules/staff/data/validators.js +8 -0
- package/dist/modules/staff/data/validators.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +8 -22
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-ui/startTimer.js +22 -0
- package/dist/modules/staff/lib/timesheets-ui/startTimer.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js +6 -16
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +34 -0
- package/src/modules/messages/api/[id]/archive/route.ts +63 -0
- package/src/modules/messages/api/[id]/attachments/route.ts +63 -0
- package/src/modules/messages/api/[id]/conversation/archive/route.ts +33 -0
- package/src/modules/messages/api/[id]/conversation/read/route.ts +33 -0
- package/src/modules/messages/api/[id]/conversation/route.ts +33 -0
- package/src/modules/messages/api/[id]/forward/route.ts +33 -0
- package/src/modules/messages/api/[id]/read/route.ts +63 -0
- package/src/modules/messages/api/[id]/reply/route.ts +33 -0
- package/src/modules/messages/api/[id]/route.ts +65 -0
- package/src/modules/messages/api/guards.ts +59 -0
- package/src/modules/messages/api/route.ts +33 -0
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +110 -0
- package/src/modules/staff/commands/timesheets-entries.ts +166 -1
- package/src/modules/staff/data/validators.ts +9 -0
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +9 -25
- package/src/modules/staff/lib/timesheets-ui/startTimer.ts +40 -0
- package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.tsx +9 -18
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { attachOperationMetadataHeader } from "../../../lib/operationMetadata.js";
|
|
2
2
|
import { resolveMessageContext } from "../../../lib/routeHelpers.js";
|
|
3
|
+
import { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from "../../guards.js";
|
|
3
4
|
import {
|
|
4
5
|
conversationMutationResponseSchema,
|
|
5
6
|
errorResponseSchema
|
|
@@ -10,6 +11,27 @@ const metadata = {
|
|
|
10
11
|
async function DELETE(req, { params }) {
|
|
11
12
|
const { ctx, scope } = await resolveMessageContext(req);
|
|
12
13
|
const commandBus = ctx.container.resolve("commandBus");
|
|
14
|
+
const guardResult = await runMessageMutationGuards(
|
|
15
|
+
ctx.container,
|
|
16
|
+
{
|
|
17
|
+
tenantId: scope.tenantId,
|
|
18
|
+
organizationId: scope.organizationId,
|
|
19
|
+
userId: scope.userId,
|
|
20
|
+
resourceKind: "messages.conversation",
|
|
21
|
+
resourceId: params.id,
|
|
22
|
+
operation: "delete",
|
|
23
|
+
requestMethod: req.method,
|
|
24
|
+
requestHeaders: req.headers,
|
|
25
|
+
mutationPayload: null
|
|
26
|
+
},
|
|
27
|
+
resolveUserFeatures(ctx.auth)
|
|
28
|
+
);
|
|
29
|
+
if (!guardResult.ok) {
|
|
30
|
+
return Response.json(
|
|
31
|
+
guardResult.errorBody ?? { error: "Operation blocked by guard" },
|
|
32
|
+
{ status: guardResult.errorStatus ?? 422 }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
13
35
|
try {
|
|
14
36
|
const { result, logEntry } = await commandBus.execute("messages.conversation.delete_for_actor", {
|
|
15
37
|
input: {
|
|
@@ -32,6 +54,16 @@ async function DELETE(req, { params }) {
|
|
|
32
54
|
resourceKind: "messages.conversation",
|
|
33
55
|
resourceId: params.id
|
|
34
56
|
});
|
|
57
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
58
|
+
tenantId: scope.tenantId,
|
|
59
|
+
organizationId: scope.organizationId,
|
|
60
|
+
userId: scope.userId,
|
|
61
|
+
resourceKind: "messages.conversation",
|
|
62
|
+
resourceId: params.id,
|
|
63
|
+
operation: "delete",
|
|
64
|
+
requestMethod: req.method,
|
|
65
|
+
requestHeaders: req.headers
|
|
66
|
+
});
|
|
35
67
|
return response;
|
|
36
68
|
} catch (error) {
|
|
37
69
|
if (error instanceof Error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/messages/api/%5Bid%5D/conversation/route.ts"],
|
|
4
|
-
"sourcesContent": ["import type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { attachOperationMetadataHeader } from '../../../lib/operationMetadata'\nimport { resolveMessageContext } from '../../../lib/routeHelpers'\nimport {\n conversationMutationResponseSchema,\n errorResponseSchema,\n} from '../../openapi'\n\nexport const metadata = {\n DELETE: { requireAuth: true, requireFeatures: ['messages.view'] },\n}\n\nexport async function DELETE(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n\n try {\n const { result, logEntry } = await commandBus.execute('messages.conversation.delete_for_actor', {\n input: {\n anchorMessageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n\n const response = Response.json(result)\n attachOperationMetadataHeader(response, logEntry, {\n resourceKind: 'messages.conversation',\n resourceId: params.id,\n })\n return response\n } catch (error) {\n if (error instanceof Error) {\n if (error.message === 'Message not found') {\n return Response.json({ error: error.message }, { status: 404 })\n }\n if (error.message === 'Access denied') {\n return Response.json({ error: error.message }, { status: 403 })\n }\n }\n throw error\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n DELETE: {\n summary: 'Delete conversation for current actor',\n responses: [\n { status: 200, description: 'Conversation deleted', schema: conversationMutationResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAEA,SAAS,qCAAqC;AAC9C,SAAS,6BAA6B;AACtC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEA,MAAM,WAAW;AAAA,EACtB,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAClE;AAEA,eAAsB,OAAO,KAAc,EAAE,OAAO,GAA+B;AACjF,QAAM,EAAE,KAAK,MAAM,IAAI,MAAM,sBAAsB,GAAG;AACtD,QAAM,aAAa,IAAI,UAAU,QAAQ,YAAY;AAErD,MAAI;AACF,UAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW,QAAQ,0CAA0C;AAAA,MAC9F,OAAO;AAAA,QACL,iBAAiB,OAAO;AAAA,QACxB,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,QAAQ,MAAM;AAAA,MAChB;AAAA,MACA,KAAK;AAAA,QACH,WAAW,IAAI;AAAA,QACf,MAAM,IAAI,QAAQ;AAAA,QAClB,mBAAmB;AAAA,QACnB,wBAAwB,MAAM;AAAA,QAC9B,iBAAiB,MAAM,iBAAiB,CAAC,MAAM,cAAc,IAAI;AAAA,QACjE,SAAS;AAAA,MACX;AAAA,IACF,CAAC;AAED,UAAM,WAAW,SAAS,KAAK,MAAM;AACrC,kCAA8B,UAAU,UAAU;AAAA,MAChD,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,IACrB,CAAC;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,UAAI,MAAM,YAAY,qBAAqB;AACzC,eAAO,SAAS,KAAK,EAAE,OAAO,MAAM,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MAChE;AACA,UAAI,MAAM,YAAY,iBAAiB;AACrC,eAAO,SAAS,KAAK,EAAE,OAAO,MAAM,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MAChE;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,wBAAwB,QAAQ,mCAAmC;AAAA,MACjG;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,iBAAiB,QAAQ,oBAAoB;AAAA,QACzE,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,oBAAoB;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AACF;",
|
|
4
|
+
"sourcesContent": ["import type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { attachOperationMetadataHeader } from '../../../lib/operationMetadata'\nimport { resolveMessageContext } from '../../../lib/routeHelpers'\nimport { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from '../../guards'\nimport {\n conversationMutationResponseSchema,\n errorResponseSchema,\n} from '../../openapi'\n\nexport const metadata = {\n DELETE: { requireAuth: true, requireFeatures: ['messages.view'] },\n}\n\nexport async function DELETE(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n\n const guardResult = await runMessageMutationGuards(\n ctx.container,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.conversation',\n resourceId: params.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n },\n resolveUserFeatures(ctx.auth),\n )\n if (!guardResult.ok) {\n return Response.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n try {\n const { result, logEntry } = await commandBus.execute('messages.conversation.delete_for_actor', {\n input: {\n anchorMessageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n\n const response = Response.json(result)\n attachOperationMetadataHeader(response, logEntry, {\n resourceKind: 'messages.conversation',\n resourceId: params.id,\n })\n await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.conversation',\n resourceId: params.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n return response\n } catch (error) {\n if (error instanceof Error) {\n if (error.message === 'Message not found') {\n return Response.json({ error: error.message }, { status: 404 })\n }\n if (error.message === 'Access denied') {\n return Response.json({ error: error.message }, { status: 403 })\n }\n }\n throw error\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n DELETE: {\n summary: 'Delete conversation for current actor',\n responses: [\n { status: 200, description: 'Conversation deleted', schema: conversationMutationResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "AAEA,SAAS,qCAAqC;AAC9C,SAAS,6BAA6B;AACtC,SAAS,qBAAqB,qCAAqC,gCAAgC;AACnG;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEA,MAAM,WAAW;AAAA,EACtB,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAClE;AAEA,eAAsB,OAAO,KAAc,EAAE,OAAO,GAA+B;AACjF,QAAM,EAAE,KAAK,MAAM,IAAI,MAAM,sBAAsB,GAAG;AACtD,QAAM,aAAa,IAAI,UAAU,QAAQ,YAAY;AAErD,QAAM,cAAc,MAAM;AAAA,IACxB,IAAI;AAAA,IACJ;AAAA,MACE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,QAAQ,MAAM;AAAA,MACd,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,WAAW;AAAA,MACX,eAAe,IAAI;AAAA,MACnB,gBAAgB,IAAI;AAAA,MACpB,iBAAiB;AAAA,IACnB;AAAA,IACA,oBAAoB,IAAI,IAAI;AAAA,EAC9B;AACA,MAAI,CAAC,YAAY,IAAI;AACnB,WAAO,SAAS;AAAA,MACd,YAAY,aAAa,EAAE,OAAO,6BAA6B;AAAA,MAC/D,EAAE,QAAQ,YAAY,eAAe,IAAI;AAAA,IAC3C;AAAA,EACF;AAEA,MAAI;AACF,UAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW,QAAQ,0CAA0C;AAAA,MAC9F,OAAO;AAAA,QACL,iBAAiB,OAAO;AAAA,QACxB,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,QAAQ,MAAM;AAAA,MAChB;AAAA,MACA,KAAK;AAAA,QACH,WAAW,IAAI;AAAA,QACf,MAAM,IAAI,QAAQ;AAAA,QAClB,mBAAmB;AAAA,QACnB,wBAAwB,MAAM;AAAA,QAC9B,iBAAiB,MAAM,iBAAiB,CAAC,MAAM,cAAc,IAAI;AAAA,QACjE,SAAS;AAAA,MACX;AAAA,IACF,CAAC;AAED,UAAM,WAAW,SAAS,KAAK,MAAM;AACrC,kCAA8B,UAAU,UAAU;AAAA,MAChD,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,IACrB,CAAC;AACD,UAAM,oCAAoC,YAAY,uBAAuB;AAAA,MAC3E,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,QAAQ,MAAM;AAAA,MACd,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,WAAW;AAAA,MACX,eAAe,IAAI;AAAA,MACnB,gBAAgB,IAAI;AAAA,IACtB,CAAC;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,UAAI,MAAM,YAAY,qBAAqB;AACzC,eAAO,SAAS,KAAK,EAAE,OAAO,MAAM,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MAChE;AACA,UAAI,MAAM,YAAY,iBAAiB;AACrC,eAAO,SAAS,KAAK,EAAE,OAAO,MAAM,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MAChE;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,wBAAwB,QAAQ,mCAAmC;AAAA,MACjG;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,iBAAiB,QAAQ,oBAAoB;AAAA,QACzE,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,oBAAoB;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { forwardMessageSchema } from "../../../data/validators.js";
|
|
2
2
|
import { attachOperationMetadataHeader } from "../../../lib/operationMetadata.js";
|
|
3
3
|
import { canUseMessageEmailFeature, parseRequestBodySafe, resolveMessageContext } from "../../../lib/routeHelpers.js";
|
|
4
|
+
import { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from "../../guards.js";
|
|
4
5
|
import { forwardResponseSchema, forwardMessageSchema as forwardSchema } from "../../openapi.js";
|
|
5
6
|
const metadata = {
|
|
6
7
|
POST: { requireAuth: true, requireFeatures: ["messages.compose"] }
|
|
@@ -13,6 +14,27 @@ async function POST(req, { params }) {
|
|
|
13
14
|
if (input.sendViaEmail && !await canUseMessageEmailFeature(ctx, scope)) {
|
|
14
15
|
return Response.json({ error: "Missing feature: messages.email" }, { status: 403 });
|
|
15
16
|
}
|
|
17
|
+
const guardResult = await runMessageMutationGuards(
|
|
18
|
+
ctx.container,
|
|
19
|
+
{
|
|
20
|
+
tenantId: scope.tenantId,
|
|
21
|
+
organizationId: scope.organizationId,
|
|
22
|
+
userId: scope.userId,
|
|
23
|
+
resourceKind: "messages.message",
|
|
24
|
+
resourceId: null,
|
|
25
|
+
operation: "create",
|
|
26
|
+
requestMethod: req.method,
|
|
27
|
+
requestHeaders: req.headers,
|
|
28
|
+
mutationPayload: input
|
|
29
|
+
},
|
|
30
|
+
resolveUserFeatures(ctx.auth)
|
|
31
|
+
);
|
|
32
|
+
if (!guardResult.ok) {
|
|
33
|
+
return Response.json(
|
|
34
|
+
guardResult.errorBody ?? { error: "Operation blocked by guard" },
|
|
35
|
+
{ status: guardResult.errorStatus ?? 422 }
|
|
36
|
+
);
|
|
37
|
+
}
|
|
16
38
|
let commandResult;
|
|
17
39
|
try {
|
|
18
40
|
commandResult = await commandBus.execute("messages.messages.forward", {
|
|
@@ -55,6 +77,16 @@ async function POST(req, { params }) {
|
|
|
55
77
|
resourceKind: "messages.message",
|
|
56
78
|
resourceId: newMessageId
|
|
57
79
|
});
|
|
80
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
81
|
+
tenantId: scope.tenantId,
|
|
82
|
+
organizationId: scope.organizationId,
|
|
83
|
+
userId: scope.userId,
|
|
84
|
+
resourceKind: "messages.message",
|
|
85
|
+
resourceId: newMessageId,
|
|
86
|
+
operation: "create",
|
|
87
|
+
requestMethod: req.method,
|
|
88
|
+
requestHeaders: req.headers
|
|
89
|
+
});
|
|
58
90
|
return response;
|
|
59
91
|
}
|
|
60
92
|
const openApi = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/messages/api/%5Bid%5D/forward/route.ts"],
|
|
4
|
-
"sourcesContent": ["import type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport { forwardMessageSchema } from '../../../data/validators'\nimport { attachOperationMetadataHeader, OperationLogEntryLike } from '../../../lib/operationMetadata'\nimport { canUseMessageEmailFeature, parseRequestBodySafe, resolveMessageContext } from '../../../lib/routeHelpers'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { forwardResponseSchema, forwardMessageSchema as forwardSchema } from '../../openapi'\nimport { MessageCommandExecuteResult } from '../../../commands/shared'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['messages.compose'] },\n}\n\nexport async function POST(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const body = await parseRequestBodySafe(req)\n const input = forwardMessageSchema.parse(body)\n if (input.sendViaEmail && !(await canUseMessageEmailFeature(ctx, scope))) {\n return Response.json({ error: 'Missing feature: messages.email' }, { status: 403 })\n }\n\n let commandResult: { result: MessageCommandExecuteResult; logEntry: unknown }\n try {\n commandResult = await commandBus.execute<unknown, MessageCommandExecuteResult>('messages.messages.forward', {\n input: {\n ...input,\n messageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n } catch (error) {\n if (error instanceof Error) {\n if (error.message === 'Message not found') {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n if (error.message === 'Access denied') {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n if (error.message === 'Forward is not allowed for this message type') {\n return Response.json({ error: 'Forward is not allowed for this message type' }, { status: 409 })\n }\n if (error.message === 'Forward body exceeds maximum length') {\n return Response.json({ error: 'Forward body exceeds maximum length' }, { status: 413 })\n }\n }\n throw error\n }\n const newMessageId = commandResult.result.id\n\n const response = Response.json({ id: newMessageId }, { status: 201 })\n attachOperationMetadataHeader(response, commandResult.logEntry as OperationLogEntryLike, {\n resourceKind: 'messages.message',\n resourceId: newMessageId,\n })\n return response\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n POST: {\n summary: 'Forward a message and optionally include attachments from the forwarded conversation slice',\n requestBody: { schema: forwardSchema },\n responses: [\n {\n status: 201,\n description: 'Message forwarded',\n schema: forwardResponseSchema,\n },\n { status: 403, description: 'Access denied' },\n { status: 404, description: 'Message not found' },\n { status: 409, description: 'Forward not allowed for message type' },\n { status: 413, description: 'Forward body exceeds maximum length' },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,4BAA4B;AACrC,SAAS,qCAA4D;AACrE,SAAS,2BAA2B,sBAAsB,6BAA6B;
|
|
4
|
+
"sourcesContent": ["import type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport { forwardMessageSchema } from '../../../data/validators'\nimport { attachOperationMetadataHeader, OperationLogEntryLike } from '../../../lib/operationMetadata'\nimport { canUseMessageEmailFeature, parseRequestBodySafe, resolveMessageContext } from '../../../lib/routeHelpers'\nimport { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from '../../guards'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { forwardResponseSchema, forwardMessageSchema as forwardSchema } from '../../openapi'\nimport { MessageCommandExecuteResult } from '../../../commands/shared'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['messages.compose'] },\n}\n\nexport async function POST(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const body = await parseRequestBodySafe(req)\n const input = forwardMessageSchema.parse(body)\n if (input.sendViaEmail && !(await canUseMessageEmailFeature(ctx, scope))) {\n return Response.json({ error: 'Missing feature: messages.email' }, { status: 403 })\n }\n\n const guardResult = await runMessageMutationGuards(\n ctx.container,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.message',\n resourceId: null,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input as Record<string, unknown>,\n },\n resolveUserFeatures(ctx.auth),\n )\n if (!guardResult.ok) {\n return Response.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n let commandResult: { result: MessageCommandExecuteResult; logEntry: unknown }\n try {\n commandResult = await commandBus.execute<unknown, MessageCommandExecuteResult>('messages.messages.forward', {\n input: {\n ...input,\n messageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n } catch (error) {\n if (error instanceof Error) {\n if (error.message === 'Message not found') {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n if (error.message === 'Access denied') {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n if (error.message === 'Forward is not allowed for this message type') {\n return Response.json({ error: 'Forward is not allowed for this message type' }, { status: 409 })\n }\n if (error.message === 'Forward body exceeds maximum length') {\n return Response.json({ error: 'Forward body exceeds maximum length' }, { status: 413 })\n }\n }\n throw error\n }\n const newMessageId = commandResult.result.id\n\n const response = Response.json({ id: newMessageId }, { status: 201 })\n attachOperationMetadataHeader(response, commandResult.logEntry as OperationLogEntryLike, {\n resourceKind: 'messages.message',\n resourceId: newMessageId,\n })\n await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.message',\n resourceId: newMessageId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n return response\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n POST: {\n summary: 'Forward a message and optionally include attachments from the forwarded conversation slice',\n requestBody: { schema: forwardSchema },\n responses: [\n {\n status: 201,\n description: 'Message forwarded',\n schema: forwardResponseSchema,\n },\n { status: 403, description: 'Access denied' },\n { status: 404, description: 'Message not found' },\n { status: 409, description: 'Forward not allowed for message type' },\n { status: 413, description: 'Forward body exceeds maximum length' },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Message, MessageRecipient } from "../../../data/entities.js";
|
|
2
2
|
import { attachOperationMetadataHeader } from "../../../lib/operationMetadata.js";
|
|
3
3
|
import { hasOrganizationAccess, resolveMessageContext } from "../../../lib/routeHelpers.js";
|
|
4
|
+
import { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from "../../guards.js";
|
|
4
5
|
import { errorResponseSchema, okResponseSchema } from "../../openapi.js";
|
|
5
6
|
const metadata = {
|
|
6
7
|
PUT: { requireAuth: true, requireFeatures: ["messages.view"] },
|
|
@@ -35,6 +36,27 @@ async function PUT(req, { params }) {
|
|
|
35
36
|
if ("response" in context) return context.response;
|
|
36
37
|
const { ctx, scope } = context;
|
|
37
38
|
const commandBus = ctx.container.resolve("commandBus");
|
|
39
|
+
const guardResult = await runMessageMutationGuards(
|
|
40
|
+
ctx.container,
|
|
41
|
+
{
|
|
42
|
+
tenantId: scope.tenantId,
|
|
43
|
+
organizationId: scope.organizationId,
|
|
44
|
+
userId: scope.userId,
|
|
45
|
+
resourceKind: "messages.message",
|
|
46
|
+
resourceId: params.id,
|
|
47
|
+
operation: "update",
|
|
48
|
+
requestMethod: req.method,
|
|
49
|
+
requestHeaders: req.headers,
|
|
50
|
+
mutationPayload: null
|
|
51
|
+
},
|
|
52
|
+
resolveUserFeatures(ctx.auth)
|
|
53
|
+
);
|
|
54
|
+
if (!guardResult.ok) {
|
|
55
|
+
return Response.json(
|
|
56
|
+
guardResult.errorBody ?? { error: "Operation blocked by guard" },
|
|
57
|
+
{ status: guardResult.errorStatus ?? 422 }
|
|
58
|
+
);
|
|
59
|
+
}
|
|
38
60
|
const { logEntry } = await commandBus.execute("messages.recipients.mark_read", {
|
|
39
61
|
input: {
|
|
40
62
|
messageId: params.id,
|
|
@@ -56,6 +78,16 @@ async function PUT(req, { params }) {
|
|
|
56
78
|
resourceKind: "messages.message",
|
|
57
79
|
resourceId: params.id
|
|
58
80
|
});
|
|
81
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
82
|
+
tenantId: scope.tenantId,
|
|
83
|
+
organizationId: scope.organizationId,
|
|
84
|
+
userId: scope.userId,
|
|
85
|
+
resourceKind: "messages.message",
|
|
86
|
+
resourceId: params.id,
|
|
87
|
+
operation: "update",
|
|
88
|
+
requestMethod: req.method,
|
|
89
|
+
requestHeaders: req.headers
|
|
90
|
+
});
|
|
59
91
|
return response;
|
|
60
92
|
}
|
|
61
93
|
async function DELETE(req, { params }) {
|
|
@@ -63,6 +95,27 @@ async function DELETE(req, { params }) {
|
|
|
63
95
|
if ("response" in context) return context.response;
|
|
64
96
|
const { ctx, scope } = context;
|
|
65
97
|
const commandBus = ctx.container.resolve("commandBus");
|
|
98
|
+
const guardResult = await runMessageMutationGuards(
|
|
99
|
+
ctx.container,
|
|
100
|
+
{
|
|
101
|
+
tenantId: scope.tenantId,
|
|
102
|
+
organizationId: scope.organizationId,
|
|
103
|
+
userId: scope.userId,
|
|
104
|
+
resourceKind: "messages.message",
|
|
105
|
+
resourceId: params.id,
|
|
106
|
+
operation: "update",
|
|
107
|
+
requestMethod: req.method,
|
|
108
|
+
requestHeaders: req.headers,
|
|
109
|
+
mutationPayload: null
|
|
110
|
+
},
|
|
111
|
+
resolveUserFeatures(ctx.auth)
|
|
112
|
+
);
|
|
113
|
+
if (!guardResult.ok) {
|
|
114
|
+
return Response.json(
|
|
115
|
+
guardResult.errorBody ?? { error: "Operation blocked by guard" },
|
|
116
|
+
{ status: guardResult.errorStatus ?? 422 }
|
|
117
|
+
);
|
|
118
|
+
}
|
|
66
119
|
const { logEntry } = await commandBus.execute("messages.recipients.mark_unread", {
|
|
67
120
|
input: {
|
|
68
121
|
messageId: params.id,
|
|
@@ -84,6 +137,16 @@ async function DELETE(req, { params }) {
|
|
|
84
137
|
resourceKind: "messages.message",
|
|
85
138
|
resourceId: params.id
|
|
86
139
|
});
|
|
140
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
141
|
+
tenantId: scope.tenantId,
|
|
142
|
+
organizationId: scope.organizationId,
|
|
143
|
+
userId: scope.userId,
|
|
144
|
+
resourceKind: "messages.message",
|
|
145
|
+
resourceId: params.id,
|
|
146
|
+
operation: "update",
|
|
147
|
+
requestMethod: req.method,
|
|
148
|
+
requestHeaders: req.headers
|
|
149
|
+
});
|
|
87
150
|
return response;
|
|
88
151
|
}
|
|
89
152
|
const openApi = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/messages/api/%5Bid%5D/read/route.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { Message, MessageRecipient } from '../../../data/entities'\nimport { attachOperationMetadataHeader } from '../../../lib/operationMetadata'\nimport { hasOrganizationAccess, resolveMessageContext } from '../../../lib/routeHelpers'\nimport type { MessageScope } from '../../../lib/routeHelpers'\nimport { errorResponseSchema, okResponseSchema } from '../../openapi'\n\nexport const metadata = {\n PUT: { requireAuth: true, requireFeatures: ['messages.view'] },\n DELETE: { requireAuth: true, requireFeatures: ['messages.view'] },\n}\n\ntype ResolvedCtx = Awaited<ReturnType<typeof resolveMessageContext>>['ctx']\n\nasync function resolveRecipientContext(\n req: Request,\n id: string,\n): Promise<\n | { ctx: ResolvedCtx; scope: MessageScope; em: EntityManager; recipient: MessageRecipient }\n | { response: Response }\n> {\n const { ctx, scope } = await resolveMessageContext(req)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const message = await em.findOne(Message, {\n id,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (!message) {\n return { response: Response.json({ error: 'Message not found' }, { status: 404 }) }\n }\n\n if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {\n return { response: Response.json({ error: 'Access denied' }, { status: 403 }) }\n }\n\n const recipient = await em.findOne(MessageRecipient, {\n messageId: id,\n recipientUserId: scope.userId,\n deletedAt: null,\n })\n\n if (!recipient) {\n return { response: Response.json({ error: 'Access denied' }, { status: 403 }) }\n }\n\n return { ctx, scope, em, recipient }\n}\n\nexport async function PUT(req: Request, { params }: { params: { id: string } }) {\n const context = await resolveRecipientContext(req, params.id)\n if ('response' in context) return context.response\n const { ctx, scope } = context\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute('messages.recipients.mark_read', {\n input: {\n messageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n\n const response = Response.json({ ok: true })\n attachOperationMetadataHeader(response, logEntry, {\n resourceKind: 'messages.message',\n resourceId: params.id,\n })\n return response\n}\n\nexport async function DELETE(req: Request, { params }: { params: { id: string } }) {\n const context = await resolveRecipientContext(req, params.id)\n if ('response' in context) return context.response\n const { ctx, scope } = context\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute('messages.recipients.mark_unread', {\n input: {\n messageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n\n const response = Response.json({ ok: true })\n attachOperationMetadataHeader(response, logEntry, {\n resourceKind: 'messages.message',\n resourceId: params.id,\n })\n return response\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n PUT: {\n summary: 'Mark message as read',\n responses: [\n { status: 200, description: 'Message marked read', schema: okResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Mark message as unread',\n responses: [\n { status: 200, description: 'Message marked unread', schema: okResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { Message, MessageRecipient } from '../../../data/entities'\nimport { attachOperationMetadataHeader } from '../../../lib/operationMetadata'\nimport { hasOrganizationAccess, resolveMessageContext } from '../../../lib/routeHelpers'\nimport type { MessageScope } from '../../../lib/routeHelpers'\nimport { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from '../../guards'\nimport { errorResponseSchema, okResponseSchema } from '../../openapi'\n\nexport const metadata = {\n PUT: { requireAuth: true, requireFeatures: ['messages.view'] },\n DELETE: { requireAuth: true, requireFeatures: ['messages.view'] },\n}\n\ntype ResolvedCtx = Awaited<ReturnType<typeof resolveMessageContext>>['ctx']\n\nasync function resolveRecipientContext(\n req: Request,\n id: string,\n): Promise<\n | { ctx: ResolvedCtx; scope: MessageScope; em: EntityManager; recipient: MessageRecipient }\n | { response: Response }\n> {\n const { ctx, scope } = await resolveMessageContext(req)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const message = await em.findOne(Message, {\n id,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (!message) {\n return { response: Response.json({ error: 'Message not found' }, { status: 404 }) }\n }\n\n if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {\n return { response: Response.json({ error: 'Access denied' }, { status: 403 }) }\n }\n\n const recipient = await em.findOne(MessageRecipient, {\n messageId: id,\n recipientUserId: scope.userId,\n deletedAt: null,\n })\n\n if (!recipient) {\n return { response: Response.json({ error: 'Access denied' }, { status: 403 }) }\n }\n\n return { ctx, scope, em, recipient }\n}\n\nexport async function PUT(req: Request, { params }: { params: { id: string } }) {\n const context = await resolveRecipientContext(req, params.id)\n if ('response' in context) return context.response\n const { ctx, scope } = context\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const guardResult = await runMessageMutationGuards(\n ctx.container,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.message',\n resourceId: params.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n },\n resolveUserFeatures(ctx.auth),\n )\n if (!guardResult.ok) {\n return Response.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n const { logEntry } = await commandBus.execute('messages.recipients.mark_read', {\n input: {\n messageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n\n const response = Response.json({ ok: true })\n attachOperationMetadataHeader(response, logEntry, {\n resourceKind: 'messages.message',\n resourceId: params.id,\n })\n await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.message',\n resourceId: params.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n return response\n}\n\nexport async function DELETE(req: Request, { params }: { params: { id: string } }) {\n const context = await resolveRecipientContext(req, params.id)\n if ('response' in context) return context.response\n const { ctx, scope } = context\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const guardResult = await runMessageMutationGuards(\n ctx.container,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.message',\n resourceId: params.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n },\n resolveUserFeatures(ctx.auth),\n )\n if (!guardResult.ok) {\n return Response.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n const { logEntry } = await commandBus.execute('messages.recipients.mark_unread', {\n input: {\n messageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n\n const response = Response.json({ ok: true })\n attachOperationMetadataHeader(response, logEntry, {\n resourceKind: 'messages.message',\n resourceId: params.id,\n })\n await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.message',\n resourceId: params.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n return response\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n PUT: {\n summary: 'Mark message as read',\n responses: [\n { status: 200, description: 'Message marked read', schema: okResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Mark message as unread',\n responses: [\n { status: 200, description: 'Message marked unread', schema: okResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { replyMessageSchema } from "../../../data/validators.js";
|
|
2
2
|
import { attachOperationMetadataHeader } from "../../../lib/operationMetadata.js";
|
|
3
3
|
import { canUseMessageEmailFeature, resolveMessageContext } from "../../../lib/routeHelpers.js";
|
|
4
|
+
import { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from "../../guards.js";
|
|
4
5
|
import {
|
|
5
6
|
errorResponseSchema,
|
|
6
7
|
forwardResponseSchema,
|
|
@@ -17,6 +18,27 @@ async function POST(req, { params }) {
|
|
|
17
18
|
if (input.sendViaEmail && !await canUseMessageEmailFeature(ctx, scope)) {
|
|
18
19
|
return Response.json({ error: "Missing feature: messages.email" }, { status: 403 });
|
|
19
20
|
}
|
|
21
|
+
const guardResult = await runMessageMutationGuards(
|
|
22
|
+
ctx.container,
|
|
23
|
+
{
|
|
24
|
+
tenantId: scope.tenantId,
|
|
25
|
+
organizationId: scope.organizationId,
|
|
26
|
+
userId: scope.userId,
|
|
27
|
+
resourceKind: "messages.message",
|
|
28
|
+
resourceId: null,
|
|
29
|
+
operation: "create",
|
|
30
|
+
requestMethod: req.method,
|
|
31
|
+
requestHeaders: req.headers,
|
|
32
|
+
mutationPayload: input
|
|
33
|
+
},
|
|
34
|
+
resolveUserFeatures(ctx.auth)
|
|
35
|
+
);
|
|
36
|
+
if (!guardResult.ok) {
|
|
37
|
+
return Response.json(
|
|
38
|
+
guardResult.errorBody ?? { error: "Operation blocked by guard" },
|
|
39
|
+
{ status: guardResult.errorStatus ?? 422 }
|
|
40
|
+
);
|
|
41
|
+
}
|
|
20
42
|
let commandResult;
|
|
21
43
|
try {
|
|
22
44
|
commandResult = await commandBus.execute("messages.messages.reply", {
|
|
@@ -59,6 +81,16 @@ async function POST(req, { params }) {
|
|
|
59
81
|
resourceKind: "messages.message",
|
|
60
82
|
resourceId: messageId
|
|
61
83
|
});
|
|
84
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
85
|
+
tenantId: scope.tenantId,
|
|
86
|
+
organizationId: scope.organizationId,
|
|
87
|
+
userId: scope.userId,
|
|
88
|
+
resourceKind: "messages.message",
|
|
89
|
+
resourceId: messageId,
|
|
90
|
+
operation: "create",
|
|
91
|
+
requestMethod: req.method,
|
|
92
|
+
requestHeaders: req.headers
|
|
93
|
+
});
|
|
62
94
|
return response;
|
|
63
95
|
}
|
|
64
96
|
const openApi = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/messages/api/%5Bid%5D/reply/route.ts"],
|
|
4
|
-
"sourcesContent": ["import type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { replyMessageSchema } from '../../../data/validators'\nimport { attachOperationMetadataHeader } from '../../../lib/operationMetadata'\nimport { canUseMessageEmailFeature, resolveMessageContext } from '../../../lib/routeHelpers'\nimport {\n errorResponseSchema,\n forwardResponseSchema,\n replyMessageSchema as replyOpenApiSchema,\n} from '../../openapi'\nimport { MessageCommandExecuteResult } from '../../../commands/shared'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['messages.compose'] },\n}\n\nexport async function POST(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const body = await req.json().catch(() => ({}))\n const input = replyMessageSchema.parse(body)\n if (input.sendViaEmail && !(await canUseMessageEmailFeature(ctx, scope))) {\n return Response.json({ error: 'Missing feature: messages.email' }, { status: 403 })\n }\n\n let commandResult\n try {\n commandResult = await commandBus.execute<unknown, MessageCommandExecuteResult>('messages.messages.reply', {\n input: {\n ...input,\n messageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n } catch (error) {\n if (error instanceof Error) {\n if (error.message === 'Message not found') {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n if (error.message === 'Access denied') {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n if (error.message === 'Reply is not allowed for this message type') {\n return Response.json({ error: 'Reply is not allowed for this message type' }, { status: 409 })\n }\n if (error.message === 'No recipients available for reply') {\n return Response.json({ error: 'No recipients available for reply' }, { status: 409 })\n }\n }\n throw error\n }\n const messageId = commandResult.result.id\n\n const response = Response.json({ id: messageId }, { status: 201 })\n attachOperationMetadataHeader(response, commandResult.logEntry, {\n resourceKind: 'messages.message',\n resourceId: messageId,\n })\n return response\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n POST: {\n summary: 'Reply to message',\n requestBody: { schema: replyOpenApiSchema },\n responses: [\n { status: 201, description: 'Reply created', schema: forwardResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n { status: 409, description: 'Reply not allowed for message type', schema: errorResponseSchema },\n { status: 409, description: 'No recipients available for reply', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAEA,SAAS,0BAA0B;AACnC,SAAS,qCAAqC;AAC9C,SAAS,2BAA2B,6BAA6B;AACjE;AAAA,EACE;AAAA,EACA;AAAA,EACA,sBAAsB;AAAA,OACjB;AAGA,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,kBAAkB,EAAE;AACnE;AAEA,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA+B;AAC/E,QAAM,EAAE,KAAK,MAAM,IAAI,MAAM,sBAAsB,GAAG;AACtD,QAAM,aAAa,IAAI,UAAU,QAAQ,YAAY;AACrD,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,QAAM,QAAQ,mBAAmB,MAAM,IAAI;AAC3C,MAAI,MAAM,gBAAgB,CAAE,MAAM,0BAA0B,KAAK,KAAK,GAAI;AACxE,WAAO,SAAS,KAAK,EAAE,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpF;AAEA,MAAI;AACJ,MAAI;AACF,oBAAgB,MAAM,WAAW,QAA8C,2BAA2B;AAAA,MACxG,OAAO;AAAA,QACL,GAAG;AAAA,QACH,WAAW,OAAO;AAAA,QAClB,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,QAAQ,MAAM;AAAA,MAChB;AAAA,MACA,KAAK;AAAA,QACH,WAAW,IAAI;AAAA,QACf,MAAM,IAAI,QAAQ;AAAA,QAClB,mBAAmB;AAAA,QACnB,wBAAwB,MAAM;AAAA,QAC9B,iBAAiB,MAAM,iBAAiB,CAAC,MAAM,cAAc,IAAI;AAAA,QACjE,SAAS;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,UAAI,MAAM,YAAY,qBAAqB;AACzC,eAAO,SAAS,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MACtE;AACA,UAAI,MAAM,YAAY,iBAAiB;AACrC,eAAO,SAAS,KAAK,EAAE,OAAO,gBAAgB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MAClE;AACA,UAAI,MAAM,YAAY,8CAA8C;AAClE,eAAO,SAAS,KAAK,EAAE,OAAO,6CAA6C,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC/F;AACA,UAAI,MAAM,YAAY,qCAAqC;AACzD,eAAO,SAAS,KAAK,EAAE,OAAO,oCAAoC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MACtF;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACA,QAAM,YAAY,cAAc,OAAO;AAEvC,QAAM,WAAW,SAAS,KAAK,EAAE,IAAI,UAAU,GAAG,EAAE,QAAQ,IAAI,CAAC;AACjE,gCAA8B,UAAU,cAAc,UAAU;AAAA,IAC9D,cAAc;AAAA,IACd,YAAY;AAAA,EACd,CAAC;AACD,SAAO;AACT;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa,EAAE,QAAQ,mBAAmB;AAAA,MAC1C,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,iBAAiB,QAAQ,sBAAsB;AAAA,MAC7E;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,iBAAiB,QAAQ,oBAAoB;AAAA,QACzE,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,oBAAoB;AAAA,QAC7E,EAAE,QAAQ,KAAK,aAAa,sCAAsC,QAAQ,oBAAoB;AAAA,QAC9F,EAAE,QAAQ,KAAK,aAAa,qCAAqC,QAAQ,oBAAoB;AAAA,MAC/F;AAAA,IACF;AAAA,EACF;AACF;",
|
|
4
|
+
"sourcesContent": ["import type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { replyMessageSchema } from '../../../data/validators'\nimport { attachOperationMetadataHeader } from '../../../lib/operationMetadata'\nimport { canUseMessageEmailFeature, resolveMessageContext } from '../../../lib/routeHelpers'\nimport { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from '../../guards'\nimport {\n errorResponseSchema,\n forwardResponseSchema,\n replyMessageSchema as replyOpenApiSchema,\n} from '../../openapi'\nimport { MessageCommandExecuteResult } from '../../../commands/shared'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['messages.compose'] },\n}\n\nexport async function POST(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const body = await req.json().catch(() => ({}))\n const input = replyMessageSchema.parse(body)\n if (input.sendViaEmail && !(await canUseMessageEmailFeature(ctx, scope))) {\n return Response.json({ error: 'Missing feature: messages.email' }, { status: 403 })\n }\n\n const guardResult = await runMessageMutationGuards(\n ctx.container,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.message',\n resourceId: null,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input as Record<string, unknown>,\n },\n resolveUserFeatures(ctx.auth),\n )\n if (!guardResult.ok) {\n return Response.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n let commandResult\n try {\n commandResult = await commandBus.execute<unknown, MessageCommandExecuteResult>('messages.messages.reply', {\n input: {\n ...input,\n messageId: params.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n } catch (error) {\n if (error instanceof Error) {\n if (error.message === 'Message not found') {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n if (error.message === 'Access denied') {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n if (error.message === 'Reply is not allowed for this message type') {\n return Response.json({ error: 'Reply is not allowed for this message type' }, { status: 409 })\n }\n if (error.message === 'No recipients available for reply') {\n return Response.json({ error: 'No recipients available for reply' }, { status: 409 })\n }\n }\n throw error\n }\n const messageId = commandResult.result.id\n\n const response = Response.json({ id: messageId }, { status: 201 })\n attachOperationMetadataHeader(response, commandResult.logEntry, {\n resourceKind: 'messages.message',\n resourceId: messageId,\n })\n await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n resourceKind: 'messages.message',\n resourceId: messageId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n return response\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n POST: {\n summary: 'Reply to message',\n requestBody: { schema: replyOpenApiSchema },\n responses: [\n { status: 201, description: 'Reply created', schema: forwardResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n { status: 409, description: 'Reply not allowed for message type', schema: errorResponseSchema },\n { status: 409, description: 'No recipients available for reply', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,6 +7,7 @@ import { getMessageObjectType } from "../../lib/message-objects-registry.js";
|
|
|
7
7
|
import { getMessageTypeOrDefault } from "../../lib/message-types-registry.js";
|
|
8
8
|
import { attachOperationMetadataHeader } from "../../lib/operationMetadata.js";
|
|
9
9
|
import { hasOrganizationAccess, resolveMessageContext } from "../../lib/routeHelpers.js";
|
|
10
|
+
import { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from "../guards.js";
|
|
10
11
|
import {
|
|
11
12
|
errorResponseSchema,
|
|
12
13
|
messageDetailResponseSchema,
|
|
@@ -225,6 +226,27 @@ async function PATCH(req, { params }) {
|
|
|
225
226
|
if (!message.isDraft) {
|
|
226
227
|
return Response.json({ error: "Only draft messages can be edited" }, { status: 409 });
|
|
227
228
|
}
|
|
229
|
+
const guardResult = await runMessageMutationGuards(
|
|
230
|
+
ctx.container,
|
|
231
|
+
{
|
|
232
|
+
tenantId: scope.tenantId,
|
|
233
|
+
organizationId: scope.organizationId,
|
|
234
|
+
userId: scope.userId,
|
|
235
|
+
resourceKind: "messages.message",
|
|
236
|
+
resourceId: message.id,
|
|
237
|
+
operation: "update",
|
|
238
|
+
requestMethod: req.method,
|
|
239
|
+
requestHeaders: req.headers,
|
|
240
|
+
mutationPayload: input
|
|
241
|
+
},
|
|
242
|
+
resolveUserFeatures(ctx.auth)
|
|
243
|
+
);
|
|
244
|
+
if (!guardResult.ok) {
|
|
245
|
+
return Response.json(
|
|
246
|
+
guardResult.errorBody ?? { error: "Operation blocked by guard" },
|
|
247
|
+
{ status: guardResult.errorStatus ?? 422 }
|
|
248
|
+
);
|
|
249
|
+
}
|
|
228
250
|
try {
|
|
229
251
|
const { logEntry } = await commandBus.execute("messages.messages.update_draft", {
|
|
230
252
|
input: {
|
|
@@ -248,6 +270,16 @@ async function PATCH(req, { params }) {
|
|
|
248
270
|
resourceKind: "messages.message",
|
|
249
271
|
resourceId: message.id
|
|
250
272
|
});
|
|
273
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
274
|
+
tenantId: scope.tenantId,
|
|
275
|
+
organizationId: scope.organizationId,
|
|
276
|
+
userId: scope.userId,
|
|
277
|
+
resourceKind: "messages.message",
|
|
278
|
+
resourceId: message.id,
|
|
279
|
+
operation: "update",
|
|
280
|
+
requestMethod: req.method,
|
|
281
|
+
requestHeaders: req.headers
|
|
282
|
+
});
|
|
251
283
|
return response;
|
|
252
284
|
} catch (error) {
|
|
253
285
|
if (error instanceof Error) {
|
|
@@ -279,6 +311,27 @@ async function DELETE(req, { params }) {
|
|
|
279
311
|
if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {
|
|
280
312
|
return Response.json({ error: "Access denied" }, { status: 403 });
|
|
281
313
|
}
|
|
314
|
+
const guardResult = await runMessageMutationGuards(
|
|
315
|
+
ctx.container,
|
|
316
|
+
{
|
|
317
|
+
tenantId: scope.tenantId,
|
|
318
|
+
organizationId: scope.organizationId,
|
|
319
|
+
userId: scope.userId,
|
|
320
|
+
resourceKind: "messages.message",
|
|
321
|
+
resourceId: params.id,
|
|
322
|
+
operation: "delete",
|
|
323
|
+
requestMethod: req.method,
|
|
324
|
+
requestHeaders: req.headers,
|
|
325
|
+
mutationPayload: null
|
|
326
|
+
},
|
|
327
|
+
resolveUserFeatures(ctx.auth)
|
|
328
|
+
);
|
|
329
|
+
if (!guardResult.ok) {
|
|
330
|
+
return Response.json(
|
|
331
|
+
guardResult.errorBody ?? { error: "Operation blocked by guard" },
|
|
332
|
+
{ status: guardResult.errorStatus ?? 422 }
|
|
333
|
+
);
|
|
334
|
+
}
|
|
282
335
|
try {
|
|
283
336
|
const { logEntry } = await commandBus.execute("messages.messages.delete_for_actor", {
|
|
284
337
|
input: {
|
|
@@ -301,6 +354,16 @@ async function DELETE(req, { params }) {
|
|
|
301
354
|
resourceKind: "messages.message",
|
|
302
355
|
resourceId: params.id
|
|
303
356
|
});
|
|
357
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
358
|
+
tenantId: scope.tenantId,
|
|
359
|
+
organizationId: scope.organizationId,
|
|
360
|
+
userId: scope.userId,
|
|
361
|
+
resourceKind: "messages.message",
|
|
362
|
+
resourceId: params.id,
|
|
363
|
+
operation: "delete",
|
|
364
|
+
requestMethod: req.method,
|
|
365
|
+
requestHeaders: req.headers
|
|
366
|
+
});
|
|
304
367
|
return response;
|
|
305
368
|
} catch (error) {
|
|
306
369
|
if (error instanceof Error && error.message === "Access denied") {
|