fix db import
All checks were successful
Deploy Nyla Frontend to Production / deploy (push) Successful in 45s
Deploy Nyla Frontend to Integration / deploy (push) Successful in 1m30s

This commit is contained in:
ValueOn AG 2026-05-28 11:25:24 +02:00
parent 275b5125c1
commit ab5ead3416

View file

@ -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')],