Generate immutable post-cleanup summaries only after queue, history, and recovery finalization. Add bilingual accessible report UI, host and cleanup metrics, report-bound JSON and sanitized error CSV exports, renderer reload recovery, duplicate-filename-safe accounting, and public-source verification coverage.
102 lines
4.0 KiB
JavaScript
102 lines
4.0 KiB
JavaScript
(function exposeUploadRecovery(root, factory) {
|
|
const api = factory();
|
|
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
if (root) root.UploadRecovery = api;
|
|
})(typeof window !== 'undefined' ? window : globalThis, () => {
|
|
const terminalStatuses = new Set(['done', 'error', 'skipped', 'aborted']);
|
|
|
|
function buildTerminalJobSnapshots(summary) {
|
|
const snapshots = new Map();
|
|
for (const file of Array.isArray(summary?.files) ? summary.files : []) {
|
|
for (const result of Array.isArray(file?.results) ? file.results : []) {
|
|
const jobId = typeof result?.jobId === 'string' ? result.jobId : '';
|
|
const status = typeof result?.status === 'string' ? result.status : '';
|
|
if (!jobId || !terminalStatuses.has(status)) continue;
|
|
const uploadResult = result.download_url || result.embed_url || result.file_code
|
|
? {
|
|
download_url: result.download_url || null,
|
|
embed_url: result.embed_url || null,
|
|
file_code: result.file_code || null
|
|
}
|
|
: null;
|
|
snapshots.set(jobId, {
|
|
jobId,
|
|
status,
|
|
error: result.error || null,
|
|
failureDetails: result.failureDetails || null,
|
|
...(result.remoteCommitUncertain === true ? { remoteCommitUncertain: true } : {}),
|
|
result: uploadResult
|
|
});
|
|
}
|
|
}
|
|
return Array.from(snapshots.values());
|
|
}
|
|
|
|
function buildFailedUploadSummary(tasks, message, now = Date.now()) {
|
|
const files = new Map();
|
|
for (const [index, task] of (Array.isArray(tasks) ? tasks : []).entries()) {
|
|
if (!task || typeof task !== 'object') continue;
|
|
const filePath = typeof task.file === 'string' ? task.file : '';
|
|
const fileName = filePath.split(/[\\/]/).pop() || `upload-${index + 1}`;
|
|
const key = filePath || `${fileName}\0${index}`;
|
|
if (!files.has(key)) files.set(key, {
|
|
name: fileName,
|
|
...(typeof task.fileKey === 'string' && task.fileKey ? { fileKey: task.fileKey } : {}),
|
|
size: 0,
|
|
results: []
|
|
});
|
|
files.get(key).results.push({
|
|
jobId: typeof task.jobId === 'string' ? task.jobId : '',
|
|
hoster: typeof task.hoster === 'string' ? task.hoster : '',
|
|
status: 'error',
|
|
error: message,
|
|
failureDetails: null,
|
|
download_url: null,
|
|
embed_url: null,
|
|
file_code: null
|
|
});
|
|
}
|
|
const grouped = Array.from(files.values());
|
|
const failed = grouped.reduce((count, file) => count + file.results.length, 0);
|
|
return {
|
|
id: `start-error-${now}`,
|
|
timestamp: new Date(now).toISOString(),
|
|
total: failed,
|
|
succeeded: 0,
|
|
failed,
|
|
skipped: 0,
|
|
files: grouped,
|
|
error: message
|
|
};
|
|
}
|
|
|
|
function getRecoveryOutcome(job, recovery) {
|
|
const status = typeof job?.status === 'string' ? job.status : 'preview';
|
|
const jobId = typeof job?.id === 'string' ? job.id : '';
|
|
const terminal = Array.isArray(recovery?.terminalJobs)
|
|
? recovery.terminalJobs.find(entry => entry?.jobId === jobId && terminalStatuses.has(entry.status))
|
|
: null;
|
|
if (terminal) {
|
|
return {
|
|
status: terminal.status,
|
|
error: terminal.error || null,
|
|
failureDetails: terminal.failureDetails || null,
|
|
...(terminal.remoteCommitUncertain === true ? { remoteCommitUncertain: true } : {}),
|
|
result: terminal.result || null,
|
|
...(recovery?.historyPending === true ? { historyPending: true } : {}),
|
|
interrupted: false
|
|
};
|
|
}
|
|
const interruptedIds = new Set(Array.isArray(recovery?.jobIds) ? recovery.jobIds.filter(Boolean) : []);
|
|
return { status, interrupted: interruptedIds.has(jobId) && !terminalStatuses.has(status) };
|
|
}
|
|
|
|
function resolveRemoteCommitUncertainty(previous, progress) {
|
|
if (progress?.remoteCommitUncertain === true) return true;
|
|
if (progress?.status === 'done') return false;
|
|
return previous === true;
|
|
}
|
|
|
|
return { buildFailedUploadSummary, buildTerminalJobSnapshots, getRecoveryOutcome, resolveRemoteCommitUncertainty };
|
|
});
|