refactor: display storage errors for nodecar in json

This commit is contained in:
zhom
2025-08-07 00:45:04 +04:00
parent 4a8e905a44
commit 1662c1efba
4 changed files with 28 additions and 135 deletions
-1
View File
@@ -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",
+2 -111
View File
@@ -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
+20 -8
View File
@@ -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;
}
}
+6 -15
View File
@@ -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