/** * Forage Chrome Extension — Background Service Worker * * Receives capture/signal messages from content.js, reads config from * chrome.storage.sync, and POSTs to the Forage server. */ const DEFAULT_CONFIG = { server: 'http://localhost:4242', user_id: 1, token: '', enabled: true, }; async function getConfig() { return new Promise(resolve => { chrome.storage.sync.get(DEFAULT_CONFIG, resolve); }); } function authHeaders(token) { const h = { 'Content-Type': 'application/json' }; if (token) h['Authorization'] = `Bearer ${token}`; return h; } chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => { handleMessage(msg) .then(sendResponse) .catch(err => { console.error('[Forage bg] error:', err.message); sendResponse({ ok: false, error: err.message }); }); return true; // keep channel open for async response }); async function handleMessage(msg) { const cfg = await getConfig(); if (!cfg.enabled) return { ok: false, reason: 'disabled' }; if (msg.type === 'capture') { let res; try { res = await fetch(`${cfg.server}/capture`, { method: 'POST', headers: authHeaders(cfg.token), body: JSON.stringify({ url: msg.url, canonical_url: msg.canonical_url || '', title: msg.title, source: msg.source, description: msg.description, reading_time_min: msg.reading_time_min, user_id: cfg.user_id, }), }); } catch (e) { console.warn('[Forage bg] /capture network error:', e.message); return { ok: false, error: e.message }; } const data = await res.json().catch(() => ({})); // Store last capture for popup display await chrome.storage.local.set({ last_capture: { title: msg.title, time: Date.now(), item_id: data.item_id }, }); return { ok: res.ok, item_id: data.item_id }; } if (msg.type === 'dwell') { if (!msg.item_id) return { ok: false, reason: 'no item_id' }; try { await fetch(`${cfg.server}/signal`, { method: 'POST', headers: authHeaders(cfg.token), body: JSON.stringify({ user_id: cfg.user_id, item_id: msg.item_id, signal_type: 'dwell', duration_ms: msg.duration_ms, }), }); } catch (e) { console.warn('[Forage bg] /signal network error:', e.message); return { ok: false, error: e.message }; } return { ok: true }; } return { ok: false, reason: 'unknown message type' }; }