后端迁移会话支撑模块

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', 'https://milo.qq.com',
] ]
export function normalizeTencentCookieMap(cookies = []) { type BrowserCookie = {
const cookieMap = {} 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) { for (const cookie of cookies) {
if (!cookie?.name) { if (!cookie?.name) {
@@ -32,7 +43,7 @@ export function normalizeTencentCookieMap(cookies = []) {
return cookieMap return cookieMap
} }
export async function loadCookieMapFromContext(browserContext) { export async function loadCookieMapFromContext(browserContext: BrowserContextLike): Promise<TencentCookieMap> {
const cookies = await browserContext.cookies(TENCENT_COOKIE_URLS) const cookies = await browserContext.cookies(TENCENT_COOKIE_URLS)
return normalizeTencentCookieMap(cookies) return normalizeTencentCookieMap(cookies)
} }
@@ -1,11 +1,33 @@
// @ts-nocheck
import { runtimeConfig } from '../../config/runtime.js' import { runtimeConfig } from '../../config/runtime.js'
import { logDebug } from '../../utils/logger.js' import { logDebug } from '../../utils/logger.js'
const SESSION_DEBUG_ENABLED = Boolean(runtimeConfig.session.debug) 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) { if (!SESSION_DEBUG_ENABLED) {
return return
} }
@@ -13,13 +35,17 @@ export function logBrowserSessionDebug(scope, detail) {
logDebug('[browser/session]', 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() const startedAt = Date.now()
while (Date.now() - startedAt < timeoutMs) { while (Date.now() - startedAt < timeoutMs) {
const matched = page.frames().find((frame) => { const matched = page.frames().find((frame): frame is TFrame => {
try { try {
return predicate(frame) return predicate(frame as TFrame)
} catch { } catch {
return false return false
} }
@@ -35,7 +61,13 @@ export async function waitForFrame(page, predicate, timeoutMs = 15_000) {
throw new Error('未找到登录二维码 frame') 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 { try {
const response = await page.context().request.get(qrUrl, { const response = await page.context().request.get(qrUrl, {
failOnStatusCode: false, 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 || '') const message = error instanceof Error ? error.message : String(error || '')
return ( return (
@@ -2,7 +2,30 @@ import path from 'node:path'
import { logBrowserSessionDebug } from './session-login-shared.js' 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 roleId = String(activityInfo?.role?.roleId || '').trim()
const roleName = String(activityInfo?.role?.roleName || '').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) const { roleId, roleName, signature } = resolveReviewSignature(session, activityInfo)
if (session.lastReview?.screenshotPath && session.lastReview?.signature === signature) { if (session.lastReview?.screenshotPath && session.lastReview?.signature === signature) {
@@ -1,5 +1,3 @@
// @ts-check
import path from 'node:path' import path from 'node:path'
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
+11
View File
@@ -436,6 +436,17 @@
- `npm run typecheck` - `npm run typecheck`
- `npm run build` - `npm run build`
- `npm test` 共 139 个用例通过 - `npm test` 共 139 个用例通过
86. 腾讯浏览器会话 cookie / review / 登录共享 / 服务配置模块迁移到 `.ts`
- `src/services/session/session-cookie-map.ts`
- `src/services/session/session-review.ts`
- `src/services/session/session-login-shared.ts`
- `src/services/session/session-service-config.ts`
87. cookie map、review 签名与截图、二维码下载、frame 等待、浏览器会话配置常量已显式类型化
88. Docker 内验证通过:
- `src/services/session/session.test.js` 共 14 个用例通过
- `npm run typecheck`
- `npm run build`
- `npm test` 共 139 个用例通过
## 下一步建议 ## 下一步建议