@open-mercato/core 0.4.6-develop-a88276bc52 → 0.4.6-develop-806a2ed6b9
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/AGENTS.md +10 -0
- package/dist/generated/entities/integration_credentials/index.js +19 -0
- package/dist/generated/entities/integration_credentials/index.js.map +7 -0
- package/dist/generated/entities/integration_log/index.js +27 -0
- package/dist/generated/entities/integration_log/index.js.map +7 -0
- package/dist/generated/entities/integration_state/index.js +27 -0
- package/dist/generated/entities/integration_state/index.js.map +7 -0
- package/dist/generated/entities/sync_cursor/index.js +19 -0
- package/dist/generated/entities/sync_cursor/index.js.map +7 -0
- package/dist/generated/entities/sync_external_id_mapping/index.js +27 -0
- package/dist/generated/entities/sync_external_id_mapping/index.js.map +7 -0
- package/dist/generated/entities/sync_mapping/index.js +19 -0
- package/dist/generated/entities/sync_mapping/index.js.map +7 -0
- package/dist/generated/entities/sync_run/index.js +45 -0
- package/dist/generated/entities/sync_run/index.js.map +7 -0
- package/dist/generated/entities/sync_schedule/index.js +35 -0
- package/dist/generated/entities/sync_schedule/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +14 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +16 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/data_sync/acl.js +11 -0
- package/dist/modules/data_sync/acl.js.map +7 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js +137 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/mappings/route.js +132 -0
- package/dist/modules/data_sync/api/mappings/route.js.map +7 -0
- package/dist/modules/data_sync/api/run.js +87 -0
- package/dist/modules/data_sync/api/run.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +49 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js +93 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/route.js +69 -0
- package/dist/modules/data_sync/api/runs/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/runs.js +66 -0
- package/dist/modules/data_sync/api/runs.js.map +7 -0
- package/dist/modules/data_sync/api/validate.js +66 -0
- package/dist/modules/data_sync/api/validate.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.js +216 -0
- package/dist/modules/data_sync/backend/data-sync/page.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.meta.js +25 -0
- package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +178 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js +14 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js.map +7 -0
- package/dist/modules/data_sync/data/entities.js +228 -0
- package/dist/modules/data_sync/data/entities.js.map +7 -0
- package/dist/modules/data_sync/data/validators.js +32 -0
- package/dist/modules/data_sync/data/validators.js.map +7 -0
- package/dist/modules/data_sync/di.js +26 -0
- package/dist/modules/data_sync/di.js.map +7 -0
- package/dist/modules/data_sync/events.js +16 -0
- package/dist/modules/data_sync/events.js.map +7 -0
- package/dist/modules/data_sync/index.js +9 -0
- package/dist/modules/data_sync/index.js.map +7 -0
- package/dist/modules/data_sync/lib/adapter-registry.js +16 -0
- package/dist/modules/data_sync/lib/adapter-registry.js.map +7 -0
- package/dist/modules/data_sync/lib/adapter.js +1 -0
- package/dist/modules/data_sync/lib/adapter.js.map +7 -0
- package/dist/modules/data_sync/lib/id-mapping.js +79 -0
- package/dist/modules/data_sync/lib/id-mapping.js.map +7 -0
- package/dist/modules/data_sync/lib/queue.js +17 -0
- package/dist/modules/data_sync/lib/queue.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +309 -0
- package/dist/modules/data_sync/lib/sync-engine.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-run-service.js +148 -0
- package/dist/modules/data_sync/lib/sync-run-service.js.map +7 -0
- package/dist/modules/data_sync/migrations/Migration20260304113737.js +17 -0
- package/dist/modules/data_sync/migrations/Migration20260304113737.js.map +7 -0
- package/dist/modules/data_sync/setup.js +13 -0
- package/dist/modules/data_sync/setup.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-export.js +14 -0
- package/dist/modules/data_sync/workers/sync-export.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-import.js +14 -0
- package/dist/modules/data_sync/workers/sync-import.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js +63 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +7 -0
- package/dist/modules/entities/lib/encryptionDefaults.js +4 -0
- package/dist/modules/entities/lib/encryptionDefaults.js.map +2 -2
- package/dist/modules/integrations/acl.js +4 -1
- package/dist/modules/integrations/acl.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +127 -0
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/health/route.js +46 -0
- package/dist/modules/integrations/api/[id]/health/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/route.js +65 -0
- package/dist/modules/integrations/api/[id]/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/state/route.js +109 -0
- package/dist/modules/integrations/api/[id]/state/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/version/route.js +117 -0
- package/dist/modules/integrations/api/[id]/version/route.js.map +7 -0
- package/dist/modules/integrations/api/guards.js +31 -0
- package/dist/modules/integrations/api/guards.js.map +7 -0
- package/dist/modules/integrations/api/logs/route.js +60 -0
- package/dist/modules/integrations/api/logs/route.js.map +7 -0
- package/dist/modules/integrations/api/openapi.js +25 -0
- package/dist/modules/integrations/api/openapi.js.map +7 -0
- package/dist/modules/integrations/api/route.js +68 -0
- package/dist/modules/integrations/api/route.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js +313 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.meta.js +15 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.meta.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +189 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js +15 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/page.js +212 -0
- package/dist/modules/integrations/backend/integrations/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/page.meta.js +22 -0
- package/dist/modules/integrations/backend/integrations/page.meta.js.map +7 -0
- package/dist/modules/integrations/data/enrichers.js +27 -12
- package/dist/modules/integrations/data/enrichers.js.map +2 -2
- package/dist/modules/integrations/data/entities.js +136 -1
- package/dist/modules/integrations/data/entities.js.map +2 -2
- package/dist/modules/integrations/data/validators.js +36 -0
- package/dist/modules/integrations/data/validators.js.map +7 -0
- package/dist/modules/integrations/di.js +24 -0
- package/dist/modules/integrations/di.js.map +7 -0
- package/dist/modules/integrations/events.js +19 -0
- package/dist/modules/integrations/events.js.map +7 -0
- package/dist/modules/integrations/lib/credentials-service.js +159 -0
- package/dist/modules/integrations/lib/credentials-service.js.map +7 -0
- package/dist/modules/integrations/lib/health-service.js +37 -0
- package/dist/modules/integrations/lib/health-service.js.map +7 -0
- package/dist/modules/integrations/lib/log-service.js +66 -0
- package/dist/modules/integrations/lib/log-service.js.map +7 -0
- package/dist/modules/integrations/lib/registry-service.js +33 -0
- package/dist/modules/integrations/lib/registry-service.js.map +7 -0
- package/dist/modules/integrations/lib/state-service.js +55 -0
- package/dist/modules/integrations/lib/state-service.js.map +7 -0
- package/dist/modules/integrations/lib/types.js +1 -0
- package/dist/modules/integrations/lib/types.js.map +7 -0
- package/dist/modules/integrations/migrations/Migration20260304113737.js +19 -0
- package/dist/modules/integrations/migrations/Migration20260304113737.js.map +7 -0
- package/dist/modules/integrations/setup.js +2 -2
- package/dist/modules/integrations/setup.js.map +2 -2
- package/dist/modules/integrations/widgets/injection-table.js.map +1 -1
- package/dist/modules/integrations/workers/log-pruner.js +18 -0
- package/dist/modules/integrations/workers/log-pruner.js.map +7 -0
- package/generated/entities/integration_credentials/index.ts +8 -0
- package/generated/entities/integration_log/index.ts +12 -0
- package/generated/entities/integration_state/index.ts +12 -0
- package/generated/entities/sync_cursor/index.ts +8 -0
- package/generated/entities/sync_external_id_mapping/index.ts +12 -0
- package/generated/entities/sync_mapping/index.ts +8 -0
- package/generated/entities/sync_run/index.ts +21 -0
- package/generated/entities/sync_schedule/index.ts +16 -0
- package/generated/entities.ids.generated.ts +14 -0
- package/generated/entity-fields-registry.ts +16 -0
- package/package.json +2 -2
- package/src/modules/data_sync/AGENTS.md +157 -0
- package/src/modules/data_sync/acl.ts +7 -0
- package/src/modules/data_sync/api/mappings/[id]/route.ts +158 -0
- package/src/modules/data_sync/api/mappings/route.ts +144 -0
- package/src/modules/data_sync/api/run.ts +97 -0
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +57 -0
- package/src/modules/data_sync/api/runs/[id]/retry.ts +108 -0
- package/src/modules/data_sync/api/runs/[id]/route.ts +81 -0
- package/src/modules/data_sync/api/runs.ts +69 -0
- package/src/modules/data_sync/api/validate.ts +73 -0
- package/src/modules/data_sync/backend/data-sync/page.meta.ts +21 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +244 -0
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.meta.ts +10 -0
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +278 -0
- package/src/modules/data_sync/data/entities.ts +180 -0
- package/src/modules/data_sync/data/validators.ts +35 -0
- package/src/modules/data_sync/di.ts +38 -0
- package/src/modules/data_sync/events.ts +12 -0
- package/src/modules/data_sync/i18n/de.json +48 -0
- package/src/modules/data_sync/i18n/en.json +48 -0
- package/src/modules/data_sync/i18n/es.json +48 -0
- package/src/modules/data_sync/i18n/pl.json +48 -0
- package/src/modules/data_sync/index.ts +5 -0
- package/src/modules/data_sync/lib/adapter-registry.ts +15 -0
- package/src/modules/data_sync/lib/adapter.ts +90 -0
- package/src/modules/data_sync/lib/id-mapping.ts +95 -0
- package/src/modules/data_sync/lib/queue.ts +19 -0
- package/src/modules/data_sync/lib/sync-engine.ts +375 -0
- package/src/modules/data_sync/lib/sync-run-service.ts +187 -0
- package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +653 -0
- package/src/modules/data_sync/migrations/Migration20260304113737.ts +19 -0
- package/src/modules/data_sync/setup.ts +11 -0
- package/src/modules/data_sync/workers/sync-export.ts +27 -0
- package/src/modules/data_sync/workers/sync-import.ts +27 -0
- package/src/modules/data_sync/workers/sync-scheduled.ts +84 -0
- package/src/modules/entities/lib/encryptionDefaults.ts +4 -0
- package/src/modules/integrations/AGENTS.md +160 -0
- package/src/modules/integrations/acl.ts +3 -0
- package/src/modules/integrations/api/[id]/credentials/route.ts +142 -0
- package/src/modules/integrations/api/[id]/health/route.ts +53 -0
- package/src/modules/integrations/api/[id]/route.ts +76 -0
- package/src/modules/integrations/api/[id]/state/route.ts +121 -0
- package/src/modules/integrations/api/[id]/version/route.ts +132 -0
- package/src/modules/integrations/api/guards.ts +59 -0
- package/src/modules/integrations/api/logs/route.ts +63 -0
- package/src/modules/integrations/api/openapi.ts +22 -0
- package/src/modules/integrations/api/route.ts +73 -0
- package/src/modules/integrations/backend/integrations/[id]/page.meta.ts +11 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +424 -0
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.meta.ts +11 -0
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +249 -0
- package/src/modules/integrations/backend/integrations/page.meta.ts +18 -0
- package/src/modules/integrations/backend/integrations/page.tsx +296 -0
- package/src/modules/integrations/data/enrichers.ts +35 -18
- package/src/modules/integrations/data/entities.ts +114 -5
- package/src/modules/integrations/data/validators.ts +41 -0
- package/src/modules/integrations/di.ts +31 -0
- package/src/modules/integrations/events.ts +17 -0
- package/src/modules/integrations/i18n/de.json +70 -0
- package/src/modules/integrations/i18n/en.json +70 -0
- package/src/modules/integrations/i18n/es.json +70 -0
- package/src/modules/integrations/i18n/pl.json +70 -0
- package/src/modules/integrations/lib/credentials-service.ts +204 -0
- package/src/modules/integrations/lib/health-service.ts +59 -0
- package/src/modules/integrations/lib/log-service.ts +84 -0
- package/src/modules/integrations/lib/registry-service.ts +42 -0
- package/src/modules/integrations/lib/state-service.ts +64 -0
- package/src/modules/integrations/lib/types.ts +4 -0
- package/src/modules/integrations/migrations/.snapshot-open-mercato.json +582 -0
- package/src/modules/integrations/migrations/Migration20260304113737.ts +21 -0
- package/src/modules/integrations/setup.ts +2 -2
- package/src/modules/integrations/widgets/injection-table.ts +1 -1
- package/src/modules/integrations/workers/log-pruner.ts +30 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
6
|
+
import { SyncMapping } from "../../../data/entities.js";
|
|
7
|
+
const idParamsSchema = z.object({ id: z.string().uuid() });
|
|
8
|
+
const updateMappingSchema = z.object({
|
|
9
|
+
mapping: z.record(z.string(), z.unknown())
|
|
10
|
+
});
|
|
11
|
+
const metadata = {
|
|
12
|
+
GET: { requireAuth: true, requireFeatures: ["data_sync.view"] },
|
|
13
|
+
PUT: { requireAuth: true, requireFeatures: ["data_sync.configure"] },
|
|
14
|
+
DELETE: { requireAuth: true, requireFeatures: ["data_sync.configure"] }
|
|
15
|
+
};
|
|
16
|
+
const openApi = {
|
|
17
|
+
tags: ["DataSync"],
|
|
18
|
+
summary: "Get, update, or delete a field mapping"
|
|
19
|
+
};
|
|
20
|
+
async function resolveParams(ctx) {
|
|
21
|
+
const rawParams = ctx.params && typeof ctx.params.then === "function" ? await ctx.params : ctx.params;
|
|
22
|
+
return idParamsSchema.safeParse(rawParams);
|
|
23
|
+
}
|
|
24
|
+
async function GET(req, ctx) {
|
|
25
|
+
const auth = await getAuthFromRequest(req);
|
|
26
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
27
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
28
|
+
}
|
|
29
|
+
const parsedParams = await resolveParams(ctx);
|
|
30
|
+
if (!parsedParams.success) {
|
|
31
|
+
return NextResponse.json({ error: "Invalid mapping id" }, { status: 400 });
|
|
32
|
+
}
|
|
33
|
+
const container = await createRequestContainer();
|
|
34
|
+
const em = container.resolve("em");
|
|
35
|
+
const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
|
|
36
|
+
const mapping = await findOneWithDecryption(
|
|
37
|
+
em,
|
|
38
|
+
SyncMapping,
|
|
39
|
+
{
|
|
40
|
+
id: parsedParams.data.id,
|
|
41
|
+
organizationId: auth.orgId,
|
|
42
|
+
tenantId: auth.tenantId
|
|
43
|
+
},
|
|
44
|
+
void 0,
|
|
45
|
+
scope
|
|
46
|
+
);
|
|
47
|
+
if (!mapping) {
|
|
48
|
+
return NextResponse.json({ error: "Mapping not found" }, { status: 404 });
|
|
49
|
+
}
|
|
50
|
+
return NextResponse.json({
|
|
51
|
+
id: mapping.id,
|
|
52
|
+
integrationId: mapping.integrationId,
|
|
53
|
+
entityType: mapping.entityType,
|
|
54
|
+
mapping: mapping.mapping,
|
|
55
|
+
createdAt: mapping.createdAt.toISOString(),
|
|
56
|
+
updatedAt: mapping.updatedAt.toISOString()
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async function PUT(req, ctx) {
|
|
60
|
+
const auth = await getAuthFromRequest(req);
|
|
61
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
62
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
63
|
+
}
|
|
64
|
+
const parsedParams = await resolveParams(ctx);
|
|
65
|
+
if (!parsedParams.success) {
|
|
66
|
+
return NextResponse.json({ error: "Invalid mapping id" }, { status: 400 });
|
|
67
|
+
}
|
|
68
|
+
const payload = await req.json().catch(() => null);
|
|
69
|
+
const parsedBody = updateMappingSchema.safeParse(payload);
|
|
70
|
+
if (!parsedBody.success) {
|
|
71
|
+
return NextResponse.json({ error: "Invalid payload", details: parsedBody.error.flatten() }, { status: 422 });
|
|
72
|
+
}
|
|
73
|
+
const container = await createRequestContainer();
|
|
74
|
+
const em = container.resolve("em");
|
|
75
|
+
const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
|
|
76
|
+
const mapping = await findOneWithDecryption(
|
|
77
|
+
em,
|
|
78
|
+
SyncMapping,
|
|
79
|
+
{
|
|
80
|
+
id: parsedParams.data.id,
|
|
81
|
+
organizationId: auth.orgId,
|
|
82
|
+
tenantId: auth.tenantId
|
|
83
|
+
},
|
|
84
|
+
void 0,
|
|
85
|
+
scope
|
|
86
|
+
);
|
|
87
|
+
if (!mapping) {
|
|
88
|
+
return NextResponse.json({ error: "Mapping not found" }, { status: 404 });
|
|
89
|
+
}
|
|
90
|
+
mapping.mapping = parsedBody.data.mapping;
|
|
91
|
+
await em.flush();
|
|
92
|
+
return NextResponse.json({
|
|
93
|
+
id: mapping.id,
|
|
94
|
+
integrationId: mapping.integrationId,
|
|
95
|
+
entityType: mapping.entityType,
|
|
96
|
+
mapping: mapping.mapping,
|
|
97
|
+
updatedAt: mapping.updatedAt.toISOString()
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async function DELETE(req, ctx) {
|
|
101
|
+
const auth = await getAuthFromRequest(req);
|
|
102
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
103
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
104
|
+
}
|
|
105
|
+
const parsedParams = await resolveParams(ctx);
|
|
106
|
+
if (!parsedParams.success) {
|
|
107
|
+
return NextResponse.json({ error: "Invalid mapping id" }, { status: 400 });
|
|
108
|
+
}
|
|
109
|
+
const container = await createRequestContainer();
|
|
110
|
+
const em = container.resolve("em");
|
|
111
|
+
const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
|
|
112
|
+
const mapping = await findOneWithDecryption(
|
|
113
|
+
em,
|
|
114
|
+
SyncMapping,
|
|
115
|
+
{
|
|
116
|
+
id: parsedParams.data.id,
|
|
117
|
+
organizationId: auth.orgId,
|
|
118
|
+
tenantId: auth.tenantId
|
|
119
|
+
},
|
|
120
|
+
void 0,
|
|
121
|
+
scope
|
|
122
|
+
);
|
|
123
|
+
if (!mapping) {
|
|
124
|
+
return NextResponse.json({ error: "Mapping not found" }, { status: 404 });
|
|
125
|
+
}
|
|
126
|
+
em.remove(mapping);
|
|
127
|
+
await em.flush();
|
|
128
|
+
return NextResponse.json({ deleted: true });
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
DELETE,
|
|
132
|
+
GET,
|
|
133
|
+
PUT,
|
|
134
|
+
metadata,
|
|
135
|
+
openApi
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/data_sync/api/mappings/%5Bid%5D/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SyncMapping } from '../../../data/entities'\n\nconst idParamsSchema = z.object({ id: z.string().uuid() })\n\nconst updateMappingSchema = z.object({\n mapping: z.record(z.string(), z.unknown()),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['data_sync.view'] },\n PUT: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n DELETE: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Get, update, or delete a field mapping',\n}\n\nasync function resolveParams(ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n return idParamsSchema.safeParse(rawParams)\n}\n\nexport async function GET(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const parsedParams = await resolveParams(ctx)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid mapping id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const mapping = await findOneWithDecryption(\n em,\n SyncMapping,\n {\n id: parsedParams.data.id,\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!mapping) {\n return NextResponse.json({ error: 'Mapping not found' }, { status: 404 })\n }\n\n return NextResponse.json({\n id: mapping.id,\n integrationId: mapping.integrationId,\n entityType: mapping.entityType,\n mapping: mapping.mapping,\n createdAt: mapping.createdAt.toISOString(),\n updatedAt: mapping.updatedAt.toISOString(),\n })\n}\n\nexport async function PUT(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const parsedParams = await resolveParams(ctx)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid mapping id' }, { status: 400 })\n }\n\n const payload = await req.json().catch(() => null)\n const parsedBody = updateMappingSchema.safeParse(payload)\n if (!parsedBody.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsedBody.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const mapping = await findOneWithDecryption(\n em,\n SyncMapping,\n {\n id: parsedParams.data.id,\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!mapping) {\n return NextResponse.json({ error: 'Mapping not found' }, { status: 404 })\n }\n\n mapping.mapping = parsedBody.data.mapping\n await em.flush()\n\n return NextResponse.json({\n id: mapping.id,\n integrationId: mapping.integrationId,\n entityType: mapping.entityType,\n mapping: mapping.mapping,\n updatedAt: mapping.updatedAt.toISOString(),\n })\n}\n\nexport async function DELETE(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const parsedParams = await resolveParams(ctx)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid mapping id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const mapping = await findOneWithDecryption(\n em,\n SyncMapping,\n {\n id: parsedParams.data.id,\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!mapping) {\n return NextResponse.json({ error: 'Mapping not found' }, { status: 404 })\n }\n\n em.remove(mapping)\n await em.flush()\n\n return NextResponse.json({ deleted: true })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { findAndCountWithDecryption, findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
6
|
+
import { SyncMapping } from "../../data/entities.js";
|
|
7
|
+
const listMappingsQuerySchema = z.object({
|
|
8
|
+
integrationId: z.string().min(1).optional(),
|
|
9
|
+
entityType: z.string().min(1).optional(),
|
|
10
|
+
page: z.coerce.number().int().min(1).default(1),
|
|
11
|
+
pageSize: z.coerce.number().int().min(1).max(100).default(20)
|
|
12
|
+
});
|
|
13
|
+
const createMappingSchema = z.object({
|
|
14
|
+
integrationId: z.string().min(1),
|
|
15
|
+
entityType: z.string().min(1),
|
|
16
|
+
mapping: z.record(z.string(), z.unknown())
|
|
17
|
+
});
|
|
18
|
+
const metadata = {
|
|
19
|
+
GET: { requireAuth: true, requireFeatures: ["data_sync.view"] },
|
|
20
|
+
POST: { requireAuth: true, requireFeatures: ["data_sync.configure"] }
|
|
21
|
+
};
|
|
22
|
+
const openApi = {
|
|
23
|
+
tags: ["DataSync"],
|
|
24
|
+
summary: "List or create field mappings"
|
|
25
|
+
};
|
|
26
|
+
async function GET(req) {
|
|
27
|
+
const auth = await getAuthFromRequest(req);
|
|
28
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
29
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
30
|
+
}
|
|
31
|
+
const url = new URL(req.url);
|
|
32
|
+
const parsed = listMappingsQuerySchema.safeParse({
|
|
33
|
+
integrationId: url.searchParams.get("integrationId") ?? void 0,
|
|
34
|
+
entityType: url.searchParams.get("entityType") ?? void 0,
|
|
35
|
+
page: url.searchParams.get("page") ?? void 0,
|
|
36
|
+
pageSize: url.searchParams.get("pageSize") ?? void 0
|
|
37
|
+
});
|
|
38
|
+
if (!parsed.success) {
|
|
39
|
+
return NextResponse.json({ error: "Invalid query", details: parsed.error.flatten() }, { status: 400 });
|
|
40
|
+
}
|
|
41
|
+
const container = await createRequestContainer();
|
|
42
|
+
const em = container.resolve("em");
|
|
43
|
+
const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
|
|
44
|
+
const where = {
|
|
45
|
+
organizationId: scope.organizationId,
|
|
46
|
+
tenantId: scope.tenantId
|
|
47
|
+
};
|
|
48
|
+
if (parsed.data.integrationId) where.integrationId = parsed.data.integrationId;
|
|
49
|
+
if (parsed.data.entityType) where.entityType = parsed.data.entityType;
|
|
50
|
+
const [items, total] = await findAndCountWithDecryption(
|
|
51
|
+
em,
|
|
52
|
+
SyncMapping,
|
|
53
|
+
where,
|
|
54
|
+
{
|
|
55
|
+
orderBy: { createdAt: "DESC" },
|
|
56
|
+
limit: parsed.data.pageSize,
|
|
57
|
+
offset: (parsed.data.page - 1) * parsed.data.pageSize
|
|
58
|
+
},
|
|
59
|
+
scope
|
|
60
|
+
);
|
|
61
|
+
return NextResponse.json({
|
|
62
|
+
items: items.map((item) => ({
|
|
63
|
+
id: item.id,
|
|
64
|
+
integrationId: item.integrationId,
|
|
65
|
+
entityType: item.entityType,
|
|
66
|
+
mapping: item.mapping,
|
|
67
|
+
createdAt: item.createdAt.toISOString(),
|
|
68
|
+
updatedAt: item.updatedAt.toISOString()
|
|
69
|
+
})),
|
|
70
|
+
total,
|
|
71
|
+
page: parsed.data.page,
|
|
72
|
+
pageSize: parsed.data.pageSize,
|
|
73
|
+
totalPages: Math.max(1, Math.ceil(total / parsed.data.pageSize))
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async function POST(req) {
|
|
77
|
+
const auth = await getAuthFromRequest(req);
|
|
78
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
79
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
80
|
+
}
|
|
81
|
+
const payload = await req.json().catch(() => null);
|
|
82
|
+
const parsed = createMappingSchema.safeParse(payload);
|
|
83
|
+
if (!parsed.success) {
|
|
84
|
+
return NextResponse.json({ error: "Invalid payload", details: parsed.error.flatten() }, { status: 422 });
|
|
85
|
+
}
|
|
86
|
+
const container = await createRequestContainer();
|
|
87
|
+
const em = container.resolve("em");
|
|
88
|
+
const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
|
|
89
|
+
const existing = await findOneWithDecryption(
|
|
90
|
+
em,
|
|
91
|
+
SyncMapping,
|
|
92
|
+
{
|
|
93
|
+
integrationId: parsed.data.integrationId,
|
|
94
|
+
entityType: parsed.data.entityType,
|
|
95
|
+
organizationId: scope.organizationId,
|
|
96
|
+
tenantId: scope.tenantId
|
|
97
|
+
},
|
|
98
|
+
void 0,
|
|
99
|
+
scope
|
|
100
|
+
);
|
|
101
|
+
if (existing) {
|
|
102
|
+
existing.mapping = parsed.data.mapping;
|
|
103
|
+
await em.flush();
|
|
104
|
+
return NextResponse.json({
|
|
105
|
+
id: existing.id,
|
|
106
|
+
integrationId: existing.integrationId,
|
|
107
|
+
entityType: existing.entityType,
|
|
108
|
+
mapping: existing.mapping
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const created = em.create(SyncMapping, {
|
|
112
|
+
integrationId: parsed.data.integrationId,
|
|
113
|
+
entityType: parsed.data.entityType,
|
|
114
|
+
mapping: parsed.data.mapping,
|
|
115
|
+
organizationId: scope.organizationId,
|
|
116
|
+
tenantId: scope.tenantId
|
|
117
|
+
});
|
|
118
|
+
await em.persistAndFlush(created);
|
|
119
|
+
return NextResponse.json({
|
|
120
|
+
id: created.id,
|
|
121
|
+
integrationId: created.integrationId,
|
|
122
|
+
entityType: created.entityType,
|
|
123
|
+
mapping: created.mapping
|
|
124
|
+
}, { status: 201 });
|
|
125
|
+
}
|
|
126
|
+
export {
|
|
127
|
+
GET,
|
|
128
|
+
POST,
|
|
129
|
+
metadata,
|
|
130
|
+
openApi
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/modules/data_sync/api/mappings/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { findAndCountWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SyncMapping } from '../../data/entities'\n\nconst listMappingsQuerySchema = z.object({\n integrationId: z.string().min(1).optional(),\n entityType: z.string().min(1).optional(),\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n})\n\nconst createMappingSchema = z.object({\n integrationId: z.string().min(1),\n entityType: z.string().min(1),\n mapping: z.record(z.string(), z.unknown()),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['data_sync.view'] },\n POST: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'List or create field mappings',\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = listMappingsQuerySchema.safeParse({\n integrationId: url.searchParams.get('integrationId') ?? undefined,\n entityType: url.searchParams.get('entityType') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n })\n\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const where: FilterQuery<SyncMapping> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n }\n if (parsed.data.integrationId) where.integrationId = parsed.data.integrationId\n if (parsed.data.entityType) where.entityType = parsed.data.entityType\n\n const [items, total] = await findAndCountWithDecryption(\n em,\n SyncMapping,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: parsed.data.pageSize,\n offset: (parsed.data.page - 1) * parsed.data.pageSize,\n },\n scope,\n )\n\n return NextResponse.json({\n items: items.map((item) => ({\n id: item.id,\n integrationId: item.integrationId,\n entityType: item.entityType,\n mapping: item.mapping,\n createdAt: item.createdAt.toISOString(),\n updatedAt: item.updatedAt.toISOString(),\n })),\n total,\n page: parsed.data.page,\n pageSize: parsed.data.pageSize,\n totalPages: Math.max(1, Math.ceil(total / parsed.data.pageSize)),\n })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const payload = await req.json().catch(() => null)\n const parsed = createMappingSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const existing = await findOneWithDecryption(\n em,\n SyncMapping,\n {\n integrationId: parsed.data.integrationId,\n entityType: parsed.data.entityType,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (existing) {\n existing.mapping = parsed.data.mapping\n await em.flush()\n return NextResponse.json({\n id: existing.id,\n integrationId: existing.integrationId,\n entityType: existing.entityType,\n mapping: existing.mapping,\n })\n }\n\n const created = em.create(SyncMapping, {\n integrationId: parsed.data.integrationId,\n entityType: parsed.data.entityType,\n mapping: parsed.data.mapping,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n await em.persistAndFlush(created)\n\n return NextResponse.json({\n id: created.id,\n integrationId: created.integrationId,\n entityType: created.entityType,\n mapping: created.mapping,\n }, { status: 201 })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
3
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
|
+
import { runSyncSchema } from "../data/validators.js";
|
|
5
|
+
import { getSyncQueue } from "../lib/queue.js";
|
|
6
|
+
const metadata = {
|
|
7
|
+
POST: { requireAuth: true, requireFeatures: ["data_sync.run"] }
|
|
8
|
+
};
|
|
9
|
+
const openApi = {
|
|
10
|
+
tags: ["DataSync"],
|
|
11
|
+
summary: "Start a data sync run"
|
|
12
|
+
};
|
|
13
|
+
async function POST(req) {
|
|
14
|
+
const auth = await getAuthFromRequest(req);
|
|
15
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
16
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
17
|
+
}
|
|
18
|
+
const payload = await req.json().catch(() => null);
|
|
19
|
+
const parsed = runSyncSchema.safeParse(payload);
|
|
20
|
+
if (!parsed.success) {
|
|
21
|
+
return NextResponse.json({ error: "Invalid payload", details: parsed.error.flatten() }, { status: 422 });
|
|
22
|
+
}
|
|
23
|
+
const container = await createRequestContainer();
|
|
24
|
+
const syncRunService = container.resolve("dataSyncRunService");
|
|
25
|
+
const progressService = container.resolve("progressService");
|
|
26
|
+
const scope = {
|
|
27
|
+
organizationId: auth.orgId,
|
|
28
|
+
tenantId: auth.tenantId
|
|
29
|
+
};
|
|
30
|
+
const overlap = await syncRunService.findRunningOverlap(
|
|
31
|
+
parsed.data.integrationId,
|
|
32
|
+
parsed.data.entityType,
|
|
33
|
+
parsed.data.direction,
|
|
34
|
+
scope
|
|
35
|
+
);
|
|
36
|
+
if (overlap) {
|
|
37
|
+
return NextResponse.json({ error: "A sync run is already in progress for this integration and entity direction" }, { status: 409 });
|
|
38
|
+
}
|
|
39
|
+
const cursor = parsed.data.fullSync ? null : await syncRunService.resolveCursor(parsed.data.integrationId, parsed.data.entityType, parsed.data.direction, scope);
|
|
40
|
+
const progressJob = await progressService.createJob(
|
|
41
|
+
{
|
|
42
|
+
jobType: `data_sync:${parsed.data.direction}`,
|
|
43
|
+
name: `Data sync ${parsed.data.integrationId}`,
|
|
44
|
+
description: `${parsed.data.entityType} ${parsed.data.direction}`,
|
|
45
|
+
cancellable: true,
|
|
46
|
+
meta: {
|
|
47
|
+
integrationId: parsed.data.integrationId,
|
|
48
|
+
entityType: parsed.data.entityType,
|
|
49
|
+
direction: parsed.data.direction
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
tenantId: auth.tenantId,
|
|
54
|
+
organizationId: auth.orgId,
|
|
55
|
+
userId: auth.sub
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
const run = await syncRunService.createRun(
|
|
59
|
+
{
|
|
60
|
+
integrationId: parsed.data.integrationId,
|
|
61
|
+
entityType: parsed.data.entityType,
|
|
62
|
+
direction: parsed.data.direction,
|
|
63
|
+
cursor,
|
|
64
|
+
triggeredBy: parsed.data.triggeredBy ?? auth.sub,
|
|
65
|
+
progressJobId: progressJob.id
|
|
66
|
+
},
|
|
67
|
+
scope
|
|
68
|
+
);
|
|
69
|
+
const queueName = parsed.data.direction === "import" ? "data-sync-import" : "data-sync-export";
|
|
70
|
+
const queue = getSyncQueue(queueName);
|
|
71
|
+
await queue.enqueue({
|
|
72
|
+
runId: run.id,
|
|
73
|
+
batchSize: parsed.data.batchSize,
|
|
74
|
+
scope: {
|
|
75
|
+
organizationId: scope.organizationId,
|
|
76
|
+
tenantId: scope.tenantId,
|
|
77
|
+
userId: auth.sub
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return NextResponse.json({ id: run.id, progressJobId: progressJob.id }, { status: 201 });
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
POST,
|
|
84
|
+
metadata,
|
|
85
|
+
openApi
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/data_sync/api/run.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { ProgressService } from '../../progress/lib/progressService'\nimport type { SyncRunService } from '../lib/sync-run-service'\nimport { runSyncSchema } from '../data/validators'\nimport { getSyncQueue } from '../lib/queue'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Start a data sync run',\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const payload = await req.json().catch(() => null)\n const parsed = runSyncSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n\n const scope = {\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n }\n\n const overlap = await syncRunService.findRunningOverlap(\n parsed.data.integrationId,\n parsed.data.entityType,\n parsed.data.direction,\n scope,\n )\n if (overlap) {\n return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })\n }\n\n const cursor = parsed.data.fullSync\n ? null\n : await syncRunService.resolveCursor(parsed.data.integrationId, parsed.data.entityType, parsed.data.direction, scope)\n\n const progressJob = await progressService.createJob(\n {\n jobType: `data_sync:${parsed.data.direction}`,\n name: `Data sync ${parsed.data.integrationId}`,\n description: `${parsed.data.entityType} ${parsed.data.direction}`,\n cancellable: true,\n meta: {\n integrationId: parsed.data.integrationId,\n entityType: parsed.data.entityType,\n direction: parsed.data.direction,\n },\n },\n {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub,\n },\n )\n\n const run = await syncRunService.createRun(\n {\n integrationId: parsed.data.integrationId,\n entityType: parsed.data.entityType,\n direction: parsed.data.direction,\n cursor,\n triggeredBy: parsed.data.triggeredBy ?? auth.sub,\n progressJobId: progressJob.id,\n },\n scope,\n )\n\n const queueName = parsed.data.direction === 'import' ? 'data-sync-import' : 'data-sync-export'\n const queue = getSyncQueue(queueName)\n await queue.enqueue({\n runId: run.id,\n batchSize: parsed.data.batchSize,\n scope: {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n userId: auth.sub,\n },\n })\n\n return NextResponse.json({ id: run.id, progressJobId: progressJob.id }, { status: 201 })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAGvC,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAEtB,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAChE;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,UAAU;AAAA,EACjB,SAAS;AACX;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AACjD,QAAM,SAAS,cAAc,UAAU,OAAO;AAC9C,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,mBAAmB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzG;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,iBAAiB,UAAU,QAAQ,oBAAoB;AAC7D,QAAM,kBAAkB,UAAU,QAAQ,iBAAiB;AAE3D,QAAM,QAAQ;AAAA,IACZ,gBAAgB,KAAK;AAAA,IACrB,UAAU,KAAK;AAAA,EACjB;AAEA,QAAM,UAAU,MAAM,eAAe;AAAA,IACnC,OAAO,KAAK;AAAA,IACZ,OAAO,KAAK;AAAA,IACZ,OAAO,KAAK;AAAA,IACZ;AAAA,EACF;AACA,MAAI,SAAS;AACX,WAAO,aAAa,KAAK,EAAE,OAAO,8EAA8E,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpI;AAEA,QAAM,SAAS,OAAO,KAAK,WACvB,OACA,MAAM,eAAe,cAAc,OAAO,KAAK,eAAe,OAAO,KAAK,YAAY,OAAO,KAAK,WAAW,KAAK;AAEtH,QAAM,cAAc,MAAM,gBAAgB;AAAA,IACxC;AAAA,MACE,SAAS,aAAa,OAAO,KAAK,SAAS;AAAA,MAC3C,MAAM,aAAa,OAAO,KAAK,aAAa;AAAA,MAC5C,aAAa,GAAG,OAAO,KAAK,UAAU,IAAI,OAAO,KAAK,SAAS;AAAA,MAC/D,aAAa;AAAA,MACb,MAAM;AAAA,QACJ,eAAe,OAAO,KAAK;AAAA,QAC3B,YAAY,OAAO,KAAK;AAAA,QACxB,WAAW,OAAO,KAAK;AAAA,MACzB;AAAA,IACF;AAAA,IACA;AAAA,MACE,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK;AAAA,MACrB,QAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,MAAM,MAAM,eAAe;AAAA,IAC/B;AAAA,MACE,eAAe,OAAO,KAAK;AAAA,MAC3B,YAAY,OAAO,KAAK;AAAA,MACxB,WAAW,OAAO,KAAK;AAAA,MACvB;AAAA,MACA,aAAa,OAAO,KAAK,eAAe,KAAK;AAAA,MAC7C,eAAe,YAAY;AAAA,IAC7B;AAAA,IACA;AAAA,EACF;AAEA,QAAM,YAAY,OAAO,KAAK,cAAc,WAAW,qBAAqB;AAC5E,QAAM,QAAQ,aAAa,SAAS;AACpC,QAAM,MAAM,QAAQ;AAAA,IAClB,OAAO,IAAI;AAAA,IACX,WAAW,OAAO,KAAK;AAAA,IACvB,OAAO;AAAA,MACL,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,MAChB,QAAQ,KAAK;AAAA,IACf;AAAA,EACF,CAAC;AAED,SAAO,aAAa,KAAK,EAAE,IAAI,IAAI,IAAI,eAAe,YAAY,GAAG,GAAG,EAAE,QAAQ,IAAI,CAAC;AACzF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
const paramsSchema = z.object({ id: z.string().uuid() });
|
|
6
|
+
const metadata = {
|
|
7
|
+
POST: { requireAuth: true, requireFeatures: ["data_sync.run"] }
|
|
8
|
+
};
|
|
9
|
+
const openApi = {
|
|
10
|
+
tags: ["DataSync"],
|
|
11
|
+
summary: "Cancel a running sync"
|
|
12
|
+
};
|
|
13
|
+
async function POST(req, ctx) {
|
|
14
|
+
const auth = await getAuthFromRequest(req);
|
|
15
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
16
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
17
|
+
}
|
|
18
|
+
const rawParams = ctx.params && typeof ctx.params.then === "function" ? await ctx.params : ctx.params;
|
|
19
|
+
const parsed = paramsSchema.safeParse(rawParams);
|
|
20
|
+
if (!parsed.success) {
|
|
21
|
+
return NextResponse.json({ error: "Invalid run id" }, { status: 400 });
|
|
22
|
+
}
|
|
23
|
+
const container = await createRequestContainer();
|
|
24
|
+
const syncRunService = container.resolve("dataSyncRunService");
|
|
25
|
+
const progressService = container.resolve("progressService");
|
|
26
|
+
const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
|
|
27
|
+
const run = await syncRunService.getRun(parsed.data.id, scope);
|
|
28
|
+
if (!run) {
|
|
29
|
+
return NextResponse.json({ error: "Run not found" }, { status: 404 });
|
|
30
|
+
}
|
|
31
|
+
if (run.status !== "running" && run.status !== "pending") {
|
|
32
|
+
return NextResponse.json({ error: "Only pending or running runs can be cancelled" }, { status: 409 });
|
|
33
|
+
}
|
|
34
|
+
if (run.progressJobId) {
|
|
35
|
+
await progressService.cancelJob(run.progressJobId, {
|
|
36
|
+
tenantId: auth.tenantId,
|
|
37
|
+
organizationId: auth.orgId,
|
|
38
|
+
userId: auth.sub
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
await syncRunService.markStatus(run.id, "cancelled", scope);
|
|
42
|
+
return NextResponse.json({ ok: true });
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
POST,
|
|
46
|
+
metadata,
|
|
47
|
+
openApi
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=cancel.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/data_sync/api/runs/%5Bid%5D/cancel.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Cancel a running sync',\n}\n\nexport async function POST(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsed = paramsSchema.safeParse(rawParams)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const run = await syncRunService.getRun(parsed.data.id, scope)\n if (!run) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n if (run.status !== 'running' && run.status !== 'pending') {\n return NextResponse.json({ error: 'Only pending or running runs can be cancelled' }, { status: 409 })\n }\n\n if (run.progressJobId) {\n await progressService.cancelJob(run.progressJobId, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub,\n })\n }\n\n await syncRunService.markStatus(run.id, 'cancelled', scope)\n return NextResponse.json({ ok: true })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAIvC,MAAM,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAEhD,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAChE;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,UAAU;AAAA,EACjB,SAAS;AACX;AAEA,eAAsB,KAAK,KAAc,KAA8D;AACrG,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,YAAa,IAAI,UAAU,OAAQ,IAAI,OAA4B,SAAS,aAC9E,MAAO,IAAI,SACV,IAAI;AAET,QAAM,SAAS,aAAa,UAAU,SAAS;AAC/C,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,iBAAiB,UAAU,QAAQ,oBAAoB;AAC7D,QAAM,kBAAkB,UAAU,QAAQ,iBAAiB;AAC3D,QAAM,QAAQ,EAAE,gBAAgB,KAAK,OAAiB,UAAU,KAAK,SAAS;AAE9E,QAAM,MAAM,MAAM,eAAe,OAAO,OAAO,KAAK,IAAI,KAAK;AAC7D,MAAI,CAAC,KAAK;AACR,WAAO,aAAa,KAAK,EAAE,OAAO,gBAAgB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtE;AACA,MAAI,IAAI,WAAW,aAAa,IAAI,WAAW,WAAW;AACxD,WAAO,aAAa,KAAK,EAAE,OAAO,gDAAgD,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtG;AAEA,MAAI,IAAI,eAAe;AACrB,UAAM,gBAAgB,UAAU,IAAI,eAAe;AAAA,MACjD,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK;AAAA,MACrB,QAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,WAAW,IAAI,IAAI,aAAa,KAAK;AAC1D,SAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AACvC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { retrySyncSchema } from "../../../data/validators.js";
|
|
6
|
+
import { getSyncQueue } from "../../../lib/queue.js";
|
|
7
|
+
const paramsSchema = z.object({ id: z.string().uuid() });
|
|
8
|
+
const metadata = {
|
|
9
|
+
POST: { requireAuth: true, requireFeatures: ["data_sync.run"] }
|
|
10
|
+
};
|
|
11
|
+
const openApi = {
|
|
12
|
+
tags: ["DataSync"],
|
|
13
|
+
summary: "Retry a failed sync run"
|
|
14
|
+
};
|
|
15
|
+
async function POST(req, ctx) {
|
|
16
|
+
const auth = await getAuthFromRequest(req);
|
|
17
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
18
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
19
|
+
}
|
|
20
|
+
const rawParams = ctx.params && typeof ctx.params.then === "function" ? await ctx.params : ctx.params;
|
|
21
|
+
const parsedParams = paramsSchema.safeParse(rawParams);
|
|
22
|
+
if (!parsedParams.success) {
|
|
23
|
+
return NextResponse.json({ error: "Invalid run id" }, { status: 400 });
|
|
24
|
+
}
|
|
25
|
+
const payload = await req.json().catch(() => null);
|
|
26
|
+
const parsedBody = retrySyncSchema.safeParse(payload ?? {});
|
|
27
|
+
if (!parsedBody.success) {
|
|
28
|
+
return NextResponse.json({ error: "Invalid payload", details: parsedBody.error.flatten() }, { status: 422 });
|
|
29
|
+
}
|
|
30
|
+
const container = await createRequestContainer();
|
|
31
|
+
const syncRunService = container.resolve("dataSyncRunService");
|
|
32
|
+
const progressService = container.resolve("progressService");
|
|
33
|
+
const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
|
|
34
|
+
const previous = await syncRunService.getRun(parsedParams.data.id, scope);
|
|
35
|
+
if (!previous) {
|
|
36
|
+
return NextResponse.json({ error: "Run not found" }, { status: 404 });
|
|
37
|
+
}
|
|
38
|
+
if (previous.status !== "failed" && previous.status !== "cancelled") {
|
|
39
|
+
return NextResponse.json({ error: "Only failed or cancelled runs can be retried" }, { status: 409 });
|
|
40
|
+
}
|
|
41
|
+
const overlap = await syncRunService.findRunningOverlap(
|
|
42
|
+
previous.integrationId,
|
|
43
|
+
previous.entityType,
|
|
44
|
+
previous.direction,
|
|
45
|
+
scope
|
|
46
|
+
);
|
|
47
|
+
if (overlap) {
|
|
48
|
+
return NextResponse.json({ error: "A sync run is already in progress for this integration and entity direction" }, { status: 409 });
|
|
49
|
+
}
|
|
50
|
+
const cursor = parsedBody.data.fromBeginning ? null : previous.cursor ?? await syncRunService.resolveCursor(previous.integrationId, previous.entityType, previous.direction, scope);
|
|
51
|
+
const progressJob = await progressService.createJob(
|
|
52
|
+
{
|
|
53
|
+
jobType: `data_sync:${previous.direction}`,
|
|
54
|
+
name: `Retry data sync ${previous.integrationId}`,
|
|
55
|
+
description: `${previous.entityType} ${previous.direction}`,
|
|
56
|
+
cancellable: true
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
tenantId: auth.tenantId,
|
|
60
|
+
organizationId: auth.orgId,
|
|
61
|
+
userId: auth.sub
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
const run = await syncRunService.createRun(
|
|
65
|
+
{
|
|
66
|
+
integrationId: previous.integrationId,
|
|
67
|
+
entityType: previous.entityType,
|
|
68
|
+
direction: previous.direction,
|
|
69
|
+
cursor,
|
|
70
|
+
triggeredBy: auth.sub,
|
|
71
|
+
progressJobId: progressJob.id
|
|
72
|
+
},
|
|
73
|
+
scope
|
|
74
|
+
);
|
|
75
|
+
const queueName = run.direction === "import" ? "data-sync-import" : "data-sync-export";
|
|
76
|
+
const queue = getSyncQueue(queueName);
|
|
77
|
+
await queue.enqueue({
|
|
78
|
+
runId: run.id,
|
|
79
|
+
batchSize: 100,
|
|
80
|
+
scope: {
|
|
81
|
+
organizationId: scope.organizationId,
|
|
82
|
+
tenantId: scope.tenantId,
|
|
83
|
+
userId: auth.sub
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return NextResponse.json({ id: run.id, progressJobId: progressJob.id }, { status: 201 });
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
POST,
|
|
90
|
+
metadata,
|
|
91
|
+
openApi
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/data_sync/api/runs/%5Bid%5D/retry.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\nimport { retrySyncSchema } from '../../../data/validators'\nimport { getSyncQueue } from '../../../lib/queue'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Retry a failed sync run',\n}\n\nexport async function POST(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const payload = await req.json().catch(() => null)\n const parsedBody = retrySyncSchema.safeParse(payload ?? {})\n if (!parsedBody.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsedBody.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const previous = await syncRunService.getRun(parsedParams.data.id, scope)\n if (!previous) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n if (previous.status !== 'failed' && previous.status !== 'cancelled') {\n return NextResponse.json({ error: 'Only failed or cancelled runs can be retried' }, { status: 409 })\n }\n\n const overlap = await syncRunService.findRunningOverlap(\n previous.integrationId,\n previous.entityType,\n previous.direction,\n scope,\n )\n if (overlap) {\n return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })\n }\n\n const cursor = parsedBody.data.fromBeginning\n ? null\n : previous.cursor ?? await syncRunService.resolveCursor(previous.integrationId, previous.entityType, previous.direction, scope)\n\n const progressJob = await progressService.createJob(\n {\n jobType: `data_sync:${previous.direction}`,\n name: `Retry data sync ${previous.integrationId}`,\n description: `${previous.entityType} ${previous.direction}`,\n cancellable: true,\n },\n {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub,\n },\n )\n\n const run = await syncRunService.createRun(\n {\n integrationId: previous.integrationId,\n entityType: previous.entityType,\n direction: previous.direction,\n cursor,\n triggeredBy: auth.sub,\n progressJobId: progressJob.id,\n },\n scope,\n )\n\n const queueName = run.direction === 'import' ? 'data-sync-import' : 'data-sync-export'\n const queue = getSyncQueue(queueName)\n await queue.enqueue({\n runId: run.id,\n batchSize: 100,\n scope: {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n userId: auth.sub,\n },\n })\n\n return NextResponse.json({ id: run.id, progressJobId: progressJob.id }, { status: 201 })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|