@open-mercato/core 0.6.6-develop.6153.1.b4c555b820 → 0.6.6-develop.6159.1.82dfc232ca
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
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { attachOperationMetadataHeader } from "../../../../lib/operationMetadata.js";
|
|
3
3
|
import { resolveMessageContext } from "../../../../lib/routeHelpers.js";
|
|
4
|
+
import { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from "../../../guards.js";
|
|
4
5
|
import { actionResultResponseSchema } from "../../../openapi.js";
|
|
5
6
|
const metadata = {
|
|
6
7
|
POST: { requireAuth: true, requireFeatures: ["messages.actions"] }
|
|
@@ -10,6 +11,27 @@ async function POST(req, { params }) {
|
|
|
10
11
|
const commandBus = ctx.container.resolve("commandBus");
|
|
11
12
|
const rawBody = await req.json().catch(() => ({}));
|
|
12
13
|
const body = typeof rawBody === "object" && rawBody && !Array.isArray(rawBody) ? rawBody : {};
|
|
14
|
+
const guardResult = await runMessageMutationGuards(
|
|
15
|
+
ctx.container,
|
|
16
|
+
{
|
|
17
|
+
tenantId: scope.tenantId,
|
|
18
|
+
organizationId: scope.organizationId,
|
|
19
|
+
userId: scope.userId,
|
|
20
|
+
resourceKind: "messages.message",
|
|
21
|
+
resourceId: params.id,
|
|
22
|
+
operation: "update",
|
|
23
|
+
requestMethod: req.method,
|
|
24
|
+
requestHeaders: req.headers,
|
|
25
|
+
mutationPayload: body
|
|
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 commandResult = await commandBus.execute("messages.actions.execute", {
|
|
15
37
|
input: {
|
|
@@ -39,6 +61,16 @@ async function POST(req, { params }) {
|
|
|
39
61
|
resourceKind: "messages.message",
|
|
40
62
|
resourceId: params.id
|
|
41
63
|
});
|
|
64
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
65
|
+
tenantId: scope.tenantId,
|
|
66
|
+
organizationId: scope.organizationId,
|
|
67
|
+
userId: scope.userId,
|
|
68
|
+
resourceKind: "messages.message",
|
|
69
|
+
resourceId: params.id,
|
|
70
|
+
operation: "update",
|
|
71
|
+
requestMethod: req.method,
|
|
72
|
+
requestHeaders: req.headers
|
|
73
|
+
});
|
|
42
74
|
return response;
|
|
43
75
|
} catch (error) {
|
|
44
76
|
if (error instanceof Error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/messages/api/%5Bid%5D/actions/%5BactionId%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport { attachOperationMetadataHeader, type OperationLogEntryLike } from '../../../../lib/operationMetadata'\nimport { resolveMessageContext } from '../../../../lib/routeHelpers'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { actionResultResponseSchema } from '../../../openapi'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['messages.actions'] },\n}\n\nexport async function POST(\n req: Request,\n { params }: { params: { id: string; actionId: string } }\n) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n\n const rawBody = await req.json().catch(() => ({}))\n const body = (typeof rawBody === 'object' && rawBody && !Array.isArray(rawBody)\n ? rawBody\n : {}) as Record<string, unknown>\n try {\n const commandResult = await commandBus.execute('messages.actions.execute', {\n input: {\n messageId: params.id,\n actionId: params.actionId,\n payload: body,\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 result = commandResult.result as {\n ok: boolean\n actionId: string\n result: Record<string, unknown>\n operationLogEntry?: OperationLogEntryLike | null\n }\n const response = Response.json({\n ok: result.ok,\n actionId: result.actionId,\n result: result.result,\n })\n attachOperationMetadataHeader(response, result.operationLogEntry ?? null, {\n resourceKind: 'messages.message',\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: '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 === 'Action not found') {\n return Response.json({ error: 'Action not found' }, { status: 404 })\n }\n if (error.message === 'Action already taken') {\n const actionTaken = (error as Error & { actionTaken?: string }).actionTaken ?? null\n return Response.json({ error: 'Action already taken', actionTaken }, { status: 409 })\n }\n if (error.message === 'Actions have expired') {\n return Response.json({ error: 'Actions have expired' }, { status: 410 })\n }\n if (error.message === 'Action has no executable target') {\n return Response.json({ error: 'Action has no executable target' }, { status: 409 })\n }\n if (error.message === 'Action failed') {\n return Response.json({ error: 'Action failed' }, { status: 500 })\n }\n }\n throw error\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n POST: {\n summary: 'Execute message action',\n requestBody: { schema: z.record(z.string(), z.unknown()).optional() },\n responses: [\n { status: 200, description: 'Action executed', schema: actionResultResponseSchema },\n { status: 403, description: 'Access denied' },\n { status: 404, description: 'Action not found' },\n { status: 409, description: 'Action already taken' },\n { status: 410, description: 'Action expired' },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAElB,SAAS,qCAAiE;AAC1E,SAAS,6BAA6B;
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport { attachOperationMetadataHeader, type OperationLogEntryLike } from '../../../../lib/operationMetadata'\nimport { resolveMessageContext } from '../../../../lib/routeHelpers'\nimport { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from '../../../guards'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { actionResultResponseSchema } from '../../../openapi'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['messages.actions'] },\n}\n\nexport async function POST(\n req: Request,\n { params }: { params: { id: string; actionId: string } }\n) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n\n const rawBody = await req.json().catch(() => ({}))\n const body = (typeof rawBody === 'object' && rawBody && !Array.isArray(rawBody)\n ? rawBody\n : {}) as Record<string, unknown>\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: params.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: body,\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 commandResult = await commandBus.execute('messages.actions.execute', {\n input: {\n messageId: params.id,\n actionId: params.actionId,\n payload: body,\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 result = commandResult.result as {\n ok: boolean\n actionId: string\n result: Record<string, unknown>\n operationLogEntry?: OperationLogEntryLike | null\n }\n const response = Response.json({\n ok: result.ok,\n actionId: result.actionId,\n result: result.result,\n })\n attachOperationMetadataHeader(response, result.operationLogEntry ?? null, {\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 } 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 === 'Action not found') {\n return Response.json({ error: 'Action not found' }, { status: 404 })\n }\n if (error.message === 'Action already taken') {\n const actionTaken = (error as Error & { actionTaken?: string }).actionTaken ?? null\n return Response.json({ error: 'Action already taken', actionTaken }, { status: 409 })\n }\n if (error.message === 'Actions have expired') {\n return Response.json({ error: 'Actions have expired' }, { status: 410 })\n }\n if (error.message === 'Action has no executable target') {\n return Response.json({ error: 'Action has no executable target' }, { status: 409 })\n }\n if (error.message === 'Action failed') {\n return Response.json({ error: 'Action failed' }, { status: 500 })\n }\n }\n throw error\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n POST: {\n summary: 'Execute message action',\n requestBody: { schema: z.record(z.string(), z.unknown()).optional() },\n responses: [\n { status: 200, description: 'Action executed', schema: actionResultResponseSchema },\n { status: 403, description: 'Access denied' },\n { status: 404, description: 'Action not found' },\n { status: 409, description: 'Action already taken' },\n { status: 410, description: 'Action expired' },\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.archive", {
|
|
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.unarchive", {
|
|
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/archive/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.archive', {\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.unarchive', {\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: 'Archive message',\n responses: [\n { status: 200, description: 'Message archived', 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: 'Unarchive message',\n responses: [\n { status: 200, description: 'Message unarchived', 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.archive', {\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.unarchive', {\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: 'Archive message',\n responses: [\n { status: 200, description: 'Message archived', 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: 'Unarchive message',\n responses: [\n { status: 200, description: 'Message unarchived', 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
|
}
|
|
@@ -4,6 +4,7 @@ import { attachmentIdsPayloadSchema, unlinkAttachmentPayloadSchema } from "../..
|
|
|
4
4
|
import { getMessageAttachments } from "../../../lib/attachments.js";
|
|
5
5
|
import { attachOperationMetadataHeader } from "../../../lib/operationMetadata.js";
|
|
6
6
|
import { resolveMessageContext } from "../../../lib/routeHelpers.js";
|
|
7
|
+
import { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from "../../guards.js";
|
|
7
8
|
import {
|
|
8
9
|
attachmentIdsPayloadSchema as attachmentIdsOpenApiSchema,
|
|
9
10
|
errorResponseSchema,
|
|
@@ -75,6 +76,27 @@ async function POST(req, { params }) {
|
|
|
75
76
|
return Response.json({ error: "Attachments can only be modified on drafts" }, { status: 409 });
|
|
76
77
|
}
|
|
77
78
|
const commandBus = ctx.container.resolve("commandBus");
|
|
79
|
+
const guardResult = await runMessageMutationGuards(
|
|
80
|
+
ctx.container,
|
|
81
|
+
{
|
|
82
|
+
tenantId: scope.tenantId,
|
|
83
|
+
organizationId: scope.organizationId,
|
|
84
|
+
userId: scope.userId,
|
|
85
|
+
resourceKind: "messages.message",
|
|
86
|
+
resourceId: message.id,
|
|
87
|
+
operation: "update",
|
|
88
|
+
requestMethod: req.method,
|
|
89
|
+
requestHeaders: req.headers,
|
|
90
|
+
mutationPayload: input
|
|
91
|
+
},
|
|
92
|
+
resolveUserFeatures(ctx.auth)
|
|
93
|
+
);
|
|
94
|
+
if (!guardResult.ok) {
|
|
95
|
+
return Response.json(
|
|
96
|
+
guardResult.errorBody ?? { error: "Operation blocked by guard" },
|
|
97
|
+
{ status: guardResult.errorStatus ?? 422 }
|
|
98
|
+
);
|
|
99
|
+
}
|
|
78
100
|
const { logEntry } = await commandBus.execute("messages.attachments.link_to_draft", {
|
|
79
101
|
input: {
|
|
80
102
|
messageId: message.id,
|
|
@@ -97,6 +119,16 @@ async function POST(req, { params }) {
|
|
|
97
119
|
resourceKind: "messages.message",
|
|
98
120
|
resourceId: params.id
|
|
99
121
|
});
|
|
122
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
123
|
+
tenantId: scope.tenantId,
|
|
124
|
+
organizationId: scope.organizationId,
|
|
125
|
+
userId: scope.userId,
|
|
126
|
+
resourceKind: "messages.message",
|
|
127
|
+
resourceId: message.id,
|
|
128
|
+
operation: "update",
|
|
129
|
+
requestMethod: req.method,
|
|
130
|
+
requestHeaders: req.headers
|
|
131
|
+
});
|
|
100
132
|
return response;
|
|
101
133
|
}
|
|
102
134
|
async function DELETE(req, { params }) {
|
|
@@ -122,6 +154,27 @@ async function DELETE(req, { params }) {
|
|
|
122
154
|
return Response.json({ error: "Attachments can only be modified on drafts" }, { status: 409 });
|
|
123
155
|
}
|
|
124
156
|
const commandBus = ctx.container.resolve("commandBus");
|
|
157
|
+
const guardResult = await runMessageMutationGuards(
|
|
158
|
+
ctx.container,
|
|
159
|
+
{
|
|
160
|
+
tenantId: scope.tenantId,
|
|
161
|
+
organizationId: scope.organizationId,
|
|
162
|
+
userId: scope.userId,
|
|
163
|
+
resourceKind: "messages.message",
|
|
164
|
+
resourceId: message.id,
|
|
165
|
+
operation: "update",
|
|
166
|
+
requestMethod: req.method,
|
|
167
|
+
requestHeaders: req.headers,
|
|
168
|
+
mutationPayload: input
|
|
169
|
+
},
|
|
170
|
+
resolveUserFeatures(ctx.auth)
|
|
171
|
+
);
|
|
172
|
+
if (!guardResult.ok) {
|
|
173
|
+
return Response.json(
|
|
174
|
+
guardResult.errorBody ?? { error: "Operation blocked by guard" },
|
|
175
|
+
{ status: guardResult.errorStatus ?? 422 }
|
|
176
|
+
);
|
|
177
|
+
}
|
|
125
178
|
const { logEntry } = await commandBus.execute("messages.attachments.unlink_from_draft", {
|
|
126
179
|
input: {
|
|
127
180
|
messageId: message.id,
|
|
@@ -144,6 +197,16 @@ async function DELETE(req, { params }) {
|
|
|
144
197
|
resourceKind: "messages.message",
|
|
145
198
|
resourceId: params.id
|
|
146
199
|
});
|
|
200
|
+
await runMessageMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
|
|
201
|
+
tenantId: scope.tenantId,
|
|
202
|
+
organizationId: scope.organizationId,
|
|
203
|
+
userId: scope.userId,
|
|
204
|
+
resourceKind: "messages.message",
|
|
205
|
+
resourceId: message.id,
|
|
206
|
+
operation: "update",
|
|
207
|
+
requestMethod: req.method,
|
|
208
|
+
requestHeaders: req.headers
|
|
209
|
+
});
|
|
147
210
|
return response;
|
|
148
211
|
}
|
|
149
212
|
const openApi = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/messages/api/%5Bid%5D/attachments/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 { parseUnlinkAttachmentIds } from '../../../commands/attachments'\nimport { Message, MessageRecipient } from '../../../data/entities'\nimport { attachmentIdsPayloadSchema, unlinkAttachmentPayloadSchema } from '../../../data/validators'\nimport { getMessageAttachments, linkAttachmentsToMessage } from '../../../lib/attachments'\nimport { attachOperationMetadataHeader } from '../../../lib/operationMetadata'\nimport { resolveMessageContext } from '../../../lib/routeHelpers'\nimport {\n attachmentIdsPayloadSchema as attachmentIdsOpenApiSchema,\n errorResponseSchema,\n messageAttachmentResponseSchema,\n okResponseSchema,\n unlinkAttachmentPayloadSchema as unlinkAttachmentOpenApiSchema,\n} from '../../openapi'\n\nexport const metadata = {\n GET: { requireAuth: true },\n POST: { requireAuth: true, requireFeatures: ['messages.attach_files'] },\n DELETE: { requireAuth: true, requireFeatures: ['messages.attach_files'] },\n}\n\nfunction hasOrganizationAccess(scopeOrganizationId: string | null, messageOrganizationId: string | null | undefined): boolean {\n if (scopeOrganizationId) {\n return messageOrganizationId === scopeOrganizationId\n }\n return messageOrganizationId == null\n}\n\nexport async function GET(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const em = ctx.container.resolve('em') as EntityManager\n\n const message = await em.findOne(Message, {\n id: params.id,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (!message) {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n\n if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n const recipient = await em.findOne(MessageRecipient, {\n messageId: params.id,\n recipientUserId: scope.userId,\n deletedAt: null,\n })\n\n if (message.senderUserId !== scope.userId && !recipient) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n const attachments = await getMessageAttachments(\n em,\n params.id,\n scope.organizationId,\n scope.tenantId\n )\n\n return Response.json({ attachments })\n}\n\nexport async function POST(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const body = await req.json().catch(() => ({}))\n const input = attachmentIdsPayloadSchema.parse(body)\n\n const message = await em.findOne(Message, {\n id: params.id,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (!message) {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n\n if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n if (message.senderUserId !== scope.userId) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n if (!message.isDraft) {\n return Response.json({ error: 'Attachments can only be modified on drafts' }, { status: 409 })\n }\n\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute('messages.attachments.link_to_draft', {\n input: {\n messageId: message.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n attachmentIds: input.attachmentIds,\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 { ctx, scope } = await resolveMessageContext(req)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const body = await req.json().catch(() => ({}))\n const input = unlinkAttachmentPayloadSchema.parse(body)\n\n const message = await em.findOne(Message, {\n id: params.id,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (!message) {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n\n if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n if (message.senderUserId !== scope.userId) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n if (!message.isDraft) {\n return Response.json({ error: 'Attachments can only be modified on drafts' }, { status: 409 })\n }\n\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute('messages.attachments.unlink_from_draft', {\n input: {\n messageId: message.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n attachmentIds: parseUnlinkAttachmentIds(input),\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 GET: {\n summary: 'List message attachments',\n responses: [\n { status: 200, description: 'Attachments', schema: messageAttachmentResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n ],\n },\n POST: {\n summary: 'Link attachments to draft message',\n requestBody: { schema: attachmentIdsOpenApiSchema },\n responses: [\n { status: 200, description: 'Attachments linked', schema: okResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n { status: 409, description: 'Only draft messages can be edited', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Unlink attachments from draft message',\n requestBody: { schema: unlinkAttachmentOpenApiSchema },\n responses: [\n { status: 200, description: 'Attachments unlinked', schema: okResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n { status: 409, description: 'Only draft messages can be edited', 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 { parseUnlinkAttachmentIds } from '../../../commands/attachments'\nimport { Message, MessageRecipient } from '../../../data/entities'\nimport { attachmentIdsPayloadSchema, unlinkAttachmentPayloadSchema } from '../../../data/validators'\nimport { getMessageAttachments, linkAttachmentsToMessage } from '../../../lib/attachments'\nimport { attachOperationMetadataHeader } from '../../../lib/operationMetadata'\nimport { resolveMessageContext } from '../../../lib/routeHelpers'\nimport { resolveUserFeatures, runMessageMutationGuardAfterSuccess, runMessageMutationGuards } from '../../guards'\nimport {\n attachmentIdsPayloadSchema as attachmentIdsOpenApiSchema,\n errorResponseSchema,\n messageAttachmentResponseSchema,\n okResponseSchema,\n unlinkAttachmentPayloadSchema as unlinkAttachmentOpenApiSchema,\n} from '../../openapi'\n\nexport const metadata = {\n GET: { requireAuth: true },\n POST: { requireAuth: true, requireFeatures: ['messages.attach_files'] },\n DELETE: { requireAuth: true, requireFeatures: ['messages.attach_files'] },\n}\n\nfunction hasOrganizationAccess(scopeOrganizationId: string | null, messageOrganizationId: string | null | undefined): boolean {\n if (scopeOrganizationId) {\n return messageOrganizationId === scopeOrganizationId\n }\n return messageOrganizationId == null\n}\n\nexport async function GET(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const em = ctx.container.resolve('em') as EntityManager\n\n const message = await em.findOne(Message, {\n id: params.id,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (!message) {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n\n if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n const recipient = await em.findOne(MessageRecipient, {\n messageId: params.id,\n recipientUserId: scope.userId,\n deletedAt: null,\n })\n\n if (message.senderUserId !== scope.userId && !recipient) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n const attachments = await getMessageAttachments(\n em,\n params.id,\n scope.organizationId,\n scope.tenantId\n )\n\n return Response.json({ attachments })\n}\n\nexport async function POST(req: Request, { params }: { params: { id: string } }) {\n const { ctx, scope } = await resolveMessageContext(req)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const body = await req.json().catch(() => ({}))\n const input = attachmentIdsPayloadSchema.parse(body)\n\n const message = await em.findOne(Message, {\n id: params.id,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (!message) {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n\n if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n if (message.senderUserId !== scope.userId) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n if (!message.isDraft) {\n return Response.json({ error: 'Attachments can only be modified on drafts' }, { status: 409 })\n }\n\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: message.id,\n operation: 'update',\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 const { logEntry } = await commandBus.execute('messages.attachments.link_to_draft', {\n input: {\n messageId: message.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n attachmentIds: input.attachmentIds,\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: message.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 { ctx, scope } = await resolveMessageContext(req)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const body = await req.json().catch(() => ({}))\n const input = unlinkAttachmentPayloadSchema.parse(body)\n\n const message = await em.findOne(Message, {\n id: params.id,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (!message) {\n return Response.json({ error: 'Message not found' }, { status: 404 })\n }\n\n if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n if (message.senderUserId !== scope.userId) {\n return Response.json({ error: 'Access denied' }, { status: 403 })\n }\n\n if (!message.isDraft) {\n return Response.json({ error: 'Attachments can only be modified on drafts' }, { status: 409 })\n }\n\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: message.id,\n operation: 'update',\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 const { logEntry } = await commandBus.execute('messages.attachments.unlink_from_draft', {\n input: {\n messageId: message.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n attachmentIds: parseUnlinkAttachmentIds(input),\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: message.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 GET: {\n summary: 'List message attachments',\n responses: [\n { status: 200, description: 'Attachments', schema: messageAttachmentResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n ],\n },\n POST: {\n summary: 'Link attachments to draft message',\n requestBody: { schema: attachmentIdsOpenApiSchema },\n responses: [\n { status: 200, description: 'Attachments linked', schema: okResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n { status: 409, description: 'Only draft messages can be edited', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Unlink attachments from draft message',\n requestBody: { schema: unlinkAttachmentOpenApiSchema },\n responses: [\n { status: 200, description: 'Attachments unlinked', schema: okResponseSchema },\n ],\n errors: [\n { status: 403, description: 'Access denied', schema: errorResponseSchema },\n { status: 404, description: 'Message not found', schema: errorResponseSchema },\n { status: 409, description: 'Only draft messages can be edited', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -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 PUT(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: "update",
|
|
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.archive_for_actor", {
|
|
15
37
|
input: {
|
|
@@ -32,6 +54,16 @@ async function PUT(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: "update",
|
|
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/archive/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 PUT: { requireAuth: true, requireFeatures: ['messages.view'] },\n}\n\nexport async function PUT(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.archive_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 PUT: {\n summary: 'Archive conversation for current actor',\n responses: [\n { status: 200, description: 'Conversation archived', 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,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAC/D;AAEA,eAAsB,IAAI,KAAc,EAAE,OAAO,GAA+B;AAC9E,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,2CAA2C;AAAA,MAC/F,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,KAAK;AAAA,MACH,SAAS;AAAA,MACT,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,mCAAmC;AAAA,MAClG;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 PUT: { requireAuth: true, requireFeatures: ['messages.view'] },\n}\n\nexport async function PUT(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: '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\n try {\n const { result, logEntry } = await commandBus.execute('messages.conversation.archive_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: 'update',\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 PUT: {\n summary: 'Archive conversation for current actor',\n responses: [\n { status: 200, description: 'Conversation archived', 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,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAC/D;AAEA,eAAsB,IAAI,KAAc,EAAE,OAAO,GAA+B;AAC9E,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,2CAA2C;AAAA,MAC/F,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,KAAK;AAAA,MACH,SAAS;AAAA,MACT,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,mCAAmC;AAAA,MAClG;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,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: "update",
|
|
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.mark_unread_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: "update",
|
|
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/read/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.mark_unread_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: 'Mark entire conversation as unread for current actor',\n responses: [\n { status: 200, description: 'Conversation marked unread', 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,+CAA+C;AAAA,MACnG,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,8BAA8B,QAAQ,mCAAmC;AAAA,MACvG;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: '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\n try {\n const { result, logEntry } = await commandBus.execute('messages.conversation.mark_unread_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: 'update',\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: 'Mark entire conversation as unread for current actor',\n responses: [\n { status: 200, description: 'Conversation marked unread', 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,+CAA+C;AAAA,MACnG,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,8BAA8B,QAAQ,mCAAmC;AAAA,MACvG;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
|
}
|