Files
2025-12-14 16:49:59 +08:00

116 lines
3.0 KiB
JavaScript

import { createReadStream, existsSync } from 'fs'
import db from '../../../utils/db.js'
import { verifyToken, extractToken } from '../../../utils/jwt.js'
import { getImagePath } from '../../../utils/upload.js'
export default defineEventHandler(async (event) => {
try {
// 验证登录 - 支持 header 和 query 参数两种方式传递 token
// 因为 <img> 标签无法设置 header,所以需要支持 query 参数
let token = extractToken(event)
// 如果 header 中没有 token,尝试从 query 参数获取
if (!token) {
const query = getQuery(event)
token = query.token
}
if (!token) {
throw createError({
statusCode: 401,
message: '请先登录'
})
}
const user = await verifyToken(token)
if (!user) {
throw createError({
statusCode: 401,
message: 'Token 无效或已过期'
})
}
// 获取路径参数,例如 /api/images/preview/uuid.webp
let path = getRouterParam(event, 'path')
// [...path] 可能返回数组,需要处理
if (Array.isArray(path)) {
path = path.join('/')
}
if (!path) {
throw createError({
statusCode: 404,
message: '图片不存在'
})
}
// 解析 uuid 和扩展名
const match = path.match(/^([a-f0-9-]+)\.(\w+)$/i)
if (!match) {
throw createError({
statusCode: 404,
message: '图片不存在'
})
}
const [, uuid, ext] = match
// 从数据库查找图片(不检查 isDeleted,管理员可以查看所有图片)
const image = await db.images.findOne({ uuid: uuid })
if (!image) {
throw createError({
statusCode: 404,
message: '图片不存在'
})
}
// 检查文件是否存在
const filePath = getImagePath(image.filename)
if (!existsSync(filePath)) {
console.log('[Admin Preview] File does not exist:', filePath)
throw createError({
statusCode: 404,
message: '图片文件不存在'
})
}
// 设置响应头
const mimeTypes = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'webp': 'image/webp',
'bmp': 'image/bmp',
'ico': 'image/x-icon',
'svg': 'image/svg+xml',
'avif': 'image/avif',
'tiff': 'image/tiff'
}
const contentType = mimeTypes[image.format] || 'application/octet-stream'
// 设置响应头(不缓存,因为是管理员预览)
setHeader(event, 'Content-Type', contentType)
setHeader(event, 'Content-Length', image.size)
setHeader(event, 'Cache-Control', 'no-store, no-cache, must-revalidate')
setHeader(event, 'X-Content-Type-Options', 'nosniff')
// 返回文件流
return sendStream(event, createReadStream(filePath))
} catch (error) {
if (error.statusCode) {
throw error
}
console.error('[Admin Preview] 获取图片失败:', error)
throw createError({
statusCode: 500,
message: '获取图片失败'
})
}
})