camofox-browser/lib/persistence.js

90 lines
2.7 KiB
JavaScript

import crypto from 'node:crypto';
import fs from 'node:fs/promises';
import path from 'node:path';
function getUserPersistencePaths(profileDir, userId) {
const rootDir = path.resolve(profileDir);
const safeUserDir = crypto
.createHash('sha256')
.update(String(userId))
.digest('hex')
.slice(0, 32);
const userDir = path.join(rootDir, safeUserDir);
return {
rootDir,
userDir,
storageStatePath: path.join(userDir, 'storage-state.json'),
metaPath: path.join(userDir, 'meta.json'),
};
}
async function loadPersistedStorageState(profileDir, userId, logger = console) {
if (!profileDir) return undefined;
const { storageStatePath } = getUserPersistencePaths(profileDir, userId);
try {
const raw = await fs.readFile(storageStatePath, 'utf8');
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== 'object') return undefined;
if (!Array.isArray(parsed.cookies)) return undefined;
if (parsed.origins !== undefined && !Array.isArray(parsed.origins)) return undefined;
return storageStatePath;
} catch (err) {
if (err?.code === 'ENOENT') return undefined;
logger?.warn?.('failed to load persisted storage state', {
userId: String(userId),
storageStatePath,
error: err?.message || String(err),
});
return undefined;
}
}
async function persistStorageState({ profileDir, userId, context, logger = console }) {
if (!profileDir || !context) {
return { persisted: false, reason: 'disabled' };
}
const { userDir, storageStatePath, metaPath } = getUserPersistencePaths(profileDir, userId);
const suffix = `.tmp-${process.pid}-${Date.now()}`;
const tmpStoragePath = `${storageStatePath}${suffix}`;
const tmpMetaPath = `${metaPath}${suffix}`;
try {
await fs.mkdir(userDir, { recursive: true });
await context.storageState({ path: tmpStoragePath });
await fs.rename(tmpStoragePath, storageStatePath);
await fs.writeFile(
tmpMetaPath,
JSON.stringify(
{
userId: String(userId),
updatedAt: new Date().toISOString(),
storageStatePath,
},
null,
2
)
);
await fs.rename(tmpMetaPath, metaPath);
return { persisted: true, userDir, storageStatePath, metaPath };
} catch (err) {
await fs.unlink(tmpStoragePath).catch(() => {});
await fs.unlink(tmpMetaPath).catch(() => {});
logger?.warn?.('failed to persist storage state', {
userId: String(userId),
storageStatePath,
error: err?.message || String(err),
});
return { persisted: false, reason: 'error', error: err };
}
}
export {
getUserPersistencePaths,
loadPersistedStorageState,
persistStorageState,
};