Files
vendoo/extension/background.js

61 lines
2.7 KiB
JavaScript

const ext = globalThis.browser || globalThis.chrome;
const csrfCache = new Map();
function arrayBufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
const chunkSize = 0x8000;
let binary = '';
for (let offset = 0; offset < bytes.length; offset += chunkSize) binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
return btoa(binary);
}
async function getCsrf(serverUrl) {
if (csrfCache.has(serverUrl)) return csrfCache.get(serverUrl);
const response = await fetch(`${serverUrl}/api/me`, { credentials: 'include', cache: 'no-store' });
if (!response.ok) return '';
const data = await response.json();
const token = data.csrf || '';
csrfCache.set(serverUrl, token);
return token;
}
ext.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
if (msg.type === 'api') {
(async () => {
try {
const { serverUrl, path, method = 'GET', body } = msg;
const opts = { method, headers: {}, credentials: 'include', cache: 'no-store' };
if (body !== undefined) { opts.headers['Content-Type'] = 'application/json'; opts.body = JSON.stringify(body); }
if (!['GET', 'HEAD', 'OPTIONS'].includes(method)) {
const csrf = await getCsrf(serverUrl);
if (csrf) opts.headers['X-CSRF-Token'] = csrf;
}
let response = await fetch(`${serverUrl}${path}`, opts);
if (response.status === 403 && !['GET', 'HEAD', 'OPTIONS'].includes(method)) {
csrfCache.delete(serverUrl);
const csrf = await getCsrf(serverUrl);
if (csrf) opts.headers['X-CSRF-Token'] = csrf;
response = await fetch(`${serverUrl}${path}`, opts);
}
const contentType = response.headers.get('content-type') || '';
const data = contentType.includes('application/json') ? await response.json() : { error: await response.text() };
sendResponse({ ok: response.ok, status: response.status, data });
} catch (error) { sendResponse({ ok: false, status: 0, error: error.message }); }
})();
return true;
}
if (msg.type === 'fetch-image') {
(async () => {
try {
const response = await fetch(msg.url, { cache: 'no-store', credentials: 'include' });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const blob = await response.blob();
if (!blob.type.startsWith('image/')) throw new Error('Antwort ist kein Bild');
const buffer = await blob.arrayBuffer();
sendResponse({ ok: true, dataUrl: `data:${blob.type};base64,${arrayBufferToBase64(buffer)}`, mime: blob.type, size: blob.size });
} catch (error) { sendResponse({ ok: false, error: error.message }); }
})();
return true;
}
});