Files
UHD_Media_Tool/packages/db/migrations/0056_browser_identity.ts
T
rootandClaude Opus 5.5 adc853aa64 feat(sites): site requests follow browser UA, smooth UA switch, CF cookies browser-only (BR-4)
- every outbound site request (adapter list/search/detail/download/health,
  RSS, login, user data) carries the account UA via SourcesRuntime.siteFetch;
  no more Deno default UA
- priority: account manual > pending > global setting > browser identity
  (fallback: same-shape Windows Firefox UA); settings UA default is empty
- migration 0056: existing cookie/password accounts keep their old UA as
  ua_pending until re-login or browser cookie write-back switches cookie+UA
  in one transaction; browser_provider.identity caches the browser identity
- cf_clearance/__cf_bm stripped on browser write-back (names kept only)
- browser sessions get per-site egress (same proxy as HTTP, or direct)
- site account page shows UA source; raw UA in tech info

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 03:17:13 +08:00

48 lines
2.7 KiB
TypeScript

import { type Kysely, sql } from "kysely";
import { execDdl } from "../src/migration-helpers.ts";
// BR-4 浏览器 UA 与指纹统一(docs/runbook/browser.md §10):
// - site_account.ua_pending:待切换。存量 Cookie / 账密账号的登录态是用旧缺省 UA(Windows Chrome 140)取得的,不立即换 UA,
// 记下旧 UA 继续用;下次重新登录或浏览器回写 Cookie 时与 Cookie 一起切到浏览器 UA(清空本列)。
// 账号手动填了 UA 的不动(手动优先);API Key / Passkey 账号与网页登录态无关,直接跟随浏览器。
// - browser_provider.identity:浏览器服务固定身份的公开部分(UA / OS / 版本,umt 插件 GET /umt/identity)+ 读取时间。
// - settings.sites.userAgent:仍是旧缺省值(Chrome 140)时删掉该键 → 新缺省空串 = 跟随浏览器;用户改过的保留为全局 UA。
// down 只删列;settings 不回填(旧版本读到缺省即 Chrome 140)。
/** BR-4 之前的缺省 UA(与 contracts LEGACY_SITE_USER_AGENT 相同;迁移不依赖应用包) */
export const LEGACY_UA =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36";
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
export async function up(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`alter table site_account add column ua_pending text;
alter table browser_provider add column identity jsonb;`,
);
const row = await db.selectFrom("settings").select("value").where("key", "=", "sites").executeTakeFirst();
const stored = (row?.value as { userAgent?: unknown } | undefined)?.userAgent;
const custom = typeof stored === "string" && stored.trim() && stored.trim() !== LEGACY_UA ? stored.trim() : null;
// 存量账号取得登录态时用的 UA:设置里改过就是改过的值,否则是旧缺省
const previous = custom ?? LEGACY_UA;
await db
.updateTable("site_account")
.set({ ua_pending: previous })
.where("auth_kind", "in", ["cookie", "password"])
.where((eb) => eb.or([eb("user_agent", "is", null), eb("user_agent", "=", "")]))
.where((eb) => eb.or([eb("secret_ref", "is not", null), eb("session_secret_ref", "is not", null)]))
.execute();
if (typeof stored === "string" && stored.trim() === LEGACY_UA) {
await db.updateTable("settings").set({ value: sql`value - 'userAgent'` }).where("key", "=", "sites").execute();
}
}
// biome-ignore lint/suspicious/noExplicitAny: 同上
export async function down(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`alter table site_account drop column if exists ua_pending;
alter table browser_provider drop column if exists identity;`,
);
}