// Service worker: performs the cross-origin POST to the Bewerbungs-Tracker. // Routing the request through the background worker (instead of the content // script) keeps it independent of the Indeed page's Content-Security-Policy. const DEFAULT_TRACKER_URL = 'http://localhost:3000'; function getTrackerUrl() { return new Promise((resolve) => { chrome.storage.sync.get({ trackerUrl: DEFAULT_TRACKER_URL }, (items) => { let url = (items.trackerUrl || DEFAULT_TRACKER_URL).trim(); url = url.replace(/\/+$/, ''); // strip trailing slashes resolve(url); }); }); } chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message && message.type === 'IMPORT_JOB') { (async () => { try { const base = await getTrackerUrl(); const res = await fetch(base + '/api/indeed-import', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(message.payload), }); let data = {}; try { data = await res.json(); } catch (_) { /* non-JSON response */ } if (!res.ok) { // Duplicate guard: surface the existing matches so the user can decide. if (res.status === 409 && data && data.duplicate) { sendResponse({ ok: false, duplicate: true, matches: data.matches || [], error: (data && data.error) || 'Für diese Stelle existiert bereits eine Bewerbung.', }); return; } sendResponse({ ok: false, error: (data && data.error) || `Server antwortete mit ${res.status}`, }); return; } sendResponse({ ok: true, id: data.id, message: data.message, openUrl: data.url ? base + data.url : null, }); } catch (err) { sendResponse({ ok: false, error: 'Konnte den Bewerbungs-Tracker nicht erreichen. Läuft er, und ist die ' + 'Adresse in den Erweiterungs-Einstellungen korrekt? (' + String(err.message) + ')', }); } })(); return true; // keep the message channel open for the async response } });