import type { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' import { Type } from 'typebox' import { ChatMessageSchema, NewChatMessageSchema } from '../schemas/chat.ts' const plugin: FastifyPluginAsyncTypebox = async (fastify) => { fastify.post( '/chat/send', { schema: { summary: 'Send message', tags: ['Chat'], operationId: 'chat.send', body: NewChatMessageSchema, response: { 200: ChatMessageSchema, }, }, }, async (req, reply) => { const user = req.user! const message = await fastify.prisma.message.create({ data: { text: req.body.text, senderId: user.id, attachments: { create: (req.body.attachments ?? []).map((attachmentId) => { return { attachment: { connect: { id: attachmentId, }, }, } }), }, }, }) if (!message) { return reply.unprocessableEntity() } const response = { id: message.id, senderId: user.id, text: message.text, createdAt: message.createdAt.toISOString(), updatedAt: message.updatedAt.toISOString(), attachments: req.body.attachments ?? [], } fastify.bus.emit('chat:new-message', response) return response }, ) fastify.get( '/chat', { schema: { summary: 'Get messages', tags: ['Chat'], operationId: 'chat.messages', querystring: Type.Object({ cursor: Type.Optional(Type.String({ format: 'uuid', description: 'Cursor to message' })), limit: Type.Number({ minimum: 1, maximum: 100, default: 10 }), }), response: { 200: Type.Object({ messages: Type.Array(ChatMessageSchema), nextCursor: Type.Optional(Type.String({ format: 'uuid', description: 'Cursor to last message' })), }), }, }, }, // eslint-disable-next-line ts/ban-ts-comment // @ts-expect-error async (req) => { const messages = await fastify.prisma.message.findMany({ orderBy: { createdAt: 'desc' }, take: req.query.limit + 1, include: { attachments: true }, ...(req.query.cursor && { cursor: { id: req.query.cursor, }, // skip: 1, }), }) const hasMore = messages.length > req.query.limit const cursorMessage = hasMore ? messages.pop() : undefined return { messages: messages.map((message) => { return { ...message, createdAt: message.createdAt.toISOString(), updatedAt: message.updatedAt.toISOString(), attachments: message.attachments.map(({ attachmentId }) => attachmentId), } }), nextCursor: cursorMessage?.id, } }, ) } export default plugin