Files
FileHost/server/api/files/upload.post.ts
2026-10-19 15:04:41 +08:00

97 lines
2.7 KiB
TypeScript

import { readFormData } from 'h3'
import type { FileItem, FileUploadResponse } from '~/types/file'
import type { UploadErrorItem } from '~/types/image'
import {
getUploadUserId,
requireUploadAuth,
verifyApiUploadToken
} from '../../utils/access'
import { MAX_FILES_PER_UPLOAD } from '../../utils/constants'
import { createApiError } from '../../utils/api-error'
import { checkUploadRateLimit } from '../../utils/rate-limit'
import { processSingleFileUpload } from '../../utils/file-bed'
import { getFileBedAccessMode } from '../../utils/env'
import {
readFormDataFieldValues,
readFormDataFiles
} from '../../utils/multipart-form'
import {
parseTagNamesParam,
resolveUploadTagIds
} from '../../utils/tags'
export default defineEventHandler(async (event) => {
await requireUploadAuth(event)
const formData = await readFormData(event)
if (!formData) {
createApiError(event, 'INVALID_REQUEST', '未收到上传文件', 400)
}
const tokenField = formData.get('token')
const formToken = typeof tokenField === 'string' ? tokenField.trim() : ''
checkUploadRateLimit(event, formToken)
const source = verifyApiUploadToken(event) ? 'api' : 'web'
const uploadUserId = await getUploadUserId(event, formToken)
const tagNamesRaw = readFormDataFieldValues(formData, 'tagNames')
const uploadTagNames = parseTagNamesParam(
tagNamesRaw.length === 1 ? tagNamesRaw[0] : tagNamesRaw
)
const resolvedTags = resolveUploadTagIds(uploadUserId, uploadTagNames)
if ('message' in resolvedTags) {
createApiError(event, 'INVALID_REQUEST', resolvedTags.message, 400)
}
const uploadTagIds = resolvedTags.tagIds
const fileParts = readFormDataFiles(
formData,
['file', 'files']
)
if (!fileParts.length) {
createApiError(event, 'INVALID_REQUEST', '未收到上传文件', 400)
}
if (fileParts.length > MAX_FILES_PER_UPLOAD) {
createApiError(
event,
'TOO_MANY_FILES',
`每次最多上传 ${MAX_FILES_PER_UPLOAD} 个文件`,
400
)
}
const items: FileItem[] = []
const errors: UploadErrorItem[] = []
for (const part of fileParts) {
const result = await processSingleFileUpload(event, {
bytes: new Uint8Array(await part.arrayBuffer()),
filename: part.name,
contentType: part.type,
source,
userId: uploadUserId,
tagIds: uploadTagIds
})
if ('error' in result) {
errors.push(result.error)
} else {
items.push(result.item)
}
}
const response: FileUploadResponse = {
success: items.length > 0,
items,
errors,
accessMode: getFileBedAccessMode()
}
if (!items.length && errors.length) {
setResponseStatus(event, 400)
}
return response
})