This commit is contained in:
Me
2023-01-13 04:30:26 +01:00
40 changed files with 785 additions and 482 deletions

2
.gitignore vendored
View File

@@ -18,7 +18,7 @@ Thumbs.db
node_modules node_modules
./srcs/requirement/nestjs/api_back/dist ./srcs/requirement/nestjs/api_back/dist
./srcs/requirements/svelte/api_front/public/build/* ./srcs/requirements/svelte/api_front/public/build/
# Logs # Logs
logs logs

View File

@@ -24,5 +24,15 @@ destroy:
- docker images -aq | xargs --no-run-if-empty docker rmi -f - docker images -aq | xargs --no-run-if-empty docker rmi -f
- docker volume ls -q | xargs --no-run-if-empty docker volume rm - docker volume ls -q | xargs --no-run-if-empty docker volume rm
# temp for hugo, only reinit database
db:
- docker rm -f postgresql
- docker rm -f nestjs
- docker volume rm -f srcs_data_nest_postgresql
docker compose -f ${DOCKERCOMPOSEPATH} up -d --build
@make start
@docker ps
stop: stop:
docker compose -f ${DOCKERCOMPOSEPATH} stop docker compose -f ${DOCKERCOMPOSEPATH} stop

View File

@@ -1,4 +1,3 @@
CONFLICT srcs/requirements/nestjs/api_back/src/friendship/friendship.service.ts
### Pour lancer le docker : ### Pour lancer le docker :
@@ -69,15 +68,25 @@ CONFLICT srcs/requirements/nestjs/api_back/src/friendship/friendship.service.ts
#### chat : #### chat :
- [ ] can create chat-rooms (public/private, password protected) - [/] create public room
- [ ] send direct messages - [ ] create private room
- [ ] block other users - [/] create direct room
- [ ] creators of chat-room are owners, untill they leave - [/] chat in room
- [ ] chat-room owner can set, change, remove password - [/] join public rooms
- [ ] chat-room owner is administrator and can set other administrators - [ ] join private rooms
- [ ] administrators can ban or mute for a time other users - [ ] join direct rooms
- [ ] send game invitation in chat - [/] see all joignable rooms
- [ ] view user profiles from chat - [/] see all my rooms
- [/] leave room
- [ ] leave direct
- [ ] invite someone in room
- [ ] make admin
- [ ] ban
- [ ] mute
- [ ] protect room with password
- [ ] bock users
- [ ] send game invitation
- [ ] view user profiles
#### game : #### game :

View File

@@ -35,6 +35,7 @@ RESET="\033[0m"
function make_env_for_docker_and_svelte function make_env_for_docker_and_svelte
{ {
docker rm -f postgresql docker rm -f postgresql
docker rm -f nestjs
docker volume rm -f srcs_data_nest_postgresql docker volume rm -f srcs_data_nest_postgresql
echo -e "${BOLD_BLUE}Creating a new environment for docker${RESET}" echo -e "${BOLD_BLUE}Creating a new environment for docker${RESET}"
NODE_ENV="" NODE_ENV=""

View File

@@ -94,10 +94,15 @@ async function clientAnnounceListener(this: WebSocket, data: string)
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(body) body: JSON.stringify(body)
}); })
if (!response.ok) .catch(error => console.log("ERROR : " + error));
if (!response || !response.ok)
{ {
this.send(JSON.stringify( new ev.EventError((await response.json()).message) )); let errMessage = "validate token error";
if (response) {
errMessage = (await response.json()).message;
}
this.send(JSON.stringify( new ev.EventError(errMessage) ));
clientTerminate(clientsMap.get(this.id)); clientTerminate(clientsMap.get(this.id));
return; return;
} }
@@ -154,6 +159,8 @@ function publicMatchmaking(player: ClientPlayer)
const compatiblePlayers: ClientPlayer[] = []; const compatiblePlayers: ClientPlayer[] = [];
compatiblePlayers.push(player); compatiblePlayers.push(player);
/* // Replace with this code to enable the possibility to play against self
for (const [id, client] of matchmakingMap) for (const [id, client] of matchmakingMap)
{ {
if (client.matchOptions === matchOptions) if (client.matchOptions === matchOptions)
@@ -163,10 +170,9 @@ function publicMatchmaking(player: ClientPlayer)
break; break;
} }
} }
} } */
// TODO: Replace with this code to disable the possibility to play against self for (const [id, client] of matchmakingMap)
/* for (const [id, client] of matchmakingMap)
{ {
if (client.matchOptions === matchOptions && client.username !== player.username) if (client.matchOptions === matchOptions && client.username !== player.username)
{ {
@@ -175,7 +181,7 @@ function publicMatchmaking(player: ClientPlayer)
break; break;
} }
} }
} */ }
if (compatiblePlayers.length >= minPlayersNumber) { if (compatiblePlayers.length >= minPlayersNumber) {
compatiblePlayers.forEach((client) => { compatiblePlayers.forEach((client) => {
@@ -231,7 +237,6 @@ function privateMatchmaking(player: ClientPlayer)
token : player.token token : player.token
}) })
}) })
.then(x => x.json())
.catch(error => console.log("ERROR : " + error)); .catch(error => console.log("ERROR : " + error));
clientTerminate(player); clientTerminate(player);
} }
@@ -252,18 +257,26 @@ function createGameSession(playersArr: ClientPlayer[], matchOptions: en.MatchOpt
gameSession.unreadyPlayersMap.set(client.id, client); gameSession.unreadyPlayersMap.set(client.id, client);
client.socket.once("message", playerReadyConfirmationListener); client.socket.once("message", playerReadyConfirmationListener);
}); });
let gameSessionPlayersIterator = gameSession.playersMap.values();
const eventMatchmakingComplete = new ev.EventMatchmakingComplete(
(<ClientPlayer>gameSessionPlayersIterator.next().value).username,
(<ClientPlayer>gameSessionPlayersIterator.next().value).username
);
// REFACTORING: Not pretty, hardcoded two players. // REFACTORING: Not pretty, hardcoded two players.
// Could be done in gameSession maybe ? // Could be done in gameSession maybe ?
const gameSessionPlayersIterator = gameSession.playersMap.values(); gameSessionPlayersIterator = gameSession.playersMap.values();
let player: ClientPlayer; let player: ClientPlayer;
player = (<ClientPlayer>gameSessionPlayersIterator.next().value); player = (<ClientPlayer>gameSessionPlayersIterator.next().value);
player.racket = gameSession.components.playerLeft; player.racket = gameSession.components.playerLeft;
player.socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) )); eventMatchmakingComplete.side = en.PlayerSide.left;
player.socket.send(JSON.stringify( eventMatchmakingComplete ));
player = (<ClientPlayer>gameSessionPlayersIterator.next().value); player = (<ClientPlayer>gameSessionPlayersIterator.next().value);
player.racket = gameSession.components.playerRight; player.racket = gameSession.components.playerRight;
player.socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) )); eventMatchmakingComplete.side = en.PlayerSide.right;
player.socket.send(JSON.stringify( eventMatchmakingComplete ));
// REFACTORING // REFACTORING
setTimeout(function abortMatch() { setTimeout(function abortMatch() {

View File

@@ -18,10 +18,13 @@ export class EventAssignId extends ServerEvent {
} }
export class EventMatchmakingComplete extends ServerEvent { export class EventMatchmakingComplete extends ServerEvent {
side: en.PlayerSide; side: en.PlayerSide = en.PlayerSide.noSide;
constructor(side: en.PlayerSide) { playerOneUsername: string;
playerTwoUsername: string;
constructor(playerOneUsername: string, playerTwoUsername: string) {
super(en.EventTypes.matchmakingComplete); super(en.EventTypes.matchmakingComplete);
this.side = side; this.playerOneUsername = playerOneUsername;
this.playerTwoUsername = playerTwoUsername;
} }
} }

View File

@@ -1,84 +1,147 @@
import { Controller, UseGuards, HttpException, HttpStatus, Get, Post, Body, Req, Res } from '@nestjs/common'; import { Controller, UseGuards, HttpException, HttpStatus, Get, Post, Delete, Body, Req, Res } from '@nestjs/common';
import { AuthenticateGuard, TwoFactorGuard } from 'src/auth/42/guards/42guards'; import { AuthenticateGuard, TwoFactorGuard } from 'src/auth/42/guards/42guards';
import { ConnectedSocket } from '@nestjs/websockets'; import { ConnectedSocket } from '@nestjs/websockets';
import { ChatService } from './chat.service'; import { ChatService } from './chat.service';
import { User } from 'src/users/entities/user.entity'; import { User } from 'src/users/entities/user.entity';
import { PartialUsersDto } from 'src/users/dto/partial-users.dto'; import { PartialUsersDto } from 'src/users/dto/partial-users.dto';
import { createRoomDto } from './dto/createRoom.dto'; import { roomDto } from './dto/room.dto';
import { joinRoomDto } from './dto/joinRoom.dto';
import { setCurrentRoomDto } from './dto/setCurrentRoom.dto'; import { setCurrentRoomDto } from './dto/setCurrentRoom.dto';
import { ChatGateway } from './chat.gateway';
import { socketDto } from './dto/socket.dto';
import { Chatroom } from './entities/chatroom.entity';
@Controller('chat') @Controller('chat')
export class ChatController { export class ChatController {
constructor( constructor(
private chatService: ChatService, private chatService: ChatService,
private chatGateway: ChatGateway,
) {} ) {}
// don't allow '+' because it's used in direct rooms name
private allowed_chars = '-#!?_';
private escape_chars(str)
{
return str.split("").join("\\");
}
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard) @UseGuards(TwoFactorGuard)
@Get('myrooms') @Get('myrooms')
async getMyRooms(@Req() req, @Res() res): Promise<object> async getMyRooms(@Req() req, @Res() res): Promise<void>
{ {
console.log("- in getMyRooms controller"); console.log("- in getMyRooms controller");
const rooms = await this.chatService.getMyRooms(req.user);
let fields = ["name", "type", "users"];
const rooms = await this.chatService.getMyRooms(req.user.username, fields);
res.status(HttpStatus.OK).json({ rooms: rooms });
console.log("- out getMyRooms controller"); console.log("- out getMyRooms controller");
return res.status(HttpStatus.OK).json({ rooms: rooms });
} }
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard) @UseGuards(TwoFactorGuard)
@Get('allrooms') @Get('allrooms')
async getAllRooms(@Req() req, @Res() res): Promise<object> async getAllRooms(@Req() req, @Res() res): Promise<void>
{ {
console.log("- in getAllRooms controller"); console.log("- in getAllRooms controller");
const rooms = await this.chatService.getAllNotMyRooms(req.user);
const rooms: roomDto[] = await this.chatService.getAllOtherRoomsAndUsers(req.user.username)
console.log("--- rooms:", rooms);
res.status(HttpStatus.OK).json({ rooms: rooms });
console.log("- out getAllRooms controller"); console.log("- out getAllRooms controller");
return res.status(HttpStatus.OK).json({ rooms: rooms });
} }
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard) @UseGuards(TwoFactorGuard)
@Get('current') @Get('current')
async setCurrentRoom(@Body() setCurrentRoomDto: setCurrentRoomDto, @Req() req, @Res() res): Promise<object> async setCurrentRoom(@Body() setCurrentRoomDto: setCurrentRoomDto, @Req() req, @Res() res): Promise<void>
{ {
console.log("- in setCurrentRoom controller"); console.log("- in setCurrentRoom controller");
const response = await this.chatService.setCurrentRoom(req.user, setCurrentRoomDto.name);
const response = await this.chatService.setCurrentRoom(req.user.username, setCurrentRoomDto.name);
res.status(HttpStatus.OK).json({ message: response });
console.log("- out setCurrentRoom controller"); console.log("- out setCurrentRoom controller");
return res.status(HttpStatus.OK).json({ message: response }); }
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Get('allowedchars')
async allowedChars(@Res() res): Promise<void>
{
console.log("- in allowedChars controller");
res.status(HttpStatus.OK).json({ chars: this.allowed_chars });
console.log("- out allowedChars controller");
} }
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard) @UseGuards(TwoFactorGuard)
@Post('create') @Post('create')
async createRoom(@Body() createRoomDto: createRoomDto, @Req() req, @Res() res): Promise<object> async createRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise<void>
{ {
console.log("- in createRoom controller"); console.log("- in createRoom controller");
const response = await this.chatService.addUserToNewRoom(req.user, createRoomDto);
let chars = this.escape_chars(this.allowed_chars);
let regex_base = `[a-zA-Z0-9\\s${chars}]`;
let test_regex = new RegExp(`^${regex_base}+$`);
if (test_regex.test(room.name) === false)
{
let forbidden_chars = room.name.replace(new RegExp(regex_base, "g"), "");
throw new HttpException(`Your room name can not contains these characters : ${forbidden_chars}`, HttpStatus.UNPROCESSABLE_ENTITY);
}
await this.chatService.addUserToNewRoom(req.user.username, room);
res.status(HttpStatus.OK).json({ room: room });
console.log("- out createRoom controller"); console.log("- out createRoom controller");
return res.status(HttpStatus.OK).json({ room_name: createRoomDto.room_name, message: response });
} }
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard) @UseGuards(TwoFactorGuard)
@Post('join') @Post('join')
async joinRoom(@Body() joinRoomDto: joinRoomDto, @Req() req, @Res() res): Promise<object> async joinRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise<void>
{ {
console.log("- in joinRoom controller"); console.log("- in joinRoom controller");
const response = await this.chatService.addUserToRoom(req.user, joinRoomDto);
let response = "";
if (room.type === 'direct')
throw new HttpException(`cannot join a direct messages room`, HttpStatus.CONFLICT);
else if (room.type === 'user')
{
room.type = 'direct';
room.users = [room.name, req.user.username];
room.name += ` + ${req.user.username}`;
await this.chatService.addUserToNewRoom(req.user.username, room);
}
else
await this.chatService.addUserToRoom(req.user.username, room.name);
let socket: socketDto = this.chatGateway.sockets.get(req.user.username);
await this.chatService.socketJoinRoom(socket, room.name);
res.status(HttpStatus.OK).json({ room: room });
console.log("- out joinRoom controller"); console.log("- out joinRoom controller");
return res.status(HttpStatus.OK).json({ room_name: joinRoomDto.room_name, message: response });
} }
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard) @UseGuards(TwoFactorGuard)
@Post('change') @Post('change')
async changeRoom(@Body() joinRoomDto: joinRoomDto, @Req() req, @Res() res): Promise<object> async changeRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise<void>
{ {
console.log("- in changeRoom controller"); console.log("- in changeRoom controller");
const response = await this.chatService.setCurrentRoom(req.user, joinRoomDto.room_name);
const response = await this.chatService.setCurrentRoom(req.user.username, room.name);
let socket: socketDto = this.chatGateway.sockets.get(req.user.username);
await this.chatService.socketChangeRoom(socket, room.name);
res.status(HttpStatus.OK).json({ room: room });
console.log("- out changeRoom controller"); console.log("- out changeRoom controller");
return res.status(HttpStatus.OK).json({ room_name: joinRoomDto.room_name, message: response });
} }
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@@ -93,12 +156,43 @@ export class ChatController {
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard) @UseGuards(TwoFactorGuard)
@Get('messages') @Get('messages')
async getMessages(@Req() req, @Res() res): Promise<object> async getMessages(@Req() req, @Res() res): Promise<void>
{ {
console.log("- in getMessages controller"); console.log("- in getMessages controller");
const messages = await this.chatService.getMessagesFromCurrentRoom(req.user);
const messages = await this.chatService.getMessagesFromCurrentRoom(req.user.username);
res.status(HttpStatus.OK).json({ messages: messages });
console.log("- out getMessages controller"); console.log("- out getMessages controller");
return res.status(HttpStatus.OK).json({ messages: messages }); }
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Get('roomusers')
async getRoomUsers(@Req() req, @Res() res): Promise<void>
{
console.log("- in getRoomUsers controller");
const room_name = await this.chatService.getCurrentRoomName(req.user.username);
const room = await this.chatService.getRoomByName(room_name);
const users = room.users;
res.status(HttpStatus.OK).json({ users: users });
console.log("- out getRoomUsers controller");
}
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Delete('removeuser')
async removeUser(@Req() req, @Res() res): Promise<void>
{
console.log("- in removeUser controller");
const room_name = await this.chatService.getCurrentRoomName(req.user.username);
let response = await this.chatService.removeUserFromRoom(req.user.username, room_name);
res.status(HttpStatus.OK).json({ message: response });
console.log("- out removeUser controller");
} }
} }

View File

@@ -1,14 +1,14 @@
import { WebSocketGateway, SubscribeMessage, WebSocketServer, MessageBody, ConnectedSocket, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets'; import { WebSocketGateway, SubscribeMessage, WebSocketServer, MessageBody, ConnectedSocket, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';
import { UsersService } from 'src/users/users.service'; import { UsersService } from 'src/users/users.service';
import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto';
import { ChatService } from './chat.service'; import { ChatService } from './chat.service';
import { socketDto } from './dto/socket.dto';
@WebSocketGateway(5000, { @WebSocketGateway(5000, {
path: '/chat', path: '/chat',
}) })
export class ChatGateway export class ChatGateway
implements OnGatewayConnection, OnGatewayDisconnect implements OnGatewayConnection, OnGatewayDisconnect
{ {
constructor constructor
( (
@@ -16,40 +16,44 @@ export class ChatGateway
private chatService: ChatService, private chatService: ChatService,
) {} ) {}
sockets = new Map<string, socketDto>();
@WebSocketServer() @WebSocketServer()
server; server;
async handleConnection(socket: socketDto) {
// how to guard the handleConnection ? console.log('- socket connected :', socket.id, socket.handshake.query.username);
// https://github.com/nestjs/nest/issues/882 socket.username = socket.handshake.query.username.toString();
async handleConnection(client) { this.sockets.set(socket.username, socket);
console.log('- Client connected :', client.id, client.handshake.query.username);
client.username = client.handshake.query.username;
} }
async handleDisconnect(client) { async handleDisconnect(socket: socketDto) {
console.log('- Client disconnected :', client.id, client.username); this.sockets.delete(socket.username);
} }
@SubscribeMessage('join') @SubscribeMessage('join')
async joinRoom(@ConnectedSocket() socket, @MessageBody() room_name: string): Promise<void> async joinRoom(@ConnectedSocket() socket: socketDto, @MessageBody() room_name: string): Promise<void>
{ {
console.log('- in joinRoom gateway'); console.log('- in joinRoom gateway');
socket.leave(socket.room); socket.leave(socket.room);
socket.join(room_name); socket.join(room_name);
socket.room = room_name; socket.room = room_name;
let message = `${socket.username} has join the room`;
await socket.to(socket.room).emit('message', "SERVER", message);
await this.chatService.addMessageToRoom(room_name, "SERVER", message);
}
console.log('- out joinRoom gateway'); @SubscribeMessage('change')
async changeRoom(@ConnectedSocket() socket: socketDto, @MessageBody() room_name: string): Promise<void>
{
console.log('- in changeRoom gateway');
await this.chatService.socketChangeRoom(socket, room_name);
} }
@SubscribeMessage('message') @SubscribeMessage('message')
async handleMessage(@ConnectedSocket() socket, @MessageBody() message: string): Promise<void> async handleMessage(@ConnectedSocket() socket: socketDto, @MessageBody() message: string): Promise<void>
{ {
console.log('- in handleMessage gateway'); console.log('- in handleMessage gateway');
//let room_name = await this.chatService.getCurrentRoom(socket.username); await this.chatService.socketIncommingMessage(socket, message);
socket.to(socket.room).emit('message', socket.username, message);
this.chatService.addMessageToCurrentRoom(socket.username, message);
console.log('- out handleMessage gateway');
} }
} }

View File

@@ -3,7 +3,6 @@ import { ChatController } from './chat.controller';
import { ChatService } from './chat.service'; import { ChatService } from './chat.service';
import { ChatGateway } from './chat.gateway'; import { ChatGateway } from './chat.gateway';
import { UsersModule } from 'src/users/users.module'; import { UsersModule } from 'src/users/users.module';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { Chatroom } from './entities/chatroom.entity'; import { Chatroom } from './entities/chatroom.entity';
import { User } from 'src/users/entities/user.entity'; import { User } from 'src/users/entities/user.entity';

View File

@@ -4,9 +4,10 @@ import { UsersService } from 'src/users/users.service';
import { Chatroom } from './entities/chatroom.entity'; import { Chatroom } from './entities/chatroom.entity';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { createRoomDto } from './dto/createRoom.dto'; import { roomDto } from './dto/room.dto';
import { joinRoomDto } from './dto/joinRoom.dto';
import { messagesDto } from './dto/messages.dto'; import { messagesDto } from './dto/messages.dto';
import { socketDto } from './dto/socket.dto';
@Injectable() @Injectable()
export class ChatService { export class ChatService {
@@ -17,8 +18,7 @@ export class ChatService {
private readonly userRepository: Repository<User>, private readonly userRepository: Repository<User>,
@InjectRepository(Chatroom) @InjectRepository(Chatroom)
private readonly chatroomRepository: Repository<Chatroom>, private readonly chatroomRepository: Repository<Chatroom>,
) { } ) {}
// temp for test // temp for test
sleep(ms) { sleep(ms) {
@@ -29,86 +29,140 @@ export class ChatService {
/* GETTERS ************************************************ /* GETTERS ************************************************
*/ */
async getMyRooms(user: User) async getMyRooms(username: string, fieldsToReturn: string[] = null): Promise<Chatroom[]>
{ {
console.log("-- in getMyRooms service"); console.log("-- in getMyRooms service");
const rooms = await this.chatroomRepository
const queryBuilder = this.chatroomRepository
.createQueryBuilder('chatroom') .createQueryBuilder('chatroom')
.where('chatroom.users LIKE :user_name', { user_name: `%${user.username}%` }) .where('chatroom.users LIKE :user_name', { user_name: `%${username}%` });
.getMany();
if (fieldsToReturn)
{
let fields = fieldsToReturn.map(field => `chatroom.${field}`);
queryBuilder.select(fields);
}
const rooms = await queryBuilder.getMany();
console.log("--- rooms:", rooms);
console.log("-- out getMyRooms service"); console.log("-- out getMyRooms service");
return rooms; return rooms;
} }
async getAllRooms() async getMyDirects(username: string): Promise<Chatroom[]>
{
console.log("-- in getAllNotMyRooms service");
const my_rooms = await this.getMyRooms(username);
const directs = my_rooms.filter(room => room.type === 'direct');
console.log("-- out getAllNotMyRooms service");
return directs;
}
async getAllRooms(): Promise<Chatroom[]>
{ {
console.log("-- in getAllRooms service"); console.log("-- in getAllRooms service");
const rooms = await this.chatroomRepository const rooms = await this.chatroomRepository
.createQueryBuilder('chatroom') .createQueryBuilder('chatroom')
.getMany(); .getMany();
console.log("--- rooms:", rooms);
console.log("-- out getAllRooms service"); console.log("-- out getAllRooms service");
return rooms; return rooms;
} }
async getAllNotMyRooms(user: User) async getAllNotMyRooms(username: string): Promise<Chatroom[]>
{ {
console.log("-- in getAllNotMyRooms service"); console.log("-- in getAllNotMyRooms service");
const user_db = await this.getUserByName(user.username);
//const user_db = await this.usersService.findOne(user.username); const user_db = await this.getUserByName(username);
const rooms = await this.chatroomRepository const rooms = await this.chatroomRepository
.createQueryBuilder('chatroom') .createQueryBuilder('chatroom')
.where('chatroom.type != :type', { type: 'private' }) .where('chatroom.type != :type', { type: 'private' })
.andWhere('chatroom.users NOT LIKE :user_name', { user_name: `%${user.username}%` }) .andWhere('chatroom.users NOT LIKE :user_name', { user_name: `%${username}%` })
.getMany(); .getMany();
//const users = await this.getAllUsers(); console.log("--- rooms:", rooms);
//let allRooms = [...rooms, ...users];
console.log("-- out getAllNotMyRooms service"); console.log("-- out getAllNotMyRooms service");
return rooms; return rooms;
} }
async getMessagesFromCurrentRoom(user: User) async getAllOtherRoomsAndUsers(username: string): Promise<roomDto[]>
{ {
console.log("-- in getMessagesFromCurrentRoom service"); console.log("-- in getAllOtherRoomsAndUsers service");
const user_db = await this.getUserByName(user.username);
//const user_db = await this.usersService.findOne(user.username);
const currentRoom = await this.getRoomByName(user_db.currentRoom);
console.log("-- out getMessagesFromCurrentRoom service"); const all_rooms = await this.getAllNotMyRooms(username);
return currentRoom.messages; const all_users = await this.getAllUsersNotMyRooms(username);
let row_rooms = all_rooms.map(room => {
return {
name: room.name,
type: room.type,
};
});
let users = all_users.map(user => {
return {
name: user.username,
type: "user",
};
});
let rooms = row_rooms.concat(users);
console.log("--- rooms:", rooms);
console.log("-- in getAllOtherRoomsAndUsers service");
return rooms;
} }
async getCurrentRoom(username: string) async getMessagesFromCurrentRoom(username: string): Promise<messagesDto[]>
{ {
console.log("-- in getCurrentRoom service"); console.log("-- in getMessagesFromCurrentRoom service");
const user_db = await this.getUserByName(username);
//const user_db = await this.usersService.findOne(username);
console.log("-- out getCurrentRoom service"); const user_db = await this.getUserByName(username);
const currentRoom = await this.getRoomByName(user_db.currentRoom);
let messages = null;
if (currentRoom)
messages = currentRoom.messages;
console.log("-- out getMessagesFromCurrentRoom service");
return messages;
}
async getCurrentRoomName(username: string): Promise<string>
{
console.log("-- in getCurrentRoomName service");
const user_db = await this.getUserByName(username);
console.log("-- out getCurrentRoomName service");
return user_db.currentRoom; return user_db.currentRoom;
} }
async getRoomByName(name: string) async getRoomByName(room_name: string): Promise<Chatroom>
{ {
console.log("-- in getRoomByName service"); console.log("-- in getRoomByName service");
const room = await this.chatroomRepository const room = await this.chatroomRepository
.createQueryBuilder('chatroom') .createQueryBuilder('chatroom')
.where('chatroom.name = :name', { name: name }) .where('chatroom.name = :name', { name: room_name })
.getOne(); .getOne();
console.log("--- room:", room);
console.log("-- out getRoomByName service"); console.log("-- out getRoomByName service");
return room; return room;
} }
async getRoomById(id: number) async getRoomById(id: number): Promise<Chatroom>
{ {
console.log("-- in getRoomById service"); console.log("-- in getRoomById service");
const room = await this.chatroomRepository const room = await this.chatroomRepository
.createQueryBuilder('chatroom') .createQueryBuilder('chatroom')
.where('chatroom.id = :id', { id: id }) .where('chatroom.id = :id', { id: id })
.getOne(); .getOne();
console.log("--- room:", room);
console.log("-- out getRoomById service"); console.log("-- out getRoomById service");
return room; return room;
@@ -118,102 +172,180 @@ export class ChatService {
/* SETTERS ************************************************ /* SETTERS ************************************************
*/ */
async setCurrentRoom(user: User, name: string) async setCurrentRoom(username: string, room_name: string): Promise<string>
{ {
console.log("-- in setCurrentRoom service"); console.log("-- in setCurrentRoom service");
const user_db = await this.getUserByName(user.username);
//const user_db = await this.usersService.findOne(user.username); const user_db = await this.getUserByName(username);
user_db.currentRoom = name; user_db.currentRoom = room_name;
this.userRepository.save(user_db); this.userRepository.save(user_db);
console.log("-- out setCurrentRoom service"); console.log("-- out setCurrentRoom service");
return `room "${name}" is now current room`; return `room "${room_name}" is now current room`;
} }
/* ADDERS ************************************************* /* ADDERS *************************************************
*/ */
async addUserToNewRoom(user: User, createRoomDto: createRoomDto) async addUserToNewRoom(username: string, room: roomDto): Promise<void>
{ {
console.log("-- in addUserToRoom service"); console.log("-- in addUserToNewRoom service");
const room = await this.getRoomByName(createRoomDto.room_name);
if (room) const find_room = await this.getRoomByName(room.name);
throw new HttpException(`This room already exist`, HttpStatus.CONFLICT); if (find_room)
throw new HttpException(`This room name already exist`, HttpStatus.CONFLICT);
// create chatroom // create chatroom
const newChatroom = new Chatroom(); const newChatroom = new Chatroom();
newChatroom.name = createRoomDto.room_name; newChatroom.name = room.name;
newChatroom.type = createRoomDto.room_type; newChatroom.type = room.type;
newChatroom.owner = user.username; newChatroom.owner = username;
newChatroom.users = [user.username]; newChatroom.users = [username];
newChatroom.messages = [{ name: "SERVER", message: `creation of room ${createRoomDto.room_name}` }]; if (room.type === 'direct')
this.chatroomRepository.save(newChatroom); newChatroom.users = room.users;
newChatroom.messages = [{ name: "SERVER", message: `creation of room ${room.name}` }];
await this.chatroomRepository.save(newChatroom);
console.log("-- out addUserToRoom service"); console.log("-- out addUserToNewRoom service");
return "successfull room creation";
} }
async addUserToRoom(user: User, joinRoomDto: joinRoomDto) async addUserToRoom(username: string, room_name: string): Promise<void>
{ {
console.log("-- in addUserToRoom service"); console.log("-- in addUserToRoom service");
const room = await this.getRoomByName(joinRoomDto.room_name);
if (room.users.includes(user.username)) const room = await this.getRoomByName(room_name);
throw new HttpException(`your have already join this room`, HttpStatus.CONFLICT); if (room.users.includes(username))
throw new HttpException(`your have already joined this room`, HttpStatus.CONFLICT);
// update room with new user // update room with new user
room.users.push(user.username); room.users.push(username);
this.chatroomRepository.save(room); this.chatroomRepository.save(room);
const rooms = await this.getMyRooms(user);
const allRooms = await this.getAllRooms();
console.log("-- out addUserToRoom service"); console.log("-- out addUserToRoom service");
return "successfull joining room";
} }
async addMessageToCurrentRoom(username: string, message: string) async addMessageToRoom(room_name: string, username: string, message: string): Promise<void>
{ {
console.log("-- in addMessageToCurrentRoom service"); console.log("-- in addMessageToRoom service");
const user_db = await this.getUserByName(username);
//const user_db = await this.usersService.findOne(username); const my_room = await this.getRoomByName(room_name);
const currentRoom = await this.getRoomByName(user_db.currentRoom);
let chat_message = { let chat_message = {
name: username, name: username,
message: message, message: message,
}; };
currentRoom.messages.push(chat_message); my_room.messages.push(chat_message);
this.chatroomRepository.save(currentRoom); this.chatroomRepository.save(my_room);
console.log("-- out addMessageToCurrentRoom service"); console.log("-- out addMessageToRoom service");
} }
/* REMOVERS *********************************************** /* REMOVERS ***********************************************
*/ */
async removeUserFromRoom(user: User, room_name: string) async removeUserFromRoom(username: string, room_name: string): Promise<string>
{ {
console.log("-- in removeUserFromRoom service"); console.log("-- in removeUserFromRoom service");
// get room
// remove user const room = await this.getRoomByName(room_name);
if (!room.users.includes(username))
throw new HttpException(`your are not in this room`, HttpStatus.CONFLICT);
// delete user from room
room.users.push(username);
room.users = room.users.filter(name => name !== username);
this.chatroomRepository.save(room);
// set current room to nothing
await this.setCurrentRoom(username, "");
console.log("-- out removeUserFromRoom service");
return "successfully leaving room";
} }
/* SEARCH IN USER ***************************************** /* SEARCH IN USER *****************************************
*/ */
async getUserByName(name: string) async getUserByName(username: string): Promise<User>
{ {
console.log("-- in getUserByName service"); console.log("-- in getUserByName service");
const user = await this.userRepository const user = await this.userRepository
.createQueryBuilder('user') .createQueryBuilder('user')
.where('user.username = :name', { name: name }) .where('user.username = :name', { name: username })
.getOne(); .getOne();
console.log("--- user:", user);
console.log("-- out getUserByName service"); console.log("-- out getUserByName service");
return user; return user;
} }
async getAllUsersNotMyRooms(username: string): Promise<User[]>
{
console.log("-- in getAllUsersNotMyRooms service");
const directs = await this.getMyDirects(username);
// get all users from directs
let usernames = directs.map(room => {
let user = room.users[0];
if (user === username)
user = room.users[1];
return user;
});
usernames.push(username);
const users = await this.userRepository
.createQueryBuilder('user')
.where('user.username NOT IN (:...usernames)', { usernames: usernames })
.getMany();
console.log("--- users:", users);
console.log("-- out getAllUsersNotMyRooms service");
return users;
}
/* GATEWAY EVENTS *****************************************
*/
async socketIncommingMessage(socket: socketDto, message: string): Promise<void>
{
console.log("-- in handleSocketIncommingMessage service");
socket.to(socket.room).emit('message', socket.username, message);
let room_name = await this.getCurrentRoomName(socket.username);
await this.addMessageToRoom(room_name, socket.username, message);
console.log("-- out handleSocketIncommingMessage service");
}
async socketChangeRoom(socket: socketDto, room_name: string): Promise<void>
{
console.log('-- in socketChangeRoom service');
socket.leave(socket.room);
socket.join(room_name);
socket.room = room_name;
console.log('-- out socketChangeRoom service');
}
async socketJoinRoom(socket: socketDto, room_name: string): Promise<void>
{
console.log('- in socketJoinRoom service');
socket.leave(socket.room);
socket.join(room_name);
socket.room = room_name;
let message = `${socket.username} has join the room`;
await socket.to(socket.room).emit('message', "SERVER", message);
await this.addMessageToRoom(room_name, "SERVER", message);
console.log('- out socketJoinRoom service');
}
} }

View File

@@ -1,18 +0,0 @@
import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator";
import { IsNull } from "typeorm";
export class createRoomDto
{
@IsString()
@IsNotEmpty()
room_name: string;
@IsString()
@IsNotEmpty()
room_type: 'public' | 'private' | 'direct';
@IsString()
@IsOptional()
room_password: string;
}

View File

@@ -1,9 +1,13 @@
import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional, IsArray } from "class-validator"; import { IsString, IsOptional } from "class-validator";
import { IsNull } from "typeorm";
export class messagesDto export class messagesDto
{ {
@IsArray() @IsString()
messages: { name: string; message: string }[]; @IsOptional()
name: string;
@IsString()
@IsOptional()
message: string;
} }

View File

@@ -0,0 +1,19 @@
import { IsBoolean, IsEmpty, IsInt, IsIn, IsNotEmpty, IsNumber, IsArray, IsString, IsOptional, IsEnum } from "class-validator";
export class roomDto
{
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@IsNotEmpty()
@IsIn(["public", "protected", "private", "direct", "user"])
type: string;
@IsArray()
@IsString({ each: true })
@IsOptional()
users?: string[]; // usernames
}

View File

@@ -1,5 +1,4 @@
import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator"; import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator";
import { IsNull } from "typeorm";
export class setCurrentRoomDto export class setCurrentRoomDto
{ {

View File

@@ -1,10 +1,13 @@
import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator"; import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator";
import { IsNull } from "typeorm"; import { Socket } from 'socket.io';
export class joinRoomDto export class socketDto extends Socket
{ {
@IsString() @IsString()
@IsNotEmpty() username: string;
room_name: string;
@IsString()
room: string;
} }

View File

@@ -1,38 +1,36 @@
import { import { Entity, Column, ManyToOne, ManyToMany, JoinTable, PrimaryGeneratedColumn } from "typeorm";
Entity, import { IsBoolean, IsEmpty, IsInt, IsIn, IsNotEmpty, IsNumber, IsArray, IsString, IsOptional, IsEnum } from "class-validator";
Column, import { Exclude, Expose } from 'class-transformer';
ManyToOne,
ManyToMany,
JoinTable,
PrimaryGeneratedColumn
} from "typeorm";
import { User } from 'src/users/entities/user.entity'; import { User } from 'src/users/entities/user.entity';
import { messagesDto } from 'src/chat/dto/messages.dto';
@Entity('chatroom') @Entity('chatroom')
export class Chatroom { export class Chatroom
{
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@Column() @Column()
@IsString()
@IsNotEmpty()
name: string; name: string;
@Column() @Column()
type: string; @IsString()
@IsNotEmpty()
// @ManyToOne(type => User, user => user.ownedRooms) @IsIn(["public", "protected", "private", "direct", "user"])
// owner: User; type: string;
//
// @ManyToMany(type => User)
// @JoinTable()
// users: User[];
@Column() @Column()
owner: string; // name owner: string; // username
@Column("simple-array") @Column("simple-array")
users: string[]; // names @IsArray()
@IsString({ each: true })
@IsOptional()
users?: string[]; // usernames
@Column("json") @Column("json")
messages: { name: string, message: string }[]; messages: messagesDto[];
} }

View File

@@ -92,7 +92,8 @@ export class GameService {
} }
this.userRepository.save(user); this.userRepository.save(user);
} }
if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME) // if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME) // WIP: need to fix STATUS.IN_GAME
if (grantTicketDto.isGameIsWithInvitation === true)
{ {
const secondUser : Partial<User> = await this.userService.findOne(grantTicketDto.playerTwoUsername) const secondUser : Partial<User> = await this.userService.findOne(grantTicketDto.playerTwoUsername)
if (!secondUser || secondUser.username === user.username) if (!secondUser || secondUser.username === user.username)
@@ -104,17 +105,18 @@ export class GameService {
tok.numberOfRegisteredUser = 0; tok.numberOfRegisteredUser = 0;
tok.token = encryptedTextToReturn; tok.token = encryptedTextToReturn;
this.tokenGameRepository.save(tok); this.tokenGameRepository.save(tok);
this.userService.updateStatus(user.id, "In Pool") this.userService.updateStatus(user.id, STATUS.IN_POOL)
return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn });
} }
else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) { // else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) { // WIP: need to fix STATUS.IN_GAME
else if (grantTicketDto.isGameIsWithInvitation === false) {
const encryptedTextToReturn = await this.encryptToken(user.username + '_' const encryptedTextToReturn = await this.encryptToken(user.username + '_'
+ grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date()) + grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date())
const tok = this.tokenGameRepository.create(grantTicketDto); const tok = this.tokenGameRepository.create(grantTicketDto);
tok.numberOfRegisteredUser = 0; tok.numberOfRegisteredUser = 0;
tok.token = encryptedTextToReturn; tok.token = encryptedTextToReturn;
this.tokenGameRepository.save(tok); this.tokenGameRepository.save(tok);
this.userService.updateStatus(user.id, "In Pool") this.userService.updateStatus(user.id, STATUS.IN_POOL)
return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn });
} }
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({message : "Internal Server Error"}); return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({message : "Internal Server Error"});
@@ -140,13 +142,13 @@ export class GameService {
const userOne : User = await this.userRepository.createQueryBuilder('user') const userOne : User = await this.userRepository.createQueryBuilder('user')
.where("user.username = :username", {username : tokenGame.playerOneUsername}) .where("user.username = :username", {username : tokenGame.playerOneUsername})
.getOne(); .getOne();
this.userService.updateStatus(userOne.id, "In Game") this.userService.updateStatus(userOne.id, STATUS.IN_GAME)
const userTwo : User = await this.userRepository.createQueryBuilder('user') const userTwo : User = await this.userRepository.createQueryBuilder('user')
.where("user.username = :username", {username : tokenGame.playerTwoUsername}) .where("user.username = :username", {username : tokenGame.playerTwoUsername})
.getOne(); .getOne();
this.deleteToken(userOne) this.deleteToken(userOne)
this.deleteToken(userTwo) this.deleteToken(userTwo)
this.userService.updateStatus(userTwo.id, "In Game") this.userService.updateStatus(userTwo.id, STATUS.IN_GAME)
} }
return true; return true;
} }
@@ -166,7 +168,7 @@ export class GameService {
const user : User = await this.userRepository.createQueryBuilder('user') const user : User = await this.userRepository.createQueryBuilder('user')
.where("user.username = :username", {username : tokenGame.playerOneUsername}) .where("user.username = :username", {username : tokenGame.playerOneUsername})
.getOne(); .getOne();
this.userService.updateStatus(user.id, "In Game") this.userService.updateStatus(user.id, STATUS.IN_GAME)
this.deleteToken(user) this.deleteToken(user)
return true; return true;
} }
@@ -196,7 +198,7 @@ export class GameService {
async declineInvitation(user : User, token : string, @Res() res : Response) async declineInvitation(user : User, token : string, @Res() res : Response)
{ {
if (user.status !== "Connected") if (user.status !== STATUS.CONNECTED)
return res.status(HttpStatus.FORBIDDEN).json({message : "You must not be in game to decline an invitation"}); return res.status(HttpStatus.FORBIDDEN).json({message : "You must not be in game to decline an invitation"});
console.log("On décline l'invitation") console.log("On décline l'invitation")
const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame') const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame')
@@ -221,10 +223,10 @@ export class GameService {
{ {
const playerOne = await this.userRepository.findOneBy({username : tokenGame.playerOneUsername}) const playerOne = await this.userRepository.findOneBy({username : tokenGame.playerOneUsername})
const playerTwo = await this.userRepository.findOneBy({username : tokenGame.playerTwoUsername}) const playerTwo = await this.userRepository.findOneBy({username : tokenGame.playerTwoUsername})
if (playerOne.status !== "Disconnected") if (playerOne.status !== STATUS.DISCONNECTED)
this.userService.updateStatus(playerOne.id, "Connected") this.userService.updateStatus(playerOne.id, STATUS.CONNECTED)
if (playerTwo.status !== "Disconnected") if (playerTwo.status !== STATUS.DISCONNECTED)
this.userService.updateStatus(playerTwo.id, "Connected") this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED)
return this.tokenGameRepository.remove(tokenGame); return this.tokenGameRepository.remove(tokenGame);
} }
return new HttpException("Token not found !", HttpStatus.NOT_FOUND) return new HttpException("Token not found !", HttpStatus.NOT_FOUND)
@@ -232,7 +234,7 @@ export class GameService {
async acceptInvitation(user : User, token : string, @Res() res : Response) async acceptInvitation(user : User, token : string, @Res() res : Response)
{ {
if (user.status !== "Connected") if (user.status !== STATUS.CONNECTED)
return res.status(HttpStatus.FORBIDDEN).send("") return res.status(HttpStatus.FORBIDDEN).send("")
const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokenGame') const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokenGame')
.andWhere('tokenGame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username}) .andWhere('tokenGame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username})
@@ -294,8 +296,8 @@ export class GameService {
this.userService.incrementVictories(playerOne.id) this.userService.incrementVictories(playerOne.id)
this.userService.incrementDefeats(playerTwo.id) this.userService.incrementDefeats(playerTwo.id)
} }
this.userService.updateStatus(playerOne.id, "Connected") this.userService.updateStatus(playerOne.id, STATUS.CONNECTED)
this.userService.updateStatus(playerTwo.id, "Connected") this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED)
return HttpStatus.OK return HttpStatus.OK
} }
} }

View File

@@ -45,7 +45,6 @@ export class UsersService {
isEnabledTwoFactorAuth: user.isEnabledTwoFactorAuth, isEnabledTwoFactorAuth: user.isEnabledTwoFactorAuth,
status: user.status, status: user.status,
stats: user.stats, stats: user.stats,
currentRoom: user.currentRoom,
}; };
console.log(`Returned Partial User.` + partialUser.username + user.username); console.log(`Returned Partial User.` + partialUser.username + user.username);
return partialUser; return partialUser;

View File

@@ -1,55 +0,0 @@
server {
listen 8080;
listen [::]:8080;
server_name localhost;
location /api/v2 {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://backend_dev:3000;
}
location /chat {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://backend_dev:5000/chat;
}
location /api/v2/game/gameserver {
deny all;
}
location /pong {
proxy_pass http://game_server:8042/pong;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://frontend_dev:8080;
}
}
server {
listen 35729 default_server;
listen [::]:35729 default_server;
server_name localhost;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://frontend_dev:35729;
}
}

View File

@@ -1,9 +1,10 @@
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
import Header from '../../pieces/Header.svelte';
import { fade, fly } from 'svelte/transition'; import { fade, fly } from 'svelte/transition';
import Header from '../../pieces/Header.svelte';
import { fetchAvatar } from "../../pieces/utils";
import * as pong from "./client/pong"; import * as pong from "./client/pong";
import { gameState } from "./client/ws"; import { gameState } from "./client/ws";
@@ -12,6 +13,9 @@
let user; let user;
let allUsers; let allUsers;
let playerOneAvatar;
let playerTwoAvatar;
//Game's stuff //Game's stuff
const options = new pong.InitOptions(); const options = new pong.InitOptions();
const gameAreaId = "game_area"; const gameAreaId = "game_area";
@@ -26,9 +30,10 @@
let showWaitPage = false; let showWaitPage = false;
let invitations = []; let invitations = [];
let watchGameStateInterval; let watchGameStateInterval;
const watchGameStateIntervalRate = 142; const watchGameStateIntervalRate = 142;
let watchMatchStartInterval;
const watchMatchStartIntervalRate = 111;
onMount( async() => { onMount( async() => {
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
@@ -39,6 +44,7 @@
}) })
onDestroy( async() => { onDestroy( async() => {
clearInterval(watchMatchStartInterval);
clearInterval(watchGameStateInterval); clearInterval(watchGameStateInterval);
pong.destroy(); pong.destroy();
}) })
@@ -76,6 +82,7 @@
showWaitPage = false; showWaitPage = false;
if (response.ok && token) if (response.ok && token)
{ {
watchMatchStartInterval = setInterval(watchMatchStart, watchMatchStartIntervalRate);
watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate); watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate);
pong.init(matchOptions, options, gameAreaId, token); pong.init(matchOptions, options, gameAreaId, token);
hiddenGame = false; hiddenGame = false;
@@ -106,6 +113,7 @@
console.log(invitation); console.log(invitation);
if (invitation.token) if (invitation.token)
{ {
watchMatchStartInterval = setInterval(watchMatchStart, watchMatchStartIntervalRate);
watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate); watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate);
options.playerOneUsername = invitation.playerOneUsername; options.playerOneUsername = invitation.playerOneUsername;
options.playerTwoUsername = invitation.playerTwoUsername; options.playerTwoUsername = invitation.playerTwoUsername;
@@ -117,16 +125,24 @@
} }
} }
async function watchMatchStart()
{
if (gameState.matchStarted)
{
clearInterval(watchMatchStartInterval);
playerOneAvatar = await fetchAvatar(gameState.playerOneUsername);
playerTwoAvatar = await fetchAvatar(gameState.playerTwoUsername);
gameState.matchStarted = gameState.matchStarted; // trigger Svelte reactivity
}
}
const watchGameState = () => { const watchGameState = () => {
console.log("watchGameState"); console.log("watchGameState");
if (gameState) { // trigger Svelte reactivity
gameState.matchStarted = gameState.matchStarted;
gameState.matchEnded = gameState.matchEnded;
gameState.matchAborted = gameState.matchAborted;
}
if (gameState.matchAborted || gameState.matchEnded) if (gameState.matchAborted || gameState.matchEnded)
{ {
clearInterval(watchGameStateInterval); clearInterval(watchGameStateInterval);
gameState.matchEnded = gameState.matchEnded; // trigger Svelte reactivity
gameState.matchAborted = gameState.matchAborted; // trigger Svelte reactivity
console.log("watchGameState, end"); console.log("watchGameState, end");
setTimeout(() => { setTimeout(() => {
resetPage(); resetPage();
@@ -176,6 +192,7 @@
} }
function leaveMatch() { function leaveMatch() {
clearInterval(watchMatchStartInterval);
clearInterval(watchGameStateInterval); clearInterval(watchGameStateInterval);
resetPage(); resetPage();
}; };
@@ -213,6 +230,14 @@
</div> </div>
{#if !hiddenGame} {#if !hiddenGame}
{#if gameState.matchStarted}
<div class="div_game">
<img class="avatar" src="{playerOneAvatar}" alt="player one avatar">
'{gameState.playerOneUsername}' VS '{gameState.playerTwoUsername}'
<img class="avatar" src="{playerTwoAvatar}" alt="player two avatar">
</div>
{/if}
{#if gameState.matchStarted && !gameState.matchEnded} {#if gameState.matchStarted && !gameState.matchEnded}
<div class="div_game"> <div class="div_game">
<button class="pong_button" on:click={leaveMatch}>forfeit</button> <button class="pong_button" on:click={leaveMatch}>forfeit</button>
@@ -356,5 +381,11 @@ canvas {
font-size: x-large; font-size: x-large;
padding: 10px; padding: 10px;
} }
.avatar {
min-height: 100px;
min-width: 100px;
max-width: 100px;
max-height: 100px;
}
</style> </style>

View File

@@ -1,10 +1,12 @@
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
import Header from '../../pieces/Header.svelte';
import MatchListElem from "../../pieces/MatchListElem.svelte";
import { fade, fly } from 'svelte/transition'; import { fade, fly } from 'svelte/transition';
import Header from '../../pieces/Header.svelte';
import MatchListElem from "../../pieces/MatchListElem.svelte";
import type { Match } from "../../pieces/Match";
import { fetchAvatar } from "../../pieces/utils";
import * as pongSpectator from "./client/pongSpectator"; import * as pongSpectator from "./client/pongSpectator";
import { gameState } from "./client/ws"; import { gameState } from "./client/ws";
@@ -13,14 +15,17 @@
let user; let user;
let allUsers; let allUsers;
//Game's stuff client side only let playerOneAvatar;
let playerTwoAvatar;
//Game's stuff
const gameAreaId = "game_area"; const gameAreaId = "game_area";
let sound = "off"; let sound = "off";
let matchList = [];
//html boolean for pages //html boolean for pages
let hiddenGame = true; let hiddenGame = true;
let matchList: Match[] = [];
let watchGameStateInterval; let watchGameStateInterval;
const watchGameStateIntervalRate = 142; const watchGameStateIntervalRate = 142;
@@ -39,22 +44,27 @@
pongSpectator.destroy(); pongSpectator.destroy();
}) })
async function initGameSpectator(gameSessionId: string, matchOptions: pongSpectator.MatchOptions) async function initGameSpectator(match: Match)
{ {
watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate); watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate);
pongSpectator.init(matchOptions, sound, gameAreaId, gameSessionId); pongSpectator.init(match.gameOptions, sound, gameAreaId, match.gameServerIdOfTheMatch);
// Users avatar
gameState.playerOneUsername = match.playerOneUsername;
gameState.playerTwoUsername = match.playerTwoUsername;
playerOneAvatar = await fetchAvatar(gameState.playerOneUsername);
playerTwoAvatar = await fetchAvatar(gameState.playerTwoUsername);
hiddenGame = false; hiddenGame = false;
}; };
const watchGameState = () => { const watchGameState = () => {
console.log("watchGameState") console.log("watchGameState")
if (gameState) { // trigger Svelte reactivity // gameState.matchStarted = gameState.matchStarted; // trigger Svelte reactivity
gameState.matchStarted = gameState.matchStarted;
gameState.matchEnded = gameState.matchEnded;
gameState.matchAborted = gameState.matchAborted;
}
if (gameState.matchAborted || gameState.matchEnded) if (gameState.matchAborted || gameState.matchEnded)
{ {
gameState.matchEnded = gameState.matchEnded; // trigger Svelte reactivity
gameState.matchAborted = gameState.matchAborted; // trigger Svelte reactivity
clearInterval(watchGameStateInterval); clearInterval(watchGameStateInterval);
console.log("watchGameState, end") console.log("watchGameState, end")
setTimeout(() => { setTimeout(() => {
@@ -80,7 +90,6 @@
.then( x => x.json() ); .then( x => x.json() );
}; };
</script> </script>
<!-- --> <!-- -->
@@ -102,6 +111,12 @@
</div> </div>
{#if !hiddenGame} {#if !hiddenGame}
<div class="div_game">
<img class="avatar" src="{playerOneAvatar}" alt="player one avatar">
'{gameState.playerOneUsername}' VS '{gameState.playerTwoUsername}'
<img class="avatar" src="{playerTwoAvatar}" alt="player two avatar">
</div>
{#if !gameState.matchEnded} {#if !gameState.matchEnded}
<div class="div_game"> <div class="div_game">
<button class="pong_button" on:click={leaveMatch}>leave</button> <button class="pong_button" on:click={leaveMatch}>leave</button>
@@ -127,7 +142,7 @@
{#if matchList.length !== 0} {#if matchList.length !== 0}
<menu id="match_list"> <menu id="match_list">
{#each matchList as match} {#each matchList as match}
<MatchListElem match={match} on:click={(e) => initGameSpectator(match.gameServerIdOfTheMatch, match.gameOptions)} /> <MatchListElem match={match} on:click={(e) => initGameSpectator(match)} />
{/each} {/each}
</menu> </menu>
{:else} {:else}
@@ -195,10 +210,15 @@ canvas {
font-size: x-large; font-size: x-large;
padding: 10px; padding: 10px;
} }
#match_list { #match_list {
font-family: 'Courier New', Courier, monospace; font-family: 'Courier New', Courier, monospace;
font-size: large; font-size: large;
} }
.avatar {
min-height: 100px;
min-width: 100px;
max-width: 100px;
max-height: 100px;
}
</style> </style>

View File

@@ -3,7 +3,7 @@ import * as c from "./constants.js"
import * as en from "../shared_js/enums.js" import * as en from "../shared_js/enums.js"
import { GameArea } from "./class/GameArea.js"; import { GameArea } from "./class/GameArea.js";
import { GameComponentsClient } from "./class/GameComponentsClient.js"; import { GameComponentsClient } from "./class/GameComponentsClient.js";
import { socket, resetGameState } from "./ws.js"; import { socket, gameState } from "./ws.js";
import { initAudio } from "./audio.js"; import { initAudio } from "./audio.js";
import type { InitOptions } from "./class/InitOptions.js"; import type { InitOptions } from "./class/InitOptions.js";
@@ -57,5 +57,5 @@ export function destroyBase()
} }
setGc(null); setGc(null);
setMatchOptions(null); setMatchOptions(null);
resetGameState(); gameState.resetGameState();
} }

View File

@@ -8,7 +8,6 @@ import { initBase, destroyBase, computeMatchOptions } from "./init.js";
export { computeMatchOptions } from "./init.js"; export { computeMatchOptions } from "./init.js";
export { MatchOptions } from "../shared_js/enums.js" export { MatchOptions } from "../shared_js/enums.js"
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
import { pong, gc } from "./global.js" import { pong, gc } from "./global.js"
import { setStartFunction } from "./global.js" import { setStartFunction } from "./global.js"

View File

@@ -10,16 +10,22 @@ import { muteFlag, soundRoblox } from "./audio.js"
import { sleep } from "./utils.js"; import { sleep } from "./utils.js";
import { Vector, VectorInteger } from "../shared_js/class/Vector.js"; import { Vector, VectorInteger } from "../shared_js/class/Vector.js";
export const gameState = { class GameState {
matchStarted: false, matchStarted: boolean;
matchEnded: false, matchEnded: boolean;
matchAborted: false matchAborted: boolean;
} playerOneUsername: string;
playerTwoUsername: string;
export function resetGameState() { constructor() {
gameState.matchStarted = false; this.resetGameState();
gameState.matchEnded = false; }
gameState.matchAborted = false; resetGameState() {
this.matchStarted = false;
this.matchEnded = false;
this.matchAborted = false;
this.playerOneUsername = "";
this.playerTwoUsername = "";
}
} }
class ClientInfo { class ClientInfo {
@@ -39,6 +45,7 @@ class ClientInfoSpectator {
const wsUrl = "ws://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/pong"; const wsUrl = "ws://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/pong";
export let socket: WebSocket; export let socket: WebSocket;
export const gameState = new GameState();
export const clientInfo = new ClientInfo(); export const clientInfo = new ClientInfo();
export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this
@@ -85,6 +92,8 @@ function preMatchListener(this: WebSocket, event: MessageEvent)
break; break;
case en.EventTypes.matchmakingComplete: case en.EventTypes.matchmakingComplete:
clientInfo.side = (<ev.EventMatchmakingComplete>data).side; clientInfo.side = (<ev.EventMatchmakingComplete>data).side;
gameState.playerOneUsername = (<ev.EventMatchmakingComplete>data).playerOneUsername;
gameState.playerTwoUsername = (<ev.EventMatchmakingComplete>data).playerTwoUsername;
if (clientInfo.side === en.PlayerSide.left) if (clientInfo.side === en.PlayerSide.left)
{ {
clientInfo.racket = gc.playerLeft; clientInfo.racket = gc.playerLeft;

View File

@@ -18,10 +18,13 @@ export class EventAssignId extends ServerEvent {
} }
export class EventMatchmakingComplete extends ServerEvent { export class EventMatchmakingComplete extends ServerEvent {
side: en.PlayerSide; side: en.PlayerSide = en.PlayerSide.noSide;
constructor(side: en.PlayerSide) { playerOneUsername: string;
playerTwoUsername: string;
constructor(playerOneUsername: string, playerTwoUsername: string) {
super(en.EventTypes.matchmakingComplete); super(en.EventTypes.matchmakingComplete);
this.side = side; this.playerOneUsername = playerOneUsername;
this.playerTwoUsername = playerTwoUsername;
} }
} }

View File

@@ -21,7 +21,7 @@
<div class="outer"> <div class="outer">
{#if user !== undefined} {#if user !== undefined}
<GenerateUserDisplay user={user} primary={true}/> <GenerateUserDisplay user={user}/>
<button on:click={() => (push('/profile/settings'))}>Profile Settings</button> <button on:click={() => (push('/profile/settings'))}>Profile Settings</button>
{:else} {:else}
<h2>Sorry</h2> <h2>Sorry</h2>

View File

@@ -3,8 +3,9 @@
import Card from '../../pieces/Card.svelte'; import Card from '../../pieces/Card.svelte';
import {onMount} from 'svelte'; import {onMount} from 'svelte';
import { push } from 'svelte-spa-router'; import { push } from 'svelte-spa-router';
import Button from '../../pieces/Button.svelte'; import Button from '../../pieces/Button.svelte';
import { fetchAvatar } from "../../pieces/utils";
let user; let user;
let avatar, newAvatar; let avatar, newAvatar;
@@ -16,40 +17,21 @@
const errors = { username: '', checkbox: '', avatar: ''}; const errors = { username: '', checkbox: '', avatar: ''};
let success = {username: '', avatar: '' }; let success = {username: '', avatar: '' };
onMount( async() => { onMount( async() => {
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
.then( (x) => x.json() ); .then( (x) => x.json() );
// do a .catch? // do a .catch?
if (user === undefined) { if (user === undefined) {
console.log('User did not load, something more official should prolly happen') console.log('User did not load, something more official should prolly happen')
} }
// i don't unerstand why this is necessary but it really doesn't like it otherwise // i don't unerstand why this is necessary but it really doesn't like it otherwise
nameTmp = user.username; nameTmp = user.username;
console.log(user.username)
set.tfa = user.isEnabledTwoFactorAuth; set.tfa = user.isEnabledTwoFactorAuth;
// tmp avatar = await fetchAvatar(user.username);
// console.log('this is what is in the avatar before fetch') })
// console.log(avatar)
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, {method: "GET"})
.then(response => {return response.blob()})
.then(data => {
const url = URL.createObjectURL(data);
avatar = url;
})
.catch(() => errors.avatar = 'Sorry your avatar could not be loaded' );
// .then(() => errors.avatar = '' ) // unnecessary i think cuz in on mount...
// tmp
// console.log('this is what is in the avatar')
// console.log(avatar)
// console.log('this is what is in the NEW Avatar')
// console.log(newAvatar)
})
const settingsHandler = async() => { const settingsHandler = async() => {
// I don't really care which i use at this point... // I don't really care which i use at this point...
@@ -91,39 +73,33 @@
// .then(() => console.log('successful sub of new settings')) // .then(() => console.log('successful sub of new settings'))
}; };
const uploadAvatar = async() => { const uploadAvatar = async() => {
errors.avatar = ''; errors.avatar = '';
if (newAvatar === undefined) { if (newAvatar === undefined) {
errors.avatar = 'You need to pick a file.' errors.avatar = 'You need to pick a file.'
return; return;
} }
const data = new FormData(); const data = new FormData();
data.append("file", newAvatar[0]); data.append("file", newAvatar[0]);
// tmp // tmp
console.log(data); console.log(data);
const responseWhenChangeAvatar = fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, const responseWhenChangeAvatar = fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`,
{ {
method : 'POST', method : 'POST',
body : data, body : data,
})
const responseFromServer = await responseWhenChangeAvatar;
if (responseFromServer.ok === true) {
uploadAvatarSuccess = true;
success.avatar = 'Your avatar has been updated';
}
else {
errors.avatar = responseFromServer.statusText;
}
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, {method: "GET"})
.then(response => {return response.blob()})
.then(data => {
const url = URL.createObjectURL(data);
avatar = url;
}) })
.catch(() => errors.avatar = 'Sorry your avatar could not be loaded' ); const responseFromServer = await responseWhenChangeAvatar;
if (responseFromServer.ok === true) {
uploadAvatarSuccess = true;
success.avatar = 'Your avatar has been updated';
}
else {
errors.avatar = responseFromServer.statusText;
}
avatar = await fetchAvatar(user.username);
} }
</script> </script>

View File

@@ -39,8 +39,7 @@
{#if user !== undefined} {#if user !== undefined}
<GenerateUserDisplay user={user} primary={false}/> <GenerateUserDisplay user={user}/>
<!-- <GenerateUserDisplay user={user} primary={true}/> -->
{:else} {:else}
<h2>Sorry</h2> <h2>Sorry</h2>
<div>Failed to load user {aUsername}</div> <div>Failed to load user {aUsername}</div>

View File

@@ -2,9 +2,9 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { fetchAvatar } from "./utils";
export let user; export let user;
export let primary; // kinda useless, not sure what i was going for... Might be userful after all
let rank = ''; let rank = '';
let avatar; let avatar;
// avatar needs to be updated!!! // avatar needs to be updated!!!
@@ -12,28 +12,10 @@
// add errors // add errors
let errors = {avatar: ''}; let errors = {avatar: ''};
onMount( async() => { onMount( async() => {
// console.log('Generate User Display, on mount ' + user.username) // console.log('Generate User Display, on mount ' + user.username)
if (primary) { avatar = await fetchAvatar(user.username);
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, {method: "GET"}) })
.then(response => {return response.blob()})
.then(data => {
const url = URL.createObjectURL(data);
avatar = url;
})
.catch(() => errors.avatar = 'Sorry your avatar could not be loaded' );
// console.log('avatar: ')
// console.log(avatar)
} else {
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar?username=${user.username}`, {method: "GET"})
.then(response => {return response.blob()})
.then(data => {
const url = URL.createObjectURL(data);
avatar = url;
})
.catch(() => errors.avatar = 'Sorry your avatar could not be loaded' );
}
})
/**** THIS IS BASICALLY ALL THE RANK LOGIC ERIC HAS MADE ****/ /**** THIS IS BASICALLY ALL THE RANK LOGIC ERIC HAS MADE ****/

View File

@@ -0,0 +1,10 @@
import type { MatchOptions } from "../pages/game/client/pongSpectator";
export { MatchOptions } from "../pages/game/client/pongSpectator";
export class Match {
gameServerIdOfTheMatch: string;
gameOptions: MatchOptions;
playerOneUsername: string;
playerTwoUsername: string;
}

View File

@@ -1,14 +1,9 @@
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
import { MatchOptions } from "../pages/game/client/pongSpectator"; import { Match, MatchOptions} from "./Match";
export let match: { export let match: Match;
gameServerIdOfTheMatch: string,
gameOptions: MatchOptions,
playerOneUsername: string,
playerTwoUsername: string
};
let matchOptionsString = ""; let matchOptionsString = "";
@@ -32,7 +27,7 @@
<li> <li>
<button on:click> <button on:click>
"{match.playerOneUsername}" VS "{match.playerTwoUsername}" '{match.playerOneUsername}' VS '{match.playerTwoUsername}'
<br/> [{matchOptionsString}] <br/> [{matchOptionsString}]
</button> </button>
</li> </li>

View File

@@ -0,0 +1,6 @@
export interface Room
{
name: string;
type: "public" | "protected" | "private" | "direct" | "user";
users?: string[];
}

View File

@@ -1,12 +1,23 @@
<script lang="ts"> <script lang="ts">
import { msgs, layout } from './Store_chat'; import { msgs, layout, allowed_chars } from './Store_chat';
import { change_room, create_room } from './Request_rooms'; import { change_room, create_room } from './Request_rooms';
import { onMount } from 'svelte';
import Button from './Element_button.svelte'; import Button from './Element_button.svelte';
import Warning from './Element_warning.svelte'; import Warning from './Element_warning.svelte';
export let back = ""; export let back = "";
let allowed_chars = 'loading...';
//let regex;
onMount(async() => {
let response = await fetch('/api/v2/chat/allowedchars');
let data = await response.json();
console.log("data:", data);
allowed_chars = data.chars;
//regex = new RegExp(`^[a-zA-Z0-9\\s${allowed_chars}]+$`);
});
let room_name: string; let room_name: string;
let room_type: string; let room_type: string;
let room_password: string; let room_password: string;
@@ -22,12 +33,16 @@
if (!formIsValid) if (!formIsValid)
return; return;
let room = {
name: room_name,
type: room_type,
};
// send the new room // send the new room
response = await create_room(room_name, room_type); response = await create_room(room);
// go to room // go to room
if (response.status === 200) if (response.status === 200)
await change_room(room_name); await change_room(response.room);
} }
</script> </script>
@@ -57,7 +72,10 @@
{/if} {/if}
<!-- name: --> <!-- name: -->
<label for="chat_name"><p>new room name :</p></label> <label for="chat_name"><p>new room name :</p></label>
<input id="chat_name" bind:value={room_name} name="room_name" required> <!--
<input id="chat_name" bind:value={room_name} name="room_name" placeholder="allowed special characters: {allowed_chars}" pattern={regex} required>
-->
<input id="chat_name" bind:value={room_name} name="room_name" placeholder="allowed special characters: {allowed_chars}" required>
<!-- [ ] pubic --> <!-- [ ] pubic -->
<label for="chat_public" class="_radio"> <label for="chat_public" class="_radio">
<p>public</p> <p>public</p>

View File

@@ -1,17 +1,19 @@
<script> <script>
import { layout, msgs, user } from './Store_chat'; import { layout, msgs, user } from './Store_chat';
import { change_room, get_room_messages, get_all_rooms } from './Request_rooms'; import { change_room, get_room_messages, get_my_rooms } from './Request_rooms';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import Button from './Element_button.svelte'; import Button from './Element_button.svelte';
let rooms = get_all_rooms(); let rooms = get_my_rooms();
// go to clicked room // go to clicked room
async function go_to_room(evt) async function go_to_room(room)
{ {
console.log("inside go_to_room"); console.log("inside go_to_room");
await change_room(evt.target.innerText);
console.log("room:", room);
await change_room(room);
await get_room_messages(); await get_room_messages();
} }
@@ -42,11 +44,10 @@
<p class="__center">/ you have no chat room yet /</p> <p class="__center">/ you have no chat room yet /</p>
</div> </div>
{#await rooms} {#await rooms}
<!-- promise is pending --> <p>rooms are loading...</p>
<p>rooms are loaded...</p>
{:then rooms} {:then rooms}
{#each rooms as room} {#each rooms as room}
<Button my_class="list" on_click={go_to_room}> <Button my_class="list" on_click={function() {go_to_room(room)}}>
{room.name} {room.name}
</Button> </Button>
{/each} {/each}

View File

@@ -1,32 +1,22 @@
<script> <script lang="ts">
import { layout, msgs, user, socket } from './Store_chat'; import { layout, msgs, user, socket } from './Store_chat';
import { join_room, change_room, get_room_messages } from './Request_rooms'; import { join_room, change_room, get_room_messages, get_all_rooms } from './Request_rooms';
import Button from './Element_button.svelte'; import Button from './Element_button.svelte';
export let back = ""; export let back = "";
let rooms = []; let rooms = get_all_rooms();
// ask api for the rooms
const get_rooms = fetch('/api/v2/chat/allrooms')
.then(resp => resp.json())
.then(data =>
{
console.log("data.rooms:", data.rooms);
for (let room of data.rooms)
console.log(room.name);
rooms = data.rooms;
});
// join the room // join the room
async function join_rooms(evt) async function join_rooms(room)
{ {
console.log("inside join_room"); console.log("inside join_room");
let room_name = evt.target.innerText;
await join_room(room_name); console.log("room:", room);
await change_room(room_name); const updated_room = await join_room(room);
console.log("updated room:", updated_room);
await change_room(updated_room);
} }
</script> </script>
@@ -58,12 +48,11 @@
<div class="__show_if_only_child"> <div class="__show_if_only_child">
<p class="__center">/ there are no public rooms yet /</p> <p class="__center">/ there are no public rooms yet /</p>
</div> </div>
{#await get_rooms} {#await rooms}
<!-- promise is pending --> <p>rooms are loading...</p>
<p>rooms are loaded...</p> {:then rooms}
{:then}
{#each rooms as room} {#each rooms as room}
<Button my_class="list" on_click={join_rooms}> <Button my_class="list" on_click={function() {join_rooms(room)}}>
{room.name} {room.name}
</Button> </Button>
{/each} {/each}

View File

@@ -1,6 +1,6 @@
<script> <script>
import { layout, socket, msgs, add_msg, room_name } from './Store_chat'; import { layout, socket, msgs, add_msg, current_room_name } from './Store_chat';
import Button from './Element_button.svelte'; import Button from './Element_button.svelte';
import Msg from './Element_msg.svelte'; import Msg from './Element_msg.svelte';
@@ -42,7 +42,7 @@
<!-- room_name --> <!-- room_name -->
<Button new_layout="room_set" my_class="room_name transparent"> <Button new_layout="room_set" my_class="room_name transparent">
{$room_name} {$current_room_name}
</Button> </Button>
<!-- close --> <!-- close -->

View File

@@ -1,10 +1,25 @@
<script> <script>
import { layout } from './Store_chat'; import { layout, current_room_name } from './Store_chat';
import { get_room_users, user_leave_room } from './Request_rooms';
import Button from './Element_button.svelte'; import Button from './Element_button.svelte';
export let back = ""; export let back = "";
let users = get_room_users();
function user_profile()
{
console/log("in user_profile");
}
function leave_room()
{
console.log("in leave_room");
user_leave_room();
layout.set("home");
}
</script> </script>
<div class="grid_box"> <div class="grid_box">
@@ -16,7 +31,7 @@
<!-- room_name --> <!-- room_name -->
<Button my_class="room_name deactivate"> <Button my_class="room_name deactivate">
&lt;room_name&gt; {$current_room_name}
</Button> </Button>
<!-- close --> <!-- close -->
@@ -26,7 +41,7 @@
<!-- panel_room_set --> <!-- panel_room_set -->
<div class="panel panel_room_set __border_top"> <div class="panel panel_room_set __border_top">
<Button new_layout="create" my_class="create"> <Button on_click={leave_room}>
leave leave
</Button> </Button>
<p>room users :</p> <p>room users :</p>
@@ -34,21 +49,15 @@
<div class="__show_if_only_child"> <div class="__show_if_only_child">
<p class="__center">/ there are no public rooms yet /</p> <p class="__center">/ there are no public rooms yet /</p>
</div> </div>
<!-- placeholders {#await users}
------------- --> <p>list of users is loading...</p>
<Button new_layout="user" my_class="list"> {:then users}
user 1 {#each users as user}
</Button> <Button new_layout="user" my_class="list" on_click={user_profile}>
<Button new_layout="user" my_class="list blocked"> {user}
user 2 </Button>
</Button> {/each}
<Button new_layout="user" my_class="list"> {/await}
user 3
</Button>
<Button new_layout="user" my_class="list">
user 4
</Button>
<!-- END placeholders -->
</div> </div>
</div> </div>

View File

@@ -1,4 +1,5 @@
import { msgs, user, layout, socket, room_name } from './Store_chat'; import { msgs, user, layout, socket, current_room_name } from './Store_chat';
import type { Room } from './Interface_chat';
export async function get_room_messages() export async function get_room_messages()
{ {
@@ -7,6 +8,9 @@ export async function get_room_messages()
const data = await response.json(); const data = await response.json();
const messages = data.messages; const messages = data.messages;
if (messages === null)
return;
messages.forEach(function(item) { messages.forEach(function(item) {
if (item.name === user.username) { if (item.name === user.username) {
item.name = "me"; item.name = "me";
@@ -16,88 +20,106 @@ export async function get_room_messages()
msgs.set(messages); msgs.set(messages);
} }
export async function create_room(room_name, room_type) export async function create_room(room: Room)
{ {
console.log("in create_room"); console.log("in create_room");
let form_data = {
room_name: room_name,
room_type: room_type,
};
// send the new room // send the new room
const response = await fetch('/api/v2/chat/create', { const response = await fetch('/api/v2/chat/create', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(form_data), body: JSON.stringify(room),
}); });
// get response status and message // get response status and message
let response_status = response.status; let response_status = response.status;
let data = await response.json(); let data = await response.json();
let response_message = ""; let response_message = "";
if (data.message)
response_message = data.message;
return { return {
status: response_status, status: response_status,
message: response_message message: data.message,
room: data.room,
}; };
} }
export async function join_room(room_name) export async function join_room(room: Room)
{ {
console.log("in join_room"); console.log("in join_room");
let name = {
room_name: room_name,
}
const response = await fetch('/api/v2/chat/join', { const response = await fetch('/api/v2/chat/join', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(name), body: JSON.stringify(room),
}); });
let data = await response.json(); let data = await response.json();
console.log(data.message);
socket.emit('join', room_name); return data.room;
} }
export async function change_room(name) export async function change_room(room: Room)
{ {
console.log("in change_room"); console.log("in change_room");
let r_name = { console.log("room:", room);
room_name: name,
}
const response = await fetch('/api/v2/chat/change', { const response = await fetch('/api/v2/chat/change', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(r_name), body: JSON.stringify(room),
}); });
let data = await response.json(); let data = await response.json();
console.log(data.message);
await get_room_messages(); await get_room_messages();
socket.emit('join', name);
room_name.set(name); let room_name = data.room.name;
if (room.type === 'direct')
{
room_name === room.users[0];
if (room_name === user.username)
room_name === room.users[1];
}
current_room_name.set(room_name);
layout.set("room"); layout.set("room");
} }
export async function get_my_rooms()
{
console.log("in get_my_rooms");
const response = await fetch('/api/v2/chat/myrooms');
const data = await response.json();
console.log("data.rooms:", data.rooms);
return data.rooms;
}
export async function get_all_rooms() export async function get_all_rooms()
{ {
console.log("in get_all_rooms"); console.log("in get_all_rooms");
// ask api for the rooms const response = await fetch('/api/v2/chat/allrooms');
const response = await fetch('/api/v2/chat/myrooms');
const data = await response.json(); const data = await response.json();
console.log("data.rooms:", data.rooms); return data.rooms;
for (let room of data.rooms)
console.log(room.name);
let rooms = data.rooms;
return rooms;
} }
export async function get_room_users()
{
console.log("in get_room_users");
const response = await fetch('/api/v2/chat/roomusers');
const data = await response.json();
return data.users;
}
export async function user_leave_room()
{
console.log("in leave_room");
const response = await fetch('/api/v2/chat/removeuser', {
method: 'DELETE',
});
}

View File

@@ -2,7 +2,7 @@ import { writable } from 'svelte/store';
export let msgs = writable([]); export let msgs = writable([]);
export let layout = writable("close"); export let layout = writable("close");
export let room_name = writable(""); export let current_room_name = writable("");
export let user; export let user;
export let socket; export let socket;
@@ -14,3 +14,4 @@ export function add_msg(name: string, message: string)
{ {
msgs.update(msgs => [...msgs, { name: "me", message: message }]); msgs.update(msgs => [...msgs, { name: "me", message: message }]);
} }

View File

@@ -0,0 +1,17 @@
export async function fetchAvatar(username: string)
{
return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar?username=${username}`)
.then((response) => {
if (!response.ok) {
throw new Error("Avatar not retrieved");
}
return response.blob();
})
.then((blob) => {
return URL.createObjectURL(blob);
})
.catch((error) => {
console.log("catch fetchAvatar: ", error);
});
}