init
This commit is contained in:
68
.gitignore
vendored
Normal file
68
.gitignore
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Typescript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
#----
|
||||||
|
#Unity
|
||||||
|
unity/
|
||||||
|
uploads/
|
||||||
|
public/.well-known
|
||||||
|
|
||||||
21
activity/activity.controller.js
Normal file
21
activity/activity.controller.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// MODELS / TOOLS
|
||||||
|
const Activity = require("./activity.model");
|
||||||
|
|
||||||
|
exports.GetAllActivities = async (req, res) => {
|
||||||
|
|
||||||
|
let activityRequest;
|
||||||
|
if (req.body.type) {
|
||||||
|
activityRequest = { type: req.body.type };
|
||||||
|
} else if (req.body.username) {
|
||||||
|
activityRequest = { username: req.body.username };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
activityRequest = { };
|
||||||
|
}
|
||||||
|
|
||||||
|
const a = await Activity.Get(activityRequest);
|
||||||
|
if (!a) return res.status(500).send({ error: "Failed!!" });
|
||||||
|
|
||||||
|
return res.status(200).send(a);
|
||||||
|
};
|
||||||
|
|
||||||
56
activity/activity.model.js
Normal file
56
activity/activity.model.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const mongoose = require("mongoose");
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const activitySchema = new Schema(
|
||||||
|
{
|
||||||
|
type: {type: String},
|
||||||
|
username: {type: String},
|
||||||
|
timestamp: {type: Date},
|
||||||
|
data: {type: Object, _id: false},
|
||||||
|
});
|
||||||
|
|
||||||
|
activitySchema.methods.toObj = function () {
|
||||||
|
var elem = this.toObject();
|
||||||
|
delete elem.__v;
|
||||||
|
delete elem._id;
|
||||||
|
return elem;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Activity = mongoose.model("Activity", activitySchema);
|
||||||
|
exports.Activity = Activity;
|
||||||
|
|
||||||
|
// ------------------------------
|
||||||
|
|
||||||
|
exports.LogActivity = async (type, username, data) => {
|
||||||
|
var activity_data = {
|
||||||
|
type: type,
|
||||||
|
username: username,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const activity = new Activity(activity_data);
|
||||||
|
return await activity.save();
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetAll = async () => {
|
||||||
|
try {
|
||||||
|
const logs = await Activity.find({});
|
||||||
|
return logs;
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Get = async (data) => {
|
||||||
|
try {
|
||||||
|
const logs = await Activity.find(data);
|
||||||
|
return logs;
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
19
activity/activity.routes.js
Normal file
19
activity/activity.routes.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
const ActivityController = require("./activity.controller");
|
||||||
|
const AuthTool = require("../authorization/auth.tool");
|
||||||
|
const config = require("../config");
|
||||||
|
|
||||||
|
const ADMIN = config.permissions.ADMIN; //Highest permision, can read and write all users
|
||||||
|
const SERVER = config.permissions.SERVER; //Middle permission, can read all users
|
||||||
|
const USER = config.permissions.USER; //Lowest permision, can only do things on same user
|
||||||
|
|
||||||
|
exports.route = function (app) {
|
||||||
|
|
||||||
|
app.get("/activity", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
ActivityController.GetAllActivities,
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
94
authorization/auth.controller.js
Normal file
94
authorization/auth.controller.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const config = require('../config.js');
|
||||||
|
const jwtSecret = config.jwt_secret;
|
||||||
|
const UserModel = require('../users/users.model');
|
||||||
|
const UserTool = require('../users/users.tool');
|
||||||
|
|
||||||
|
exports.Login = (req, res) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
let refreshId = req.login.userId + jwtSecret;
|
||||||
|
let refresh_key = crypto.randomBytes(16).toString('base64');
|
||||||
|
let refresh_hash = crypto.createHmac('sha512', refresh_key).update(refreshId).digest("base64");
|
||||||
|
req.login.refresh_key = refresh_key;
|
||||||
|
|
||||||
|
let access_token = jwt.sign(req.login, jwtSecret);
|
||||||
|
|
||||||
|
//Delete some keys for security, empty keys are never valid, also update login time
|
||||||
|
var update = {refresh_key: refresh_key, proof_key: "", password_recovery_key: "", last_login_time: new Date(), last_online_time: new Date()};
|
||||||
|
UserModel.patch(req.login.userId, update);
|
||||||
|
|
||||||
|
var odata = {
|
||||||
|
id: req.login.userId,
|
||||||
|
username: req.login.username,
|
||||||
|
access_token: access_token,
|
||||||
|
refresh_token: refresh_hash,
|
||||||
|
permission_level: req.login.permission_level,
|
||||||
|
validation_level: req.login.validation_level,
|
||||||
|
duration: config.jwt_expiration,
|
||||||
|
server_time: new Date(),
|
||||||
|
version: config.version
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(201).send(odata);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
return res.status(500).send({error: err});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.KeepOnline = async(req, res, next) => {
|
||||||
|
|
||||||
|
var token = req.jwt;
|
||||||
|
UserModel.patch(token.userId, {last_online_time: new Date()});
|
||||||
|
|
||||||
|
var data = {id: token.userId, username: token.username, login_time: new Date(token.iat * 1000), server_time: new Date() };
|
||||||
|
return res.status(200).send(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetVersion = (req, res) =>{
|
||||||
|
return res.status(200).send({version: config.version});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----- verify user -----------
|
||||||
|
|
||||||
|
exports.ValidateToken = async(req, res, next) => {
|
||||||
|
|
||||||
|
var token = req.jwt;
|
||||||
|
var data = {id: token.userId, username: token.username, login_time: new Date(token.iat * 1000), server_time: new Date() };
|
||||||
|
return res.status(200).send(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.CreateProof = async(req, res) =>
|
||||||
|
{
|
||||||
|
var userId = req.jwt.userId;
|
||||||
|
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found"});
|
||||||
|
|
||||||
|
user.proof_key = crypto.randomBytes(20).toString('base64');
|
||||||
|
await UserModel.save(user);
|
||||||
|
|
||||||
|
return res.status(200).send({proof: user.proof_key});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.ValidateProof = async(req, res) =>
|
||||||
|
{
|
||||||
|
var username = req.params.username;
|
||||||
|
var proof = req.params.proof;
|
||||||
|
|
||||||
|
if(!username || typeof username != "string" || !proof || typeof proof != "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var user = await UserModel.getByUsername(username);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found"});
|
||||||
|
|
||||||
|
if(!user.proof_key || user.proof_key != proof)
|
||||||
|
return res.status(403).send({error: "Invalid Proof"});
|
||||||
|
|
||||||
|
return res.status(200).send();
|
||||||
|
}
|
||||||
44
authorization/auth.routes.js
Normal file
44
authorization/auth.routes.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
const AuthController = require('./auth.controller');
|
||||||
|
const AuthTool = require('./auth.tool');
|
||||||
|
|
||||||
|
exports.route = function (app) {
|
||||||
|
|
||||||
|
//Body: username, password
|
||||||
|
app.post('/auth', app.auth_limiter, [
|
||||||
|
AuthTool.isLoginValid,
|
||||||
|
AuthController.Login
|
||||||
|
]);
|
||||||
|
|
||||||
|
//Body: refresh_token
|
||||||
|
app.post('/auth/refresh', app.auth_limiter, [
|
||||||
|
AuthTool.isRefreshValid,
|
||||||
|
AuthController.Login
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get('/auth/keep',[
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthController.KeepOnline
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get('/auth/validate',[
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthController.ValidateToken
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/auth/proof/create", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthController.CreateProof
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/auth/proof/:username/:proof", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthController.ValidateProof
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get('/version', [
|
||||||
|
AuthController.GetVersion
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
162
authorization/auth.tool.js
Normal file
162
authorization/auth.tool.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const config = require('../config.js');
|
||||||
|
const UserModel = require('../users/users.model');
|
||||||
|
|
||||||
|
//-----Validations------
|
||||||
|
|
||||||
|
var AuthTool = {};
|
||||||
|
|
||||||
|
|
||||||
|
AuthTool.isValidJWT = (req, res, next) => {
|
||||||
|
|
||||||
|
if (!req.headers['authorization'])
|
||||||
|
return res.status(401).send();
|
||||||
|
|
||||||
|
try {
|
||||||
|
//Validate access token
|
||||||
|
let authorization = req.headers['authorization'];
|
||||||
|
req.jwt = jwt.verify(authorization, config.jwt_secret);
|
||||||
|
|
||||||
|
//Validate expiry time
|
||||||
|
const nowSeconds = Math.round(Number(new Date()) / 1000);
|
||||||
|
const expiration = req.jwt.iat + config.jwt_expiration;
|
||||||
|
if(nowSeconds > expiration)
|
||||||
|
return res.status(403).send({error: "Expired"});
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
return res.status(403).send({error: "Invalid Token"});
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthTool.isLoginValid = async(req, res, next) => {
|
||||||
|
|
||||||
|
if (!req.body || !req.body.password)
|
||||||
|
return res.status(400).send({error: 'Invalid params'});
|
||||||
|
|
||||||
|
//Requires EITHER username or email, dont need both
|
||||||
|
if (!req.body.email && !req.body.username)
|
||||||
|
return res.status(400).send({error: 'Invalid params'});
|
||||||
|
|
||||||
|
var user = null;
|
||||||
|
|
||||||
|
if(req.body.email)
|
||||||
|
user = await UserModel.getByEmail(req.body.email);
|
||||||
|
else if(req.body.username)
|
||||||
|
user = await UserModel.getByUsername(req.body.username);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "Invalid username or password"});
|
||||||
|
|
||||||
|
let validPass = AuthTool.validatePassword(user, req.body.password);
|
||||||
|
if(!validPass)
|
||||||
|
return res.status(400).send({error: 'Invalid username or password'});
|
||||||
|
|
||||||
|
if(user.permission_level <= 0)
|
||||||
|
return res.status(403).send({error: "Your account has been disabled, please contact support!"});
|
||||||
|
|
||||||
|
req.login = {
|
||||||
|
userId: user.id,
|
||||||
|
username: user.username,
|
||||||
|
email: user.email,
|
||||||
|
permission_level: user.permission_level,
|
||||||
|
validation_level: user.validation_level,
|
||||||
|
provider: req.body.email ? 'email' : 'username',
|
||||||
|
};
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthTool.isRefreshValid = async(req, res, next) => {
|
||||||
|
|
||||||
|
if (!req.body || !req.body.refresh_token)
|
||||||
|
return res.status(400).send();
|
||||||
|
|
||||||
|
if (!req.headers['authorization'])
|
||||||
|
return res.status(401).send();
|
||||||
|
|
||||||
|
if (typeof req.body.refresh_token !== "string")
|
||||||
|
return res.status(400).send();
|
||||||
|
|
||||||
|
try {
|
||||||
|
//Validate access token
|
||||||
|
let authorization = req.headers['authorization'];
|
||||||
|
req.jwt = jwt.verify(authorization, config.jwt_secret);
|
||||||
|
|
||||||
|
//Validate expiry time
|
||||||
|
const nowUnixSeconds = Math.round(Number(new Date()) / 1000);
|
||||||
|
const expiration = req.jwt.iat + config.jwt_refresh_expiration;
|
||||||
|
if(nowUnixSeconds > expiration)
|
||||||
|
return res.status(403).send({error: "Token Expired"});
|
||||||
|
|
||||||
|
//Validate refresh token
|
||||||
|
let refresh_token = req.body.refresh_token;
|
||||||
|
let hash = crypto.createHmac('sha512', req.jwt.refresh_key).update(req.jwt.userId + config.jwt_secret).digest("base64");
|
||||||
|
if (hash !== refresh_token)
|
||||||
|
return res.status(403).send({error: 'Invalid refresh token'});
|
||||||
|
|
||||||
|
//Validate refresh key in DB
|
||||||
|
var user = await UserModel.getById(req.jwt.userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "Invalid user"});
|
||||||
|
|
||||||
|
if(user.refresh_key !== req.jwt.refresh_key)
|
||||||
|
return res.status(403).send({error: 'Invalid refresh key'});
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
return res.status(403).send({error: "Invalid Token"});
|
||||||
|
}
|
||||||
|
|
||||||
|
req.login = req.jwt;
|
||||||
|
delete req.login.iat; //Delete previous iat to generate a new one
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthTool.hashPassword = (password) => {
|
||||||
|
let saltNew = crypto.randomBytes(16).toString('base64');
|
||||||
|
let hashNew = crypto.createHmac('sha512', saltNew).update(password).digest("base64");
|
||||||
|
let newPass = saltNew + "$" + hashNew;
|
||||||
|
return newPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthTool.validatePassword = (user, password) =>
|
||||||
|
{
|
||||||
|
let passwordFields = user.password.split('$');
|
||||||
|
let salt = passwordFields[0];
|
||||||
|
let hash = crypto.createHmac('sha512', salt).update(password).digest("base64");
|
||||||
|
return hash === passwordFields[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
//--- Permisions -----
|
||||||
|
|
||||||
|
AuthTool.isPermissionLevel = (required_permission) => {
|
||||||
|
return (req, res, next) => {
|
||||||
|
let user_permission_level = parseInt(req.jwt.permission_level);
|
||||||
|
if (user_permission_level >= required_permission) {
|
||||||
|
return next();
|
||||||
|
} else {
|
||||||
|
return res.status(403).send({error: "Permission Denied"});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthTool.isSameUserOr = (required_permission) => {
|
||||||
|
return (req, res, next) => {
|
||||||
|
let user_permission_level = parseInt(req.jwt.permission_level);
|
||||||
|
let userId = req.params.userId || "";
|
||||||
|
let same_user = (req.jwt.userId === userId || req.jwt.username.toLowerCase() === userId.toLowerCase());
|
||||||
|
if (userId && same_user) {
|
||||||
|
return next();
|
||||||
|
} else {
|
||||||
|
if (user_permission_level >= required_permission) {
|
||||||
|
return next();
|
||||||
|
} else {
|
||||||
|
return res.status(403).send({error: "Permission Denied"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = AuthTool;
|
||||||
134
cards/cards.controller.js
Normal file
134
cards/cards.controller.js
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
const CardModel = require('../cards/cards.model');
|
||||||
|
const Activity = require("../activity/activity.model");
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
exports.AddCard = async(req, res) =>
|
||||||
|
{
|
||||||
|
var tid = req.body.tid;
|
||||||
|
var type = req.body.type;
|
||||||
|
var team = req.body.team;
|
||||||
|
var rarity = req.body.rarity || "";
|
||||||
|
var mana = req.body.mana || 0;
|
||||||
|
var attack = req.body.attack || 0;
|
||||||
|
var hp = req.body.hp || 0;
|
||||||
|
var cost = req.body.cost || 1;
|
||||||
|
var packs = req.body.packs || [];
|
||||||
|
|
||||||
|
if(!tid || typeof tid !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!type || typeof type !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!team || typeof team !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!rarity || typeof rarity !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!Number.isInteger(mana) || !Number.isInteger(attack) || !Number.isInteger(hp) || !Number.isInteger(cost))
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(packs && !Array.isArray(packs))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
tid: tid,
|
||||||
|
type: type,
|
||||||
|
team: team,
|
||||||
|
rarity: rarity,
|
||||||
|
mana: mana,
|
||||||
|
attack: attack,
|
||||||
|
hp: hp,
|
||||||
|
cost: cost,
|
||||||
|
packs: packs,
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update or create
|
||||||
|
var card = await CardModel.get(tid);
|
||||||
|
if(card)
|
||||||
|
card = await CardModel.update(card, data);
|
||||||
|
else
|
||||||
|
card = await CardModel.create(data);
|
||||||
|
|
||||||
|
if(!card)
|
||||||
|
return res.status(500).send({error: "Error updating card"});
|
||||||
|
|
||||||
|
return res.status(200).send(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.AddCardList = async(req, res) =>
|
||||||
|
{
|
||||||
|
var cards = req.body.cards;
|
||||||
|
if(!Array.isArray(cards))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var ocards = [];
|
||||||
|
for(var i=0; i<cards.length; i++)
|
||||||
|
{
|
||||||
|
var card = cards[i];
|
||||||
|
if(card && card.tid && card.type && card.team)
|
||||||
|
{
|
||||||
|
var data = {
|
||||||
|
tid: card.tid,
|
||||||
|
type: card.type,
|
||||||
|
team: card.team,
|
||||||
|
rarity: card.rarity || "",
|
||||||
|
mana: card.mana || 0,
|
||||||
|
attack: card.attack || 0,
|
||||||
|
hp: card.hp || 0,
|
||||||
|
cost: card.cost || 0,
|
||||||
|
packs: card.packs || [],
|
||||||
|
}
|
||||||
|
|
||||||
|
var ccard = await CardModel.get(card.tid);
|
||||||
|
if(ccard)
|
||||||
|
ccard = await CardModel.update(ccard, data);
|
||||||
|
else
|
||||||
|
ccard = await CardModel.create(data);
|
||||||
|
|
||||||
|
ocards.push(ccard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send(ocards);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteCard = async(req, res) =>
|
||||||
|
{
|
||||||
|
CardModel.remove(req.params.tid);
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteAll = async(req, res) =>
|
||||||
|
{
|
||||||
|
CardModel.removeAll();
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetCard = async(req, res) =>
|
||||||
|
{
|
||||||
|
var tid = req.params.tid;
|
||||||
|
|
||||||
|
if(!tid)
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var card = await CardModel.get(tid);
|
||||||
|
if(!card)
|
||||||
|
return res.status(404).send({error: "Card not found: " + tid});
|
||||||
|
|
||||||
|
return res.status(200).send(card.toObj());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetAll = async(req, res) =>
|
||||||
|
{
|
||||||
|
var cards = await CardModel.getAll();
|
||||||
|
|
||||||
|
for(var i=0; i<cards.length; i++){
|
||||||
|
cards[i] = cards[i].toObj();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send(cards);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
111
cards/cards.model.js
Normal file
111
cards/cards.model.js
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const cardsSchema = new Schema({
|
||||||
|
|
||||||
|
tid: { type: String, index: true, unique: true, default: "" },
|
||||||
|
type: { type: String, default: "" },
|
||||||
|
team: { type: String, default: "" },
|
||||||
|
rarity: {type: String, default: ""},
|
||||||
|
mana: {type: Number, default: 0},
|
||||||
|
attack: {type: Number, default: 0},
|
||||||
|
hp: {type: Number, default: 0},
|
||||||
|
cost: {type: Number, default: 0},
|
||||||
|
|
||||||
|
packs: [{type: String}], //Card is available in those packs
|
||||||
|
});
|
||||||
|
|
||||||
|
cardsSchema.methods.toObj = function() {
|
||||||
|
var card = this.toObject();
|
||||||
|
delete card.__v;
|
||||||
|
delete card._id;
|
||||||
|
return card;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Card = mongoose.model('Cards', cardsSchema);
|
||||||
|
|
||||||
|
exports.get = async(tid) => {
|
||||||
|
try{
|
||||||
|
var card = await Card.findOne({tid: tid});
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async(filter) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
filter = filter || {};
|
||||||
|
var cards = await Card.find(filter);
|
||||||
|
return cards || [];
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getByPack = async(packId, filter) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
filter = filter || {};
|
||||||
|
if(packId)
|
||||||
|
{
|
||||||
|
filter.packs = {$in:[packId]};
|
||||||
|
}
|
||||||
|
var cards = await Card.find(filter);
|
||||||
|
return cards || [];
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.create = async(data) => {
|
||||||
|
try{
|
||||||
|
var card = new Card(data);
|
||||||
|
return await card.save();
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.update = async(card, data) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
if(!card) return null;
|
||||||
|
|
||||||
|
for (let i in data) {
|
||||||
|
card[i] = data[i];
|
||||||
|
card.markModified(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var updated = await card.save();
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.remove = async(tid) => {
|
||||||
|
try{
|
||||||
|
var result = await Card.deleteOne({tid: tid});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.removeAll = async() => {
|
||||||
|
try{
|
||||||
|
var result = await Card.deleteMany({});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
42
cards/cards.routes.js
Normal file
42
cards/cards.routes.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const CardController = require('./cards.controller');
|
||||||
|
const AuthTool = require('../authorization/auth.tool');
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
const ADMIN = config.permissions.ADMIN; //Highest permision, can read and write all users
|
||||||
|
const SERVER = config.permissions.SERVER; //Higher permission, can read all users
|
||||||
|
const USER = config.permissions.USER; //Lowest permision, can only do things on same user
|
||||||
|
|
||||||
|
exports.route = function (app) {
|
||||||
|
|
||||||
|
app.get('/cards/:tid', [
|
||||||
|
CardController.GetCard
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get('/cards', [
|
||||||
|
CardController.GetAll
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post('/cards/add', [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
CardController.AddCard
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post('/cards/add/list', [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
CardController.AddCardList
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/cards/:tid", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
CardController.DeleteCard
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/cards", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
CardController.DeleteAll
|
||||||
|
]);
|
||||||
|
};
|
||||||
111
cards/cards.tool.js
Normal file
111
cards/cards.tool.js
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
const config = require('../config.js');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const CardModel = require('../cards/cards.model');
|
||||||
|
|
||||||
|
const CardTool = {};
|
||||||
|
|
||||||
|
CardTool.getPackCards = async(pack) =>
|
||||||
|
{
|
||||||
|
var pack_cards = await CardModel.getByPack(pack.tid);
|
||||||
|
|
||||||
|
var cards = [];
|
||||||
|
for(var i=0; i<pack.cards; i++)
|
||||||
|
{
|
||||||
|
if(pack.random)
|
||||||
|
{
|
||||||
|
//Randomized set
|
||||||
|
var rarity_tid = CardTool.getRandomRarity(pack, i==0);
|
||||||
|
var variant_tid = CardTool.getRandomVariant(pack);
|
||||||
|
var rarity_cards = CardTool.getCardArray(pack_cards, rarity_tid);
|
||||||
|
var card = CardTool.getRandomCard(rarity_cards);
|
||||||
|
if(card)
|
||||||
|
{
|
||||||
|
var cardQ = {tid: card.tid, variant: variant_tid, quantity: 1};
|
||||||
|
cards.push(cardQ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(i < pack_cards.length)
|
||||||
|
{
|
||||||
|
//Fixed set
|
||||||
|
var card = pack_cards[i];
|
||||||
|
var variant_tid = CardTool.getRandomVariant(pack);
|
||||||
|
var cardQ = {tid: card.tid, variant: variant_tid, quantity: 1};
|
||||||
|
cards.push(cardQ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cards;
|
||||||
|
};
|
||||||
|
|
||||||
|
CardTool.getRandomRarity = (pack, is_first) =>
|
||||||
|
{
|
||||||
|
var rarities = is_first ? pack.rarities_1st : pack.rarities;
|
||||||
|
if(!rarities || rarities.length == 0)
|
||||||
|
return ""; //Any rarity
|
||||||
|
|
||||||
|
var total = 0;
|
||||||
|
for(var rarity of rarities) {
|
||||||
|
total += rarity.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rvalue = Math.floor(Math.random()*total);
|
||||||
|
|
||||||
|
for(var i=0; i<rarities.length; i++)
|
||||||
|
{
|
||||||
|
var rarity = rarities[i];
|
||||||
|
if(rvalue < rarity.value)
|
||||||
|
{
|
||||||
|
return rarity.tid;
|
||||||
|
}
|
||||||
|
rvalue -= rarity.value;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
CardTool.getRandomVariant = (pack) =>
|
||||||
|
{
|
||||||
|
var variants = pack.variants;
|
||||||
|
if(!variants || variants.length == 0)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var total = 0;
|
||||||
|
for(var variant of variants) {
|
||||||
|
total += variant.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rvalue = Math.floor(Math.random()*total);
|
||||||
|
|
||||||
|
for(var i=0; i<variants.length; i++)
|
||||||
|
{
|
||||||
|
var variant = variants[i];
|
||||||
|
if(rvalue < variant.value)
|
||||||
|
{
|
||||||
|
return variant.tid;
|
||||||
|
}
|
||||||
|
rvalue -= variant.value;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
CardTool.getCardArray = (all_cards, rarity) =>
|
||||||
|
{
|
||||||
|
var valid_cards = [];
|
||||||
|
for(var i=0; i<all_cards.length; i++)
|
||||||
|
{
|
||||||
|
var card = all_cards[i];
|
||||||
|
if(!rarity || card.rarity == rarity)
|
||||||
|
valid_cards.push(card);
|
||||||
|
}
|
||||||
|
return valid_cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
CardTool.getRandomCard = (all_cards, suffix) =>
|
||||||
|
{
|
||||||
|
if(all_cards.length > 0)
|
||||||
|
{
|
||||||
|
var card = all_cards[Math.floor(Math.random()*all_cards.length)];
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = CardTool;
|
||||||
73
config.js
Normal file
73
config.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
module.exports = {
|
||||||
|
version: "1.13",
|
||||||
|
|
||||||
|
port: 80,
|
||||||
|
port_https: 443,
|
||||||
|
api_title: "TCG Engine API", //Display name
|
||||||
|
api_url: "", //If you set the URL, will block all direct IP access, or wrong url access, leave blank to allow all url access
|
||||||
|
|
||||||
|
//HTTPS config, certificate is required if you want to enable HTTPS
|
||||||
|
https_key: "/etc/letsencrypt/live/yoursite.com/privkey.pem",
|
||||||
|
https_ca: "/etc/letsencrypt/live/yoursite.com/chain.pem",
|
||||||
|
https_cert: "/etc/letsencrypt/live/yoursite.com/cert.pem",
|
||||||
|
allow_http: true,
|
||||||
|
allow_https: false,
|
||||||
|
|
||||||
|
//JS Web Token Config
|
||||||
|
jwt_secret: "JWT_123456789", //Change this to a unique secret value
|
||||||
|
jwt_expiration: 3600 * 10, //In seconds (10 hours)
|
||||||
|
jwt_refresh_expiration: 3600 * 100, //In seconds (100 hours)
|
||||||
|
|
||||||
|
//User Permissions Config
|
||||||
|
permissions: {
|
||||||
|
USER: 1,
|
||||||
|
SERVER: 5,
|
||||||
|
ADMIN: 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
//Mongo Connection
|
||||||
|
mongo_user: "",
|
||||||
|
mongo_pass: "",
|
||||||
|
mongo_host: "127.0.0.1",
|
||||||
|
mongo_port: "27017",
|
||||||
|
mongo_db: "tcgengine",
|
||||||
|
|
||||||
|
//Limiter to protect from DDOS, will block IP that do too many requests
|
||||||
|
limiter_window: 1000 * 120, //in ms, will reset the counts after this time
|
||||||
|
limiter_max: 500, //max nb of GET requests within the time window
|
||||||
|
limiter_post_max: 100, //max nb of POST requests within the time window
|
||||||
|
limiter_auth_max: 10, //max nb of Login/Register request within the time window
|
||||||
|
limiter_proxy: false, //Must be set to true if your server is behind a proxy, otherwise the proxy itself will be blocked
|
||||||
|
|
||||||
|
ip_whitelist: ["127.0.0.1"], //These IP are not affected by the limiter, for example you could add your game server's IP
|
||||||
|
ip_blacklist: [], //These IP are blocked forever
|
||||||
|
|
||||||
|
//Email config, required for the API to send emails
|
||||||
|
smtp_enabled: false,
|
||||||
|
smtp_name: "TCG Engine", //Name of sender in emails
|
||||||
|
smtp_email: "", //Email used to send
|
||||||
|
smtp_server: "", //SMTP server URL
|
||||||
|
smtp_port: "465",
|
||||||
|
smtp_user: "", //SMTP auth user
|
||||||
|
smtp_password: "", //SMTP auth password
|
||||||
|
|
||||||
|
//ELO settings
|
||||||
|
elo_k: 32, //Higher K number will affect elo more each match
|
||||||
|
elo_ini_k: 128, //K value for the first X matches can be higher
|
||||||
|
elo_ini_match: 5, //X number of match for the previous value
|
||||||
|
|
||||||
|
//New Users
|
||||||
|
start_coins: 5000,
|
||||||
|
start_elo: 1000,
|
||||||
|
|
||||||
|
//Match Rewards
|
||||||
|
coins_victory: 200, //Victory coins reward
|
||||||
|
coins_defeat: 100, //Defeat coins reward
|
||||||
|
xp_victory: 100, //Victory xp reward
|
||||||
|
xp_defeat: 50, //Defeat xp reward
|
||||||
|
|
||||||
|
//Market
|
||||||
|
sell_ratio: 0.8, //Sell ratio compared to buy price
|
||||||
|
avatar_cost: 500,
|
||||||
|
cardback_cost: 1000,
|
||||||
|
};
|
||||||
75
decks/decks.controller.js
Normal file
75
decks/decks.controller.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
const DeckModel = require('../decks/decks.model');
|
||||||
|
const Activity = require("../activity/activity.model");
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
exports.AddDeck = async(req, res) =>
|
||||||
|
{
|
||||||
|
var tid = req.body.tid;
|
||||||
|
var title = req.body.title;
|
||||||
|
var hero = req.body.hero;
|
||||||
|
var cards = req.body.cards;
|
||||||
|
|
||||||
|
if(!tid || typeof tid !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!title || typeof title !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!hero || typeof hero !== "object")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(cards && !Array.isArray(cards))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var deck_data = {
|
||||||
|
tid: tid,
|
||||||
|
title: title,
|
||||||
|
hero: hero,
|
||||||
|
cards: cards || [],
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update or create
|
||||||
|
var deck = await DeckModel.get(tid);
|
||||||
|
if(deck)
|
||||||
|
deck = await DeckModel.update(deck, deck_data);
|
||||||
|
else
|
||||||
|
deck = await DeckModel.create(deck_data);
|
||||||
|
|
||||||
|
if(!deck)
|
||||||
|
res.status(500).send({error: "Error updating deck"});
|
||||||
|
|
||||||
|
return res.status(200).send(deck);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteDeck = async(req, res) => {
|
||||||
|
DeckModel.remove(req.params.tid);
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteAll = async(req, res) => {
|
||||||
|
DeckModel.removeAll();
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetDeck = async(req, res) =>
|
||||||
|
{
|
||||||
|
var deckId = req.params.tid;
|
||||||
|
|
||||||
|
if(!deckId)
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var deck = await DeckModel.get(deckId);
|
||||||
|
if(!deck)
|
||||||
|
return res.status(404).send({error: "Deck not found: " + deckId});
|
||||||
|
|
||||||
|
return res.status(200).send(deck.toObj());
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.GetAll = async(req, res) =>
|
||||||
|
{
|
||||||
|
var decks = await DeckModel.getAll();
|
||||||
|
return res.status(200).send(decks);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
99
decks/decks.model.js
Normal file
99
decks/decks.model.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const deckSchema = new Schema({
|
||||||
|
|
||||||
|
tid: { type: String, index: true, unique: true, default: "" },
|
||||||
|
title: { type: String, default: "" },
|
||||||
|
hero: {tid: String, variant: String, _id: false},
|
||||||
|
cards: [{tid: String, variant: String, quantity: Number, _id: false}],
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
deckSchema.methods.toObj = function() {
|
||||||
|
var deck = this.toObject();
|
||||||
|
delete deck.__v;
|
||||||
|
delete deck._id;
|
||||||
|
return deck;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Deck = mongoose.model('Decks', deckSchema);
|
||||||
|
|
||||||
|
exports.get = async(deckId) => {
|
||||||
|
try{
|
||||||
|
var deck = await Deck.findOne({tid: deckId});
|
||||||
|
return deck;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getList = async(decks_tids) => {
|
||||||
|
try{
|
||||||
|
var decks = await Deck.find({tid: { $in: decks_tids } });
|
||||||
|
return decks || [];
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async() => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var decks = await Deck.find()
|
||||||
|
return decks || [];
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.create = async(data) => {
|
||||||
|
try{
|
||||||
|
var deck = new Deck(data);
|
||||||
|
return await deck.save();
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.update = async(deck, data) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
if(!deck) return null;
|
||||||
|
|
||||||
|
for (let i in data) {
|
||||||
|
deck[i] = data[i];
|
||||||
|
deck.markModified(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var updated = await deck.save();
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.remove = async(deckId) => {
|
||||||
|
try{
|
||||||
|
var result = await Deck.deleteOne({tid: deckId});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.removeAll = async() => {
|
||||||
|
try{
|
||||||
|
var result = await Deck.deleteMany({});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
36
decks/decks.routes.js
Normal file
36
decks/decks.routes.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const DeckController = require('./decks.controller');
|
||||||
|
const AuthTool = require('../authorization/auth.tool');
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
const ADMIN = config.permissions.ADMIN; //Highest permision, can read and write all users
|
||||||
|
const SERVER = config.permissions.SERVER; //Higher permission, can read all users
|
||||||
|
const USER = config.permissions.USER; //Lowest permision, can only do things on same user
|
||||||
|
|
||||||
|
exports.route = function (app) {
|
||||||
|
|
||||||
|
app.get('/decks/:tid', [
|
||||||
|
DeckController.GetDeck
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get('/decks', [
|
||||||
|
DeckController.GetAll
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post('/decks/add', [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
DeckController.AddDeck
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/decks/:tid", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
DeckController.DeleteDeck
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/decks", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
DeckController.DeleteAll
|
||||||
|
]);
|
||||||
|
};
|
||||||
21
jobs/jobs.js
Normal file
21
jobs/jobs.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
const schedule = require('node-schedule');
|
||||||
|
|
||||||
|
const ExecuteJobs = async() =>
|
||||||
|
{
|
||||||
|
//console.log('Run Hourly Jobs.....');
|
||||||
|
|
||||||
|
//Add custom hourly jobs here
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.InitJobs = function()
|
||||||
|
{
|
||||||
|
schedule.scheduleJob('* 1 * * *', function(){ // this for one hour
|
||||||
|
ExecuteJobs();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Test run when starting
|
||||||
|
ExecuteJobs();
|
||||||
|
}
|
||||||
215
market/market.controller.js
Normal file
215
market/market.controller.js
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
const UserModel = require('../users/users.model');
|
||||||
|
const MarketModel = require('./market.model');
|
||||||
|
const UserTool = require('../users/users.tool');
|
||||||
|
const DateTool = require('../tools/date.tool');
|
||||||
|
const Activity = require("../activity/activity.model");
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
exports.addOffer = async(req, res) => {
|
||||||
|
|
||||||
|
var username = req.jwt.username;
|
||||||
|
var card_tid = req.body.card;
|
||||||
|
var variant = req.body.variant;
|
||||||
|
var quantity = req.body.quantity;
|
||||||
|
var price = req.body.price;
|
||||||
|
|
||||||
|
//Validate params
|
||||||
|
if (!username || !card_tid || !variant || !quantity || !price)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(typeof username !== "string"|| typeof quantity !== "number" || typeof price !== "number" || typeof card_tid !== "string" || typeof variant !== "string" )
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!Number.isInteger(quantity) || !Number.isInteger(price) || price <= 0 || quantity <= 0)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get user
|
||||||
|
var user = await UserModel.getByUsername(username);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Can't find user " + username });
|
||||||
|
|
||||||
|
if(!UserTool.hasCard(user, card_tid, variant, quantity))
|
||||||
|
return res.status(400).send({ error: "You don't have those cards!" });
|
||||||
|
|
||||||
|
//Offer
|
||||||
|
var offer = {
|
||||||
|
seller: username,
|
||||||
|
card: card_tid,
|
||||||
|
variant: variant,
|
||||||
|
quantity: quantity,
|
||||||
|
price: price,
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove card from user
|
||||||
|
var removeCards = [{tid: card_tid, variant: variant, quantity: -quantity}];
|
||||||
|
var addSucc = await UserTool.addCards(user, removeCards);
|
||||||
|
if(!addSucc)
|
||||||
|
return res.status(500).send({ error: "Error removing cards from user " + username });
|
||||||
|
|
||||||
|
//Update database
|
||||||
|
var uOffer = await MarketModel.add(username, card_tid, variant, offer);
|
||||||
|
var uUser = await UserModel.update(user, { cards: user.cards, });
|
||||||
|
|
||||||
|
if(!uUser || !uOffer)
|
||||||
|
return res.status(500).send({ error: "Error creating market offer " + username });
|
||||||
|
|
||||||
|
//Activity
|
||||||
|
//var act = await Activity.LogActivity("market_add", req.jwt.username, uOffer.toObj());
|
||||||
|
//if (!act) return res.status(500).send({ error: "Failed to log activity!" });
|
||||||
|
|
||||||
|
return res.status(200).send(uOffer.toObj());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.removeOffer = async(req, res) => {
|
||||||
|
|
||||||
|
var username = req.jwt.username;
|
||||||
|
var card_tid = req.body.card;
|
||||||
|
var variant = req.body.variant;
|
||||||
|
|
||||||
|
//Validate params
|
||||||
|
if (!username || !card_tid || !variant)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(typeof username !== "string"|| typeof card_tid !== "string" || typeof variant !== "string" )
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
var user = await UserModel.getByUsername(username);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Can't find user " + username });
|
||||||
|
|
||||||
|
var offer = await MarketModel.getOffer(username, card_tid, variant)
|
||||||
|
if (!offer)
|
||||||
|
return res.status(404).send({ error: "No market offer for " + username + " " + card_tid });
|
||||||
|
|
||||||
|
//Add cards user
|
||||||
|
var addCards = [{tid: card_tid, variant: variant, quantity: offer.quantity}];
|
||||||
|
var addSucc = await UserTool.addCards(user, addCards);
|
||||||
|
if(!addSucc)
|
||||||
|
return res.status(500).send({ error: "Error adding cards to user " + username });
|
||||||
|
|
||||||
|
//Update database
|
||||||
|
var uUser = await UserModel.update(user, { cards: user.cards });
|
||||||
|
var uOffer = await MarketModel.remove(username, card_tid, variant);
|
||||||
|
|
||||||
|
if(!uUser || !uOffer)
|
||||||
|
return res.status(500).send({ error: "Error removing market offer " + username });
|
||||||
|
|
||||||
|
//Activity
|
||||||
|
//var act = await Activity.LogActivity("market_remove", req.jwt.username, {});
|
||||||
|
//if (!act) return res.status(500).send({ error: "Failed to log activity!" });
|
||||||
|
|
||||||
|
return res.status(200).send({success: uOffer});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.trade = async(req, res) => {
|
||||||
|
|
||||||
|
var username = req.jwt.username;
|
||||||
|
var seller_user = req.body.seller;
|
||||||
|
var card_tid = req.body.card;
|
||||||
|
var variant = req.body.variant;
|
||||||
|
var quantity = req.body.quantity;
|
||||||
|
|
||||||
|
//Validate params
|
||||||
|
if (!username || !seller_user || !card_tid || !variant || !quantity)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(typeof seller_user !== "string" || typeof card_tid !== "string" || typeof variant !== "string" || typeof quantity !== "number")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!Number.isInteger(quantity) || quantity <= 0)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get user
|
||||||
|
var user = await UserModel.getByUsername(username);
|
||||||
|
var seller = await UserModel.getByUsername(seller_user);
|
||||||
|
if (!user || !seller)
|
||||||
|
return res.status(404).send({ error: "Can't find user " + username + " or " + seller_user });
|
||||||
|
|
||||||
|
if(user.id == seller.id)
|
||||||
|
return res.status(403).send({ error: "Can't trade with yourself!" });
|
||||||
|
|
||||||
|
//Get offer
|
||||||
|
var offer = await MarketModel.getOffer(seller_user, card_tid, variant)
|
||||||
|
if (!offer)
|
||||||
|
return res.status(404).send({ error: "No market offer for " + seller_user + " " + card_tid });
|
||||||
|
|
||||||
|
var value = quantity * offer.price;
|
||||||
|
if(user.coins < value)
|
||||||
|
return res.status(403).send({ error: "Not enough coins to trade!" });
|
||||||
|
if(quantity > offer.quantity)
|
||||||
|
return res.status(403).send({ error: "Not enough cards to trade!" });
|
||||||
|
|
||||||
|
//Add cards and coins
|
||||||
|
var addCards = [{tid: card_tid, variant: variant, quantity: quantity}];
|
||||||
|
var addSucc = await UserTool.addCards(user, addCards);
|
||||||
|
if(!addSucc)
|
||||||
|
return res.status(500).send({ error: "Error adding cards to user " + username });
|
||||||
|
|
||||||
|
user.coins -= value;
|
||||||
|
seller.coins += value;
|
||||||
|
|
||||||
|
//Update database
|
||||||
|
var uUser = await UserModel.update(user, { coins: user.coins, cards: user.cards });
|
||||||
|
var uSeller = await UserModel.update(seller, { coins: seller.coins });
|
||||||
|
var uOffer = await MarketModel.reduce(seller_user, card_tid, variant, quantity);
|
||||||
|
if(!uUser || !uOffer || !uSeller)
|
||||||
|
return res.status(500).send({ error: "Error trading market offer " + username + " " + seller_user });
|
||||||
|
|
||||||
|
//Activity
|
||||||
|
var aData = {buyer: username, seller: seller_user, card: card_tid, quantity: quantity, price: offer.price };
|
||||||
|
var act = await Activity.LogActivity("market_trade", req.jwt.username, aData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!" });
|
||||||
|
|
||||||
|
return res.status(200).send(aData);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getBySeller = async(req, res) => {
|
||||||
|
|
||||||
|
if(!req.params.username)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
var list = await MarketModel.getBySeller(req.params.username);
|
||||||
|
for(var i=0; i<list.length; i++){
|
||||||
|
list[i] = list[i].toObj();
|
||||||
|
}
|
||||||
|
return res.status(200).send(list);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getByCard = async(req, res) => {
|
||||||
|
|
||||||
|
var tid = req.params.tid;
|
||||||
|
var variant = req.params.variant;
|
||||||
|
|
||||||
|
if(!tid || !variant)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
var list = await MarketModel.getByCard(tid, variant);
|
||||||
|
for(var i=0; i<list.length; i++){
|
||||||
|
list[i] = list[i].toObj();
|
||||||
|
}
|
||||||
|
return res.status(200).send(list);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getOffer = async(req, res) => {
|
||||||
|
|
||||||
|
var tid = req.params.tid;
|
||||||
|
var variant = req.params.variant;
|
||||||
|
var username = req.params.username;
|
||||||
|
|
||||||
|
if(!tid || !variant || !username)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
var offer = await MarketModel.getOffer(username, tid, variant);
|
||||||
|
if(!offer)
|
||||||
|
return res.status(404).send({ error: "Offer not found" });
|
||||||
|
|
||||||
|
return res.status(200).send(offer.toObj());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async(req, res) => {
|
||||||
|
var list = await MarketModel.getAll();
|
||||||
|
for(var i=0; i<list.length; i++){
|
||||||
|
list[i] = list[i].toObj();
|
||||||
|
}
|
||||||
|
return res.status(200).send(list);
|
||||||
|
};
|
||||||
146
market/market.model.js
Normal file
146
market/market.model.js
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const marketSchema = new Schema({
|
||||||
|
|
||||||
|
seller: {type: String, index: true},
|
||||||
|
card: {type: String, index: true},
|
||||||
|
variant: {type: String},
|
||||||
|
quantity: {type: Number},
|
||||||
|
price: {type: Number},
|
||||||
|
time: {type: Date},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
marketSchema.methods.toObj = function() {
|
||||||
|
var offer = this.toObject();
|
||||||
|
delete offer.__v;
|
||||||
|
delete offer._id;
|
||||||
|
return offer;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Market = mongoose.model('Markets', marketSchema);
|
||||||
|
|
||||||
|
// Market DATA MODELS ------------------------------------------------
|
||||||
|
|
||||||
|
exports.getOffer = async(user, card_tid, variant_id) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var regex = new RegExp(["^", user, "$"].join(""), "i");
|
||||||
|
var offer = await Market.findOne({seller: regex, card: card_tid, variant: variant_id});
|
||||||
|
return offer;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getBySeller = async(user) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var regex = new RegExp(["^", user, "$"].join(""), "i");
|
||||||
|
var offers = await Market.find({seller: regex});
|
||||||
|
offers = offers || [];
|
||||||
|
return offers;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getByCard = async(card_tid, variant_id) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var offers = await Market.find({card: card_tid, variant: variant_id});
|
||||||
|
offers = offers || [];
|
||||||
|
return offers;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async() => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var offers = await Market.find()
|
||||||
|
offers = offers || [];
|
||||||
|
return offers;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAllLimit = async(perPage, page) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var offers = await Market.find().limit(perPage).skip(perPage * page)
|
||||||
|
offers = offers || [];
|
||||||
|
return offers;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.add = async(user, card, variant, data) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var offer = await Market.findOne({seller: user, card: card, variant: variant});
|
||||||
|
|
||||||
|
if(!offer)
|
||||||
|
{
|
||||||
|
offer = new Market(data);
|
||||||
|
offer.date = Date.now();
|
||||||
|
return await offer.save();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offer.quantity += data.quantity;
|
||||||
|
offer.price = data.price;
|
||||||
|
offer.date = Date.now();
|
||||||
|
|
||||||
|
var updated = await offer.save();
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.reduce = async(user, card, variant, quantity) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var offer = await Market.findOne({seller: user, card: card, variant: variant});
|
||||||
|
if(offer)
|
||||||
|
{
|
||||||
|
offer.quantity -= quantity;
|
||||||
|
if(offer.quantity > 0)
|
||||||
|
{
|
||||||
|
var updated = await offer.save();
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
var result = await Market.deleteOne({seller: user, card: card});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.remove = async(user, card, variant) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var result = await Market.deleteOne({seller: user, card: card, variant: variant});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
51
market/market.routes.js
Normal file
51
market/market.routes.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
const MarketController = require('./market.controller');
|
||||||
|
const AuthTool = require('../authorization/auth.tool');
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
const ADMIN = config.permissions.ADMIN; //Highest permision, can read and write all users
|
||||||
|
const SERVER = config.permissions.SERVER; //Middle permission, can read all users and grant rewards
|
||||||
|
const USER = config.permissions.USER; //Lowest permision, can only do things on same user
|
||||||
|
|
||||||
|
exports.route = function (app) {
|
||||||
|
|
||||||
|
app.post("/market/cards/add", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
MarketController.addOffer,
|
||||||
|
]);
|
||||||
|
app.post("/market/cards/remove", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
MarketController.removeOffer,
|
||||||
|
]);
|
||||||
|
app.post("/market/cards/trade", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
MarketController.trade,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/market/cards/", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
MarketController.getAll,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/market/cards/user/:username", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
MarketController.getBySeller,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/market/cards/card/:tid", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
MarketController.getByCard,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/market/cards/offer/:username/:tid", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
MarketController.getOffer,
|
||||||
|
]);
|
||||||
|
|
||||||
|
};
|
||||||
123
matches/matches.controller.js
Normal file
123
matches/matches.controller.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
const MatchModel = require('./matches.model');
|
||||||
|
const MatchTool = require('./matches.tool');
|
||||||
|
const UserModel = require('../users/users.model');
|
||||||
|
const DateTool = require('../tools/date.tool');
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
exports.addMatch = async(req, res) => {
|
||||||
|
|
||||||
|
var tid = req.body.tid;
|
||||||
|
var players = req.body.players;
|
||||||
|
var ranked = req.body.ranked ? true : false;
|
||||||
|
var mode = req.body.mode || "";
|
||||||
|
|
||||||
|
if (!tid || !players || !Array.isArray(players) || players.length != 2)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if (mode && typeof mode !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
var fmatch = await MatchModel.get(tid);
|
||||||
|
if(fmatch)
|
||||||
|
return res.status(400).send({error:"Match already exists: " + tid});
|
||||||
|
|
||||||
|
var player0 = await UserModel.getByUsername(players[0]);
|
||||||
|
var player1 = await UserModel.getByUsername(players[1]);
|
||||||
|
if(!player0 || !player1)
|
||||||
|
return res.status(404).send({error:"Can't find players"});
|
||||||
|
|
||||||
|
if(player0.id == player1.id)
|
||||||
|
return res.status(400).send({error:"Can't play against yourself"});
|
||||||
|
|
||||||
|
var match = {};
|
||||||
|
match.tid = tid;
|
||||||
|
match.players = players;
|
||||||
|
match.winner = "";
|
||||||
|
match.completed = false;
|
||||||
|
match.ranked = ranked;
|
||||||
|
match.mode = mode;
|
||||||
|
match.start = Date.now();
|
||||||
|
match.end = Date.now();
|
||||||
|
match.udata = [];
|
||||||
|
match.udata.push(MatchTool.GetPlayerData(player0));
|
||||||
|
match.udata.push(MatchTool.GetPlayerData(player1));
|
||||||
|
|
||||||
|
var match = await MatchModel.create(match);
|
||||||
|
if(!match)
|
||||||
|
return res.status(500).send({error:"Unable to create match"});
|
||||||
|
|
||||||
|
res.status(200).send(match);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.completeMatch = async(req, res) => {
|
||||||
|
|
||||||
|
var matchId = req.body.tid;
|
||||||
|
var winner = req.body.winner;
|
||||||
|
|
||||||
|
if (!matchId || !winner)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(typeof matchId != "string" || typeof winner != "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters" });
|
||||||
|
|
||||||
|
var match = await MatchModel.get(matchId);
|
||||||
|
if(!match)
|
||||||
|
return res.status(404).send({error: "Match not found"});
|
||||||
|
|
||||||
|
if(match.completed)
|
||||||
|
return res.status(400).send({error: "Match already completed"});
|
||||||
|
|
||||||
|
var player0 = await UserModel.getByUsername(match.players[0]);
|
||||||
|
var player1 = await UserModel.getByUsername(match.players[1]);
|
||||||
|
if(!player0 || !player1)
|
||||||
|
return res.status(404).send({error:"Can't find players"});
|
||||||
|
|
||||||
|
match.end = Date.now();
|
||||||
|
match.winner = winner;
|
||||||
|
match.completed = true;
|
||||||
|
|
||||||
|
//Add Rewards
|
||||||
|
if(match.ranked)
|
||||||
|
{
|
||||||
|
match.udata[0].reward = await MatchTool.GainMatchReward(player0, player1, winner);
|
||||||
|
match.udata[1].reward = await MatchTool.GainMatchReward(player1, player0, winner);
|
||||||
|
match.markModified('udata');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Save match
|
||||||
|
var uMatch = await match.save();
|
||||||
|
|
||||||
|
//Return
|
||||||
|
res.status(200).send(uMatch);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async(req, res) => {
|
||||||
|
|
||||||
|
var start = req.query.start ? DateTool.tagToDate(req.query.start) : null;
|
||||||
|
var end = req.query.end ? DateTool.tagToDate(req.query.end) : null;
|
||||||
|
|
||||||
|
var matches = await MatchModel.list(start, end);
|
||||||
|
if(!matches)
|
||||||
|
return res.status(400).send({error: "Invalid Parameters"});
|
||||||
|
|
||||||
|
return res.status(200).send(matches);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getByTid = async(req, res) => {
|
||||||
|
|
||||||
|
var match = await MatchModel.get(req.params.tid);
|
||||||
|
if(!match)
|
||||||
|
return res.status(404).send({error: "Match not found " + req.params.tid});
|
||||||
|
|
||||||
|
return res.status(200).send(match);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getLatest = async(req, res) => {
|
||||||
|
|
||||||
|
var match = await MatchModel.getLast(req.params.userId);
|
||||||
|
if(!match)
|
||||||
|
return res.status(404).send({error: "Match not found for user " + req.params.userId});
|
||||||
|
|
||||||
|
return res.status(200).send(match);
|
||||||
|
};
|
||||||
87
matches/matches.model.js
Normal file
87
matches/matches.model.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const matchSchema = new Schema({
|
||||||
|
|
||||||
|
tid: { type: String, index: true, default: "" },
|
||||||
|
players: [{type: String, default: []}],
|
||||||
|
winner: {type: String, default: ""},
|
||||||
|
completed: {type: Boolean, default: false},
|
||||||
|
ranked: {type: Boolean, default: false},
|
||||||
|
mode: { type: String, default: "" },
|
||||||
|
|
||||||
|
start: {type: Date, default: null},
|
||||||
|
end: {type: Date, default: null},
|
||||||
|
|
||||||
|
udata: [{ type: Object, _id: false }],
|
||||||
|
});
|
||||||
|
|
||||||
|
matchSchema.methods.toObj = function() {
|
||||||
|
var match = this.toObject();
|
||||||
|
delete match.__v;
|
||||||
|
delete match._id;
|
||||||
|
return match;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Match = mongoose.model('Matches', matchSchema);
|
||||||
|
|
||||||
|
exports.get = async(matchId) => {
|
||||||
|
try{
|
||||||
|
var match = await Match.findOne({tid: matchId});
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async() => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var matches = await Match.find()
|
||||||
|
return matches || [];
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.create = async(matchData) => {
|
||||||
|
const match = new Match(matchData);
|
||||||
|
return await match.save();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.list = async(startTime, endTime, winnerId, completed) => {
|
||||||
|
|
||||||
|
startTime = startTime || new Date(-8640000000000000);
|
||||||
|
endTime = endTime || new Date(8640000000000000);
|
||||||
|
|
||||||
|
var options = {};
|
||||||
|
|
||||||
|
if(startTime && endTime)
|
||||||
|
options.end = { $gte: startTime, $lte: endTime };
|
||||||
|
|
||||||
|
if(winnerId)
|
||||||
|
options.players = winnerId;
|
||||||
|
|
||||||
|
if(completed)
|
||||||
|
options.completed = true;
|
||||||
|
|
||||||
|
try{
|
||||||
|
var matches = await Match.find(options)
|
||||||
|
return matches || [];
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.remove = async(matchId) => {
|
||||||
|
try{
|
||||||
|
var result = await Match.deleteOne({tid: matchId});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
34
matches/matches.routes.js
Normal file
34
matches/matches.routes.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
const MatchesController = require('./matches.controller');
|
||||||
|
const MatchesTool = require('./matches.tool');
|
||||||
|
const AuthTool = require('../authorization/auth.tool');
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
const ADMIN = config.permissions.ADMIN; //Highest permision, can read and write all users
|
||||||
|
const SERVER = config.permissions.SERVER; //Higher permission, can read all users
|
||||||
|
const USER = config.permissions.USER; //Lowest permision, can only do things on same user
|
||||||
|
|
||||||
|
exports.route = function (app) {
|
||||||
|
|
||||||
|
app.post('/matches/add', app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(SERVER),
|
||||||
|
MatchesController.addMatch
|
||||||
|
]);
|
||||||
|
app.post('/matches/complete', app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(SERVER),
|
||||||
|
MatchesController.completeMatch
|
||||||
|
]);
|
||||||
|
|
||||||
|
//-- Getter
|
||||||
|
app.get('/matches', [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(SERVER),
|
||||||
|
MatchesController.getAll
|
||||||
|
]);
|
||||||
|
app.get('/matches/:tid', [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
MatchesController.getByTid
|
||||||
|
]);
|
||||||
|
};
|
||||||
71
matches/matches.tool.js
Normal file
71
matches/matches.tool.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
const UserTool = require('../users/users.tool');
|
||||||
|
const config = require('../config.js');
|
||||||
|
|
||||||
|
|
||||||
|
var MatchTool = {};
|
||||||
|
|
||||||
|
|
||||||
|
MatchTool.calculateELO = (player_elo, opponent_elo, progress, won, lost) =>
|
||||||
|
{
|
||||||
|
var p_elo = player_elo || 1000;
|
||||||
|
var o_elo = opponent_elo || 1000;
|
||||||
|
|
||||||
|
var p_elo_log = Math.pow(10.0, p_elo / 400.0);
|
||||||
|
var o_elo_log = Math.pow(10.0, o_elo / 400.0);
|
||||||
|
var p_expected = p_elo_log / (p_elo_log + o_elo_log);
|
||||||
|
var p_score = won ? 1.0 : (lost ? 0.0 : 0.5);
|
||||||
|
|
||||||
|
progress = Math.min(Math.max(progress, 0.0), 1.0);
|
||||||
|
var elo_k = progress * config.elo_k + (1.0 - progress) * config.elo_ini_k;
|
||||||
|
var new_elo = Math.round(p_elo + elo_k * (p_score - p_expected));
|
||||||
|
return new_elo;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchTool.GetPlayerData = (player) =>
|
||||||
|
{
|
||||||
|
var data = {};
|
||||||
|
data.username = player.username;
|
||||||
|
data.elo = player.elo;
|
||||||
|
data.reward = {};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchTool.GainMatchReward = async(player, opponent, winner_username) => {
|
||||||
|
|
||||||
|
var player_elo = player.elo;
|
||||||
|
var opponent_elo = opponent.elo;
|
||||||
|
var won = winner_username == player.username;
|
||||||
|
var lost = winner_username == opponent.username;
|
||||||
|
|
||||||
|
//Rewards
|
||||||
|
var xp = won ? config.xp_victory : config.xp_defeat;
|
||||||
|
var coins = won ? config.coins_victory : config.coins_defeat;
|
||||||
|
|
||||||
|
player.xp += xp;
|
||||||
|
player.coins += coins;
|
||||||
|
|
||||||
|
//Match winrate
|
||||||
|
player.matches +=1;
|
||||||
|
|
||||||
|
if(won)
|
||||||
|
player.victories += 1;
|
||||||
|
else if (lost)
|
||||||
|
player.defeats += 1;
|
||||||
|
|
||||||
|
//Calculate elo
|
||||||
|
var match_count = player.matches || 0;
|
||||||
|
var match_progress = Math.min(Math.max(match_count / config.elo_ini_match, 0.0), 1.0);
|
||||||
|
var new_elo = MatchTool.calculateELO(player_elo, opponent_elo, match_progress, won, lost);
|
||||||
|
player.elo = new_elo;
|
||||||
|
player.save();
|
||||||
|
|
||||||
|
var reward = {
|
||||||
|
elo: player.elo,
|
||||||
|
xp: xp,
|
||||||
|
coins: coins
|
||||||
|
};
|
||||||
|
|
||||||
|
return reward;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = MatchTool;
|
||||||
16
package.json
Normal file
16
package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "tcg-engine-api",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "server.js",
|
||||||
|
"author": "IndieMarc",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"express-rate-limit": "^6.7.0",
|
||||||
|
"express-slow-down": "^1.5.0",
|
||||||
|
"jsonwebtoken": "^9.0.0",
|
||||||
|
"mongoose": "^6.8.4",
|
||||||
|
"node-schedule": "^2.1.1",
|
||||||
|
"nodemailer": "^6.9.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
80
packs/packs.controller.js
Normal file
80
packs/packs.controller.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
const PackModel = require("./packs.model");
|
||||||
|
|
||||||
|
exports.AddPack = async(req, res) =>
|
||||||
|
{
|
||||||
|
var tid = req.body.tid;
|
||||||
|
var cards = req.body.cards || 1;
|
||||||
|
var cost = req.body.cost || 1;
|
||||||
|
var random = req.body.random || false;
|
||||||
|
var rarities_1st = req.body.rarities_1st || [];
|
||||||
|
var rarities = req.body.rarities || [];
|
||||||
|
var variants = req.body.variants || [];
|
||||||
|
|
||||||
|
if(!tid || typeof tid !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!Number.isInteger(cards) || !Number.isInteger(cost))
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!Array.isArray(rarities_1st) || !Array.isArray(rarities) || !Array.isArray(variants))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
tid: tid,
|
||||||
|
cards: cards,
|
||||||
|
cost: cost,
|
||||||
|
random: random,
|
||||||
|
rarities_1st: rarities_1st,
|
||||||
|
rarities: rarities,
|
||||||
|
variants: variants,
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update or create
|
||||||
|
var pack = await PackModel.get(tid);
|
||||||
|
if(pack)
|
||||||
|
pack = await PackModel.update(pack, data);
|
||||||
|
else
|
||||||
|
pack = await PackModel.create(data);
|
||||||
|
|
||||||
|
if(!pack)
|
||||||
|
return res.status(500).send({error: "Error updating pack"});
|
||||||
|
|
||||||
|
return res.status(200).send(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeletePack = async(req, res) =>
|
||||||
|
{
|
||||||
|
PackModel.remove(req.params.tid);
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteAll = async(req, res) =>
|
||||||
|
{
|
||||||
|
PackModel.removeAll();
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetPack = async(req, res) =>
|
||||||
|
{
|
||||||
|
var tid = req.params.tid;
|
||||||
|
|
||||||
|
if(!tid)
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var pack = await PackModel.get(tid);
|
||||||
|
if(!pack)
|
||||||
|
return res.status(404).send({error: "Pack not found: " + tid});
|
||||||
|
|
||||||
|
return res.status(200).send(pack.toObj());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetAll = async(req, res) =>
|
||||||
|
{
|
||||||
|
var packs = await PackModel.getAll();
|
||||||
|
|
||||||
|
for(var i=0; i<packs.length; i++){
|
||||||
|
packs[i] = packs[i].toObj();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send(packs);
|
||||||
|
};
|
||||||
96
packs/packs.model.js
Normal file
96
packs/packs.model.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
const mongoose = require("mongoose");
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const packsSchema = new Schema({
|
||||||
|
|
||||||
|
tid: { type: String, index: true, unique: true },
|
||||||
|
cards: {type: Number, default: 1}, //Number of cards per pack
|
||||||
|
cost: {type: Number, default: 0}, //Cost in coins
|
||||||
|
random: {type: Boolean, default: true},
|
||||||
|
rarities: [{type: Object}], //Probabilities to get each rarities
|
||||||
|
rarities_1st: [{type: Object}], //Probabilities but for the first card only
|
||||||
|
variants: [{type: Object}], //Probabilities of variants
|
||||||
|
});
|
||||||
|
|
||||||
|
packsSchema.methods.toObj = function() {
|
||||||
|
var elem = this.toObject();
|
||||||
|
delete elem.__v;
|
||||||
|
delete elem._id;
|
||||||
|
return elem;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Pack = mongoose.model("Packs", packsSchema);
|
||||||
|
exports.Pack = Pack;
|
||||||
|
|
||||||
|
exports.create = async(data) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var pack = new Pack(data);
|
||||||
|
return await pack.save();
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.get = async(set_tid) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var pack = await Pack.findOne({tid: set_tid});
|
||||||
|
return pack;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async() => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var packs = await Pack.find({});
|
||||||
|
return packs;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.update = async(pack, data) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
if(!pack) return null;
|
||||||
|
|
||||||
|
for (let i in data) {
|
||||||
|
pack[i] = data[i];
|
||||||
|
pack.markModified(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var updated = await pack.save();
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.remove = async(pack_tid) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var result = await Pack.deleteOne({tid: pack_tid});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.removeAll = async() => {
|
||||||
|
try{
|
||||||
|
var result = await Pack.deleteMany({});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
36
packs/packs.routes.js
Normal file
36
packs/packs.routes.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const config = require("../config");
|
||||||
|
const PacksController = require("./packs.controller");
|
||||||
|
const AuthTool = require("../authorization/auth.tool");
|
||||||
|
|
||||||
|
const ADMIN = config.permissions.ADMIN; //Highest permision, can read and write all users
|
||||||
|
const SERVER = config.permissions.SERVER; //Higher permission, can read all users
|
||||||
|
const USER = config.permissions.USER; //Lowest permision, can only do things on same user
|
||||||
|
|
||||||
|
exports.route = (app) => {
|
||||||
|
|
||||||
|
app.get("/packs", [
|
||||||
|
PacksController.GetAll
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/packs/:tid", [
|
||||||
|
PacksController.GetPack
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/packs/add", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
PacksController.AddPack
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/packs/:tid", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
PacksController.DeletePack
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/packs", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
PacksController.DeleteAll
|
||||||
|
]);
|
||||||
|
};
|
||||||
99
rewards/rewards.controller.js
Normal file
99
rewards/rewards.controller.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
const RewardModel = require('../rewards/rewards.model');
|
||||||
|
const Activity = require("../activity/activity.model");
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
exports.AddReward = async(req, res) =>
|
||||||
|
{
|
||||||
|
var rewardId = req.body.tid;
|
||||||
|
var group = req.body.group;
|
||||||
|
var repeat = req.body.repeat;
|
||||||
|
var xp = req.body.xp;
|
||||||
|
var coins = req.body.coins;
|
||||||
|
var cards = req.body.cards;
|
||||||
|
var packs = req.body.packs;
|
||||||
|
var decks = req.body.decks;
|
||||||
|
var avatars = req.body.avatars;
|
||||||
|
var cardbacks = req.body.cardbacks;
|
||||||
|
|
||||||
|
if(!rewardId || typeof rewardId !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(group && typeof group !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(xp && !Number.isInteger(xp))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
if(coins && !Number.isInteger(coins))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
if(cards && !Array.isArray(cards))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
if(packs && !Array.isArray(packs))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
if(decks && !Array.isArray(decks))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
if(avatars && !Array.isArray(avatars))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
if(cardbacks && !Array.isArray(cardbacks))
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var reward_data = {
|
||||||
|
tid: rewardId,
|
||||||
|
group: group || "",
|
||||||
|
repeat: repeat || false,
|
||||||
|
xp: xp || 0,
|
||||||
|
coins: coins || 0,
|
||||||
|
cards: cards || [],
|
||||||
|
packs: packs || [],
|
||||||
|
decks: decks || [],
|
||||||
|
avatars: avatars || [],
|
||||||
|
cardbacks: cardbacks || [],
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update or create
|
||||||
|
var reward = await RewardModel.get(rewardId);
|
||||||
|
if(reward)
|
||||||
|
reward = await RewardModel.update(reward, reward_data);
|
||||||
|
else
|
||||||
|
reward = await RewardModel.create(reward_data);
|
||||||
|
|
||||||
|
if(!reward)
|
||||||
|
res.status(500).send({error: "Error updating reward"});
|
||||||
|
|
||||||
|
//Activity
|
||||||
|
const act = await Activity.LogActivity("reward_add", req.jwt.username, reward);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!" });
|
||||||
|
|
||||||
|
return res.status(200).send(reward);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteReward = async(req, res) => {
|
||||||
|
RewardModel.remove(req.params.tid);
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteAll = async(req, res) => {
|
||||||
|
RewardModel.removeAll();
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetReward = async(req, res) =>
|
||||||
|
{
|
||||||
|
var rewardTid = req.params.tid;
|
||||||
|
|
||||||
|
if(!rewardTid)
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var reward = await RewardModel.get(rewardTid);
|
||||||
|
if(!reward)
|
||||||
|
return res.status(404).send({error: "Reward not found: " + rewardTid});
|
||||||
|
|
||||||
|
return res.status(200).send(reward.toObj());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetAll = async(req, res) =>
|
||||||
|
{
|
||||||
|
var rewards = await RewardModel.getAll();
|
||||||
|
return res.status(200).send(rewards);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
107
rewards/rewards.model.js
Normal file
107
rewards/rewards.model.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const rewardSchema = new Schema({
|
||||||
|
|
||||||
|
tid: { type: String, index: true, unique: true, default: "" },
|
||||||
|
group: { type: String, index: true, default: "" },
|
||||||
|
repeat: { type : Boolean, default: false }, //If true, can be gained multiple times but only server/admin can grant it
|
||||||
|
|
||||||
|
xp: { type: Number, default: 0 },
|
||||||
|
coins: { type: Number, default: 0 },
|
||||||
|
cards: [{type: String}],
|
||||||
|
packs: [{type: String}],
|
||||||
|
decks: [{type: String}],
|
||||||
|
avatars: [{type: String}],
|
||||||
|
cardbacks: [{type: String}],
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
rewardSchema.methods.toObj = function() {
|
||||||
|
var reward = this.toObject();
|
||||||
|
delete reward.__v;
|
||||||
|
delete reward._id;
|
||||||
|
return reward;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Reward = mongoose.model('Rewards', rewardSchema);
|
||||||
|
|
||||||
|
exports.get = async(rewardId) => {
|
||||||
|
try{
|
||||||
|
var reward = await Reward.findOne({tid: rewardId});
|
||||||
|
return reward;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getGroup = async(group) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var rewards = await Reward.find({group: group})
|
||||||
|
return rewards || [];
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async() => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var rewards = await Reward.find()
|
||||||
|
return rewards || [];
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.create = async(data) => {
|
||||||
|
try{
|
||||||
|
var reward = new Reward(data);
|
||||||
|
return await reward.save();
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.update = async(reward, data) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
if(!reward) return null;
|
||||||
|
|
||||||
|
for (let i in data) {
|
||||||
|
reward[i] = data[i];
|
||||||
|
reward.markModified(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var updated = await reward.save();
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.remove = async(rewardId) => {
|
||||||
|
try{
|
||||||
|
var result = await Reward.deleteOne({tid: rewardId});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.removeAll = async() => {
|
||||||
|
try{
|
||||||
|
var result = await Reward.deleteMany({});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
40
rewards/rewards.routes.js
Normal file
40
rewards/rewards.routes.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const RewardController = require('./rewards.controller');
|
||||||
|
const AuthTool = require('../authorization/auth.tool');
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
const ADMIN = config.permissions.ADMIN; //Highest permision, can read and write all users
|
||||||
|
const SERVER = config.permissions.SERVER; //Higher permission, can read all users
|
||||||
|
const USER = config.permissions.USER; //Lowest permision, can only do things on same user
|
||||||
|
|
||||||
|
exports.route = function (app) {
|
||||||
|
|
||||||
|
app.get('/rewards/:tid', [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
RewardController.GetReward
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get('/rewards', [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(SERVER),
|
||||||
|
RewardController.GetAll
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post('/rewards/add', [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
RewardController.AddReward
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/rewards/:tid", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
RewardController.DeleteReward
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/rewards", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
RewardController.DeleteAll
|
||||||
|
]);
|
||||||
|
};
|
||||||
134
server.js
Normal file
134
server.js
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const http = require('http');
|
||||||
|
const https = require('https');
|
||||||
|
const express = require('express');
|
||||||
|
const config = require('./config.js');
|
||||||
|
const Limiter = require('./tools/limiter.tool');
|
||||||
|
const mongoose = require("mongoose");
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// CONNECTION TO DATABASE
|
||||||
|
var user = ""; //User is optional if auth not enabled
|
||||||
|
if(config.mongo_user && config.mongo_pass)
|
||||||
|
user = config.mongo_user + ":" + config.mongo_pass + "@";
|
||||||
|
|
||||||
|
var connect = "mongodb://" + user + config.mongo_host + ":" + config.mongo_port + "/" + config.mongo_db + "?authSource=admin";
|
||||||
|
mongoose.set('strictQuery', false);
|
||||||
|
mongoose.connection.on("connected", () => {
|
||||||
|
console.log("Connected to MongoDB!");
|
||||||
|
});
|
||||||
|
mongoose.connection.on('error', function(err) {
|
||||||
|
console.error('Connection to MongoDB failed!');
|
||||||
|
});
|
||||||
|
mongoose.connect(connect);
|
||||||
|
|
||||||
|
//Limiter to prevent attacks
|
||||||
|
Limiter.limit(app);
|
||||||
|
|
||||||
|
//Headers
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
|
res.header('Access-Control-Allow-Credentials', 'true');
|
||||||
|
res.header('Access-Control-Allow-Methods', 'GET,HEAD,PUT,PATCH,POST,DELETE');
|
||||||
|
res.header('Access-Control-Expose-Headers', 'Content-Length');
|
||||||
|
res.header('Access-Control-Allow-Headers', 'Accept, Authorization, Content-Type, X-Requested-With, Range');
|
||||||
|
if (req.method === 'OPTIONS') {
|
||||||
|
return res.send(200);
|
||||||
|
} else {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Parse JSON body
|
||||||
|
app.use(express.json({ limit: "100kb" }));
|
||||||
|
|
||||||
|
//Log request
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
var today = new Date();
|
||||||
|
var date = today.getFullYear() +'-'+(today.getMonth()+1)+'-'+today.getDate();
|
||||||
|
var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
|
||||||
|
var date_tag = "[" + date + " " + time + "]";
|
||||||
|
console.log(date_tag + " " + req.method + " " + req.originalUrl);
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
|
||||||
|
//Route root DIR
|
||||||
|
app.get('/', function(req, res){
|
||||||
|
res.status(200).send(config.api_title + " " + config.version);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Public folder
|
||||||
|
app.use('/', express.static('public'))
|
||||||
|
|
||||||
|
//Routing
|
||||||
|
const AuthorizationRouter = require('./authorization/auth.routes');
|
||||||
|
AuthorizationRouter.route(app);
|
||||||
|
|
||||||
|
const UsersRouter = require('./users/users.routes');
|
||||||
|
UsersRouter.route(app);
|
||||||
|
|
||||||
|
const CardsRouter = require('./cards/cards.routes');
|
||||||
|
CardsRouter.route(app);
|
||||||
|
|
||||||
|
const PacksRouter = require('./packs/packs.routes');
|
||||||
|
PacksRouter.route(app);
|
||||||
|
|
||||||
|
const DecksRouter = require('./decks/decks.routes');
|
||||||
|
DecksRouter.route(app);
|
||||||
|
|
||||||
|
const VariantRouter = require('./variants/variants.routes');
|
||||||
|
VariantRouter.route(app);
|
||||||
|
|
||||||
|
const MatchesRouter = require('./matches/matches.routes');
|
||||||
|
MatchesRouter.route(app);
|
||||||
|
|
||||||
|
const RewardsRouter = require('./rewards/rewards.routes');
|
||||||
|
RewardsRouter.route(app);
|
||||||
|
|
||||||
|
const MarketRouter = require('./market/market.routes');
|
||||||
|
MarketRouter.route(app);
|
||||||
|
|
||||||
|
const ActivityRouter = require("./activity/activity.routes");
|
||||||
|
ActivityRouter.route(app);
|
||||||
|
|
||||||
|
//Read SSL cert
|
||||||
|
var ReadSSL = function()
|
||||||
|
{
|
||||||
|
var privateKey = fs.readFileSync(config.https_key, 'utf8');
|
||||||
|
var certificate = fs.readFileSync(config.https_cert, 'utf8');
|
||||||
|
var cert_authority = fs.readFileSync(config.https_ca, 'utf8');
|
||||||
|
var credentials = {key: privateKey, cert: certificate, ca: cert_authority};
|
||||||
|
return credentials;
|
||||||
|
};
|
||||||
|
|
||||||
|
//HTTP
|
||||||
|
if(config.allow_http){
|
||||||
|
var httpServer = http.createServer(app);
|
||||||
|
httpServer.listen(config.port, function () {
|
||||||
|
console.log('http listening port %s', config.port);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//HTTPS
|
||||||
|
if(config.allow_https && fs.existsSync(config.https_key)) {
|
||||||
|
var httpsServer = https.createServer(ReadSSL(), app);
|
||||||
|
httpsServer.listen(config.port_https, function () {
|
||||||
|
console.log('https listening port %s', config.port_https);
|
||||||
|
});
|
||||||
|
|
||||||
|
//HTTPS auto-reload ssl
|
||||||
|
var timeout;
|
||||||
|
fs.watch(config.https_cert, () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
httpsServer.setSecureContext(ReadSSL());
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Start jobs
|
||||||
|
const Jobs = require("./jobs/jobs");
|
||||||
|
Jobs.InitJobs();
|
||||||
|
|
||||||
|
module.exports = app
|
||||||
65
tools/date.tool.js
Normal file
65
tools/date.tool.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
var DateTool = {};
|
||||||
|
|
||||||
|
// -------- Date & timestamp -------
|
||||||
|
DateTool.isDate = function(date)
|
||||||
|
{
|
||||||
|
if (Object.prototype.toString.call(date) === "[object Date]") {
|
||||||
|
return !isNaN(date.getTime());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
DateTool.tagToDate = function(tag)
|
||||||
|
{
|
||||||
|
if(typeof tag !== "string")
|
||||||
|
return null;
|
||||||
|
|
||||||
|
[year, month, day] = tag.split("-");
|
||||||
|
var d = new Date(year, month - 1, day, 0, 0, 0, 0);
|
||||||
|
return DateTool.isDate(d) ? d : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
DateTool.dateToTag = function(d)
|
||||||
|
{
|
||||||
|
if(!DateTool.isDate(d))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var year = '' + d.getFullYear();
|
||||||
|
var month = '' + (d.getMonth() + 1);
|
||||||
|
var day = '' + d.getDate();
|
||||||
|
if (day.length < 2) day = '0' + day;
|
||||||
|
return [year, month, day].join('-');
|
||||||
|
};
|
||||||
|
|
||||||
|
DateTool.getStartOfDay = function(date){
|
||||||
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTool.addDays = function(date, days) {
|
||||||
|
return new Date(date.getTime() + days*24*60*60*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTool.addHours = function(date, hours) {
|
||||||
|
return new Date(date.getTime() + hours*60*60*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTool.addMinutes = function(date, minutes) {
|
||||||
|
return new Date(date.getTime() + minutes*60000);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTool.minDate = function()
|
||||||
|
{
|
||||||
|
return new Date(-8640000000000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTool.maxDate = function()
|
||||||
|
{
|
||||||
|
return new Date(8640000000000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTool.countDays = function(from, to) {
|
||||||
|
const ms_per_day = 1000 * 60 * 60 * 24;
|
||||||
|
return Math.round((to - from) / ms_per_day);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DateTool;
|
||||||
121
tools/email.tool.js
Normal file
121
tools/email.tool.js
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
|
||||||
|
const nodeMailer = require('nodemailer');
|
||||||
|
const config = require('../config');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
//Send One Mail
|
||||||
|
exports.SendEmail = function(email_to, subject, text, callback){
|
||||||
|
|
||||||
|
if(!config.smtp_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
console.log("Sending email to: " + email_to);
|
||||||
|
|
||||||
|
let transporter = nodeMailer.createTransport({
|
||||||
|
host: config.smtp_server,
|
||||||
|
port: config.smtp_port, //Port must be 465 (encrypted) or 587 (STARTTSL, first pre-request is unencrypted to know the encryption method supported, followed by encrypted request)
|
||||||
|
secure: (config.smtp_port == "465"), //On port 587 secure must be false since it will first send unsecured pre-request to know which encryption to use
|
||||||
|
requireTLS: true, //Force using encryption on port 587 on the second request
|
||||||
|
auth: {
|
||||||
|
user: config.smtp_user,
|
||||||
|
pass: config.smtp_password,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mailOptions = {
|
||||||
|
from: '"' + config.smtp_name + '" <' + config.smtp_email + '>', // sender address
|
||||||
|
to: email_to, // list of receivers
|
||||||
|
subject: subject, // Subject line
|
||||||
|
//text: text, // plain text body
|
||||||
|
html: text, // html body
|
||||||
|
};
|
||||||
|
|
||||||
|
transporter.sendMail(mailOptions, (error, info) => {
|
||||||
|
if (error) {
|
||||||
|
if(callback)
|
||||||
|
callback(false, error);
|
||||||
|
console.log(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(callback)
|
||||||
|
callback(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//Send same mail to multiple recipients (emails array)
|
||||||
|
exports.SendEmailList = function(emails, subject, text, callback){
|
||||||
|
|
||||||
|
if(!config.smtp_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!Array.isArray(emails))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(emails.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let transporter = nodeMailer.createTransport({
|
||||||
|
pool: true,
|
||||||
|
host: config.smtp_server,
|
||||||
|
port: config.smtp_port,
|
||||||
|
secure: (config.smtp_port == "465"),
|
||||||
|
requireTLS: true,
|
||||||
|
auth: {
|
||||||
|
user: config.smtp_user,
|
||||||
|
pass: config.smtp_password,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var email_list = emails;
|
||||||
|
var email_from = '"' + config.smtp_name + '" <' + config.smtp_email + '>';
|
||||||
|
var total = emails.length;
|
||||||
|
var sent_success = 0;
|
||||||
|
var sent_count = 0;
|
||||||
|
var ended = false;
|
||||||
|
|
||||||
|
transporter.on("idle", function () {
|
||||||
|
|
||||||
|
while (transporter.isIdle() && email_list.length > 0)
|
||||||
|
{
|
||||||
|
var email_to = email_list.shift();
|
||||||
|
let mailOptions = {
|
||||||
|
from: email_from,
|
||||||
|
to: email_to,
|
||||||
|
subject: subject,
|
||||||
|
html: text,
|
||||||
|
};
|
||||||
|
|
||||||
|
transporter.sendMail(mailOptions, (error, info) => {
|
||||||
|
sent_count++;
|
||||||
|
if (!error) {
|
||||||
|
sent_success++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(email_list.length == 0 && sent_count == total && !ended)
|
||||||
|
{
|
||||||
|
ended = true;
|
||||||
|
if(callback)
|
||||||
|
callback(sent_success);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.ReadTemplate = function(template)
|
||||||
|
{
|
||||||
|
const rootDir = path.dirname(require.main.filename);
|
||||||
|
const fullpath = rootDir + "/emails/" + template;
|
||||||
|
|
||||||
|
try{
|
||||||
|
const html = fs.readFileSync(fullpath, "utf8");
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
tools/file.tool.js
Normal file
16
tools/file.tool.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
exports.readFileArraySync = function(filename){
|
||||||
|
|
||||||
|
var data = fs.readFileSync(filename, {encoding: "utf8"});
|
||||||
|
var adata = data.split('\r\n');
|
||||||
|
return adata;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.readFileArray = function(filename, callback){
|
||||||
|
|
||||||
|
fs.readFile(filename, {encoding: "utf8"}, function(data){
|
||||||
|
var adata = data.split('\r\n');
|
||||||
|
callback(adata);
|
||||||
|
});
|
||||||
|
};
|
||||||
46
tools/limiter.tool.js
Normal file
46
tools/limiter.tool.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
const RateLimit = require('express-rate-limit');
|
||||||
|
//const Slowdown = require('express-slow-down');
|
||||||
|
const config = require('../config.js');
|
||||||
|
|
||||||
|
exports.limit = function(app)
|
||||||
|
{
|
||||||
|
//Restrict to access from domain only
|
||||||
|
app.use(function(req, res, next)
|
||||||
|
{
|
||||||
|
//Ip address
|
||||||
|
req.ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|
||||||
|
|
||||||
|
if(config.ip_blacklist.includes(req.ip))
|
||||||
|
return res.status(401).send("Forbidden");
|
||||||
|
|
||||||
|
//Check server host
|
||||||
|
var host = req.hostname;
|
||||||
|
if(config.api_url && host != config.api_url)
|
||||||
|
return res.status(401).send("Forbidden");
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Rate limiter
|
||||||
|
if(config.limiter_proxy)
|
||||||
|
app.enable('trust proxy'); // only if your server is behind a reverse proxy
|
||||||
|
|
||||||
|
app.use(RateLimit({
|
||||||
|
windowMs: config.limiter_window,
|
||||||
|
max: config.limiter_max,
|
||||||
|
skip: function(req) { return config.ip_whitelist.includes(req.ip); },
|
||||||
|
}));
|
||||||
|
app.auth_limiter = RateLimit({
|
||||||
|
windowMs: config.limiter_window,
|
||||||
|
max: config.limiter_auth_max,
|
||||||
|
skip: function(req) { return config.ip_whitelist.includes(req.ip); },
|
||||||
|
handler: function (req, res) {
|
||||||
|
res.status(429).send({error: "Too many requests!"});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
app.post_limiter = RateLimit({
|
||||||
|
windowMs: config.limiter_window,
|
||||||
|
max: config.limiter_post_max,
|
||||||
|
skip: function(req) { return config.ip_whitelist.includes(req.ip); },
|
||||||
|
});
|
||||||
|
}
|
||||||
92
tools/validator.tool.js
Normal file
92
tools/validator.tool.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
const FileTool = require('../tools/file.tool');
|
||||||
|
|
||||||
|
var Validator = {};
|
||||||
|
|
||||||
|
Validator.isInteger = function(value){
|
||||||
|
return Number.isInteger(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Validator.isNumber = function(value){
|
||||||
|
return !isNaN(parseFloat(value)) && isFinite(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
Validator.validateUsername = function(username){
|
||||||
|
if(typeof username != "string")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(username.length < 3 || username.length > 50)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//Cant have some special characters, must be letters or digits and start with a letter
|
||||||
|
var regex = /^[a-zA-Z][a-zA-Z\d]+$/;
|
||||||
|
if(!regex.test(username))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Validator.validatePhone = function(phone){
|
||||||
|
if(typeof phone != "string")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(phone.length < 7)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!/^[0-9]+$/.test(phone))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Validator.validateEmail = function(email){
|
||||||
|
|
||||||
|
if(typeof email != "string")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(email.length < 7 || email.length > 320)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var regex_email = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
if(!regex_email.test(email))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Validator.validatePassword = function(pass){
|
||||||
|
|
||||||
|
if(typeof pass != "string")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(pass.length < 4 || pass.length > 50)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//Password validations could be improved here
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Validator.countQuantity = function(array){
|
||||||
|
|
||||||
|
if (!array || !Array.isArray(array))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var total = 0;
|
||||||
|
for (const elem of array) {
|
||||||
|
var q = elem.quantity || 1;
|
||||||
|
total += q;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Returns true or false checking if array has the expected quantity
|
||||||
|
Validator.validateArray = function(array, quantity){
|
||||||
|
var nb = Validator.countQuantity(array);
|
||||||
|
return quantity == nb;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Validator;
|
||||||
100
tools/web.tool.js
Normal file
100
tools/web.tool.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
var http = require('http');
|
||||||
|
var url = require('url');
|
||||||
|
|
||||||
|
var WebTool = {};
|
||||||
|
|
||||||
|
// -------- Http -----------------
|
||||||
|
WebTool.get = function(path, callback) {
|
||||||
|
|
||||||
|
var hostname = url.parse(path).hostname;
|
||||||
|
var pathname = url.parse(path).pathname;
|
||||||
|
|
||||||
|
var post_options = {
|
||||||
|
host: hostname,
|
||||||
|
port: '80',
|
||||||
|
path: pathname,
|
||||||
|
method: 'GET'
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = http.request(post_options, function(res) {
|
||||||
|
res.setEncoding('utf8');
|
||||||
|
var oData = "";
|
||||||
|
res.on('data', function (chunk) {
|
||||||
|
oData += chunk;
|
||||||
|
});
|
||||||
|
res.on('end', function(){
|
||||||
|
callback(oData, res.statusCode);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
request.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
WebTool.post = function(path, data, callback) {
|
||||||
|
|
||||||
|
var post_data = JSON.stringify(data);
|
||||||
|
var hostname = url.parse(path).hostname;
|
||||||
|
var pathname = url.parse(path).pathname;
|
||||||
|
|
||||||
|
var post_options = {
|
||||||
|
host: hostname,
|
||||||
|
port: '80',
|
||||||
|
path: pathname,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
'Content-Length': post_data.length
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = http.request(post_options, function(res) {
|
||||||
|
res.setEncoding('utf8');
|
||||||
|
var oData = "";
|
||||||
|
res.on('data', function (chunk) {
|
||||||
|
oData += chunk;
|
||||||
|
});
|
||||||
|
res.on('end', function(){
|
||||||
|
callback(oData, res.statusCode);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
request.write(post_data);
|
||||||
|
request.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
WebTool.toObject = function(json)
|
||||||
|
{
|
||||||
|
try{
|
||||||
|
var data = JSON.parse(json);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebTool.toJson = function(data)
|
||||||
|
{
|
||||||
|
try{
|
||||||
|
var data = JSON.stringify(json);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebTool.GenerateUID = function(length, numberOnly)
|
||||||
|
{
|
||||||
|
var result = '';
|
||||||
|
var characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
if(numberOnly)
|
||||||
|
characters = '0123456789';
|
||||||
|
var charactersLength = characters.length;
|
||||||
|
for ( var i = 0; i < length; i++ ) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = WebTool;
|
||||||
529
users/users.cards.controller.js
Normal file
529
users/users.cards.controller.js
Normal file
@@ -0,0 +1,529 @@
|
|||||||
|
const UserModel = require("./users.model");
|
||||||
|
const PackModel = require("../packs/packs.model");
|
||||||
|
const CardModel = require("../cards/cards.model");
|
||||||
|
const VariantModel = require('../variants/variants.model');
|
||||||
|
const UserTool = require("./users.tool");
|
||||||
|
const CardTool = require("../cards/cards.tool");
|
||||||
|
const Activity = require("../activity/activity.model");
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
exports.UpdateDeck = async(req, res) => {
|
||||||
|
|
||||||
|
if(!req.params.deckId)
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var userId = req.jwt.userId;
|
||||||
|
var deckId = req.params.deckId;
|
||||||
|
|
||||||
|
var ndeck = {
|
||||||
|
tid: req.params.deckId,
|
||||||
|
title: req.body.title || "Deck",
|
||||||
|
hero: req.body.hero || {},
|
||||||
|
cards: req.body.cards || [],
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found: " + userId});
|
||||||
|
|
||||||
|
var decks = user.decks || [];
|
||||||
|
var found = false;
|
||||||
|
var index = 0;
|
||||||
|
for(var i=0; i<decks.length; i++){
|
||||||
|
var deck = decks[i];
|
||||||
|
if(deck.tid == deckId)
|
||||||
|
{
|
||||||
|
decks[i]= ndeck;
|
||||||
|
found = true;
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add new
|
||||||
|
if(!found && ndeck.cards.length > 0)
|
||||||
|
decks.push(ndeck);
|
||||||
|
|
||||||
|
//Delete deck
|
||||||
|
if(found && ndeck.cards.length == 0)
|
||||||
|
decks.splice(index, 1);
|
||||||
|
|
||||||
|
var userData = { decks: decks};
|
||||||
|
var upUser = await UserModel.update(user, userData);
|
||||||
|
if (!upUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
return res.status(200).send(upUser.decks);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteDeck = async(req, res) => {
|
||||||
|
|
||||||
|
if(!req.params.deckId)
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var userId = req.jwt.userId;
|
||||||
|
var deckId = req.params.deckId;
|
||||||
|
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found: " + userId});
|
||||||
|
|
||||||
|
var decks = user.decks || {};
|
||||||
|
var index = -1;
|
||||||
|
for(var i=0; i<decks.length; i++){
|
||||||
|
var deck = decks[i];
|
||||||
|
if(deck.tid == deckId)
|
||||||
|
{
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index >= 0)
|
||||||
|
decks.splice(index, 1);
|
||||||
|
|
||||||
|
var userData = { decks: decks};
|
||||||
|
var upUser = await UserModel.update(user, userData);
|
||||||
|
if (!upUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
return res.status(200).send(upUser.decks);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.BuyCard = async (req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const cardId = req.body.card;
|
||||||
|
const variantId = req.body.variant;
|
||||||
|
const quantity = req.body.quantity || 1;
|
||||||
|
|
||||||
|
if (!cardId || typeof cardId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!variantId || typeof variantId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!Number.isInteger(quantity) || quantity <= 0)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get the user add update the array
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Cant find user " + userId });
|
||||||
|
|
||||||
|
var card = await CardModel.get(cardId);
|
||||||
|
if (!card)
|
||||||
|
return res.status(404).send({ error: "Cant find card " + cardId });
|
||||||
|
|
||||||
|
if(card.cost <= 0)
|
||||||
|
return res.status(400).send({ error: "Can't be purchased" });
|
||||||
|
|
||||||
|
var variant = await VariantModel.get(variantId);
|
||||||
|
var factor = variant != null ? variant.cost_factor : 1;
|
||||||
|
var cost = quantity * factor * card.cost;
|
||||||
|
if(user.coins < cost)
|
||||||
|
return res.status(400).send({ error: "Not enough coins" });
|
||||||
|
|
||||||
|
user.coins -= cost;
|
||||||
|
|
||||||
|
var valid = await UserTool.addCards(user, [{tid: cardId, variant: variantId, quantity: quantity}]);
|
||||||
|
if (!valid)
|
||||||
|
return res.status(500).send({ error: "Error when adding cards" });
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["coins", "cards"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
const activityData = {card: cardId, variant: variantId, quantity: quantity};
|
||||||
|
const act = await Activity.LogActivity("user_buy_card", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
return res.status(200).send();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.SellCard = async (req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const cardId = req.body.card;
|
||||||
|
const variantId = req.body.variant;
|
||||||
|
const quantity = req.body.quantity || 1;
|
||||||
|
|
||||||
|
if (!cardId || typeof cardId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!variantId || typeof variantId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!Number.isInteger(quantity) || quantity <= 0)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get the user add update the array
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Cant find user " + userId });
|
||||||
|
|
||||||
|
var card = await CardModel.get(cardId);
|
||||||
|
if (!card)
|
||||||
|
return res.status(404).send({ error: "Cant find card " + cardId });
|
||||||
|
|
||||||
|
if(card.cost <= 0)
|
||||||
|
return res.status(400).send({ error: "Can't be sold" });
|
||||||
|
|
||||||
|
var variant = await VariantModel.get(variantId);
|
||||||
|
|
||||||
|
if(!UserTool.hasCard(user, cardId, variantId, quantity))
|
||||||
|
return res.status(400).send({ error: "Not enough cards" });
|
||||||
|
|
||||||
|
var factor = variant != null ? variant.cost_factor : 1;
|
||||||
|
var cost = quantity * Math.round(card.cost * factor * config.sell_ratio);
|
||||||
|
user.coins += cost;
|
||||||
|
|
||||||
|
var valid = await UserTool.addCards(user, [{tid: cardId, variant: variantId, quantity: -quantity}]);
|
||||||
|
if (!valid)
|
||||||
|
return res.status(500).send({ error: "Error when removing cards" });
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["coins", "cards"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
const activityData = {card: cardId, variant: variantId, quantity: quantity};
|
||||||
|
const act = await Activity.LogActivity("user_sell_card", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
return res.status(200).send();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.SellDuplicateCards = async (req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const rarityId = req.body.rarity || ""; //If not set, will sell cards of all rarities
|
||||||
|
const variantId = req.body.variant || ""; //If not set, will sell cards of all variants
|
||||||
|
const keep = req.body.keep; //Number of copies to keep
|
||||||
|
|
||||||
|
if(typeof rarityId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(typeof variantId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!Number.isInteger(keep) || keep < 0)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get the user add update the array
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Cant find user " + userId });
|
||||||
|
|
||||||
|
var all_variants = await VariantModel.getAll();
|
||||||
|
if (!all_variants)
|
||||||
|
return res.status(404).send({ error: "Cant find variants" });
|
||||||
|
|
||||||
|
var all_cards = await CardModel.getAll();
|
||||||
|
if (!all_cards)
|
||||||
|
return res.status(404).send({ error: "Cant find cards" });
|
||||||
|
|
||||||
|
var cards_to_sell = [];
|
||||||
|
var coins = 0;
|
||||||
|
for(var i=0; i<user.cards.length; i++)
|
||||||
|
{
|
||||||
|
var card = user.cards[i];
|
||||||
|
var card_data = UserTool.getData(all_cards, card.tid);
|
||||||
|
if(card_data && card_data.cost > 0 && card.quantity > keep)
|
||||||
|
{
|
||||||
|
if(!variantId || card.variant == variantId)
|
||||||
|
{
|
||||||
|
if(!rarityId || card_data.rarity == rarityId)
|
||||||
|
{
|
||||||
|
var variant = UserTool.getData(all_variants, card.variant);
|
||||||
|
var quantity = card.quantity - keep;
|
||||||
|
var sell = {tid: card.tid, variant: card.variant, quantity: -quantity};
|
||||||
|
var factor = variant != null ? variant.cost_factor : 1;
|
||||||
|
var cost = quantity * Math.round(card_data.cost * factor * config.sell_ratio);
|
||||||
|
cards_to_sell.push(sell);
|
||||||
|
coins += cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cards_to_sell.length == 0)
|
||||||
|
return res.status(200).send();
|
||||||
|
|
||||||
|
user.coins += coins;
|
||||||
|
|
||||||
|
var valid = await UserTool.addCards(user, cards_to_sell);
|
||||||
|
if (!valid)
|
||||||
|
return res.status(500).send({ error: "Error when removing cards" });
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["coins", "cards"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
const activityData = {rarity: rarityId, variant: variantId, keep: keep};
|
||||||
|
const act = await Activity.LogActivity("user_sell_cards_duplicate", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
return res.status(200).send();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.BuyPack = async (req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const packId = req.body.pack;
|
||||||
|
const quantity = req.body.quantity || 1;
|
||||||
|
|
||||||
|
if (!packId || typeof packId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!Number.isInteger(quantity) || quantity <= 0)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get the user add update the array
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Cant find user " + userId });
|
||||||
|
|
||||||
|
var pack = await PackModel.get(packId);
|
||||||
|
if (!pack)
|
||||||
|
return res.status(404).send({ error: "Cant find pack " + packId });
|
||||||
|
|
||||||
|
if(pack.cost <= 0)
|
||||||
|
return res.status(400).send({ error: "Can't be purchased" });
|
||||||
|
|
||||||
|
var cost = quantity * pack.cost;
|
||||||
|
if(user.coins < cost)
|
||||||
|
return res.status(400).send({ error: "Not enough coins" });
|
||||||
|
|
||||||
|
user.coins -= cost;
|
||||||
|
|
||||||
|
var valid = await UserTool.addPacks(user, [{tid: packId, quantity: quantity}]);
|
||||||
|
if (!valid)
|
||||||
|
return res.status(500).send({ error: "Error when adding packs" });
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["coins", "packs"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
const activityData = {pack: packId, quantity: quantity};
|
||||||
|
const act = await Activity.LogActivity("user_buy_pack", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
return res.status(200).send();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.SellPack = async (req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const packId = req.body.pack;
|
||||||
|
const quantity = req.body.quantity || 1;
|
||||||
|
|
||||||
|
if (!packId || typeof packId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if(!Number.isInteger(quantity) || quantity <= 0)
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get the user add update the array
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Cant find user " + userId });
|
||||||
|
|
||||||
|
var pack = await PackModel.get(packId);
|
||||||
|
if (!pack)
|
||||||
|
return res.status(404).send({ error: "Cant find pack " + packId });
|
||||||
|
|
||||||
|
if(pack.cost <= 0)
|
||||||
|
return res.status(400).send({ error: "Can't be sold" });
|
||||||
|
|
||||||
|
if(!UserTool.hasPack(user, packId, quantity))
|
||||||
|
return res.status(400).send({ error: "Not enough coins" });
|
||||||
|
|
||||||
|
var cost = quantity * Math.round(pack.cost * config.sell_ratio);
|
||||||
|
user.coins += cost;
|
||||||
|
|
||||||
|
var valid = await UserTool.addPacks(user, [{tid: packId, quantity: -quantity}]);
|
||||||
|
if (!valid)
|
||||||
|
return res.status(500).send({ error: "Error when adding packs" });
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["coins", "packs"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
const activityData = {pack: packId, quantity: quantity};
|
||||||
|
const act = await Activity.LogActivity("user_sell_pack", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
return res.status(200).send();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.OpenPack = async (req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const packId = req.body.pack;
|
||||||
|
|
||||||
|
if (!packId || typeof packId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get the user add update the array
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Cant find user " + userId });
|
||||||
|
|
||||||
|
var pack = await PackModel.get(packId);
|
||||||
|
if (!pack)
|
||||||
|
return res.status(404).send({ error: "Cant find pack " + packId });
|
||||||
|
|
||||||
|
if(!UserTool.hasPack(user, packId, 1))
|
||||||
|
return res.status(400).send({ error: "You don't have this pack" });
|
||||||
|
|
||||||
|
var cardsToAdd = await CardTool.getPackCards(pack);
|
||||||
|
var validCards = await UserTool.addCards(user, cardsToAdd);
|
||||||
|
var validPacks = await UserTool.addPacks(user, [{tid: packId, quantity: -1}]);
|
||||||
|
|
||||||
|
if (!validCards || !validPacks)
|
||||||
|
return res.status(500).send({ error: "Error when adding cards" });
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["cards", "packs"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
const activityData = {pack: packId, cards: cardsToAdd};
|
||||||
|
const act = await Activity.LogActivity("user_open_pack", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
return res.status(200).send(cardsToAdd);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.BuyAvatar = async (req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const avatarId = req.body.avatar;
|
||||||
|
|
||||||
|
if (!avatarId || typeof avatarId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get the user add update the array
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Cant find user " + userId });
|
||||||
|
|
||||||
|
var cost = config.avatar_cost;
|
||||||
|
if(user.coins < cost)
|
||||||
|
return res.status(400).send({ error: "Not enough coins" });
|
||||||
|
|
||||||
|
if(UserTool.hasAvatar(user, avatarId))
|
||||||
|
return res.status(400).send({ error: "Already have this avatar" });
|
||||||
|
|
||||||
|
user.coins -= cost;
|
||||||
|
UserTool.addAvatars(user, [avatarId]);
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["coins", "avatars"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
const activityData = {avatar: avatarId};
|
||||||
|
const act = await Activity.LogActivity("user_buy_avatar", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
return res.status(200).send();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.BuyCardback = async (req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const cardbackId = req.body.cardback;
|
||||||
|
|
||||||
|
if (!cardbackId || typeof cardbackId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get the user add update the array
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Cant find user " + userId });
|
||||||
|
|
||||||
|
var cost = config.cardback_cost;
|
||||||
|
if(user.coins < cost)
|
||||||
|
return res.status(400).send({ error: "Not enough coins" });
|
||||||
|
|
||||||
|
if(UserTool.hasCardback(user, cardbackId))
|
||||||
|
return res.status(400).send({ error: "Already have this cardback" });
|
||||||
|
|
||||||
|
user.coins -= cost;
|
||||||
|
UserTool.addCardbacks(user, [cardbackId]);
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["coins", "cardbacks"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
const activityData = {cardback: cardbackId};
|
||||||
|
const act = await Activity.LogActivity("user_buy_cardback", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
return res.status(200).send();
|
||||||
|
};
|
||||||
|
|
||||||
|
//Fix variant from previous version
|
||||||
|
exports.FixVariants = async (req, res) =>
|
||||||
|
{
|
||||||
|
var from = req.body.from || "";
|
||||||
|
var to = req.body.to || "";
|
||||||
|
|
||||||
|
if (from && typeof packId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
if (to && typeof packId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
var users = await UserModel.getAll();
|
||||||
|
var default_variant = await VariantModel.getDefault();
|
||||||
|
var default_tid = default_variant ? default_variant.tid : "";
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
for(var u=0; u<users.length; u++)
|
||||||
|
{
|
||||||
|
var user = users[u];
|
||||||
|
var changed = false;
|
||||||
|
for(var i=0; i<user.cards.length; i++)
|
||||||
|
{
|
||||||
|
var card = user.cards[i];
|
||||||
|
if(!card.variant)
|
||||||
|
{
|
||||||
|
card.variant = default_tid;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if(from && to && card.variant == from)
|
||||||
|
{
|
||||||
|
card.variant = to;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(changed)
|
||||||
|
{
|
||||||
|
var new_cards = user.cards;
|
||||||
|
user.cards = [];
|
||||||
|
await UserTool.addCards(user, new_cards); //Re-add in correct format
|
||||||
|
UserModel.save(user, ["cards"]);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
const act = await Activity.LogActivity("fix_variants", req.jwt.username, {});
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
return res.status(200).send({updated: count});
|
||||||
|
}
|
||||||
505
users/users.controller.js
Normal file
505
users/users.controller.js
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
const UserModel = require('./users.model');
|
||||||
|
const UserTool = require('./users.tool');
|
||||||
|
const RewardModel = require('../rewards/rewards.model');
|
||||||
|
const DateTool = require('../tools/date.tool');
|
||||||
|
const Activity = require("../activity/activity.model");
|
||||||
|
const Validator = require('../tools/validator.tool');
|
||||||
|
const AuthTool = require('../authorization/auth.tool');
|
||||||
|
const Email = require('../tools/email.tool');
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
//Register new user
|
||||||
|
exports.RegisterUser = async (req, res, next) => {
|
||||||
|
|
||||||
|
if(!req.body.email || !req.body.username || !req.body.password){
|
||||||
|
return res.status(400).send({error: 'Invalid parameters'});
|
||||||
|
}
|
||||||
|
|
||||||
|
var email = req.body.email;
|
||||||
|
var username = req.body.username;
|
||||||
|
var password = req.body.password;
|
||||||
|
var avatar = req.body.avatar || "";
|
||||||
|
|
||||||
|
//Validations
|
||||||
|
if(!Validator.validateUsername(username)){
|
||||||
|
return res.status(400).send({error: 'Invalid username'});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Validator.validateEmail(email)){
|
||||||
|
return res.status(400).send({error: 'Invalid email'});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Validator.validatePassword(password)){
|
||||||
|
return res.status(400).send({error: 'Invalid password'});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(avatar && typeof avatar !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid avatar"});
|
||||||
|
|
||||||
|
var user_username = await UserModel.getByUsername(username);
|
||||||
|
var user_email = await UserModel.getByEmail(email);
|
||||||
|
|
||||||
|
if(user_username)
|
||||||
|
return res.status(400).send({error: 'Username already exists'});
|
||||||
|
if(user_email)
|
||||||
|
return res.status(400).send({error: 'Email already exists'});
|
||||||
|
|
||||||
|
//Check if its first user
|
||||||
|
var nb_users = await UserModel.count();
|
||||||
|
var permission = nb_users > 0 ? 1 : 10; //First user has 10
|
||||||
|
var validation = nb_users > 0 ? 0 : 1; //First user has 1
|
||||||
|
|
||||||
|
//User Data
|
||||||
|
var user = {};
|
||||||
|
|
||||||
|
user.username = username;
|
||||||
|
user.email = email;
|
||||||
|
user.avatar = avatar;
|
||||||
|
user.permission_level = permission;
|
||||||
|
user.validation_level = validation;
|
||||||
|
|
||||||
|
user.coins = config.start_coins;
|
||||||
|
user.elo = config.start_elo;
|
||||||
|
user.xp = 0;
|
||||||
|
|
||||||
|
user.account_create_time = new Date();
|
||||||
|
user.last_login_time = new Date();
|
||||||
|
user.last_online_time = new Date();
|
||||||
|
user.email_confirm_key = UserTool.generateID(20);
|
||||||
|
|
||||||
|
UserTool.setUserPassword(user, password);
|
||||||
|
|
||||||
|
//Create user
|
||||||
|
var nUser = await UserModel.create(user);
|
||||||
|
if(!nUser)
|
||||||
|
return res.status(500).send({ error: "Unable to create user" });
|
||||||
|
|
||||||
|
//Send confirm email
|
||||||
|
UserTool.sendEmailConfirmKey(nUser, user.email, user.email_confirm_key);
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
var activityData = {username: user.username, email: user.email };
|
||||||
|
var act = await Activity.LogActivity("register", user.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
//Return response
|
||||||
|
return res.status(200).send({ success: true, id: nUser._id });
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetAll = async(req, res) => {
|
||||||
|
|
||||||
|
let user_permission_level = parseInt(req.jwt.permission_level);
|
||||||
|
let is_admin = (user_permission_level >= config.permissions.SERVER);
|
||||||
|
|
||||||
|
var list = await UserModel.getAll();
|
||||||
|
for(var i=0; i<list.length; i++){
|
||||||
|
if(is_admin)
|
||||||
|
list[i] = list[i].deleteSecrets();
|
||||||
|
else
|
||||||
|
list[i] = list[i].deleteAdminOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send(list);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetUser = async(req, res) => {
|
||||||
|
var user = await UserModel.getById(req.params.userId);
|
||||||
|
if(!user)
|
||||||
|
user = await UserModel.getByUsername(req.params.userId);
|
||||||
|
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found " + req.params.userId});
|
||||||
|
|
||||||
|
let user_permission_level = parseInt(req.jwt.permission_level);
|
||||||
|
let is_admin = (user_permission_level >= config.permissions.SERVER);
|
||||||
|
if(is_admin || req.params.userId == req.jwt.userId || req.params.userId == req.jwt.username)
|
||||||
|
user = user.deleteSecrets();
|
||||||
|
else
|
||||||
|
user = user.deleteAdminOnly();
|
||||||
|
|
||||||
|
user.server_time = new Date(); //Return server time
|
||||||
|
return res.status(200).send(user);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.EditUser = async(req, res) => {
|
||||||
|
|
||||||
|
var userId = req.params.userId;
|
||||||
|
var avatar = req.body.avatar;
|
||||||
|
var cardback = req.body.cardback;
|
||||||
|
|
||||||
|
if(!userId || typeof userId !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(avatar && typeof avatar !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid avatar"});
|
||||||
|
|
||||||
|
if(cardback && typeof cardback !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid avatar"});
|
||||||
|
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found: " + userId});
|
||||||
|
|
||||||
|
var userData = {};
|
||||||
|
|
||||||
|
if(avatar && avatar.length < 50)
|
||||||
|
userData.avatar = avatar;
|
||||||
|
|
||||||
|
if(cardback && cardback.length < 50)
|
||||||
|
userData.cardback = cardback;
|
||||||
|
|
||||||
|
//Add other variables you'd like to be able to edit here
|
||||||
|
//Avoid allowing changing username, email or password here, since those require additional security validations and should have their own functions
|
||||||
|
|
||||||
|
//Update user
|
||||||
|
var result = await UserModel.update(user, userData);
|
||||||
|
if(!result)
|
||||||
|
return res.status(400).send({error: "Error updating user: " + userId});
|
||||||
|
|
||||||
|
return res.status(200).send(result.deleteSecrets());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.EditEmail = async(req, res) => {
|
||||||
|
|
||||||
|
var userId = req.jwt.userId;
|
||||||
|
var email = req.body.email;
|
||||||
|
|
||||||
|
if(!userId || typeof userId !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!email || !Validator.validateEmail(email))
|
||||||
|
return res.status(400).send({error: "Invalid email"});
|
||||||
|
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found: " + userId});
|
||||||
|
|
||||||
|
if(email == user.email)
|
||||||
|
return res.status(400).send({error: "Email unchanged"});
|
||||||
|
|
||||||
|
//Find email
|
||||||
|
var foundUserEmail = await UserModel.getByEmail(email);
|
||||||
|
if(foundUserEmail)
|
||||||
|
return res.status(403).send({error: "Email already exists"});
|
||||||
|
|
||||||
|
var prev_email = user.email;
|
||||||
|
var userData = {};
|
||||||
|
userData.email = email;
|
||||||
|
userData.validation_level = 0;
|
||||||
|
userData.email_confirm_key = UserTool.generateID(20);
|
||||||
|
|
||||||
|
//Update user
|
||||||
|
var result = await UserModel.update(user, userData);
|
||||||
|
if(!result)
|
||||||
|
return res.status(400).send({error: "Error updating user email: " + userId});
|
||||||
|
|
||||||
|
//Send confirmation email
|
||||||
|
UserTool.sendEmailConfirmKey(user, email, userData.email_confirm_key);
|
||||||
|
UserTool.sendEmailChangeEmail(user, prev_email, email);
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
var activityData = {prev_email: prev_email, new_email: email };
|
||||||
|
var a = await Activity.LogActivity("edit_email", req.jwt.username, {activityData});
|
||||||
|
if (!a) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
return res.status(200).send(result.deleteSecrets());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.EditPassword = async(req, res) => {
|
||||||
|
|
||||||
|
var userId = req.jwt.userId;
|
||||||
|
var password = req.body.password_new;
|
||||||
|
var password_previous = req.body.password_previous;
|
||||||
|
|
||||||
|
if(!userId || typeof userId !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!password || !password_previous || typeof password !== "string" || typeof password_previous !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found: " + userId});
|
||||||
|
|
||||||
|
let validPass = AuthTool.validatePassword(user, password_previous);
|
||||||
|
if(!validPass)
|
||||||
|
return res.status(401).send({error: "Invalid previous password"});
|
||||||
|
|
||||||
|
UserTool.setUserPassword(user, password);
|
||||||
|
|
||||||
|
var result = await UserModel.save(user, ["password", "refresh_key", "password_recovery_key"]);
|
||||||
|
if(!result)
|
||||||
|
return res.status(500).send({error: "Error updating user password: " + userId});
|
||||||
|
|
||||||
|
//Send confirmation email
|
||||||
|
UserTool.sendEmailChangePassword(user, user.email);
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
var a = await Activity.LogActivity("edit_password", req.jwt.username, {});
|
||||||
|
if (!a) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.EditPermissions = async(req, res) => {
|
||||||
|
|
||||||
|
var userId = req.params.userId;
|
||||||
|
var permission_level = req.body.permission_level;
|
||||||
|
|
||||||
|
if(!userId || typeof userId !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!Validator.isInteger(permission_level))
|
||||||
|
return res.status(400).send({error: "Invalid permission"});
|
||||||
|
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found: " + userId});
|
||||||
|
|
||||||
|
var userData = {};
|
||||||
|
|
||||||
|
//Change avatar
|
||||||
|
userData.permission_level = permission_level;
|
||||||
|
|
||||||
|
//Update user
|
||||||
|
var result = await UserModel.update(user, userData);
|
||||||
|
if(!result)
|
||||||
|
return res.status(400).send({error: "Error updating user: " + userId});
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
var activityData = {username: user.username, permission_level: userData.permission_level };
|
||||||
|
var act = await Activity.LogActivity("edit_permission", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
return res.status(200).send(result.deleteSecrets());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.ResetPassword = async(req, res) => {
|
||||||
|
|
||||||
|
var email = req.body.email;
|
||||||
|
|
||||||
|
if(!config.smtp_enabled)
|
||||||
|
return res.status(400).send({error: "Email SMTP is not configured"});
|
||||||
|
|
||||||
|
if(!email || typeof email !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var user = await UserModel.getByEmail(email);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found: " + email});
|
||||||
|
|
||||||
|
user.password_recovery_key = UserTool.generateID(10, true);
|
||||||
|
await UserModel.save(user, ["password_recovery_key"]);
|
||||||
|
|
||||||
|
UserTool.sendEmailPasswordRecovery(user, email);
|
||||||
|
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.ResetPasswordConfirm = async(req, res) => {
|
||||||
|
|
||||||
|
var email = req.body.email;
|
||||||
|
var code = req.body.code;
|
||||||
|
var password = req.body.password;
|
||||||
|
|
||||||
|
if(!email || typeof email !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!code || typeof code !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!password || typeof password !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var user = await UserModel.getByEmail(email);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found: " + email});
|
||||||
|
|
||||||
|
if(!user.password_recovery_key || user.password_recovery_key.toUpperCase() != code)
|
||||||
|
return res.status(403).send({error: "Invalid Recovery Code"});
|
||||||
|
|
||||||
|
UserTool.setUserPassword(user, password);
|
||||||
|
|
||||||
|
var result = await UserModel.save(user, ["password", "refresh_key", "password_recovery_key"]);
|
||||||
|
if(!result)
|
||||||
|
return res.status(500).send({error: "Error updating user password: " + email});
|
||||||
|
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
//In this function all message are returned in direct text because the email link is accessed from browser
|
||||||
|
exports.ConfirmEmail = async (req, res) =>{
|
||||||
|
|
||||||
|
if(!req.params.userId || !req.params.code){
|
||||||
|
return res.status(404).send("Code invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await UserModel.getById(req.params.userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send("Code invalid");
|
||||||
|
|
||||||
|
if(user.email_confirm_key != req.params.code)
|
||||||
|
return res.status(404).send("Code invalid");
|
||||||
|
|
||||||
|
if(user.validation_level >= 1)
|
||||||
|
return res.status(400).send("Email already confirmed!");
|
||||||
|
|
||||||
|
//Code valid!
|
||||||
|
var data = {validation_level: Math.max(user.validation_level, 1)};
|
||||||
|
await UserModel.update(user, data);
|
||||||
|
|
||||||
|
return res.status(200).send("Email confirmed!");
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.ResendEmail = async(req, res) =>
|
||||||
|
{
|
||||||
|
var userId = req.jwt.userId;
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found " + userId});
|
||||||
|
|
||||||
|
if(user.validation_level > 0)
|
||||||
|
return res.status(403).send({error: "Email already confirmed"});
|
||||||
|
|
||||||
|
UserTool.sendEmailConfirmKey(user, user.email, user.email_confirm_key);
|
||||||
|
|
||||||
|
return res.status(200).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.SendEmail = async (req, res) =>{
|
||||||
|
|
||||||
|
var subject = req.body.title;
|
||||||
|
var text = req.body.text;
|
||||||
|
var email = req.body.email;
|
||||||
|
|
||||||
|
if(!subject || typeof subject !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!text || typeof text !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(!email || typeof email !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
Email.SendEmail(email, subject, text, function(result){
|
||||||
|
console.log("Sent email to: " + email + ": " + result);
|
||||||
|
return res.status(200).send({success: result});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// reward is an object containing rewards to give
|
||||||
|
exports.GiveReward = async(req, res) =>
|
||||||
|
{
|
||||||
|
var userId = req.params.userId;
|
||||||
|
var reward = req.body.reward;
|
||||||
|
|
||||||
|
//Validate params
|
||||||
|
if (!userId || typeof userId !== "string")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
if (!reward || typeof reward !== "object")
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
//Get the user add update the array
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Cant find user " + userId });
|
||||||
|
|
||||||
|
var valid = await UserTool.GainUserReward(user, reward);
|
||||||
|
if (!valid)
|
||||||
|
return res.status(500).send({ error: "Error when adding rewards " + userId });
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["cards"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
// Activity Log -------------
|
||||||
|
var activityData = {reward: reward, user: user.username};
|
||||||
|
var act = await Activity.LogActivity("reward_give", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
return res.status(200).send(updatedUser.deleteSecrets());
|
||||||
|
};
|
||||||
|
|
||||||
|
// reward is an ID of reward to give
|
||||||
|
exports.GainReward = async(req, res) =>
|
||||||
|
{
|
||||||
|
var userId = req.params.userId;
|
||||||
|
var rewardId = req.body.reward;
|
||||||
|
|
||||||
|
if(!userId || !rewardId)
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
if(typeof rewardId !== "string")
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var user = await UserModel.getById(userId);
|
||||||
|
if(!user)
|
||||||
|
return res.status(404).send({error: "User not found: " + userId});
|
||||||
|
|
||||||
|
var reward = await RewardModel.get(rewardId);
|
||||||
|
if(!reward)
|
||||||
|
return res.status(404).send({error: "Reward not found: " + rewardId});
|
||||||
|
|
||||||
|
if(reward.repeat && req.jwt.permission_level < config.permissions.SERVER)
|
||||||
|
return res.status(404).send({error: "Insufficient Permission"});
|
||||||
|
|
||||||
|
if(!reward.repeat && user.rewards.includes(rewardId))
|
||||||
|
return res.status(403).send({error: "Reward already claimed: " + rewardId});
|
||||||
|
|
||||||
|
if(!reward.repeat && reward.group && user.rewards.includes(reward.group))
|
||||||
|
return res.status(403).send({error: "Reward group already claimed: " + reward.group});
|
||||||
|
|
||||||
|
//Save reward
|
||||||
|
if(!user.rewards.includes(reward.tid))
|
||||||
|
user.rewards.push(reward.tid);
|
||||||
|
|
||||||
|
if(reward.group && !user.rewards.includes(reward.group))
|
||||||
|
user.rewards.push(reward.group);
|
||||||
|
|
||||||
|
//Add reward to user
|
||||||
|
var valid = await UserTool.GainUserReward(user, reward);
|
||||||
|
|
||||||
|
//Check if succeed
|
||||||
|
if(!valid)
|
||||||
|
return res.status(500).send({error: "Failed adding reward: " + rewardId + " for " + userId});
|
||||||
|
|
||||||
|
//Update the user
|
||||||
|
var updatedUser = await UserModel.save(user, ["rewards", "xp", "coins", "cards", "decks", "avatars", "cardbacks"]);
|
||||||
|
if (!updatedUser) return res.status(500).send({ error: "Error updating user: " + userId });
|
||||||
|
|
||||||
|
//Log activity
|
||||||
|
var activityData = {reward: reward, user: user.username};
|
||||||
|
var act = await Activity.LogActivity("reward_gain", req.jwt.username, activityData);
|
||||||
|
if (!act) return res.status(500).send({ error: "Failed to log activity!!" });
|
||||||
|
|
||||||
|
return res.status(200).send(user.deleteSecrets());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetOnline = async(req, res) =>
|
||||||
|
{
|
||||||
|
//Count online users
|
||||||
|
var time = new Date();
|
||||||
|
time = DateTool.addMinutes(time, -10);
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
var users = await UserModel.getAll();
|
||||||
|
var usernames = [];
|
||||||
|
for(var i=0; i<users.length; i++)
|
||||||
|
{
|
||||||
|
var user = users[i];
|
||||||
|
if(user.last_online_time > time)
|
||||||
|
{
|
||||||
|
usernames.push(user.username);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.status(200).send({online: count, total: users.length, users: usernames});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Delete = async(req, res) => {
|
||||||
|
UserModel.remove(req.params.userId);
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
135
users/users.friends.controller.js
Normal file
135
users/users.friends.controller.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
const UserModel = require("./users.model");
|
||||||
|
const Activity = require("../activity/activity.model");
|
||||||
|
const UserTool = require('./users.tool');
|
||||||
|
const config = require('../config.js');
|
||||||
|
|
||||||
|
exports.AddFriend = async (req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const username = req.body.username;
|
||||||
|
|
||||||
|
//Validate params
|
||||||
|
if (!username || !userId) {
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the user
|
||||||
|
const user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Can't find user" });
|
||||||
|
|
||||||
|
const friend = await UserModel.getByUsername(username);
|
||||||
|
if (!friend)
|
||||||
|
return res.status(404).send({ error: "Can't find friend" });
|
||||||
|
|
||||||
|
if(user.id == friend.id)
|
||||||
|
return res.status(400).send({ error: "Can't add yourself" });
|
||||||
|
|
||||||
|
//Add Friend
|
||||||
|
if(!user.friends.includes(friend.username))
|
||||||
|
user.friends.push(friend.username);
|
||||||
|
|
||||||
|
//Add request other friend
|
||||||
|
if(!friend.friends.includes(user.username) && !friend.friends_requests.includes(user.username))
|
||||||
|
friend.friends_requests.push(user.username)
|
||||||
|
|
||||||
|
//Remove self request
|
||||||
|
if(user.friends_requests.includes(friend.username))
|
||||||
|
user.friends_requests.remove(friend.username);
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["friends", "friends_requests"]);
|
||||||
|
if (!updatedUser) return res.status(400).send({ error: "Error updating user" });
|
||||||
|
|
||||||
|
//Update the other user
|
||||||
|
var updatedFriend = await UserModel.save(friend, ["friends_requests"]);
|
||||||
|
if (!updatedFriend) return res.status(400).send({ error: "Error updating user" });
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
return res.status(200).send(updatedUser.deleteSecrets());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.RemoveFriend = async(req, res) => {
|
||||||
|
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
const username = req.body.username;
|
||||||
|
|
||||||
|
//Validate params
|
||||||
|
if (!username || !userId) {
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the user
|
||||||
|
const user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Can't find user" });
|
||||||
|
|
||||||
|
const friend = await UserModel.getByUsername(username);
|
||||||
|
if (!friend)
|
||||||
|
return res.status(404).send({ error: "Can't find friend" });
|
||||||
|
|
||||||
|
if(user.friends.includes(friend.username))
|
||||||
|
user.friends.remove(friend.username);
|
||||||
|
if(user.friends_requests.includes(friend.username))
|
||||||
|
user.friends_requests.remove(friend.username);
|
||||||
|
if(friend.friends_requests.includes(user.username))
|
||||||
|
friend.friends_requests.remove(user.username)
|
||||||
|
|
||||||
|
//Update the user array
|
||||||
|
var updatedUser = await UserModel.save(user, ["friends", "friends_requests"]);
|
||||||
|
if (!updatedUser) return res.status(400).send({ error: "Error updating user" });
|
||||||
|
|
||||||
|
var updatedFriend = await UserModel.save(friend, ["friends_requests"]);
|
||||||
|
if (!updatedFriend) return res.status(400).send({ error: "Error updating user" });
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
return res.status(200).send(updatedUser.deleteSecrets());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.ListFriends = async(req, res) =>
|
||||||
|
{
|
||||||
|
const userId = req.jwt.userId;
|
||||||
|
|
||||||
|
//Validate params
|
||||||
|
if (!userId) {
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the user
|
||||||
|
const user = await UserModel.getById(userId);
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).send({ error: "Can't find user" });
|
||||||
|
|
||||||
|
var friends_users = user.friends || [];
|
||||||
|
var requests_users = user.friends_requests || [];
|
||||||
|
|
||||||
|
const friends = await UserModel.getUsernameList(friends_users);
|
||||||
|
if (!friends)
|
||||||
|
return res.status(404).send({ error: "Can't find user friends" });
|
||||||
|
|
||||||
|
const requests = await UserModel.getUsernameList(requests_users);
|
||||||
|
if (!requests)
|
||||||
|
return res.status(404).send({ error: "Can't find user friends" });
|
||||||
|
|
||||||
|
//Reduce visible fields
|
||||||
|
for(var i=0; i<friends.length; i++)
|
||||||
|
{
|
||||||
|
friends[i] = {
|
||||||
|
username: friends[i].username,
|
||||||
|
avatar: friends[i].avatar,
|
||||||
|
last_online_time: friends[i].last_online_time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i=0; i<requests.length; i++)
|
||||||
|
{
|
||||||
|
requests[i] = {
|
||||||
|
username: requests[i].username,
|
||||||
|
avatar: requests[i].avatar,
|
||||||
|
last_online_time: requests[i].last_online_time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({username: user.username, friends: friends, friends_requests: requests, server_time: new Date()});
|
||||||
|
|
||||||
|
}
|
||||||
246
users/users.model.js
Normal file
246
users/users.model.js
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const userSchema = new Schema({
|
||||||
|
|
||||||
|
username: {type: String, required: true, index: true, unique: true, default: ""},
|
||||||
|
email: {type: String, required: true, index: true, default: ""},
|
||||||
|
password: {type: String, required: true, default: ""},
|
||||||
|
|
||||||
|
permission_level: {type: Number, required: true, default: 1}, //Admin or not?
|
||||||
|
validation_level: {type: Number, required: true, default: 0}, //Validation level increases by confirming email
|
||||||
|
account_create_time: {type: Date, default: null},
|
||||||
|
last_login_time: {type: Date, default: null},
|
||||||
|
last_online_time: {type: Date, default: null},
|
||||||
|
|
||||||
|
refresh_key: {type: String, default: ""}, //Used for refreshing the current login JWT token
|
||||||
|
proof_key: {type: String, default: ""}, //Used to proof to a another server who you are
|
||||||
|
email_confirm_key: {type: String, default: ""}, //Used to confirm email
|
||||||
|
password_recovery_key: {type: String, default: ""}, //Used for password recovery
|
||||||
|
|
||||||
|
avatar: {type: String, default: ""},
|
||||||
|
cardback: {type: String, default: ""},
|
||||||
|
coins: {type: Number, default: 0},
|
||||||
|
xp: {type: Number, default: 0},
|
||||||
|
elo: {type: Number, default: 1000},
|
||||||
|
|
||||||
|
matches: {type: Number, default: 0},
|
||||||
|
victories: {type: Number, default: 0},
|
||||||
|
defeats: {type: Number, default: 0},
|
||||||
|
|
||||||
|
cards: [{ tid: String, variant: String, quantity: Number, _id: false }],
|
||||||
|
packs: [{ tid: String, quantity: Number, _id: false }],
|
||||||
|
decks: [{ type: Object, _id: false }],
|
||||||
|
avatars: [{type: String}],
|
||||||
|
cardbacks: [{type: String}],
|
||||||
|
rewards: [{type: String}],
|
||||||
|
|
||||||
|
friends: [{type: String}],
|
||||||
|
friends_requests: [{type: String}],
|
||||||
|
});
|
||||||
|
|
||||||
|
userSchema.virtual('id').get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
userSchema.methods.toObj = function() {
|
||||||
|
var user = this.toObject();
|
||||||
|
user.id = user._id;
|
||||||
|
delete user.__v;
|
||||||
|
delete user._id;
|
||||||
|
return user;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Hide sensitive information
|
||||||
|
userSchema.methods.deleteSecrets = function(){
|
||||||
|
var user = this.toObject();
|
||||||
|
user.id = user._id;
|
||||||
|
delete user.__v;
|
||||||
|
delete user._id;
|
||||||
|
delete user.password;
|
||||||
|
delete user.refresh_key;
|
||||||
|
delete user.proof_key;
|
||||||
|
delete user.email_confirm_key;
|
||||||
|
delete user.password_recovery_key;
|
||||||
|
return user;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Hide non-admin information, for example only admins can read user emails
|
||||||
|
userSchema.methods.deleteAdminOnly = function(){
|
||||||
|
var user = this.toObject();
|
||||||
|
delete user.__v;
|
||||||
|
delete user._id;
|
||||||
|
delete user.email;
|
||||||
|
delete user.permission_level;
|
||||||
|
delete user.validation_level;
|
||||||
|
delete user.password;
|
||||||
|
delete user.refresh_key;
|
||||||
|
delete user.proof_key;
|
||||||
|
delete user.email_confirm_key;
|
||||||
|
delete user.password_recovery_key;
|
||||||
|
return user;
|
||||||
|
};
|
||||||
|
|
||||||
|
const User = mongoose.model('Users', userSchema);
|
||||||
|
exports.UserModel = User;
|
||||||
|
|
||||||
|
// USER DATA MODELS ------------------------------------------------
|
||||||
|
|
||||||
|
exports.getById = async(id) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var user = await User.findOne({_id: id});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getByEmail = async(email) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var regex = new RegExp(["^", email, "$"].join(""), "i");
|
||||||
|
var user = await User.findOne({email: regex});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getByUsername = async(username) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var regex = new RegExp(["^", username, "$"].join(""), "i");
|
||||||
|
var user = await User.findOne({username: regex});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.create = async(userData) => {
|
||||||
|
const user = new User(userData);
|
||||||
|
return await user.save();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async() => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var users = await User.find()
|
||||||
|
users = users || [];
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAllLimit = async(perPage, page) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var users = await User.find().limit(perPage).skip(perPage * page)
|
||||||
|
users = users || [];
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//List users contained in the username list
|
||||||
|
exports.getUsernameList = async(username_list) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var users = await User.find({ username: { $in: username_list } });
|
||||||
|
return users || [];
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Saves an already loaded User, by providing a string list of changed keys
|
||||||
|
exports.save = async(user, modified_list) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
if(!user) return null;
|
||||||
|
|
||||||
|
if(modified_list)
|
||||||
|
{
|
||||||
|
for (let i=0; i<modified_list.length; i++) {
|
||||||
|
user.markModified(modified_list[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await user.save();
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.log(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Update an already loaded user, by providing an object containing new values
|
||||||
|
exports.update = async(user, userData) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
if(!user) return null;
|
||||||
|
|
||||||
|
for (let i in userData) {
|
||||||
|
user[i] = userData[i];
|
||||||
|
user.markModified(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedUser = await user.save();
|
||||||
|
return updatedUser;
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.log(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Load, and then update a user, based on userId and an object containing new values
|
||||||
|
exports.patch = async(userId, userData) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var user = await User.findById ({_id: userId});
|
||||||
|
if(!user) return null;
|
||||||
|
|
||||||
|
for (let i in userData) {
|
||||||
|
user[i] = userData[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedUser = await user.save();
|
||||||
|
return updatedUser;
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.log(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.remove = async(userId) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var result = await User.deleteOne({_id: userId});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.count = async() =>
|
||||||
|
{
|
||||||
|
try{
|
||||||
|
var count = await User.countDocuments({});
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
215
users/users.routes.js
Normal file
215
users/users.routes.js
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
const UsersController = require('./users.controller');
|
||||||
|
const UsersCardsController = require("./users.cards.controller");
|
||||||
|
const UsersFriendsController = require("./users.friends.controller");
|
||||||
|
const AuthTool = require('../authorization/auth.tool');
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
const ADMIN = config.permissions.ADMIN; //Highest permision, can read and write all users
|
||||||
|
const SERVER = config.permissions.SERVER; //Middle permission, can read all users and grant rewards
|
||||||
|
const USER = config.permissions.USER; //Lowest permision, can only do things on same user
|
||||||
|
|
||||||
|
exports.route = function (app) {
|
||||||
|
|
||||||
|
//Body: username, email, password, avatar
|
||||||
|
app.post("/users/register", app.auth_limiter, [
|
||||||
|
UsersController.RegisterUser,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/users", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersController.GetAll,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/users/:userId", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersController.GetUser,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// USER - EDITS ----------------------
|
||||||
|
|
||||||
|
//Body: avatar, userId allows an admin to edit another user
|
||||||
|
app.post("/users/edit/:userId", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
AuthTool.isSameUserOr(ADMIN),
|
||||||
|
UsersController.EditUser,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//Body: permission
|
||||||
|
app.post("/users/permission/edit/:userId", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
UsersController.EditPermissions,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//Body: email
|
||||||
|
app.post("/users/email/edit", app.auth_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersController.EditEmail,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//Body: password_previous, password_new
|
||||||
|
app.post("/users/password/edit", app.auth_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersController.EditPassword,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//Body: email
|
||||||
|
app.post("/users/password/reset", app.auth_limiter, [
|
||||||
|
UsersController.ResetPassword,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//body: email, code, password (password is the new one)
|
||||||
|
app.post("/users/password/reset/confirm", app.auth_limiter, [
|
||||||
|
UsersController.ResetPasswordConfirm,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/*app.post("/users/delete/:userId", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
UsersController.Delete,
|
||||||
|
]);*/
|
||||||
|
|
||||||
|
// USER - EMAIL CONFIRMATION ---------------------------
|
||||||
|
|
||||||
|
//Email confirm
|
||||||
|
app.get("/users/email/confirm/:userId/:code", [
|
||||||
|
UsersController.ConfirmEmail,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//Ask to resend confirmation email
|
||||||
|
app.post("/users/email/resend", app.auth_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersController.ResendEmail,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//Send a test email to one email address
|
||||||
|
//body: title, text, email
|
||||||
|
app.post("/users/email/send", app.auth_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
UsersController.SendEmail,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// USER - CARDS --------------------------------------
|
||||||
|
|
||||||
|
app.post("/users/packs/open/", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.OpenPack,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/users/packs/buy/", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.BuyPack,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/users/packs/sell/", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.SellPack,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/users/cards/buy/", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.BuyCard,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/users/cards/sell/", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.SellCard,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/users/cards/sell/duplicate", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.SellDuplicateCards,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/users/cards/variants/fix/", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isSameUserOr(SERVER),
|
||||||
|
UsersCardsController.FixVariants,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/users/avatar/buy", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.BuyAvatar,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/users/cardback/buy", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.BuyCardback,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
// USER - DECKS --------------------------------------
|
||||||
|
|
||||||
|
//Decks
|
||||||
|
app.post('/users/deck/:deckId', app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.UpdateDeck
|
||||||
|
]);
|
||||||
|
app.delete('/users/deck/:deckId', app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersCardsController.DeleteDeck
|
||||||
|
]);
|
||||||
|
|
||||||
|
// USER - Friends --------------------------------------
|
||||||
|
|
||||||
|
//body: username (friend username)
|
||||||
|
app.post("/users/friends/add/", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersFriendsController.AddFriend,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//body: username (friend username)
|
||||||
|
app.post("/users/friends/remove/", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersFriendsController.RemoveFriend,
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/users/friends/list/", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
UsersFriendsController.ListFriends,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// USER - REWARDS ---------------------------
|
||||||
|
|
||||||
|
//body: reward (object containing all rewards to give, doesnt exist in mongo db)
|
||||||
|
app.post("/users/rewards/give/:userId", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(SERVER),
|
||||||
|
UsersController.GiveReward,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//body: reward (ID of the reward to give already in mongo db), only SERVER can give repeating rewards
|
||||||
|
app.post("/users/rewards/gain/:userId", app.post_limiter, [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(USER),
|
||||||
|
AuthTool.isSameUserOr(SERVER),
|
||||||
|
UsersController.GainReward,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// USER - STATS ---------------------------
|
||||||
|
|
||||||
|
app.get("/online", [
|
||||||
|
UsersController.GetOnline
|
||||||
|
]);
|
||||||
|
|
||||||
|
};
|
||||||
352
users/users.tool.js
Normal file
352
users/users.tool.js
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
const config = require('../config.js');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const Email = require('../tools/email.tool');
|
||||||
|
const AuthTool = require('../authorization/auth.tool');
|
||||||
|
const DeckModel = require('../decks/decks.model');
|
||||||
|
const Validator = require('../tools/validator.tool');
|
||||||
|
const VariantModel = require('../variants/variants.model.js');
|
||||||
|
|
||||||
|
const UserTool = {};
|
||||||
|
|
||||||
|
UserTool.generateID = function(length, easyRead) {
|
||||||
|
var result = '';
|
||||||
|
var characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
if(easyRead)
|
||||||
|
characters = 'abcdefghijklmnpqrstuvwxyz123456789'; //Remove confusing characters like 0 and O
|
||||||
|
var charactersLength = characters.length;
|
||||||
|
for ( var i = 0; i < length; i++ ) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserTool.setUserPassword = (user, password) =>
|
||||||
|
{
|
||||||
|
user.password = AuthTool.hashPassword(password);
|
||||||
|
user.password_recovery_key = ""; //After changing password, disable recovery until inited again
|
||||||
|
user.refresh_key = crypto.randomBytes(16).toString('base64'); //Logout previous logins by changing the refresh_key
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------- Rewards -----------
|
||||||
|
|
||||||
|
UserTool.GainUserReward = async(user, reward) =>
|
||||||
|
{
|
||||||
|
//Add reward to user
|
||||||
|
user.coins += reward.coins || 0;
|
||||||
|
user.xp += reward.xp || 0;
|
||||||
|
|
||||||
|
UserTool.addAvatars(user, reward.avatars);
|
||||||
|
UserTool.addCardbacks(user, reward.cardbacks);
|
||||||
|
|
||||||
|
//Add cards and decks
|
||||||
|
var valid_c = await UserTool.addCards(user, reward.cards || []);
|
||||||
|
var valid_p = await UserTool.addPacks(user, reward.packs || []);
|
||||||
|
var valid_d = await UserTool.addDecks(user, reward.decks || []);
|
||||||
|
return valid_c && valid_p && valid_d;
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------- Cards, Packs and Decks --------
|
||||||
|
|
||||||
|
//newCards is just an array of string (card tid), or an array of object {tid: "", quantity: 1}
|
||||||
|
UserTool.addCards = async(user, newCards) =>
|
||||||
|
{
|
||||||
|
var cards = user.cards;
|
||||||
|
|
||||||
|
if(!Array.isArray(cards) || !Array.isArray(newCards))
|
||||||
|
return false; //Wrong params
|
||||||
|
|
||||||
|
if(newCards.length == 0)
|
||||||
|
return true; //No card to add, succeeded
|
||||||
|
|
||||||
|
//Count quantities
|
||||||
|
var prevTotal = Validator.countQuantity(cards);
|
||||||
|
var addTotal = Validator.countQuantity(newCards);
|
||||||
|
|
||||||
|
var variant_default = await VariantModel.getDefault();
|
||||||
|
var default_tid = variant_default ? variant_default.tid : "";
|
||||||
|
|
||||||
|
//Loop on cards to add
|
||||||
|
for (let c = 0; c < newCards.length; c++) {
|
||||||
|
|
||||||
|
var cardAdd = newCards[c];
|
||||||
|
var cardAddTid = typeof cardAdd === 'object' ? cardAdd.tid : cardAdd;
|
||||||
|
var cardAddVariant = typeof cardAdd === 'object' ? cardAdd.variant : default_tid;
|
||||||
|
var cardAddQ = typeof cardAdd === 'object' ? cardAdd.quantity : 1;
|
||||||
|
|
||||||
|
if (cardAddTid && typeof cardAddTid === "string") {
|
||||||
|
var quantity = cardAddQ || 1; //default is 1
|
||||||
|
var found = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < cards.length; i++) {
|
||||||
|
if (cards[i].tid == cardAddTid && cards[i].variant == cardAddVariant) {
|
||||||
|
cards[i].quantity += quantity;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
cards.push({
|
||||||
|
tid: cardAddTid,
|
||||||
|
variant: cardAddVariant,
|
||||||
|
quantity: quantity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove empty
|
||||||
|
for(var i=cards.length-1; i>=0; i--)
|
||||||
|
{
|
||||||
|
var card = cards[i];
|
||||||
|
if(!card.quantity || card.quantity <= 0)
|
||||||
|
cards.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Validate quantities to make sure the array was updated correctly, this is to prevent users from loosing all their cards because of server error which would be terrible.
|
||||||
|
var valid = Validator.validateArray(cards, prevTotal + addTotal);
|
||||||
|
return valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.addPacks = async (user, newPacks) => {
|
||||||
|
|
||||||
|
var packs = user.packs;
|
||||||
|
|
||||||
|
if(!Array.isArray(packs) || !Array.isArray(newPacks))
|
||||||
|
return false; //Wrong params
|
||||||
|
|
||||||
|
if(newPacks.length == 0)
|
||||||
|
return true; //No pack to add, succeeded
|
||||||
|
|
||||||
|
//Count quantities
|
||||||
|
var prevTotal = Validator.countQuantity(packs);
|
||||||
|
var addTotal = Validator.countQuantity(newPacks);
|
||||||
|
|
||||||
|
//Loop on packs to add
|
||||||
|
for (let c = 0; c < newPacks.length; c++) {
|
||||||
|
|
||||||
|
var packAdd = newPacks[c];
|
||||||
|
var packAddTid = typeof packAdd === 'object' ? packAdd.tid : packAdd;
|
||||||
|
var packAddQ = typeof packAdd === 'object' ? packAdd.quantity : 1;
|
||||||
|
|
||||||
|
if (packAddTid && typeof packAddTid === "string") {
|
||||||
|
var quantity = packAddQ || 1; //default is 1
|
||||||
|
var found = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < packs.length; i++) {
|
||||||
|
if (packs[i].tid == packAddTid) {
|
||||||
|
packs[i].quantity += quantity;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
packs.push({
|
||||||
|
tid: packAddTid,
|
||||||
|
quantity: quantity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove empty
|
||||||
|
for(var i=packs.length-1; i>=0; i--)
|
||||||
|
{
|
||||||
|
var pack = packs[i];
|
||||||
|
if(!pack.quantity || pack.quantity <= 0)
|
||||||
|
packs.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Validate quantities to make sure the array was updated correctly, this is to prevent users from loosing all their packs because of server error which would be terrible.
|
||||||
|
var valid = Validator.validateArray(packs, prevTotal + addTotal);
|
||||||
|
return valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
//newDecks is just an array of string (deck tid)
|
||||||
|
UserTool.addDecks = async(user, newDecks) =>
|
||||||
|
{
|
||||||
|
var decks = user.decks;
|
||||||
|
|
||||||
|
if(!Array.isArray(decks) || !Array.isArray(newDecks))
|
||||||
|
return false; //Wrong params
|
||||||
|
|
||||||
|
if(newDecks.length == 0)
|
||||||
|
return true; //No deck to add, succeeded
|
||||||
|
|
||||||
|
var ndecks = await DeckModel.getList(newDecks);
|
||||||
|
if(!ndecks)
|
||||||
|
return false; //Decks not found
|
||||||
|
|
||||||
|
//Loop on cards to add
|
||||||
|
for (let c = 0; c < ndecks.length; c++) {
|
||||||
|
|
||||||
|
var deckAdd = ndecks[c];
|
||||||
|
var valid_c = await UserTool.addCards(user, deckAdd.cards);
|
||||||
|
if(!valid_c)
|
||||||
|
return false; //Failed adding cards
|
||||||
|
|
||||||
|
decks.push({
|
||||||
|
tid: deckAdd.tid + "_" + UserTool.generateID(5),
|
||||||
|
title: deckAdd.title || "",
|
||||||
|
hero: deckAdd.hero || {},
|
||||||
|
cards: deckAdd.cards || [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.addAvatars = (user, avatars) =>
|
||||||
|
{
|
||||||
|
if(!avatars || !Array.isArray(avatars))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (let i = 0; i < avatars.length; i++) {
|
||||||
|
var avatar = avatars[i];
|
||||||
|
if(avatar && typeof avatar === "string" && !user.avatars.includes(avatar))
|
||||||
|
user.avatars.push(avatar);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.addCardbacks = (user, cardbacks) =>
|
||||||
|
{
|
||||||
|
if(!cardbacks || !Array.isArray(cardbacks))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (let i = 0; i < cardbacks.length; i++) {
|
||||||
|
var cardback = cardbacks[i];
|
||||||
|
if(cardback && typeof cardback === "string" && !user.cardbacks.includes(cardback))
|
||||||
|
user.cardbacks.push(cardback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.hasCard = (user, card_id, variant_id, quantity) =>
|
||||||
|
{
|
||||||
|
for (let c = 0; c < user.cards.length; c++) {
|
||||||
|
var acard = user.cards[c];
|
||||||
|
var aquantity = acard.quantity || 1;
|
||||||
|
if(acard.tid == card_id && acard.variant == variant_id && aquantity >= quantity)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.hasPack = (user, card_tid, quantity) =>
|
||||||
|
{
|
||||||
|
for (let c = 0; c < user.packs.length; c++) {
|
||||||
|
var apack = user.packs[c];
|
||||||
|
var aquantity = apack.quantity || 1;
|
||||||
|
if(apack.tid == card_tid && aquantity >= quantity)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.hasAvatar = (user, avatarId) =>
|
||||||
|
{
|
||||||
|
return user.avatars.includes(avatarId);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserTool.hasCardback = (user, cardbackId) =>
|
||||||
|
{
|
||||||
|
return user.cardbacks.includes(cardbackId);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserTool.getDeck = (user, deck_tid) =>
|
||||||
|
{
|
||||||
|
var deck = {};
|
||||||
|
if(user && user.decks)
|
||||||
|
{
|
||||||
|
for(var i=0; i<user.decks.length; i++)
|
||||||
|
{
|
||||||
|
var adeck = user.decks[i];
|
||||||
|
if(adeck.tid == deck_tid)
|
||||||
|
{
|
||||||
|
deck = adeck;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deck;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.getData = (all_data, tid) =>
|
||||||
|
{
|
||||||
|
for(var i=0; i<all_data.length; i++)
|
||||||
|
{
|
||||||
|
if(all_data[i].tid == tid)
|
||||||
|
return all_data[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------- Emails --------
|
||||||
|
|
||||||
|
UserTool.sendEmailConfirmKey = (user, email, email_confirm_key) => {
|
||||||
|
|
||||||
|
if(!email || !user) return;
|
||||||
|
|
||||||
|
var subject = config.api_title + " - Email Confirmation";
|
||||||
|
var http = config.allow_https ? "https://" : "http://";
|
||||||
|
var confirm_link = http + config.api_url + "/users/email/confirm/" + user.id + "/" + email_confirm_key;
|
||||||
|
|
||||||
|
var text = "Hello " + user.username + "<br>";
|
||||||
|
text += "Welcome! <br><br>";
|
||||||
|
text += "To confirm your email, click here: <br><a href='" + confirm_link + "'>" + confirm_link + "</a><br><br>";
|
||||||
|
text += "Thank you and see you soon!<br>";
|
||||||
|
|
||||||
|
Email.SendEmail(email, subject, text, function(result){
|
||||||
|
console.log("Sent email to: " + email + ": " + result);
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.sendEmailChangeEmail = (user, email, new_email) => {
|
||||||
|
|
||||||
|
if(!email || !user) return;
|
||||||
|
|
||||||
|
var subject = config.api_title + " - Email Changed";
|
||||||
|
|
||||||
|
var text = "Hello " + user.username + "<br>";
|
||||||
|
text += "Your email was succesfully changed to: " + new_email + "<br>";
|
||||||
|
text += "If you believe this is an error, please contact support immediately.<br><br>"
|
||||||
|
text += "Thank you and see you soon!<br>";
|
||||||
|
|
||||||
|
Email.SendEmail(email, subject, text, function(result){
|
||||||
|
console.log("Sent email to: " + email + ": " + result);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.sendEmailChangePassword = (user, email) => {
|
||||||
|
|
||||||
|
if(!email || !user) return;
|
||||||
|
|
||||||
|
var subject = config.api_title + " - Password Changed";
|
||||||
|
|
||||||
|
var text = "Hello " + user.username + "<br>";
|
||||||
|
text += "Your password was succesfully changed<br>";
|
||||||
|
text += "If you believe this is an error, please contact support immediately.<br><br>"
|
||||||
|
text += "Thank you and see you soon!<br>";
|
||||||
|
|
||||||
|
Email.SendEmail(email, subject, text, function(result){
|
||||||
|
console.log("Sent email to: " + email + ": " + result);
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
UserTool.sendEmailPasswordRecovery = (user, email) => {
|
||||||
|
|
||||||
|
if(!email || !user) return;
|
||||||
|
|
||||||
|
var subject = config.api_title + " - Password Recovery";
|
||||||
|
|
||||||
|
var text = "Hello " + user.username + "<br>";
|
||||||
|
text += "Here is your password recovery code: " + user.password_recovery_key.toUpperCase() + "<br><br>";
|
||||||
|
text += "Thank you and see you soon!<br>";
|
||||||
|
|
||||||
|
Email.SendEmail(email, subject, text, function(result){
|
||||||
|
console.log("Sent email to: " + email + ": " + result);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = UserTool;
|
||||||
66
variants/variants.controller.js
Normal file
66
variants/variants.controller.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
const VariantModel = require("./variants.model");
|
||||||
|
|
||||||
|
exports.AddVariant = async(req, res) =>
|
||||||
|
{
|
||||||
|
var tid = req.body.tid;
|
||||||
|
var cost_factor = req.body.cost_factor || 1;
|
||||||
|
var is_default = req.body.is_default || false;
|
||||||
|
|
||||||
|
if(!Number.isInteger(cost_factor))
|
||||||
|
return res.status(400).send({ error: "Invalid parameters" });
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
tid: tid,
|
||||||
|
cost_factor: cost_factor,
|
||||||
|
is_default: is_default,
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update or create
|
||||||
|
var variant = await VariantModel.get(tid);
|
||||||
|
if(variant)
|
||||||
|
variant = await VariantModel.update(variant, data);
|
||||||
|
else
|
||||||
|
variant = await VariantModel.create(data);
|
||||||
|
|
||||||
|
if(!variant)
|
||||||
|
return res.status(500).send({error: "Error updating variant"});
|
||||||
|
|
||||||
|
return res.status(200).send(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteVariant = async(req, res) =>
|
||||||
|
{
|
||||||
|
VariantModel.remove(req.params.tid);
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.DeleteAll = async(req, res) =>
|
||||||
|
{
|
||||||
|
VariantModel.removeAll();
|
||||||
|
return res.status(204).send({});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetVariant = async(req, res) =>
|
||||||
|
{
|
||||||
|
var tid = req.params.tid;
|
||||||
|
|
||||||
|
if(!tid)
|
||||||
|
return res.status(400).send({error: "Invalid parameters"});
|
||||||
|
|
||||||
|
var variant = await VariantModel.get(tid);
|
||||||
|
if(!variant)
|
||||||
|
return res.status(404).send({error: "Variant not found: " + tid});
|
||||||
|
|
||||||
|
return res.status(200).send(variant.toObj());
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GetAll = async(req, res) =>
|
||||||
|
{
|
||||||
|
var variants = await VariantModel.getAll();
|
||||||
|
|
||||||
|
for(var i=0; i<variants.length; i++){
|
||||||
|
variants[i] = variants[i].toObj();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send(variants);
|
||||||
|
};
|
||||||
103
variants/variants.model.js
Normal file
103
variants/variants.model.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
const mongoose = require("mongoose");
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const variantsSchema = new Schema({
|
||||||
|
|
||||||
|
tid: { type: String, index: true, unique: true },
|
||||||
|
cost_factor: {type: Number, default: 1}, //Cost multiplier
|
||||||
|
is_default: {type: Boolean, default: false},
|
||||||
|
});
|
||||||
|
|
||||||
|
variantsSchema.methods.toObj = function() {
|
||||||
|
var elem = this.toObject();
|
||||||
|
delete elem.__v;
|
||||||
|
delete elem._id;
|
||||||
|
return elem;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Variant = mongoose.model("Variants", variantsSchema);
|
||||||
|
exports.Variant = Variant;
|
||||||
|
|
||||||
|
exports.create = async(data) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var variant = new Variant(data);
|
||||||
|
return await variant.save();
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.get = async(variant_tid) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var variant = await Variant.findOne({tid: variant_tid});
|
||||||
|
return variant;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getDefault = async() => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var variant = await Variant.findOne({is_default: true});
|
||||||
|
return variant;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getAll = async() => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var variants = await Variant.find({});
|
||||||
|
return variants;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.update = async(variant, data) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
if(!variant) return null;
|
||||||
|
|
||||||
|
for (let i in data) {
|
||||||
|
variant[i] = data[i];
|
||||||
|
variant.markModified(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var updated = await variant.save();
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.remove = async(variant_tid) => {
|
||||||
|
|
||||||
|
try{
|
||||||
|
var result = await Variant.deleteOne({tid: variant_tid});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.removeAll = async() => {
|
||||||
|
try{
|
||||||
|
var result = await Variant.deleteMany({});
|
||||||
|
return result && result.deletedCount > 0;
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
36
variants/variants.routes.js
Normal file
36
variants/variants.routes.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const config = require("../config");
|
||||||
|
const VariantsController = require("./variants.controller");
|
||||||
|
const AuthTool = require("../authorization/auth.tool");
|
||||||
|
|
||||||
|
const ADMIN = config.permissions.ADMIN; //Highest permision, can read and write all users
|
||||||
|
const SERVER = config.permissions.SERVER; //Higher permission, can read all users
|
||||||
|
const USER = config.permissions.USER; //Lowest permision, can only do things on same user
|
||||||
|
|
||||||
|
exports.route = (app) => {
|
||||||
|
|
||||||
|
app.get("/variants", [
|
||||||
|
VariantsController.GetAll
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.get("/variants/:tid", [
|
||||||
|
VariantsController.GetVariant
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.post("/variants/add", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
VariantsController.AddVariant
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/variants/:tid", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
VariantsController.DeleteVariant
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.delete("/variants", [
|
||||||
|
AuthTool.isValidJWT,
|
||||||
|
AuthTool.isPermissionLevel(ADMIN),
|
||||||
|
VariantsController.DeleteAll
|
||||||
|
]);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user