Changeset 0.9.1 (#51)
This commit is contained in:
@@ -54,6 +54,25 @@ const API = {
|
||||
async health() {
|
||||
const resp = await fetch(BASE + '/api/v1/health', { signal: AbortSignal.timeout(8000) });
|
||||
if (!resp.ok) throw new Error(`Health: ${resp.status}`);
|
||||
return this._parseJSON(resp, '/api/v1/health');
|
||||
},
|
||||
|
||||
// Detect proxy interception: HTTP 200 but HTML instead of JSON
|
||||
async _parseJSON(resp, context) {
|
||||
const ct = (resp.headers.get('content-type') || '').toLowerCase();
|
||||
if (ct.includes('text/html')) {
|
||||
// Proxy returned an HTML page (block page, auth wall, etc.)
|
||||
let title = 'unknown';
|
||||
try {
|
||||
const html = await resp.clone().text();
|
||||
const m = html.match(/<title[^>]*>([^<]+)<\/title>/i);
|
||||
if (m) title = m[1].trim();
|
||||
} catch (e) { /* best effort */ }
|
||||
const err = new Error(`Proxy interception detected on ${context}: "${title}"`);
|
||||
err.proxyBlocked = true;
|
||||
err.proxyTitle = title;
|
||||
throw err;
|
||||
}
|
||||
return resp.json();
|
||||
},
|
||||
|
||||
@@ -157,9 +176,21 @@ const API = {
|
||||
}
|
||||
|
||||
if (!resp.ok) {
|
||||
const ct = (resp.headers.get('content-type') || '').toLowerCase();
|
||||
if (ct.includes('text/html')) {
|
||||
const err = new Error('Proxy blocked the regeneration request');
|
||||
err.proxyBlocked = true;
|
||||
throw err;
|
||||
}
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
throw new Error(err.error || `HTTP ${resp.status}`);
|
||||
}
|
||||
const ct = (resp.headers.get('content-type') || '').toLowerCase();
|
||||
if (ct.includes('text/html')) {
|
||||
const err = new Error('Proxy intercepted the regeneration stream');
|
||||
err.proxyBlocked = true;
|
||||
throw err;
|
||||
}
|
||||
return resp;
|
||||
},
|
||||
|
||||
@@ -205,9 +236,22 @@ const API = {
|
||||
}
|
||||
|
||||
if (!resp.ok) {
|
||||
const ct = (resp.headers.get('content-type') || '').toLowerCase();
|
||||
if (ct.includes('text/html')) {
|
||||
const err = new Error('Proxy blocked the completion request');
|
||||
err.proxyBlocked = true;
|
||||
throw err;
|
||||
}
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
throw new Error(err.error || `HTTP ${resp.status}`);
|
||||
}
|
||||
// Also check 200 OK but HTML (proxy returning block page with 200)
|
||||
const ct = (resp.headers.get('content-type') || '').toLowerCase();
|
||||
if (ct.includes('text/html')) {
|
||||
const err = new Error('Proxy intercepted the completion stream');
|
||||
err.proxyBlocked = true;
|
||||
throw err;
|
||||
}
|
||||
return resp;
|
||||
},
|
||||
|
||||
@@ -381,6 +425,13 @@ const API = {
|
||||
|
||||
// ── HTTP Internals ───────────────────────
|
||||
|
||||
_esc(s) {
|
||||
if (!s) return '';
|
||||
const d = document.createElement('div');
|
||||
d.textContent = s;
|
||||
return d.innerHTML;
|
||||
},
|
||||
|
||||
_authHeaders() {
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -413,11 +464,26 @@ const API = {
|
||||
|
||||
const resp = await fetch(BASE + path, opts);
|
||||
if (!resp.ok) {
|
||||
// Check if the error response is also a proxy page
|
||||
const ct = (resp.headers.get('content-type') || '').toLowerCase();
|
||||
if (ct.includes('text/html')) {
|
||||
let title = 'unknown';
|
||||
try {
|
||||
const html = await resp.text();
|
||||
const m = html.match(/<title[^>]*>([^<]+)<\/title>/i);
|
||||
if (m) title = m[1].trim();
|
||||
} catch (e) { /* */ }
|
||||
const err = new Error(`Proxy blocked ${method} ${path}: "${title}"`);
|
||||
err.status = resp.status;
|
||||
err.proxyBlocked = true;
|
||||
err.proxyTitle = title;
|
||||
throw err;
|
||||
}
|
||||
const data = await resp.json().catch(() => ({}));
|
||||
const err = new Error(data.error || `HTTP ${resp.status}`);
|
||||
err.status = resp.status;
|
||||
throw err;
|
||||
}
|
||||
return resp.json();
|
||||
return this._parseJSON(resp, path);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user