feat(server): remove old /admin/uploads, validate isResized on product endpoints

This commit is contained in:
Kirill
2026-05-17 17:51:47 +05:00
parent 9226bcc571
commit 5637bb7db9
2 changed files with 39 additions and 35 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ await fastify.register(jwt, {
await fastify.register(multipart, {
limits: {
files: 10,
/** Совпадает с лимитом одного файла для `POST /api/admin/uploads` (товары, галерея). */
/** Совпадает с лимитом одного файла для `POST /api/admin/gallery/upload` (галерея). */
fileSize: getProductImageMaxFileBytes(),
},
})
+38 -34
View File
@@ -1,11 +1,4 @@
import { upsertGalleryImagesByUrls } from '../../lib/gallery.js'
import { prisma } from '../../lib/prisma.js'
import {
formatFileTooLargeMessage,
getProductImageMaxFileBytes,
isMultipartFileTooLargeError,
} from '../../lib/upload-limits.js'
import { persistMultipartImages } from '../../lib/upload-images.js'
const CREATE_PRODUCT_SCHEMA = {
body: {
@@ -59,33 +52,6 @@ export async function registerAdminProductRoutes(fastify) {
},
)
fastify.post(
'/api/admin/uploads',
{ preHandler: [fastify.verifyAdmin] },
async (request, reply) => {
try {
const urls = await persistMultipartImages(request, {
maxFiles: 10,
maxFileBytes: getProductImageMaxFileBytes(),
eager: true,
})
await upsertGalleryImagesByUrls(urls)
return { urls }
} catch (error) {
let message = error instanceof Error ? error.message : 'Не удалось загрузить файлы'
let statusCode =
error && typeof error === 'object' && 'statusCode' in error && Number.isInteger(error.statusCode)
? Number(error.statusCode)
: 400
if (isMultipartFileTooLargeError(error)) {
message = formatFileTooLargeMessage(getProductImageMaxFileBytes())
statusCode = 413
}
return reply.code(statusCode).send({ error: message })
}
},
)
fastify.post(
'/api/admin/products',
{ preHandler: [fastify.verifyAdmin], schema: CREATE_PRODUCT_SCHEMA },
@@ -122,6 +88,25 @@ export async function registerAdminProductRoutes(fastify) {
return
}
if (Array.isArray(body.imageUrls) && body.imageUrls.length > 0) {
const urls = body.imageUrls.map((u) => String(u || '').trim()).filter(Boolean)
if (urls.length > 0) {
const galleryImages = await prisma.galleryImage.findMany({
where: { url: { in: urls } },
select: { url: true, isResized: true },
})
const galleryMap = new Map(galleryImages.map((g) => [g.url, g]))
const notFound = urls.filter((u) => !galleryMap.has(u))
const notResized = urls.filter((u) => galleryMap.get(u) && !galleryMap.get(u).isResized)
if (notFound.length > 0) {
return reply.code(400).send({ error: 'Некоторые изображения не найдены в галерее' })
}
if (notResized.length > 0) {
return reply.code(400).send({ error: 'Изображения должны быть обработаны (resize) перед прикреплением к товару' })
}
}
}
const n = Number(body.quantity)
if (!Number.isInteger(n) || n < 0 || n > 10) {
reply.code(400).send({ error: 'Количество — целое число от 0 до 10' })
@@ -228,6 +213,25 @@ export async function registerAdminProductRoutes(fastify) {
data.categoryId = cid
}
if (body.imageUrls !== undefined && Array.isArray(body.imageUrls)) {
const urls = body.imageUrls.map((u) => String(u || '').trim()).filter(Boolean)
if (urls.length > 0) {
const galleryImages = await prisma.galleryImage.findMany({
where: { url: { in: urls } },
select: { url: true, isResized: true },
})
const galleryMap = new Map(galleryImages.map((g) => [g.url, g]))
const notFound = urls.filter((u) => !galleryMap.has(u))
const notResized = urls.filter((u) => galleryMap.get(u) && !galleryMap.get(u).isResized)
if (notFound.length > 0) {
return reply.code(400).send({ error: 'Некоторые изображения не найдены в галерее' })
}
if (notResized.length > 0) {
return reply.code(400).send({ error: 'Изображения должны быть обработаны (resize) перед прикреплением к товару' })
}
}
}
const imagesUpdate =
body.imageUrls !== undefined
? {