release: v2.1.16
Add queue search and filters, safe upload diagnostics, session report exports, account check visibility, and explicit interrupted-upload recovery.\n\nSanitize diagnostic response snippets in persisted results and rotation logs, protect CSV exports against formula injection, and add regression coverage for diagnostics and reports.
This commit is contained in:
+15
-1
@@ -11,6 +11,7 @@ const ClouddropUploader = require('./clouddrop-upload');
|
||||
const Semaphore = require('./semaphore');
|
||||
const Throttle = require('./throttle');
|
||||
const { probeFileHead } = require('./file-probe');
|
||||
const { normalizeFailureDetails } = require('./upload-diagnostics');
|
||||
|
||||
const DEFAULT_SETTINGS = {
|
||||
retries: 3,
|
||||
@@ -433,6 +434,7 @@ class UploadManager extends EventEmitter {
|
||||
const uploadId = crypto.randomBytes(8).toString('hex');
|
||||
const jobId = task.jobId || uploadId;
|
||||
const fileName = path.basename(task.file);
|
||||
const jobStartedAt = Date.now();
|
||||
let fileSize = 0;
|
||||
let fileNotFound = false;
|
||||
const cachedResult = results && results.get(task.file);
|
||||
@@ -452,6 +454,8 @@ class UploadManager extends EventEmitter {
|
||||
let finalResultRecorded = false;
|
||||
let finalStatus = 'error';
|
||||
let lastError = null;
|
||||
let lastFailureDetails = null;
|
||||
let finalAttempt = 0;
|
||||
|
||||
const recordFinalResult = (status, payload = {}) => {
|
||||
if (finalResultRecorded) return;
|
||||
@@ -462,6 +466,11 @@ class UploadManager extends EventEmitter {
|
||||
hoster: task.hoster,
|
||||
status,
|
||||
error: payload.error || null,
|
||||
accountId: task.accountId || null,
|
||||
attempt: payload.attempt || finalAttempt,
|
||||
maxAttempts,
|
||||
durationSec: Number.isFinite(payload.elapsed) ? payload.elapsed : Math.round((Date.now() - jobStartedAt) / 1000),
|
||||
failureDetails: payload.failureDetails || lastFailureDetails,
|
||||
download_url: payload.result ? payload.result.download_url || null : null,
|
||||
embed_url: payload.result ? payload.result.embed_url || null : null,
|
||||
file_code: payload.result ? payload.result.file_code || null : null
|
||||
@@ -482,6 +491,7 @@ class UploadManager extends EventEmitter {
|
||||
elapsed: payload.elapsed || 0,
|
||||
remaining: 0,
|
||||
error: payload.error || null,
|
||||
failureDetails: payload.failureDetails || lastFailureDetails,
|
||||
result: payload.result || null,
|
||||
attempt: payload.attempt || maxAttempts,
|
||||
maxAttempts
|
||||
@@ -582,6 +592,7 @@ class UploadManager extends EventEmitter {
|
||||
|
||||
const attemptsAllowed = memoSuspect ? 0 : maxAttempts;
|
||||
for (let attempt = 1; attempt <= attemptsAllowed; attempt++) {
|
||||
finalAttempt = attempt;
|
||||
if (signal.aborted || this.stopAfterActive) break;
|
||||
|
||||
if (attempt > 1) {
|
||||
@@ -721,6 +732,7 @@ class UploadManager extends EventEmitter {
|
||||
const isSpeedRestart = speedAbort && speedAbort.signal.aborted && !signal.aborted;
|
||||
if (!signal.aborted && !isSpeedRestart) {
|
||||
const diag = (err && typeof err === 'object' && err.diagnostic) || {};
|
||||
lastFailureDetails = normalizeFailureDetails(diag);
|
||||
this._rotLog('upload-failure', {
|
||||
jobId, hoster: task.hoster, accountId: task.accountId, fileName,
|
||||
attempt,
|
||||
@@ -733,7 +745,7 @@ class UploadManager extends EventEmitter {
|
||||
detectedKind: (typeof fileProbe !== 'undefined' && fileProbe && fileProbe.kind) ? fileProbe.kind : null,
|
||||
isVideoLike: !!(typeof fileProbe !== 'undefined' && fileProbe && fileProbe.isVideoLike),
|
||||
headHex: (typeof fileProbe !== 'undefined' && fileProbe && fileProbe.headHex) ? fileProbe.headHex.slice(0, 32) : null,
|
||||
payloadSnippet: diag.payloadSnippet || null
|
||||
payloadSnippet: lastFailureDetails ? lastFailureDetails.responseSnippet || null : null
|
||||
});
|
||||
}
|
||||
if (signal.aborted) {
|
||||
@@ -955,6 +967,7 @@ class UploadManager extends EventEmitter {
|
||||
// loop iterates: marks this account failed too, asks main for the next
|
||||
// fallback, and so on.
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
finalAttempt = attempt;
|
||||
if (signal.aborted || this.stopAfterActive) break;
|
||||
if (attempt > 1) {
|
||||
this._emitProgress(uploadId, fileName, task.hoster, { accountId: task.accountId,
|
||||
@@ -1011,6 +1024,7 @@ class UploadManager extends EventEmitter {
|
||||
} catch (err) {
|
||||
this.activeJobs.delete(uploadId);
|
||||
lastError = err;
|
||||
lastFailureDetails = normalizeFailureDetails(err && err.diagnostic);
|
||||
if (!signal.aborted) {
|
||||
this._rotLog('upload-failure', {
|
||||
jobId, hoster: task.hoster, accountId: task.accountId, fileName,
|
||||
|
||||
Reference in New Issue
Block a user