后端迁移会话支撑模块
This commit is contained in:
+14
-3
@@ -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)
|
||||
}
|
||||
+40
-8
@@ -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 (
|
||||
+25
-2
@@ -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) {
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
// @ts-check
|
||||
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
Reference in New Issue
Block a user