40 行
1.4 KiB
JavaScript

import { execFileSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { requireAdmin } from '../../utils/auth.js'
/**
* 对 public/uploads 下的图片跑 tesseract chi_sim OCR。
* Body: { image: '/uploads/reviews/xxx.jpg' }
* 仅允许 /uploads/ 前缀,防止任意文件读取。
*/
export default defineEventHandler(async (event) => {
requireAdmin(event)
const { image } = await readBody(event)
if (!image || typeof image !== 'string' || !image.startsWith('/uploads/')) {
throw createError({ statusCode: 422, message: '仅支持 /uploads/ 下的图片' })
}
const rel = image.replace(/^\/+/, '')
const abs = resolve(join(process.cwd(), 'public', rel))
const allowedRoot = resolve(join(process.cwd(), 'public/uploads'))
if (!abs.startsWith(allowedRoot + '/') && abs !== allowedRoot) {
throw createError({ statusCode: 400, message: '非法路径' })
}
if (!existsSync(abs)) {
throw createError({ statusCode: 404, message: '图片不存在' })
}
try {
const text = execFileSync('tesseract', [abs, 'stdout', '-l', 'chi_sim'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
maxBuffer: 10 * 1024 * 1024,
})
return { text: text || '' }
} catch (e) {
throw createError({ statusCode: 500, message: 'OCR 执行失败(请确认已安装 tesseract + chi_sim 语言包)' })
}
})