feat(browser): fixed Windows identity and per-site egress in umt-cookies plugin (BR-4)
- browser:launching hook generates a Windows fingerprint once (camoufox-js, os=windows), persists it to /root/.camofox/umt-identity.json and reuses it on every launch; Firefox/Camoufox upgrades only bump UA/version fields - Windows fontconfig with absolute font dir (upstream path was broken) - GET /umt/identity (public UA/OS/version) and PUT /umt/sessions/:id/proxy (per-context proxy; Firefox no longer uses container HTTP_PROXY env) - adapter identity()/setEgress(), splitBrowserOnlyCookies for cf_clearance/__cf_bm Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
6663afce7e
commit
ebd07c9230
8 files changed
+746
-5
No files matched your search
@@ -126,6 +126,22 @@ export type MockCamofox = MockHttp & {
|
||||
restart(opts?: { loseProfiles?: boolean }): void;
|
||||
/** 当前实例标识 */
|
||||
readonly instanceId: string;
|
||||
/** 各 profile 的出口(`PUT /umt/sessions/:userId/proxy`,BR-4):null = 直连 */
|
||||
egress: Map<string, { server: string; username?: string; password?: string } | null>;
|
||||
};
|
||||
|
||||
/** mock 的固定浏览器身份(`GET /umt/identity`,BR-4) */
|
||||
export const MOCK_CAMOFOX_IDENTITY = {
|
||||
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:152.0) Gecko/20100101 Firefox/152.0",
|
||||
os: "windows",
|
||||
platform: "Win32",
|
||||
firefoxVersion: "152",
|
||||
camoufoxVersion: "152.0.4-mock",
|
||||
fingerprintId: "mockfp000001",
|
||||
revision: 1,
|
||||
schema: 1,
|
||||
createdAt: "2026-09-30T00:00:00.000Z",
|
||||
updatedAt: "2026-09-30T00:00:00.000Z",
|
||||
};
|
||||
|
||||
/** 1x1 透明 PNG */
|
||||
@@ -331,6 +347,7 @@ export async function createMockCamofox(opts: MockCamofoxOptions = {}): Promise<
|
||||
const actions: MockCamofoxAction[] = [];
|
||||
const loggedIn = new Set<string>();
|
||||
const cookieJars = new Map<string, MockCamofoxCookie[]>();
|
||||
const egress = new Map<string, { server: string; username?: string; password?: string } | null>();
|
||||
const cookieApi = opts.cookieApi ?? "plugin";
|
||||
let instanceId = `inst-${crypto.randomUUID()}`;
|
||||
/** 重启前存在过的 tabId:之后访问返回 410 */
|
||||
@@ -795,6 +812,21 @@ export async function createMockCamofox(opts: MockCamofoxOptions = {}): Promise<
|
||||
},
|
||||
],
|
||||
[/^\/umt\/instance$/, () => (cookieApi === "plugin" ? { json: { instanceId } } : err(404, "no route"))],
|
||||
[
|
||||
/^\/umt\/identity$/,
|
||||
() => (cookieApi === "plugin" ? { json: { ...MOCK_CAMOFOX_IDENTITY } } : err(404, "no route")),
|
||||
],
|
||||
[
|
||||
/^\/umt\/sessions\/([^/]+)\/proxy$/,
|
||||
(m, _q, req, b) => {
|
||||
if (cookieApi !== "plugin" || req.method !== "PUT") return err(404, "no route");
|
||||
const userId = decodeURIComponent(m[1]!);
|
||||
const p = b.proxy as { server?: string } | null | undefined;
|
||||
if (p !== null && (!p || typeof p.server !== "string")) return err(400, "proxy must be {server} or null");
|
||||
egress.set(userId, p === null ? null : { ...(p as { server: string }) });
|
||||
return { json: { ok: true, userId, egress: p ? "proxy" : "direct", mode: "site", restarted: false } };
|
||||
},
|
||||
],
|
||||
[
|
||||
/^\/umt\/sessions\/([^/]+)\/cookies$/,
|
||||
(m, q, req, b) => {
|
||||
@@ -858,6 +890,7 @@ export async function createMockCamofox(opts: MockCamofoxOptions = {}): Promise<
|
||||
actions,
|
||||
loggedIn,
|
||||
cookieJars,
|
||||
egress,
|
||||
expireLogin(userId?: string) {
|
||||
if (userId) loggedIn.delete(userId);
|
||||
else loggedIn.clear();
|
||||
|
||||
Reference in new issue
Block a user