后端迁移会话微信登录模块
This commit is contained in:
+67
-15
@@ -1,12 +1,64 @@
|
|||||||
// @ts-nocheck
|
/// <reference lib="dom" />
|
||||||
|
|
||||||
import fs from 'node:fs/promises'
|
import fs from 'node:fs/promises'
|
||||||
|
|
||||||
import { downloadRemoteQrImage, logBrowserSessionDebug, waitForFrame } from './session-login-shared.js'
|
import { downloadRemoteQrImage, logBrowserSessionDebug, waitForFrame } from './session-login-shared.js'
|
||||||
|
|
||||||
export async function ensureWxLoginReady(page) {
|
type LocatorLike = {
|
||||||
|
first: () => LocatorLike
|
||||||
|
waitFor: (options?: { state?: string; timeout?: number }) => Promise<void>
|
||||||
|
screenshot: (options: { path: string }) => Promise<void>
|
||||||
|
isVisible: () => Promise<boolean>
|
||||||
|
click: (options?: { timeout?: number }) => Promise<void>
|
||||||
|
evaluate: <T = unknown>(callback: (element: Element) => T | Promise<T>) => Promise<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
context: () => {
|
||||||
|
request: {
|
||||||
|
get: (url: string, options?: any) => Promise<{
|
||||||
|
ok: () => boolean
|
||||||
|
status: () => number
|
||||||
|
body: () => Promise<Buffer>
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WxLoginViewState = {
|
||||||
|
qrVisible: boolean
|
||||||
|
quickLoginVisible: boolean
|
||||||
|
switchToNormalVisible: boolean
|
||||||
|
qrCandidates: Array<{
|
||||||
|
index: number
|
||||||
|
visible: boolean
|
||||||
|
src: string
|
||||||
|
naturalWidth: number
|
||||||
|
}>
|
||||||
|
bodyText: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WxQrState = {
|
||||||
|
visible: boolean
|
||||||
|
qrVisible?: boolean
|
||||||
|
scanned: boolean
|
||||||
|
expired: boolean
|
||||||
|
message: string
|
||||||
|
frameUrl?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ensureWxLoginReady(page: PageLike): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const frame = await waitForFrame(
|
const frame = await waitForFrame<FrameLike>(
|
||||||
page,
|
page,
|
||||||
(item) => item.url().includes('open.weixin.qq.com/connect/qrconnect'),
|
(item) => item.url().includes('open.weixin.qq.com/connect/qrconnect'),
|
||||||
10_000,
|
10_000,
|
||||||
@@ -52,9 +104,9 @@ export async function ensureWxLoginReady(page) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function captureWxQrImage(page, qrImagePath) {
|
export async function captureWxQrImage(page: PageLike, qrImagePath: string): Promise<void> {
|
||||||
const qrLocator = await findWxQrLocator(page)
|
const qrLocator = await findWxQrLocator(page)
|
||||||
const frame = await waitForFrame(
|
const frame = await waitForFrame<FrameLike>(
|
||||||
page,
|
page,
|
||||||
(item) => item.url().includes('open.weixin.qq.com/connect/qrconnect'),
|
(item) => item.url().includes('open.weixin.qq.com/connect/qrconnect'),
|
||||||
10_000,
|
10_000,
|
||||||
@@ -70,11 +122,11 @@ export async function captureWxQrImage(page, qrImagePath) {
|
|||||||
const downloaded = await downloadRemoteQrImage(page, qrUrl, qrImagePath, frame.url(), 'wx')
|
const downloaded = await downloadRemoteQrImage(page, qrUrl, qrImagePath, frame.url(), 'wx')
|
||||||
logBrowserSessionDebug('wx.capture.downloadResult', {
|
logBrowserSessionDebug('wx.capture.downloadResult', {
|
||||||
qrUrl,
|
qrUrl,
|
||||||
downloaded: Boolean(downloaded?.length),
|
downloaded: Buffer.isBuffer(downloaded) && downloaded.length > 0,
|
||||||
qrImagePath,
|
qrImagePath,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (downloaded?.length) {
|
if (Buffer.isBuffer(downloaded) && downloaded.length > 0) {
|
||||||
await fs.writeFile(qrImagePath, downloaded)
|
await fs.writeFile(qrImagePath, downloaded)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -86,10 +138,10 @@ export async function captureWxQrImage(page, qrImagePath) {
|
|||||||
await qrLocator.screenshot({ path: qrImagePath })
|
await qrLocator.screenshot({ path: qrImagePath })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findWxQrLocator(page) {
|
export async function findWxQrLocator(page: PageLike): Promise<LocatorLike> {
|
||||||
await ensureWxLoginReady(page)
|
await ensureWxLoginReady(page)
|
||||||
|
|
||||||
const frame = await waitForFrame(
|
const frame = await waitForFrame<FrameLike>(
|
||||||
page,
|
page,
|
||||||
(item) => item.url().includes('open.weixin.qq.com/connect/qrconnect'),
|
(item) => item.url().includes('open.weixin.qq.com/connect/qrconnect'),
|
||||||
)
|
)
|
||||||
@@ -109,7 +161,7 @@ export async function findWxQrLocator(page) {
|
|||||||
return locator
|
return locator
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function extractWxQrState(page) {
|
export async function extractWxQrState(page: Pick<PageLike, 'frames'>): Promise<WxQrState> {
|
||||||
const frame = page.frames().find((item) => item.url().includes('open.weixin.qq.com/connect/qrconnect'))
|
const frame = page.frames().find((item) => item.url().includes('open.weixin.qq.com/connect/qrconnect'))
|
||||||
|
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
@@ -146,10 +198,10 @@ export async function extractWxQrState(page) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function inspectWxLoginView(frame) {
|
async function inspectWxLoginView(frame: FrameLike): Promise<WxLoginViewState> {
|
||||||
return frame
|
return frame
|
||||||
.evaluate(() => {
|
.evaluate(() => {
|
||||||
const isVisible = (element) => {
|
const isVisible = (element: Element | null) => {
|
||||||
if (!(element instanceof HTMLElement)) {
|
if (!(element instanceof HTMLElement)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -203,7 +255,7 @@ async function inspectWxLoginView(frame) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function switchWxQuickLoginToQr(frame) {
|
async function switchWxQuickLoginToQr(frame: FrameLike): Promise<boolean> {
|
||||||
const switcherCandidates = [
|
const switcherCandidates = [
|
||||||
frame.locator('.js_switchToNormal:visible').first(),
|
frame.locator('.js_switchToNormal:visible').first(),
|
||||||
frame.locator('.js_switchToNormal').first(),
|
frame.locator('.js_switchToNormal').first(),
|
||||||
@@ -279,10 +331,10 @@ async function switchWxQuickLoginToQr(frame) {
|
|||||||
.catch(() => false)
|
.catch(() => false)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveWxQrImageUrl(frame) {
|
async function resolveWxQrImageUrl(frame: FrameLike): Promise<string> {
|
||||||
return frame
|
return frame
|
||||||
.evaluate(() => {
|
.evaluate(() => {
|
||||||
const isVisible = (element) => {
|
const isVisible = (element: Element | null) => {
|
||||||
if (!(element instanceof HTMLElement)) {
|
if (!(element instanceof HTMLElement)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -480,6 +480,14 @@
|
|||||||
- `npm run typecheck`
|
- `npm run typecheck`
|
||||||
- `npm run build`
|
- `npm run build`
|
||||||
- `npm test` 共 139 个用例通过
|
- `npm test` 共 139 个用例通过
|
||||||
|
101. 腾讯浏览器 WX 登录模块迁移到 `.ts`:
|
||||||
|
- `src/services/session/session-wx.ts`
|
||||||
|
102. WX 登录 iframe、快捷登录切换、二维码候选状态、远程下载 fallback、二维码 locator 与扫码状态已显式类型化
|
||||||
|
103. Docker 内验证通过:
|
||||||
|
- `src/services/session/session.test.js` 共 14 个用例通过
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run build`
|
||||||
|
- `npm test` 共 139 个用例通过
|
||||||
|
|
||||||
## 下一步建议
|
## 下一步建议
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user