This commit is contained in:
yaoyanwei
2025-08-04 16:25:38 +08:00
parent 8d542ea201
commit 4b2bb35c20
46 changed files with 5128 additions and 0 deletions

View 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
View 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({});
};

View 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
View 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
View 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
View 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;