diff --git a/lib/doodstream-upload.js b/lib/doodstream-upload.js index b39b939..47d786a 100644 --- a/lib/doodstream-upload.js +++ b/lib/doodstream-upload.js @@ -198,6 +198,8 @@ class DoodstreamUploader { // Use the standard upload server endpoint const res = await this._fetch(BASE_URL + '/?op=upload_server'); const text = await res.text(); + const ctype = (res.headers && res.headers.get) ? (res.headers.get('content-type') || '') : ''; + _debugLog(`upload_server: status=${res.status} ctype=${ctype} body(800)=${(text || '').slice(0, 800)}`); let json; try { json = JSON.parse(text); } catch { json = null; } @@ -211,8 +213,18 @@ class DoodstreamUploader { const srvMatch = html.match(/srv_url['":\s]+['"]?(https?:\/\/[^'">\s]+)['"]?/i); if (srvMatch) return srvMatch[1]; - // Last resort fallback - return 'https://tr1128ve.cloudatacdn.com/upload/01'; + // No upload server could be extracted. We MUST NOT silently fall back to a + // hardcoded node: that node is stale and accepts the bytes but returns an + // empty form (no filecode) — so the user wastes ~90s uploading 95 MB into a + // dead end and gets a cryptic "kein Filecode" 90s later. Fail fast and put + // the raw responses in the error so the real format change is diagnosable. + const urlHints = (html.match(/https?:\/\/[^'">\s]+/g) || []).slice(0, 4).join(' , '); + _debugLog(`upload_server: NO SERVER. upload-page html(2000)=${(html || '').slice(0, 2000)}`); + throw new Error( + `Doodstream: konnte Upload-Server nicht ermitteln (Endpoint geaendert?). ` + + `op=upload_server status=${res.status} ctype=${ctype} body=${(text || '').slice(0, 300)} ` + + `| upload-page URL-Treffer: ${urlHints || 'keine'}` + ); } /** diff --git a/tests/doodstream-upload.test.js b/tests/doodstream-upload.test.js index ea541e0..ca1d895 100644 --- a/tests/doodstream-upload.test.js +++ b/tests/doodstream-upload.test.js @@ -63,3 +63,39 @@ test('happy path: link in result page wins', async () => { const res = await up._parseUploadResponse(cdnForm({ fn: 'jjsuhr931ds9', st: 'OK' })); assert.equal(res.file_code, 'jjsuhr931ds9'); }); + +// --- _getUploadServer: discovery must never fall back to a hardcoded node --- +function fakeRes(body, { status = 200, ctype = 'text/html' } = {}) { + return { status, headers: { get: (h) => (h.toLowerCase() === 'content-type' ? ctype : null) }, text: async () => body }; +} + +test('getUploadServer: returns JSON result when present', async () => { + const up = new DoodstreamUploader(); + up._fetch = async (url) => { + assert.match(url, /op=upload_server/); + return fakeRes(JSON.stringify({ result: 'https://node42.cloudatacdn.com/upload/01' }), { ctype: 'application/json' }); + }; + assert.equal(await up._getUploadServer(), 'https://node42.cloudatacdn.com/upload/01'); +}); + +test('getUploadServer: falls back to srv_url in upload-page HTML', async () => { + const up = new DoodstreamUploader(); + up._fetch = async (url) => { + if (/op=upload_server/.test(url)) return fakeRes('not json'); + return fakeRes(''); + }; + assert.equal(await up._getUploadServer(), 'https://node7.cloudatacdn.com/upload/01'); +}); + +test('getUploadServer: throws (no silent dead fallback) when discovery fails', async () => { + const up = new DoodstreamUploader(); + up._fetch = async () => fakeRes('login required', { status: 200 }); + await assert.rejects( + () => up._getUploadServer(), + (err) => { + assert.match(err.message, /konnte Upload-Server nicht ermitteln/); + assert.doesNotMatch(err.message, /tr1128ve\.cloudatacdn\.com/); // never the hardcoded node + return true; + } + ); +});