Files
2025-02-16 13:39:13 +03:30

1473 lines
42 KiB
JavaScript

const User = require("../models/User");
const { body, validationResult } = require("express-validator");
const { rateLimit } = require("express-rate-limit");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const sharp = require("sharp");
const { secretKey } = require("./../authentication");
const { _sr } = require("../plugins/serverResponses");
const {
res404,
res500,
hasWhiteSpaces,
isLatinCharactersWithSymbol,
generateRandomDigits,
} = require("../plugins/controllersHelperFunctions");
const fs = require("fs");
const nodemailer = require("nodemailer");
const Category = require("../models/Category");
const Logger = require("../models/Logger");
const cacheService = require("../plugins/cache");
const { sms } = require("../plugins/sms");
////// variables
const profileWidth = 500;
const profileHeight = 500;
const profileQuality = 100;
const hostURL = "localhost:6688";
const limit = 20;
const select = "-password -token";
const limiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes
limit: 10,
standardHeaders: "draft-7",
legacyHeaders: false,
});
/** @todo add private to query */
/** auth --------------------------------------------- */
/////////////////////////////////////////////////////////////////////// login
module.exports.login = [
limiter,
[
body("username")
.if((value, { req }) => {
return req.body.password && req.body.password.length >= 4;
})
.custom((value, { req }) => {
var test = value;
return User.findOne({ username: value }).then((user) => {
if (!user) return Promise.reject(_sr["fa"].not_found.password);
else return true;
});
}),
body("password")
.notEmpty()
.withMessage(_sr["fa"].required.password)
.bail()
.isLength({ min: 4 })
.withMessage(_sr["fa"].min_char.min4),
body("remember_me")
.exists()
.withMessage(_sr["fa"].required.remember_me)
.bail()
.isBoolean()
.withMessage(_sr["fa"].format.boolean),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
try {
var { username, password, remember_me } = req.body;
/** get user */
var user = await User.findOne({
$or: [{ username: username }, { email: username }],
});
const logger = await Logger.findOne({ user: user._id });
if (!user.registration_done) {
return res.status(403).json({
message: `اکانت شما غیرفعال می باشد لطفا با ادمین تماس بگیرید`,
});
}
const isMatch = await bcrypt.compare(password, user.password);
/**check user */
if (user) {
/** check password */
if (isMatch) {
if (logger) {
await Logger.findOneAndUpdate(
{ user: user._id },
{ $set: { LastLogin: new Date() } },
{ new: true }
);
} else {
const newLogger = new Logger({
user: user,
LastLogin: new Date(),
});
await newLogger.save();
}
/** if have token and it true show result else generate new token */
if (user.token) {
jwt.verify(
user.token.replace(/^Bearer\s/, ""),
secretKey,
async (err, decoeded) => {
if (err || !decoeded) {
/** generate new token */
var token = await jwt.sign({ _id: user._id }, secretKey, {
expiresIn: remember_me ? "7d" : "1h",
});
user.token = token;
await user.save();
return res.json({ token: user.token });
} else {
return res.json({ token: user.token });
}
}
);
} else {
/** generate new token */
var token = await jwt.sign({ _id: user._id }, secretKey, {
expiresIn: remember_me ? "7d" : "1h",
});
user.token = token;
await user.save();
return res.json({ token: user.token });
}
} else {
return res.status(422).json({
validation: { username: { msg: _sr["fa"].not_found.password } },
});
}
} else {
return res.status(422).json({
validation: { username: { msg: _sr["fa"].not_found.password } },
});
}
} catch (error) {
return res500(res, error?.message);
}
},
];
/** otp --------------------------------------------- */
/////////////////////////////////////////////////////////////////////// otp
module.exports.otp = [
limiter,
[
body("phone")
.notEmpty()
.withMessage(_sr["fa"].required.phone_number)
.bail()
.isMobilePhone("fa-IR")
.withMessage(_sr["fa"].required.phone_number),
// .isLength({ min: 4 })
// .withMessage(_sr["fa"].min_char.min4),
],
async (req, res) => {
console.log("body", req.body);
try {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
const { phone } = req.body;
const user = await User.findOne({ mobileNumber: phone });
if (!user)
return res.status(406).json({
message: "شماره موبایل نامعتبر می باشد",
});
const existOtp = cacheService.get(`OTP:LOGIN:${phone}`);
if (existOtp)
return res.status(400).json({
message: "کد به شماره موبایل شما ارسال شده است",
ttl: (cacheService.getTtl(`OTP:LOGIN:${phone}`) - Date.now()) / 1000,
});
const otp = crypto.randomInt(10000, 99999);
console.log("==> otp :", otp);
const message = `کد ورود شما ${otp} میباشد. استانداری لغو11`;
/** save otp in cache */
cacheService.set(`OTP:LOGIN:${phone}`, otp, 120);
/** send sms to user */
const smsResult = await sms(phone, message);
if (!smsResult) {
console.log(smsResult);
return res500(res, `مشکلی در ارسال پیامک پیش آمده است`);
}
return res.status(200).json({
message: "کد به شماره موبایل شما ارسال شد",
});
} catch (error) {
return res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// verify otp
module.exports.verifyOtp = [
limiter,
[
body("phone")
.notEmpty()
.withMessage(_sr["fa"].required.phone_number)
.bail()
.isMobilePhone("fa-IR")
.withMessage(_sr["fa"].required.phone_number),
body("code")
.notEmpty()
.withMessage(_sr["fa"].required.otp_code)
.bail()
.isLength({ min: 5, max: 5 })
.withMessage(_sr["fa"].format.otp_length),
],
async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
const { phone, code } = req.body;
// Check if the OTP exists in the cache
const cachedOtp = cacheService.get(`OTP:LOGIN:${phone}`);
if (!cachedOtp) {
return res.status(400).json({
message: "کد وارد شده منقضی شده است یا نامعتبر می‌باشد",
});
}
// Verify if the provided OTP matches the cached OTP
if (cachedOtp.toString() !== code.trim()) {
return res.status(422).json({
validation: { code: { msg: "کد وارد شده نامعتبر می‌باشد" } },
});
}
// Find the user by phone number
const user = await User.findOne({ mobileNumber: phone });
if (!user) {
return res.status(406).json({
message: "شماره موبایل نامعتبر می باشد",
});
}
// Check if the user's registration is complete
if (!user.registration_done) {
return res.status(403).json({
message: `اکانت شما غیرفعال می باشد لطفا با ادمین تماس بگیرید`,
});
}
// Create or update login logger
const logger = await Logger.findOne({ user: user._id });
console.log("admin logged in", req.ip);
if (logger) {
await Logger.findOneAndUpdate(
{ user: user._id },
{ $set: { LastLogin: new Date(), ip: req.ip } },
{ new: true }
);
} else {
const newLogger = new Logger({
user: user,
LastLogin: new Date(),
});
await newLogger.save();
}
// Check if the user already has a valid token
if (user.token) {
jwt.verify(
user.token.replace(/^Bearer\s/, ""),
secretKey,
async (err, decoded) => {
if (err || !decoded) {
// Generate a new token if the existing one is invalid
const token = await jwt.sign({ _id: user._id }, secretKey, {
expiresIn: "2h",
});
user.token = token;
await user.save();
// Remove the OTP from the cache
cacheService.del(`OTP:LOGIN:${phone}`);
return res.json({ token: user.token });
} else {
// Remove the OTP from the cache
cacheService.del(`OTP:LOGIN:${phone}`);
return res.json({ token: user.token });
}
}
);
} else {
// Generate a new token if none exists
const token = await jwt.sign({ _id: user._id }, secretKey, {
expiresIn: "1h",
});
user.token = token;
await user.save();
// Remove the OTP from the cache
cacheService.del(`OTP:LOGIN:${phone}`);
return res.json({ token: user.token });
}
} catch (error) {
return res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// logout
module.exports.logout = [
async (req, res) => {
const token = req.headers.authorization?.replace(/^Bearer\s/, "");
if (token) {
User.findOneAndUpdate(
{ token: token },
{ token: null },
(err, oldData) => {
if (err || !oldData)
res.status(401).json({ message: "Your not logged in" });
else return res.json({ message: "You are logged out." });
}
);
} else {
return res.status(401).json({ message: "Your not logged in" });
}
},
];
/////////////////////////////////////////////////////////////////////// get user
module.exports.getuser = [
async (req, res) => {
const token = req.headers.authorization?.replace(/^Bearer\s/, "");
// check if token is not destroyed
var user = await User.findOne({ token: token }).select("-password");
if (!user) {
return res.status(401).json({ message: "unauthorized" });
}
if (!user.registration_done) {
return res.status(401).json({
message: `اکانت شما غیرفعال می باشد لطفا با ادمین تماس بگیرید`,
});
}
//verifies secret and checks if the token is expired
try {
var checkToken = await jwt.verify(
user.token.replace(/^Bearer\s/, ""),
secretKey
);
if (!checkToken) {
return res.status(401).json({ message: "unauthorized" });
}
return res.json({ user: user });
} catch (error) {
return res.status(401).json({ message: "unauthorized" });
}
},
];
/** end auth --------------------------------------------- */
/////////////////////////////////////////////////////////////////////// admin
module.exports.registerAdmin = [
[
body("firstName").notEmpty().withMessage(_sr["fa"].required.first_name),
body("lastName").notEmpty().withMessage(_sr["fa"].required.last_name),
body("mobileNumber")
.if((value, { req }) => {
return req.body.mobileNumber;
})
.custom((value, { req }) => {
if (!_.isEmpty(value) && !lodash.isNumeric(value)) {
return Promise.reject(_sr["fa"].format.number);
}
return User.findOne({ mobileNumber: value }).then((user) => {
if (user) return Promise.reject(_sr["fa"].duplicated.phoneNumber);
else return true;
});
}),
// body('email')
// .optional().isEmail().withMessage(_sr['fa'].format.email)
// .if((value, {req}) => {
// return req.body.email
// })
// .custom((value, {req}) => {
// return User.findOne({email: value})
// .then(user => {
// if (user) return Promise.reject(_sr['fa'].duplicated.email)
// else return true
// })
// }),
body("username")
.isLength({ min: 4 })
.withMessage(_sr["fa"].min_char.min4)
.bail()
.custom((value, { req }) => {
if (hasWhiteSpaces(value))
return Promise.reject(_sr["fa"].response.whiteSpace);
else return true;
})
.bail()
.custom((value, { req }) => {
if (!isLatinCharactersWithSymbol(value))
return Promise.reject(_sr["fa"].response.latinChar);
else return true;
})
.bail()
.custom((value, { req }) => {
return User.findOne({ username: value }).then((user) => {
if (user) return Promise.reject(_sr["fa"].duplicated.username);
else return true;
});
}),
body("password")
.isLength({ min: 4 })
.withMessage(_sr["fa"].min_char.min4)
.custom((value, { req }) => {
if (value === req.body.password_confirmation) return Promise.resolve();
else return Promise.reject(_sr["fa"].response.passwords_not_match);
}),
body("permissions")
.if((value, { req }) => {
return req.body.specialPermissions;
})
.custom((value, { req }) => {
var permission = _.isString(value) ? JSON.parse(value) : value;
if (!_.isArray(permission)) {
return Promise.reject(`این فیلد باید از نوع آرایه باشد`);
}
if (!permission.length) {
return Promise.reject(`شما باید یک دسترسی انتخاب کنید`);
}
if (
permission.length &&
!permission.includes("superAdmin") &&
!permission.includes("news") &&
!permission.includes("categories")
) {
return Promise.reject(
`شما باید همراه با دسترسی مخصوص، دسترسی مدیریت صفحات یا اخبار را ارسال کنید`
);
} else {
return true;
}
}),
body("specialPermissions")
.optional()
.custom((value, { req }) => {
var specialPermissions =
_.isString(value) && !_.isEmpty(value) ? JSON.parse(value) : value;
if (!_.isArray(specialPermissions)) {
return Promise.reject(`این فیلد باید از نوع آرایه باشد`);
}
return true;
}),
body("portals").custom((value, { req }) => {
var portals = _.isString(value) ? JSON.parse(value) : value;
if (!_.isArray(portals)) {
return Promise.reject(`این فیلد باید از نوع آرایه باشد`);
}
if (!portals.length) {
return Promise.reject(`شما باید محدوده دسترسی را مشخص کنید`);
}
let isValid = portals.every((item) => {
return portal.includes(item);
});
if (isValid) {
return true;
} else {
return Promise.reject(`${_sr["fa"].optional.onlyValid} => ${portal}`);
}
}),
body("registration_done").isBoolean().withMessage(_sr["fa"].format.boolean),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
try {
var {
username,
password,
firstName,
lastName,
email,
mobileNumber,
permissions,
specialPermissions,
registration_done,
portals,
} = req.body;
/** this code fix problem with array in form data */
permissions = _.isString(permissions)
? JSON.parse(permissions)
: permissions;
specialPermissions = _.isString(specialPermissions)
? JSON.parse(specialPermissions)
: specialPermissions;
portals = _.isString(portals) ? JSON.parse(portals) : portals;
var data = {
username,
firstName,
lastName,
email,
mobileNumber,
permissions,
registration_done,
private: false,
_creator: req.user?._id || null,
_updatedBy: req.user?._id || null,
specialPermissions: permissions.includes("superAdmin")
? []
: specialPermissions,
portals: [...new Set(portals)],
};
/**
* if we have pic upload to server
*/
if (req.files && req.files.profilePic) {
const image = req.files.profilePic;
if (!_sr.supportedImageFormats.includes(image.mimetype.split("/")[1])) {
return res.status(422).json({
validation: { profilePic: { msg: _sr["fa"].format.image } },
});
}
const imageName =
"user_" +
Date.now() +
"_" +
username +
"." +
image.mimetype.split("/")[1];
data.profilePic = imageName;
try {
const target = sharp(image.data);
await target
.resize(profileWidth, profileHeight, {
fit: "cover",
})
.jpeg({
quality: profileQuality,
})
.toFile(`./static/uploads/images/users/${imageName}`);
} catch (e) {
return res500(res, e?.message);
}
}
/**
* generate salt
*/
var salt = bcrypt.genSaltSync(10);
/**
* hash password with salt
*/
var hashPassword = bcrypt.hashSync(password, salt);
if (!hashPassword) {
return res500(res, `error in hash`);
}
data.password = hashPassword;
/**
* create new admin
*/
const newAdmin = await User.create(data);
/**
* if we have error show him
*/
if (!newAdmin) {
return res500(`error in register admin`);
}
/**
* show result
*/
return res.status(201).json({
message: _sr["fa"].response.success_save,
});
} catch (error) {
return res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// get user
module.exports.getUserByAdmin = [
async (req, res) => {
try {
const userId = req.params.id;
/** get one user */
const user = await User.findById(userId)
.populate("specialPermissions")
.select(select);
/** if not exist show message to client */
if (!user) {
return res404(res, `کاربر مورد نظر پیدا نشد`);
}
/** show result */
return res.json(user);
} catch (error) {
res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// get all user
module.exports.getAllUsers = [
async (req, res) => {
try {
/** get all user */
const users = await User.paginate(
{ private: false },
{ page: req.query.page, limit, select }
);
/** if not exist show message to client */
if (!users) {
return res500(
res,
`مشکلی در گرفتن اطلاعات پیش آمده لطفا دوباره تلاش کنید`
);
}
/** show result */
return res.json(users);
} catch (error) {
return res500(res, error?.message);
}
},
];
module.exports.changeUserStatus = [
async (req, res) => {
try {
const userId = req.params.id;
let user = await User.findOne({ private: false, _id: userId });
if (!user) {
return res404(res, `کاربر مورد نظر پیدا نشد`);
}
user.registration_done = !user.registration_done;
user.save();
/** show result */
return res.json(_sr["fa"].response.success_save);
} catch (error) {
return res500(res, `مشکلی در اجرای درخواست شما بوجود امده است`);
}
},
];
/////////////////////////////////////////////////////////////////////// delete one user
module.exports.deleteOneUser = [
async (req, res) => {
try {
const userId = req.params.id;
/** get one user */
const user = await User.findOne({ _id: userId });
/** if not exist show message to client */
if (!user) {
return res404(res, `کاربر مورد نظر پیدا نشد`);
}
if (user.private) {
return res
.status(403)
.json({ message: `شما نمیتوانید این کاربر را حذف کنید` });
}
/**
* if exist old pic delete it from file
*/
if (user.profilePic) {
var oldPic = `./static${user.profilePic}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
/** delete user */
await user.deleteOne();
/** show result */
return res.json(user);
} catch (error) {
res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// update user
module.exports.updateUser = [
[
body("firstName").notEmpty().withMessage(_sr["fa"].required.first_name),
body("lastName").notEmpty().withMessage(_sr["fa"].required.last_name),
body("mobileNumber")
.if((value, { req }) => {
return req.body.mobileNumber;
})
.custom((value, { req }) => {
if (!_.isEmpty(value) && !lodash.isNumeric(value)) {
return Promise.reject(_sr["fa"].format.number);
}
return true;
}),
// body('email')
// .optional().isEmail().withMessage(_sr['fa'].format.email),
body("permissions")
.if((value, { req }) => {
return req.body.specialPermissions;
})
.custom((value, { req }) => {
let permission = _.isString(value) ? JSON.parse(value) : value;
let specialPermissions = _.isString(req.body.specialPermissions)
? JSON.parse(req.body.specialPermissions)
: req.body.specialPermissions;
if (!_.isArray(permission)) {
return Promise.reject(`این فیلد باید از نوع آرایه باشد`);
}
if (!permission.length) {
return Promise.reject(`شما باید یک دسترسی انتخاب کنید`);
}
if (
specialPermissions.length !== 0 &&
!permission.includes("superAdmin") &&
!permission.includes("news") &&
!permission.includes("categories")
) {
return Promise.reject(
`شما باید همراه با دسترسی مخصوص، دسترسی مدیریت صفحات یا اخبار را ارسال کنید`
);
} else {
return true;
}
}),
body("specialPermissions")
.optional()
.custom((value, { req }) => {
var specialPermissions =
_.isString(value) && !_.isEmpty(value) ? JSON.parse(value) : value;
if (!_.isArray(specialPermissions)) {
return Promise.reject(`این فیلد باید از نوع آرایه باشد`);
}
return true;
}),
body("portals").custom((value, { req }) => {
var portals = _.isString(value) ? JSON.parse(value) : value;
if (!_.isArray(portals)) {
return Promise.reject(`این فیلد باید از نوع آرایه باشد`);
}
if (!portals.length) {
return Promise.reject(`شما باید محدوده دسترسی را مشخص کنید`);
}
let isValid = portals.every((item) => {
return portal.includes(item);
});
if (isValid) {
return true;
} else {
return Promise.reject(`${_sr["fa"].optional.onlyValid} => ${portal}`);
}
}),
body("registration_done").isBoolean().withMessage(_sr["fa"].format.boolean),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
try {
const userId = req.params.id;
var {
firstName,
lastName,
email,
mobileNumber,
permissions,
specialPermissions,
registration_done,
portals,
} = req.body;
/** this code fix problem with array in form data */
permissions = _.isString(permissions)
? JSON.parse(permissions)
: permissions;
specialPermissions = _.isString(specialPermissions)
? JSON.parse(specialPermissions)
: specialPermissions;
portals = _.isString(portals) ? JSON.parse(portals) : portals;
/** get one user */
var user = await User.findById(userId);
/** if not exist show message to client */
if (!user) {
return res404(res, `کاربر مورد نظر پیدا نشد`);
}
if (!user.private && !req.user.private) {
return res
.status(403)
.json({ message: `شما نمیتوانید این کاربر را آپدیت کنید` });
}
/** check new email is dupllicate or not */
if (user.email !== email) {
var checkDupplicate = await User.find({ email: email });
if (checkDupplicate.length) {
return res.status(422).json({
validation: { email: { msg: `${_sr["fa"].duplicated.email}` } },
});
}
}
/** check new mobileNumber is dupllicate or not */
if (user.mobileNumber !== mobileNumber) {
var checkDupplicate = await User.find({ mobileNumber: mobileNumber });
if (checkDupplicate.length) {
return res.status(422).json({
validation: {
mobileNumber: { msg: `${_sr["fa"].duplicated.phoneNumber}` },
},
});
}
}
user.firstName = firstName;
user.lastName = lastName;
user.password = user.password;
user.scope = user.scope;
user.token = user.token;
user.mobileNumber = mobileNumber;
user.email = email;
user.registration_done = registration_done;
user._creator = req.user._id;
user.username = user.username;
user.permissions = permissions;
user.specialPermissions = permissions?.includes("superAdmin")
? []
: specialPermissions;
user.private = user.private;
user.portals = [...new Set(portals)];
if (req.files && req.files.profilePic) {
const image = req.files.profilePic;
if (!_sr.supportedImageFormats.includes(image.mimetype.split("/")[1])) {
return res.status(422).json({
validation: { profilePic: { msg: _sr["fa"].format.image } },
});
}
const imageName =
"user_" +
Date.now() +
"_" +
user.username +
"." +
image.mimetype.split("/")[1];
try {
const target = sharp(image.data);
await target
.resize(profileWidth, profileHeight, {
fit: "cover",
})
.jpeg({
quality: profileQuality,
})
.toFile(`./static/uploads/images/users/${imageName}`);
/**
* if exist old pic delete it from file
*/
if (user.profilePic) {
var oldPic = `./static${user.profilePic}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
} catch (e) {
return res500(res, e?.message);
}
user.profilePic = imageName;
}
await user.save();
/** show result */
return res.json(user);
} catch (error) {
res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// change password
module.exports.changePassword = [
[
body("password")
.isLength({ min: 4 })
.withMessage(_sr["fa"].min_char.min4)
.custom((value, { req }) => {
if (value === req.body.password_confirmation) return Promise.resolve();
else return Promise.reject(_sr["fa"].response.passwords_not_match);
}),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
try {
const userId = req.params.id;
const password = req.body.password;
/** get one user */
var user = await User.findById(userId);
/** if not exist show message to client */
if (!user) {
return res404(res, `کاربر مورد نظر پیدا نشد`);
}
if (!user.private && !req.user.private) {
return res
.status(403)
.json({ message: `شما نمی توانید رمز عبور این کاربر را تغییر دهید` });
}
/**
* generate salt
*/
var salt = bcrypt.genSaltSync(10);
/**
* hash password with salt
*/
var hashPassword = bcrypt.hashSync(password, salt);
if (!hashPassword) {
return res500(res, `error in hash`);
}
user.password = hashPassword;
user.token = null;
await user.save();
/** show result */
return res.json({ message: _sr["fa"].response.success_save });
} catch (error) {
res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// get profile(get self)
module.exports.getProfile = [
async (req, res) => {
try {
const userId = req.user._id;
/** get one user */
const user = await User.findById(userId).select(select);
/** if not exist show message to client */
if (!user) {
return res404(res, `کاربر مورد نظر پیدا نشد`);
}
/** show result */
return res.json(user);
} catch (error) {
res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// update profile(get self)
module.exports.updateProfile = [
[
body("firstName").notEmpty().withMessage(_sr["fa"].required.first_name),
body("lastName").notEmpty().withMessage(_sr["fa"].required.last_name),
body("mobileNumber").isNumeric().withMessage(_sr["fa"].format.number),
// body('email')
// .isEmail().withMessage(_sr['fa'].format.email),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
try {
const userId = req.user._id;
const { firstName, lastName, email, mobileNumber } = req.body;
/** get one user */
var user = await User.findById(userId);
/** if not exist show message to client */
if (!user) {
return res404(res, `کاربر مورد نظر پیدا نشد`);
}
/** check new email is dupllicate or not */
if (user.email != email) {
var checkDupplicate = await User.find({ email: email });
if (checkDupplicate.length) {
return res.status(422).json({
validation: { email: { msg: `${_sr["fa"].duplicated.email}` } },
});
}
}
/** check new mobileNumber is dupllicate or not */
if (user.mobileNumber != mobileNumber) {
var checkDupplicate = await User.find({ mobileNumber: mobileNumber });
if (checkDupplicate.length) {
return res.status(422).json({
validation: {
mobileNumber: { msg: `${_sr["fa"].duplicated.phoneNumber}` },
},
});
}
}
user.firstName = firstName;
user.lastName = lastName;
user.password = user.password;
user.scope = user.scope;
user.token = user.token;
user.mobileNumber = mobileNumber;
user.email = email;
user.registration_done = user.registration_done;
user._creator = req.user._id;
user.username = user.username;
user.permissions = user.permissions;
user.specialPermissions = user.specialPermissions;
user.private = user.private;
/** if exist a picture */
if (req.files && req.files.profilePic) {
const image = req.files.profilePic;
if (!_sr.supportedImageFormats.includes(image.mimetype.split("/")[1])) {
return res.status(422).json({
validation: { profilePic: { msg: _sr["fa"].format.image } },
});
}
const imageName =
"user_" +
Date.now() +
"_" +
user.username +
"." +
image.mimetype.split("/")[1];
try {
const target = sharp(image.data);
await target
.resize(profileWidth, profileHeight, {
fit: "cover",
})
.jpeg({
quality: profileQuality,
})
.toFile(`./static/uploads/images/users/${imageName}`);
/**
* if exist old pic delete it from file
*/
if (user.profilePic) {
var oldPic = `./static${user.profilePic}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
} catch (e) {
return res500(res, e?.message);
}
user.profilePic = imageName;
}
await user.save();
/** show result */
return res.json(user);
} catch (error) {
res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// change password (profile)
module.exports.changePasswordProfile = [
[
body("password")
.isLength({ min: 4 })
.withMessage(_sr["fa"].min_char.min4)
.custom((value, { req }) => {
if (value === req.body.password_confirmation) return Promise.resolve();
else return Promise.reject(_sr["fa"].response.passwords_not_match);
}),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
try {
const userId = req.user._id;
const password = req.body.password;
/** get one user */
var user = await User.findById(userId);
/** if not exist show message to client */
if (!user) {
return res404(res, `کاربر مورد نظر پیدا نشد`);
}
/**
* generate salt
*/
var salt = bcrypt.genSaltSync(10);
/**
* hash password with salt
*/
var hashPassword = bcrypt.hashSync(password, salt);
if (!hashPassword) {
return res500(res, `مشکلی در تغییر پسورد پیش آمده است`);
}
user.password = hashPassword;
user.token = null;
await user.save();
/** show result */
return res.json({ message: _sr["fa"].response.success_save });
} catch (error) {
res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// forget password generate token
module.exports.forgetPassGenerateToken = [
[
body("email")
.optional()
.isEmail()
.withMessage(_sr["fa"].format.email)
.if((value, { req }) => {
return req.body.email;
})
.custom((value, { req }) => {
return User.findOne({ email: value }).then((user) => {
if (!user) return Promise.reject(_sr["fa"].not_found.email);
else return true;
});
}),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
try {
const email = req.body.email;
/** get one user */
var user = await User.findOne({ email: email });
/** if not exist show message to client */
if (!user) {
return res404(res, `کابر مورد نظر پیدا نشد`);
}
const token = crypto.randomBytes(20).toString("hex");
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let transporter = nodemailer.createTransport({
host: "www.rubynaturalco.com",
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: "recovery@rubynaturalco.com",
pass: "jd-m2?3ao?b(",
},
tls: {
rejectUnauthorized: false,
},
});
const emailHTMLTemplate = `
<div style="direction: rtl;background-color: #065495;overflow: hidden;">
<div style="width: 50%;border-radius: 10px;margin: 150px auto;background: #fff;text-align: center;padding: 20px;box-shadow: 0 15px 30px #000;">
<H1 style="color: #000;">برای بازیابی کلمه عبور خود روی لینک زیر کلیک کنید.</H1>
<div style="text-align: center;margin-top: 20px;">
<a href="http://${hostURL}/auth/resetpass-newpass/${token}"
target="_blank"
style="display: inline-block;padding: 15px;background: #065495;color: #fff;border-radius: 5px;text-decoration: none;">بازیابی کلمه عبور</a>
</div>
</div>
<span style="opacity: 0">${Date.now()}</span>
</div>
`;
// send mail with defined transport object
let info = await transporter.sendMail({
from: '"Ostandari Portal" <recovery@rubynaturalco.com>', // sender address
to: user.email, // list of receivers
subject: "لینک بازیابی کلمه عبور",
text: "کلمه عبور خود را بازیابی کنید",
html: emailHTMLTemplate,
});
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
user.token = token;
await user.save();
/** show result */
return res.json(token);
} catch (error) {
res500(res, error?.message);
}
},
];
/////////////////////////////////////////////////////////////////////// forget password get token and change pass
module.exports.forgetPassGetToken = [
[
body("password")
.isLength({ min: 4 })
.withMessage(_sr["fa"].min_char.min4)
.custom((value, { req }) => {
if (value === req.body.password_confirmation) return Promise.resolve();
else return Promise.reject(_sr["fa"].response.passwords_not_match);
}),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(422).json({ validation: errors.mapped() });
try {
var token = req.params.token;
var password = req.body.password;
var user = await User.findOne({ token: token });
if (!user) {
return res500(
res,
_sr["fa"].response.expired_reset_link
); /** @todo change response */
}
/**
* generate salt
*/
var salt = bcrypt.genSaltSync(10);
/**
* hash password with salt
*/
var hashPassword = bcrypt.hashSync(password, salt);
if (!hashPassword) {
return res500(
res,
`مشکلی در تغییر رمز عبور پیش آمده است`
); /** @todo change response */
}
user.password = hashPassword;
user.token = null;
await user.save();
/** show result */
return res.json({ message: _sr["fa"].response.success_save });
} catch (error) {
res500(res, error?.message);
}
},
];
////////////////////////////////////////////////////////////////////// search
module.exports.search = [
async (req, res) => {
try {
var query = { private: false };
query["$and"] = [];
var {
name,
email,
phone,
permissions,
specialPermissions,
portals,
status,
username,
} = req.body;
/** search by name and family */
if (name && !_.isEmpty(name)) {
query["$and"].push({
$or: [
{
firstName: {
$regex: ".*" + name + ".*",
},
lastName: {
$regex: ".*" + name + ".*",
},
},
],
});
}
/** search by email */
if (email && !_.isEmpty(email)) {
query["$and"].push({
email: email,
});
}
/** search by phone */
if (phone && !_.isEmpty(phone)) {
query["$and"].push({
mobileNumber: phone,
});
}
/** search by permissions */
if (permissions && !_.isEmpty(permissions)) {
query["$and"].push({
permissions: {
$in: permissions,
},
});
}
/** search by specialPermissions */
if (specialPermissions && !_.isEmpty(specialPermissions)) {
query["$and"].push({
specialPermissions: {
$in: specialPermissions,
},
});
}
/** search by portals */
if (portals && !_.isEmpty(portals)) {
query["$and"].push({
portals: {
$in: portals,
},
});
}
/** search by status */
if (!_.isUndefined(status) && _.isBoolean(status)) {
query["$and"].push({
registration_done: status,
});
}
/** search by username */
if (username && !_.isEmpty(username)) {
query["$and"].push({
username: username,
});
}
_.isEmpty(query["$and"]) && delete query["$and"];
// _.isEmpty(input['$or']) && delete input['$or'];
/** get users */
var users = await User.paginate(query, {
page: req.query.page,
limit,
select,
});
/** show result */
return res.json(users);
} catch (error) {
return res500(res, error?.message);
}
},
];