420 lines
13 KiB
JavaScript
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');
|