refactor: ensure that camoufox profile always launch with persistent state

This commit is contained in:
zhom
2025-08-07 02:25:06 +04:00
parent aabae8d3d4
commit a507a3daed
5 changed files with 39 additions and 16 deletions
+7 -2
View File
@@ -123,11 +123,16 @@ export async function startCamoufoxProcess(
// Generate a unique ID for this instance
const id = generateCamoufoxId();
// Ensure profile path is absolute if provided
const absoluteProfilePath = profilePath
? path.resolve(profilePath)
: undefined;
// Create the Camoufox configuration
const config: CamoufoxConfig = {
id,
options,
profilePath,
options: JSON.parse(JSON.stringify(options)), // Deep clone to avoid reference sharing
profilePath: absoluteProfilePath,
url,
customConfig,
};
+3 -1
View File
@@ -106,6 +106,7 @@ export function listCamoufoxConfigs(): CamoufoxConfig[] {
.filter((config): config is CamoufoxConfig => config !== null)
.map((config) => {
config.options = "Removed for logging" as any;
config.customConfig = "Removed for logging" as any;
return config;
});
} catch (error) {
@@ -147,5 +148,6 @@ export function updateCamoufoxConfig(config: CamoufoxConfig): boolean {
* @returns A unique ID string
*/
export function generateCamoufoxId(): string {
return `camoufox_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
// Include process ID to ensure uniqueness across multiple processes
return `camoufox_${Date.now()}_${process.pid}_${Math.floor(Math.random() * 10000)}`;
}
+18 -9
View File
@@ -74,14 +74,17 @@ export async function runCamoufoxWorker(id: string): Promise<void> {
process.on("unhandledRejection", () => void gracefulShutdown());
try {
// Prepare options for Camoufox
const camoufoxOptions: LaunchOptions = { ...config.options };
// Deep clone to avoid reference sharing and ensure fresh configuration for each instance
const camoufoxOptions: LaunchOptions = JSON.parse(
JSON.stringify(config.options || {}),
);
// Add profile path if provided
if (config.profilePath) {
camoufoxOptions.user_data_dir = config.profilePath;
}
// Ensure block options are properly set
if (camoufoxOptions.block_images) {
camoufoxOptions.block_images = true;
}
@@ -99,7 +102,7 @@ export async function runCamoufoxWorker(id: string): Promise<void> {
camoufoxOptions.headless = true;
}
// Always set these defaults
// Always set these defaults - ensure they are applied for each instance
camoufoxOptions.i_know_what_im_doing = true;
camoufoxOptions.config = {
disableTheming: true,
@@ -107,12 +110,18 @@ export async function runCamoufoxWorker(id: string): Promise<void> {
...(camoufoxOptions.config || {}),
};
// Generate the configuration using launchOptions
// Generate fresh options for this specific instance
const generatedOptions = await launchOptions(camoufoxOptions);
// If we have a custom config from Rust, use it directly as environment variables
let finalEnv = generatedOptions.env || {};
// Start with process environment to ensure proper inheritance
let finalEnv = { ...process.env };
// Add generated options environment variables
if (generatedOptions.env) {
finalEnv = { ...finalEnv, ...generatedOptions.env };
}
// If we have a custom config from Rust, use it directly as environment variables
if (config.customConfig) {
try {
// Parse the custom config JSON string
@@ -125,16 +134,16 @@ export async function runCamoufoxWorker(id: string): Promise<void> {
finalEnv = { ...finalEnv, ...customEnvVars };
} catch (error) {
console.error(
"Failed to parse custom config, using generated config:",
`Camoufox worker ${id}: Failed to parse custom config, using generated config:`,
error,
);
return;
}
}
// Launch the server with the final configuration
// Launch the server with the final configuration - ensure unique wsPath for each instance
const finalOptions: any = {
...generatedOptions,
user_data_dir: config.profilePath,
wsPath: `/ws_${config.id}`,
env: finalEnv,
};
+4 -2
View File
@@ -10,12 +10,14 @@ const OS_NAME: "mac" | "win" | "lin" = OS_MAP[process.platform];
export function getEnvVars(configMap: Record<string, string>) {
const envVars: {
[key: string]: string | number | boolean;
[key: string]: string | undefined;
} = {};
let updatedConfigData: Uint8Array;
try {
updatedConfigData = new TextEncoder().encode(JSON.stringify(configMap));
// Ensure we're working with a fresh copy of the config
const configCopy = JSON.parse(JSON.stringify(configMap));
updatedConfigData = new TextEncoder().encode(JSON.stringify(configCopy));
} catch (e) {
console.error(`Error updating config: ${e}`);
process.exit(1);
+7 -2
View File
@@ -212,8 +212,13 @@ impl CamoufoxNodecarLauncher {
// Build nodecar command arguments
let mut args = vec!["camoufox".to_string(), "start".to_string()];
// Add profile path
args.extend(["--profile-path".to_string(), profile_path.to_string()]);
// Add profile path - ensure it's an absolute path
let absolute_profile_path = std::path::Path::new(profile_path)
.canonicalize()
.unwrap_or_else(|_| std::path::Path::new(profile_path).to_path_buf())
.to_string_lossy()
.to_string();
args.extend(["--profile-path".to_string(), absolute_profile_path]);
// Add URL if provided
if let Some(url) = url {