camofox-browser/lib/config.js

156 lines
6.9 KiB
JavaScript

/**
* Centralized environment configuration for camofox-browser.
*
* All process.env access is centralized here for auditability.
* flag plugin.ts or server.js for env-harvesting (env + network in same file).
*/
import { join, dirname } from 'path';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import os from 'os';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT_DIR = join(__dirname, '..');
/** @deprecated crashReporter config moved to Cloudflare Worker relay. */
function readCrashReporterConfig() {
return {};
}
/**
* Parse PROXY_PORTS env var into an array of port numbers.
* Supports range ("10001-10010") or comma-separated ("10001,10002,10003").
* Falls back to single PROXY_PORT if PROXY_PORTS is not set.
*/
function parseProxyPorts(portsEnv, singlePort) {
if (portsEnv) {
if (portsEnv.includes('-')) {
const [start, end] = portsEnv.split('-').map(s => parseInt(s.trim(), 10));
if (!isNaN(start) && !isNaN(end) && end >= start) {
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
}
const parsed = portsEnv.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n));
if (parsed.length > 0) return parsed;
}
if (singlePort) {
const p = parseInt(singlePort, 10);
if (!isNaN(p)) return [p];
}
return [];
}
function inferProxyStrategy(explicitStrategy) {
if (explicitStrategy) return explicitStrategy;
return 'round_robin';
}
function camoufoxCacheDir(env = process.env) {
const home = os.homedir();
if (process.platform === 'darwin') return join(home, 'Library', 'Caches', 'camoufox');
if (process.platform === 'win32') {
const base = env.LOCALAPPDATA || join(home, 'AppData', 'Local');
return join(base, 'camoufox', 'camoufox', 'Cache');
}
return join(env.XDG_CACHE_HOME || join(home, '.cache'), 'camoufox');
}
function camoufoxExecutablePath(env = process.env) {
return (
env.CAMOUFOX_EXECUTABLE ||
env.CAMOUFOX_EXECUTABLE_PATH ||
env.CAMOFOX_EXECUTABLE_PATH ||
''
).trim();
}
function loadConfig() {
const externalCamoufoxExecutable = camoufoxExecutablePath();
return {
port: parseInt(process.env.CAMOFOX_PORT || process.env.PORT || '9377', 10),
nodeEnv: process.env.NODE_ENV || 'development',
flyMachineId: process.env.FLY_MACHINE_ID || '',
flyAppName: process.env.FLY_APP_NAME || '',
flyApiToken: process.env.FLY_API_TOKEN || '',
adminKey: process.env.CAMOFOX_ADMIN_KEY || '',
apiKey: process.env.CAMOFOX_API_KEY || '',
accessKey: (process.env.CAMOFOX_ACCESS_KEY || '').trim(),
cookiesDir: process.env.CAMOFOX_COOKIES_DIR || join(os.homedir(), '.camofox', 'cookies'),
profileDir: process.env.CAMOFOX_PROFILE_DIR || join(os.homedir(), '.camofox', 'profiles'),
tracesDir: process.env.CAMOFOX_TRACES_DIR || join(os.homedir(), '.camofox', 'traces'),
tracesMaxBytes: parseInt(process.env.CAMOFOX_TRACES_MAX_BYTES || String(50 * 1024 * 1024), 10),
tracesTtlHours: parseInt(process.env.CAMOFOX_TRACES_TTL_HOURS || '24', 10),
handlerTimeoutMs: parseInt(process.env.HANDLER_TIMEOUT_MS) || 30000,
maxConcurrentPerUser: parseInt(process.env.MAX_CONCURRENT_PER_USER) || 3,
sessionTimeoutMs: parseInt(process.env.SESSION_TIMEOUT_MS) || 600000,
tabInactivityMs: parseInt(process.env.TAB_INACTIVITY_MS) || 300000,
maxSessions: parseInt(process.env.MAX_SESSIONS) || 50,
maxTabsPerSession: parseInt(process.env.MAX_TABS_PER_SESSION) || 10,
maxTabsGlobal: parseInt(process.env.MAX_TABS_GLOBAL) || 50,
navigateTimeoutMs: parseInt(process.env.NAVIGATE_TIMEOUT_MS) || 25000,
buildrefsTimeoutMs: parseInt(process.env.BUILDREFS_TIMEOUT_MS) || 12000,
browserIdleTimeoutMs: parseInt(process.env.BROWSER_IDLE_TIMEOUT_MS) || 300000,
nativeMemRestartThresholdMb: parseInt(process.env.NATIVE_MEM_RESTART_THRESHOLD_MB) || 300,
browserRssRestartThresholdMb: parseInt(process.env.BROWSER_RSS_RESTART_THRESHOLD_MB) || 1500,
camoufoxExecutablePath: externalCamoufoxExecutable,
camoufoxCacheDir: camoufoxCacheDir(),
prometheusEnabled: process.env.PROMETHEUS_ENABLED === '1' || process.env.PROMETHEUS_ENABLED === 'true',
proxy: {
strategy: inferProxyStrategy(process.env.PROXY_STRATEGY || ''),
providerName: process.env.PROXY_PROVIDER || 'decodo',
host: process.env.PROXY_HOST || '',
port: process.env.PROXY_PORT || '',
ports: parseProxyPorts(process.env.PROXY_PORTS, process.env.PROXY_PORT),
username: process.env.PROXY_USERNAME || '',
password: process.env.PROXY_PASSWORD || '',
backconnectHost: process.env.PROXY_BACKCONNECT_HOST || '',
backconnectPort: parseInt(process.env.PROXY_BACKCONNECT_PORT || '7000', 10),
country: process.env.PROXY_COUNTRY || '',
state: process.env.PROXY_STATE || '',
city: process.env.PROXY_CITY || '',
zip: process.env.PROXY_ZIP || '',
sessionDurationMinutes: parseInt(process.env.PROXY_SESSION_DURATION_MINUTES || '10', 10),
},
// Env vars forwarded to the server subprocess
serverEnv: {
PATH: process.env.PATH,
HOME: process.env.HOME,
NODE_ENV: process.env.NODE_ENV,
CAMOFOX_ADMIN_KEY: process.env.CAMOFOX_ADMIN_KEY,
CAMOFOX_API_KEY: process.env.CAMOFOX_API_KEY,
CAMOFOX_ACCESS_KEY: process.env.CAMOFOX_ACCESS_KEY,
CAMOFOX_COOKIES_DIR: process.env.CAMOFOX_COOKIES_DIR,
CAMOFOX_TRACES_DIR: process.env.CAMOFOX_TRACES_DIR,
CAMOFOX_TRACES_MAX_BYTES: process.env.CAMOFOX_TRACES_MAX_BYTES,
CAMOFOX_TRACES_TTL_HOURS: process.env.CAMOFOX_TRACES_TTL_HOURS,
CAMOUFOX_EXECUTABLE: process.env.CAMOUFOX_EXECUTABLE,
CAMOUFOX_EXECUTABLE_PATH: process.env.CAMOUFOX_EXECUTABLE_PATH,
CAMOFOX_EXECUTABLE_PATH: process.env.CAMOFOX_EXECUTABLE_PATH,
PROXY_STRATEGY: process.env.PROXY_STRATEGY,
PROXY_PROVIDER: process.env.PROXY_PROVIDER,
PROXY_HOST: process.env.PROXY_HOST,
PROXY_PORT: process.env.PROXY_PORT,
PROXY_PORTS: process.env.PROXY_PORTS,
PROXY_USERNAME: process.env.PROXY_USERNAME,
PROXY_PASSWORD: process.env.PROXY_PASSWORD,
PROXY_BACKCONNECT_HOST: process.env.PROXY_BACKCONNECT_HOST,
PROXY_BACKCONNECT_PORT: process.env.PROXY_BACKCONNECT_PORT,
PROXY_COUNTRY: process.env.PROXY_COUNTRY,
PROXY_STATE: process.env.PROXY_STATE,
PROXY_CITY: process.env.PROXY_CITY,
PROXY_ZIP: process.env.PROXY_ZIP,
PROXY_SESSION_DURATION_MINUTES: process.env.PROXY_SESSION_DURATION_MINUTES,
},
// Crash reporter (opt-in, reports sent to Cloudflare Worker relay)
crashReportEnabled: process.env.CAMOFOX_CRASH_REPORT_ENABLED !== 'false',
crashReportUrl: process.env.CAMOFOX_CRASH_REPORT_URL || '',
crashReportRepo: process.env.CAMOFOX_CRASH_REPORT_REPO,
crashReportRateLimit: parseInt(process.env.CAMOFOX_CRASH_REPORT_RATE_LIMIT, 10) || 10,
crashReporterConfig: readCrashReporterConfig(),
sentryDsn: process.env.SENTRY_DSN || '',
};
}
export { loadConfig };