204 lines
8.4 KiB
JavaScript
204 lines
8.4 KiB
JavaScript
import { prisma } from '../lib/prisma.js'
|
|
|
|
function normalizePhoneLite(input) {
|
|
const s = String(input || '').trim()
|
|
if (!s) return ''
|
|
return s.replace(/[\s()-]/g, '')
|
|
}
|
|
|
|
function validateAddressPayload(body, reply) {
|
|
const labelRaw = body?.label
|
|
const label = labelRaw === null || labelRaw === undefined ? null : String(labelRaw).trim()
|
|
if (label !== null && label.length > 40) return reply.code(400).send({ error: 'Метка адреса максимум 40 символов' })
|
|
|
|
const recipientName = String(body?.recipientName || '').trim()
|
|
if (!recipientName) return reply.code(400).send({ error: 'Укажите ФИО получателя' })
|
|
if (recipientName.length > 80) return reply.code(400).send({ error: 'ФИО получателя максимум 80 символов' })
|
|
|
|
const recipientPhone = normalizePhoneLite(body?.recipientPhone)
|
|
if (!recipientPhone) return reply.code(400).send({ error: 'Укажите телефон получателя' })
|
|
if (!/^\+?\d{7,20}$/.test(recipientPhone)) return reply.code(400).send({ error: 'Некорректный телефон получателя' })
|
|
|
|
const addressLine = String(body?.addressLine || '').trim()
|
|
if (!addressLine) return reply.code(400).send({ error: 'Укажите адрес' })
|
|
if (addressLine.length > 200) return reply.code(400).send({ error: 'Адрес максимум 200 символов' })
|
|
|
|
const commentRaw = body?.comment
|
|
const comment = commentRaw === null || commentRaw === undefined ? null : String(commentRaw).trim()
|
|
if (comment !== null && comment.length > 200)
|
|
return reply.code(400).send({ error: 'Комментарий максимум 200 символов' })
|
|
|
|
const lat = Number(body?.lat)
|
|
const lng = Number(body?.lng)
|
|
if (!Number.isFinite(lat) || lat < -90 || lat > 90) return reply.code(400).send({ error: 'Некорректная широта' })
|
|
if (!Number.isFinite(lng) || lng < -180 || lng > 180) return reply.code(400).send({ error: 'Некорректная долгота' })
|
|
|
|
return {
|
|
label,
|
|
recipientName,
|
|
recipientPhone,
|
|
addressLine,
|
|
comment,
|
|
lat,
|
|
lng,
|
|
}
|
|
}
|
|
|
|
export async function registerUserAddressRoutes(fastify) {
|
|
fastify.get('/api/me/addresses', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
try {
|
|
const userId = request.user.sub
|
|
const items = await prisma.shippingAddress.findMany({
|
|
where: { userId },
|
|
orderBy: [{ isDefault: 'desc' }, { updatedAt: 'desc' }],
|
|
})
|
|
return { items }
|
|
} catch (err) {
|
|
request.log.error(err)
|
|
return reply.code(500).send({ error: 'Не удалось загрузить адреса' })
|
|
}
|
|
})
|
|
|
|
fastify.post('/api/me/addresses', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
try {
|
|
const userId = request.user.sub
|
|
const validated = validateAddressPayload(request.body, reply)
|
|
if (!validated) return
|
|
|
|
const isDefault = Boolean(request.body?.isDefault)
|
|
const created = await prisma.$transaction(async (tx) => {
|
|
if (isDefault) {
|
|
await tx.shippingAddress.updateMany({ where: { userId, isDefault: true }, data: { isDefault: false } })
|
|
}
|
|
return tx.shippingAddress.create({
|
|
data: {
|
|
userId,
|
|
...validated,
|
|
isDefault,
|
|
},
|
|
})
|
|
})
|
|
return reply.code(201).send({ item: created })
|
|
} catch (err) {
|
|
request.log.error(err)
|
|
return reply.code(500).send({ error: 'Не удалось создать адрес' })
|
|
}
|
|
})
|
|
|
|
fastify.patch('/api/me/addresses/:id', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
try {
|
|
const userId = request.user.sub
|
|
const { id } = request.params
|
|
const existing = await prisma.shippingAddress.findFirst({ where: { id, userId } })
|
|
if (!existing) return reply.code(404).send({ error: 'Адрес не найден' })
|
|
|
|
const body = request.body ?? {}
|
|
const data = {}
|
|
|
|
if (body.label !== undefined) {
|
|
const labelRaw = body.label
|
|
const label = labelRaw === null || labelRaw === undefined ? null : String(labelRaw).trim()
|
|
if (label !== null && label.length > 40)
|
|
return reply.code(400).send({ error: 'Метка адреса максимум 40 символов' })
|
|
data.label = label && label.length ? label : null
|
|
}
|
|
|
|
if (body.recipientName !== undefined) {
|
|
const v = String(body.recipientName || '').trim()
|
|
if (!v) return reply.code(400).send({ error: 'Укажите ФИО получателя' })
|
|
if (v.length > 80) return reply.code(400).send({ error: 'ФИО получателя максимум 80 символов' })
|
|
data.recipientName = v
|
|
}
|
|
|
|
if (body.recipientPhone !== undefined) {
|
|
const v = normalizePhoneLite(body.recipientPhone)
|
|
if (!v) return reply.code(400).send({ error: 'Укажите телефон получателя' })
|
|
if (!/^\+?\d{7,20}$/.test(v)) return reply.code(400).send({ error: 'Некорректный телефон получателя' })
|
|
data.recipientPhone = v
|
|
}
|
|
|
|
if (body.addressLine !== undefined) {
|
|
const v = String(body.addressLine || '').trim()
|
|
if (!v) return reply.code(400).send({ error: 'Укажите адрес' })
|
|
if (v.length > 200) return reply.code(400).send({ error: 'Адрес максимум 200 символов' })
|
|
data.addressLine = v
|
|
}
|
|
|
|
if (body.comment !== undefined) {
|
|
const commentRaw = body.comment
|
|
const comment = commentRaw === null || commentRaw === undefined ? null : String(commentRaw).trim()
|
|
if (comment !== null && comment.length > 200)
|
|
return reply.code(400).send({ error: 'Комментарий максимум 200 символов' })
|
|
data.comment = comment && comment.length ? comment : null
|
|
}
|
|
|
|
if (body.lat !== undefined) {
|
|
const lat = Number(body.lat)
|
|
if (!Number.isFinite(lat) || lat < -90 || lat > 90)
|
|
return reply.code(400).send({ error: 'Некорректная широта' })
|
|
data.lat = lat
|
|
}
|
|
|
|
if (body.lng !== undefined) {
|
|
const lng = Number(body.lng)
|
|
if (!Number.isFinite(lng) || lng < -180 || lng > 180)
|
|
return reply.code(400).send({ error: 'Некорректная долгота' })
|
|
data.lng = lng
|
|
}
|
|
|
|
const setDefault = body.isDefault === true
|
|
const updated = await prisma.$transaction(async (tx) => {
|
|
if (setDefault) {
|
|
await tx.shippingAddress.updateMany({ where: { userId, isDefault: true }, data: { isDefault: false } })
|
|
}
|
|
return tx.shippingAddress.update({
|
|
where: { id },
|
|
data: {
|
|
...data,
|
|
...(setDefault ? { isDefault: true } : {}),
|
|
},
|
|
})
|
|
})
|
|
|
|
return { item: updated }
|
|
} catch (err) {
|
|
request.log.error(err)
|
|
return reply.code(500).send({ error: 'Не удалось обновить адрес' })
|
|
}
|
|
})
|
|
|
|
fastify.delete('/api/me/addresses/:id', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
try {
|
|
const userId = request.user.sub
|
|
const { id } = request.params
|
|
const existing = await prisma.shippingAddress.findFirst({ where: { id, userId } })
|
|
if (!existing) return reply.code(404).send({ error: 'Адрес не найден' })
|
|
|
|
await prisma.shippingAddress.delete({ where: { id } })
|
|
return reply.code(204).send()
|
|
} catch (err) {
|
|
request.log.error(err)
|
|
return reply.code(500).send({ error: 'Не удалось удалить адрес' })
|
|
}
|
|
})
|
|
|
|
fastify.post('/api/me/addresses/:id/default', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
try {
|
|
const userId = request.user.sub
|
|
const { id } = request.params
|
|
const existing = await prisma.shippingAddress.findFirst({ where: { id, userId } })
|
|
if (!existing) return reply.code(404).send({ error: 'Адрес не найден' })
|
|
|
|
const updated = await prisma.$transaction(async (tx) => {
|
|
await tx.shippingAddress.updateMany({ where: { userId, isDefault: true }, data: { isDefault: false } })
|
|
return tx.shippingAddress.update({ where: { id }, data: { isDefault: true } })
|
|
})
|
|
|
|
return { item: updated }
|
|
} catch (err) {
|
|
request.log.error(err)
|
|
return reply.code(500).send({ error: 'Не удалось установить адрес по умолчанию' })
|
|
}
|
|
})
|
|
}
|