Add files via upload
This commit is contained in:
72
api/README.md
Normal file
72
api/README.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# SENG 513 - UI / API
|
||||||
|
|
||||||
|
The following System Requirements and Installation steps are required for running the project locally. <br>
|
||||||
|
If you are accessing the project remotely ([vivideradicator.ca](http://www.vivideradicator.ca)), you may skip to the Usage section of this document. <br>
|
||||||
|
|
||||||
|
## System Requirements
|
||||||
|
Ensure that the following are already installed on your operating system of choice. <br>
|
||||||
|
> Node JS <br>
|
||||||
|
> NPM <br>
|
||||||
|
> MongoDB
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
User Interface
|
||||||
|
1. Clone or download the code found within the GitHub SENG513-UI repository, to a path/of/your/choosing <br>
|
||||||
|
1b. This can be done from within VS Code, via `git clone https://github.com/Rafael1321/SENG513-UI.git` <br>
|
||||||
|
2. Using the command prompt, `cd ~path/of/your/choosing/SENG513-UI` will navigate you to the proper directory <br>
|
||||||
|
3. Install all required project dependencies using `npm i` <br>
|
||||||
|
4. Run the application with `npm start` <br>
|
||||||
|
</br>
|
||||||
|
|
||||||
|
API <br>
|
||||||
|
Note that you must be running MongoDB before beginning this step. <br>
|
||||||
|
Instructions can be found at https://www.mongodb.com/docs/manual/installation/ <br>
|
||||||
|
1. Clone or download the code found within the GitHub SENG513-API repository, to a path/of/your/choosing <br>
|
||||||
|
1b. This can be done from within VS Code, via `git clone https://github.com/Rafael1321/SENG513-API.git` <br>
|
||||||
|
2. Using the command prompt, `cd ~path/of/your/choosing/SENG513-API` will navigate you to the proper directory <br>
|
||||||
|
3. Install all required project dependencies using `npm i` <br>
|
||||||
|
4. Run the application with `npm start` <br>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
The following steps can be used to experience the full functionality of the application. </br>
|
||||||
|
|
||||||
|
1. You will be greeted by the login/account registration page <br>
|
||||||
|
1b. If you do not yet have an account on DuoFinder, feel free to create one now. The 2 starter accounts below may also be used <br>
|
||||||
|
DO NOT use real password information here, it is not yet encrypted <br>
|
||||||
|
1c. Profile 1: email = vivideradicator@gmail.com password = seng513grading <br>
|
||||||
|
1d. Profile 2: email = me@vivideradicator.ca password = seng513grading <br>
|
||||||
|
2. Enter your email address and passwords into their respective fields <br>
|
||||||
|
3. Once logged in, the profile card can be seen. Clicking the edit button in the top right will enable customizing of your profile <br>
|
||||||
|
3b. You may change your profile picture, bio, gender, and age. Customize your profile as desired, and save the changes by pressing the same edit button again <br>
|
||||||
|
3c. Set your desired filters by pushing the 'Chat Filters' button, which will limit who you can match with based on your wishes <br>
|
||||||
|
4. To connect with another user, press the 'Find Duo' button <br>
|
||||||
|
4b. If you are wishing to test the functionality by yourself, simply log into another account on a seperate window and search for a match on both accounts <br>
|
||||||
|
5. Once connected, you may send messages in real-time to the other user. Pushing the 'Share Contact' button will automatically send your in-game information to the other user <br>
|
||||||
|
5b. Move onto another connection with the "Next" button, or return to your profile page by clicking the 'x' in the top-right corner <br>
|
||||||
|
6. Once back on your profile page, view your past matches with the 'Chat History' button <br>
|
||||||
|
6b. You may re-read past messages and give a rating to anyone you have talked to previously. To give a rating, press the top-right 'Rate Player' button <br>
|
||||||
|
7. Enjoy!
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
*v1.0* <br>
|
||||||
|
Front-end only. Focus on creating a visually appealing and easy-to-use UI. <br>
|
||||||
|
Use-state-based system, where changes were not saved and lost upon refreshing/exiting the app. <br>
|
||||||
|
<br>
|
||||||
|
*v2.0* </br>
|
||||||
|
Introduced save states and a backend. User information now saved. <br>
|
||||||
|
*v2.1* </br>
|
||||||
|
Real-time data share between users made possible with sockets. <br>
|
||||||
|
<br>
|
||||||
|
*v3.0* <br>
|
||||||
|
Polishing of the app, squashing of several major bugs. <br>
|
||||||
|
Small UI adjustments, ensuring all systems work as they should. <br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
## Contributors and Credit
|
||||||
|
Gaganjot Brar <br>
|
||||||
|
Harkamal Randhawa <br>
|
||||||
|
Martha Ibarra <br>
|
||||||
|
Rafael Flores Souza <br>
|
||||||
|
Richard Gingrich <br>
|
||||||
|
Tyler Chen
|
||||||
45
api/controllers/chats.js
Normal file
45
api/controllers/chats.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
const chats = require("../models/chat");
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
module.exports.saveMessage = async (req, res) => {
|
||||||
|
try{
|
||||||
|
const {senderId, receiverId, message} = req.body;
|
||||||
|
|
||||||
|
if(!senderId) return res.type('json').status(400).send("Sender id was not provided.");
|
||||||
|
if(!receiverId) return res.type('json').status(400).send("Receiver id was not provided.");
|
||||||
|
if(!message) return res.type('json').status(400).send("Message was not provided");
|
||||||
|
|
||||||
|
const newChat = new chats({
|
||||||
|
senderId:senderId,
|
||||||
|
receiverId:receiverId,
|
||||||
|
message:message
|
||||||
|
});
|
||||||
|
|
||||||
|
await newChat.save();
|
||||||
|
|
||||||
|
return res.type('json').status(201).send(newChat);
|
||||||
|
|
||||||
|
}catch(err) {
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.retrieveMessages = async (req, res) => {
|
||||||
|
try{
|
||||||
|
const {senderId, receiverId} = req.body;
|
||||||
|
|
||||||
|
if(!senderId) return res.type('json').status(400).send("Sender id was not provided.");
|
||||||
|
if(!receiverId) return res.type('json').status(400).send("Receiver id was not provided.");
|
||||||
|
|
||||||
|
const messages = [];
|
||||||
|
const cursor = chats.find({$or : [{$and:[{senderId:senderId}, {receiverId:receiverId}]}, {$and:[{senderId:receiverId}, {receiverId:senderId}]}]}).sort("timestamp").cursor();
|
||||||
|
for (let message = await cursor.next(); message != null; message = await cursor.next()) {
|
||||||
|
messages.push({senderId:message.senderId, receiverId:message.receiverId, message:message.message});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.type('json').status(200).send(messages);
|
||||||
|
|
||||||
|
}catch(err) {
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
45
api/controllers/commendations.js
Normal file
45
api/controllers/commendations.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
const commendation = require("../models/commendation");
|
||||||
|
const user = require("../models/user");
|
||||||
|
|
||||||
|
module.exports.saveCommendation = async (req, res) => {
|
||||||
|
try{
|
||||||
|
const {commenderId, commendedId, score} = req.body;
|
||||||
|
|
||||||
|
if(!commenderId) return res.type('json').status(400).send("Commender id was not provided.");
|
||||||
|
if(!commendedId) return res.type('json').status(400).send("Commended id was not provided.");
|
||||||
|
if(!score) return res.type('json').status(400).send("Score was not provided.");
|
||||||
|
if(score <= 0 || score > 10) return res.type('json').status(400).send("Score must be >= 1 and <= 10");
|
||||||
|
|
||||||
|
// Make sure the person is not commending again
|
||||||
|
let foundCommendation = await commendation.findOne({$and: [{commendedId:commendedId}, {commenderId:commenderId}]}).exec();
|
||||||
|
if(foundCommendation) return res.type('json').status(400).send("You have already commended this user.");
|
||||||
|
|
||||||
|
// Compute average reputation of commended person
|
||||||
|
let totalReputation = score, totalNumOfCommends = 1, newReputation = 0;
|
||||||
|
const cursor = commendation.find({commendedId:commendedId}).cursor();
|
||||||
|
for (let commendation = await cursor.next(); commendation != null; commendation = await cursor.next()) {
|
||||||
|
totalReputation += commendation.score;
|
||||||
|
totalNumOfCommends += 1;
|
||||||
|
}
|
||||||
|
newReputation = Math.round(totalReputation/totalNumOfCommends);
|
||||||
|
|
||||||
|
// Save New Commendation
|
||||||
|
await new commendation({
|
||||||
|
commendedId: commendedId,
|
||||||
|
commenderId: commenderId,
|
||||||
|
score: score
|
||||||
|
}).save();
|
||||||
|
|
||||||
|
// Update users reputation
|
||||||
|
const updatedUser = await user.findOneAndUpdate({_id: commendedId},
|
||||||
|
{reputation:newReputation},
|
||||||
|
{returnOriginal: false});
|
||||||
|
|
||||||
|
if(!updatedUser) return res.type('json').status(400).send("Commended user reputation could not be updated.");
|
||||||
|
|
||||||
|
return res.type('json').status(200).send();
|
||||||
|
|
||||||
|
}catch(err) {
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
61
api/controllers/filters.js
Normal file
61
api/controllers/filters.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
const filter = require("../models/filter");
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
module.exports.retrieve = async (req, res) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
const userId = req.params.userId;
|
||||||
|
|
||||||
|
// Basic Validation
|
||||||
|
if(!userId) return res.type('json').status(400).send('The user id is missing');
|
||||||
|
|
||||||
|
// Check if the filters exist
|
||||||
|
var searchedFilters = await filter.findOne({userId:userId}).exec();
|
||||||
|
|
||||||
|
if (!searchedFilters){
|
||||||
|
return res.type('json').status(404).send("Filters for specified user were not found.");
|
||||||
|
}else{
|
||||||
|
return res.type('json').status(200).send(searchedFilters);
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch(err) {
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.upsert = async(req, res) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
const {userId, filters} = req.body;
|
||||||
|
|
||||||
|
// Basic Validation
|
||||||
|
if(!userId) return res.type('json').status(400).send('The user id is missing');
|
||||||
|
|
||||||
|
// Check if the filters exist
|
||||||
|
var searchedFilters = await filter.findOne({userId:userId}).exec();
|
||||||
|
|
||||||
|
if(searchedFilters){ // Update the filters
|
||||||
|
|
||||||
|
if(!filters) return res.type('json').status(400).send('The filters are is missing');
|
||||||
|
|
||||||
|
const updatedFilters = await filter.findOneAndUpdate({userId:userId},
|
||||||
|
{...filters},
|
||||||
|
{returnOriginal: false});
|
||||||
|
return res.type('json').status(200).send(updatedFilters);
|
||||||
|
|
||||||
|
}else{ // Insert new filters [with default values]
|
||||||
|
|
||||||
|
const newFilters = new filter({
|
||||||
|
userId: userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
await newFilters.save();
|
||||||
|
|
||||||
|
return res.type('json').status(201).send(newFilters);
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch(err) {
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
29
api/controllers/matchings.js
Normal file
29
api/controllers/matchings.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
const matching = require("../models/matching");
|
||||||
|
const user = require("../models/user");
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
module.exports.retrieveMatchHistory = async (req, res) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
const userId = req.params.userId;
|
||||||
|
|
||||||
|
// Basic Validation
|
||||||
|
if(!userId) return res.type('json').status(400).send('The user id is missing');
|
||||||
|
|
||||||
|
// Check if the filters exist
|
||||||
|
const userList = [];
|
||||||
|
const cursor = matching.find({ $or:[ {'firstUser': userId}, {'secondUser': userId} ]}).sort('timestamp').cursor();
|
||||||
|
for (let match = await cursor.next(); match != null; match = await cursor.next()) {
|
||||||
|
if(match.firstUser !== userId){
|
||||||
|
userList.push(await user.findOne({_id:match.firstUser}).exec());
|
||||||
|
}else if(match.secondUser !== userId){
|
||||||
|
userList.push(await user.findOne({_id:match.secondUser}).exec());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.type('json').status(200).send(userList);
|
||||||
|
|
||||||
|
}catch(err) {
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
188
api/controllers/users.js
Normal file
188
api/controllers/users.js
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
const user = require("../models/user");
|
||||||
|
const _ = require('lodash');
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
module.exports.registerUser = async (req, res) => {
|
||||||
|
try{
|
||||||
|
const{displayName, gameName, tagLine, email, password, avatarImage} = req.body;
|
||||||
|
|
||||||
|
// Validation of body variables
|
||||||
|
if(!gameName) return res.type('json').status(404).send('The game name is missing');
|
||||||
|
if(!tagLine) return res.type('json').status(404).send('The tag line is missing');
|
||||||
|
if(!email) return res.type('json').status(404).send('The email is missing');
|
||||||
|
if(!password) return res.type('json').status(404).send('The password is missing');
|
||||||
|
|
||||||
|
let searchedUser = await user.findOne({gameName:gameName, tagLine:tagLine}).exec();
|
||||||
|
if(searchedUser) return res.type('json').status(400).send("Game name and tagline combination already in use.");
|
||||||
|
|
||||||
|
// Getting user's extra info from riot's api
|
||||||
|
const baseUrl1 = process.env.RIOT_BASE_URL1;
|
||||||
|
const fullUrl1 = `${baseUrl1}${gameName}\\${tagLine}`
|
||||||
|
|
||||||
|
let response = await axios.get(fullUrl1);
|
||||||
|
var extraData = response.data.data;
|
||||||
|
|
||||||
|
// Getting user's rank from riot's api
|
||||||
|
const baseUrl2 = process.env.RIOT_BASE_URL2;
|
||||||
|
const fullUrl2 = `${baseUrl2}${extraData.region}\\${gameName}\\${tagLine}`;
|
||||||
|
|
||||||
|
let response2 = await axios.get(fullUrl2);
|
||||||
|
let rank = parseRank(response2.data.data.currenttierpatched);
|
||||||
|
|
||||||
|
const newUser = new user({
|
||||||
|
riotId: extraData.puuid,
|
||||||
|
displayName: !displayName?gameName:displayName,
|
||||||
|
gameName: gameName,
|
||||||
|
tagLine: tagLine,
|
||||||
|
email: email,
|
||||||
|
password: await bcrypt.hash(password, await bcrypt.genSalt(10)),
|
||||||
|
avatarImage: avatarImage,
|
||||||
|
rank: [rank.rankType, rank.rankLevel],
|
||||||
|
accountLevel: extraData.account_level,
|
||||||
|
region: toRegionNo(extraData.region)
|
||||||
|
});
|
||||||
|
|
||||||
|
await newUser.save();
|
||||||
|
|
||||||
|
return res.type('json').status(201).send( _.pick(newUser, ['_id', 'riotId', 'displayName', 'gameName','tagLine', 'email', 'avatarImage', 'rank', 'accountLevel', 'region', 'age', 'gender', 'reputation', 'playerType', 'aboutMe']));
|
||||||
|
|
||||||
|
}catch(err){
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.loginUser = async (req, res) => {
|
||||||
|
try{
|
||||||
|
const {email, password} = req.body;
|
||||||
|
|
||||||
|
if(!email) return res.type('json').status(404).send('The username is missing');
|
||||||
|
if(!password) return res.type('json').status(404).send('The password is missing');
|
||||||
|
|
||||||
|
// Check if the username exists
|
||||||
|
var searchedUser = await user.findOne({email:email}).select("+password").exec();
|
||||||
|
|
||||||
|
if (!searchedUser){
|
||||||
|
return res.type('json').status(404).send("User was not found.");
|
||||||
|
}else{
|
||||||
|
// Check if the passwords match
|
||||||
|
const isPasswordValid = await bcrypt.compare(password, searchedUser.password);
|
||||||
|
if(!isPasswordValid){
|
||||||
|
return res.type('json').status(404).send("The password is incorrect.");
|
||||||
|
}
|
||||||
|
return res.type('json').status(200).send(_.pick(searchedUser, ['_id', 'riotId', 'displayName', 'gameName','tagLine','email', 'avatarImage', 'rank', 'accountLevel', 'region', 'age', 'gender', 'reputation', 'playerType', 'aboutMe']));
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch(err) {
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.updateUser = async (req, res) => {
|
||||||
|
try{
|
||||||
|
const updateDTO = req.body;
|
||||||
|
|
||||||
|
if(!updateDTO.userId) return res.type('json').status(400).send('A user id must be provided');
|
||||||
|
|
||||||
|
// Check if the username exists
|
||||||
|
var searchedUser = await user.findOne({_id:updateDTO.userId}).exec();
|
||||||
|
|
||||||
|
if (!searchedUser){
|
||||||
|
return res.type('json').status(404).send("User to update was not found.");
|
||||||
|
}else{
|
||||||
|
|
||||||
|
let updateParams = _.pick(updateDTO, ['displayName', 'age', 'gender', 'playerType', 'aboutMe', 'avatarImage']);
|
||||||
|
const updatedUser = await user.findOneAndUpdate({_id:updateDTO.userId},
|
||||||
|
{...updateParams},
|
||||||
|
{returnOriginal: false});
|
||||||
|
|
||||||
|
return res.type('json').status(200).send(updatedUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch(err) {
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Implement end-point to retrieve a user by ID (we only have login)
|
||||||
|
|
||||||
|
module.exports.findUser = async (req, res) => {
|
||||||
|
try{
|
||||||
|
const userId = req.params.userId;
|
||||||
|
|
||||||
|
if(!userId) return res.type('json').status(400).send('Invalid user id provided.');
|
||||||
|
|
||||||
|
// Find the user
|
||||||
|
var searchedUser = await user.findOne({_id:userId}).exec();
|
||||||
|
|
||||||
|
if (!searchedUser){
|
||||||
|
return res.type('json').status(404).send("User was not found.");
|
||||||
|
}else{
|
||||||
|
return res.type('json').status(200).send(_.pick(searchedUser, ['_id', 'riotId', 'displayName', 'gameName','tagLine','email', 'avatarImage', 'rank', 'accountLevel', 'region', 'age', 'gender', 'reputation', 'playerType', 'aboutMe']));
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch(err) {
|
||||||
|
return res.type('json').status(500).send(err.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Helper Functions */
|
||||||
|
|
||||||
|
function toRegionNo(region){
|
||||||
|
let res = -1;
|
||||||
|
switch(region){
|
||||||
|
case 'na':
|
||||||
|
res = 0;
|
||||||
|
break;
|
||||||
|
case 'eu':
|
||||||
|
res = 1;
|
||||||
|
break;
|
||||||
|
case 'ap':
|
||||||
|
res = 2;
|
||||||
|
break;
|
||||||
|
case 'kr':
|
||||||
|
res = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRank(rank){
|
||||||
|
|
||||||
|
let rankName = rank.substring(0, rank.indexOf(" "));
|
||||||
|
|
||||||
|
let rankType = -1;
|
||||||
|
let rankLevel = rank.substring(rank.indexOf(" ") + 1) * 1;
|
||||||
|
|
||||||
|
switch(rankName.toLowerCase()){
|
||||||
|
case 'iron':
|
||||||
|
rankType = 1;
|
||||||
|
break;
|
||||||
|
case 'bronze':
|
||||||
|
rankType = 2;
|
||||||
|
break;
|
||||||
|
case 'silver':
|
||||||
|
rankType = 3;
|
||||||
|
break;
|
||||||
|
case 'gold':
|
||||||
|
rankType = 4;
|
||||||
|
break;
|
||||||
|
case 'platinum':
|
||||||
|
rankType = 5;
|
||||||
|
break;
|
||||||
|
case 'diamond':
|
||||||
|
rankType = 6;
|
||||||
|
break;
|
||||||
|
case 'ascendant':
|
||||||
|
rankType = 7;
|
||||||
|
break;
|
||||||
|
case 'immortal':
|
||||||
|
rankType = 8;
|
||||||
|
break;
|
||||||
|
case 'radiant':
|
||||||
|
rankType = 9;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {rankType: rankType, rankLevel: rankLevel};
|
||||||
|
}
|
||||||
28
api/models/chat.js
Normal file
28
api/models/chat.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const ChatsSchema = new Schema({
|
||||||
|
senderId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
receiverId:{
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
timestamp : {
|
||||||
|
type: Date,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = mongoose.model("Chats", ChatsSchema);
|
||||||
22
api/models/commendation.js
Normal file
22
api/models/commendation.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const CommendationsSchema = new Schema({
|
||||||
|
commenderId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
commendedId:{
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
score: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
unique: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = mongoose.model("Commendations", CommendationsSchema);
|
||||||
42
api/models/filter.js
Normal file
42
api/models/filter.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const FiltersSchema = new Schema({
|
||||||
|
userId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
serverPreference: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: 0 // na
|
||||||
|
},
|
||||||
|
gameMode: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: 1 // casual
|
||||||
|
},
|
||||||
|
rankDisparity:{
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: [1, 1, 5, 2]
|
||||||
|
},
|
||||||
|
ageRange:{
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: [18, 25]
|
||||||
|
},
|
||||||
|
genders: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: [true, true, true, true]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = mongoose.model("Filters", FiltersSchema);
|
||||||
23
api/models/matching.js
Normal file
23
api/models/matching.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const MatchingSchema = new Schema({
|
||||||
|
firstUser: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
secondUser: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
timestamp : {
|
||||||
|
type: Date,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = mongoose.model("Matching", MatchingSchema);
|
||||||
17
api/models/socketModel.js
Normal file
17
api/models/socketModel.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const SocketSchema = new Schema({
|
||||||
|
userId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
socketId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = mongoose.model("SocketModel", SocketSchema);
|
||||||
89
api/models/user.js
Normal file
89
api/models/user.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const UserSchema = new Schema({
|
||||||
|
riotId: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
displayName: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
gameName: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
tagLine:{
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false,
|
||||||
|
select: false
|
||||||
|
},
|
||||||
|
avatarImage: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
rank : {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
accountLevel : {
|
||||||
|
type: Number,
|
||||||
|
required: true ,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
region : {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
|
age : {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
gender : {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: -1
|
||||||
|
},
|
||||||
|
reputation : {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: 5 // In the middle
|
||||||
|
},
|
||||||
|
playerType : {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: 1 // Casual
|
||||||
|
},
|
||||||
|
aboutMe : {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
unique: false,
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = mongoose.model("User", UserSchema);
|
||||||
3390
api/package-lock.json
generated
Normal file
3390
api/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
api/package.json
Normal file
31
api/package.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "seng513-api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "nodemon server.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/Rafael1321/SENG513-API.git"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/Rafael1321/SENG513-API/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/Rafael1321/SENG513-API#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"bcrypt": "^5.0.1",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^16.0.2",
|
||||||
|
"express": "^4.18.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"mongoose": "^6.6.1",
|
||||||
|
"nodemon": "^2.0.20",
|
||||||
|
"socket.io": "^4.5.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
12
api/routes/chats.js
Normal file
12
api/routes/chats.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
const chats = require("../controllers/chats");
|
||||||
|
|
||||||
|
router.route('/chats/retrieve')
|
||||||
|
.post(chats.retrieveMessages);
|
||||||
|
|
||||||
|
router.route('/chats/save')
|
||||||
|
.post(chats.saveMessage);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
9
api/routes/commendations.js
Normal file
9
api/routes/commendations.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
const commendations = require("../controllers/commendations");
|
||||||
|
|
||||||
|
router.route('/commendations')
|
||||||
|
.post(commendations.saveCommendation);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
12
api/routes/filters.js
Normal file
12
api/routes/filters.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
const filters = require("../controllers/filters");
|
||||||
|
|
||||||
|
router.route('/filters/:userId')
|
||||||
|
.get(filters.retrieve);
|
||||||
|
|
||||||
|
router.route('/filters')
|
||||||
|
.post(filters.upsert);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
9
api/routes/matchings.js
Normal file
9
api/routes/matchings.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
const matchings = require("../controllers/matchings");
|
||||||
|
|
||||||
|
router.route('/matchings/:userId')
|
||||||
|
.get(matchings.retrieveMatchHistory);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
18
api/routes/users.js
Normal file
18
api/routes/users.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
const users = require("../controllers/users");
|
||||||
|
|
||||||
|
router.route('/users/:userId')
|
||||||
|
.get(users.findUser);
|
||||||
|
|
||||||
|
router.route('/users/register')
|
||||||
|
.post(users.registerUser);
|
||||||
|
|
||||||
|
router.route('/users/login')
|
||||||
|
.post(users.loginUser);
|
||||||
|
|
||||||
|
router.route('/users/update')
|
||||||
|
.put(users.updateUser);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
68
api/server.js
Normal file
68
api/server.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const mongoose = require("mongoose");
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
const path = require('path');
|
||||||
|
const { Server } = require("socket.io");
|
||||||
|
const { createServer } = require('http');
|
||||||
|
const {broadcasting} = require('./sockets/broadcasting');
|
||||||
|
const cors=require("cors");
|
||||||
|
|
||||||
|
// DotEnv Configuration
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
// Mongo DB Configuration
|
||||||
|
const dbConfig = {
|
||||||
|
database : process.env.MONGO_DB ?? '',
|
||||||
|
hostname : process.env.MONGO_HOST ?? '',
|
||||||
|
port : process.env.MONGO_PORT ?? '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const connectionURI = `mongodb://${dbConfig.hostname}:${dbConfig.port}/${dbConfig.database}`
|
||||||
|
mongoose.connect(connectionURI);
|
||||||
|
const db = mongoose.connection;
|
||||||
|
db.on("error", console.error.bind(console, "connection error:"));
|
||||||
|
db.once("open", () => {
|
||||||
|
console.log("Database connected");
|
||||||
|
});
|
||||||
|
|
||||||
|
// App configuration
|
||||||
|
const appConfig = { port : process.env.APP_PORT ?? '5000' }
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
app.use(cors({
|
||||||
|
origin:'*',
|
||||||
|
credentials:true,
|
||||||
|
optionSuccessStatus:200,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Route Configuration
|
||||||
|
const userRoutes = require("./routes/users");
|
||||||
|
const filtersRoutes = require("./routes/filters");
|
||||||
|
const matchingRoutes = require("./routes/matchings");
|
||||||
|
const chatRoutes = require("./routes/chats");
|
||||||
|
const commendationRoutes = require("./routes/commendations");
|
||||||
|
|
||||||
|
app.use("/", userRoutes);
|
||||||
|
app.use("/", filtersRoutes);
|
||||||
|
app.use("/", matchingRoutes);
|
||||||
|
app.use("/", chatRoutes);
|
||||||
|
app.use("/", commendationRoutes)
|
||||||
|
|
||||||
|
// Starting the node.js server
|
||||||
|
app.listen(appConfig.port, () => {
|
||||||
|
console.log(`Serving express server on port ${appConfig.port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Socket configuration
|
||||||
|
|
||||||
|
const httpServer = createServer();
|
||||||
|
const io = new Server(httpServer);
|
||||||
|
|
||||||
|
broadcasting(io); // Used for sending and receiving simple real-time messages between users.
|
||||||
|
|
||||||
|
const socketConfig = { port : process.env.SOCKET_PORT ?? '2000'}
|
||||||
|
httpServer.listen(socketConfig.port, () => {
|
||||||
|
console.log(`Serving socket server on port ${socketConfig.port}`);
|
||||||
|
});
|
||||||
218
api/sockets/broadcasting.js
Normal file
218
api/sockets/broadcasting.js
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
const user = require("../models/user");
|
||||||
|
const matching = require("../models/matching");
|
||||||
|
const socketModel = require("../models/socketModel");
|
||||||
|
const filter = require("../models/filter");
|
||||||
|
|
||||||
|
// Used to keep track of who is connected and know their socket
|
||||||
|
const connectedUsers = new Map();
|
||||||
|
let matchingQueue = [];
|
||||||
|
|
||||||
|
function broadcasting(io){ // For connection and disconnection
|
||||||
|
|
||||||
|
io.on("connection", async (socket) => {
|
||||||
|
|
||||||
|
const connectedUserId = socket.handshake.query.userId;
|
||||||
|
|
||||||
|
if(!connectedUserId){
|
||||||
|
socket.emit(`error_user_connected`, {msg:`Invalid connected user id ${connectedUserId}.`});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!connectedUsers.has(connectedUserId)){
|
||||||
|
connectedUsers.set(connectedUserId, socket.id);
|
||||||
|
|
||||||
|
// Check if entry exists in the db
|
||||||
|
const socketInfo = await socketModel.findOne({userId:connectedUserId}).exec();
|
||||||
|
if(socketInfo){
|
||||||
|
if(socketInfo.socketId !== socket.socketId){
|
||||||
|
await socketModel.updateOne({userId:connectedUserId}, {socketId:socket.id}).exec();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
await new socketModel({
|
||||||
|
userId: connectedUserId,
|
||||||
|
socketId: socket.id
|
||||||
|
}).save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// save in database
|
||||||
|
socket.emit(`success_user_connected`, {msg:`User with id ${connectedUserId} CONNECTED.`});
|
||||||
|
}else{
|
||||||
|
socket.emit(`error_user_connected`, {msg:`User with id ${connectedUserId} is already connected.`});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MATCHING */
|
||||||
|
|
||||||
|
socket.on('find_matching', async (findMatchDTO) => {
|
||||||
|
|
||||||
|
// In case user id is invalid.
|
||||||
|
if(!findMatchDTO.userId){
|
||||||
|
socket.emit('error_find_matching',{msg:'User id is invalid.'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!findMatchDTO.filters){
|
||||||
|
socket.emit('error_find_matching', {msg:"Filters are invalid."});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case user id is not found
|
||||||
|
if(!connectedUsers.has(findMatchDTO.userId)){
|
||||||
|
socket.emit('error_find_matching', {msg:'User id does not exist.'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit('success_find_matching', {msg:`User with id ${findMatchDTO.userId} ONLINE.`});
|
||||||
|
|
||||||
|
// Attempting to find a match
|
||||||
|
let matchFound = -1;
|
||||||
|
const user1 = await user.findOne({_id:findMatchDTO.userId}).exec();
|
||||||
|
const user1Filters = findMatchDTO.filters;
|
||||||
|
for(let i = 0; i < matchingQueue.length && matchFound == -1; i++){
|
||||||
|
var user2Id = matchingQueue[i];
|
||||||
|
matching.countDocuments( {$or: [ { $and:[ {'firstUser': findMatchDTO.userId}, {'secondUser': user2Id} ]}, { $and:[ {'firstUser': user2Id}, {'secondUser': findMatchDTO.userId} ]}]}).exec().then( count => {
|
||||||
|
if(!count){
|
||||||
|
user.findOne({_id:user2Id}).exec().then( user2 => {
|
||||||
|
filter.findOne({userId:user2Id}).exec().then( user2Filters => {
|
||||||
|
|
||||||
|
// Region Matched
|
||||||
|
const regionMatched = (user1.region === user2Filters.serverPreference) && (user2.region === user1Filters.serverPreference);
|
||||||
|
|
||||||
|
// Game Mode Type
|
||||||
|
const playerTypeMatched = (user1.playerType === user2Filters.gameMode) && (user2.playerType === user1Filters.gameMode);
|
||||||
|
|
||||||
|
// Rank Macthed
|
||||||
|
const rankMatchedUser1 = (user1.rank[0] >= user2Filters.rankDisparity[0] && user1.rank[0] <= user2Filters.rankDisparity[2]);
|
||||||
|
const rankMatchedUser2 = (user2.rank[0] >= user1Filters.rankDisparity[0] && user2.rank[0] <= user1Filters.rankDisparity[2]);
|
||||||
|
const rankMatched = rankMatchedUser1 && rankMatchedUser2;
|
||||||
|
|
||||||
|
// Age Matched
|
||||||
|
const ageMatched = (user1.age >= user2Filters.ageRange[0] && user1.age <= user2Filters.ageRange[1]) &&
|
||||||
|
(user2.age >= user1Filters.ageRange[0] && user2.age <= user1Filters.ageRange[1]);
|
||||||
|
|
||||||
|
// Gender Matched
|
||||||
|
let genderMatched = false;
|
||||||
|
if(user1.gender !== -1 && user2.gender !== -1){
|
||||||
|
genderMatched = user2Filters.genders[user1.gender] && user1Filters.genders[user2.gender];
|
||||||
|
}
|
||||||
|
|
||||||
|
// They are match based on filters...
|
||||||
|
if(regionMatched && playerTypeMatched && rankMatched && ageMatched && genderMatched){
|
||||||
|
|
||||||
|
// Notify each user of the match and remove the matched user from queue
|
||||||
|
socket.emit('match_found', {user:user2});
|
||||||
|
|
||||||
|
if(!connectedUsers.has(user2Id)){
|
||||||
|
socketModel.findOne({userId:receiverId}).exec().then( socketInfo => {
|
||||||
|
if(socketInfo){
|
||||||
|
connectedUsers.set(socketInfo.userId, socketInfo.socketId);
|
||||||
|
io.to(socketInfo.socketId).emit('match_found', {user:user1});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
io.to(connectedUsers?.get(user2Id)).emit('match_found', {user:user1}); // TODO: add DB call if not in there
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store matching in the DB
|
||||||
|
const newMatch = new matching({
|
||||||
|
firstUser: findMatchDTO.userId,
|
||||||
|
secondUser: user2Id,
|
||||||
|
});
|
||||||
|
newMatch.save();
|
||||||
|
|
||||||
|
// Remove matched user from queue
|
||||||
|
matchingQueue = immutableRemove(matchFound, matchingQueue);
|
||||||
|
matchFound = i;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(matchFound === -1){
|
||||||
|
matchingQueue.push(findMatchDTO.userId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('stop_matching', (userId) => {
|
||||||
|
// In case user id is invalid.
|
||||||
|
if(!userId){
|
||||||
|
socket.emit('error_stop_matching',{msg:'User id is invalid.'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case user id is not found
|
||||||
|
if(!connectedUsers.has(userId)){
|
||||||
|
socket.emit('error_stop_matching', {msg:'User id does not exist.'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removing user from the matching queue
|
||||||
|
matchingQueue = immutableRemove(matchingQueue.indexOf(userId), matchingQueue);
|
||||||
|
|
||||||
|
socket.emit('success_stop_matching', {msg:`User with id ${userId} OFFLINE`});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* CHAT */
|
||||||
|
socket.on("send_msg" , async (receiverId, msg) => { // msg = Text
|
||||||
|
|
||||||
|
if(!receiverId){
|
||||||
|
socket.emit('error_send_msg', {msg:"Invalid receiver id."});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!connectedUsers.has(receiverId)){
|
||||||
|
|
||||||
|
// Check if it exists in DB
|
||||||
|
const socketInfo = await socketModel.findOne({userId:receiverId}).exec();
|
||||||
|
|
||||||
|
if(!socketInfo){
|
||||||
|
socket.emit('error_send_msg', {msg:"User with that id is not online."})
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
connectedUsers.set(receiverId, socketInfo.socketId);
|
||||||
|
io.to(socketInfo.socketId).emit("receive_msg", {msg:msg});
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
io.to(connectedUsers?.get(receiverId)).emit("receive_msg", {msg:msg});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* DISCONNECT*/
|
||||||
|
socket.on("disconnect", (socket) => {
|
||||||
|
|
||||||
|
const connectedUserId = getUserIdFromSocketId(socket.id);
|
||||||
|
if(!connectedUserId) return;
|
||||||
|
|
||||||
|
// Remove user from map
|
||||||
|
if(connectedUsers.has(connectedUserId)){
|
||||||
|
connectedUsers.delete(connectedUserId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HELPER FUNCTIONS */
|
||||||
|
|
||||||
|
function getUserIdFromSocketId(socketId){
|
||||||
|
let userId = "";
|
||||||
|
for (let [uId, info] of connectedUsers) {
|
||||||
|
if(info.socketId === socketId){
|
||||||
|
userId = uId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function immutableRemove(idx, array){
|
||||||
|
|
||||||
|
if(idx < 0 || idx >= array.length) return array;
|
||||||
|
|
||||||
|
newArray = [];
|
||||||
|
for(let i = 0; i < array.length; i++){
|
||||||
|
if(idx !== i) newArray.push(array[i]);
|
||||||
|
}
|
||||||
|
return newArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { broadcasting };
|
||||||
Reference in New Issue
Block a user