some spot refactoring
This commit is contained in:
parent
4b3563cacd
commit
2e536a2232
4 changed files with 326 additions and 299 deletions
|
|
@ -1,9 +1,17 @@
|
||||||
import { WebRequest } from "webextension-polyfill";
|
import {
|
||||||
|
createSpotNetworkRequest,
|
||||||
|
stopTrackingNetwork,
|
||||||
|
startTrackingNetwork,
|
||||||
|
SpotNetworkRequest,
|
||||||
|
} from "../utils/networkTracking";
|
||||||
|
import {
|
||||||
|
isTokenExpired
|
||||||
|
} from '../utils/jwt'
|
||||||
|
|
||||||
export default defineBackground(() => {
|
export default defineBackground(() => {
|
||||||
const CHECK_INT = 60 * 1000;
|
const CHECK_INT = 60 * 1000;
|
||||||
const PING_INT = 30 * 1000;
|
const PING_INT = 30 * 1000;
|
||||||
const VER = '1.0.3';
|
const VER = "1.0.7";
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
popup: {
|
popup: {
|
||||||
|
|
@ -101,6 +109,12 @@ export default defineBackground(() => {
|
||||||
stopped: "stopped",
|
stopped: "stopped",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultSettings = {
|
||||||
|
openInNewTab: true,
|
||||||
|
consoleLogs: true,
|
||||||
|
networkLogs: true,
|
||||||
|
ingestPoint: "https://app.openreplay.com",
|
||||||
|
};
|
||||||
const defaultSpotObj = {
|
const defaultSpotObj = {
|
||||||
name: "",
|
name: "",
|
||||||
comment: "",
|
comment: "",
|
||||||
|
|
@ -126,12 +140,7 @@ export default defineBackground(() => {
|
||||||
let finalReady = false;
|
let finalReady = false;
|
||||||
let finalSpotObj: SpotObj = defaultSpotObj;
|
let finalSpotObj: SpotObj = defaultSpotObj;
|
||||||
let onStop: (() => void) | null = null;
|
let onStop: (() => void) | null = null;
|
||||||
let settings = {
|
let settings = defaultSettings;
|
||||||
openInNewTab: true,
|
|
||||||
consoleLogs: true,
|
|
||||||
networkLogs: true,
|
|
||||||
ingestPoint: "https://app.openreplay.com",
|
|
||||||
};
|
|
||||||
let recordingState = {
|
let recordingState = {
|
||||||
activeTabId: null,
|
activeTabId: null,
|
||||||
area: null,
|
area: null,
|
||||||
|
|
@ -139,6 +148,8 @@ export default defineBackground(() => {
|
||||||
audioPerm: 0,
|
audioPerm: 0,
|
||||||
} as Record<string, any>;
|
} as Record<string, any>;
|
||||||
let jwtToken = "";
|
let jwtToken = "";
|
||||||
|
let refreshInt: any;
|
||||||
|
let pingInt: any;
|
||||||
|
|
||||||
function setJWTToken(token: string) {
|
function setJWTToken(token: string) {
|
||||||
jwtToken = token;
|
jwtToken = token;
|
||||||
|
|
@ -147,11 +158,28 @@ export default defineBackground(() => {
|
||||||
void browser.runtime.sendMessage({
|
void browser.runtime.sendMessage({
|
||||||
type: messages.popup.loginExist,
|
type: messages.popup.loginExist,
|
||||||
});
|
});
|
||||||
|
if (!refreshInt) {
|
||||||
|
refreshInt = setInterval(() => {
|
||||||
|
void refreshToken();
|
||||||
|
}, CHECK_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pingInt) {
|
||||||
|
pingInt = setInterval(() => {
|
||||||
|
void pingJWT();
|
||||||
|
}, PING_INT);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
void browser.storage.local.remove("jwtToken");
|
void browser.storage.local.remove("jwtToken");
|
||||||
void browser.runtime.sendMessage({
|
void browser.runtime.sendMessage({
|
||||||
type: messages.popup.to.noLogin,
|
type: messages.popup.to.noLogin,
|
||||||
});
|
});
|
||||||
|
if (refreshInt) {
|
||||||
|
clearInterval(refreshInt);
|
||||||
|
}
|
||||||
|
if (pingInt) {
|
||||||
|
clearInterval(pingInt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,21 +195,35 @@ export default defineBackground(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let slackChannels: { name: string; webhookId: number }[] = [];
|
let slackChannels: { name: string; webhookId: number }[] = [];
|
||||||
const refreshToken = async () => {
|
|
||||||
const data = await browser.storage.local.get(["jwtToken", "settings"]);
|
void checkTokenValidity();
|
||||||
|
browser.storage.local.get("settings").then(async (data: any) => {
|
||||||
if (!data.settings) {
|
if (!data.settings) {
|
||||||
|
void browser.storage.local.set({ settings });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
settings = Object.assign(settings, data.settings);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function refreshToken() {
|
||||||
|
const data = await browser.storage.local.get(["jwtToken", "settings"]);
|
||||||
|
if (!data.settings) {
|
||||||
|
await browser.storage.local.set({ defaultSettings });
|
||||||
|
data.settings = defaultSettings;
|
||||||
|
}
|
||||||
|
if (!data.jwtToken) {
|
||||||
|
setJWTToken("");
|
||||||
|
}
|
||||||
const { jwtToken, settings } = data;
|
const { jwtToken, settings } = data;
|
||||||
const ingest = safeApiUrl(settings.ingestPoint);
|
const refreshUrl = `${safeApiUrl(settings.ingestPoint)}/api/spot/refresh`
|
||||||
const refreshUrl = safeApiUrl(`${ingest}/api`);
|
console.log(settings.ingestPoint, refreshUrl);
|
||||||
if (!isTokenExpired(jwtToken) || !jwtToken) {
|
if (!isTokenExpired(jwtToken) || !jwtToken) {
|
||||||
if (refreshInt) {
|
if (refreshInt) {
|
||||||
clearInterval(refreshInt);
|
clearInterval(refreshInt);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const resp = await fetch(`${refreshUrl}/spot/refresh`, {
|
const resp = await fetch(refreshUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${jwtToken}`,
|
Authorization: `Bearer ${jwtToken}`,
|
||||||
|
|
@ -199,7 +241,7 @@ export default defineBackground(() => {
|
||||||
const refreshedJwt = dataObj.jwt;
|
const refreshedJwt = dataObj.jwt;
|
||||||
setJWTToken(refreshedJwt);
|
setJWTToken(refreshedJwt);
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
|
|
||||||
const fetchSlackChannels = async (token: string, ingest: string) => {
|
const fetchSlackChannels = async (token: string, ingest: string) => {
|
||||||
await refreshToken();
|
await refreshToken();
|
||||||
|
|
@ -217,51 +259,15 @@ export default defineBackground(() => {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let refreshInt: any;
|
|
||||||
let pingInt: any;
|
|
||||||
browser.storage.local
|
|
||||||
.get(["jwtToken", "settings"])
|
|
||||||
.then(async (data: any) => {
|
|
||||||
if (!data.settings) {
|
|
||||||
void browser.storage.local.set({ settings });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
settings = Object.assign(settings, data.settings);
|
|
||||||
|
|
||||||
if (!data.jwtToken) {
|
|
||||||
console.error("No JWT token found in storage");
|
|
||||||
void browser.runtime.sendMessage({
|
|
||||||
type: messages.popup.to.noLogin,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = safeApiUrl(`${data.settings.ingestPoint}/api`);
|
|
||||||
const ok = await refreshToken();
|
|
||||||
if (ok) {
|
|
||||||
fetchSlackChannels(data.jwtToken, url).catch((e) => {
|
|
||||||
console.error(e);
|
|
||||||
void refreshToken();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!refreshInt) {
|
|
||||||
refreshInt = setInterval(() => {
|
|
||||||
void refreshToken();
|
|
||||||
}, CHECK_INT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pingInt) {
|
|
||||||
pingInt = setInterval(() => {
|
|
||||||
void pingJWT();
|
|
||||||
}, PING_INT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function pingJWT(): Promise<void> {
|
async function pingJWT(): Promise<void> {
|
||||||
const data = await browser.storage.local.get(["jwtToken", "settings"]);
|
const data = await browser.storage.local.get(["jwtToken", "settings"]);
|
||||||
if (!data.settings) {
|
if (!data.settings) {
|
||||||
return;
|
await browser.storage.local.set({ defaultSettings });
|
||||||
|
data.settings = defaultSettings;
|
||||||
|
}
|
||||||
|
if (!data.jwtToken) {
|
||||||
|
setJWTToken("");
|
||||||
}
|
}
|
||||||
const { jwtToken, settings } = data;
|
const { jwtToken, settings } = data;
|
||||||
const ingest = safeApiUrl(settings.ingestPoint);
|
const ingest = safeApiUrl(settings.ingestPoint);
|
||||||
|
|
@ -277,7 +283,7 @@ export default defineBackground(() => {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${jwtToken}`,
|
Authorization: `Bearer ${jwtToken}`,
|
||||||
'Ext-Version': VER
|
"Ext-Version": VER,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
|
|
@ -289,6 +295,30 @@ export default defineBackground(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastReq: Record<string, any> | null = null;
|
let lastReq: Record<string, any> | null = null;
|
||||||
|
|
||||||
|
async function checkTokenValidity() {
|
||||||
|
const data = await browser.storage.local.get("jwtToken");
|
||||||
|
console.log(data)
|
||||||
|
if (!data.jwtToken) {
|
||||||
|
void browser.runtime.sendMessage({
|
||||||
|
type: messages.popup.to.noLogin,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ok = await refreshToken();
|
||||||
|
if (ok) {
|
||||||
|
if (!refreshInt) {
|
||||||
|
refreshInt = setInterval(() => {
|
||||||
|
void refreshToken();
|
||||||
|
}, CHECK_INT);
|
||||||
|
}
|
||||||
|
if (!pingInt) {
|
||||||
|
pingInt = setInterval(() => {
|
||||||
|
void pingJWT();
|
||||||
|
}, PING_INT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
browser.runtime.onMessage.addListener((request, sender, respond) => {
|
browser.runtime.onMessage.addListener((request, sender, respond) => {
|
||||||
if (request.type === messages.content.from.contentReady) {
|
if (request.type === messages.content.from.contentReady) {
|
||||||
|
|
@ -819,7 +849,7 @@ export default defineBackground(() => {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${jwtToken}`,
|
Authorization: `Bearer ${jwtToken}`,
|
||||||
'Ext-Version': VER
|
"Ext-Version": VER,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
|
|
@ -916,208 +946,25 @@ export default defineBackground(() => {
|
||||||
void browser.runtime.setUninstallURL("https://forms.gle/sMo8da2AvrPg5o7YA");
|
void browser.runtime.setUninstallURL("https://forms.gle/sMo8da2AvrPg5o7YA");
|
||||||
browser.runtime.onInstalled.addListener(async ({ reason }) => {
|
browser.runtime.onInstalled.addListener(async ({ reason }) => {
|
||||||
// Also fired on update and browser_update
|
// Also fired on update and browser_update
|
||||||
if (reason !== "install") return;
|
if (reason === "install") {
|
||||||
|
await browser.tabs.create({
|
||||||
await browser.tabs.create({
|
url: "https://www.openreplay.com/spot/welcome?ref=extension",
|
||||||
url: "https://www.openreplay.com/spot/welcome?ref=extension",
|
active: true,
|
||||||
active: true,
|
});
|
||||||
});
|
}
|
||||||
|
// in future:
|
||||||
|
// const tabs = await browser.tabs.query({}) as chrome.tabs.Tab[]
|
||||||
|
// for (const tab of tabs) {
|
||||||
|
// if (tab.id) {
|
||||||
|
// this will require more permissions, do we even want this?
|
||||||
|
// void chrome.tabs.executeScript(tab.id, {file: "content"});
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
await checkTokenValidity();
|
||||||
|
await initializeOffscreenDocument();
|
||||||
});
|
});
|
||||||
void initializeOffscreenDocument();
|
void initializeOffscreenDocument();
|
||||||
|
|
||||||
type TrackedRequest = {
|
|
||||||
statusCode: number;
|
|
||||||
requestHeaders: Record<string, string>;
|
|
||||||
responseHeaders: Record<string, string>;
|
|
||||||
} & (
|
|
||||||
| WebRequest.OnBeforeRequestDetailsType
|
|
||||||
| WebRequest.OnBeforeSendHeadersDetailsType
|
|
||||||
| WebRequest.OnCompletedDetailsType
|
|
||||||
| WebRequest.OnErrorOccurredDetailsType
|
|
||||||
| WebRequest.OnResponseStartedDetailsType
|
|
||||||
);
|
|
||||||
|
|
||||||
interface SpotNetworkRequest {
|
|
||||||
encodedBodySize: number;
|
|
||||||
responseBodySize: number;
|
|
||||||
duration: number;
|
|
||||||
method: TrackedRequest["method"];
|
|
||||||
type: string;
|
|
||||||
time: TrackedRequest["timeStamp"];
|
|
||||||
statusCode: number;
|
|
||||||
error?: string;
|
|
||||||
url: TrackedRequest["url"];
|
|
||||||
fromCache: boolean;
|
|
||||||
body: string;
|
|
||||||
requestHeaders: Record<string, string>;
|
|
||||||
responseHeaders: Record<string, string>;
|
|
||||||
}
|
|
||||||
const rawRequests: (TrackedRequest & {
|
|
||||||
startTs: number;
|
|
||||||
duration: number;
|
|
||||||
})[] = [];
|
|
||||||
function filterHeaders(headers: Record<string, string>) {
|
|
||||||
const filteredHeaders: Record<string, string> = {};
|
|
||||||
const privateHs = [
|
|
||||||
"x-api-key",
|
|
||||||
"www-authenticate",
|
|
||||||
"x-csrf-token",
|
|
||||||
"x-requested-with",
|
|
||||||
"x-forwarded-for",
|
|
||||||
"x-real-ip",
|
|
||||||
"cookie",
|
|
||||||
"authorization",
|
|
||||||
"auth",
|
|
||||||
"proxy-authorization",
|
|
||||||
"set-cookie",
|
|
||||||
];
|
|
||||||
if (Array.isArray(headers)) {
|
|
||||||
headers.forEach(({ name, value }) => {
|
|
||||||
if (privateHs.includes(name.toLowerCase())) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
filteredHeaders[name] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
for (const [key, value] of Object.entries(headers)) {
|
|
||||||
if (!privateHs.includes(key.toLowerCase())) {
|
|
||||||
filteredHeaders[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filteredHeaders;
|
|
||||||
}
|
|
||||||
function createSpotNetworkRequest(
|
|
||||||
trackedRequest: TrackedRequest,
|
|
||||||
trackedTab?: number,
|
|
||||||
) {
|
|
||||||
if (trackedRequest.tabId === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (trackedTab && trackedTab !== trackedRequest.tabId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
["ping", "beacon", "image", "script", "font"].includes(
|
|
||||||
trackedRequest.type,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (!trackedRequest.statusCode || trackedRequest.statusCode < 400) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const type = ["stylesheet", "script", "image", "media", "font"].includes(
|
|
||||||
trackedRequest.type,
|
|
||||||
)
|
|
||||||
? "resource"
|
|
||||||
: trackedRequest.type;
|
|
||||||
|
|
||||||
const requestHeaders = trackedRequest.requestHeaders
|
|
||||||
? filterHeaders(trackedRequest.requestHeaders)
|
|
||||||
: {};
|
|
||||||
const responseHeaders = trackedRequest.responseHeaders
|
|
||||||
? filterHeaders(trackedRequest.responseHeaders)
|
|
||||||
: {};
|
|
||||||
|
|
||||||
const reqSize = trackedRequest.reqBody
|
|
||||||
? trackedRequest.requestSize || trackedRequest.reqBody.length
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
const status = getRequestStatus(trackedRequest);
|
|
||||||
const request: SpotNetworkRequest = {
|
|
||||||
method: trackedRequest.method,
|
|
||||||
type,
|
|
||||||
body: trackedRequest.reqBody,
|
|
||||||
requestHeaders,
|
|
||||||
responseHeaders,
|
|
||||||
time: trackedRequest.timeStamp,
|
|
||||||
statusCode: status,
|
|
||||||
error: trackedRequest.error,
|
|
||||||
url: trackedRequest.url,
|
|
||||||
fromCache: trackedRequest.fromCache || false,
|
|
||||||
encodedBodySize: reqSize,
|
|
||||||
responseBodySize: trackedRequest.responseSize,
|
|
||||||
duration: trackedRequest.duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
function modifyOnSpot(request: TrackedRequest) {
|
|
||||||
const id = request.requestId;
|
|
||||||
const index = rawRequests.findIndex((r) => r.requestId === id);
|
|
||||||
const ts = Date.now();
|
|
||||||
const start = rawRequests[index]?.startTs ?? ts;
|
|
||||||
rawRequests[index] = {
|
|
||||||
...rawRequests[index],
|
|
||||||
...request,
|
|
||||||
duration: ts - start,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const trackOnBefore = (
|
|
||||||
details: WebRequest.OnBeforeRequestDetailsType & { reqBody: string },
|
|
||||||
) => {
|
|
||||||
if (details.method === "POST" && details.requestBody) {
|
|
||||||
const requestBody = details.requestBody;
|
|
||||||
if (requestBody.formData) {
|
|
||||||
details.reqBody = JSON.stringify(requestBody.formData);
|
|
||||||
} else if (requestBody.raw) {
|
|
||||||
const raw = requestBody.raw[0]?.bytes;
|
|
||||||
if (raw) {
|
|
||||||
details.reqBody = new TextDecoder("utf-8").decode(raw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rawRequests.push({ ...details, startTs: Date.now(), duration: 0 });
|
|
||||||
};
|
|
||||||
const trackOnCompleted = (details: WebRequest.OnCompletedDetailsType) => {
|
|
||||||
modifyOnSpot(details);
|
|
||||||
};
|
|
||||||
const trackOnHeaders = (
|
|
||||||
details: WebRequest.OnBeforeSendHeadersDetailsType,
|
|
||||||
) => {
|
|
||||||
modifyOnSpot(details);
|
|
||||||
};
|
|
||||||
const trackOnError = (details: WebRequest.OnErrorOccurredDetailsType) => {
|
|
||||||
modifyOnSpot(details);
|
|
||||||
};
|
|
||||||
function startTrackingNetwork() {
|
|
||||||
rawRequests.length = 0;
|
|
||||||
browser.webRequest.onBeforeRequest.addListener(
|
|
||||||
// @ts-ignore
|
|
||||||
trackOnBefore,
|
|
||||||
{ urls: ["<all_urls>"] },
|
|
||||||
["requestBody"],
|
|
||||||
);
|
|
||||||
browser.webRequest.onBeforeSendHeaders.addListener(
|
|
||||||
trackOnHeaders,
|
|
||||||
{ urls: ["<all_urls>"] },
|
|
||||||
["requestHeaders"],
|
|
||||||
);
|
|
||||||
browser.webRequest.onCompleted.addListener(
|
|
||||||
trackOnCompleted,
|
|
||||||
{
|
|
||||||
urls: ["<all_urls>"],
|
|
||||||
},
|
|
||||||
["responseHeaders"],
|
|
||||||
);
|
|
||||||
browser.webRequest.onErrorOccurred.addListener(
|
|
||||||
trackOnError,
|
|
||||||
{
|
|
||||||
urls: ["<all_urls>"],
|
|
||||||
},
|
|
||||||
["extraHeaders"],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopTrackingNetwork() {
|
|
||||||
browser.webRequest.onBeforeRequest.removeListener(trackOnBefore);
|
|
||||||
browser.webRequest.onCompleted.removeListener(trackOnCompleted);
|
|
||||||
browser.webRequest.onErrorOccurred.removeListener(trackOnError);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initializeOffscreenDocument() {
|
async function initializeOffscreenDocument() {
|
||||||
const existingContexts = await browser.runtime.getContexts({});
|
const existingContexts = await browser.runtime.getContexts({});
|
||||||
let recording = false;
|
let recording = false;
|
||||||
|
|
@ -1125,9 +972,8 @@ export default defineBackground(() => {
|
||||||
const offscreenDocument = existingContexts.find(
|
const offscreenDocument = existingContexts.find(
|
||||||
(c: { contextType: string }) => c.contextType === "OFFSCREEN_DOCUMENT",
|
(c: { contextType: string }) => c.contextType === "OFFSCREEN_DOCUMENT",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (offscreenDocument) {
|
if (offscreenDocument) {
|
||||||
await browser.offscreen.closeDocument()
|
await browser.offscreen.closeDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
await browser.offscreen.createDocument({
|
await browser.offscreen.createDocument({
|
||||||
|
|
@ -1153,22 +999,15 @@ export default defineBackground(() => {
|
||||||
}
|
}
|
||||||
let activeTab = activeTabs[0];
|
let activeTab = activeTabs[0];
|
||||||
const sendTo = message.activeTabId || activeTab.id!;
|
const sendTo = message.activeTabId || activeTab.id!;
|
||||||
if (!contentArmy[sendTo]) {
|
let attempts = 0;
|
||||||
let tries = 0;
|
while (!contentArmy[sendTo] && attempts < 100) {
|
||||||
const exist = await new Promise((res) => {
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
const interval = setInterval(() => {
|
attempts++;
|
||||||
if (contentArmy[sendTo] || tries < 500) {
|
|
||||||
clearInterval(interval);
|
|
||||||
res(tries < 500);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
if (!exist) throw new Error("Can't find required tab");
|
|
||||||
}
|
}
|
||||||
try {
|
if (contentArmy[sendTo]) {
|
||||||
void browser.tabs.sendMessage(sendTo, message);
|
await browser.tabs.sendMessage(sendTo, message);
|
||||||
} catch (e) {
|
} else {
|
||||||
console.error("Sending to active tab", e, message);
|
console.error("Content script not ready in tab", sendTo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1381,29 +1220,4 @@ export default defineBackground(() => {
|
||||||
console.error("Error starting recording", e, activeTab, activeTabId);
|
console.error("Error starting recording", e, activeTab, activeTabId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodeJwt = (jwt: string): any => {
|
|
||||||
const base64Url = jwt.split(".")[1];
|
|
||||||
if (!base64Url) {
|
|
||||||
return { exp: 0 };
|
|
||||||
}
|
|
||||||
const base64 = base64Url.replace("-", "+").replace("_", "/");
|
|
||||||
return JSON.parse(atob(base64));
|
|
||||||
};
|
|
||||||
|
|
||||||
const isTokenExpired = (token: string): boolean => {
|
|
||||||
const decoded: any = decodeJwt(token);
|
|
||||||
const currentTime = Date.now() / 1000;
|
|
||||||
return decoded.exp < currentTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
function getRequestStatus(request: any): number {
|
|
||||||
if (request.statusCode) {
|
|
||||||
return request.statusCode;
|
|
||||||
}
|
|
||||||
if (request.error) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 200;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "wxt-starter",
|
"name": "wxt-starter",
|
||||||
"description": "manifest.json description",
|
"description": "manifest.json description",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "wxt",
|
"dev": "wxt",
|
||||||
|
|
|
||||||
14
spot/utils/jwt.ts
Normal file
14
spot/utils/jwt.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
export const decodeJwt = (jwt: string): any => {
|
||||||
|
const base64Url = jwt.split(".")[1];
|
||||||
|
if (!base64Url) {
|
||||||
|
return { exp: 0 };
|
||||||
|
}
|
||||||
|
const base64 = base64Url.replace("-", "+").replace("_", "/");
|
||||||
|
return JSON.parse(atob(base64));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isTokenExpired = (token: string): boolean => {
|
||||||
|
const decoded: any = decodeJwt(token);
|
||||||
|
const currentTime = Date.now() / 1000;
|
||||||
|
return decoded.exp < currentTime;
|
||||||
|
};
|
||||||
199
spot/utils/networkTracking.ts
Normal file
199
spot/utils/networkTracking.ts
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
import { WebRequest } from "webextension-polyfill";
|
||||||
|
export type TrackedRequest = {
|
||||||
|
statusCode: number;
|
||||||
|
requestHeaders: Record<string, string>;
|
||||||
|
responseHeaders: Record<string, string>;
|
||||||
|
} & (
|
||||||
|
| WebRequest.OnBeforeRequestDetailsType
|
||||||
|
| WebRequest.OnBeforeSendHeadersDetailsType
|
||||||
|
| WebRequest.OnCompletedDetailsType
|
||||||
|
| WebRequest.OnErrorOccurredDetailsType
|
||||||
|
| WebRequest.OnResponseStartedDetailsType
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface SpotNetworkRequest {
|
||||||
|
encodedBodySize: number;
|
||||||
|
responseBodySize: number;
|
||||||
|
duration: number;
|
||||||
|
method: TrackedRequest["method"];
|
||||||
|
type: string;
|
||||||
|
time: TrackedRequest["timeStamp"];
|
||||||
|
statusCode: number;
|
||||||
|
error?: string;
|
||||||
|
url: TrackedRequest["url"];
|
||||||
|
fromCache: boolean;
|
||||||
|
body: string;
|
||||||
|
requestHeaders: Record<string, string>;
|
||||||
|
responseHeaders: Record<string, string>;
|
||||||
|
}
|
||||||
|
const rawRequests: (TrackedRequest & {
|
||||||
|
startTs: number;
|
||||||
|
duration: number;
|
||||||
|
})[] = [];
|
||||||
|
function filterHeaders(headers: Record<string, string>) {
|
||||||
|
const filteredHeaders: Record<string, string> = {};
|
||||||
|
const privateHs = [
|
||||||
|
"x-api-key",
|
||||||
|
"www-authenticate",
|
||||||
|
"x-csrf-token",
|
||||||
|
"x-requested-with",
|
||||||
|
"x-forwarded-for",
|
||||||
|
"x-real-ip",
|
||||||
|
"cookie",
|
||||||
|
"authorization",
|
||||||
|
"auth",
|
||||||
|
"proxy-authorization",
|
||||||
|
"set-cookie",
|
||||||
|
];
|
||||||
|
if (Array.isArray(headers)) {
|
||||||
|
headers.forEach(({ name, value }) => {
|
||||||
|
if (privateHs.includes(name.toLowerCase())) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
filteredHeaders[name] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for (const [key, value] of Object.entries(headers)) {
|
||||||
|
if (!privateHs.includes(key.toLowerCase())) {
|
||||||
|
filteredHeaders[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredHeaders;
|
||||||
|
}
|
||||||
|
export function createSpotNetworkRequest(
|
||||||
|
trackedRequest: TrackedRequest,
|
||||||
|
trackedTab?: number,
|
||||||
|
) {
|
||||||
|
if (trackedRequest.tabId === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (trackedTab && trackedTab !== trackedRequest.tabId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
["ping", "beacon", "image", "script", "font"].includes(trackedRequest.type)
|
||||||
|
) {
|
||||||
|
if (!trackedRequest.statusCode || trackedRequest.statusCode < 400) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const type = ["stylesheet", "script", "image", "media", "font"].includes(
|
||||||
|
trackedRequest.type,
|
||||||
|
)
|
||||||
|
? "resource"
|
||||||
|
: trackedRequest.type;
|
||||||
|
|
||||||
|
const requestHeaders = trackedRequest.requestHeaders
|
||||||
|
? filterHeaders(trackedRequest.requestHeaders)
|
||||||
|
: {};
|
||||||
|
const responseHeaders = trackedRequest.responseHeaders
|
||||||
|
? filterHeaders(trackedRequest.responseHeaders)
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const reqSize = trackedRequest.reqBody
|
||||||
|
? trackedRequest.requestSize || trackedRequest.reqBody.length
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const status = getRequestStatus(trackedRequest);
|
||||||
|
const request: SpotNetworkRequest = {
|
||||||
|
method: trackedRequest.method,
|
||||||
|
type,
|
||||||
|
body: trackedRequest.reqBody,
|
||||||
|
requestHeaders,
|
||||||
|
responseHeaders,
|
||||||
|
time: trackedRequest.timeStamp,
|
||||||
|
statusCode: status,
|
||||||
|
error: trackedRequest.error,
|
||||||
|
url: trackedRequest.url,
|
||||||
|
fromCache: trackedRequest.fromCache || false,
|
||||||
|
encodedBodySize: reqSize,
|
||||||
|
responseBodySize: trackedRequest.responseSize,
|
||||||
|
duration: trackedRequest.duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
function modifyOnSpot(request: TrackedRequest) {
|
||||||
|
const id = request.requestId;
|
||||||
|
const index = rawRequests.findIndex((r) => r.requestId === id);
|
||||||
|
const ts = Date.now();
|
||||||
|
const start = rawRequests[index]?.startTs ?? ts;
|
||||||
|
rawRequests[index] = {
|
||||||
|
...rawRequests[index],
|
||||||
|
...request,
|
||||||
|
duration: ts - start,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const trackOnBefore = (
|
||||||
|
details: WebRequest.OnBeforeRequestDetailsType & { reqBody: string },
|
||||||
|
) => {
|
||||||
|
if (details.method === "POST" && details.requestBody) {
|
||||||
|
const requestBody = details.requestBody;
|
||||||
|
if (requestBody.formData) {
|
||||||
|
details.reqBody = JSON.stringify(requestBody.formData);
|
||||||
|
} else if (requestBody.raw) {
|
||||||
|
const raw = requestBody.raw[0]?.bytes;
|
||||||
|
if (raw) {
|
||||||
|
details.reqBody = new TextDecoder("utf-8").decode(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rawRequests.push({ ...details, startTs: Date.now(), duration: 0 });
|
||||||
|
};
|
||||||
|
const trackOnCompleted = (details: WebRequest.OnCompletedDetailsType) => {
|
||||||
|
modifyOnSpot(details);
|
||||||
|
};
|
||||||
|
const trackOnHeaders = (details: WebRequest.OnBeforeSendHeadersDetailsType) => {
|
||||||
|
modifyOnSpot(details);
|
||||||
|
};
|
||||||
|
const trackOnError = (details: WebRequest.OnErrorOccurredDetailsType) => {
|
||||||
|
modifyOnSpot(details);
|
||||||
|
};
|
||||||
|
export function startTrackingNetwork() {
|
||||||
|
rawRequests.length = 0;
|
||||||
|
browser.webRequest.onBeforeRequest.addListener(
|
||||||
|
// @ts-ignore
|
||||||
|
trackOnBefore,
|
||||||
|
{ urls: ["<all_urls>"] },
|
||||||
|
["requestBody"],
|
||||||
|
);
|
||||||
|
browser.webRequest.onBeforeSendHeaders.addListener(
|
||||||
|
trackOnHeaders,
|
||||||
|
{ urls: ["<all_urls>"] },
|
||||||
|
["requestHeaders"],
|
||||||
|
);
|
||||||
|
browser.webRequest.onCompleted.addListener(
|
||||||
|
trackOnCompleted,
|
||||||
|
{
|
||||||
|
urls: ["<all_urls>"],
|
||||||
|
},
|
||||||
|
["responseHeaders"],
|
||||||
|
);
|
||||||
|
browser.webRequest.onErrorOccurred.addListener(
|
||||||
|
trackOnError,
|
||||||
|
{
|
||||||
|
urls: ["<all_urls>"],
|
||||||
|
},
|
||||||
|
["extraHeaders"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stopTrackingNetwork() {
|
||||||
|
browser.webRequest.onBeforeRequest.removeListener(trackOnBefore);
|
||||||
|
browser.webRequest.onCompleted.removeListener(trackOnCompleted);
|
||||||
|
browser.webRequest.onErrorOccurred.removeListener(trackOnError);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRequestStatus(request: any): number {
|
||||||
|
if (request.statusCode) {
|
||||||
|
return request.statusCode;
|
||||||
|
}
|
||||||
|
if (request.error) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue