deploy
This commit is contained in:
@@ -1,6 +1,22 @@
|
||||
import {
|
||||
getOrCreateUnspecifiedCategory,
|
||||
isUnspecifiedCategorySlug,
|
||||
UNSPECIFIED_CATEGORY_SLUG,
|
||||
} from '../../lib/default-category.js'
|
||||
import { prisma } from '../../lib/prisma.js'
|
||||
|
||||
export async function registerAdminCategoryRoutes(fastify, { slugify } = {}) {
|
||||
fastify.get(
|
||||
'/api/admin/categories',
|
||||
{ preHandler: [fastify.verifyAdmin] },
|
||||
async () => {
|
||||
const items = await prisma.category.findMany({
|
||||
orderBy: [{ sort: 'asc' }, { name: 'asc' }],
|
||||
})
|
||||
return { items }
|
||||
},
|
||||
)
|
||||
|
||||
fastify.post(
|
||||
'/api/admin/categories',
|
||||
{ preHandler: [fastify.verifyAdmin] },
|
||||
@@ -12,6 +28,10 @@ export async function registerAdminCategoryRoutes(fastify, { slugify } = {}) {
|
||||
return
|
||||
}
|
||||
const slug = String(body.slug ?? '').trim() || slugify(name) || `cat-${Date.now()}`
|
||||
if (isUnspecifiedCategorySlug(slug)) {
|
||||
reply.code(400).send({ error: `Slug «${UNSPECIFIED_CATEGORY_SLUG}» зарезервирован` })
|
||||
return
|
||||
}
|
||||
const sort = body.sort !== undefined && body.sort !== null && body.sort !== '' ? Number(body.sort) : undefined
|
||||
const exists = await prisma.category.findUnique({ where: { slug } })
|
||||
if (exists) {
|
||||
@@ -28,5 +48,90 @@ export async function registerAdminCategoryRoutes(fastify, { slugify } = {}) {
|
||||
reply.code(201).send(category)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fastify.patch(
|
||||
'/api/admin/categories/:id',
|
||||
{ preHandler: [fastify.verifyAdmin] },
|
||||
async (request, reply) => {
|
||||
const { id } = request.params
|
||||
const body = request.body ?? {}
|
||||
const existing = await prisma.category.findUnique({ where: { id } })
|
||||
if (!existing) {
|
||||
reply.code(404).send({ error: 'Категория не найдена' })
|
||||
return
|
||||
}
|
||||
|
||||
const data = {}
|
||||
if (body.name !== undefined) data.name = String(body.name ?? '').trim()
|
||||
if (body.sort !== undefined) {
|
||||
const s = Number(body.sort)
|
||||
if (!Number.isFinite(s)) {
|
||||
reply.code(400).send({ error: 'Некорректный sort' })
|
||||
return
|
||||
}
|
||||
data.sort = Math.round(s)
|
||||
}
|
||||
if (body.slug !== undefined) {
|
||||
const s = String(body.slug ?? '').trim()
|
||||
if (isUnspecifiedCategorySlug(existing.slug) && s !== UNSPECIFIED_CATEGORY_SLUG) {
|
||||
reply.code(400).send({ error: 'Нельзя сменить slug служебной категории «Не указано»' })
|
||||
return
|
||||
}
|
||||
if (!s) {
|
||||
reply.code(400).send({ error: 'Slug не может быть пустым' })
|
||||
return
|
||||
}
|
||||
if (s !== existing.slug) {
|
||||
if (isUnspecifiedCategorySlug(s)) {
|
||||
reply.code(400).send({ error: `Slug «${UNSPECIFIED_CATEGORY_SLUG}» зарезервирован` })
|
||||
return
|
||||
}
|
||||
const clash = await prisma.category.findFirst({ where: { slug: s, NOT: { id } } })
|
||||
if (clash) {
|
||||
reply.code(409).send({ error: 'Такой slug уже занят' })
|
||||
return
|
||||
}
|
||||
}
|
||||
data.slug = s
|
||||
}
|
||||
|
||||
if (Object.keys(data).length === 0) {
|
||||
return existing
|
||||
}
|
||||
if (data.name !== undefined && !data.name) {
|
||||
reply.code(400).send({ error: 'Укажите название' })
|
||||
return
|
||||
}
|
||||
|
||||
const updated = await prisma.category.update({ where: { id }, data })
|
||||
return updated
|
||||
},
|
||||
)
|
||||
|
||||
fastify.delete(
|
||||
'/api/admin/categories/:id',
|
||||
{ preHandler: [fastify.verifyAdmin] },
|
||||
async (request, reply) => {
|
||||
const { id } = request.params
|
||||
const existing = await prisma.category.findUnique({ where: { id } })
|
||||
if (!existing) {
|
||||
reply.code(404).send({ error: 'Категория не найдена' })
|
||||
return
|
||||
}
|
||||
if (isUnspecifiedCategorySlug(existing.slug)) {
|
||||
reply.code(409).send({ error: 'Служебную категорию «Не указано» нельзя удалить' })
|
||||
return
|
||||
}
|
||||
|
||||
const fallback = await getOrCreateUnspecifiedCategory()
|
||||
await prisma.$transaction([
|
||||
prisma.product.updateMany({
|
||||
where: { categoryId: id },
|
||||
data: { categoryId: fallback.id },
|
||||
}),
|
||||
prisma.category.delete({ where: { id } }),
|
||||
])
|
||||
return reply.code(204).send()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getOrCreateUnspecifiedCategory } from '../../lib/default-category.js'
|
||||
import { upsertGalleryImagesByUrls } from '../../lib/gallery.js'
|
||||
import { prisma } from '../../lib/prisma.js'
|
||||
import {
|
||||
@@ -60,10 +61,15 @@ export async function registerAdminProductRoutes(
|
||||
return
|
||||
}
|
||||
const slug = String(body.slug ?? '').trim() || slugify(title) || `item-${Date.now()}`
|
||||
const categoryId = String(body.categoryId ?? '').trim()
|
||||
let categoryId = String(body.categoryId ?? '').trim()
|
||||
if (!categoryId) {
|
||||
reply.code(400).send({ error: 'Укажите категорию' })
|
||||
return
|
||||
categoryId = (await getOrCreateUnspecifiedCategory()).id
|
||||
} else {
|
||||
const cat = await prisma.category.findUnique({ where: { id: categoryId } })
|
||||
if (!cat) {
|
||||
reply.code(400).send({ error: 'Категория не найдена' })
|
||||
return
|
||||
}
|
||||
}
|
||||
const priceCents = Number(body.priceCents)
|
||||
if (!Number.isFinite(priceCents) || priceCents < 0) {
|
||||
@@ -190,7 +196,20 @@ export async function registerAdminProductRoutes(
|
||||
data.imageUrl = body.imageUrl ? String(body.imageUrl) : null
|
||||
}
|
||||
if (body.published !== undefined) data.published = Boolean(body.published)
|
||||
if (body.categoryId !== undefined) data.categoryId = String(body.categoryId)
|
||||
if (body.categoryId !== undefined) {
|
||||
const raw = body.categoryId
|
||||
if (raw === null || raw === '') {
|
||||
data.categoryId = (await getOrCreateUnspecifiedCategory()).id
|
||||
} else {
|
||||
const cid = String(raw).trim()
|
||||
const cat = await prisma.category.findUnique({ where: { id: cid } })
|
||||
if (!cat) {
|
||||
reply.code(400).send({ error: 'Категория не найдена' })
|
||||
return
|
||||
}
|
||||
data.categoryId = cid
|
||||
}
|
||||
}
|
||||
|
||||
if (body.inStock !== undefined) data.inStock = Boolean(body.inStock)
|
||||
if (body.leadTimeDays !== undefined) {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { prisma } from '../../lib/prisma.js'
|
||||
|
||||
const MAX_SLIDES = 20
|
||||
|
||||
export async function registerCatalogSliderRoutes(fastify) {
|
||||
fastify.get('/api/catalog-slider', async () => {
|
||||
const slides = await prisma.catalogSliderSlide.findMany({
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
include: { galleryImage: true },
|
||||
})
|
||||
return {
|
||||
slides: slides.map((s) => ({
|
||||
id: s.id,
|
||||
url: s.galleryImage.url,
|
||||
caption: s.caption,
|
||||
})),
|
||||
}
|
||||
})
|
||||
|
||||
fastify.get(
|
||||
'/api/admin/catalog-slider',
|
||||
{ preHandler: [fastify.verifyAdmin] },
|
||||
async () => {
|
||||
const slides = await prisma.catalogSliderSlide.findMany({
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
include: { galleryImage: true },
|
||||
})
|
||||
return {
|
||||
slides: slides.map((s) => ({
|
||||
id: s.id,
|
||||
galleryImageId: s.galleryImageId,
|
||||
url: s.galleryImage.url,
|
||||
caption: s.caption,
|
||||
})),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
fastify.put(
|
||||
'/api/admin/catalog-slider',
|
||||
{ preHandler: [fastify.verifyAdmin] },
|
||||
async (request, reply) => {
|
||||
const body = request.body ?? {}
|
||||
const rawSlides = body.slides
|
||||
if (!Array.isArray(rawSlides)) {
|
||||
return reply.code(400).send({ error: 'Ожидается slides: массив' })
|
||||
}
|
||||
if (rawSlides.length > MAX_SLIDES) {
|
||||
return reply.code(400).send({ error: `Не более ${MAX_SLIDES} слайдов` })
|
||||
}
|
||||
|
||||
const seenGalleryIds = new Set()
|
||||
const normalized = []
|
||||
for (let i = 0; i < rawSlides.length; i++) {
|
||||
const row = rawSlides[i]
|
||||
const galleryImageId = String(row?.galleryImageId ?? '').trim()
|
||||
if (!galleryImageId) {
|
||||
return reply.code(400).send({ error: `Слайд ${i + 1}: укажите galleryImageId` })
|
||||
}
|
||||
if (seenGalleryIds.has(galleryImageId)) {
|
||||
return reply.code(400).send({ error: 'Одно изображение нельзя добавить дважды' })
|
||||
}
|
||||
seenGalleryIds.add(galleryImageId)
|
||||
const img = await prisma.galleryImage.findUnique({ where: { id: galleryImageId } })
|
||||
if (!img) {
|
||||
return reply.code(400).send({ error: `Изображение не найдено: ${galleryImageId}` })
|
||||
}
|
||||
const caption = row?.caption == null ? '' : String(row.caption).slice(0, 500)
|
||||
normalized.push({ galleryImageId, caption, sortOrder: i })
|
||||
}
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.catalogSliderSlide.deleteMany({})
|
||||
for (const n of normalized) {
|
||||
await tx.catalogSliderSlide.create({
|
||||
data: {
|
||||
sortOrder: n.sortOrder,
|
||||
caption: n.caption,
|
||||
galleryImageId: n.galleryImageId,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const slides = await prisma.catalogSliderSlide.findMany({
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
include: { galleryImage: true },
|
||||
})
|
||||
return {
|
||||
slides: slides.map((s) => ({
|
||||
id: s.id,
|
||||
galleryImageId: s.galleryImageId,
|
||||
url: s.galleryImage.url,
|
||||
caption: s.caption,
|
||||
})),
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user