added more services and used table referencing
This commit is contained in:
parent
b885159784
commit
820fc9934c
@ -34,7 +34,7 @@ export class ChatGateway
|
||||
|
||||
private readonly logger = new Logger(ChatGateway.name);
|
||||
|
||||
private users = new Map<string, { userId: string; username: string }>(); // {socketId:{userId:'12rt3',username:'suman'}}
|
||||
// private users = new Map<string, { userId: string; username: string }>(); // {socketId:{userId:'12rt3',username:'suman'}} // redundant
|
||||
|
||||
afterInit(server: Server) {
|
||||
this.logger.log('/chat namespace initialized');
|
||||
@ -61,11 +61,6 @@ export class ChatGateway
|
||||
id: payload.sub, // mongo id
|
||||
username: payload.username,
|
||||
};
|
||||
// in memeory
|
||||
this.users.set(client.id, {
|
||||
userId: payload.sub,
|
||||
username: payload.username,
|
||||
});
|
||||
|
||||
this.logger.log(`[+] Authenticated: ${payload.username} (${client.id})`);
|
||||
// tell the new client their own info
|
||||
@ -89,9 +84,8 @@ export class ChatGateway
|
||||
}
|
||||
|
||||
handleDisconnect(client: Socket) {
|
||||
const user = this.users.get(client.id);
|
||||
const user=client.data?.user
|
||||
if (user) {
|
||||
this.users.delete(client.id);
|
||||
this.logger.log(`[-] Disconnected: ${user.username} (${client.id})`);
|
||||
this.server.emit('serverNotice', `[-] ${user.username} left`);
|
||||
}
|
||||
@ -100,13 +94,13 @@ export class ChatGateway
|
||||
@UseGuards(WsJwtGuard)
|
||||
@SubscribeMessage('joinRoom')
|
||||
handleJoinRoom(
|
||||
@MessageBody() body: JoinLeaveRoomDto, //roomId
|
||||
@MessageBody() body: JoinLeaveRoomDto, //roomId(conversation id)
|
||||
@ConnectedSocket() client: Socket,
|
||||
) {
|
||||
// extract user
|
||||
const username = client.data.user.username;
|
||||
|
||||
// join user in the room(front-end will provide roomId(standard appraoch))
|
||||
// join user in the room
|
||||
client.join(body.roomId);
|
||||
|
||||
// tell the joiner he successfully joined
|
||||
@ -170,7 +164,6 @@ export class ChatGateway
|
||||
@MessageBody() body: { targetUserId: string },
|
||||
@ConnectedSocket() client: Socket,
|
||||
) {
|
||||
|
||||
return this.chatService.handleStartP2P(body.targetUserId, client, this.server);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import { IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator';
|
||||
|
||||
export class ChatMessageDto {
|
||||
@IsString({ message: 'text must be a string' })
|
||||
@IsNotEmpty({ message: 'text cannot be empty' })
|
||||
@MinLength(1, { message: 'Message is too short' })
|
||||
@MaxLength(500, { message: 'Message cannot exceed 500 characters' })
|
||||
text!: string;
|
||||
}
|
||||
@ -1,8 +1,17 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
} from '@nestjs/common';
|
||||
import { ConversationsService } from './conversations.service';
|
||||
import { CreateConversationDto } from './dto/create-conversation.dto';
|
||||
import { UpdateConversationDto } from './dto/update-conversation.dto';
|
||||
import { ApiOperation } from '@nestjs/swagger';
|
||||
import path from 'path';
|
||||
|
||||
@Controller('conversations')
|
||||
export class ConversationsController {
|
||||
@ -14,24 +23,25 @@ export class ConversationsController {
|
||||
return await this.conversationsService.create(createConversationDto);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'Get all conversations' })
|
||||
@Get()
|
||||
async findAll() {
|
||||
return await this.conversationsService.findAll();
|
||||
@ApiOperation({ summary: 'Get all conversations where the user is in' })
|
||||
@Get(':userId')
|
||||
async findAll(@Param('userId') userId: string) {
|
||||
return await this.conversationsService.findAll(userId);
|
||||
}
|
||||
|
||||
// @Get(':id')
|
||||
// findOne(@Param('id') id: string) {
|
||||
// return this.conversationsService.findOne(+id);
|
||||
// }
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() updateConversationDto: UpdateConversationDto) {
|
||||
return this.conversationsService.update(id, updateConversationDto);
|
||||
@Patch(':convId/add-participant')
|
||||
async addParticipant(
|
||||
@Param('convId') convId: string,
|
||||
@Body('userId') userId: string,
|
||||
) {
|
||||
return await this.conversationsService.addParticipant(convId, userId);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.conversationsService.remove(+id);
|
||||
@Patch(':convId/remove-participant')
|
||||
async removeParticipant(
|
||||
@Param('convId') convId: string,
|
||||
@Body('userId') userId: string,
|
||||
) {
|
||||
return this.conversationsService.removeParticipant(convId, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,45 +1,92 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
ConflictException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { CreateConversationDto } from './dto/create-conversation.dto';
|
||||
import { UpdateConversationDto } from './dto/update-conversation.dto';
|
||||
import { Conversation, ConversationDocument, ConversationType } from './schemas/conversation.schema';
|
||||
import {
|
||||
Conversation,
|
||||
ConversationDocument,
|
||||
ConversationType,
|
||||
} from './schemas/conversation.schema';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { UserDocument } from 'src/users/schemas/user.schema';
|
||||
|
||||
type PopulatedConversation = Omit<ConversationDocument, 'participants'> & {
|
||||
participants: UserDocument[];
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class ConversationsService {
|
||||
|
||||
constructor(
|
||||
@InjectModel(Conversation.name) private conversationModel: Model<ConversationDocument>,
|
||||
|
||||
@InjectModel(Conversation.name)
|
||||
private conversationModel: Model<ConversationDocument>,
|
||||
) {}
|
||||
async create(createConversationDto: CreateConversationDto):Promise<ConversationDocument> {
|
||||
return await this.conversationModel.create(createConversationDto)
|
||||
async create(
|
||||
createConversationDto: CreateConversationDto,
|
||||
): Promise<ConversationDocument> {
|
||||
return await this.conversationModel.create(createConversationDto);
|
||||
}
|
||||
|
||||
async findAll():Promise<ConversationDocument[]> {
|
||||
return await this.conversationModel.find().exec()
|
||||
async findAll(userId: string): Promise<PopulatedConversation[]> {
|
||||
return await this.conversationModel
|
||||
.find({ participants: userId })
|
||||
.populate<{ participants: UserDocument[] }>('participants', 'name')
|
||||
.exec();
|
||||
}
|
||||
|
||||
async findP2p(senderId:string, targetUserId:string){
|
||||
async findP2p(
|
||||
senderId: string,
|
||||
targetUserId: string,
|
||||
): Promise<ConversationDocument | null> {
|
||||
return await this.conversationModel.findOne({
|
||||
type: ConversationType.P2P,
|
||||
participants: { $all: [senderId, targetUserId], $size: 2 },
|
||||
});
|
||||
}
|
||||
|
||||
// findOne(id: number) {
|
||||
// return `This action returns a #${id} conversation`;
|
||||
// }
|
||||
|
||||
async update(id: string, updateConversationDto: UpdateConversationDto) {
|
||||
await this.conversationModel.findByIdAndUpdate(
|
||||
{_id:id},
|
||||
updateConversationDto,
|
||||
{new:true}
|
||||
)
|
||||
async addParticipant(
|
||||
convId: string,
|
||||
userId: string,
|
||||
): Promise<ConversationDocument | null> {
|
||||
const conv = await this.conversationModel.findById(convId);
|
||||
if (!conv) {
|
||||
throw new NotFoundException('Conversation not found');
|
||||
}
|
||||
// prevent modifying p2p chat
|
||||
if (conv.type === 'p2p') {
|
||||
throw new ConflictException('Can not add users to P2P chat');
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} conversation`;
|
||||
return await this.conversationModel.findByIdAndUpdate(
|
||||
convId,
|
||||
{
|
||||
$addToSet: { participants: userId }, // prevent duplicate addition
|
||||
},
|
||||
{ new: true },
|
||||
);
|
||||
}
|
||||
|
||||
async removeParticipant(
|
||||
conversationId: string,
|
||||
userId: string,
|
||||
): Promise<ConversationDocument | null> {
|
||||
const convo = await this.conversationModel.findById(conversationId);
|
||||
|
||||
if (!convo) throw new NotFoundException('Conversation not found');
|
||||
|
||||
if (convo.type === 'p2p') {
|
||||
throw new ConflictException('Can not remove users from P2P chat');
|
||||
}
|
||||
|
||||
return await this.conversationModel.findByIdAndUpdate(
|
||||
conversationId,
|
||||
{
|
||||
$pull: { participants: userId },
|
||||
},
|
||||
{ new: true },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { Document } from "mongoose";
|
||||
import { Document, Types } from "mongoose";
|
||||
|
||||
export type ConversationDocument = Conversation & Document;
|
||||
export enum ConversationType{
|
||||
@ -16,13 +16,13 @@ export class Conversation {
|
||||
})
|
||||
type!:ConversationType
|
||||
|
||||
@Prop({type:[String], required:true})
|
||||
participants!:string[]
|
||||
@Prop({type:[Types.ObjectId], ref:'User',required:true})
|
||||
participants!:Types.ObjectId[]
|
||||
|
||||
@Prop({required:false,default:null})
|
||||
name?:string // for p2p no name
|
||||
|
||||
createdAt!:Date // no prop
|
||||
createdAt!:Date // no prop decorator
|
||||
}
|
||||
|
||||
export const ConversationSchema = SchemaFactory.createForClass(Conversation);
|
||||
|
||||
@ -1,34 +1,16 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import { MessagesService } from './messages.service';
|
||||
import { CreateMessageDto } from './dto/create-message.dto';
|
||||
import { UpdateMessageDto } from './dto/update-message.dto';
|
||||
|
||||
@Controller('messages')
|
||||
export class MessagesController {
|
||||
constructor(private readonly messagesService: MessagesService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() createMessageDto: CreateMessageDto) {
|
||||
return this.messagesService.create(createMessageDto);
|
||||
|
||||
@Get(':convId')
|
||||
async findAll(@Param('convId') conId:string) {
|
||||
return await this.messagesService.findAll(conId);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.messagesService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.messagesService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() updateMessageDto: UpdateMessageDto) {
|
||||
return this.messagesService.update(+id, updateMessageDto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.messagesService.remove(+id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,11 @@ import { UpdateMessageDto } from './dto/update-message.dto';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Message, MessageDocument } from './schemas/message.schema';
|
||||
import { Model } from 'mongoose';
|
||||
import { UserDocument } from 'src/users/schemas/user.schema';
|
||||
|
||||
type PopulatedMessage = Omit<MessageDocument, 'senderId'> & {
|
||||
senderId: UserDocument;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class MessagesService {
|
||||
@ -14,19 +19,10 @@ export class MessagesService {
|
||||
return await this.MessageModel.create(createMessageDto);
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all messages`;
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} message`;
|
||||
}
|
||||
|
||||
update(id: number, updateMessageDto: UpdateMessageDto) {
|
||||
return `This action updates a #${id} message`;
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} message`;
|
||||
async findAll(convId: string): Promise<PopulatedMessage[]> {
|
||||
return await this.MessageModel.find({ conversationId: convId })
|
||||
.populate<{ senderId: UserDocument }>('senderId', 'name')
|
||||
.sort({ createdAt: 1 })
|
||||
.exec();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document } from 'mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
|
||||
export type MessageDocument = Message & Document;
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Message {
|
||||
@Prop({ required: true })
|
||||
conversationId!: string;
|
||||
@Prop({ type: Types.ObjectId, ref: 'Conversation', required: true })
|
||||
conversationId!: Types.ObjectId;
|
||||
|
||||
@Prop({ required: true })
|
||||
senderId!: string;
|
||||
@Prop({ type: Types.ObjectId, ref: 'User', required: true })
|
||||
senderId!: Types.ObjectId; //we can pass string
|
||||
|
||||
@Prop({ required: false })
|
||||
text!: string;
|
||||
|
||||
// no prop
|
||||
createdAt!:Date
|
||||
createdAt!: Date;
|
||||
}
|
||||
|
||||
export const MessageSchema = SchemaFactory.createForClass(Message);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user