base commit

This commit is contained in:
@kirill.komarov
2026-05-10 14:28:35 +05:00
parent 1e376caecc
commit 5ddde15fd3
102 changed files with 332 additions and 7657 deletions
+92 -5
View File
@@ -1,5 +1,7 @@
import { prisma } from '../lib/prisma.js'
import { issueEmailCode, normalizeEmail, verifyEmailCode } from '../lib/auth.js'
import { escapeHtml } from '../lib/escape-html.js'
import { prisma } from '../lib/prisma.js'
import { saveImageBufferToUploads } from '../lib/upload-images.js'
function mapUserForClient(user) {
const adminEmail = normalizeEmail(process.env.ADMIN_EMAIL)
@@ -704,11 +706,96 @@ export async function registerAuthRoutes(fastify) {
if (order.status === 'DRAFT') {
await prisma.order.update({ where: { id }, data: { status: 'PENDING_PAYMENT' } })
nextStatus = 'PENDING_PAYMENT'
} else if (order.status === 'PENDING_PAYMENT') {
await prisma.order.update({ where: { id }, data: { status: 'PAYMENT_VERIFICATION' } })
nextStatus = 'PAYMENT_VERIFICATION'
return { ok: true, status: nextStatus }
}
return { ok: true, status: nextStatus }
if (order.status === 'PAYMENT_VERIFICATION') {
return { ok: true, status: nextStatus }
}
if (order.status === 'PENDING_PAYMENT') {
if (!request.isMultipart()) {
return reply
.code(400)
.send({ error: 'Отправьте multipart/form-data: поле detail и/или файл receipt' })
}
let detail = ''
let receiptBuffer = null
let receiptFilename = ''
try {
const parts = request.parts()
for await (const part of parts) {
if (part.type === 'file') {
if (part.fieldname === 'receipt') {
if (receiptBuffer !== null) {
return reply.code(400).send({ error: 'Допускается один файл receipt' })
}
receiptBuffer = await part.toBuffer()
receiptFilename = part.filename ?? 'receipt'
}
} else if (part.type === 'field' && part.fieldname === 'detail') {
detail = String(part.value ?? '').trim()
}
}
} catch (err) {
const msg = err instanceof Error ? err.message : 'Не удалось разобрать форму'
return reply.code(400).send({ error: msg })
}
const hasDetail = detail.length > 0
const hasReceipt = receiptBuffer !== null && receiptBuffer.length > 0
if (!hasDetail && !hasReceipt) {
return reply
.code(400)
.send({ error: 'Укажите текст о платеже и/или прикрепите изображение чека' })
}
const maxDetail = 2000
if (detail.length > maxDetail) {
return reply.code(400).send({ error: `Текст не длиннее ${maxDetail} символов` })
}
let attachmentUrl = null
if (hasReceipt) {
try {
attachmentUrl = await saveImageBufferToUploads(receiptFilename, receiptBuffer)
} catch (err) {
const message = err instanceof Error ? err.message : 'Не удалось сохранить файл'
const statusCode =
err && typeof err === 'object' && 'statusCode' in err && Number.isInteger(err.statusCode)
? Number(err.statusCode)
: 400
return reply.code(statusCode).send({ error: message })
}
}
const bodyHtml = hasDetail
? `<p>${escapeHtml(detail).replace(/\r\n|\n|\r/g, '<br/>')}</p>`
: ''
const messageText = `<p><strong>Подтверждение оплаты (перевод ВТБ / Сбербанк)</strong></p>${bodyHtml}`
try {
await prisma.$transaction(async (tx) => {
await tx.order.update({ where: { id }, data: { status: 'PAYMENT_VERIFICATION' } })
await tx.orderMessage.create({
data: {
orderId: id,
authorType: 'user',
text: messageText,
attachmentUrl,
},
})
})
} catch (err) {
return reply.code(500).send({ error: 'Не удалось сохранить оплату' })
}
return { ok: true, status: 'PAYMENT_VERIFICATION' }
}
return reply.code(409).send({ error: 'Сейчас нельзя выполнить оплату для этого заказа' })
},
)