后端迁移会话运行时模块
This commit is contained in:
+24
-12
@@ -1,32 +1,43 @@
|
|||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
import { runtimeConfig } from '../../config/runtime.js'
|
import { runtimeConfig } from '../../config/runtime.js'
|
||||||
|
|
||||||
const DEFAULT_TIMEOUT_MS = 60_000
|
const DEFAULT_TIMEOUT_MS = 60_000
|
||||||
|
|
||||||
export async function recognizeTencentCaptcha(payload) {
|
type OcrAction = 'recognize' | 'batch'
|
||||||
|
|
||||||
|
type OcrRequestPayload = Record<string, unknown>
|
||||||
|
|
||||||
|
type OcrHttpResult = Record<string, any> & {
|
||||||
|
code: number
|
||||||
|
msg?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function recognizeTencentCaptcha(payload: OcrRequestPayload): Promise<OcrHttpResult> {
|
||||||
return callOcrViaHttp('recognize', payload)
|
return callOcrViaHttp('recognize', payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function recognizeImageCaptcha(payload) {
|
export async function recognizeImageCaptcha(payload: OcrRequestPayload): Promise<OcrHttpResult> {
|
||||||
return callOcrViaHttp('recognize', payload)
|
return callOcrViaHttp('recognize', payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function batchRecognizeTencentCaptcha(payload) {
|
export async function batchRecognizeTencentCaptcha(payload: OcrRequestPayload): Promise<OcrHttpResult> {
|
||||||
return callOcrViaHttp('batch', payload, { timeoutMs: 180_000 })
|
return callOcrViaHttp('batch', payload, { timeoutMs: 180_000 })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function warmupLocalOcrWorker() {
|
export async function warmupLocalOcrWorker(): Promise<void> {
|
||||||
await warmupOcrViaHttp()
|
await warmupOcrViaHttp()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function closeLocalOcrWorker() {
|
export async function closeLocalOcrWorker(): Promise<void> {
|
||||||
// HTTP 模式无持久连接,无需关闭。
|
// HTTP 模式无持久连接,无需关闭。
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── HTTP Call ───────────────────────────────────────────────────────────────
|
// ── HTTP Call ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async function callOcrViaHttp(action, payload, { timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
|
async function callOcrViaHttp(
|
||||||
|
action: OcrAction,
|
||||||
|
payload: OcrRequestPayload,
|
||||||
|
{ timeoutMs = DEFAULT_TIMEOUT_MS }: { timeoutMs?: number } = {},
|
||||||
|
): Promise<OcrHttpResult> {
|
||||||
const baseUrl = String(runtimeConfig.ocr.baseUrl).trim().replace(/\/+$/, '')
|
const baseUrl = String(runtimeConfig.ocr.baseUrl).trim().replace(/\/+$/, '')
|
||||||
|
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
@@ -58,14 +69,15 @@ async function callOcrViaHttp(action, payload, { timeoutMs = DEFAULT_TIMEOUT_MS
|
|||||||
if (!result || typeof result !== 'object' || Array.isArray(result)) {
|
if (!result || typeof result !== 'object' || Array.isArray(result)) {
|
||||||
throw new Error('OCR 服务返回了无效响应')
|
throw new Error('OCR 服务返回了无效响应')
|
||||||
}
|
}
|
||||||
if (result.code !== 0) {
|
const ocrResult = result as OcrHttpResult
|
||||||
throw new Error(`OCR 服务返回错误:${result.msg || '未知错误'}`)
|
if (ocrResult.code !== 0) {
|
||||||
|
throw new Error(`OCR 服务返回错误:${ocrResult.msg || '未知错误'}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return ocrResult
|
||||||
}
|
}
|
||||||
|
|
||||||
async function warmupOcrViaHttp() {
|
async function warmupOcrViaHttp(): Promise<void> {
|
||||||
const baseUrl = String(runtimeConfig.ocr.baseUrl).trim().replace(/\/+$/, '')
|
const baseUrl = String(runtimeConfig.ocr.baseUrl).trim().replace(/\/+$/, '')
|
||||||
|
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
+20
-8
@@ -1,5 +1,3 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
import { chromium } from 'playwright'
|
import { chromium } from 'playwright'
|
||||||
|
|
||||||
import { runtimeConfig } from '../../config/runtime.js'
|
import { runtimeConfig } from '../../config/runtime.js'
|
||||||
@@ -9,10 +7,24 @@ import {
|
|||||||
} from './session-browser-config.js'
|
} from './session-browser-config.js'
|
||||||
import { CHROME_PATH } from './session-service-config.js'
|
import { CHROME_PATH } from './session-service-config.js'
|
||||||
|
|
||||||
let browserPromise = null
|
type BrowserLike = {
|
||||||
|
on: (event: 'disconnected', listener: () => void) => void
|
||||||
|
close: () => Promise<void>
|
||||||
|
newContext: (options?: Record<string, unknown>) => Promise<any>
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionServiceErrorOptions = {
|
||||||
|
statusCode?: number
|
||||||
|
errorCode?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
let browserPromise: Promise<BrowserLike> | null = null
|
||||||
|
|
||||||
export class SessionServiceError extends Error {
|
export class SessionServiceError extends Error {
|
||||||
constructor(message, { statusCode = 500, errorCode = 'session_error' } = {}) {
|
statusCode: number
|
||||||
|
errorCode: string
|
||||||
|
|
||||||
|
constructor(message: string, { statusCode = 500, errorCode = 'session_error' }: SessionServiceErrorOptions = {}) {
|
||||||
super(message)
|
super(message)
|
||||||
this.name = 'SessionServiceError'
|
this.name = 'SessionServiceError'
|
||||||
this.statusCode = statusCode
|
this.statusCode = statusCode
|
||||||
@@ -40,10 +52,10 @@ export async function warmupTencentBrowser() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function ensureBrowser() {
|
export async function ensureBrowser(): Promise<BrowserLike> {
|
||||||
if (!browserPromise) {
|
if (!browserPromise) {
|
||||||
const launchOptions = resolveBrowserLaunchOptions()
|
const launchOptions = resolveBrowserLaunchOptions()
|
||||||
browserPromise = chromium.launch(buildChromiumLaunchOptions(launchOptions))
|
browserPromise = (chromium.launch(buildChromiumLaunchOptions(launchOptions)) as Promise<BrowserLike>)
|
||||||
.then((browser) => {
|
.then((browser) => {
|
||||||
browser.on('disconnected', () => {
|
browser.on('disconnected', () => {
|
||||||
browserPromise = null
|
browserPromise = null
|
||||||
@@ -66,7 +78,7 @@ export function resolveBrowserLaunchOptions() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function closeBrowserIfIdle({ activeSessionCount = 0 } = {}) {
|
export async function closeBrowserIfIdle({ activeSessionCount = 0 }: { activeSessionCount?: number } = {}): Promise<void> {
|
||||||
if (activeSessionCount > 0 || !browserPromise) {
|
if (activeSessionCount > 0 || !browserPromise) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -85,6 +97,6 @@ export async function closeBrowserIfIdle({ activeSessionCount = 0 } = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildChromiumLaunchOptions(launchOptions) {
|
function buildChromiumLaunchOptions(launchOptions: ReturnType<typeof resolveBrowserLaunchOptionsBase>) {
|
||||||
return buildChromiumLaunchOptionsBase(launchOptions, { chromePath: CHROME_PATH })
|
return buildChromiumLaunchOptionsBase(launchOptions, { chromePath: CHROME_PATH })
|
||||||
}
|
}
|
||||||
@@ -447,6 +447,15 @@
|
|||||||
- `npm run typecheck`
|
- `npm run typecheck`
|
||||||
- `npm run build`
|
- `npm run build`
|
||||||
- `npm test` 共 139 个用例通过
|
- `npm test` 共 139 个用例通过
|
||||||
|
89. 腾讯浏览器会话 OCR / browser runtime 模块迁移到 `.ts`:
|
||||||
|
- `src/services/session/ocr.ts`
|
||||||
|
- `src/services/session/session-service-runtime.ts`
|
||||||
|
90. OCR HTTP payload / 响应、OCR 超时、浏览器预热、Playwright browser promise 生命周期、SessionServiceError 已显式类型化
|
||||||
|
91. Docker 内验证通过:
|
||||||
|
- `src/services/session/session.test.js` 与 `src/services/session/session-redeem.test.js` 共 20 个用例通过
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run build`
|
||||||
|
- `npm test` 共 139 个用例通过
|
||||||
|
|
||||||
## 下一步建议
|
## 下一步建议
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user