@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.
Files changed (226) hide show
  1. package/AGENTS.md +10 -0
  2. package/dist/generated/entities/integration_credentials/index.js +19 -0
  3. package/dist/generated/entities/integration_credentials/index.js.map +7 -0
  4. package/dist/generated/entities/integration_log/index.js +27 -0
  5. package/dist/generated/entities/integration_log/index.js.map +7 -0
  6. package/dist/generated/entities/integration_state/index.js +27 -0
  7. package/dist/generated/entities/integration_state/index.js.map +7 -0
  8. package/dist/generated/entities/sync_cursor/index.js +19 -0
  9. package/dist/generated/entities/sync_cursor/index.js.map +7 -0
  10. package/dist/generated/entities/sync_external_id_mapping/index.js +27 -0
  11. package/dist/generated/entities/sync_external_id_mapping/index.js.map +7 -0
  12. package/dist/generated/entities/sync_mapping/index.js +19 -0
  13. package/dist/generated/entities/sync_mapping/index.js.map +7 -0
  14. package/dist/generated/entities/sync_run/index.js +45 -0
  15. package/dist/generated/entities/sync_run/index.js.map +7 -0
  16. package/dist/generated/entities/sync_schedule/index.js +35 -0
  17. package/dist/generated/entities/sync_schedule/index.js.map +7 -0
  18. package/dist/generated/entities.ids.generated.js +14 -0
  19. package/dist/generated/entities.ids.generated.js.map +2 -2
  20. package/dist/generated/entity-fields-registry.js +16 -0
  21. package/dist/generated/entity-fields-registry.js.map +2 -2
  22. package/dist/modules/data_sync/acl.js +11 -0
  23. package/dist/modules/data_sync/acl.js.map +7 -0
  24. package/dist/modules/data_sync/api/mappings/[id]/route.js +137 -0
  25. package/dist/modules/data_sync/api/mappings/[id]/route.js.map +7 -0
  26. package/dist/modules/data_sync/api/mappings/route.js +132 -0
  27. package/dist/modules/data_sync/api/mappings/route.js.map +7 -0
  28. package/dist/modules/data_sync/api/run.js +87 -0
  29. package/dist/modules/data_sync/api/run.js.map +7 -0
  30. package/dist/modules/data_sync/api/runs/[id]/cancel.js +49 -0
  31. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +7 -0
  32. package/dist/modules/data_sync/api/runs/[id]/retry.js +93 -0
  33. package/dist/modules/data_sync/api/runs/[id]/retry.js.map +7 -0
  34. package/dist/modules/data_sync/api/runs/[id]/route.js +69 -0
  35. package/dist/modules/data_sync/api/runs/[id]/route.js.map +7 -0
  36. package/dist/modules/data_sync/api/runs.js +66 -0
  37. package/dist/modules/data_sync/api/runs.js.map +7 -0
  38. package/dist/modules/data_sync/api/validate.js +66 -0
  39. package/dist/modules/data_sync/api/validate.js.map +7 -0
  40. package/dist/modules/data_sync/backend/data-sync/page.js +216 -0
  41. package/dist/modules/data_sync/backend/data-sync/page.js.map +7 -0
  42. package/dist/modules/data_sync/backend/data-sync/page.meta.js +25 -0
  43. package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +7 -0
  44. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +178 -0
  45. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +7 -0
  46. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js +14 -0
  47. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js.map +7 -0
  48. package/dist/modules/data_sync/data/entities.js +228 -0
  49. package/dist/modules/data_sync/data/entities.js.map +7 -0
  50. package/dist/modules/data_sync/data/validators.js +32 -0
  51. package/dist/modules/data_sync/data/validators.js.map +7 -0
  52. package/dist/modules/data_sync/di.js +26 -0
  53. package/dist/modules/data_sync/di.js.map +7 -0
  54. package/dist/modules/data_sync/events.js +16 -0
  55. package/dist/modules/data_sync/events.js.map +7 -0
  56. package/dist/modules/data_sync/index.js +9 -0
  57. package/dist/modules/data_sync/index.js.map +7 -0
  58. package/dist/modules/data_sync/lib/adapter-registry.js +16 -0
  59. package/dist/modules/data_sync/lib/adapter-registry.js.map +7 -0
  60. package/dist/modules/data_sync/lib/adapter.js +1 -0
  61. package/dist/modules/data_sync/lib/adapter.js.map +7 -0
  62. package/dist/modules/data_sync/lib/id-mapping.js +79 -0
  63. package/dist/modules/data_sync/lib/id-mapping.js.map +7 -0
  64. package/dist/modules/data_sync/lib/queue.js +17 -0
  65. package/dist/modules/data_sync/lib/queue.js.map +7 -0
  66. package/dist/modules/data_sync/lib/sync-engine.js +309 -0
  67. package/dist/modules/data_sync/lib/sync-engine.js.map +7 -0
  68. package/dist/modules/data_sync/lib/sync-run-service.js +148 -0
  69. package/dist/modules/data_sync/lib/sync-run-service.js.map +7 -0
  70. package/dist/modules/data_sync/migrations/Migration20260304113737.js +17 -0
  71. package/dist/modules/data_sync/migrations/Migration20260304113737.js.map +7 -0
  72. package/dist/modules/data_sync/setup.js +13 -0
  73. package/dist/modules/data_sync/setup.js.map +7 -0
  74. package/dist/modules/data_sync/workers/sync-export.js +14 -0
  75. package/dist/modules/data_sync/workers/sync-export.js.map +7 -0
  76. package/dist/modules/data_sync/workers/sync-import.js +14 -0
  77. package/dist/modules/data_sync/workers/sync-import.js.map +7 -0
  78. package/dist/modules/data_sync/workers/sync-scheduled.js +63 -0
  79. package/dist/modules/data_sync/workers/sync-scheduled.js.map +7 -0
  80. package/dist/modules/entities/lib/encryptionDefaults.js +4 -0
  81. package/dist/modules/entities/lib/encryptionDefaults.js.map +2 -2
  82. package/dist/modules/integrations/acl.js +4 -1
  83. package/dist/modules/integrations/acl.js.map +2 -2
  84. package/dist/modules/integrations/api/[id]/credentials/route.js +127 -0
  85. package/dist/modules/integrations/api/[id]/credentials/route.js.map +7 -0
  86. package/dist/modules/integrations/api/[id]/health/route.js +46 -0
  87. package/dist/modules/integrations/api/[id]/health/route.js.map +7 -0
  88. package/dist/modules/integrations/api/[id]/route.js +65 -0
  89. package/dist/modules/integrations/api/[id]/route.js.map +7 -0
  90. package/dist/modules/integrations/api/[id]/state/route.js +109 -0
  91. package/dist/modules/integrations/api/[id]/state/route.js.map +7 -0
  92. package/dist/modules/integrations/api/[id]/version/route.js +117 -0
  93. package/dist/modules/integrations/api/[id]/version/route.js.map +7 -0
  94. package/dist/modules/integrations/api/guards.js +31 -0
  95. package/dist/modules/integrations/api/guards.js.map +7 -0
  96. package/dist/modules/integrations/api/logs/route.js +60 -0
  97. package/dist/modules/integrations/api/logs/route.js.map +7 -0
  98. package/dist/modules/integrations/api/openapi.js +25 -0
  99. package/dist/modules/integrations/api/openapi.js.map +7 -0
  100. package/dist/modules/integrations/api/route.js +68 -0
  101. package/dist/modules/integrations/api/route.js.map +7 -0
  102. package/dist/modules/integrations/backend/integrations/[id]/page.js +313 -0
  103. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +7 -0
  104. package/dist/modules/integrations/backend/integrations/[id]/page.meta.js +15 -0
  105. package/dist/modules/integrations/backend/integrations/[id]/page.meta.js.map +7 -0
  106. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +189 -0
  107. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +7 -0
  108. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js +15 -0
  109. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js.map +7 -0
  110. package/dist/modules/integrations/backend/integrations/page.js +212 -0
  111. package/dist/modules/integrations/backend/integrations/page.js.map +7 -0
  112. package/dist/modules/integrations/backend/integrations/page.meta.js +22 -0
  113. package/dist/modules/integrations/backend/integrations/page.meta.js.map +7 -0
  114. package/dist/modules/integrations/data/enrichers.js +27 -12
  115. package/dist/modules/integrations/data/enrichers.js.map +2 -2
  116. package/dist/modules/integrations/data/entities.js +136 -1
  117. package/dist/modules/integrations/data/entities.js.map +2 -2
  118. package/dist/modules/integrations/data/validators.js +36 -0
  119. package/dist/modules/integrations/data/validators.js.map +7 -0
  120. package/dist/modules/integrations/di.js +24 -0
  121. package/dist/modules/integrations/di.js.map +7 -0
  122. package/dist/modules/integrations/events.js +19 -0
  123. package/dist/modules/integrations/events.js.map +7 -0
  124. package/dist/modules/integrations/lib/credentials-service.js +159 -0
  125. package/dist/modules/integrations/lib/credentials-service.js.map +7 -0
  126. package/dist/modules/integrations/lib/health-service.js +37 -0
  127. package/dist/modules/integrations/lib/health-service.js.map +7 -0
  128. package/dist/modules/integrations/lib/log-service.js +66 -0
  129. package/dist/modules/integrations/lib/log-service.js.map +7 -0
  130. package/dist/modules/integrations/lib/registry-service.js +33 -0
  131. package/dist/modules/integrations/lib/registry-service.js.map +7 -0
  132. package/dist/modules/integrations/lib/state-service.js +55 -0
  133. package/dist/modules/integrations/lib/state-service.js.map +7 -0
  134. package/dist/modules/integrations/lib/types.js +1 -0
  135. package/dist/modules/integrations/lib/types.js.map +7 -0
  136. package/dist/modules/integrations/migrations/Migration20260304113737.js +19 -0
  137. package/dist/modules/integrations/migrations/Migration20260304113737.js.map +7 -0
  138. package/dist/modules/integrations/setup.js +2 -2
  139. package/dist/modules/integrations/setup.js.map +2 -2
  140. package/dist/modules/integrations/widgets/injection-table.js.map +1 -1
  141. package/dist/modules/integrations/workers/log-pruner.js +18 -0
  142. package/dist/modules/integrations/workers/log-pruner.js.map +7 -0
  143. package/generated/entities/integration_credentials/index.ts +8 -0
  144. package/generated/entities/integration_log/index.ts +12 -0
  145. package/generated/entities/integration_state/index.ts +12 -0
  146. package/generated/entities/sync_cursor/index.ts +8 -0
  147. package/generated/entities/sync_external_id_mapping/index.ts +12 -0
  148. package/generated/entities/sync_mapping/index.ts +8 -0
  149. package/generated/entities/sync_run/index.ts +21 -0
  150. package/generated/entities/sync_schedule/index.ts +16 -0
  151. package/generated/entities.ids.generated.ts +14 -0
  152. package/generated/entity-fields-registry.ts +16 -0
  153. package/package.json +2 -2
  154. package/src/modules/data_sync/AGENTS.md +157 -0
  155. package/src/modules/data_sync/acl.ts +7 -0
  156. package/src/modules/data_sync/api/mappings/[id]/route.ts +158 -0
  157. package/src/modules/data_sync/api/mappings/route.ts +144 -0
  158. package/src/modules/data_sync/api/run.ts +97 -0
  159. package/src/modules/data_sync/api/runs/[id]/cancel.ts +57 -0
  160. package/src/modules/data_sync/api/runs/[id]/retry.ts +108 -0
  161. package/src/modules/data_sync/api/runs/[id]/route.ts +81 -0
  162. package/src/modules/data_sync/api/runs.ts +69 -0
  163. package/src/modules/data_sync/api/validate.ts +73 -0
  164. package/src/modules/data_sync/backend/data-sync/page.meta.ts +21 -0
  165. package/src/modules/data_sync/backend/data-sync/page.tsx +244 -0
  166. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.meta.ts +10 -0
  167. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +278 -0
  168. package/src/modules/data_sync/data/entities.ts +180 -0
  169. package/src/modules/data_sync/data/validators.ts +35 -0
  170. package/src/modules/data_sync/di.ts +38 -0
  171. package/src/modules/data_sync/events.ts +12 -0
  172. package/src/modules/data_sync/i18n/de.json +48 -0
  173. package/src/modules/data_sync/i18n/en.json +48 -0
  174. package/src/modules/data_sync/i18n/es.json +48 -0
  175. package/src/modules/data_sync/i18n/pl.json +48 -0
  176. package/src/modules/data_sync/index.ts +5 -0
  177. package/src/modules/data_sync/lib/adapter-registry.ts +15 -0
  178. package/src/modules/data_sync/lib/adapter.ts +90 -0
  179. package/src/modules/data_sync/lib/id-mapping.ts +95 -0
  180. package/src/modules/data_sync/lib/queue.ts +19 -0
  181. package/src/modules/data_sync/lib/sync-engine.ts +375 -0
  182. package/src/modules/data_sync/lib/sync-run-service.ts +187 -0
  183. package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +653 -0
  184. package/src/modules/data_sync/migrations/Migration20260304113737.ts +19 -0
  185. package/src/modules/data_sync/setup.ts +11 -0
  186. package/src/modules/data_sync/workers/sync-export.ts +27 -0
  187. package/src/modules/data_sync/workers/sync-import.ts +27 -0
  188. package/src/modules/data_sync/workers/sync-scheduled.ts +84 -0
  189. package/src/modules/entities/lib/encryptionDefaults.ts +4 -0
  190. package/src/modules/integrations/AGENTS.md +160 -0
  191. package/src/modules/integrations/acl.ts +3 -0
  192. package/src/modules/integrations/api/[id]/credentials/route.ts +142 -0
  193. package/src/modules/integrations/api/[id]/health/route.ts +53 -0
  194. package/src/modules/integrations/api/[id]/route.ts +76 -0
  195. package/src/modules/integrations/api/[id]/state/route.ts +121 -0
  196. package/src/modules/integrations/api/[id]/version/route.ts +132 -0
  197. package/src/modules/integrations/api/guards.ts +59 -0
  198. package/src/modules/integrations/api/logs/route.ts +63 -0
  199. package/src/modules/integrations/api/openapi.ts +22 -0
  200. package/src/modules/integrations/api/route.ts +73 -0
  201. package/src/modules/integrations/backend/integrations/[id]/page.meta.ts +11 -0
  202. package/src/modules/integrations/backend/integrations/[id]/page.tsx +424 -0
  203. package/src/modules/integrations/backend/integrations/bundle/[id]/page.meta.ts +11 -0
  204. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +249 -0
  205. package/src/modules/integrations/backend/integrations/page.meta.ts +18 -0
  206. package/src/modules/integrations/backend/integrations/page.tsx +296 -0
  207. package/src/modules/integrations/data/enrichers.ts +35 -18
  208. package/src/modules/integrations/data/entities.ts +114 -5
  209. package/src/modules/integrations/data/validators.ts +41 -0
  210. package/src/modules/integrations/di.ts +31 -0
  211. package/src/modules/integrations/events.ts +17 -0
  212. package/src/modules/integrations/i18n/de.json +70 -0
  213. package/src/modules/integrations/i18n/en.json +70 -0
  214. package/src/modules/integrations/i18n/es.json +70 -0
  215. package/src/modules/integrations/i18n/pl.json +70 -0
  216. package/src/modules/integrations/lib/credentials-service.ts +204 -0
  217. package/src/modules/integrations/lib/health-service.ts +59 -0
  218. package/src/modules/integrations/lib/log-service.ts +84 -0
  219. package/src/modules/integrations/lib/registry-service.ts +42 -0
  220. package/src/modules/integrations/lib/state-service.ts +64 -0
  221. package/src/modules/integrations/lib/types.ts +4 -0
  222. package/src/modules/integrations/migrations/.snapshot-open-mercato.json +582 -0
  223. package/src/modules/integrations/migrations/Migration20260304113737.ts +21 -0
  224. package/src/modules/integrations/setup.ts +2 -2
  225. package/src/modules/integrations/widgets/injection-table.ts +1 -1
  226. 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
+ }