feat: migrate app routes and business modules
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import { createRequestError } from './request-client.js'
|
||||
import { normalizeOptionalResponseText } from './response-normalizers.js'
|
||||
import { assertPlainPayload, normalizeOptionalText } from './request-normalizers.js'
|
||||
|
||||
const normalizeFileResponseText = (value, field) =>
|
||||
normalizeOptionalResponseText(value, field, '文件服务响应', 'FILE_RESPONSE_INVALID')
|
||||
|
||||
export const normalizeUploadId = (value, field) => {
|
||||
if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${field}必须是非空字符串`)
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
const normalizeUploadFileName = (value) => {
|
||||
if (typeof value !== 'string' || !value.trim()) throw new TypeError('fileName必须是非空字符串')
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
export const normalizeUploadMd5 = (value, field) => {
|
||||
if (typeof value !== 'string' || !/^[a-f0-9]{32}$/i.test(value)) {
|
||||
throw new TypeError(`${field}必须是 32 位十六进制 MD5`)
|
||||
}
|
||||
return value.toLowerCase()
|
||||
}
|
||||
|
||||
const normalizeUploadSize = (value, field) => {
|
||||
if (!Number.isSafeInteger(value) || value <= 0) throw new TypeError(`${field}必须是正安全整数`)
|
||||
return value
|
||||
}
|
||||
|
||||
export const normalizeResumableInitPayload = (payload) => {
|
||||
const allowedFields = new Set(['uploadId', 'fileName', 'fileMd5', 'totalSize', 'totalChunks', 'chunkSize', 'contentType'])
|
||||
assertPlainPayload(payload, allowedFields, '文件初始化请求')
|
||||
if (!Number.isInteger(payload.totalChunks) || payload.totalChunks <= 0 || payload.totalChunks > 2147483647) {
|
||||
throw new TypeError('totalChunks必须是正 int32')
|
||||
}
|
||||
const normalizedPayload = {
|
||||
uploadId: normalizeUploadId(payload.uploadId, 'uploadId'),
|
||||
fileName: normalizeUploadFileName(payload.fileName),
|
||||
fileMd5: normalizeUploadMd5(payload.fileMd5, 'fileMd5'),
|
||||
totalSize: normalizeUploadSize(payload.totalSize, 'totalSize'),
|
||||
totalChunks: payload.totalChunks,
|
||||
chunkSize: normalizeUploadSize(payload.chunkSize, 'chunkSize')
|
||||
}
|
||||
const contentType = normalizeOptionalText(payload.contentType, 'contentType')
|
||||
if (contentType) normalizedPayload.contentType = contentType
|
||||
return normalizedPayload
|
||||
}
|
||||
|
||||
export const normalizeResumableCompletePayload = (payload) => {
|
||||
const allowedFields = new Set(['uploadId', 'fileName', 'fileMd5', 'totalSize', 'totalChunks'])
|
||||
assertPlainPayload(payload, allowedFields, '文件完成请求')
|
||||
if (!Number.isInteger(payload.totalChunks) || payload.totalChunks <= 0 || payload.totalChunks > 2147483647) {
|
||||
throw new TypeError('totalChunks必须是正 int32')
|
||||
}
|
||||
return {
|
||||
uploadId: normalizeUploadId(payload.uploadId, 'uploadId'),
|
||||
fileName: normalizeUploadFileName(payload.fileName),
|
||||
fileMd5: normalizeUploadMd5(payload.fileMd5, 'fileMd5'),
|
||||
totalSize: normalizeUploadSize(payload.totalSize, 'totalSize'),
|
||||
totalChunks: payload.totalChunks
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeUploadOssId = (value, field) => {
|
||||
const id = typeof value === 'number' && Number.isSafeInteger(value) ? String(value) : value
|
||||
if (typeof id !== 'string' || !/^[1-9]\d*$/.test(id)) {
|
||||
throw createRequestError(`文件服务字段 ${field} 无效`, 'FILE_RESPONSE_INVALID')
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
export const normalizeResumableInitResult = (payload) => {
|
||||
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
||||
throw createRequestError('文件初始化响应格式无效', 'FILE_RESPONSE_INVALID')
|
||||
}
|
||||
if (typeof payload.instant !== 'boolean') {
|
||||
throw createRequestError('文件初始化响应缺少 instant', 'FILE_RESPONSE_INVALID')
|
||||
}
|
||||
const ossId = payload.ossId === undefined || payload.ossId === null ? null : normalizeUploadOssId(payload.ossId, 'ossId')
|
||||
if (payload.instant && !ossId) {
|
||||
throw createRequestError('秒传响应缺少 ossId', 'FILE_RESPONSE_INVALID')
|
||||
}
|
||||
const uploadId = payload.instant && (payload.uploadId === undefined || payload.uploadId === null)
|
||||
? null
|
||||
: normalizeUploadId(payload.uploadId, '响应 uploadId')
|
||||
return {
|
||||
uploadId,
|
||||
instant: payload.instant,
|
||||
ossId,
|
||||
url: normalizeFileResponseText(payload.url, 'url'),
|
||||
fileName: normalizeFileResponseText(payload.fileName, 'fileName')
|
||||
}
|
||||
}
|
||||
|
||||
export const normalizeResumableCompleteResult = (payload) => {
|
||||
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
||||
throw createRequestError('文件完成响应格式无效', 'FILE_RESPONSE_INVALID')
|
||||
}
|
||||
return {
|
||||
ossId: normalizeUploadOssId(payload.ossId, 'ossId'),
|
||||
url: normalizeFileResponseText(payload.url, 'url'),
|
||||
thumbnailUrl: normalizeFileResponseText(payload.thumbnailUrl, 'thumbnailUrl'),
|
||||
fileName: normalizeFileResponseText(payload.fileName, 'fileName')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user