feat(import): use WebSocket push for progress updates with polling fallback
This commit is contained in:
@@ -882,7 +882,7 @@ function showProgress(show, message = '', percent = null) {
|
||||
// Batch progress monitoring
|
||||
// -----------------------------
|
||||
const TERMINAL_BATCH_STATUSES = new Set(['success', 'completed_with_errors', 'failed']);
|
||||
let batchProgress = { timer: null, auditId: null };
|
||||
let batchProgress = { timer: null, auditId: null, wsMgr: null };
|
||||
|
||||
async function fetchCurrentBatch() {
|
||||
try {
|
||||
@@ -899,6 +899,8 @@ function stopBatchProgressPolling() {
|
||||
batchProgress.timer = null;
|
||||
}
|
||||
batchProgress.auditId = null;
|
||||
try { if (batchProgress.wsMgr) { batchProgress.wsMgr.close(); } } catch (_) {}
|
||||
batchProgress.wsMgr = null;
|
||||
}
|
||||
|
||||
async function pollBatchProgressOnce(auditId) {
|
||||
@@ -924,9 +926,48 @@ async function pollBatchProgressOnce(auditId) {
|
||||
function startBatchProgressPolling(auditId) {
|
||||
stopBatchProgressPolling();
|
||||
batchProgress.auditId = auditId;
|
||||
// immediate + interval polling
|
||||
pollBatchProgressOnce(auditId);
|
||||
batchProgress.timer = setInterval(() => pollBatchProgressOnce(auditId), 1500);
|
||||
// Try WebSocket first; fallback to polling
|
||||
try {
|
||||
const mgr = new (window.notifications && window.notifications.NotificationManager ? window.notifications.NotificationManager : null)({
|
||||
getUrl: () => `/api/import/batch-progress/ws/${encodeURIComponent(auditId)}`,
|
||||
onMessage: (msg) => {
|
||||
if (!msg || !msg.type) return;
|
||||
if (msg.type === 'progress') {
|
||||
const p = msg.data || {};
|
||||
const percent = Number(p.percent || 0);
|
||||
const total = Number(p.total_files || 0);
|
||||
const processed = Number(p.processed_files || 0);
|
||||
const status = String(p.status || 'running');
|
||||
const statusNice = status.replaceAll('_', ' ');
|
||||
const msgText = total > 0
|
||||
? `Processing ${processed}/${total} (${percent.toFixed(1)}%) · ${statusNice}`
|
||||
: `Processing… ${statusNice}`;
|
||||
showProgress(true, msgText, percent);
|
||||
if (TERMINAL_BATCH_STATUSES.has(status)) {
|
||||
stopBatchProgressPolling();
|
||||
}
|
||||
}
|
||||
},
|
||||
onStateChange: (state) => {
|
||||
if (state === 'error' || state === 'closed' || state === 'offline') {
|
||||
// fallback to polling
|
||||
if (!batchProgress.timer) {
|
||||
pollBatchProgressOnce(auditId);
|
||||
batchProgress.timer = setInterval(() => pollBatchProgressOnce(auditId), 1500);
|
||||
}
|
||||
}
|
||||
},
|
||||
autoConnect: true,
|
||||
debug: false
|
||||
});
|
||||
batchProgress.wsMgr = mgr;
|
||||
// Safety: also do an immediate HTTP fetch as first snapshot
|
||||
pollBatchProgressOnce(auditId);
|
||||
} catch (_) {
|
||||
// immediate + interval polling
|
||||
pollBatchProgressOnce(auditId);
|
||||
batchProgress.timer = setInterval(() => pollBatchProgressOnce(auditId), 1500);
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureAuditIdWithRetry(maxAttempts = 10, delayMs = 500) {
|
||||
|
||||
Reference in New Issue
Block a user