@open-mercato/core 0.6.3-develop.3593.1.8eda8943fa → 0.6.3-develop.3598.1.259c138c7d

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sync_excel/api/import/route.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport type { ProgressService } from '@open-mercato/core/modules/progress/lib/progressService'\nimport type { SyncRunService } from '@open-mercato/core/modules/data_sync/lib/sync-run-service'\nimport { startDataSyncRun } from '@open-mercato/core/modules/data_sync/lib/start-run'\nimport { SyncMapping } from '@open-mercato/core/modules/data_sync/data/entities'\nimport type { CredentialsService } from '@open-mercato/core/modules/integrations/lib/credentials-service'\nimport type { IntegrationStateService } from '@open-mercato/core/modules/integrations/lib/state-service'\nimport { SyncExcelUpload } from '../../data/entities'\nimport { Attachment } from '../../../attachments/data/entities'\nimport { createCursor } from '../../lib/adapters/customers'\nimport {\n syncExcelImportRequestSchema,\n syncExcelImportResponseSchema,\n} from '../../data/validators'\nimport { resolveSyncExcelConcreteScope } from '../../lib/scope'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['sync_excel.run'] },\n}\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi = {\n tags: ['SyncExcel'],\n summary: 'Start a CSV import run for a stored sync_excel upload',\n methods: {\n POST: {\n summary: 'Start CSV import',\n responses: [\n { status: 201, description: 'Import run started', schema: syncExcelImportResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Upload not found', schema: errorSchema },\n { status: 409, description: 'Import overlap detected', schema: errorSchema },\n { status: 422, description: 'Invalid import payload', schema: errorSchema },\n ],\n },\n },\n}\n\nexport async function POST(request: Request) {\n const auth = await getAuthFromRequest(request)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const payload = await readJsonSafe(request)\n const parsedPayload = syncExcelImportRequestSchema.safeParse(payload)\n if (!parsedPayload.success) {\n return NextResponse.json({ error: 'Invalid import payload.' }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService\n const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request })\n if (!scopeResult.ok) {\n return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status })\n }\n const { scope } = scopeResult\n\n const upload = await findOneWithDecryption(\n em,\n SyncExcelUpload,\n {\n id: parsedPayload.data.uploadId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!upload) {\n return NextResponse.json({ error: 'Upload preview not found.' }, { status: 404 })\n }\n\n const attachment = await findOneWithDecryption(\n em,\n Attachment,\n {\n id: upload.attachmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!attachment) {\n return NextResponse.json({ error: 'Upload attachment not found.' }, { status: 404 })\n }\n\n if (upload.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Upload entity type does not match requested import target.' }, { status: 422 })\n }\n\n if (parsedPayload.data.mapping.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Mapping entity type does not match requested import target.' }, { status: 422 })\n }\n\n const overlap = await syncRunService.findRunningOverlap('sync_excel', parsedPayload.data.entityType, 'import', scope)\n if (overlap) {\n return NextResponse.json({ error: 'A sync_excel import is already in progress for this entity type.' }, { status: 409 })\n }\n\n const existingMapping = await findOneWithDecryption(\n em,\n SyncMapping,\n {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (existingMapping) {\n existingMapping.mapping = parsedPayload.data.mapping\n } else {\n em.persist(em.create(SyncMapping, {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n mapping: parsedPayload.data.mapping,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n }))\n }\n\n await credentialsService.save('sync_excel', {}, scope)\n await integrationStateService.upsert('sync_excel', { isEnabled: true }, scope)\n await em.flush()\n\n const { run, progressJob } = await startDataSyncRun({\n syncRunService,\n progressService,\n scope: {\n ...scope,\n userId: auth.sub,\n },\n input: {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n direction: 'import',\n cursor: createCursor(upload.id, 0),\n triggeredBy: auth.sub,\n batchSize: parsedPayload.data.batchSize ?? 100,\n progressJob: {\n jobType: 'sync_excel:import',\n name: `CSV import \u2014 ${parsedPayload.data.entityType}`,\n description: upload.filename,\n meta: {\n integrationId: 'sync_excel',\n uploadId: upload.id,\n hiddenFromTopBar: false,\n },\n },\n },\n })\n\n upload.syncRunId = run.id\n upload.status = 'importing'\n await em.flush()\n\n return NextResponse.json(syncExcelImportResponseSchema.parse({\n runId: run.id,\n progressJobId: progressJob?.id ?? null,\n status: run.status,\n }), { status: 201 })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport type { ProgressService } from '@open-mercato/core/modules/progress/lib/progressService'\nimport type { SyncRunService } from '@open-mercato/core/modules/data_sync/lib/sync-run-service'\nimport { startDataSyncRun } from '@open-mercato/core/modules/data_sync/lib/start-run'\nimport { SyncMapping } from '@open-mercato/core/modules/data_sync/data/entities'\nimport type { CredentialsService } from '@open-mercato/core/modules/integrations/lib/credentials-service'\nimport type { IntegrationStateService } from '@open-mercato/core/modules/integrations/lib/state-service'\nimport { SyncExcelUpload } from '../../data/entities'\nimport { Attachment } from '../../../attachments/data/entities'\nimport { createCursor } from '../../lib/adapters/customers'\nimport {\n syncExcelImportRequestSchema,\n syncExcelImportResponseSchema,\n} from '../../data/validators'\nimport { resolveSyncExcelConcreteScope } from '../../lib/scope'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['sync_excel.run'] },\n}\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi = {\n tags: ['SyncExcel'],\n summary: 'Start a CSV import run for a stored sync_excel upload',\n methods: {\n POST: {\n summary: 'Start CSV import',\n responses: [\n { status: 201, description: 'Import run started', schema: syncExcelImportResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Upload not found', schema: errorSchema },\n { status: 409, description: 'Import overlap detected', schema: errorSchema },\n { status: 422, description: 'Invalid import payload', schema: errorSchema },\n ],\n },\n },\n}\n\nexport async function POST(request: Request) {\n try {\n const auth = await getAuthFromRequest(request)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const payload = await readJsonSafe(request)\n const parsedPayload = syncExcelImportRequestSchema.safeParse(payload)\n if (!parsedPayload.success) {\n return NextResponse.json({ error: 'Invalid import payload.' }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService\n const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request })\n if (!scopeResult.ok) {\n return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status })\n }\n const { scope } = scopeResult\n\n const upload = await findOneWithDecryption(\n em,\n SyncExcelUpload,\n {\n id: parsedPayload.data.uploadId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!upload) {\n return NextResponse.json({ error: 'Upload preview not found.' }, { status: 404 })\n }\n\n const attachment = await findOneWithDecryption(\n em,\n Attachment,\n {\n id: upload.attachmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!attachment) {\n return NextResponse.json({ error: 'Upload attachment not found.' }, { status: 404 })\n }\n\n if (upload.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Upload entity type does not match requested import target.' }, { status: 422 })\n }\n\n if (parsedPayload.data.mapping.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Mapping entity type does not match requested import target.' }, { status: 422 })\n }\n\n const overlap = await syncRunService.findRunningOverlap('sync_excel', parsedPayload.data.entityType, 'import', scope)\n if (overlap) {\n return NextResponse.json({ error: 'A sync_excel import is already in progress for this entity type.' }, { status: 409 })\n }\n\n const existingMapping = await findOneWithDecryption(\n em,\n SyncMapping,\n {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (existingMapping) {\n existingMapping.mapping = parsedPayload.data.mapping\n } else {\n em.persist(em.create(SyncMapping, {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n mapping: parsedPayload.data.mapping,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n }))\n }\n\n await credentialsService.save('sync_excel', {}, scope)\n await integrationStateService.upsert('sync_excel', { isEnabled: true }, scope)\n await em.flush()\n\n const { run, progressJob } = await startDataSyncRun({\n syncRunService,\n progressService,\n scope: {\n ...scope,\n userId: auth.sub,\n },\n input: {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n direction: 'import',\n cursor: createCursor(upload.id, 0),\n triggeredBy: auth.sub,\n batchSize: parsedPayload.data.batchSize ?? 100,\n progressJob: {\n jobType: 'sync_excel:import',\n name: `CSV import \u2014 ${parsedPayload.data.entityType}`,\n description: upload.filename,\n meta: {\n integrationId: 'sync_excel',\n uploadId: upload.id,\n hiddenFromTopBar: false,\n },\n },\n },\n })\n\n upload.syncRunId = run.id\n upload.status = 'importing'\n await em.flush()\n\n return NextResponse.json(syncExcelImportResponseSchema.parse({\n runId: run.id,\n progressJobId: progressJob?.id ?? null,\n status: run.status,\n }), { status: 201 })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n const stack = error instanceof Error ? error.stack : undefined\n console.error('[sync_excel.import] unhandled error', { message, stack })\n return NextResponse.json(\n { error: 'Failed to start sync_excel import.', message },\n { status: 500 },\n )\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.3-develop.3593.1.8eda8943fa",
3
+ "version": "0.6.3-develop.3598.1.259c138c7d",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -242,16 +242,16 @@
242
242
  "zod": "^4.4.3"
243
243
  },
244
244
  "peerDependencies": {
245
- "@open-mercato/ai-assistant": "0.6.3-develop.3593.1.8eda8943fa",
246
- "@open-mercato/shared": "0.6.3-develop.3593.1.8eda8943fa",
247
- "@open-mercato/ui": "0.6.3-develop.3593.1.8eda8943fa",
245
+ "@open-mercato/ai-assistant": "0.6.3-develop.3598.1.259c138c7d",
246
+ "@open-mercato/shared": "0.6.3-develop.3598.1.259c138c7d",
247
+ "@open-mercato/ui": "0.6.3-develop.3598.1.259c138c7d",
248
248
  "react": "^19.0.0",
249
249
  "react-dom": "^19.0.0"
250
250
  },
251
251
  "devDependencies": {
252
- "@open-mercato/ai-assistant": "0.6.3-develop.3593.1.8eda8943fa",
253
- "@open-mercato/shared": "0.6.3-develop.3593.1.8eda8943fa",
254
- "@open-mercato/ui": "0.6.3-develop.3593.1.8eda8943fa",
252
+ "@open-mercato/ai-assistant": "0.6.3-develop.3598.1.259c138c7d",
253
+ "@open-mercato/shared": "0.6.3-develop.3598.1.259c138c7d",
254
+ "@open-mercato/ui": "0.6.3-develop.3598.1.259c138c7d",
255
255
  "@testing-library/dom": "^10.4.1",
256
256
  "@testing-library/jest-dom": "^6.9.1",
257
257
  "@testing-library/react": "^16.3.1",
@@ -872,7 +872,32 @@ export async function createSalesDocument(page: Page, options: CreateDocumentOpt
872
872
  return id;
873
873
  }
874
874
 
875
- await createButton.click();
875
+ // The Create button can detach during the click as the React form re-renders
876
+ // after lookups settle. Retry briefly with a short stability wait, then fall
877
+ // back to the API fixture path if the click never lands.
878
+ let clicked = false;
879
+ for (let attempt = 0; attempt < 3; attempt += 1) {
880
+ try {
881
+ await page.waitForLoadState('networkidle', { timeout: 2_000 }).catch(() => {});
882
+ const liveButton = page.getByRole('button', { name: /^Create$/i }).first();
883
+ if (!(await liveButton.isVisible().catch(() => false))) break;
884
+ if (!(await liveButton.isEnabled().catch(() => false))) {
885
+ await page.waitForTimeout(300);
886
+ continue;
887
+ }
888
+ await liveButton.click({ timeout: 5_000 });
889
+ clicked = true;
890
+ break;
891
+ } catch {
892
+ await page.waitForTimeout(300);
893
+ }
894
+ }
895
+ if (!clicked) {
896
+ const token = await resolveFallbackToken();
897
+ const id = await createSalesDocumentFixture(page, token, options.kind, customerQuery, channelQuery, fixtureContext);
898
+ await openSalesDocumentPage(page, id, options.kind);
899
+ return id;
900
+ }
876
901
  const navigated = await page.waitForURL(
877
902
  new RegExp(
878
903
  `/backend/sales/(?:documents/[0-9a-f-]{36}\\?kind=${options.kind}|${options.kind === 'order' ? 'orders' : 'quotes'}/[0-9a-f-]{36})$`,
@@ -20,85 +20,95 @@ export const openApi = {
20
20
  }
21
21
 
22
22
  export async function POST(req: Request) {
23
- const auth = await getAuthFromRequest(req)
24
- if (!auth?.tenantId || !auth.orgId) {
25
- return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
26
- }
23
+ try {
24
+ const auth = await getAuthFromRequest(req)
25
+ if (!auth?.tenantId || !auth.orgId) {
26
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
27
+ }
27
28
 
28
- const payload = await readJsonSafe(req)
29
- const parsed = runSyncSchema.safeParse(payload)
30
- if (!parsed.success) {
31
- return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })
32
- }
29
+ const payload = await readJsonSafe(req)
30
+ const parsed = runSyncSchema.safeParse(payload)
31
+ if (!parsed.success) {
32
+ return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })
33
+ }
33
34
 
34
- const container = await createRequestContainer()
35
- const syncRunService = container.resolve('dataSyncRunService') as SyncRunService
36
- const progressService = container.resolve('progressService') as ProgressService
37
- const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService
35
+ const container = await createRequestContainer()
36
+ const syncRunService = container.resolve('dataSyncRunService') as SyncRunService
37
+ const progressService = container.resolve('progressService') as ProgressService
38
+ const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService
38
39
 
39
- const scope = {
40
- organizationId: auth.orgId as string,
41
- tenantId: auth.tenantId,
42
- }
40
+ const scope = {
41
+ organizationId: auth.orgId as string,
42
+ tenantId: auth.tenantId,
43
+ }
43
44
 
44
- const integration = getIntegration(parsed.data.integrationId)
45
- if (!integration?.providerKey) {
46
- return NextResponse.json({ error: 'Integration not found' }, { status: 404 })
47
- }
45
+ const integration = getIntegration(parsed.data.integrationId)
46
+ if (!integration?.providerKey) {
47
+ return NextResponse.json({ error: 'Integration not found' }, { status: 404 })
48
+ }
48
49
 
49
- const adapter = getDataSyncAdapter(integration.providerKey)
50
- if (!adapter) {
51
- return NextResponse.json({ error: 'No registered sync adapter for provider' }, { status: 404 })
52
- }
53
- if (adapter.runMode === 'provider') {
54
- return NextResponse.json(
55
- {
56
- error: 'This integration must be started from its provider-specific import flow.',
57
- settingsPath: `/backend/integrations/${encodeURIComponent(parsed.data.integrationId)}`,
58
- },
59
- { status: 422 },
60
- )
61
- }
50
+ const adapter = getDataSyncAdapter(integration.providerKey)
51
+ if (!adapter) {
52
+ return NextResponse.json({ error: 'No registered sync adapter for provider' }, { status: 404 })
53
+ }
54
+ if (adapter.runMode === 'provider') {
55
+ return NextResponse.json(
56
+ {
57
+ error: 'This integration must be started from its provider-specific import flow.',
58
+ settingsPath: `/backend/integrations/${encodeURIComponent(parsed.data.integrationId)}`,
59
+ },
60
+ { status: 422 },
61
+ )
62
+ }
62
63
 
63
- if (!adapter.supportedEntities.includes(parsed.data.entityType)) {
64
- return NextResponse.json({ error: 'Unsupported entity type for this integration' }, { status: 422 })
65
- }
64
+ if (!adapter.supportedEntities.includes(parsed.data.entityType)) {
65
+ return NextResponse.json({ error: 'Unsupported entity type for this integration' }, { status: 422 })
66
+ }
66
67
 
67
- const integrationEnabled = await integrationStateService.isEnabled(parsed.data.integrationId, scope)
68
- if (!integrationEnabled) {
69
- return NextResponse.json({ error: 'Integration is disabled' }, { status: 409 })
70
- }
68
+ const integrationEnabled = await integrationStateService.isEnabled(parsed.data.integrationId, scope)
69
+ if (!integrationEnabled) {
70
+ return NextResponse.json({ error: 'Integration is disabled' }, { status: 409 })
71
+ }
71
72
 
72
- const overlap = await syncRunService.findRunningOverlap(
73
- parsed.data.integrationId,
74
- parsed.data.entityType,
75
- parsed.data.direction,
76
- scope,
77
- )
78
- if (overlap) {
79
- return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })
80
- }
73
+ const overlap = await syncRunService.findRunningOverlap(
74
+ parsed.data.integrationId,
75
+ parsed.data.entityType,
76
+ parsed.data.direction,
77
+ scope,
78
+ )
79
+ if (overlap) {
80
+ return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })
81
+ }
81
82
 
82
- const cursor = parsed.data.fullSync
83
- ? null
84
- : await syncRunService.resolveCursor(parsed.data.integrationId, parsed.data.entityType, parsed.data.direction, scope)
83
+ const cursor = parsed.data.fullSync
84
+ ? null
85
+ : await syncRunService.resolveCursor(parsed.data.integrationId, parsed.data.entityType, parsed.data.direction, scope)
85
86
 
86
- const { run, progressJob } = await startDataSyncRun({
87
- syncRunService,
88
- progressService,
89
- scope: {
90
- ...scope,
91
- userId: auth.sub,
92
- },
93
- input: {
94
- integrationId: parsed.data.integrationId,
95
- entityType: parsed.data.entityType,
96
- direction: parsed.data.direction,
97
- cursor,
98
- triggeredBy: parsed.data.triggeredBy ?? auth.sub,
99
- batchSize: parsed.data.batchSize,
100
- },
101
- })
87
+ const { run, progressJob } = await startDataSyncRun({
88
+ syncRunService,
89
+ progressService,
90
+ scope: {
91
+ ...scope,
92
+ userId: auth.sub,
93
+ },
94
+ input: {
95
+ integrationId: parsed.data.integrationId,
96
+ entityType: parsed.data.entityType,
97
+ direction: parsed.data.direction,
98
+ cursor,
99
+ triggeredBy: parsed.data.triggeredBy ?? auth.sub,
100
+ batchSize: parsed.data.batchSize,
101
+ },
102
+ })
102
103
 
103
- return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 })
104
+ return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 })
105
+ } catch (error) {
106
+ const message = error instanceof Error ? error.message : String(error)
107
+ const stack = error instanceof Error ? error.stack : undefined
108
+ console.error('[data_sync.run] unhandled error', { message, stack })
109
+ return NextResponse.json(
110
+ { error: 'Failed to start data sync run.', message },
111
+ { status: 500 },
112
+ )
113
+ }
104
114
  }
@@ -138,27 +138,37 @@ export async function GET(req: Request) {
138
138
  }
139
139
 
140
140
  export async function POST(req: Request) {
141
- const auth = await getAuthFromRequest(req)
142
- if (!auth || !auth.tenantId) {
143
- return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
144
- }
145
-
146
- const body = await req.json()
147
- const parsed = createProgressJobSchema.safeParse(body)
148
- if (!parsed.success) {
149
- return NextResponse.json({ error: 'Invalid input', details: parsed.error.flatten() }, { status: 400 })
141
+ try {
142
+ const auth = await getAuthFromRequest(req)
143
+ if (!auth || !auth.tenantId) {
144
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
145
+ }
146
+
147
+ const body = await req.json()
148
+ const parsed = createProgressJobSchema.safeParse(body)
149
+ if (!parsed.success) {
150
+ return NextResponse.json({ error: 'Invalid input', details: parsed.error.flatten() }, { status: 400 })
151
+ }
152
+
153
+ const container = await createRequestContainer()
154
+ const progressService = container.resolve('progressService') as import('../../lib/progressService').ProgressService
155
+
156
+ const job = await progressService.createJob(parsed.data, {
157
+ tenantId: auth.tenantId,
158
+ organizationId: auth.orgId,
159
+ userId: auth.sub,
160
+ })
161
+
162
+ return NextResponse.json({ id: job.id }, { status: 201 })
163
+ } catch (error) {
164
+ const message = error instanceof Error ? error.message : String(error)
165
+ const stack = error instanceof Error ? error.stack : undefined
166
+ console.error('[progress.jobs.create] unhandled error', { message, stack })
167
+ return NextResponse.json(
168
+ { error: 'Failed to create progress job.', message },
169
+ { status: 500 },
170
+ )
150
171
  }
151
-
152
- const container = await createRequestContainer()
153
- const progressService = container.resolve('progressService') as import('../../lib/progressService').ProgressService
154
-
155
- const job = await progressService.createJob(parsed.data, {
156
- tenantId: auth.tenantId,
157
- organizationId: auth.orgId,
158
- userId: auth.sub,
159
- })
160
-
161
- return NextResponse.json({ id: job.id }, { status: 201 })
162
172
  }
163
173
 
164
174
  const jobListItemSchema = z.object({