/** * 按 sessionKey 解析持久 Chrome Profile 目录 */ const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const { RUNTIME_DIR, PERSIST_BROWSER_PROFILE, PROFILE_MAX_AGE_MS } = require('./constants'); const PROFILES_DIR = path.join(RUNTIME_DIR, 'profiles'); /** * @param {string} sessionKey * @param {'standard'|'real'} profile * @returns {string|null} */ function resolveProfileDir(sessionKey, profile = 'standard') { if (!sessionKey || !PERSIST_BROWSER_PROFILE) { return null; } const hash = crypto.createHash('md5').update(String(sessionKey)).digest('hex'); const dir = path.join(PROFILES_DIR, `${hash}-${profile}`); fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); return dir; } function profileExists(sessionKey, profile = 'standard') { const dir = resolveProfileDir(sessionKey, profile); if (!dir) return false; return fs.existsSync(dir); } function pruneStaleProfiles() { if (!PERSIST_BROWSER_PROFILE || PROFILE_MAX_AGE_MS <= 0) { return 0; } if (!fs.existsSync(PROFILES_DIR)) { return 0; } const now = Date.now(); let removed = 0; for (const name of fs.readdirSync(PROFILES_DIR)) { const full = path.join(PROFILES_DIR, name); try { const stat = fs.statSync(full); if (!stat.isDirectory()) continue; if (now - stat.mtimeMs > PROFILE_MAX_AGE_MS) { fs.rmSync(full, { recursive: true, force: true }); removed++; } } catch (_) { /* ignore */ } } return removed; } function getProfileStats() { if (!fs.existsSync(PROFILES_DIR)) { return { profilesDir: PROFILES_DIR, count: 0 }; } const count = fs.readdirSync(PROFILES_DIR).filter((name) => { try { return fs.statSync(path.join(PROFILES_DIR, name)).isDirectory(); } catch (_) { return false; } }).length; return { profilesDir: PROFILES_DIR, count }; } module.exports = { PROFILES_DIR, resolveProfileDir, profileExists, pruneStaleProfiles, getProfileStats, };