@open-mercato/core 0.10.1-develop.7785.1.ec0898da36 → 0.10.1-develop.7786.1.g3645046322
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/configs/api/cache/route.js +18 -45
- package/dist/modules/configs/api/cache/route.js.map +2 -2
- package/dist/modules/integrations/api/guards.js +37 -7
- package/dist/modules/integrations/api/guards.js.map +2 -2
- package/dist/modules/messages/api/guards.js +37 -7
- package/dist/modules/messages/api/guards.js.map +2 -2
- package/dist/modules/payment_gateways/api/guards.js +37 -7
- package/dist/modules/payment_gateways/api/guards.js.map +2 -2
- package/dist/modules/push_notifications/api/custom-send/route.js +18 -38
- package/dist/modules/push_notifications/api/custom-send/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js +17 -49
- package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/send/route.js +17 -49
- package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/configs/api/cache/route.ts +19 -48
- package/src/modules/integrations/api/guards.ts +45 -7
- package/src/modules/messages/api/guards.ts +45 -7
- package/src/modules/payment_gateways/api/guards.ts +45 -7
- package/src/modules/push_notifications/api/custom-send/route.ts +18 -52
- package/src/modules/sales/api/quotes/convert/route.ts +17 -75
- package/src/modules/sales/api/quotes/send/route.ts +17 -75
|
@@ -4,10 +4,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
5
5
|
import { runWithCacheTenant } from "@open-mercato/cache";
|
|
6
6
|
import { collectCrudCacheStats, purgeCrudCacheSegment } from "@open-mercato/shared/lib/crud/cache-stats";
|
|
7
|
-
import {
|
|
8
|
-
bridgeLegacyGuard,
|
|
9
|
-
runMutationGuards
|
|
10
|
-
} from "@open-mercato/shared/lib/crud/mutation-guard-registry";
|
|
7
|
+
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
11
8
|
import {
|
|
12
9
|
configsTag,
|
|
13
10
|
cacheStatsResponseSchema,
|
|
@@ -22,10 +19,6 @@ const metadata = {
|
|
|
22
19
|
GET: { requireAuth: true, requireFeatures: ["configs.cache.view"] },
|
|
23
20
|
POST: { requireAuth: true, requireFeatures: ["configs.cache.manage"] }
|
|
24
21
|
};
|
|
25
|
-
function resolveUserFeatures(auth) {
|
|
26
|
-
const features = auth?.features;
|
|
27
|
-
return Array.isArray(features) ? features.filter((value) => typeof value === "string") : [];
|
|
28
|
-
}
|
|
29
22
|
async function GET(req) {
|
|
30
23
|
const auth = await getAuthFromRequest(req);
|
|
31
24
|
if (!auth?.sub) {
|
|
@@ -85,30 +78,23 @@ async function POST(req) {
|
|
|
85
78
|
{ status: 400 }
|
|
86
79
|
);
|
|
87
80
|
}
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (guard) {
|
|
102
|
-
const guardResult = await runMutationGuards([guard], guardInput, {
|
|
103
|
-
userFeatures: resolveUserFeatures(auth)
|
|
104
|
-
});
|
|
105
|
-
if (!guardResult.ok) {
|
|
106
|
-
return NextResponse.json(
|
|
107
|
-
guardResult.errorBody ?? { error: translate("configs.cache.purgeError", "Failed to purge cache segment.") },
|
|
108
|
-
{ status: guardResult.errorStatus ?? 422 }
|
|
109
|
-
);
|
|
81
|
+
const guardResult = await runRouteMutationGuards({
|
|
82
|
+
container,
|
|
83
|
+
req,
|
|
84
|
+
auth: {
|
|
85
|
+
tenantId: tenantScope ?? "",
|
|
86
|
+
organizationId: auth.orgId ?? null,
|
|
87
|
+
userId: auth.sub
|
|
88
|
+
},
|
|
89
|
+
input: {
|
|
90
|
+
resourceKind: "configs.cache",
|
|
91
|
+
resourceId: segment ?? tenantScope ?? "all",
|
|
92
|
+
operation: "delete",
|
|
93
|
+
mutationPayload: { action, ...segment ? { segment } : {} }
|
|
110
94
|
}
|
|
111
|
-
|
|
95
|
+
});
|
|
96
|
+
if (!guardResult.ok) {
|
|
97
|
+
return guardResult.response;
|
|
112
98
|
}
|
|
113
99
|
let responseBody;
|
|
114
100
|
if (action === "purgeSegment") {
|
|
@@ -120,20 +106,7 @@ async function POST(req) {
|
|
|
120
106
|
const stats = await runWithCacheTenant(tenantScope, () => collectCrudCacheStats(cache));
|
|
121
107
|
responseBody = { action: "purgeAll", stats };
|
|
122
108
|
}
|
|
123
|
-
|
|
124
|
-
if (!callback.guard.afterSuccess) continue;
|
|
125
|
-
await callback.guard.afterSuccess({
|
|
126
|
-
tenantId: tenantScope ?? "",
|
|
127
|
-
organizationId: auth.orgId ?? null,
|
|
128
|
-
userId: auth.sub,
|
|
129
|
-
resourceKind: "configs.cache",
|
|
130
|
-
resourceId: segment ?? tenantScope ?? "all",
|
|
131
|
-
operation: "delete",
|
|
132
|
-
requestMethod: req.method,
|
|
133
|
-
requestHeaders: req.headers,
|
|
134
|
-
metadata: callback.metadata ?? null
|
|
135
|
-
});
|
|
136
|
-
}
|
|
109
|
+
await guardResult.runAfterSuccess();
|
|
137
110
|
return NextResponse.json(responseBody);
|
|
138
111
|
} catch (error) {
|
|
139
112
|
logger.error("failed to purge cache", { component: "cache", err: error });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/configs/api/cache/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport { collectCrudCacheStats, purgeCrudCacheSegment } from '@open-mercato/shared/lib/crud/cache-stats'\nimport {
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,0BAA8C;AACvD,SAAS,uBAAuB,6BAA6B;AAC7D
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport { collectCrudCacheStats, purgeCrudCacheSegment } from '@open-mercato/shared/lib/crud/cache-stats'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport {\n configsTag,\n cacheStatsResponseSchema,\n cachePurgeRequestSchema,\n cachePurgeAllResponseSchema,\n cachePurgeSegmentResponseSchema,\n configErrorSchema,\n} from '../openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('configs')\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['configs.cache.view'] },\n POST: { requireAuth: true, requireFeatures: ['configs.cache.manage'] },\n} as const\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const { translate } = await resolveTranslations()\n\n try {\n const container = await createRequestContainer()\n let cache: CacheStrategy\n try {\n cache = (container.resolve('cache') as CacheStrategy)\n } catch {\n return NextResponse.json(\n { error: translate('configs.cache.unavailable', 'Cache service is unavailable.') },\n { status: 503 },\n )\n }\n const stats = await runWithCacheTenant(auth.tenantId ?? null, () => collectCrudCacheStats(cache))\n return NextResponse.json(stats)\n } catch (error) {\n logger.error('failed to resolve cache stats', { component: 'cache', err: error })\n return NextResponse.json(\n { error: translate('configs.cache.unavailable', 'Cache service is unavailable.') },\n { status: 500 },\n )\n }\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const { translate } = await resolveTranslations()\n\n let body: any = null\n try {\n body = await req.json()\n } catch {\n body = null\n }\n\n const action = typeof body?.action === 'string' ? body.action : 'purgeAll'\n\n try {\n const container = await createRequestContainer()\n let cache: CacheStrategy\n try {\n cache = (container.resolve('cache') as CacheStrategy)\n } catch {\n return NextResponse.json(\n { error: translate('configs.cache.unavailable', 'Cache service is unavailable.') },\n { status: 503 },\n )\n }\n const tenantScope = auth.tenantId ?? null\n\n const segment =\n action === 'purgeSegment'\n ? (typeof body?.segment === 'string' ? body.segment.trim() : '')\n : null\n if (action === 'purgeSegment' && !segment) {\n return NextResponse.json(\n { error: translate('configs.cache.purgeError', 'Failed to purge cache segment.') },\n { status: 400 },\n )\n }\n\n // Mutation-guard contract for this custom write route (packages/core/AGENTS.md\n // \u2192 API Routes). A cache purge carries no per-record optimistic-lock version, so\n // the default OSS guard short-circuits; wiring it keeps the route on the shared\n // write-guard interception path for any tenant-registered guard.\n const guardResult = await runRouteMutationGuards({\n container,\n req,\n auth: {\n tenantId: tenantScope ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n },\n input: {\n resourceKind: 'configs.cache',\n resourceId: segment ?? tenantScope ?? 'all',\n operation: 'delete',\n mutationPayload: { action, ...(segment ? { segment } : {}) },\n },\n })\n if (!guardResult.ok) {\n return guardResult.response\n }\n\n let responseBody: Record<string, unknown>\n if (action === 'purgeSegment') {\n const result = await runWithCacheTenant(tenantScope, () => purgeCrudCacheSegment(cache, segment!))\n const stats = await runWithCacheTenant(tenantScope, () => collectCrudCacheStats(cache))\n responseBody = { action: 'purgeSegment', segment, deleted: result.deleted, stats }\n } else {\n await runWithCacheTenant(tenantScope, () => cache.clear())\n const stats = await runWithCacheTenant(tenantScope, () => collectCrudCacheStats(cache))\n responseBody = { action: 'purgeAll', stats }\n }\n\n await guardResult.runAfterSuccess()\n\n return NextResponse.json(responseBody)\n } catch (error) {\n logger.error('failed to purge cache', { component: 'cache', err: error })\n return NextResponse.json(\n { error: translate('configs.cache.purgeError', 'Failed to purge cache segment.') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: configsTag,\n summary: 'Cache management',\n methods: {\n GET: {\n summary: 'Get cache statistics',\n description: 'Returns detailed cache statistics including total entries and breakdown by cache segments. Requires cache service to be available.',\n responses: [\n { status: 200, description: 'Cache statistics', schema: cacheStatsResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to resolve cache stats', schema: configErrorSchema },\n { status: 503, description: 'Cache service unavailable', schema: configErrorSchema },\n ],\n },\n POST: {\n summary: 'Purge cache',\n description: 'Purges cache entries. Supports two actions: purgeAll (clears entire cache) or purgeSegment (clears specific segment). Returns updated cache statistics after purge.',\n requestBody: {\n contentType: 'application/json',\n schema: cachePurgeRequestSchema,\n },\n responses: [\n { status: 200, description: 'All cache cleared successfully', schema: cachePurgeAllResponseSchema },\n { status: 200, description: 'Cache segment cleared successfully', schema: cachePurgeSegmentResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid request - missing segment identifier for purgeSegment action', schema: configErrorSchema },\n { status: 401, description: 'Unauthorized', schema: configErrorSchema },\n { status: 500, description: 'Failed to purge cache', schema: configErrorSchema },\n { status: 503, description: 'Cache service unavailable', schema: configErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,25 +2,55 @@ import {
|
|
|
2
2
|
bridgeLegacyGuard,
|
|
3
3
|
runMutationGuards
|
|
4
4
|
} from "@open-mercato/shared/lib/crud/mutation-guard-registry";
|
|
5
|
+
import { getAllMutationGuardInstances } from "@open-mercato/shared/lib/crud/mutation-guard-store";
|
|
6
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
7
|
+
import { getTelemetryRuntime } from "@open-mercato/shared/lib/telemetry/runtime";
|
|
8
|
+
const logger = createLogger("integrations");
|
|
5
9
|
function resolveUserFeatures(auth) {
|
|
6
10
|
const features = auth?.features;
|
|
7
11
|
if (!Array.isArray(features)) return [];
|
|
8
12
|
return features.filter((value) => typeof value === "string");
|
|
9
13
|
}
|
|
10
|
-
async function runIntegrationMutationGuards(container, input,
|
|
14
|
+
async function runIntegrationMutationGuards(container, input, extraFeatures = []) {
|
|
15
|
+
const allGuards = [...getAllMutationGuardInstances()];
|
|
11
16
|
const legacyGuard = bridgeLegacyGuard(container);
|
|
12
|
-
if (
|
|
17
|
+
if (legacyGuard) allGuards.push(legacyGuard);
|
|
18
|
+
if (!allGuards.length) {
|
|
13
19
|
return { ok: true, afterSuccessCallbacks: [] };
|
|
14
20
|
}
|
|
15
|
-
|
|
21
|
+
let grantedFeatures = [];
|
|
22
|
+
try {
|
|
23
|
+
const rbacService = container.resolve("rbacService");
|
|
24
|
+
grantedFeatures = await rbacService?.getGrantedFeatures?.(input.userId, {
|
|
25
|
+
tenantId: input.tenantId,
|
|
26
|
+
organizationId: input.organizationId
|
|
27
|
+
}) ?? [];
|
|
28
|
+
} catch (err) {
|
|
29
|
+
logger.error("Failed to resolve mutation guard feature grants", { err });
|
|
30
|
+
getTelemetryRuntime()?.reportError(err, {
|
|
31
|
+
module: "integrations",
|
|
32
|
+
code: "integrations.mutation_guard_feature_resolution_failed"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const userFeatures = Array.from(/* @__PURE__ */ new Set([...grantedFeatures, ...extraFeatures]));
|
|
36
|
+
return runMutationGuards(allGuards, input, { userFeatures });
|
|
16
37
|
}
|
|
17
38
|
async function runIntegrationMutationGuardAfterSuccess(callbacks, input) {
|
|
18
39
|
for (const callback of callbacks) {
|
|
19
40
|
if (!callback.guard.afterSuccess) continue;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
try {
|
|
42
|
+
await callback.guard.afterSuccess({
|
|
43
|
+
...input,
|
|
44
|
+
metadata: callback.metadata ?? null
|
|
45
|
+
});
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logger.error("Mutation guard afterSuccess failed", { guardId: callback.guard.id, err });
|
|
48
|
+
getTelemetryRuntime()?.reportError(err, {
|
|
49
|
+
module: "integrations",
|
|
50
|
+
code: "integrations.mutation_guard_after_success_failed",
|
|
51
|
+
attributes: { guardId: callback.guard.id }
|
|
52
|
+
});
|
|
53
|
+
}
|
|
24
54
|
}
|
|
25
55
|
}
|
|
26
56
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/integrations/api/guards.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport {\n bridgeLegacyGuard,\n runMutationGuards,\n type MutationGuard,\n type MutationGuardInput,\n} from '@open-mercato/shared/lib/crud/mutation-guard-registry'\n\ntype GuardAfterCallback = {\n guard: MutationGuard\n metadata: Record<string, unknown> | null\n}\n\nexport function resolveUserFeatures(auth: unknown): string[] {\n const features = (auth as { features?: unknown })?.features\n if (!Array.isArray(features)) return []\n return features.filter((value): value is string => typeof value === 'string')\n}\n\nexport async function runIntegrationMutationGuards(\n container: AwilixContainer,\n input: MutationGuardInput,\n
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;
|
|
4
|
+
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport {\n bridgeLegacyGuard,\n runMutationGuards,\n type MutationGuard,\n type MutationGuardInput,\n} from '@open-mercato/shared/lib/crud/mutation-guard-registry'\nimport { getAllMutationGuardInstances } from '@open-mercato/shared/lib/crud/mutation-guard-store'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'\n\nconst logger = createLogger('integrations')\n\ntype GuardAfterCallback = {\n guard: MutationGuard\n metadata: Record<string, unknown> | null\n}\n\n/** @deprecated Mutation guard grants are resolved from RBAC by `runIntegrationMutationGuards`. */\nexport function resolveUserFeatures(auth: unknown): string[] {\n const features = (auth as { features?: unknown })?.features\n if (!Array.isArray(features)) return []\n return features.filter((value): value is string => typeof value === 'string')\n}\n\nexport async function runIntegrationMutationGuards(\n container: AwilixContainer,\n input: MutationGuardInput,\n extraFeatures: string[] = [],\n): Promise<{\n ok: boolean\n errorBody?: Record<string, unknown>\n errorStatus?: number\n modifiedPayload?: Record<string, unknown>\n afterSuccessCallbacks: GuardAfterCallback[]\n}> {\n const allGuards: MutationGuard[] = [...getAllMutationGuardInstances()]\n const legacyGuard = bridgeLegacyGuard(container)\n if (legacyGuard) allGuards.push(legacyGuard)\n if (!allGuards.length) {\n return { ok: true, afterSuccessCallbacks: [] }\n }\n\n let grantedFeatures: string[] = []\n try {\n const rbacService = container.resolve('rbacService') as {\n getGrantedFeatures?: (\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n } | undefined\n grantedFeatures = await rbacService?.getGrantedFeatures?.(input.userId, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n }) ?? []\n } catch (err) {\n logger.error('Failed to resolve mutation guard feature grants', { err })\n getTelemetryRuntime()?.reportError(err, {\n module: 'integrations',\n code: 'integrations.mutation_guard_feature_resolution_failed',\n })\n }\n\n const userFeatures = Array.from(new Set([...grantedFeatures, ...extraFeatures]))\n return runMutationGuards(allGuards, input, { userFeatures })\n}\n\nexport async function runIntegrationMutationGuardAfterSuccess(\n callbacks: GuardAfterCallback[],\n input: {\n tenantId: string\n organizationId: string | null\n userId: string\n resourceKind: string\n resourceId: string\n operation: 'create' | 'update' | 'delete'\n requestMethod: string\n requestHeaders: Headers\n },\n): Promise<void> {\n for (const callback of callbacks) {\n if (!callback.guard.afterSuccess) continue\n try {\n await callback.guard.afterSuccess({\n ...input,\n metadata: callback.metadata ?? null,\n })\n } catch (err) {\n logger.error('Mutation guard afterSuccess failed', { guardId: callback.guard.id, err })\n getTelemetryRuntime()?.reportError(err, {\n module: 'integrations',\n code: 'integrations.mutation_guard_after_success_failed',\n attributes: { guardId: callback.guard.id },\n })\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,oCAAoC;AAC7C,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AAEpC,MAAM,SAAS,aAAa,cAAc;AAQnC,SAAS,oBAAoB,MAAyB;AAC3D,QAAM,WAAY,MAAiC;AACnD,MAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG,QAAO,CAAC;AACtC,SAAO,SAAS,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ;AAC9E;AAEA,eAAsB,6BACpB,WACA,OACA,gBAA0B,CAAC,GAO1B;AACD,QAAM,YAA6B,CAAC,GAAG,6BAA6B,CAAC;AACrE,QAAM,cAAc,kBAAkB,SAAS;AAC/C,MAAI,YAAa,WAAU,KAAK,WAAW;AAC3C,MAAI,CAAC,UAAU,QAAQ;AACrB,WAAO,EAAE,IAAI,MAAM,uBAAuB,CAAC,EAAE;AAAA,EAC/C;AAEA,MAAI,kBAA4B,CAAC;AACjC,MAAI;AACF,UAAM,cAAc,UAAU,QAAQ,aAAa;AAMnD,sBAAkB,MAAM,aAAa,qBAAqB,MAAM,QAAQ;AAAA,MACtE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC,KAAK,CAAC;AAAA,EACT,SAAS,KAAK;AACZ,WAAO,MAAM,mDAAmD,EAAE,IAAI,CAAC;AACvE,wBAAoB,GAAG,YAAY,KAAK;AAAA,MACtC,QAAQ;AAAA,MACR,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,aAAa,CAAC,CAAC;AAC/E,SAAO,kBAAkB,WAAW,OAAO,EAAE,aAAa,CAAC;AAC7D;AAEA,eAAsB,wCACpB,WACA,OAUe;AACf,aAAW,YAAY,WAAW;AAChC,QAAI,CAAC,SAAS,MAAM,aAAc;AAClC,QAAI;AACF,YAAM,SAAS,MAAM,aAAa;AAAA,QAChC,GAAG;AAAA,QACH,UAAU,SAAS,YAAY;AAAA,MACjC,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO,MAAM,sCAAsC,EAAE,SAAS,SAAS,MAAM,IAAI,IAAI,CAAC;AACtF,0BAAoB,GAAG,YAAY,KAAK;AAAA,QACtC,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,YAAY,EAAE,SAAS,SAAS,MAAM,GAAG;AAAA,MAC3C,CAAC;AAAA,IACH;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,25 +2,55 @@ import {
|
|
|
2
2
|
bridgeLegacyGuard,
|
|
3
3
|
runMutationGuards
|
|
4
4
|
} from "@open-mercato/shared/lib/crud/mutation-guard-registry";
|
|
5
|
+
import { getAllMutationGuardInstances } from "@open-mercato/shared/lib/crud/mutation-guard-store";
|
|
6
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
7
|
+
import { getTelemetryRuntime } from "@open-mercato/shared/lib/telemetry/runtime";
|
|
8
|
+
const logger = createLogger("messages");
|
|
5
9
|
function resolveUserFeatures(auth) {
|
|
6
10
|
const features = auth?.features;
|
|
7
11
|
if (!Array.isArray(features)) return [];
|
|
8
12
|
return features.filter((value) => typeof value === "string");
|
|
9
13
|
}
|
|
10
|
-
async function runMessageMutationGuards(container, input,
|
|
14
|
+
async function runMessageMutationGuards(container, input, extraFeatures = []) {
|
|
15
|
+
const allGuards = [...getAllMutationGuardInstances()];
|
|
11
16
|
const legacyGuard = bridgeLegacyGuard(container);
|
|
12
|
-
if (
|
|
17
|
+
if (legacyGuard) allGuards.push(legacyGuard);
|
|
18
|
+
if (!allGuards.length) {
|
|
13
19
|
return { ok: true, afterSuccessCallbacks: [] };
|
|
14
20
|
}
|
|
15
|
-
|
|
21
|
+
let grantedFeatures = [];
|
|
22
|
+
try {
|
|
23
|
+
const rbacService = container.resolve("rbacService");
|
|
24
|
+
grantedFeatures = await rbacService?.getGrantedFeatures?.(input.userId, {
|
|
25
|
+
tenantId: input.tenantId,
|
|
26
|
+
organizationId: input.organizationId
|
|
27
|
+
}) ?? [];
|
|
28
|
+
} catch (err) {
|
|
29
|
+
logger.error("Failed to resolve mutation guard feature grants", { err });
|
|
30
|
+
getTelemetryRuntime()?.reportError(err, {
|
|
31
|
+
module: "messages",
|
|
32
|
+
code: "messages.mutation_guard_feature_resolution_failed"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const userFeatures = Array.from(/* @__PURE__ */ new Set([...grantedFeatures, ...extraFeatures]));
|
|
36
|
+
return runMutationGuards(allGuards, input, { userFeatures });
|
|
16
37
|
}
|
|
17
38
|
async function runMessageMutationGuardAfterSuccess(callbacks, input) {
|
|
18
39
|
for (const callback of callbacks) {
|
|
19
40
|
if (!callback.guard.afterSuccess) continue;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
try {
|
|
42
|
+
await callback.guard.afterSuccess({
|
|
43
|
+
...input,
|
|
44
|
+
metadata: callback.metadata ?? null
|
|
45
|
+
});
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logger.error("Mutation guard afterSuccess failed", { guardId: callback.guard.id, err });
|
|
48
|
+
getTelemetryRuntime()?.reportError(err, {
|
|
49
|
+
module: "messages",
|
|
50
|
+
code: "messages.mutation_guard_after_success_failed",
|
|
51
|
+
attributes: { guardId: callback.guard.id }
|
|
52
|
+
});
|
|
53
|
+
}
|
|
24
54
|
}
|
|
25
55
|
}
|
|
26
56
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/messages/api/guards.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport {\n bridgeLegacyGuard,\n runMutationGuards,\n type MutationGuard,\n type MutationGuardInput,\n} from '@open-mercato/shared/lib/crud/mutation-guard-registry'\n\ntype GuardAfterCallback = {\n guard: MutationGuard\n metadata: Record<string, unknown> | null\n}\n\nexport function resolveUserFeatures(auth: unknown): string[] {\n const features = (auth as { features?: unknown })?.features\n if (!Array.isArray(features)) return []\n return features.filter((value): value is string => typeof value === 'string')\n}\n\nexport async function runMessageMutationGuards(\n container: AwilixContainer,\n input: MutationGuardInput,\n
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;
|
|
4
|
+
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport {\n bridgeLegacyGuard,\n runMutationGuards,\n type MutationGuard,\n type MutationGuardInput,\n} from '@open-mercato/shared/lib/crud/mutation-guard-registry'\nimport { getAllMutationGuardInstances } from '@open-mercato/shared/lib/crud/mutation-guard-store'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'\n\nconst logger = createLogger('messages')\n\ntype GuardAfterCallback = {\n guard: MutationGuard\n metadata: Record<string, unknown> | null\n}\n\n/** @deprecated Mutation guard grants are resolved from RBAC by `runMessageMutationGuards`. */\nexport function resolveUserFeatures(auth: unknown): string[] {\n const features = (auth as { features?: unknown })?.features\n if (!Array.isArray(features)) return []\n return features.filter((value): value is string => typeof value === 'string')\n}\n\nexport async function runMessageMutationGuards(\n container: AwilixContainer,\n input: MutationGuardInput,\n extraFeatures: string[] = [],\n): Promise<{\n ok: boolean\n errorBody?: Record<string, unknown>\n errorStatus?: number\n modifiedPayload?: Record<string, unknown>\n afterSuccessCallbacks: GuardAfterCallback[]\n}> {\n const allGuards: MutationGuard[] = [...getAllMutationGuardInstances()]\n const legacyGuard = bridgeLegacyGuard(container)\n if (legacyGuard) allGuards.push(legacyGuard)\n if (!allGuards.length) {\n return { ok: true, afterSuccessCallbacks: [] }\n }\n\n let grantedFeatures: string[] = []\n try {\n const rbacService = container.resolve('rbacService') as {\n getGrantedFeatures?: (\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n } | undefined\n grantedFeatures = await rbacService?.getGrantedFeatures?.(input.userId, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n }) ?? []\n } catch (err) {\n logger.error('Failed to resolve mutation guard feature grants', { err })\n getTelemetryRuntime()?.reportError(err, {\n module: 'messages',\n code: 'messages.mutation_guard_feature_resolution_failed',\n })\n }\n\n const userFeatures = Array.from(new Set([...grantedFeatures, ...extraFeatures]))\n return runMutationGuards(allGuards, input, { userFeatures })\n}\n\nexport async function runMessageMutationGuardAfterSuccess(\n callbacks: GuardAfterCallback[],\n input: {\n tenantId: string\n organizationId: string | null\n userId: string\n resourceKind: string\n resourceId: string\n operation: 'create' | 'update' | 'delete'\n requestMethod: string\n requestHeaders: Headers\n },\n): Promise<void> {\n for (const callback of callbacks) {\n if (!callback.guard.afterSuccess) continue\n try {\n await callback.guard.afterSuccess({\n ...input,\n metadata: callback.metadata ?? null,\n })\n } catch (err) {\n logger.error('Mutation guard afterSuccess failed', { guardId: callback.guard.id, err })\n getTelemetryRuntime()?.reportError(err, {\n module: 'messages',\n code: 'messages.mutation_guard_after_success_failed',\n attributes: { guardId: callback.guard.id },\n })\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,oCAAoC;AAC7C,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AAEpC,MAAM,SAAS,aAAa,UAAU;AAQ/B,SAAS,oBAAoB,MAAyB;AAC3D,QAAM,WAAY,MAAiC;AACnD,MAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG,QAAO,CAAC;AACtC,SAAO,SAAS,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ;AAC9E;AAEA,eAAsB,yBACpB,WACA,OACA,gBAA0B,CAAC,GAO1B;AACD,QAAM,YAA6B,CAAC,GAAG,6BAA6B,CAAC;AACrE,QAAM,cAAc,kBAAkB,SAAS;AAC/C,MAAI,YAAa,WAAU,KAAK,WAAW;AAC3C,MAAI,CAAC,UAAU,QAAQ;AACrB,WAAO,EAAE,IAAI,MAAM,uBAAuB,CAAC,EAAE;AAAA,EAC/C;AAEA,MAAI,kBAA4B,CAAC;AACjC,MAAI;AACF,UAAM,cAAc,UAAU,QAAQ,aAAa;AAMnD,sBAAkB,MAAM,aAAa,qBAAqB,MAAM,QAAQ;AAAA,MACtE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC,KAAK,CAAC;AAAA,EACT,SAAS,KAAK;AACZ,WAAO,MAAM,mDAAmD,EAAE,IAAI,CAAC;AACvE,wBAAoB,GAAG,YAAY,KAAK;AAAA,MACtC,QAAQ;AAAA,MACR,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,aAAa,CAAC,CAAC;AAC/E,SAAO,kBAAkB,WAAW,OAAO,EAAE,aAAa,CAAC;AAC7D;AAEA,eAAsB,oCACpB,WACA,OAUe;AACf,aAAW,YAAY,WAAW;AAChC,QAAI,CAAC,SAAS,MAAM,aAAc;AAClC,QAAI;AACF,YAAM,SAAS,MAAM,aAAa;AAAA,QAChC,GAAG;AAAA,QACH,UAAU,SAAS,YAAY;AAAA,MACjC,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO,MAAM,sCAAsC,EAAE,SAAS,SAAS,MAAM,IAAI,IAAI,CAAC;AACtF,0BAAoB,GAAG,YAAY,KAAK;AAAA,QACtC,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,YAAY,EAAE,SAAS,SAAS,MAAM,GAAG;AAAA,MAC3C,CAAC;AAAA,IACH;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,25 +2,55 @@ import {
|
|
|
2
2
|
bridgeLegacyGuard,
|
|
3
3
|
runMutationGuards
|
|
4
4
|
} from "@open-mercato/shared/lib/crud/mutation-guard-registry";
|
|
5
|
+
import { getAllMutationGuardInstances } from "@open-mercato/shared/lib/crud/mutation-guard-store";
|
|
6
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
7
|
+
import { getTelemetryRuntime } from "@open-mercato/shared/lib/telemetry/runtime";
|
|
8
|
+
const logger = createLogger("payment_gateways");
|
|
5
9
|
function resolveUserFeatures(auth) {
|
|
6
10
|
const features = auth?.features;
|
|
7
11
|
if (!Array.isArray(features)) return [];
|
|
8
12
|
return features.filter((value) => typeof value === "string");
|
|
9
13
|
}
|
|
10
|
-
async function runPaymentGatewayMutationGuards(container, input,
|
|
14
|
+
async function runPaymentGatewayMutationGuards(container, input, extraFeatures = []) {
|
|
15
|
+
const allGuards = [...getAllMutationGuardInstances()];
|
|
11
16
|
const legacyGuard = bridgeLegacyGuard(container);
|
|
12
|
-
if (
|
|
17
|
+
if (legacyGuard) allGuards.push(legacyGuard);
|
|
18
|
+
if (!allGuards.length) {
|
|
13
19
|
return { ok: true, afterSuccessCallbacks: [] };
|
|
14
20
|
}
|
|
15
|
-
|
|
21
|
+
let grantedFeatures = [];
|
|
22
|
+
try {
|
|
23
|
+
const rbacService = container.resolve("rbacService");
|
|
24
|
+
grantedFeatures = await rbacService?.getGrantedFeatures?.(input.userId, {
|
|
25
|
+
tenantId: input.tenantId,
|
|
26
|
+
organizationId: input.organizationId
|
|
27
|
+
}) ?? [];
|
|
28
|
+
} catch (err) {
|
|
29
|
+
logger.error("Failed to resolve mutation guard feature grants", { err });
|
|
30
|
+
getTelemetryRuntime()?.reportError(err, {
|
|
31
|
+
module: "payment_gateways",
|
|
32
|
+
code: "payment_gateways.mutation_guard_feature_resolution_failed"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const userFeatures = Array.from(/* @__PURE__ */ new Set([...grantedFeatures, ...extraFeatures]));
|
|
36
|
+
return runMutationGuards(allGuards, input, { userFeatures });
|
|
16
37
|
}
|
|
17
38
|
async function runPaymentGatewayMutationGuardAfterSuccess(callbacks, input) {
|
|
18
39
|
for (const callback of callbacks) {
|
|
19
40
|
if (!callback.guard.afterSuccess) continue;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
try {
|
|
42
|
+
await callback.guard.afterSuccess({
|
|
43
|
+
...input,
|
|
44
|
+
metadata: callback.metadata ?? null
|
|
45
|
+
});
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logger.error("Mutation guard afterSuccess failed", { guardId: callback.guard.id, err });
|
|
48
|
+
getTelemetryRuntime()?.reportError(err, {
|
|
49
|
+
module: "payment_gateways",
|
|
50
|
+
code: "payment_gateways.mutation_guard_after_success_failed",
|
|
51
|
+
attributes: { guardId: callback.guard.id }
|
|
52
|
+
});
|
|
53
|
+
}
|
|
24
54
|
}
|
|
25
55
|
}
|
|
26
56
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/payment_gateways/api/guards.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport {\n bridgeLegacyGuard,\n runMutationGuards,\n type MutationGuard,\n type MutationGuardInput,\n} from '@open-mercato/shared/lib/crud/mutation-guard-registry'\n\ntype GuardAfterCallback = {\n guard: MutationGuard\n metadata: Record<string, unknown> | null\n}\n\nexport function resolveUserFeatures(auth: unknown): string[] {\n const features = (auth as { features?: unknown })?.features\n if (!Array.isArray(features)) return []\n return features.filter((value): value is string => typeof value === 'string')\n}\n\nexport async function runPaymentGatewayMutationGuards(\n container: AwilixContainer,\n input: MutationGuardInput,\n
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;
|
|
4
|
+
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport {\n bridgeLegacyGuard,\n runMutationGuards,\n type MutationGuard,\n type MutationGuardInput,\n} from '@open-mercato/shared/lib/crud/mutation-guard-registry'\nimport { getAllMutationGuardInstances } from '@open-mercato/shared/lib/crud/mutation-guard-store'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'\n\nconst logger = createLogger('payment_gateways')\n\ntype GuardAfterCallback = {\n guard: MutationGuard\n metadata: Record<string, unknown> | null\n}\n\n/** @deprecated Mutation guard grants are resolved from RBAC by `runPaymentGatewayMutationGuards`. */\nexport function resolveUserFeatures(auth: unknown): string[] {\n const features = (auth as { features?: unknown })?.features\n if (!Array.isArray(features)) return []\n return features.filter((value): value is string => typeof value === 'string')\n}\n\nexport async function runPaymentGatewayMutationGuards(\n container: AwilixContainer,\n input: MutationGuardInput,\n extraFeatures: string[] = [],\n): Promise<{\n ok: boolean\n errorBody?: Record<string, unknown>\n errorStatus?: number\n modifiedPayload?: Record<string, unknown>\n afterSuccessCallbacks: GuardAfterCallback[]\n}> {\n const allGuards: MutationGuard[] = [...getAllMutationGuardInstances()]\n const legacyGuard = bridgeLegacyGuard(container)\n if (legacyGuard) allGuards.push(legacyGuard)\n if (!allGuards.length) {\n return { ok: true, afterSuccessCallbacks: [] }\n }\n\n let grantedFeatures: string[] = []\n try {\n const rbacService = container.resolve('rbacService') as {\n getGrantedFeatures?: (\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n } | undefined\n grantedFeatures = await rbacService?.getGrantedFeatures?.(input.userId, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n }) ?? []\n } catch (err) {\n logger.error('Failed to resolve mutation guard feature grants', { err })\n getTelemetryRuntime()?.reportError(err, {\n module: 'payment_gateways',\n code: 'payment_gateways.mutation_guard_feature_resolution_failed',\n })\n }\n\n const userFeatures = Array.from(new Set([...grantedFeatures, ...extraFeatures]))\n return runMutationGuards(allGuards, input, { userFeatures })\n}\n\nexport async function runPaymentGatewayMutationGuardAfterSuccess(\n callbacks: GuardAfterCallback[],\n input: {\n tenantId: string\n organizationId: string | null\n userId: string\n resourceKind: string\n resourceId: string\n operation: 'create' | 'update' | 'delete'\n requestMethod: string\n requestHeaders: Headers\n },\n): Promise<void> {\n for (const callback of callbacks) {\n if (!callback.guard.afterSuccess) continue\n try {\n await callback.guard.afterSuccess({\n ...input,\n metadata: callback.metadata ?? null,\n })\n } catch (err) {\n logger.error('Mutation guard afterSuccess failed', { guardId: callback.guard.id, err })\n getTelemetryRuntime()?.reportError(err, {\n module: 'payment_gateways',\n code: 'payment_gateways.mutation_guard_after_success_failed',\n attributes: { guardId: callback.guard.id },\n })\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,oCAAoC;AAC7C,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AAEpC,MAAM,SAAS,aAAa,kBAAkB;AAQvC,SAAS,oBAAoB,MAAyB;AAC3D,QAAM,WAAY,MAAiC;AACnD,MAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG,QAAO,CAAC;AACtC,SAAO,SAAS,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ;AAC9E;AAEA,eAAsB,gCACpB,WACA,OACA,gBAA0B,CAAC,GAO1B;AACD,QAAM,YAA6B,CAAC,GAAG,6BAA6B,CAAC;AACrE,QAAM,cAAc,kBAAkB,SAAS;AAC/C,MAAI,YAAa,WAAU,KAAK,WAAW;AAC3C,MAAI,CAAC,UAAU,QAAQ;AACrB,WAAO,EAAE,IAAI,MAAM,uBAAuB,CAAC,EAAE;AAAA,EAC/C;AAEA,MAAI,kBAA4B,CAAC;AACjC,MAAI;AACF,UAAM,cAAc,UAAU,QAAQ,aAAa;AAMnD,sBAAkB,MAAM,aAAa,qBAAqB,MAAM,QAAQ;AAAA,MACtE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC,KAAK,CAAC;AAAA,EACT,SAAS,KAAK;AACZ,WAAO,MAAM,mDAAmD,EAAE,IAAI,CAAC;AACvE,wBAAoB,GAAG,YAAY,KAAK;AAAA,MACtC,QAAQ;AAAA,MACR,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,aAAa,CAAC,CAAC;AAC/E,SAAO,kBAAkB,WAAW,OAAO,EAAE,aAAa,CAAC;AAC7D;AAEA,eAAsB,2CACpB,WACA,OAUe;AACf,aAAW,YAAY,WAAW;AAChC,QAAI,CAAC,SAAS,MAAM,aAAc;AAClC,QAAI;AACF,YAAM,SAAS,MAAM,aAAa;AAAA,QAChC,GAAG;AAAA,QACH,UAAU,SAAS,YAAY;AAAA,MACjC,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO,MAAM,sCAAsC,EAAE,SAAS,SAAS,MAAM,IAAI,IAAI,CAAC;AACtF,0BAAoB,GAAG,YAAY,KAAK;AAAA,QACtC,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,YAAY,EAAE,SAAS,SAAS,MAAM,GAAG;AAAA,MAC3C,CAAC;AAAA,IACH;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,10 +7,7 @@ import { resolveSingleOrganizationIdOrDeny } from "@open-mercato/core/modules/di
|
|
|
7
7
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
8
8
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
9
9
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
10
|
-
import {
|
|
11
|
-
bridgeLegacyGuard,
|
|
12
|
-
runMutationGuards
|
|
13
|
-
} from "@open-mercato/shared/lib/crud/mutation-guard-registry";
|
|
10
|
+
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
14
11
|
import {
|
|
15
12
|
customSendSchema,
|
|
16
13
|
customSendResponseSchema,
|
|
@@ -23,16 +20,6 @@ const errorResponseSchema = z.object({ error: z.string() });
|
|
|
23
20
|
const metadata = {
|
|
24
21
|
POST: { requireAuth: true, requireFeatures: ["push_notifications.send_custom"] }
|
|
25
22
|
};
|
|
26
|
-
function resolveUserFeatures(auth) {
|
|
27
|
-
const features = auth?.features;
|
|
28
|
-
if (!Array.isArray(features)) return [];
|
|
29
|
-
return features.filter((value) => typeof value === "string");
|
|
30
|
-
}
|
|
31
|
-
async function runGuards(container, userFeatures, input) {
|
|
32
|
-
const legacyGuard = bridgeLegacyGuard(container);
|
|
33
|
-
if (!legacyGuard) return { ok: true, afterSuccessCallbacks: [] };
|
|
34
|
-
return runMutationGuards([legacyGuard], input, { userFeatures });
|
|
35
|
-
}
|
|
36
23
|
async function POST(req) {
|
|
37
24
|
const { translate } = await resolveTranslations();
|
|
38
25
|
try {
|
|
@@ -47,23 +34,23 @@ async function POST(req) {
|
|
|
47
34
|
const body = customSendSchema.parse(await readJsonSafe(req, {}));
|
|
48
35
|
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
49
36
|
const organizationId = resolveSingleOrganizationIdOrDeny(scope, auth) ?? null;
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
37
|
+
const guardResult = await runRouteMutationGuards({
|
|
38
|
+
container,
|
|
39
|
+
req,
|
|
40
|
+
auth: {
|
|
41
|
+
tenantId: auth.tenantId,
|
|
42
|
+
organizationId,
|
|
43
|
+
userId: auth.sub
|
|
44
|
+
},
|
|
45
|
+
input: {
|
|
46
|
+
resourceKind: RESOURCE_KIND,
|
|
47
|
+
resourceId: body.recipientUserId,
|
|
48
|
+
operation: "create",
|
|
49
|
+
mutationPayload: body
|
|
50
|
+
}
|
|
51
|
+
});
|
|
62
52
|
if (!guardResult.ok) {
|
|
63
|
-
return
|
|
64
|
-
guardResult.errorBody ?? { error: translate("push_notifications.errors.send_failed", "Operation blocked") },
|
|
65
|
-
{ status: guardResult.errorStatus ?? 422 }
|
|
66
|
-
);
|
|
53
|
+
return guardResult.response;
|
|
67
54
|
}
|
|
68
55
|
const service = container.resolve("pushNotificationService");
|
|
69
56
|
const result = await service.sendCustomPush({
|
|
@@ -78,14 +65,7 @@ async function POST(req) {
|
|
|
78
65
|
pushOptions: body.pushOptions,
|
|
79
66
|
silent: body.silent ?? false
|
|
80
67
|
});
|
|
81
|
-
|
|
82
|
-
if (!callback.guard.afterSuccess) continue;
|
|
83
|
-
await callback.guard.afterSuccess({
|
|
84
|
-
...guardInput,
|
|
85
|
-
resourceId: body.recipientUserId,
|
|
86
|
-
metadata: callback.metadata ?? null
|
|
87
|
-
});
|
|
88
|
-
}
|
|
68
|
+
await guardResult.runAfterSuccess();
|
|
89
69
|
const responseBody = result.enqueued === 0 ? {
|
|
90
70
|
enqueued: 0,
|
|
91
71
|
warning: CUSTOM_SEND_NO_DEVICES_WARNING,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/push_notifications/api/custom-send/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport {
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,yCAAyC;AAClD,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAC7B
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport {\n customSendSchema,\n customSendResponseSchema,\n CUSTOM_SEND_NO_DEVICES_WARNING,\n} from '../../data/validators'\nimport type { PushNotificationService } from '../../lib/send-custom-push'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\n\nconst logger = createLogger('push_notifications')\n\nconst RESOURCE_KIND = 'push_notifications.push_notification_delivery'\n\nconst errorResponseSchema = z.object({ error: z.string() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['push_notifications.send_custom'] },\n}\n\nexport async function POST(req: Request) {\n const { translate } = await resolveTranslations()\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.sub) {\n return NextResponse.json(\n { error: translate('push_notifications.errors.unauthorized', 'Unauthorized') },\n { status: 401 },\n )\n }\n\n const body = customSendSchema.parse(await readJsonSafe(req, {}))\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = resolveSingleOrganizationIdOrDeny(scope, auth) ?? null\n\n // Custom write route \u2192 wire the mutation-guard registry (AGENTS \u2192 API Routes). The send creates\n // append-only delivery rows; map it to a `create` on the delivery resource keyed by recipient.\n const guardResult = await runRouteMutationGuards({\n container,\n req,\n auth: {\n tenantId: auth.tenantId,\n organizationId,\n userId: auth.sub,\n },\n input: {\n resourceKind: RESOURCE_KIND,\n resourceId: body.recipientUserId,\n operation: 'create',\n mutationPayload: body,\n },\n })\n if (!guardResult.ok) {\n return guardResult.response\n }\n\n const service = container.resolve('pushNotificationService') as PushNotificationService\n const result = await service.sendCustomPush({\n resolve: (<T = unknown,>(name: string): T => container.resolve(name) as T),\n tenantId: auth.tenantId,\n userId: body.recipientUserId,\n organizationId,\n deviceId: body.deviceId,\n title: body.title,\n body: body.body ?? null,\n data: body.data,\n pushOptions: body.pushOptions,\n silent: body.silent ?? false,\n })\n\n await guardResult.runAfterSuccess()\n\n // A well-formed request that enqueued nothing (no push channel, no in-scope device, or no device\n // whose provider matches an active channel) previously returned a bare 201 \u2014 a silent\n // success-with-no-send that hid, for example, a tenant-level admin targeting org-scoped devices.\n // Surface an explicit machine-readable warning + human message so the caller can react.\n const responseBody: z.infer<typeof customSendResponseSchema> =\n result.enqueued === 0\n ? {\n enqueued: 0,\n warning: CUSTOM_SEND_NO_DEVICES_WARNING,\n message: translate(\n 'push_notifications.warnings.no_matching_devices_in_scope',\n 'No push-capable devices matched this recipient in the selected scope, so nothing was sent.',\n ),\n }\n : { enqueued: result.enqueued }\n\n // 201 Created only when jobs were actually enqueued; the no-op branch returns 200 OK so callers\n // that key off the status code aren't told something was created when nothing was.\n return NextResponse.json(responseBody, { status: result.enqueued === 0 ? 200 : 201 })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('push_notifications.errors.invalid_payload', 'Invalid request'), details: err.flatten() },\n { status: 400 },\n )\n }\n logger.error('push_notifications.custom-send.POST failed', { err })\n return NextResponse.json(\n { error: translate('push_notifications.errors.send_failed', 'Failed to send push notification') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi = {\n POST: {\n summary: 'Send a custom push notification',\n description:\n \"Admin-only: deliver a one-off, free-text visible push to all of a single user's push-capable devices. No in-app notification or email is created.\",\n tags: ['PushNotifications'],\n requestBody: { schema: customSendSchema },\n responses: {\n 200: {\n description:\n 'Nothing was deliverable in scope: `enqueued` is 0 and a `warning` code plus human `message` explain why (no silent no-op). Returned instead of 201 because nothing was created.',\n content: { 'application/json': { schema: customSendResponseSchema } },\n },\n 201: {\n description: 'Per-device push jobs enqueued.',\n content: { 'application/json': { schema: customSendResponseSchema } },\n },\n 400: {\n description: 'Invalid request',\n content: { 'application/json': { schema: errorResponseSchema } },\n },\n 401: {\n description: 'Unauthorized',\n content: { 'application/json': { schema: errorResponseSchema } },\n },\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|