141 lines
4.5 KiB
JavaScript
141 lines
4.5 KiB
JavaScript
|
|
/**
|
|||
|
|
* puppeteer-api 共享常量与 Chrome 启动环境工具
|
|||
|
|
*/
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
const puppeteer = require('puppeteer-extra');
|
|||
|
|
|
|||
|
|
/** 默认 Chrome 可执行文件路径(生产环境可通过环境变量覆盖) */
|
|||
|
|
const DEFAULT_CHROME_EXECUTABLE = '/www/wwwroot/puppeteer-api/.cache/puppeteer/chrome/linux-149.0.7827.22/chrome-linux64/chrome';
|
|||
|
|
|
|||
|
|
/** 默认 User-Agent,与 standard / real 双轨保持一致 */
|
|||
|
|
const DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
|||
|
|
|
|||
|
|
/** Chrome 启动基础参数:多实例 headless 场景下复用 */
|
|||
|
|
const BASE_LAUNCH_ARGS = [
|
|||
|
|
'--no-sandbox',
|
|||
|
|
'--disable-setuid-sandbox',
|
|||
|
|
'--disable-dev-shm-usage',
|
|||
|
|
'--disable-gpu',
|
|||
|
|
'--disable-extensions',
|
|||
|
|
'--disable-background-networking',
|
|||
|
|
'--disable-software-rasterizer',
|
|||
|
|
'--no-first-run',
|
|||
|
|
'--no-default-browser-check',
|
|||
|
|
'--disable-breakpad',
|
|||
|
|
'--disable-crash-reporter',
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
/** puppeteer-api 专用运行时目录(与 server.js 同级 .runtime) */
|
|||
|
|
const RUNTIME_DIR = path.join(__dirname, '..', '.runtime');
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 确保 .runtime 子目录存在且 www 用户可写
|
|||
|
|
* @param {string} name
|
|||
|
|
* @returns {string}
|
|||
|
|
*/
|
|||
|
|
function ensureRuntimeSubdir(name) {
|
|||
|
|
const dir = path.join(RUNTIME_DIR, name);
|
|||
|
|
fs.mkdirSync(dir, { recursive: true, mode: 0o777 });
|
|||
|
|
return dir;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 解析 Chrome 可执行文件路径
|
|||
|
|
* @returns {string}
|
|||
|
|
*/
|
|||
|
|
function resolveChromeExecutable() {
|
|||
|
|
const candidates = [
|
|||
|
|
process.env.PUPPETEER_EXECUTABLE_PATH,
|
|||
|
|
process.env.CHROME_EXECUTABLE,
|
|||
|
|
DEFAULT_CHROME_EXECUTABLE,
|
|||
|
|
].filter(Boolean);
|
|||
|
|
|
|||
|
|
for (const candidate of candidates) {
|
|||
|
|
if (fs.existsSync(candidate)) {
|
|||
|
|
return candidate;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const bundled = puppeteer.executablePath();
|
|||
|
|
if (bundled && fs.existsSync(bundled)) {
|
|||
|
|
return bundled;
|
|||
|
|
}
|
|||
|
|
} catch (_) {
|
|||
|
|
/* puppeteer 未内置浏览器时忽略 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return candidates[0] || DEFAULT_CHROME_EXECUTABLE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生产 Linux 常注入无效 DBUS 地址;删除而非设为 /dev/null
|
|||
|
|
* @returns {NodeJS.ProcessEnv}
|
|||
|
|
*/
|
|||
|
|
function buildBrowserLaunchEnv() {
|
|||
|
|
const env = { ...process.env };
|
|||
|
|
delete env.DBUS_SESSION_BUS_ADDRESS;
|
|||
|
|
delete env.DBUS_SYSTEM_BUS_ADDRESS;
|
|||
|
|
|
|||
|
|
const xdgRuntime = ensureRuntimeSubdir('xdg-runtime');
|
|||
|
|
env.XDG_RUNTIME_DIR = xdgRuntime;
|
|||
|
|
env.XDG_CONFIG_HOME = ensureRuntimeSubdir('xdg-config');
|
|||
|
|
env.XDG_CACHE_HOME = ensureRuntimeSubdir('xdg-cache');
|
|||
|
|
env.TMPDIR = ensureRuntimeSubdir('tmp');
|
|||
|
|
|
|||
|
|
return env;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 校验 Chrome 可执行文件存在
|
|||
|
|
* @returns {string}
|
|||
|
|
*/
|
|||
|
|
function assertChromeExecutable() {
|
|||
|
|
const executablePath = resolveChromeExecutable();
|
|||
|
|
if (!fs.existsSync(executablePath)) {
|
|||
|
|
throw new Error(
|
|||
|
|
`Chrome 可执行文件不存在: ${executablePath}。请在 puppeteer-api 目录执行: npx puppeteer browsers install chrome`
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
return executablePath;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 并发与超时配置(可通过环境变量覆盖) */
|
|||
|
|
const MAX_CONCURRENT_BROWSERS = Math.max(1, parseInt(process.env.MAX_CONCURRENT_BROWSERS || '4', 10));
|
|||
|
|
const MAX_CONCURRENT_BROWSERS_REAL = Math.max(1, parseInt(process.env.MAX_CONCURRENT_BROWSERS_REAL || '2', 10));
|
|||
|
|
const QUEUE_TIMEOUT_MS = Math.max(5000, parseInt(process.env.QUEUE_TIMEOUT_MS || '120000', 10));
|
|||
|
|
const API_INTERCEPT_TIMEOUT_MS = Math.max(10000, parseInt(process.env.API_INTERCEPT_TIMEOUT_MS || '30000', 10));
|
|||
|
|
const API_INTERCEPT_TIMEOUT_REAL_MS = Math.max(
|
|||
|
|
API_INTERCEPT_TIMEOUT_MS,
|
|||
|
|
parseInt(process.env.API_INTERCEPT_TIMEOUT_REAL_MS || '90000', 10)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/** Captcha API 配置 */
|
|||
|
|
const CAPTCHA_PROVIDER = (process.env.CAPTCHA_PROVIDER || 'capsolver').toLowerCase();
|
|||
|
|
const CAPTCHA_API_KEY = process.env.CAPTCHA_API_KEY || '';
|
|||
|
|
const CAPTCHA_MAX_WAIT_MS = Math.max(30000, parseInt(process.env.CAPTCHA_MAX_WAIT_MS || '120000', 10));
|
|||
|
|
|
|||
|
|
/** 会话 Cookie 持久化 TTL(毫秒) */
|
|||
|
|
const SESSION_TTL_MS = Math.max(60000, parseInt(process.env.SESSION_TTL_MS || '1800000', 10));
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
DEFAULT_CHROME_EXECUTABLE,
|
|||
|
|
DEFAULT_UA,
|
|||
|
|
BASE_LAUNCH_ARGS,
|
|||
|
|
RUNTIME_DIR,
|
|||
|
|
ensureRuntimeSubdir,
|
|||
|
|
resolveChromeExecutable,
|
|||
|
|
buildBrowserLaunchEnv,
|
|||
|
|
assertChromeExecutable,
|
|||
|
|
MAX_CONCURRENT_BROWSERS,
|
|||
|
|
MAX_CONCURRENT_BROWSERS_REAL,
|
|||
|
|
QUEUE_TIMEOUT_MS,
|
|||
|
|
API_INTERCEPT_TIMEOUT_MS,
|
|||
|
|
API_INTERCEPT_TIMEOUT_REAL_MS,
|
|||
|
|
CAPTCHA_PROVIDER,
|
|||
|
|
CAPTCHA_API_KEY,
|
|||
|
|
CAPTCHA_MAX_WAIT_MS,
|
|||
|
|
SESSION_TTL_MS,
|
|||
|
|
};
|