u
This commit is contained in:
127
src/api/index.js
127
src/api/index.js
@@ -1686,6 +1686,133 @@ export const company = {
|
||||
body: { companyId },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* POST /api/company/import
|
||||
* Uploads and streams catalog import (.zip) archive via Server-Sent Events.
|
||||
* Tracks client upload progress (bytes/percentage) and processes incoming SSE events in real-time.
|
||||
*
|
||||
* @param {File|Blob|null} file
|
||||
* @param {object} [callbacks]
|
||||
* @param {(progress: { loaded: number, total: number, percent: number }) => void} [callbacks.onUploadProgress]
|
||||
* @param {(message: string) => void} [callbacks.onStatus]
|
||||
* @param {(progress: { phase: string, current: number, total: number, item?: string }) => void} [callbacks.onProgress]
|
||||
* @param {(stats: object) => void} [callbacks.onDone]
|
||||
* @param {(error: string) => void} [callbacks.onError]
|
||||
* @param {object} [options]
|
||||
* @param {AbortSignal} [options.signal]
|
||||
*/
|
||||
importCatalog(file, callbacks = {}, { signal } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/api/company/import');
|
||||
xhr.withCredentials = true;
|
||||
xhr.setRequestHeader('Accept', 'text/event-stream');
|
||||
|
||||
if (signal) {
|
||||
if (signal.aborted) {
|
||||
return reject(new DOMException('Aborted', 'AbortError'));
|
||||
}
|
||||
signal.addEventListener('abort', () => {
|
||||
xhr.abort();
|
||||
reject(new DOMException('Aborted', 'AbortError'));
|
||||
});
|
||||
}
|
||||
|
||||
// Track request body upload progress
|
||||
if (file && xhr.upload) {
|
||||
xhr.upload.onprogress = (event) => {
|
||||
if (event.lengthComputable && event.total > 0) {
|
||||
const percent = Math.min(100, Math.round((event.loaded / event.total) * 100));
|
||||
callbacks.onUploadProgress?.({
|
||||
loaded: event.loaded,
|
||||
total: event.total,
|
||||
percent,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Stream and parse incoming SSE chunks as the server processes the archive
|
||||
let processedIndex = 0;
|
||||
let buffer = '';
|
||||
|
||||
const processChunks = () => {
|
||||
const text = xhr.responseText || '';
|
||||
if (text.length <= processedIndex) return;
|
||||
const newChunk = text.slice(processedIndex);
|
||||
processedIndex = text.length;
|
||||
|
||||
buffer += newChunk;
|
||||
const messages = buffer.split('\n\n');
|
||||
buffer = messages.pop() || '';
|
||||
|
||||
for (const msg of messages) {
|
||||
if (!msg.trim()) continue;
|
||||
|
||||
const eventMatch = msg.match(/^event:\s*(\w+)/m);
|
||||
const dataMatch = msg.match(/^data:\s*(.+)$/m);
|
||||
|
||||
const eventType = eventMatch ? eventMatch[1] : 'message';
|
||||
let eventData = null;
|
||||
if (dataMatch) {
|
||||
try {
|
||||
eventData = JSON.parse(dataMatch[1]);
|
||||
} catch {
|
||||
eventData = dataMatch[1];
|
||||
}
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case 'status':
|
||||
callbacks.onStatus?.(eventData?.message);
|
||||
break;
|
||||
case 'progress':
|
||||
callbacks.onProgress?.(eventData);
|
||||
break;
|
||||
case 'done':
|
||||
callbacks.onDone?.(eventData?.stats ?? eventData);
|
||||
break;
|
||||
case 'error':
|
||||
callbacks.onError?.(eventData?.message || 'Fehler beim Importieren');
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onprogress = processChunks;
|
||||
|
||||
xhr.onload = () => {
|
||||
processChunks();
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
resolve();
|
||||
} else {
|
||||
let errMsg = `Upload fehlgeschlagen (HTTP ${xhr.status})`;
|
||||
try {
|
||||
const parsed = JSON.parse(xhr.responseText);
|
||||
errMsg = parsed.error || parsed.message || errMsg;
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
reject(new Error(errMsg));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
reject(new Error('Netzwerkfehler beim Upload der Datei'));
|
||||
};
|
||||
|
||||
xhr.onabort = () => {
|
||||
reject(new DOMException('Aborted', 'AbortError'));
|
||||
};
|
||||
|
||||
const formData = new FormData();
|
||||
if (file) {
|
||||
formData.append('file', file);
|
||||
}
|
||||
xhr.send(formData);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const posterminal = {
|
||||
|
||||
Reference in New Issue
Block a user