deploy
This commit is contained in:
@@ -14,7 +14,7 @@ export function uploadError(message, statusCode = 400) {
|
||||
return err
|
||||
}
|
||||
|
||||
export async function persistMultipartImages(request, { maxFiles = 10 } = {}) {
|
||||
export async function persistMultipartImages(request, { maxFiles = 10, maxFileBytes }) {
|
||||
if (!request.isMultipart()) {
|
||||
throw uploadError('Ожидается multipart/form-data')
|
||||
}
|
||||
@@ -23,9 +23,14 @@ export async function persistMultipartImages(request, { maxFiles = 10 } = {}) {
|
||||
await fs.promises.mkdir(uploadsDir, { recursive: true })
|
||||
|
||||
const urls = []
|
||||
const parts = request.parts()
|
||||
const parts = request.parts({
|
||||
limits: {
|
||||
fileSize: maxFileBytes,
|
||||
files: maxFiles,
|
||||
},
|
||||
})
|
||||
for await (const part of parts) {
|
||||
if (part.type !== 'file') continue
|
||||
if (!part.file) continue
|
||||
if (urls.length >= maxFiles) {
|
||||
throw uploadError(`Можно загрузить не более ${maxFiles} файл(ов)`)
|
||||
}
|
||||
@@ -40,6 +45,12 @@ export async function persistMultipartImages(request, { maxFiles = 10 } = {}) {
|
||||
urls.push(`/uploads/${fileName}`)
|
||||
}
|
||||
|
||||
if (urls.length === 0) {
|
||||
throw uploadError(
|
||||
'Файлы не получены. Проверьте, что запрос multipart/form-data и поля — файлы изображений (png, jpg, webp).',
|
||||
)
|
||||
}
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
const MB = 1024 * 1024
|
||||
|
||||
/** Фото товаров в админке (на файл). По умолчанию 20 МБ. */
|
||||
export const PRODUCT_IMAGE_MAX_FILE_BYTES = 20 * MB
|
||||
|
||||
/** Отзывы, чек оплаты и прочие загрузки (на файл). По умолчанию 2 МБ. */
|
||||
export const OTHER_UPLOAD_MAX_FILE_BYTES = 2 * MB
|
||||
|
||||
export function getProductImageMaxFileBytes() {
|
||||
const n = Number(process.env.PRODUCT_IMAGE_MAX_FILE_BYTES)
|
||||
return Number.isFinite(n) && n > 0 ? Math.floor(n) : PRODUCT_IMAGE_MAX_FILE_BYTES
|
||||
}
|
||||
|
||||
export function getOtherUploadMaxFileBytes() {
|
||||
const n = Number(process.env.OTHER_UPLOAD_MAX_FILE_BYTES)
|
||||
return Number.isFinite(n) && n > 0 ? Math.floor(n) : OTHER_UPLOAD_MAX_FILE_BYTES
|
||||
}
|
||||
|
||||
/** Лимит тела HTTP: до 10 фото товара за запрос + запас. */
|
||||
export function getMaxUploadBodyBytes() {
|
||||
const n = Number(process.env.MAX_UPLOAD_BODY_BYTES)
|
||||
if (Number.isFinite(n) && n > 0) return Math.floor(n)
|
||||
return getProductImageMaxFileBytes() * 10 + MB
|
||||
}
|
||||
|
||||
/** @param {unknown} error */
|
||||
export function isMultipartFileTooLargeError(error) {
|
||||
if (!error || typeof error !== 'object') return false
|
||||
if (error.code === 'FST_REQ_FILE_TOO_LARGE') return true
|
||||
const msg = String(Reflect.get(error, 'message') ?? '')
|
||||
return /request file too large|file too large/i.test(msg)
|
||||
}
|
||||
|
||||
/** @param {number} maxFileBytes */
|
||||
export function formatFileTooLargeMessage(maxFileBytes) {
|
||||
const mb = Math.max(1, Math.round(maxFileBytes / MB))
|
||||
return `Файл слишком большой (максимум ${mb} МБ).`
|
||||
}
|
||||
Reference in New Issue
Block a user