fix(doodstream): extend 3.3.29 account-poison protection to the API path

The new API upload path POSTs to the same cloudatacdn.com nodes as the web
path, so it can hit the same backend flake — a 2xx response with no filecode, or
a transient "no servers available" from /api/upload/server (now that the stale
fallback node is gone). hosters.uploadFile threw GENERIC errors for both, which
the upload-manager would treat as an account failure → mark-failed →
pre-job-swap-blocked on the next batch: the exact symptom 3.3.29 fixed for the
web path, reintroduced via the unprotected API path.

Tag both API-path analogs of the empty form as err.hosterTransient=true:
- codeless 2xx ("lieferte keine file_code-Antwort") — bytes accepted, no link.
- transient "no upload server" (shouldRetryServerLookup true: no-servers/busy/
  try-again) — but NOT genuine auth failures (invalid key/unauthorized), which
  stay classified as account errors.

The upload-manager checks _isHosterTransientError (flag-based) before the
account-error classifier, so both now fail the file without blacklisting the
account. Consumption side already covered by the 3.3.29 regression test. 173/173.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Administrator 2026-05-28 21:48:13 +02:00
parent a8d81cbf0d
commit 76c56cf13b

View File

@ -378,7 +378,13 @@ async function getUploadServer(hosterName, hosterConfig, apiKey, signal) {
} }
if (lastMessage) { if (lastMessage) {
throw new Error(`Kein Upload-Server erhalten: ${lastMessage}`); const e = new Error(`Kein Upload-Server erhalten: ${lastMessage}`);
// "no servers available" / busy / try-again is a transient hoster-side
// condition, not an account fault — tag it so the account isn't blacklisted.
// Genuine auth failures (invalid key / unauthorized / forbidden) make
// shouldRetryServerLookup return false and stay classified as account errors.
if (shouldRetryServerLookup(lastMessage)) e.hosterTransient = true;
throw e;
} }
throw new Error('Kein Upload-Server erhalten. API-Key pruefen.'); throw new Error('Kein Upload-Server erhalten. API-Key pruefen.');
} }
@ -545,9 +551,18 @@ async function uploadFile(hosterName, filePath, apiKey, onProgress, signal, thro
const isOkishNoPayload = /^(ok|success|done|accepted)$/i.test(msg); const isOkishNoPayload = /^(ok|success|done|accepted)$/i.test(msg);
if (isOkishNoPayload || !msg) { if (isOkishNoPayload || !msg) {
const snippet = JSON.stringify(payload).slice(0, 400); const snippet = JSON.stringify(payload).slice(0, 400);
throw new Error( // 2xx with no filecode: the hoster accepted the upload (bytes sent, status
// OK) but returned no usable link. For doodstream this is the API-path
// analog of the web empty-form — the backend file-registration timing out
// under large-file load. It's a hoster-side flake, NOT an account problem,
// so tag it hosterTransient: the upload-manager then fails this file WITHOUT
// blacklisting the account (same protection the web path got in 3.3.29) and
// the account stays usable for the next retry/batch.
const err = new Error(
`Upload zu ${hosterName} lieferte keine file_code-Antwort (Payload: ${snippet})` `Upload zu ${hosterName} lieferte keine file_code-Antwort (Payload: ${snippet})`
); );
err.hosterTransient = true;
throw err;
} }
throw new Error(msg); throw new Error(msg);
} }