192 lines
5.3 KiB
JavaScript
192 lines
5.3 KiB
JavaScript
const STORAGE_KEY = "auth_jwt";
|
|
const BACKEND_URL = "http://localhost:5173"; // TODO: Backend URL konfigurieren
|
|
|
|
const PARSE_TIMEOUT_MS = 15000; // 15 seconds
|
|
const activeParseRequests = new Map(); // Map<tabId, { timeout, originalSender, resolve }>
|
|
|
|
// Messages from content script (der von der Web-App kommt)
|
|
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
|
// Auth messages vom Content Script
|
|
if (msg?.type === "AUTH_JWT" && msg.jwt) {
|
|
chrome.storage.local.set({ [STORAGE_KEY]: msg.jwt }).then(() => {
|
|
sendResponse({ ok: true });
|
|
});
|
|
return true; // async
|
|
}
|
|
|
|
if (msg?.type === "AUTH_CLEARED") {
|
|
chrome.storage.local.remove(STORAGE_KEY).then(() => {
|
|
sendResponse({ ok: true });
|
|
});
|
|
return true;
|
|
}
|
|
|
|
// API calls vom Popup
|
|
if (msg?.type === "GET_JWT") {
|
|
getJwt().then(jwt => {
|
|
sendResponse({ jwt });
|
|
});
|
|
return true;
|
|
}
|
|
|
|
if (msg?.type === "CALL_API" && msg.path) {
|
|
callProtectedApi(msg.path, msg.payload).then(data => {
|
|
sendResponse({ ok: true, data });
|
|
}).catch(err => {
|
|
sendResponse({ ok: false, error: err.message });
|
|
});
|
|
return true;
|
|
}
|
|
|
|
// eBay Parsing Request (from Web App via content script or directly)
|
|
if (msg?.action === "PARSE_URL" && msg.url) {
|
|
handleParseRequest(msg.url, sendResponse);
|
|
return true; // async
|
|
}
|
|
|
|
// eBay Parsing Response (from eBay content script)
|
|
if (msg?.action === "PARSE_COMPLETE") {
|
|
handleParseComplete(sender.tab?.id, msg.data);
|
|
return true;
|
|
}
|
|
});
|
|
|
|
// Handle messages from external web apps (externally_connectable)
|
|
chrome.runtime.onMessageExternal.addListener((msg, sender, sendResponse) => {
|
|
if (msg?.action === "PARSE_URL" && msg.url) {
|
|
handleParseRequest(msg.url, sendResponse);
|
|
return true; // async
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Handles eBay URL parsing request
|
|
* Creates a hidden tab, waits for load, sends parse message to content script
|
|
*/
|
|
async function handleParseRequest(url, sendResponse) {
|
|
try {
|
|
// Validate URL
|
|
if (!url || typeof url !== 'string' || !url.toLowerCase().includes('ebay.')) {
|
|
sendResponse({ ok: false, error: "Invalid eBay URL" });
|
|
return;
|
|
}
|
|
|
|
// Create hidden tab
|
|
const tab = await chrome.tabs.create({
|
|
url: url,
|
|
active: false
|
|
});
|
|
|
|
const tabId = tab.id;
|
|
|
|
// Set up timeout
|
|
const timeoutId = setTimeout(() => {
|
|
cleanupParseRequest(tabId, null, { ok: false, error: "timeout" });
|
|
}, PARSE_TIMEOUT_MS);
|
|
|
|
// Store request info
|
|
activeParseRequests.set(tabId, {
|
|
timeout: timeoutId,
|
|
sendResponse: sendResponse
|
|
});
|
|
|
|
// Wait for tab to load, then send parse message
|
|
const checkTabLoaded = (updatedTabId, changeInfo, updatedTab) => {
|
|
if (updatedTabId !== tabId) return;
|
|
|
|
// Tab is fully loaded
|
|
if (changeInfo.status === 'complete' && updatedTab.url) {
|
|
chrome.tabs.onUpdated.removeListener(checkTabLoaded);
|
|
|
|
// Small delay to ensure DOM is ready
|
|
setTimeout(() => {
|
|
// Send parse message to content script
|
|
chrome.tabs.sendMessage(tabId, { action: "PARSE_EBAY" })
|
|
.then(response => {
|
|
if (response && response.ok && response.data) {
|
|
handleParseComplete(tabId, response.data);
|
|
} else {
|
|
cleanupParseRequest(tabId, null, { ok: false, error: "Parsing failed" });
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.error("Error sending parse message:", err);
|
|
cleanupParseRequest(tabId, null, { ok: false, error: "Content script error" });
|
|
});
|
|
}, 1000); // 1 second delay for DOM ready
|
|
}
|
|
};
|
|
|
|
chrome.tabs.onUpdated.addListener(checkTabLoaded);
|
|
|
|
} catch (error) {
|
|
console.error("Error in handleParseRequest:", error);
|
|
sendResponse({ ok: false, error: error.message || "Unknown error" });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles parse complete response from content script
|
|
*/
|
|
function handleParseComplete(tabId, data) {
|
|
cleanupParseRequest(tabId, data, null);
|
|
}
|
|
|
|
/**
|
|
* Cleans up parse request: closes tab, clears timeout, sends response
|
|
*/
|
|
async function cleanupParseRequest(tabId, data, error) {
|
|
const request = activeParseRequests.get(tabId);
|
|
if (!request) return;
|
|
|
|
// Clear timeout
|
|
if (request.timeout) {
|
|
clearTimeout(request.timeout);
|
|
}
|
|
|
|
// Remove from active requests
|
|
activeParseRequests.delete(tabId);
|
|
|
|
// Close tab
|
|
try {
|
|
await chrome.tabs.remove(tabId);
|
|
} catch (err) {
|
|
// Tab might already be closed
|
|
console.warn("Could not close tab:", err);
|
|
}
|
|
|
|
// Send response
|
|
if (request.sendResponse) {
|
|
if (error) {
|
|
request.sendResponse(error);
|
|
} else if (data) {
|
|
request.sendResponse({ ok: true, data: data });
|
|
} else {
|
|
request.sendResponse({ ok: false, error: "Unknown error" });
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
export async function getJwt() {
|
|
const data = await chrome.storage.local.get(STORAGE_KEY);
|
|
return data[STORAGE_KEY] || "";
|
|
}
|
|
|
|
export async function callProtectedApi(path, payload) {
|
|
const jwt = await getJwt();
|
|
if (!jwt) throw new Error("Not authed");
|
|
|
|
const res = await fetch(`${BACKEND_URL}${path}`, {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
"authorization": `Bearer ${jwt}`
|
|
},
|
|
body: JSON.stringify(payload || {})
|
|
});
|
|
|
|
if (!res.ok) throw new Error(`API error: ${res.status}`);
|
|
return await res.json();
|
|
}
|