diff --git a/app/api/extensions/ext/[...path]/route.ts b/app/api/extensions/ext/[...path]/route.ts index 08610f34..ce2621be 100644 --- a/app/api/extensions/ext/[...path]/route.ts +++ b/app/api/extensions/ext/[...path]/route.ts @@ -9,6 +9,9 @@ import type { ApiRouteDefinition } from '@/lib/extensions/types' ensureInitialized() +// Heavy extension routes (SIE import, migration) need up to 5 minutes +export const maxDuration = 300 + /** * Match a request path against a route pattern. * Supports :param wildcards (e.g., /:id/confirm). diff --git a/app/api/import/sie/execute/route.ts b/app/api/import/sie/execute/route.ts index 8f85f247..57432285 100644 --- a/app/api/import/sie/execute/route.ts +++ b/app/api/import/sie/execute/route.ts @@ -8,6 +8,9 @@ import { BAS_REFERENCE } from '@/lib/bookkeeping/bas-data' import { getBASReference } from '@/lib/bookkeeping/bas-reference' import type { AccountMapping, SIEAccountMappingRecord } from '@/lib/import/types' +// SIE imports with many vouchers need extended execution time +export const maxDuration = 300 + /** * POST /api/import/sie/execute * Execute the SIE import diff --git a/components/extensions/general/ArcimMigrationWorkspace.tsx b/components/extensions/general/ArcimMigrationWorkspace.tsx index f2b88408..3dc7ecd0 100644 --- a/components/extensions/general/ArcimMigrationWorkspace.tsx +++ b/components/extensions/general/ArcimMigrationWorkspace.tsx @@ -370,15 +370,12 @@ function PreviewStep({ Anslutet till {providerName} - - Vi har hämtat information om ditt företag. Kontrollera att det stämmer. - {isLoading && (
-

Hämtar företagsinformation och bokföringsdata...

+

Hämtar bokföringsdata...

)} @@ -389,28 +386,6 @@ function PreviewStep({ )} - {preview?.companyInfo && ( -
- - - - - - - - -
- )} - - {preview && !preview.companyInfo && !isLoading && ( -

- Ingen företagsinformation kunde hämtas. Du kan fylla i uppgifterna manuellt under Inställningar. -

- )} - {/* SIE stats summary */} {preview?.sieAvailable && preview.sieStats && (
diff --git a/extensions/general/arcim-migration/lib/arcim-client.ts b/extensions/general/arcim-migration/lib/arcim-client.ts index d5f2fb3c..6c37ae9f 100644 --- a/extensions/general/arcim-migration/lib/arcim-client.ts +++ b/extensions/general/arcim-migration/lib/arcim-client.ts @@ -31,17 +31,22 @@ function getApiKey(): string { async function request( path: string, - options: RequestInit = {} + options: RequestInit = {}, + timeoutMs: number = 120_000 ): Promise { const url = `${getBaseUrl()}${path}` + const controller = new AbortController() + const timer = setTimeout(() => controller.abort(), timeoutMs) + const response = await fetch(url, { ...options, + signal: controller.signal, headers: { 'Authorization': `Bearer ${getApiKey()}`, 'Content-Type': 'application/json', ...options.headers, }, - }) + }).finally(() => clearTimeout(timer)) if (!response.ok) { const body = await response.text().catch(() => '') diff --git a/extensions/general/enable-banking/components/BankConnectionStatus.tsx b/extensions/general/enable-banking/components/BankConnectionStatus.tsx index 26d4148d..bdb7c6d9 100644 --- a/extensions/general/enable-banking/components/BankConnectionStatus.tsx +++ b/extensions/general/enable-banking/components/BankConnectionStatus.tsx @@ -98,7 +98,7 @@ export function BankConnectionStatus({ const isConnectionExpired = connection.status === 'expired' const isConnectionError = connection.status === 'error' - const errorMessage = 'error_message' in connection ? String((connection as Record).error_message || '') : '' + const errorMessage = connection.error_message ?? '' return (
diff --git a/lib/import/sie-import.ts b/lib/import/sie-import.ts index 02fe4266..10c21e53 100644 --- a/lib/import/sie-import.ts +++ b/lib/import/sie-import.ts @@ -580,6 +580,7 @@ async function importVouchers( for (let batchStart = 0; batchStart < preparedVouchers.length; batchStart += BATCH_SIZE) { const batch = preparedVouchers.slice(batchStart, batchStart + BATCH_SIZE) const batchNumber = Math.floor(batchStart / BATCH_SIZE) + 1 + let batchWasRetried = false // Prepare journal entry headers const entryInserts = batch.map((v, i) => ({ @@ -600,7 +601,7 @@ async function importVouchers( for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { if (attempt > 0) { - retriedBatches++ + batchWasRetried = true const backoffMs = Math.pow(2, attempt - 1) * 1000 // 1s, 2s, 4s console.log(`[sie-import] Retrying batch ${batchNumber} (attempt ${attempt + 1}/${MAX_RETRIES + 1}) after ${backoffMs}ms`) await new Promise(resolve => setTimeout(resolve, backoffMs)) @@ -675,7 +676,7 @@ async function importVouchers( for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { if (attempt > 0) { - retriedBatches++ + batchWasRetried = true const backoffMs = Math.pow(2, attempt - 1) * 1000 console.log(`[sie-import] Retrying batch ${batchNumber} lines (attempt ${attempt + 1}/${MAX_RETRIES + 1}) after ${backoffMs}ms`) await new Promise(resolve => setTimeout(resolve, backoffMs)) @@ -727,6 +728,11 @@ async function importVouchers( } } + // Count distinct batches that needed retries (not individual attempts) + if (batchWasRetried) { + retriedBatches++ + } + // Small delay between batches to prevent Supabase/Cloudflare rate limiting if (batchStart + BATCH_SIZE < preparedVouchers.length) { await new Promise(resolve => setTimeout(resolve, INTER_BATCH_DELAY_MS)) diff --git a/types/index.ts b/types/index.ts index c3e63d87..ccfbd8a8 100644 --- a/types/index.ts +++ b/types/index.ts @@ -167,6 +167,7 @@ export interface BankConnection { // Consent consent_expires: string | null last_synced_at: string | null + error_message: string | null created_at: string updated_at: string