后端迁移会话QQ登录模块
This commit is contained in:
@@ -13,14 +13,18 @@ type LocatorLike = {
|
||||
count: () => Promise<number>
|
||||
first: () => LocatorLike
|
||||
click: (options?: { timeout?: number }) => Promise<void>
|
||||
waitFor: (options?: { state?: string; timeout?: number }) => Promise<void>
|
||||
screenshot: (options: { path: string }) => Promise<void>
|
||||
}
|
||||
|
||||
type BrowserPageLike = {
|
||||
frames: () => any[]
|
||||
locator: (selector: string) => LocatorLike
|
||||
waitForTimeout: (milliseconds: number) => Promise<void>
|
||||
evaluate: <T = any>(callback: any) => Promise<T>
|
||||
reload: (options?: { waitUntil?: string }) => Promise<unknown>
|
||||
goto: (url: string, options?: { waitUntil?: string }) => Promise<unknown>
|
||||
context: () => any
|
||||
}
|
||||
|
||||
type SessionForQrCapture = {
|
||||
|
||||
+52
-13
@@ -1,4 +1,4 @@
|
||||
// @ts-nocheck
|
||||
/// <reference lib="dom" />
|
||||
|
||||
import fs from 'node:fs/promises'
|
||||
|
||||
@@ -6,9 +6,48 @@ import { downloadRemoteQrImage, logBrowserSessionDebug, waitForFrame } from './s
|
||||
|
||||
const QQ_QR_TARGET_SIZE = 144
|
||||
|
||||
export async function ensureQqLoginReady(page) {
|
||||
type LocatorLike = {
|
||||
count: () => Promise<number>
|
||||
click: (options?: { timeout?: number }) => Promise<void>
|
||||
waitFor: (options?: { state?: string; timeout?: number }) => Promise<void>
|
||||
screenshot: (options: { path: string }) => Promise<void>
|
||||
first: () => LocatorLike
|
||||
}
|
||||
|
||||
type FrameLike = {
|
||||
url: () => string
|
||||
locator: (selector: string) => LocatorLike
|
||||
waitForTimeout: (milliseconds: number) => Promise<void>
|
||||
evaluate: <T = any>(callback: any) => Promise<T>
|
||||
}
|
||||
|
||||
type PageLike = {
|
||||
frames: () => FrameLike[]
|
||||
waitForTimeout: (milliseconds: number) => Promise<void>
|
||||
locator: (selector: string) => LocatorLike
|
||||
context: () => {
|
||||
request: {
|
||||
get: (url: string, options?: any) => Promise<{
|
||||
ok: () => boolean
|
||||
status: () => number
|
||||
body: () => Promise<Buffer>
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type QqQrState = {
|
||||
visible: boolean
|
||||
qrVisible?: boolean
|
||||
scanned: boolean
|
||||
expired: boolean
|
||||
message: string
|
||||
frameUrl?: string
|
||||
}
|
||||
|
||||
export async function ensureQqLoginReady(page: PageLike): Promise<void> {
|
||||
try {
|
||||
const frame = await waitForFrame(
|
||||
const frame = await waitForFrame<FrameLike>(
|
||||
page,
|
||||
(item) => item.url().includes('xui.ptlogin2.qq.com/cgi-bin/xlogin'),
|
||||
10_000,
|
||||
@@ -26,8 +65,8 @@ export async function ensureQqLoginReady(page) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function captureQqQrImage(page, qrImagePath) {
|
||||
const frame = await waitForFrame(
|
||||
export async function captureQqQrImage(page: PageLike, qrImagePath: string): Promise<void> {
|
||||
const frame = await waitForFrame<FrameLike>(
|
||||
page,
|
||||
(item) => item.url().includes('xui.ptlogin2.qq.com/cgi-bin/xlogin'),
|
||||
10_000,
|
||||
@@ -45,11 +84,11 @@ export async function captureQqQrImage(page, qrImagePath) {
|
||||
const body = await downloadRemoteQrImage(page, effectiveQrUrl, qrImagePath, frame.url(), 'qq')
|
||||
logBrowserSessionDebug('qq.capture.downloadResult', {
|
||||
qrUrl: effectiveQrUrl,
|
||||
downloaded: Boolean(body?.length),
|
||||
downloaded: Buffer.isBuffer(body) && body.length > 0,
|
||||
qrImagePath,
|
||||
})
|
||||
|
||||
if (body?.length) {
|
||||
if (Buffer.isBuffer(body) && body.length > 0) {
|
||||
await fs.writeFile(qrImagePath, body)
|
||||
return
|
||||
}
|
||||
@@ -71,10 +110,10 @@ export async function captureQqQrImage(page, qrImagePath) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function findQqQrLocator(page, frame = null) {
|
||||
export async function findQqQrLocator(page: PageLike, frame: FrameLike | null = null): Promise<LocatorLike> {
|
||||
const effectiveFrame =
|
||||
frame ||
|
||||
await waitForFrame(page, (item) => item.url().includes('xui.ptlogin2.qq.com/cgi-bin/xlogin'))
|
||||
await waitForFrame<FrameLike>(page, (item) => item.url().includes('xui.ptlogin2.qq.com/cgi-bin/xlogin'))
|
||||
const locator = effectiveFrame.locator('#qrlogin_img')
|
||||
await locator.waitFor({ state: 'visible', timeout: 15_000 })
|
||||
logBrowserSessionDebug('qq.findQrLocator.visibleReady', {
|
||||
@@ -83,7 +122,7 @@ export async function findQqQrLocator(page, frame = null) {
|
||||
return locator
|
||||
}
|
||||
|
||||
export async function extractQqQrState(page) {
|
||||
export async function extractQqQrState(page: Pick<PageLike, 'frames'>): Promise<QqQrState> {
|
||||
const frame = page.frames().find((item) => item.url().includes('xui.ptlogin2.qq.com/cgi-bin/xlogin'))
|
||||
|
||||
if (!frame) {
|
||||
@@ -98,7 +137,7 @@ export async function extractQqQrState(page) {
|
||||
try {
|
||||
const { qrVisible, bodyText } = await frame
|
||||
.evaluate(() => {
|
||||
const isVisible = (element) => {
|
||||
const isVisible = (element: Element | null) => {
|
||||
if (!(element instanceof HTMLElement)) {
|
||||
return false
|
||||
}
|
||||
@@ -151,7 +190,7 @@ export async function extractQqQrState(page) {
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveQqQrImageUrl(frame) {
|
||||
async function resolveQqQrImageUrl(frame: FrameLike): Promise<string> {
|
||||
return frame
|
||||
.evaluate(() => {
|
||||
const qrImage = document.querySelector('#qrlogin_img')
|
||||
@@ -176,7 +215,7 @@ async function resolveQqQrImageUrl(frame) {
|
||||
.catch(() => '')
|
||||
}
|
||||
|
||||
function upgradeQqQrImageUrl(qrUrl) {
|
||||
function upgradeQqQrImageUrl(qrUrl: string): string {
|
||||
if (!qrUrl) {
|
||||
return ''
|
||||
}
|
||||
@@ -472,6 +472,14 @@
|
||||
- `npm run typecheck`
|
||||
- `npm run build`
|
||||
- `npm test` 共 139 个用例通过
|
||||
98. 腾讯浏览器 QQ 登录模块迁移到 `.ts`:
|
||||
- `src/services/session/session-qq.ts`
|
||||
99. QQ 登录 iframe、二维码 URL 提取 / 升级、远程下载 fallback、二维码 locator 与扫码状态已显式类型化
|
||||
100. Docker 内验证通过:
|
||||
- `src/services/session/session.test.js` 共 14 个用例通过
|
||||
- `npm run typecheck`
|
||||
- `npm run build`
|
||||
- `npm test` 共 139 个用例通过
|
||||
|
||||
## 下一步建议
|
||||
|
||||
|
||||
Reference in New Issue
Block a user