/** * FC backend client. Talks to /api/credentials (FC-3b), /api/sources * (FC-3a), and the new /api/extension/* endpoints (FC-3g). */ class FabledCuratorAPI { constructor() { this.baseUrl = null; this.apiKey = null; } async init() { const cfg = await browser.storage.local.get(['apiUrl', 'apiKey']); this.baseUrl = cfg.apiUrl || null; this.apiKey = cfg.apiKey || null; return this.isConfigured(); } isConfigured() { return !!(this.baseUrl && this.apiKey); } async request(method, endpoint, data = null) { if (!this.isConfigured()) { throw new Error('Not configured. Set FC URL + API key in settings.'); } const url = `${this.baseUrl.replace(/\/+$/, '')}${endpoint}`; const options = { method, headers: { 'Content-Type': 'application/json', 'X-Extension-Key': this.apiKey, }, }; if (data) options.body = JSON.stringify(data); let response; try { response = await fetch(url, options); } catch (e) { throw new Error('Cannot connect to FabledCurator. Check URL.'); } if (!response.ok) { let message; try { const body = await response.json(); message = body.error || `HTTP ${response.status}`; if (body.detail) message += `: ${body.detail}`; } catch { message = `HTTP ${response.status}: ${response.statusText}`; } const err = new Error(message); err.status = response.status; throw err; } if (response.status === 204) return { success: true }; return response.json(); } // FC-3b — credentials. uploadCredentials(platform, credentialType, data) { return this.request('POST', '/credentials', { platform, credential_type: credentialType, data, }); } getCredentials() { return this.request('GET', '/credentials'); } // FC-3a — sources. listSources() { return this.request('GET', '/sources'); } triggerSourceCheck(sourceId) { return this.request('POST', `/sources/${sourceId}/check`); } // FC-3g — extension-specific. quickAddSource(url) { return this.request('POST', '/extension/quick-add-source', { url }); } // Connection test = the cheapest read with auth. testConnection() { return this.request('GET', '/credentials'); } } const api = new FabledCuratorAPI();