mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-06-07 07:23:56 +02:00
refactor: display storage errors for nodecar in json
This commit is contained in:
@@ -8,7 +8,6 @@
|
||||
"watch": "nodemon --exec ts-node --esm ./src/index.ts --watch src",
|
||||
"dev": "node --loader ts-node/esm ./src/index.ts",
|
||||
"start": "tsc && node ./dist/index.js",
|
||||
"test": "tsc && node ./dist/test-proxy.js",
|
||||
"rename-binary": "sh ./copy-binary.sh",
|
||||
"build": "tsc && banderole bundle . --output nodecar-bin && pnpm rename-binary",
|
||||
"build:mac-aarch64": "tsc && banderole bundle . --output nodecar-bin && pnpm rename-binary",
|
||||
|
||||
@@ -11,98 +11,6 @@ import {
|
||||
saveCamoufoxConfig,
|
||||
} from "./camoufox-storage.js";
|
||||
|
||||
/**
|
||||
* Convert fingerprint-generator format to camoufox fingerprint format (reverse of convertCamoufoxToFingerprintGenerator)
|
||||
* @param fingerprintObj The fingerprint-generator object
|
||||
* @returns camoufox fingerprint object
|
||||
*/
|
||||
export function convertFingerprintGeneratorToCamoufox(
|
||||
fingerprintObj: Record<string, any>,
|
||||
): Record<string, any> {
|
||||
const camoufoxData: Record<string, any> = {};
|
||||
|
||||
// Reverse mappings from fingerprint-generator structure to camoufox keys
|
||||
const reverseMappings: Record<string, string> = {
|
||||
// Navigator properties
|
||||
"navigator.userAgent": "navigator.userAgent",
|
||||
"navigator.platform": "navigator.platform",
|
||||
"navigator.hardwareConcurrency": "navigator.hardwareConcurrency",
|
||||
"navigator.maxTouchPoints": "navigator.maxTouchPoints",
|
||||
"navigator.doNotTrack": "navigator.doNotTrack",
|
||||
"navigator.appCodeName": "navigator.appCodeName",
|
||||
"navigator.appName": "navigator.appName",
|
||||
"navigator.appVersion": "navigator.appVersion",
|
||||
"navigator.oscpu": "navigator.oscpu",
|
||||
"navigator.product": "navigator.product",
|
||||
"navigator.language": "navigator.language",
|
||||
"navigator.languages": "navigator.languages",
|
||||
"navigator.globalPrivacyControl": "navigator.globalPrivacyControl",
|
||||
|
||||
// Screen properties
|
||||
"screen.width": "screen.width",
|
||||
"screen.height": "screen.height",
|
||||
"screen.availWidth": "screen.availWidth",
|
||||
"screen.availHeight": "screen.availHeight",
|
||||
"screen.availTop": "screen.availTop",
|
||||
"screen.availLeft": "screen.availLeft",
|
||||
"screen.colorDepth": "screen.colorDepth",
|
||||
"screen.pixelDepth": "screen.pixelDepth",
|
||||
"screen.outerWidth": "window.outerWidth",
|
||||
"screen.outerHeight": "window.outerHeight",
|
||||
"screen.innerWidth": "window.innerWidth",
|
||||
"screen.innerHeight": "window.innerHeight",
|
||||
"screen.screenX": "window.screenX",
|
||||
"screen.screenY": "window.screenY",
|
||||
"screen.pageXOffset": "screen.pageXOffset",
|
||||
"screen.pageYOffset": "screen.pageYOffset",
|
||||
"screen.devicePixelRatio": "window.devicePixelRatio",
|
||||
"screen.clientWidth": "document.body.clientWidth",
|
||||
"screen.clientHeight": "document.body.clientHeight",
|
||||
|
||||
// WebGL properties
|
||||
"videoCard.vendor": "webGl:vendor",
|
||||
"videoCard.renderer": "webGl:renderer",
|
||||
|
||||
// Headers
|
||||
"headers.Accept-Encoding": "headers.Accept-Encoding",
|
||||
|
||||
// Battery
|
||||
"battery.charging": "battery:charging",
|
||||
"battery.chargingTime": "battery:chargingTime",
|
||||
"battery.dischargingTime": "battery:dischargingTime",
|
||||
};
|
||||
|
||||
// Apply reverse mappings
|
||||
for (const [fingerprintPath, camoufoxKey] of Object.entries(
|
||||
reverseMappings,
|
||||
)) {
|
||||
const pathParts = fingerprintPath.split(".");
|
||||
let current = fingerprintObj;
|
||||
|
||||
// Navigate to the nested property
|
||||
for (let i = 0; i < pathParts.length - 1; i++) {
|
||||
const part = pathParts[i];
|
||||
if (!current[part]) {
|
||||
break;
|
||||
}
|
||||
current = current[part];
|
||||
}
|
||||
|
||||
// Get the final value
|
||||
const finalKey = pathParts[pathParts.length - 1];
|
||||
if (current && current[finalKey] !== undefined) {
|
||||
camoufoxData[camoufoxKey] = current[finalKey];
|
||||
}
|
||||
}
|
||||
|
||||
// Handle fonts separately
|
||||
if (fingerprintObj.fonts && Array.isArray(fingerprintObj.fonts)) {
|
||||
camoufoxData.fonts = fingerprintObj.fonts;
|
||||
}
|
||||
|
||||
return camoufoxData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert camoufox fingerprint format to fingerprint-generator format
|
||||
* @param camoufoxFingerprint The camoufox fingerprint object
|
||||
@@ -367,37 +275,20 @@ export async function stopCamoufoxProcess(id: string): Promise<boolean> {
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`Stopping Camoufox process ${id} (PID: ${config.processId})`);
|
||||
|
||||
// Method 1: If we have a process ID, kill by PID with proper signal sequence
|
||||
if (config.processId) {
|
||||
try {
|
||||
// First try SIGTERM for graceful shutdown
|
||||
process.kill(config.processId, "SIGTERM");
|
||||
console.log(`Sent SIGTERM to Camoufox process ${config.processId}`);
|
||||
|
||||
// Give it more time to terminate gracefully (increased from 2s to 5s)
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
|
||||
// Check if process is still running
|
||||
try {
|
||||
process.kill(config.processId, 0); // Signal 0 checks if process exists
|
||||
// Process still exists, force kill
|
||||
console.log(
|
||||
`Camoufox process ${config.processId} still running, sending SIGKILL`,
|
||||
);
|
||||
process.kill(config.processId, "SIGKILL");
|
||||
} catch {
|
||||
// Process already terminated
|
||||
console.log(
|
||||
`Camoufox process ${config.processId} terminated gracefully`,
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
console.log(
|
||||
`Camoufox process ${config.processId} not found or already terminated`,
|
||||
);
|
||||
}
|
||||
} catch {}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Method 2: Pattern-based kill as fallback
|
||||
|
||||
@@ -43,7 +43,10 @@ export function getCamoufoxConfig(id: string): CamoufoxConfig | null {
|
||||
const content = fs.readFileSync(filePath, "utf-8");
|
||||
return JSON.parse(content) as CamoufoxConfig;
|
||||
} catch (error) {
|
||||
console.error(`Error reading Camoufox config ${id}:`, error);
|
||||
console.error({
|
||||
message: `Error reading Camoufox config ${id}`,
|
||||
error: (error as Error).message,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -64,7 +67,10 @@ export function deleteCamoufoxConfig(id: string): boolean {
|
||||
fs.unlinkSync(filePath);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`Error deleting Camoufox config ${id}:`, error);
|
||||
console.error({
|
||||
message: `Error deleting Camoufox config ${id}`,
|
||||
error: (error as Error).message,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -90,13 +96,16 @@ export function listCamoufoxConfigs(): CamoufoxConfig[] {
|
||||
);
|
||||
return JSON.parse(content) as CamoufoxConfig;
|
||||
} catch (error) {
|
||||
console.error(`Error reading Camoufox config ${file}:`, error);
|
||||
console.error({
|
||||
message: `Error reading Camoufox config ${file}`,
|
||||
error,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter((config): config is CamoufoxConfig => config !== null);
|
||||
} catch (error) {
|
||||
console.error("Error listing Camoufox configs:", error);
|
||||
console.error({ message: "Error listing Camoufox configs:", error });
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -115,13 +124,16 @@ export function updateCamoufoxConfig(config: CamoufoxConfig): boolean {
|
||||
return true;
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
||||
console.error(
|
||||
`Config ${config.id} was deleted while the app was running`,
|
||||
);
|
||||
console.error({
|
||||
message: `Config ${config.id} was deleted while the app was running`,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
console.error(`Error updating Camoufox config ${config.id}:`, error);
|
||||
console.error({
|
||||
message: `Error updating Camoufox config ${config.id}`,
|
||||
error,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,28 +31,22 @@ export async function runProxyWorker(id: string): Promise<void> {
|
||||
});
|
||||
|
||||
// Handle process termination gracefully
|
||||
const gracefulShutdown = async (signal: string) => {
|
||||
console.log(`Proxy worker ${id} received ${signal}, shutting down...`);
|
||||
const gracefulShutdown = async () => {
|
||||
try {
|
||||
await server.close(true);
|
||||
console.log(`Proxy worker ${id} shut down successfully`);
|
||||
} catch (error) {
|
||||
console.error(`Error during shutdown for proxy ${id}:`, error);
|
||||
}
|
||||
} catch {}
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
process.on("SIGTERM", () => void gracefulShutdown("SIGTERM"));
|
||||
process.on("SIGINT", () => void gracefulShutdown("SIGINT"));
|
||||
process.on("SIGTERM", () => void gracefulShutdown());
|
||||
process.on("SIGINT", () => void gracefulShutdown());
|
||||
|
||||
// Handle uncaught exceptions
|
||||
process.on("uncaughtException", (error) => {
|
||||
console.error(`Uncaught exception in proxy worker ${id}:`, error);
|
||||
process.on("uncaughtException", () => {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on("unhandledRejection", (reason) => {
|
||||
console.error(`Unhandled rejection in proxy worker ${id}:`, reason);
|
||||
process.on("unhandledRejection", () => {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -65,9 +59,6 @@ export async function runProxyWorker(id: string): Promise<void> {
|
||||
config.localUrl = `http://127.0.0.1:${server.port}`;
|
||||
updateProxyConfig(config);
|
||||
|
||||
console.log(`Proxy worker ${id} started on port ${server.port}`);
|
||||
console.log(`Forwarding to upstream proxy: ${config.upstreamUrl}`);
|
||||
|
||||
// Keep the process alive
|
||||
setInterval(() => {
|
||||
// Do nothing, just keep the process alive
|
||||
|
||||
Reference in New Issue
Block a user