feat: emit notification events from existing routes
This commit is contained in:
@@ -27,6 +27,7 @@ export async function issueEmailCode({ email, purpose, userId = null }) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
await sendLoginCodeEmail({ to: email, code })
|
await sendLoginCodeEmail({ to: email, code })
|
||||||
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseEnvBool(raw) {
|
function parseEnvBool(raw) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { prisma } from '../../lib/prisma.js'
|
import { prisma } from '../../lib/prisma.js'
|
||||||
import { canTransitionAdminOrderStatus } from '../../lib/order-status.js'
|
import { canTransitionAdminOrderStatus } from '../../lib/order-status.js'
|
||||||
|
import { NOTIFICATION_EVENTS } from '../../shared/constants/notification-events.js'
|
||||||
|
|
||||||
export async function registerAdminOrderRoutes(fastify) {
|
export async function registerAdminOrderRoutes(fastify) {
|
||||||
fastify.get(
|
fastify.get(
|
||||||
@@ -108,6 +109,14 @@ export async function registerAdminOrderRoutes(fastify) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updated = await prisma.order.update({ where: { id }, data: { status: next } })
|
const updated = await prisma.order.update({ where: { id }, data: { status: next } })
|
||||||
|
|
||||||
|
request.server.eventBus.emit(NOTIFICATION_EVENTS.ORDER_STATUS_CHANGED, {
|
||||||
|
orderId: updated.id,
|
||||||
|
userId: existing.userId,
|
||||||
|
oldStatus: existing.status,
|
||||||
|
newStatus: next,
|
||||||
|
})
|
||||||
|
|
||||||
return { item: updated }
|
return { item: updated }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -156,6 +165,14 @@ export async function registerAdminOrderRoutes(fastify) {
|
|||||||
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
||||||
|
|
||||||
const msg = await prisma.orderMessage.create({ data: { orderId: id, authorType: 'admin', text } })
|
const msg = await prisma.orderMessage.create({ data: { orderId: id, authorType: 'admin', text } })
|
||||||
|
|
||||||
|
request.server.eventBus.emit(NOTIFICATION_EVENTS.ORDER_MESSAGE_ADMIN_REPLY, {
|
||||||
|
orderId: id,
|
||||||
|
userId: order.userId,
|
||||||
|
messageId: msg.id,
|
||||||
|
preview: text,
|
||||||
|
})
|
||||||
|
|
||||||
return reply.code(201).send({ item: msg })
|
return reply.code(201).send({ item: msg })
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { prisma } from '../../lib/prisma.js'
|
import { prisma } from '../../lib/prisma.js'
|
||||||
|
import { NOTIFICATION_EVENTS } from '../../shared/constants/notification-events.js'
|
||||||
|
|
||||||
export async function registerAdminReviewRoutes(fastify) {
|
export async function registerAdminReviewRoutes(fastify) {
|
||||||
fastify.get(
|
fastify.get(
|
||||||
@@ -43,7 +44,10 @@ export async function registerAdminReviewRoutes(fastify) {
|
|||||||
return reply.code(400).send({ error: 'action должен быть approve или reject' })
|
return reply.code(400).send({ error: 'action должен быть approve или reject' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const existing = await prisma.review.findUnique({ where: { id } })
|
const existing = await prisma.review.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: { product: { select: { title: true } }, user: { select: { name: true, email: true } } },
|
||||||
|
})
|
||||||
if (!existing) return reply.code(404).send({ error: 'Отзыв не найден' })
|
if (!existing) return reply.code(404).send({ error: 'Отзыв не найден' })
|
||||||
|
|
||||||
const updated = await prisma.review.update({
|
const updated = await prisma.review.update({
|
||||||
@@ -53,6 +57,14 @@ export async function registerAdminReviewRoutes(fastify) {
|
|||||||
moderatedAt: new Date(),
|
moderatedAt: new Date(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
request.server.eventBus.emit('review:created', {
|
||||||
|
rating: updated.rating,
|
||||||
|
text: updated.text || '',
|
||||||
|
productTitle: existing.product?.title || '',
|
||||||
|
userName: existing.user?.name || existing.user?.email || '',
|
||||||
|
reviewId: updated.id,
|
||||||
|
})
|
||||||
|
|
||||||
return { item: updated }
|
return { item: updated }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { issueEmailCode, normalizeEmail, verifyEmailCode } from '../lib/auth.js'
|
import { issueEmailCode, normalizeEmail, verifyEmailCode } from '../lib/auth.js'
|
||||||
import { prisma } from '../lib/prisma.js'
|
import { prisma } from '../lib/prisma.js'
|
||||||
|
import { NOTIFICATION_EVENTS } from '../../shared/constants/notification-events.js'
|
||||||
|
|
||||||
function mapUserForClient(user) {
|
function mapUserForClient(user) {
|
||||||
const adminEmail = normalizeEmail(process.env.ADMIN_EMAIL)
|
const adminEmail = normalizeEmail(process.env.ADMIN_EMAIL)
|
||||||
@@ -18,7 +19,17 @@ export async function registerAuthRoutes(fastify) {
|
|||||||
const email = normalizeEmail(request.body?.email)
|
const email = normalizeEmail(request.body?.email)
|
||||||
if (!email || !email.includes('@')) return reply.code(400).send({ error: 'Некорректная почта' })
|
if (!email || !email.includes('@')) return reply.code(400).send({ error: 'Некорректная почта' })
|
||||||
|
|
||||||
await issueEmailCode({ email, purpose: 'login' })
|
const code = await issueEmailCode({ email, purpose: 'login' })
|
||||||
|
|
||||||
|
const adminEmail = process.env.ADMIN_EMAIL?.trim().toLowerCase()
|
||||||
|
const isAdmin = email === adminEmail
|
||||||
|
|
||||||
|
request.server.eventBus.emit(NOTIFICATION_EVENTS.AUTH_CODE_REQUESTED, {
|
||||||
|
email,
|
||||||
|
code,
|
||||||
|
isAdmin,
|
||||||
|
})
|
||||||
|
|
||||||
return { ok: true }
|
return { ok: true }
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -37,6 +48,13 @@ export async function registerAuthRoutes(fastify) {
|
|||||||
create: { email },
|
create: { email },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Ensure notification preference exists
|
||||||
|
await prisma.notificationPreference.upsert({
|
||||||
|
where: { userId: user.id },
|
||||||
|
create: { userId: user.id, globalEnabled: true },
|
||||||
|
update: {},
|
||||||
|
})
|
||||||
|
|
||||||
const token = fastify.jwt.sign({ sub: user.id, email: user.email })
|
const token = fastify.jwt.sign({ sub: user.id, email: user.email })
|
||||||
return { token, user: mapUserForClient(user) }
|
return { token, user: mapUserForClient(user) }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { prisma } from '../lib/prisma.js'
|
import { prisma } from '../lib/prisma.js'
|
||||||
|
import { NOTIFICATION_EVENTS } from '../../shared/constants/notification-events.js'
|
||||||
|
|
||||||
export async function registerUserMessageRoutes(fastify) {
|
export async function registerUserMessageRoutes(fastify) {
|
||||||
fastify.get(
|
fastify.get(
|
||||||
@@ -26,6 +27,14 @@ export async function registerUserMessageRoutes(fastify) {
|
|||||||
if (!text) return reply.code(400).send({ error: 'Сообщение пустое' })
|
if (!text) return reply.code(400).send({ error: 'Сообщение пустое' })
|
||||||
if (text.length > 2000) return reply.code(400).send({ error: 'Сообщение слишком длинное' })
|
if (text.length > 2000) return reply.code(400).send({ error: 'Сообщение слишком длинное' })
|
||||||
const msg = await prisma.orderMessage.create({ data: { orderId: id, authorType: 'user', text } })
|
const msg = await prisma.orderMessage.create({ data: { orderId: id, authorType: 'user', text } })
|
||||||
|
|
||||||
|
request.server.eventBus.emit(NOTIFICATION_EVENTS.ORDER_MESSAGE_SENT, {
|
||||||
|
orderId: id,
|
||||||
|
authorType: 'user',
|
||||||
|
messageId: msg.id,
|
||||||
|
preview: text,
|
||||||
|
})
|
||||||
|
|
||||||
return reply.code(201).send({ item: msg })
|
return reply.code(201).send({ item: msg })
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { isDeliveryCarrier } from '../lib/delivery-carrier.js'
|
import { isDeliveryCarrier } from '../lib/delivery-carrier.js'
|
||||||
import { prisma } from '../lib/prisma.js'
|
import { prisma } from '../lib/prisma.js'
|
||||||
|
import { NOTIFICATION_EVENTS } from '../../shared/constants/notification-events.js'
|
||||||
|
|
||||||
export async function registerUserOrderRoutes(fastify) {
|
export async function registerUserOrderRoutes(fastify) {
|
||||||
// ---- Создание заказа (checkout) ----
|
// ---- Создание заказа (checkout) ----
|
||||||
@@ -156,6 +157,23 @@ export async function registerUserOrderRoutes(fastify) {
|
|||||||
return reply.code(409).send({ error: (e instanceof Error && e.message) || 'Недостаточно товара' })
|
return reply.code(409).send({ error: (e instanceof Error && e.message) || 'Недостаточно товара' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit notification events
|
||||||
|
request.server.eventBus.emit(NOTIFICATION_EVENTS.ORDER_CREATED, {
|
||||||
|
orderId: created.id,
|
||||||
|
userId,
|
||||||
|
totalCents: created.totalCents,
|
||||||
|
itemsCount: cartItems.length,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Also emit admin notification
|
||||||
|
request.server.eventBus.emit('order:created:admin', {
|
||||||
|
orderId: created.id,
|
||||||
|
userId,
|
||||||
|
userEmail: request.user.email || '',
|
||||||
|
totalCents: created.totalCents,
|
||||||
|
itemsCount: cartItems.length,
|
||||||
|
})
|
||||||
|
|
||||||
return reply.code(201).send({ orderId: created.id })
|
return reply.code(201).send({ orderId: created.id })
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { prisma } from '../lib/prisma.js'
|
|||||||
import { escapeHtml } from '../lib/escape-html.js'
|
import { escapeHtml } from '../lib/escape-html.js'
|
||||||
import { getOtherUploadMaxFileBytes } from '../lib/upload-limits.js'
|
import { getOtherUploadMaxFileBytes } from '../lib/upload-limits.js'
|
||||||
import { saveImageBufferToUploads } from '../lib/upload-images.js'
|
import { saveImageBufferToUploads } from '../lib/upload-images.js'
|
||||||
|
import { NOTIFICATION_EVENTS } from '../../shared/constants/notification-events.js'
|
||||||
|
|
||||||
export async function registerUserPaymentRoutes(fastify) {
|
export async function registerUserPaymentRoutes(fastify) {
|
||||||
fastify.post(
|
fastify.post(
|
||||||
@@ -105,6 +106,12 @@ export async function registerUserPaymentRoutes(fastify) {
|
|||||||
return reply.code(500).send({ error: 'Не удалось сохранить оплату' })
|
return reply.code(500).send({ error: 'Не удалось сохранить оплату' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request.server.eventBus.emit(NOTIFICATION_EVENTS.PAYMENT_STATUS_CHANGED, {
|
||||||
|
orderId: id,
|
||||||
|
userId,
|
||||||
|
paymentStatus: 'pending',
|
||||||
|
})
|
||||||
|
|
||||||
return { ok: true, status: 'PENDING_PAYMENT' }
|
return { ok: true, status: 'PENDING_PAYMENT' }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user