Files
eship/Extension/background/service-worker.js
2026-01-17 17:07:46 +01:00

420 lines
13 KiB
JavaScript

// 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');