// EShip Extension - Background Service Worker // Handles Appwrite authentication and message passing console.log('[SW] Service Worker starting...'); // Define APPWRITE_CONFIG directly (fallback if config.js fails to load) var APPWRITE_CONFIG = { endpoint: 'https://appwrite.webklar.com/v1', projectId: '696b82bb0036d2e547ad', apiKey: 'standard_d48c6eebe825b55e685d8e66ea056161105470702da77b730aca08c106ffbadfa2375ff675dbe9e01d7bb72b4a9fa001ff7c365b73759bc5fb3da432c3cd9cee1151e67517e9838d1f96f942d9891ce66ddc6f11c0fdd67a24f7c84e0fa9999a74dacf2c6aa3533998c177f190fc87ffb5a30b27474be21aece4c70d71d205ba' }; // Global state let client, account; let scriptsLoaded = false; let initError = null; let DEFAULT_TOOLS = [ { id: 'highlight_prices', name: 'Preise hervorheben', enabled: false, settings: { selector: '.price', borderColor: '#ff0000', borderWidth: '2px' } } ]; // Register message listener FIRST - before any initialization chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { console.log('[SW] Message received:', request.action); // PING handler - synchronous response if (request.action === 'PING') { console.log('[SW] PING received, responding'); sendResponse({ success: true, message: 'Service Worker is alive' }); return false; // Synchronous response } // Handle other messages asynchronously (async () => { try { // Ensure scripts are loaded (synchronous operation) if (!scriptsLoaded) { loadScripts(); } // Ensure Appwrite is initialized if (!client || !account) { initAppwrite(); } // Handle the message const response = await handleMessage(request, sender); console.log('[SW] Sending response:', response); sendResponse(response); } catch (error) { console.error('[SW] Error handling message:', error); sendResponse({ success: false, error: error.message || 'Unbekannter Fehler' }); } })(); return true; // Keep channel open for async }); // Load scripts - try relative path first, then fallback to inline function loadScripts() { if (scriptsLoaded) return; try { console.log('[SW] Loading scripts...'); // Try loading config first (smaller file) - will override default if successful try { importScripts('../config.js'); console.log('[SW] config.js loaded (overriding default)'); } catch (e) { console.error('[SW] Failed to load config.js:', e); console.log('[SW] Using default inline config'); } // Try loading Appwrite SDK try { importScripts('../lib/appwrite.min.js'); console.log('[SW] appwrite.min.js loaded via importScripts'); } catch (e) { console.error('[SW] Failed to load appwrite.min.js via importScripts:', e); // Load Appwrite SDK inline loadAppwriteSDKInline(); console.log('[SW] appwrite.min.js loaded inline'); } scriptsLoaded = true; console.log('[SW] Scripts loaded successfully'); } catch (error) { console.error('[SW] Failed to load scripts:', error); throw error; } } // Load Appwrite SDK inline if importScripts fails function loadAppwriteSDKInline() { if (typeof Appwrite !== 'undefined') return; (function(global) { 'use strict'; const Appwrite = {}; class Client { constructor() { this.config = { endpoint: 'https://appwrite.webklar.com/v1', project: '' }; this.headers = { 'content-type': 'application/json', 'x-sdk-name': 'Chrome Extension', 'x-sdk-platform': 'client', 'x-sdk-language': 'web', 'x-sdk-version': '21.0.0', }; } setEndpoint(endpoint) { this.config.endpoint = endpoint; return this; } setProject(project) { this.config.project = project; this.headers['x-appwrite-project'] = project; return this; } setKey(key) { if (key) this.headers['x-appwrite-key'] = key; else delete this.headers['x-appwrite-key']; return this; } async call(method, path, headers = {}, params = {}) { const url = new URL(this.config.endpoint + path); const options = { method: method.toUpperCase(), headers: { ...this.headers, ...headers }, credentials: 'include' }; if (method === 'GET') { for (const [key, value] of Object.entries(params)) { if (value !== undefined) url.searchParams.append(key, value); } } else { options.body = JSON.stringify(params); } const response = await fetch(url.toString(), options); const contentType = response.headers.get('content-type') || ''; let data = contentType.includes('application/json') ? await response.json() : await response.text(); if (response.status >= 400) { throw new AppwriteException(data.message || 'Unknown error', response.status, data.type || '', data); } return data; } } class Account { constructor(client) { this.client = client; } async get() { return await this.client.call('GET', '/account'); } async createEmailPasswordSession(email, password) { return await this.client.call('POST', '/account/sessions/email', {}, { email, password }); } async deleteSession(sessionId) { return await this.client.call('DELETE', `/account/sessions/${sessionId}`); } async getSession(sessionId) { return await this.client.call('GET', `/account/sessions/${sessionId}`); } } class AppwriteException extends Error { constructor(message, code = 0, type = '', response = null) { super(message); this.name = 'AppwriteException'; this.code = code; this.type = type; this.response = response; } } Appwrite.Client = Client; Appwrite.Account = Account; Appwrite.AppwriteException = AppwriteException; global.Appwrite = Appwrite; })(typeof self !== 'undefined' ? self : this); } // Initialize Appwrite // Note: For user authentication, we don't use API key (only for server operations) function initAppwrite() { try { if (!scriptsLoaded) { throw new Error('Scripts nicht geladen'); } if (typeof Appwrite === 'undefined') { throw new Error('Appwrite SDK nicht geladen'); } // APPWRITE_CONFIG should be available (defined at top of file or loaded from config.js) if (typeof APPWRITE_CONFIG === 'undefined') { throw new Error('APPWRITE_CONFIG nicht definiert'); } const config = APPWRITE_CONFIG; console.log('[SW] Using config:', { endpoint: config.endpoint, projectId: config.projectId }); // Initialize client WITHOUT API key for user authentication // API key is only for server-side operations, not user login client = new Appwrite.Client(); client .setEndpoint(config.endpoint) .setProject(config.projectId); // Do NOT set API key here - it conflicts with user sessions console.log('[SW] Client configured with project:', config.projectId); console.log('[SW] Note: API Key not set (only for server operations, not user auth)'); account = new Appwrite.Account(client); initError = null; console.log('[SW] Appwrite Client initialized (session-based, no API key)'); return true; } catch (error) { console.error('[SW] Failed to initialize Appwrite:', error); initError = error.message; return false; } } // Handle messages async function handleMessage(request, sender) { switch (request.action) { case 'CHECK_AUTH': return await checkAuth(); case 'LOGIN': return await login(request.email, request.password); case 'LOGOUT': return await logout(); case 'GET_USER': return await getUser(); case 'GET_SETTINGS': return await getSettings(); case 'SAVE_SETTINGS': return await saveSettings(request.settings); case 'GET_SESSION': return await getSession(); default: return { success: false, error: 'Unknown action: ' + request.action }; } } // Check if user is authenticated async function checkAuth() { try { // Check if we have a stored session const stored = await chrome.storage.local.get(['session']); if (!stored.session || !stored.session.sessionToken) { return { success: true, authenticated: false, user: null }; } // Verify session with API server const API_SERVER_URL = 'http://localhost:3001'; try { const response = await fetch(`${API_SERVER_URL}/api/extension/auth`, { method: 'GET', headers: { 'x-session-token': stored.session.sessionToken } }); const data = await response.json(); if (data.success && data.authenticated) { return { success: true, authenticated: true, user: data.user }; } } catch (e) { console.log('[SW] API server check failed:', e.message); } return { success: true, authenticated: false, user: null }; } catch (error) { console.log('[SW] Auth check failed:', error.message); return { success: true, authenticated: false, user: null }; } } // Login with email and password // Uses API server proxy to avoid platform registration issues async function login(email, password) { try { console.log('[SW] Attempting login for:', email); // Use API server instead of direct Appwrite call // This avoids the need to register Extension ID as platform const API_SERVER_URL = 'http://localhost:3001'; const response = await fetch(`${API_SERVER_URL}/api/extension/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); const data = await response.json(); if (!data.success) { throw new Error(data.error || 'Login fehlgeschlagen'); } // Store session info including token await chrome.storage.local.set({ session: { userId: data.user.$id, sessionId: data.session.$id, sessionToken: data.session.token || data.session.$id, expire: data.session.expire } }); console.log('[SW] Login successful for:', data.user.email); // Initialize Appwrite client with session for future requests if (!client || !account) { initAppwrite(); } return { success: true, user: data.user, session: data.session }; } catch (error) { console.error('[SW] Login error:', error); return { success: false, error: error.message || 'Login fehlgeschlagen' }; } } // Logout async function logout() { try { // Get session token const stored = await chrome.storage.local.get(['session']); const sessionToken = stored.session?.sessionToken; if (sessionToken) { // Logout via API server const API_SERVER_URL = 'http://localhost:3001'; try { await fetch(`${API_SERVER_URL}/api/extension/logout`, { method: 'POST', headers: { 'x-session-token': sessionToken } }); } catch (e) { // Ignore errors } } await chrome.storage.local.remove(['session']); return { success: true }; } catch (error) { await chrome.storage.local.remove(['session']); return { success: true }; } } // Get current user async function getUser() { try { const user = await account.get(); return { success: true, user }; } catch (error) { return { success: false, error: error.message }; } } // Get session async function getSession() { try { const session = await account.getSession('current'); return { success: true, session }; } catch (error) { return { success: false, error: error.message }; } } // Get tool settings async function getSettings() { try { const result = await chrome.storage.sync.get(['tools']); const tools = result.tools || DEFAULT_TOOLS; return { success: true, tools }; } catch (error) { return { success: true, tools: DEFAULT_TOOLS }; } } // Save tool settings async function saveSettings(tools) { try { await chrome.storage.sync.set({ tools }); // Notify content scripts const tabs = await chrome.tabs.query({ url: ['http://localhost:5173/*', 'http://localhost:3000/*'] }); for (const tab of tabs) { try { await chrome.tabs.sendMessage(tab.id, { action: 'SETTINGS_UPDATED', tools }); } catch (e) { // Tab might not have content script } } return { success: true }; } catch (error) { return { success: false, error: error.message }; } } // Initialize on install chrome.runtime.onInstalled.addListener(async (details) => { if (details.reason === 'install') { await chrome.storage.sync.set({ tools: DEFAULT_TOOLS }); console.log('[SW] Extension installed'); } }); console.log('[SW] Service Worker ready');