From ab5ead3416f4b8ba3cb17ece86ad8a8502239a2f Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Thu, 28 May 2026 11:25:24 +0200
Subject: [PATCH] fix db import
---
src/pages/admin/AdminDatabaseHealthPage.tsx | 81 ++++++++++++++++++---
1 file changed, 71 insertions(+), 10 deletions(-)
diff --git a/src/pages/admin/AdminDatabaseHealthPage.tsx b/src/pages/admin/AdminDatabaseHealthPage.tsx
index 2df9fb1..6ab7b8b 100644
--- a/src/pages/admin/AdminDatabaseHealthPage.tsx
+++ b/src/pages/admin/AdminDatabaseHealthPage.tsx
@@ -961,38 +961,99 @@ const MigrationTab: React.FC = () => {
const fileMb = (file.size / (1024 * 1024)).toFixed(1);
try {
+ // Phase 1: Upload file to disk
const formData = new FormData();
formData.append('file', file);
- const res = await api.post('/api/admin/database-health/migration/upload-import', formData, {
+ const uploadRes = await api.post('/api/admin/database-health/migration/upload-import', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 0,
onUploadProgress: (e) => {
if (e.total) {
const pct = Math.round((e.loaded / e.total) * 100);
const loadedMb = (e.loaded / (1024 * 1024)).toFixed(1);
- setUploadProgress(pct < 100
- ? t('Upload: {loaded} / {total} MB ({pct}%)', { loaded: loadedMb, total: fileMb, pct })
- : t('Validierung laeuft ({total} MB)...', { total: fileMb }),
+ setUploadProgress(
+ pct < 100
+ ? t('Upload: {loaded} / {total} MB ({pct}%)', { loaded: loadedMb, total: fileMb, pct })
+ : t('Verarbeitung wird gestartet...'),
);
}
},
});
+
+ const token = uploadRes.data.token;
+ if (!token) throw new Error('No token returned from upload');
+
+ // Phase 2: Stream validation + split with progress
+ const baseURL = api.defaults.baseURL || '';
+ const streamUrl = `${baseURL}/api/admin/database-health/migration/process-import-stream?token=${encodeURIComponent(token)}`;
+ const streamRes = await fetch(streamUrl, { credentials: 'include' });
+
+ if (!streamRes.ok) {
+ const errText = await streamRes.text();
+ throw new Error(`Server ${streamRes.status}: ${errText}`);
+ }
+ if (!streamRes.body) throw new Error('ReadableStream not supported');
+
+ const reader = streamRes.body.getReader();
+ const decoder = new TextDecoder();
+ let buf = '';
+ let finalResult: any = null;
+
+ for (;;) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ buf += decoder.decode(value, { stream: true });
+
+ const lines = buf.split('\n');
+ buf = lines.pop() || '';
+
+ for (const line of lines) {
+ if (!line.trim()) continue;
+ try {
+ const evt = JSON.parse(line);
+ if (evt.phase === 'validate') {
+ setUploadProgress(
+ t('Pass 1 Validierung: {db}.{table} ({rows} Datensaetze)', {
+ db: evt.db, table: evt.table, rows: evt.rows,
+ }),
+ );
+ } else if (evt.phase === 'split') {
+ setUploadProgress(
+ t('Pass 2 Split: {db}.{table} ({rows} Datensaetze)', {
+ db: evt.db, table: evt.table, rows: evt.rows,
+ }),
+ );
+ } else if (evt.phase === 'done') {
+ finalResult = evt.result;
+ } else if (evt.phase === 'error') {
+ throw new Error(evt.detail || 'Processing failed');
+ }
+ } catch (parseErr) {
+ if ((parseErr as Error).message?.startsWith('Processing'))
+ throw parseErr;
+ }
+ }
+ }
+
setUploadProgress('');
- importTokenRef.current = res.data.token || '';
+
+ if (!finalResult) throw new Error('No result received from processing stream');
+
+ importTokenRef.current = finalResult.token || token;
setValidation({
- valid: res.data.valid,
- summary: (res.data.databases || []).map((d: any) => ({
+ valid: finalResult.valid,
+ summary: (finalResult.databases || []).map((d: any) => ({
database: d.database,
tableCount: d.tableCount,
recordCount: d.recordCount,
registered: true,
})),
- warnings: res.data.warnings || [],
- systemObjectsFound: res.data.systemObjectsFound || [],
+ warnings: finalResult.warnings || [],
+ systemObjectsFound: finalResult.systemObjectsFound || [],
});
} catch (err: any) {
setUploadProgress('');
- const detail = err?.response?.data?.detail;
+ const detail = err?.response?.data?.detail || err?.message;
setValidation({
valid: false, summary: [], systemObjectsFound: [],
warnings: [typeof detail === 'string' ? detail : t('Upload oder Validierung fehlgeschlagen')],