Browse Source

chat gateway push

master
nicolas-arnaud 2 years ago
parent
commit
03628572bd
  1. 8
      back/volume/src/chat/chat.controller.ts
  2. 143
      back/volume/src/chat/chat.gateway.ts
  3. 17
      back/volume/src/chat/chat.module.ts
  4. 16
      back/volume/src/chat/chat.service.ts
  5. 5
      back/volume/src/chat/entity/channel.entity.ts
  6. 9
      back/volume/src/chat/entity/connection.entity.ts
  7. 43
      back/volume/src/chat/message.service.ts
  8. 5
      back/volume/src/users/users.controller.ts
  9. 5
      back/volume/src/users/users.module.ts
  10. 25
      back/volume/src/users/users.service.ts

8
back/volume/src/chat/chat.controller.ts

@ -6,13 +6,12 @@ import {
Get, Get,
NotFoundException, NotFoundException,
Param, Param,
ParseIntPipe,
Post, Post,
UseGuards UseGuards
} from '@nestjs/common' } from '@nestjs/common'
import { AuthenticatedGuard } from 'src/auth/42-auth.guard' import { AuthenticatedGuard } from 'src/auth/42-auth.guard'
import { UsersService } from 'src/users/users.service' import { UsersService } from 'src/users/users.service'
import { ChannelService } from './chat.service' import { ChatService } from './chat.service'
import { CreateChannelDto } from './dto/create-channel.dto' import { CreateChannelDto } from './dto/create-channel.dto'
import { IdDto, PasswordDto, MuteDto } from './dto/updateUser.dto' import { IdDto, PasswordDto, MuteDto } from './dto/updateUser.dto'
@ -25,7 +24,7 @@ import { Profile } from 'passport-42'
@Controller('channels') @Controller('channels')
export class ChatController { export class ChatController {
constructor ( constructor (
private readonly channelService: ChannelService, private readonly channelService: ChatService,
private readonly usersService: UsersService private readonly usersService: UsersService
) {} ) {}
@ -135,7 +134,8 @@ export class ChatController {
throw new BadRequestException('You cannot mute the owner of the channel') throw new BadRequestException('You cannot mute the owner of the channel')
if (await this.channelService.getMuteDuration(channel.id, mute.data[0]) > 0) if (await this.channelService.getMuteDuration(channel.id, mute.data[0]) > 0)
throw new BadRequestException('User is already muted from this channel') throw new BadRequestException('User is already muted from this channel')
channel.muted.push(mute.data) let newMute: Array<number> = [mute.data[0], Date.now() + mute.data[1] * 1000]
channel.muted.push(newMute)
this.channelService.save(channel) this.channelService.save(channel)
} }

143
back/volume/src/chat/chat.gateway.ts

@ -1,22 +1,25 @@
/*
import { UnauthorizedException, UseGuards } from '@nestjs/common'
import { import {
type OnGatewayConnection, type OnGatewayConnection,
type OnGatewayDisconnect, type OnGatewayDisconnect,
MessageBody,
SubscribeMessage, SubscribeMessage,
WebSocketGateway, WebSocketGateway,
WebSocketServer WebSocketServer,
WsException
} from '@nestjs/websockets' } from '@nestjs/websockets'
import { Socket, Server } from 'socket.io' import { Socket, Server } from 'socket.io'
// import { User } from 'users/user.entity';
import { UsersService } from 'src/users/users.service'
import { BadRequestException } from '@nestjs/common'
import { ChatService } from './chat.service' import { ChatService } from './chat.service'
import Message from './entity/message.entity'
import * as bcrypt from 'bcrypt'
import { MessageService } from './message.service'
import { type User } from 'src/users/entity/user.entity' import { type User } from 'src/users/entity/user.entity'
import { UsersService } from 'src/users/users.service' import { CreateMessageDto } from './dto/create-message.dto'
import { Channel } from './entity/channel.entity' import { InjectRepository } from '@nestjs/typeorm'
import { Message } from './entity/message.entity' import { Repository } from 'typeorm'
import ConnectedUser from './entity/connection.entity'
import { CreateChannelDto } from './dto/createChannel.dto' import { ConnectionDto } from './dto/connection.dto'
@WebSocketGateway({ @WebSocketGateway({
cors: { origin: /^(http|ws):\/\/localhost(:\d+)?$/ } cors: { origin: /^(http|ws):\/\/localhost(:\d+)?$/ }
@ -27,77 +30,89 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
constructor ( constructor (
private readonly userService: UsersService, private readonly userService: UsersService,
private readonly chatService: ChatService private readonly messageService: MessageService,
) {} private readonly chatService: ChatService,
@InjectRepository(ConnectedUser)
async handleConnection (socket: Socket) { private readonly connectedUserRepository: Repository<ConnectedUser>
try { ) {
const user: User | null = await this.userService.findUser(
socket.data.user.ftId
)
if (user == null) {
socket.emit('Error', new UnauthorizedException())
// socket.disconnect();
return
} else {
socket.data.user = user
const channels = await this.chatService.getChannelsForUser(user.id)
// Only emit rooms to the specific connected client
return this.server.to(socket.id).emit('channel', channels)
}
} catch {
socket.emit('Error', new UnauthorizedException())
// socket.disconnect();
}
} }
handleDisconnect (socket: Socket) { async handleConnection (socket: Socket): Promise<void> {
// socket.disconnect() // console.log(socket.handshake.headers)
// const cookie = socket.handshake.headers.cookie as string
// const { authentication: authenticationToken } = parse(cookie)
// console.log(authenticationToken)
// const user = await this.userService.find(authenticationToken)
// if (!user) {
// this.handleDisconnect(socket)
// throw new WsException('Invalid credentials.')
// }
// return user
} }
@SubscribeMessage('createChannel') handleDisconnect (socket: Socket): void {
async onCreateChannel ( socket.disconnect()
socket: Socket,
@MessageBody() channeldto: CreateChannelDto
): Promise<Channel | null> {
const channel = new Channel()
channel.name = channeldto.name
// const owner = await this.userService.findUser(channeldto.owner)
// if (owner == null) return null
// channel.owners.push(owner)
channel.password = channeldto.password
/// ...///
return await this.chatService.createChannel(channel, socket.data.user)
} }
@SubscribeMessage('joinChannel') @SubscribeMessage('joinChannel')
async onJoinChannel (socket: Socket, channel: Channel) { async onJoinChannel (socket: Socket, connect: ConnectionDto): Promise<void> {
// add user to channel const channel = await this.chatService.getChannel(connect.ChannelId)
const messages = await this.chatService.findMessagesInChannelForUser( if (channel.banned.find((e) => e.id == connect.UserId) != null) {
throw new WsException('You are banned from entering this channel')
}
const user = (await this.userService.findUser(connect.UserId)) as User
if (
channel.users.find((e) => e.id === user.id) == null &&
channel.password !== ''
) {
if (!(await bcrypt.compare(channel.password, connect.pwd))) {
throw new BadRequestException()
}
} else await this.chatService.addUserToChannel(channel, user)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
{
const conUser = new ConnectedUser()
conUser.user = user
conUser.channel = channel
conUser.socket = socket.id
await this.connectedUserRepository.save(conUser)
}
const messages = await this.messageService.findMessagesInChannelForUser(
channel, channel,
socket.data.user user
) )
this.server.to(socket.id).emit('messages', messages) this.server.to(socket.id).emit('messages', messages)
await socket.join(channel.name)
} }
@SubscribeMessage('leaveChannel') @SubscribeMessage('leaveChannel')
async onLeaveChannel (socket: Socket) { async onLeaveChannel (socket: Socket): Promise<void> {
await this.chatService.deleteBySocketId(socket.id) const id = socket.id as any
await this.connectedUserRepository.delete({ socket: id })
socket.disconnect()
} }
@SubscribeMessage('addMessage') @SubscribeMessage('addMessage')
async onAddMessage (socket: Socket, message: Message) { async onAddMessage (socket: Socket, message: CreateMessageDto): Promise<void> {
const createdMessage: Message = await this.chatService.createMessage({ const channel = await this.chatService.getChannel(message.ChannelId)
...message, if (await this.chatService.getMuteDuration(channel.id, message.UserId) > 0) {
author: socket.data.user throw new WsException('You are muted')
}) }
const channel = await this.chatService.getChannel( const createdMessage: Message = await this.messageService.createMessage(
createdMessage.channel.id message
) )
if (channel != null) { socket.in(channel.name).emit('newMessage', createdMessage)
const users = await this.userService.findOnlineInChannel(channel) }
@SubscribeMessage('kickUser')
async onKickUser (socket: Socket, chan: number, from: number, to: number): Promise<void> {
const channel = await this.chatService.getChannel(chan)
if (
channel.owner.id !== from ||
channel.admins.find((e) => e.id === from) == null
) {
throw new WsException('You do not have the required privileges')
} }
/// TODO: Send message to users await this.onLeaveChannel(socket)
} }
} }
*/

17
back/volume/src/chat/chat.module.ts

@ -1,23 +1,26 @@
import { Module } from '@nestjs/common' import { forwardRef, Module } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm' import { TypeOrmModule } from '@nestjs/typeorm'
import { AuthModule } from 'src/auth/auth.module' import { AuthModule } from 'src/auth/auth.module'
import { UsersModule } from 'src/users/users.module' import { UsersModule } from 'src/users/users.module'
// import { ChatGateway } from './chat.gateway' import { ChatGateway } from './chat.gateway'
import { ChatController } from './chat.controller' import { ChatController } from './chat.controller'
import { ChannelService } from './chat.service' import { ChatService } from './chat.service'
import { MessageService } from './message.service'
import Channel from './entity/channel.entity' import Channel from './entity/channel.entity'
import Message from './entity/message.entity' import Message from './entity/message.entity'
import ConnectedUser from './entity/connection.entity'
@Module({ @Module({
imports: [ imports: [
AuthModule,
UsersModule, UsersModule,
TypeOrmModule.forFeature([Channel]), AuthModule,
TypeOrmModule.forFeature([Message]) TypeOrmModule.forFeature([Channel, Message, ConnectedUser]),
], ],
controllers: [ChatController], controllers: [ChatController],
providers: [ChannelService] providers: [ChatService, ChatGateway, MessageService],
exports: [ChatService]
}) })
export class ChatModule {} export class ChatModule {}

16
back/volume/src/chat/chat.service.ts

@ -1,4 +1,4 @@
import { Injectable, NotFoundException } from '@nestjs/common' import { Inject, Injectable, NotFoundException } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm' import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm' import { Repository } from 'typeorm'
@ -7,9 +7,10 @@ import { UsersService } from 'src/users/users.service'
import type User from 'src/users/entity/user.entity' import type User from 'src/users/entity/user.entity'
import Channel from './entity/channel.entity' import Channel from './entity/channel.entity'
import { Cron } from '@nestjs/schedule'
@Injectable() @Injectable()
export class ChannelService { export class ChatService {
constructor ( constructor (
@InjectRepository(Channel) @InjectRepository(Channel)
private readonly ChannelRepository: Repository<Channel>, private readonly ChannelRepository: Repository<Channel>,
@ -30,6 +31,8 @@ export class ChannelService {
return await this.ChannelRepository.save(newChannel) return await this.ChannelRepository.save(newChannel)
} }
async updatePassword (id: number, password: string) { async updatePassword (id: number, password: string) {
let channel: Channel | null = await this.ChannelRepository.findOneBy({id}) let channel: Channel | null = await this.ChannelRepository.findOneBy({id})
if (channel === null) { throw new NotFoundException(`Channel #${id} not found`) } if (channel === null) { throw new NotFoundException(`Channel #${id} not found`) }
@ -56,6 +59,15 @@ export class ChannelService {
return rooms return rooms
} }
@Cron('*/6 * * * * *')
async updateMutelists(): Promise<void> {
let channels = await this.ChannelRepository.find({})
channels.forEach((channel) => {
channel.muted = channel.muted.filter((data) => { return (data[0] - Date.now()) > 0;});
this.ChannelRepository.save(channel);
})
}
async addUserToChannel (channel: Channel, user: User): Promise<Channel> { async addUserToChannel (channel: Channel, user: User): Promise<Channel> {
channel.owner = user channel.owner = user
return await this.ChannelRepository.save(channel) return await this.ChannelRepository.save(channel)

5
back/volume/src/chat/entity/channel.entity.ts

@ -56,7 +56,6 @@ export default class Channel {
@JoinTable() @JoinTable()
banned: User[] banned: User[]
@ManyToMany(() => User) // refuse post @Column('text', {array: true})
@JoinTable() muted: number[][]
muted: Array<Array<number>>
} }

9
back/volume/src/chat/entity/connection.entity.ts

@ -1,16 +1,17 @@
import { Column, Entity, OneToOne } from 'typeorm' import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm'
import Channel from './channel.entity' import Channel from './channel.entity'
import User from 'src/users/entity/user.entity' import User from 'src/users/entity/user.entity'
@Entity() @Entity()
export class connectedUser { export default class ConnectedUser {
@OneToOne(() => User) @OneToOne(() => User)
user: User user: User
@OneToOne(() => Channel, (channel) => channel.id) @OneToOne(() => Channel)
@JoinColumn()
channel: Channel channel: Channel
@Column() @PrimaryGeneratedColumn()
socket: string socket: string
} }

43
back/volume/src/chat/message.service.ts

@ -0,0 +1,43 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ChatService } from './chat.service';
import { UsersService } from 'src/users/users.service';
import { type CreateMessageDto } from './dto/create-message.dto';
import User from 'src/users/entity/user.entity';
import Channel from './entity/channel.entity';
import Message from './entity/message.entity';
@Injectable()
export class MessageService {
constructor(
@InjectRepository(Message)
private readonly MessageRepository: Repository<Message>,
private readonly channelService: ChatService,
private readonly usersService: UsersService,
) {}
async createMessage(message: CreateMessageDto): Promise<Message> {
const msg = new Message();
msg.text = message.text;
msg.channel = await this.channelService.getChannel(message.ChannelId);
msg.author = (await this.usersService.findUser(message.UserId)) as User;
msg.channel.messages.push(msg);
return await this.MessageRepository.save(
this.MessageRepository.create(msg),
);
}
async findMessagesInChannelForUser(
channel: Channel,
user: User,
): Promise<Message[]> {
return await this.MessageRepository.createQueryBuilder('message')
.where('message.channel = :chan', { chan: channel })
.andWhere('message.author NOT IN (:...blocked)', {
blocked: user.blocked,
})
.getMany();
}
}

5
back/volume/src/users/users.controller.ts

@ -65,11 +65,6 @@ export class UsersController {
return await this.usersService.getLeaderboard() return await this.usersService.getLeaderboard()
} }
@Get(':id/rank')
@UseGuards(AuthenticatedGuard)
async getRank (@Param('id', ParseIntPipe) id: number): Promise<number> {
return await this.usersService.getRank(id)
}
@Post('avatar') @Post('avatar')
@UseGuards(AuthenticatedGuard) @UseGuards(AuthenticatedGuard)
@Redirect('http://localhost') @Redirect('http://localhost')

5
back/volume/src/users/users.module.ts

@ -4,9 +4,12 @@ import { User } from './entity/user.entity'
import { UsersController } from './users.controller' import { UsersController } from './users.controller'
import { UsersService } from './users.service' import { UsersService } from './users.service'
import { PongModule } from 'src/pong/pong.module' import { PongModule } from 'src/pong/pong.module'
import { ChatModule } from 'src/chat/chat.module'
@Module({ @Module({
imports: [forwardRef(() => PongModule), TypeOrmModule.forFeature([User])], imports: [
forwardRef(() => PongModule),
TypeOrmModule.forFeature([User])],
controllers: [UsersController], controllers: [UsersController],
providers: [UsersService], providers: [UsersService],
exports: [UsersService] exports: [UsersService]

25
back/volume/src/users/users.service.ts

@ -12,7 +12,7 @@ import User from './entity/user.entity'
@Catch(QueryFailedError, EntityNotFoundError) @Catch(QueryFailedError, EntityNotFoundError)
export class UsersService { export class UsersService {
constructor ( constructor (
@InjectRepository(User) private readonly usersRepository: Repository<User> @InjectRepository(User) private readonly usersRepository: Repository<User>,
) {} ) {}
async save (user: User): Promise<void> { async save (user: User): Promise<void> {
@ -31,7 +31,6 @@ export class UsersService {
relations: { results: true } relations: { results: true }
}) })
if (user == null) throw new BadRequestException('User not found.') if (user == null) throw new BadRequestException('User not found.')
user.rank = (await this.getRank(user.ftId)) + 1;
return user return user
} }
@ -45,12 +44,12 @@ export class UsersService {
this.usersRepository.save(usr).catch((err) => console.log(err)) this.usersRepository.save(usr).catch((err) => console.log(err))
} }
}) })
this.getLeaderboard();
} }
async findUser (ftId: number): Promise<User | null> { async findUser (ftId: number): Promise<User | null> {
const user = await this.usersRepository.findOneBy({ ftId }) const user = await this.usersRepository.findOneBy({ ftId })
if (user == null) return null if (user == null) return null
user.rank = (await this.getRank(user.ftId)) + 1;
user.lastAccess = Date.now() user.lastAccess = Date.now()
if (user.status === 'offline') user.status = 'online' if (user.status === 'offline') user.status = 'online'
await this.usersRepository.save(user) await this.usersRepository.save(user)
@ -117,21 +116,17 @@ export class UsersService {
async getLeaderboard (): Promise<User[]> { async getLeaderboard (): Promise<User[]> {
const leaderboard = await this.usersRepository.find({ const leaderboard = await this.usersRepository.find({
order: { order: {
winrate: 'DESC' winrate: 'ASC'
} }
}) })
let ret = leaderboard.filter((user) => user.rank !== 0) let ret = leaderboard.filter((user) => user.matchs !== 0)
ret.forEach((follower) => follower.socketKey = '') let r = 0
return ret ret.forEach((usr) => {
} usr.rank = r++
this.usersRepository.save(usr)
async getRank (ftId: number): Promise<number> { usr.socketKey = ''
const leaderboard = await this.usersRepository.find({
order: {
winrate: 'DESC'
}
}) })
return leaderboard.findIndex((user) => user.ftId === ftId) return ret
} }
async invit (ftId: number, targetFtId: number): Promise<string> { async invit (ftId: number, targetFtId: number): Promise<string> {

Loading…
Cancel
Save