added basic auth, schemas and some ws event handling
This commit is contained in:
parent
c25480ded7
commit
538198840c
785
package-lock.json
generated
785
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -21,10 +21,21 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/config": "^4.0.4",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/jwt": "^11.0.2",
|
||||
"@nestjs/mapped-types": "*",
|
||||
"@nestjs/mongoose": "^11.0.4",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/platform-socket.io": "^11.1.19",
|
||||
"@nestjs/swagger": "^11.3.0",
|
||||
"@nestjs/websockets": "^11.1.19",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.15.1",
|
||||
"mongoose": "^9.4.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1"
|
||||
"rxjs": "^7.8.1",
|
||||
"socket.io": "^4.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
|
||||
@ -1,9 +1,32 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { UsersModule } from './users/users.module';
|
||||
import { ConversationsModule } from './conversations/conversations.module';
|
||||
import { MessagesModule } from './messages/messages.module';
|
||||
import { ChatModule } from './chat/chat.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }), //access env anywhere
|
||||
|
||||
// connect db
|
||||
MongooseModule.forRootAsync({
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
uri: configService.get<string>('DB_URI'),
|
||||
}),
|
||||
}),
|
||||
UsersModule,
|
||||
ConversationsModule,
|
||||
MessagesModule,
|
||||
ChatModule,
|
||||
AuthModule,
|
||||
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
|
||||
38
src/auth/auth.controller.ts
Normal file
38
src/auth/auth.controller.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { UsersService } from './../users/users.service';
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { ApiOperation } from '@nestjs/swagger';
|
||||
import { CreateUserDto } from 'src/users/dto/create-user.dto';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService,
|
||||
|
||||
) {}
|
||||
|
||||
@Post('/token')
|
||||
@ApiOperation({ summary: 'sign in', description: 'Authenticates a user Returns a JWT access token.' })
|
||||
create(@Body() createUserDto: CreateUserDto ) {
|
||||
return this.authService.create(createUserDto);
|
||||
}
|
||||
|
||||
// @Get()
|
||||
// findAll() {
|
||||
// return this.authService.findAll();
|
||||
// }
|
||||
|
||||
// @Get(':id')
|
||||
// findOne(@Param('id') id: string) {
|
||||
// return this.authService.findOne(+id);
|
||||
// }
|
||||
|
||||
// @Patch(':id')
|
||||
// update(@Param('id') id: string, @Body() updateAuthDto: UpdateAuthDto) {
|
||||
// return this.authService.update(+id, updateAuthDto);
|
||||
// }
|
||||
|
||||
// @Delete(':id')
|
||||
// remove(@Param('id') id: string) {
|
||||
// return this.authService.remove(+id);
|
||||
// }
|
||||
}
|
||||
23
src/auth/auth.module.ts
Normal file
23
src/auth/auth.module.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { UsersService } from 'src/users/users.service';
|
||||
import { JwtModule, JwtService } from '@nestjs/jwt';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { UsersModule } from 'src/users/users.module';
|
||||
|
||||
@Module({
|
||||
imports:[UsersModule,
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: (config: ConfigService) => ({
|
||||
secret: config.get('JWT_SECRET'),
|
||||
signOptions: { expiresIn: config.get('JWT_EXPIRES_IN', '7') },
|
||||
}),
|
||||
}),
|
||||
],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
||||
45
src/auth/auth.service.ts
Normal file
45
src/auth/auth.service.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { CreateUserDto } from './../users/dto/create-user.dto';
|
||||
import { User } from './../users/schemas/user.schema';
|
||||
import { UsersService } from './../users/users.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UpdateAuthDto } from './dto/update-auth.dto';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private readonly usersService: UsersService,
|
||||
private readonly jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
async create(createUserDto: CreateUserDto) {
|
||||
// check if user exist
|
||||
let user=await this.usersService.findOne(createUserDto.name)
|
||||
if(!user){
|
||||
user = await this.usersService.create(createUserDto);
|
||||
}
|
||||
const token = this.jwtService.sign({
|
||||
// sub: user.name.toLowerCase().replace(/\s+/g, '-'), // as name is unique
|
||||
sub:user._id, // mongodb id
|
||||
username: user.name,
|
||||
});
|
||||
|
||||
return { token, username: user.name };
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all auth`;
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} auth`;
|
||||
}
|
||||
|
||||
update(id: number, updateAuthDto: UpdateAuthDto) {
|
||||
return `This action updates a #${id} auth`;
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} auth`;
|
||||
}
|
||||
}
|
||||
1
src/auth/dto/create-auth.dto.ts
Normal file
1
src/auth/dto/create-auth.dto.ts
Normal file
@ -0,0 +1 @@
|
||||
export class CreateAuthDto {}
|
||||
4
src/auth/dto/update-auth.dto.ts
Normal file
4
src/auth/dto/update-auth.dto.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateAuthDto } from './create-auth.dto';
|
||||
|
||||
export class UpdateAuthDto extends PartialType(CreateAuthDto) {}
|
||||
184
src/chat/chat.gateway.ts
Normal file
184
src/chat/chat.gateway.ts
Normal file
@ -0,0 +1,184 @@
|
||||
import {
|
||||
WebSocketGateway,
|
||||
SubscribeMessage,
|
||||
MessageBody,
|
||||
OnGatewayInit,
|
||||
OnGatewayConnection,
|
||||
OnGatewayDisconnect,
|
||||
WebSocketServer,
|
||||
WsException,
|
||||
ConnectedSocket,
|
||||
} from '@nestjs/websockets';
|
||||
import { ChatService } from './chat.service';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import { Logger, UseGuards } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { JoinLeaveRoomDto } from './dto/join-leave-room.dto';
|
||||
import { WsJwtGuard } from './guards/ws-jwt.guard';
|
||||
import { RoomMessageDto } from './dto/room-message.dto';
|
||||
|
||||
@WebSocketGateway({
|
||||
namespace: '/chat',
|
||||
cors: { origin: '*' },
|
||||
})
|
||||
export class ChatGateway
|
||||
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
|
||||
{
|
||||
constructor(
|
||||
private readonly chatService: ChatService,
|
||||
private readonly jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
@WebSocketServer()
|
||||
server!: Server;
|
||||
|
||||
private readonly logger = new Logger(ChatGateway.name);
|
||||
|
||||
private users = new Map<string, { userId: string; username: string }>(); // {socketId:{userId:'12rt3',username:'suman'}}
|
||||
|
||||
afterInit(server: Server) {
|
||||
this.logger.log('/chat namespace initialized');
|
||||
// server.setMaxListeners(20);
|
||||
}
|
||||
|
||||
handleConnection(client: Socket) {
|
||||
try {
|
||||
// st-1: Extract token
|
||||
const token =
|
||||
client.handshake.auth?.token || client.handshake.headers?.authorization; // fallback
|
||||
|
||||
if (!token) {
|
||||
throw new WsException('No token provided');
|
||||
}
|
||||
// strip 'Bearer' prefix if present
|
||||
const rawToken = token.replace(/^Bearer\s+/i, '');
|
||||
|
||||
// st-2: verify jwt
|
||||
const payload = this.jwtService.verify(rawToken);
|
||||
|
||||
// st-3:attach user to client
|
||||
client.data.user = {
|
||||
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
|
||||
client.emit('connected', {
|
||||
YourId: client.id,
|
||||
username: payload.username,
|
||||
message: `welcome, ${payload.username}`,
|
||||
});
|
||||
|
||||
// tell everyone else someone joined
|
||||
client.broadcast.emit('serverNotice', `[+] ${payload.username} joined`);
|
||||
|
||||
} catch (err) {
|
||||
// st-4: Disconnect unauthorized clients
|
||||
this.logger.warn(`[!] Rejected connection: ${err}`);
|
||||
client.emit('authError', {
|
||||
mesage: err || 'Unauthorized',
|
||||
});
|
||||
|
||||
client.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
handleDisconnect(client: Socket) {
|
||||
const user = this.users.get(client.id);
|
||||
if (user) {
|
||||
this.users.delete(client.id);
|
||||
this.logger.log(`[-] Disconnected: ${user.username} (${client.id})`);
|
||||
this.server.emit('serverNotice', `[-] ${user.username} left`);
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(WsJwtGuard)
|
||||
@SubscribeMessage('joinRoom')
|
||||
handleJoinRoom(
|
||||
@MessageBody() body: JoinLeaveRoomDto, //roomId
|
||||
@ConnectedSocket() client: Socket,
|
||||
) {
|
||||
|
||||
// extract user
|
||||
const username=client.data.user.username;
|
||||
|
||||
// join user in the room(front-end will provide roomId(standard appraoch))
|
||||
client.join(body.roomId)
|
||||
|
||||
// tell the joiner he successfully joined
|
||||
client.emit("roomJoined",{
|
||||
roomId:body.roomId,
|
||||
message:`You joined room: ${body.roomId}`,
|
||||
})
|
||||
|
||||
// also tell others in the room except the new joiner
|
||||
client.to(body.roomId).emit("roomNotice",{
|
||||
roomId: body.roomId,
|
||||
message: `[+] ${username} joined the room`,
|
||||
})
|
||||
|
||||
// return acknowledgement
|
||||
return { success: true, roomId: body.roomId };
|
||||
|
||||
}
|
||||
|
||||
@UseGuards(WsJwtGuard)
|
||||
@SubscribeMessage('leaveRoom')
|
||||
handleLeaveRoom(
|
||||
@MessageBody() body: JoinLeaveRoomDto, //roomId
|
||||
@ConnectedSocket() client: Socket,
|
||||
) {
|
||||
// extract user
|
||||
const username=client.data.user.username;
|
||||
|
||||
// remove the user from the room
|
||||
client.leave(body.roomId)
|
||||
|
||||
// confirm to the leaver
|
||||
client.emit('roomLeft', { roomId: body.roomId });
|
||||
|
||||
// Notify remaining memebers only
|
||||
client.to(body.roomId).emit('roomNotice', {
|
||||
roomId: body.roomId,
|
||||
message: `[-] ${username} left the room`,
|
||||
});
|
||||
|
||||
return { success: true, roomId: body.roomId };
|
||||
}
|
||||
|
||||
@UseGuards(WsJwtGuard)
|
||||
@SubscribeMessage('roomMessage')
|
||||
handleRoomMessage(
|
||||
@MessageBody() body:RoomMessageDto, //text:string
|
||||
@ConnectedSocket() sender:Socket
|
||||
){
|
||||
// extract user
|
||||
const username=sender.data.user.username
|
||||
// Check if user is in room
|
||||
const isMember = sender.rooms.has(body.roomId);
|
||||
if (!isMember) {
|
||||
sender.emit('roomError', {
|
||||
message: 'You are not allowed',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(`[ROOM:${body.roomId}] ${username}: ${body.text}`);
|
||||
|
||||
// send everone in the room(including the sender)
|
||||
this.server.to(body.roomId).emit('roomMessage', {
|
||||
roomId: body.roomId,
|
||||
senderId: sender.id,
|
||||
username,
|
||||
text: body.text,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
20
src/chat/chat.module.ts
Normal file
20
src/chat/chat.module.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ChatService } from './chat.service';
|
||||
import { ChatGateway } from './chat.gateway';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: (config: ConfigService) => ({
|
||||
secret: config.get('JWT_SECRET'),
|
||||
signOptions: { expiresIn: config.get('JWT_EXPIRES_IN', '7') },
|
||||
}),
|
||||
}),
|
||||
],
|
||||
providers: [ChatGateway, ChatService],
|
||||
})
|
||||
export class ChatModule {}
|
||||
6
src/chat/chat.service.ts
Normal file
6
src/chat/chat.service.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class ChatService {
|
||||
|
||||
}
|
||||
9
src/chat/dto/chat-message.dto.ts
Normal file
9
src/chat/dto/chat-message.dto.ts
Normal file
@ -0,0 +1,9 @@
|
||||
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;
|
||||
}
|
||||
12
src/chat/dto/join-leave-room.dto.ts
Normal file
12
src/chat/dto/join-leave-room.dto.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { IsNotEmpty, IsString, Matches, MaxLength } from "class-validator";
|
||||
|
||||
export class JoinLeaveRoomDto {
|
||||
@IsString()
|
||||
@IsNotEmpty({ message: 'roomId cannot be empty' })
|
||||
@MaxLength(50, { message: 'Room name too long (max 50 chars)' })
|
||||
@Matches(/^[a-z0-9-_]+$/, {
|
||||
message:
|
||||
'roomId can only contain lowercase letters, numbers, hyphens and underscores',
|
||||
})
|
||||
roomId!: string;
|
||||
}
|
||||
13
src/chat/dto/room-message.dto.ts
Normal file
13
src/chat/dto/room-message.dto.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { IsNotEmpty, IsString, Matches, MaxLength, MinLength } from "class-validator";
|
||||
export class RoomMessageDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Matches(/^[a-z0-9-_]+$/, { message: 'Invalid roomId format' })
|
||||
roomId!: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty({ message: 'Message cannot be empty' })
|
||||
@MinLength(1)
|
||||
@MaxLength(500, { message: 'Message cannot exceed 500 characters' })
|
||||
text!: string;
|
||||
}
|
||||
20
src/chat/guards/ws-jwt.guard.ts
Normal file
20
src/chat/guards/ws-jwt.guard.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { WsException } from '@nestjs/websockets';
|
||||
import { Socket } from 'socket.io';
|
||||
|
||||
@Injectable()
|
||||
export class WsJwtGuard implements CanActivate {
|
||||
canActivate(context: ExecutionContext): boolean {
|
||||
const client: Socket = context.switchToWs().getClient<Socket>();
|
||||
|
||||
// client.data.user was set in handleConnection() after JWT verification
|
||||
const user = client.data?.user;
|
||||
if (!user) {
|
||||
throw new WsException('Unauthorized — no authenticated user on socket');
|
||||
}
|
||||
|
||||
// optionally check role
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
34
src/conversations/conversations.controller.ts
Normal file
34
src/conversations/conversations.controller.ts
Normal file
@ -0,0 +1,34 @@
|
||||
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';
|
||||
|
||||
@Controller('conversations')
|
||||
export class ConversationsController {
|
||||
constructor(private readonly conversationsService: ConversationsService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() createConversationDto: CreateConversationDto) {
|
||||
return this.conversationsService.create(createConversationDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.conversationsService.findAll();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.conversationsService.remove(+id);
|
||||
}
|
||||
}
|
||||
18
src/conversations/conversations.module.ts
Normal file
18
src/conversations/conversations.module.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConversationsService } from './conversations.service';
|
||||
import { ConversationsController } from './conversations.controller';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { Conversation, ConversationSchema } from './schemas/conversation.schema';
|
||||
|
||||
@Module({
|
||||
imports:[
|
||||
MongooseModule.forFeature([
|
||||
{name:Conversation.name,schema:ConversationSchema},
|
||||
|
||||
])
|
||||
],
|
||||
controllers: [ConversationsController],
|
||||
providers: [ConversationsService],
|
||||
exports:[ConversationsService]
|
||||
})
|
||||
export class ConversationsModule {}
|
||||
34
src/conversations/conversations.service.ts
Normal file
34
src/conversations/conversations.service.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateConversationDto } from './dto/create-conversation.dto';
|
||||
import { UpdateConversationDto } from './dto/update-conversation.dto';
|
||||
import { Conversation, ConversationDocument } from './schemas/conversation.schema';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
|
||||
@Injectable()
|
||||
export class ConversationsService {
|
||||
|
||||
constructor(
|
||||
@InjectModel(Conversation.name) private userModel: Model<ConversationDocument>,
|
||||
|
||||
) {}
|
||||
create(createConversationDto: CreateConversationDto) {
|
||||
return 'This action adds a new conversation';
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all conversations`;
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} conversation`;
|
||||
}
|
||||
|
||||
update(id: number, updateConversationDto: UpdateConversationDto) {
|
||||
return `This action updates a #${id} conversation`;
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} conversation`;
|
||||
}
|
||||
}
|
||||
1
src/conversations/dto/create-conversation.dto.ts
Normal file
1
src/conversations/dto/create-conversation.dto.ts
Normal file
@ -0,0 +1 @@
|
||||
export class CreateConversationDto {}
|
||||
4
src/conversations/dto/update-conversation.dto.ts
Normal file
4
src/conversations/dto/update-conversation.dto.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateConversationDto } from './create-conversation.dto';
|
||||
|
||||
export class UpdateConversationDto extends PartialType(CreateConversationDto) {}
|
||||
27
src/conversations/schemas/conversation.schema.ts
Normal file
27
src/conversations/schemas/conversation.schema.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
|
||||
export type ConversationDocument = Conversation & Document;
|
||||
export enum ConversationType{
|
||||
P2P='p2p',
|
||||
GROUP='group'
|
||||
}
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Conversation {
|
||||
@Prop({
|
||||
type:String,
|
||||
enum:ConversationType,
|
||||
required:true
|
||||
})
|
||||
type!:ConversationType
|
||||
|
||||
@Prop({type:[String], required:true})
|
||||
participants!:string[]
|
||||
|
||||
@Prop({required:false,default:null})
|
||||
name!:string // for p2p no name
|
||||
|
||||
createdAt!:Date
|
||||
}
|
||||
|
||||
export const ConversationSchema = SchemaFactory.createForClass(Conversation);
|
||||
33
src/main.ts
33
src/main.ts
@ -1,8 +1,39 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
await app.listen(process.env.PORT ?? 3000);
|
||||
app.enableCors({ origin: '*' });
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist:true, // strips unknown properties from @Body()
|
||||
forbidNonWhitelisted:true, // throws 400 if unknown props are sent
|
||||
transform:true // converts query string numbers/booleans from strings → correct types
|
||||
})
|
||||
)
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Chat app')
|
||||
.setDescription('The chat API description')
|
||||
.setVersion('1.0')
|
||||
// .addBearerAuth(
|
||||
// {
|
||||
// type: 'http',
|
||||
// scheme: 'bearer',
|
||||
// bearerFormat: 'JWT',
|
||||
// in: 'header',
|
||||
// },
|
||||
// 'jwt',
|
||||
// )
|
||||
.build();
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api', app, document);
|
||||
|
||||
const configService=app.get(ConfigService)
|
||||
const port=configService.get<number>('PORT')|| 3000;
|
||||
|
||||
await app.listen(port);
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
1
src/messages/dto/create-message.dto.ts
Normal file
1
src/messages/dto/create-message.dto.ts
Normal file
@ -0,0 +1 @@
|
||||
export class CreateMessageDto {}
|
||||
4
src/messages/dto/update-message.dto.ts
Normal file
4
src/messages/dto/update-message.dto.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateMessageDto } from './create-message.dto';
|
||||
|
||||
export class UpdateMessageDto extends PartialType(CreateMessageDto) {}
|
||||
34
src/messages/messages.controller.ts
Normal file
34
src/messages/messages.controller.ts
Normal file
@ -0,0 +1,34 @@
|
||||
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()
|
||||
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);
|
||||
}
|
||||
}
|
||||
17
src/messages/messages.module.ts
Normal file
17
src/messages/messages.module.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MessagesService } from './messages.service';
|
||||
import { MessagesController } from './messages.controller';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { Message, MessageSchema } from './schemas/message.schema';
|
||||
|
||||
@Module({
|
||||
imports:[
|
||||
MongooseModule.forFeature([
|
||||
{name:Message.name,schema:MessageSchema},
|
||||
])
|
||||
],
|
||||
controllers: [MessagesController],
|
||||
providers: [MessagesService],
|
||||
exports:[MessagesService]
|
||||
})
|
||||
export class MessagesModule {}
|
||||
30
src/messages/messages.service.ts
Normal file
30
src/messages/messages.service.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateMessageDto } from './dto/create-message.dto';
|
||||
import { UpdateMessageDto } from './dto/update-message.dto';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Message, MessageDocument } from './schemas/message.schema';
|
||||
import { Model } from 'mongoose';
|
||||
|
||||
@Injectable()
|
||||
export class MessagesService {
|
||||
constructor( @InjectModel(Message.name) private MessageModel: Model<MessageDocument>,){}
|
||||
create(createMessageDto: CreateMessageDto) {
|
||||
return 'This action adds a new message';
|
||||
}
|
||||
|
||||
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`;
|
||||
}
|
||||
}
|
||||
19
src/messages/schemas/message.schema.ts
Normal file
19
src/messages/schemas/message.schema.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
|
||||
export type MessageDocument = Message & Document;
|
||||
|
||||
@Schema({timestamps:true})
|
||||
export class Message {
|
||||
@Prop({ required:true})
|
||||
conversationId!:string
|
||||
|
||||
@Prop({ required:true})
|
||||
senderId!:string
|
||||
|
||||
@Prop({ required:false})
|
||||
text!:string
|
||||
|
||||
createdAt!:Date
|
||||
}
|
||||
|
||||
export const MessageSchema = SchemaFactory.createForClass(Message);
|
||||
9
src/users/dto/create-user.dto.ts
Normal file
9
src/users/dto/create-user.dto.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import {IsNotEmpty, IsString } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateUserDto {
|
||||
@ApiProperty({ example: 'Jane Doe', description: 'Full name of the user' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name!: string;
|
||||
}
|
||||
4
src/users/dto/update-user.dto.ts
Normal file
4
src/users/dto/update-user.dto.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateUserDto } from './create-user.dto';
|
||||
|
||||
export class UpdateUserDto extends PartialType(CreateUserDto) {}
|
||||
11
src/users/schemas/user.schema.ts
Normal file
11
src/users/schemas/user.schema.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { Document } from 'mongoose';
|
||||
|
||||
export type UserDocument = User & Document;
|
||||
|
||||
@Schema({timestamps:true})
|
||||
export class User {
|
||||
@Prop({required:true, unique:true})
|
||||
name!:string
|
||||
}
|
||||
export const UserSchema = SchemaFactory.createForClass(User);
|
||||
34
src/users/users.controller.ts
Normal file
34
src/users/users.controller.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
// @Post()
|
||||
// create(@Body() createUserDto: CreateUserDto) {
|
||||
// return this.usersService.create(createUserDto);
|
||||
// }
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.usersService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.usersService.findOne(id);
|
||||
}
|
||||
|
||||
// @Patch(':id')
|
||||
// update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
|
||||
// return this.usersService.update(+id, updateUserDto);
|
||||
// }
|
||||
|
||||
// @Delete(':id')
|
||||
// remove(@Param('id') id: string) {
|
||||
// return this.usersService.remove(+id);
|
||||
// }
|
||||
}
|
||||
18
src/users/users.module.ts
Normal file
18
src/users/users.module.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersController } from './users.controller';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { User, UserSchema } from './schemas/user.schema';
|
||||
|
||||
@Module({
|
||||
imports:[
|
||||
MongooseModule.forFeature([
|
||||
{name:User.name,schema:UserSchema},
|
||||
|
||||
])
|
||||
],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
exports:[UsersService]
|
||||
})
|
||||
export class UsersModule {}
|
||||
33
src/users/users.service.ts
Normal file
33
src/users/users.service.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { User, UserDocument } from './schemas/user.schema';
|
||||
import { Model } from 'mongoose';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
|
||||
constructor(@InjectModel(User.name) private userModel:Model<UserDocument>){
|
||||
|
||||
}
|
||||
async create(createUserDto: CreateUserDto):Promise<UserDocument> {
|
||||
return await this.userModel.create(createUserDto)
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all users`;
|
||||
}
|
||||
|
||||
async findOne(name: string):Promise<UserDocument | null> {
|
||||
return await this.userModel.findOne({name}).exec()
|
||||
}
|
||||
|
||||
// update(id: number, updateUserDto: UpdateUserDto) {
|
||||
// return `This action updates a #${id} user`;
|
||||
// }
|
||||
|
||||
// remove(id: number) {
|
||||
// return `This action removes a #${id} user`;
|
||||
// }
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user