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:
Sucukdeluxe
2026-08-12 12:04:47 +02:00
parent c843b4e623
commit a299fc204b
16 changed files with 361 additions and 17 deletions
+15 -1
View File
@@ -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,