feat(backend): refactor OCR client to support HTTP + subprocess dual mode
- runtime-config.js: add baseUrl field to ocr type definition - default.cjs: add ocr.baseUrl default (empty string = subprocess mode) - runtime.js: add OCR_BASE_URL env var parsing - ocr.js: add HTTP mode (when OCR_BASE_URL set) alongside existing subprocess mode - resolveOcrMode() detects mode from runtimeConfig.ocr.baseUrl - callOcrViaHttp() calls FastAPI endpoints directly with payload - warmupOcrViaHttp() calls /health endpoint for HTTP mode warmup - callOcrRequest() routes between HTTP and subprocess based on mode - All 5 original exports preserved (recognizeTencentCaptcha, etc.) - Subprocess mode remains backward compatible (default when no OCR_BASE_URL)
This commit is contained in:
@@ -22,14 +22,89 @@ export async function batchRecognizeTencentCaptcha(payload) {
|
||||
}
|
||||
|
||||
export async function warmupLocalOcrWorker() {
|
||||
const mode = resolveOcrMode()
|
||||
if (mode === 'http') {
|
||||
await warmupOcrViaHttp()
|
||||
return
|
||||
}
|
||||
await runOcrCommand('healthcheck', { timeoutMs: 30_000 })
|
||||
}
|
||||
|
||||
export async function closeLocalOcrWorker() {
|
||||
// OCR 改为按次调用 Python 子进程,这里不再维护常驻 worker。
|
||||
// OCR 改为按次调用 Python 子进程或 HTTP 服务,这里不再维护常驻 worker。
|
||||
}
|
||||
|
||||
// ── Mode Resolution ──────────────────────────────────────────────────────────
|
||||
|
||||
function resolveOcrMode() {
|
||||
if (String(runtimeConfig.ocr.baseUrl || '').trim()) {
|
||||
return 'http'
|
||||
}
|
||||
return 'subprocess'
|
||||
}
|
||||
|
||||
// ── HTTP Mode ─────────────────────────────────────────────────────────────────
|
||||
|
||||
async function callOcrViaHttp(action, payload, { timeoutMs }) {
|
||||
const baseUrl = String(runtimeConfig.ocr.baseUrl).trim().replace(/\/+$/, '')
|
||||
const url = `${baseUrl}/ocr/${action}`
|
||||
|
||||
let response
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
signal: AbortSignal.timeout(timeoutMs),
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.name === 'TimeoutError') {
|
||||
throw new Error(`OCR 请求超时(${timeoutMs}ms),请检查 OCR 服务状态`)
|
||||
}
|
||||
throw new Error(`OCR 服务不可用(${baseUrl}),请检查 ocr-worker 容器是否正常运行`)
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`OCR 服务返回错误:HTTP ${response.status}`)
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
if (!result || typeof result !== 'object' || Array.isArray(result)) {
|
||||
throw new Error('OCR 服务返回了无效响应')
|
||||
}
|
||||
if (result.code !== 0) {
|
||||
throw new Error(`OCR 服务返回错误:${result.msg || '未知错误'}`)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async function warmupOcrViaHttp() {
|
||||
const baseUrl = String(runtimeConfig.ocr.baseUrl).trim().replace(/\/+$/, '')
|
||||
let response
|
||||
try {
|
||||
response = await fetch(`${baseUrl}/health`, {
|
||||
signal: AbortSignal.timeout(30_000),
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.name === 'TimeoutError') {
|
||||
throw new Error(`OCR 服务健康检查超时,请检查 ${baseUrl} 是否可访问`)
|
||||
}
|
||||
throw new Error(`OCR 服务不可用(${baseUrl}),请检查 ocr-worker 容器是否正常运行`)
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error(`OCR 服务健康检查失败:HTTP ${response.status}`)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Request Router ────────────────────────────────────────────────────────────
|
||||
|
||||
async function callOcrRequest(action, payload, { timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
|
||||
const mode = resolveOcrMode()
|
||||
if (mode === 'http') {
|
||||
return await callOcrViaHttp(action, payload, { timeoutMs })
|
||||
}
|
||||
// subprocess mode
|
||||
const response = await runOcrCommand('request', {
|
||||
timeoutMs,
|
||||
input: JSON.stringify({
|
||||
@@ -45,6 +120,8 @@ async function callOcrRequest(action, payload, { timeoutMs = DEFAULT_TIMEOUT_MS
|
||||
return response
|
||||
}
|
||||
|
||||
// ── Subprocess Mode ───────────────────────────────────────────────────────────
|
||||
|
||||
async function runOcrCommand(command, { timeoutMs = DEFAULT_TIMEOUT_MS, input = '' } = {}) {
|
||||
const projectRoot = resolveOcrProjectRoot()
|
||||
const workerCommand = resolveWorkerCommand(projectRoot)
|
||||
|
||||
Reference in New Issue
Block a user