后端迁移会话支撑模块

This commit is contained in:
yml
2026-05-21 15:24:03 +08:00
parent 73b0ee211f
commit 4453f17678
5 changed files with 90 additions and 15 deletions
@@ -5,8 +5,19 @@ const TENCENT_COOKIE_URLS = [
'https://milo.qq.com',
]
export function normalizeTencentCookieMap(cookies = []) {
const cookieMap = {}
type BrowserCookie = {
name?: string
value?: string
}
type BrowserContextLike = {
cookies: (urls?: string[]) => Promise<BrowserCookie[]>
}
export type TencentCookieMap = Record<string, string>
export function normalizeTencentCookieMap(cookies: BrowserCookie[] = []): TencentCookieMap {
const cookieMap: TencentCookieMap = {}
for (const cookie of cookies) {
if (!cookie?.name) {
@@ -32,7 +43,7 @@ export function normalizeTencentCookieMap(cookies = []) {
return cookieMap
}
export async function loadCookieMapFromContext(browserContext) {
export async function loadCookieMapFromContext(browserContext: BrowserContextLike): Promise<TencentCookieMap> {
const cookies = await browserContext.cookies(TENCENT_COOKIE_URLS)
return normalizeTencentCookieMap(cookies)
}
@@ -1,11 +1,33 @@
// @ts-nocheck
import { runtimeConfig } from '../../config/runtime.js'
import { logDebug } from '../../utils/logger.js'
const SESSION_DEBUG_ENABLED = Boolean(runtimeConfig.session.debug)
export function logBrowserSessionDebug(scope, detail) {
type FrameLike = unknown
type PageWithFrames = {
frames: () => FrameLike[]
waitForTimeout: (milliseconds: number) => Promise<void>
}
type QrDownloadResponse = {
ok: () => boolean
status: () => number
body: () => Promise<Buffer>
}
type PageWithRequestContext = {
context: () => {
request: {
get: (url: string, options?: {
failOnStatusCode?: boolean
headers?: Record<string, string>
}) => Promise<QrDownloadResponse>
}
}
}
export function logBrowserSessionDebug(scope: string, detail?: unknown): void {
if (!SESSION_DEBUG_ENABLED) {
return
}
@@ -13,13 +35,17 @@ export function logBrowserSessionDebug(scope, detail) {
logDebug('[browser/session]', scope, detail)
}
export async function waitForFrame(page, predicate, timeoutMs = 15_000) {
export async function waitForFrame<TFrame extends FrameLike>(
page: PageWithFrames,
predicate: (frame: TFrame) => boolean,
timeoutMs = 15_000,
): Promise<TFrame> {
const startedAt = Date.now()
while (Date.now() - startedAt < timeoutMs) {
const matched = page.frames().find((frame) => {
const matched = page.frames().find((frame): frame is TFrame => {
try {
return predicate(frame)
return predicate(frame as TFrame)
} catch {
return false
}
@@ -35,7 +61,13 @@ export async function waitForFrame(page, predicate, timeoutMs = 15_000) {
throw new Error('未找到登录二维码 frame')
}
export async function downloadRemoteQrImage(page, qrUrl, qrImagePath, referer = '', loginType = 'unknown') {
export async function downloadRemoteQrImage(
page: PageWithRequestContext,
qrUrl: string,
qrImagePath: string,
referer = '',
loginType = 'unknown',
): Promise<Buffer | false | null> {
try {
const response = await page.context().request.get(qrUrl, {
failOnStatusCode: false,
@@ -68,7 +100,7 @@ export async function downloadRemoteQrImage(page, qrUrl, qrImagePath, referer =
}
}
export function isRetryableQrCaptureError(error) {
export function isRetryableQrCaptureError(error: unknown): boolean {
const message = error instanceof Error ? error.message : String(error || '')
return (
@@ -2,7 +2,30 @@ import path from 'node:path'
import { logBrowserSessionDebug } from './session-login-shared.js'
export function resolveReviewSignature(session, activityInfo) {
type BrowserPageLike = {
screenshot: (options: { path: string; fullPage?: boolean }) => Promise<unknown>
}
type ReviewSession = {
sessionId: string
loginType: string
sessionDir: string
page: BrowserPageLike
lastReview?: {
screenshotPath?: string
signature?: string
[key: string]: unknown
} | null
}
type ActivityInfoForReview = {
role?: {
roleId?: string
roleName?: string
}
}
export function resolveReviewSignature(session: Pick<ReviewSession, 'loginType'>, activityInfo: ActivityInfoForReview) {
const roleId = String(activityInfo?.role?.roleId || '').trim()
const roleName = String(activityInfo?.role?.roleName || '').trim()
@@ -13,7 +36,7 @@ export function resolveReviewSignature(session, activityInfo) {
}
}
export async function ensureReviewScreenshot(session, activityInfo) {
export async function ensureReviewScreenshot(session: ReviewSession, activityInfo: ActivityInfoForReview): Promise<boolean> {
const { roleId, roleName, signature } = resolveReviewSignature(session, activityInfo)
if (session.lastReview?.screenshotPath && session.lastReview?.signature === signature) {
@@ -1,5 +1,3 @@
// @ts-check
import path from 'node:path'
import { fileURLToPath } from 'node:url'