Messenger/packages/backend/src/modules/conversations/conversations.routes.ts
Заид Омар Медхат | Zaid Omar Medhat f5b620111b redesign
2026-07-11 04:49:07 +05:00

296 lines
9.3 KiB
TypeScript

import type { FastifyInstance } from 'fastify';
import {
createDirectBodySchema,
createGroupBodySchema,
createChannelBodySchema,
addMemberBodySchema,
setAvatarBodySchema,
readBodySchema,
conversationSchema,
conversationListSchema,
conversationMemberListSchema,
errorSchema,
} from '@altricade/core';
import type {
CreateDirectBody,
CreateGroupBody,
CreateChannelBody,
AddMemberBody,
ReadBody,
} from '@altricade/core';
const bearerAuth = [{ bearerAuth: [] }];
const idParamsSchema = {
type: 'object',
required: ['id'],
properties: { id: { type: 'string', format: 'uuid' } },
} as const;
const memberParamsSchema = {
type: 'object',
required: ['id', 'userId'],
properties: { id: { type: 'string', format: 'uuid' }, userId: { type: 'string', format: 'uuid' } },
} as const;
export const conversationsRoutes = (app: FastifyInstance): Promise<void> => {
app.get(
'/conversations',
{
schema: {
tags: ['conversations'],
summary: 'List my conversations (DMs + groups, newest first)',
security: bearerAuth,
response: { 200: conversationListSchema, 401: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
return reply.send(await app.conversationsService.list(user.id));
},
);
app.post<{ Body: CreateDirectBody }>(
'/conversations/direct',
{
schema: {
tags: ['conversations'],
summary: 'Start (or reopen) a direct conversation with a user',
security: bearerAuth,
body: createDirectBodySchema,
response: { 201: conversationSchema, 400: errorSchema, 401: errorSchema, 404: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
const conversation = await app.conversationsService.createDirect(user.id, request.body.username);
return reply.code(201).send(conversation);
},
);
app.post<{ Body: CreateGroupBody }>(
'/conversations',
{
schema: {
tags: ['conversations'],
summary: 'Create a group conversation',
security: bearerAuth,
body: createGroupBodySchema,
response: { 201: conversationSchema, 401: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
const conversation = await app.conversationsService.createGroup(
user.id,
request.body.title,
request.body.members ?? [],
);
return reply.code(201).send(conversation);
},
);
app.post<{ Body: CreateChannelBody }>(
'/conversations/channel',
{
schema: {
tags: ['conversations'],
summary: 'Create a channel (broadcast: only owner/admins post)',
security: bearerAuth,
body: createChannelBodySchema,
response: { 201: conversationSchema, 401: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
const conversation = await app.conversationsService.createChannel(
user.id,
request.body.title,
request.body.description ?? null,
request.body.members ?? [],
);
return reply.code(201).send(conversation);
},
);
app.get<{ Params: { id: string } }>(
'/conversations/:id',
{
schema: {
tags: ['conversations'],
summary: 'Get a conversation (member only)',
security: bearerAuth,
params: idParamsSchema,
response: { 200: conversationSchema, 401: errorSchema, 403: errorSchema, 404: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
return reply.send(await app.conversationsService.get(request.params.id, user.id));
},
);
app.get<{ Params: { id: string } }>(
'/conversations/:id/members',
{
schema: {
tags: ['conversations'],
summary: 'List conversation members (member only)',
security: bearerAuth,
params: idParamsSchema,
response: { 200: conversationMemberListSchema, 401: errorSchema, 403: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
return reply.send(await app.conversationsService.listMembers(request.params.id, user.id));
},
);
app.post<{ Params: { id: string }; Body: AddMemberBody }>(
'/conversations/:id/members',
{
schema: {
tags: ['conversations'],
summary: 'Add a member to a group (owner only)',
security: bearerAuth,
params: idParamsSchema,
body: addMemberBodySchema,
response: { 201: conversationMemberListSchema, 401: errorSchema, 403: errorSchema, 404: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
const members = await app.conversationsService.addMember(
request.params.id,
user.id,
request.body.username,
);
return reply.code(201).send(members);
},
);
app.delete<{ Params: { id: string; userId: string } }>(
'/conversations/:id/members/:userId',
{
schema: {
tags: ['conversations'],
summary: 'Remove a member (owner) or leave (self)',
security: bearerAuth,
params: memberParamsSchema,
response: { 401: errorSchema, 403: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
await app.conversationsService.removeMember(request.params.id, user.id, request.params.userId);
return reply.code(204).send();
},
);
app.post<{ Params: { id: string }; Body: { objectKey: string } }>(
'/conversations/:id/avatar',
{
schema: {
tags: ['conversations'],
summary: 'Set a group/channel photo (owner/admin, from an uploaded object key)',
security: bearerAuth,
params: idParamsSchema,
body: setAvatarBodySchema,
response: {
200: conversationSchema,
400: errorSchema,
401: errorSchema,
403: errorSchema,
404: errorSchema,
},
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
const imageUrl = app.mediaService.avatarPublicUrl(request.body.objectKey);
return reply.send(
await app.conversationsService.setAvatar(request.params.id, user.id, imageUrl),
);
},
);
app.post<{ Params: { id: string } }>(
'/conversations/:id/clear',
{
schema: {
tags: ['conversations'],
summary: 'Clear history for me (chat stays listed)',
security: bearerAuth,
params: idParamsSchema,
response: { 401: errorSchema, 403: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
await app.conversationsService.clear(request.params.id, user.id);
return reply.code(204).send();
},
);
app.post<{ Params: { id: string } }>(
'/conversations/:id/hide',
{
schema: {
tags: ['conversations'],
summary: 'Delete chat for me (returns on new activity)',
security: bearerAuth,
params: idParamsSchema,
response: { 401: errorSchema, 403: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
await app.conversationsService.hide(request.params.id, user.id);
return reply.code(204).send();
},
);
app.post<{ Params: { id: string }; Body: ReadBody }>(
'/conversations/:id/read',
{
schema: {
tags: ['live'],
summary: 'Mark read up to a sequence number',
security: bearerAuth,
params: idParamsSchema,
body: readBodySchema,
response: { 401: errorSchema, 403: errorSchema },
},
preHandler: app.authenticate,
},
async (request, reply) => {
const user = request.authUser;
if (user === undefined) return reply.code(401).send({ error: 'unauthorized' });
await app.conversationsService.markRead(request.params.id, user.id, request.body.seq);
return reply.code(204).send();
},
);
return Promise.resolve();
};