const crypto = require('crypto') const nodemailer = require('nodemailer') const bcrypt = require('bcryptjs') const { body, validationResult } = require('express-validator') const jwt = require('jsonwebtoken') const User = require('../models/User') const { secretKey } = require('../authentication') const permissions = require('../plugins/permissions') const { _sr } = require('../plugins/serverResponses') const sms = require('../plugins/SMS_Module') const { notifyAdmin } = require('../WebSocket/controllers/admin_EventsController') const { saveUserToArpa, updateUserOnArpa } = require('../ArpaWebservice.js') const { res404, res500, checkValidations, removeWhiteSpaces, checkMobileNumber, normalizeMobileNumber, nameOptimizer, checkNationalCode, isPersian, isPersianWithDigits, generateRandomDigits } = require('../plugins/controllersHelperFunctions') const _faSr = _sr.fa const transporter = nodemailer.createTransport({ host: 'smtp.c1.liara.email', port: 587, secure: false, // true for 465, false for other ports auth: { user: 'boring_bouman_81kd95', pass: 'ec9e96d3-6108-4ca5-97a9-39fff09d3557' }, tls: { rejectUnauthorized: false } }) /// ///////////////////////////////////////////////////////////////// verify account functions function sendConfirmationEmail(userId) { return new Promise(async (resolve, reject) => { try { const user = await User.findById(userId) if (!user) throw new Error('invalid id') const emailKey = generateRandomDigits(6) user.emailConfirmationKey = emailKey await user.save() /// //// email configs // const hostURL = 'www.asan-service.com' const emailHTMLTemplate = `

این ایمیل در وبسایت آسان سرویس ثبت شده است.

کد تایید آدرس ایمیل شما:

${emailKey}

` // send mail with defined transport object await transporter.sendMail({ from: '"AsanService" ', // sender address to: user.email, // list of receivers subject: 'تایید ایمیل', text: emailHTMLTemplate, html: emailHTMLTemplate }) resolve() } catch (err) { reject(err) } }) } function sendConfirmationSMS(userId) { return new Promise(async (resolve, reject) => { try { const user = await User.findById(userId) if (!user) throw new Error('invalid id') const mobileKey = generateRandomDigits(6) user.mobileConfirmationKey = mobileKey await user.save() await sms.auth.mobileVerificationCode(user.mobile_number, mobileKey, new Date()) resolve() } catch (err) { reject(err) } }) } /// //////////////////////////////////////////////////////////////////// admin accounts module.exports.register_admin = [ [ body('first_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2), body('last_name') .notEmpty() .withMessage(_faSr.required.last_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2), body('username') .notEmpty() .withMessage(_faSr.required.username) .bail() .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) .custom((value, { req }) => { return User.findOne({ username: value }).then(admin => { if (admin && admin.username === value) return Promise.reject(_faSr.duplicated.username) else return true }) }), body('password') .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) .custom((value, { req }) => { if (value === req.body.password_confirmation) return true else return Promise.reject(_faSr.response.passwords_not_match) }) ], checkValidations(validationResult), async (req, res) => { try { const { first_name, last_name, username, permissions, personalCode } = req.body const data = { first_name, last_name, username, permissions, personalCode } data._creator = req.user_id data.confirmed = true data.scope = ['admin'] const salt = await bcrypt.genSalt(10) const hash = await bcrypt.hash(req.body.password, salt) data.password = hash const admin = new User(data) await admin.save() return res.json({ message: _faSr.response.success_save }) } catch (err) { return res500(res, err) } } ] module.exports.update_admin = [ [ body('first_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2), body('last_name') .notEmpty() .withMessage(_faSr.required.last_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2), body('username') .notEmpty() .withMessage(_faSr.required.username) .bail() .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) .custom((value, { req }) => { return User.findOne({ username: value }).then(admin => { if (admin && admin.username === value && admin._id.toString() !== req.params.id) return Promise.reject(_faSr.duplicated.username) else return true }) }) ], checkValidations(validationResult), (req, res) => { const { username, first_name, last_name, permissions, personalCode } = req.body const data = { username, first_name, last_name, permissions, personalCode } data._creator = req.user_id User.findByIdAndUpdate(req.params.id, data, (err, oldData) => { if (err || !oldData) return res.status(404).json({ message: _faSr.not_found.user_id }) return res.json({ message: _faSr.response.success_activation }) }) } ] module.exports.get_admins = [ (req, res) => { const regex = /admin/ User.find({ scope: regex, private: false }, (err, admins) => { if (err) return res500(res, err) return res.json(admins) }) } ] module.exports.get_one_admin = [ (req, res) => { User.findById(req.params.id) .select('first_name last_name username permissions _creator personalCode') .populate('_creator', 'first_name last_name') .exec((err, admin) => { if (err || !admin) return res.status(404).json({ message: _faSr.not_found.user_id }) else return res.json(admin) }) } ] module.exports.delete_admin = [ (req, res) => { User.findByIdAndDelete(req.params.id, (err, user) => { if (err || !user) return res.status(404).json({ message: _faSr.not_found.user_id }) else return res.json({ message: _faSr.response.success_save }) }) } ] module.exports.permissions = [ (req, res) => { return res.status(200).json(permissions) } ] /// //////////////////////////////////////////////////////////////////// user accounts module.exports.register_user = [ [ body('first_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('last_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('national_code') .notEmpty() .withMessage(_faSr.required.national_code) .bail() .custom((value, { req }) => { if (checkNationalCode(value)) return true else return Promise.reject(_faSr.format.national_code) }) .bail() .custom((value, { req }) => { const nationalCode = value.trim() return User.findOne({ national_code: nationalCode }).then(user => { if (user && user.national_code === nationalCode) return Promise.reject(new Error('کاربر با این کد ملی از قبل وجود دارد')) else return true }) }), body('province_id').notEmpty().withMessage(_faSr.required.province), body('city_id').notEmpty().withMessage(_faSr.required.city), body('address') .notEmpty() .withMessage(_faSr.required.address) .bail() .custom((value, { req }) => { if (!isPersianWithDigits(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('postal_code') .notEmpty() .withMessage(_faSr.required.postal_code) .bail() .isNumeric() .withMessage(_faSr.format.number), body('email') .notEmpty() .withMessage(_faSr.required.email) .bail() .isEmail() .withMessage(_faSr.format.email) .bail() .custom((value, { req }) => { const email = value.trim().toLowerCase() return User.findOne({ email }).then(user => { if (user && user.email === email) return Promise.reject(_faSr.duplicated.email) else return true }) }), body('tel_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .isNumeric() .withMessage(_faSr.format.phone_number), body('mobile_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .custom((value, { req }) => { const mobileNStatus = checkMobileNumber(value) if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number) else return Promise.resolve() }) .bail() .custom((value, { req }) => { const mobileNumber = value.trim() return User.findOne({ mobile_number: mobileNumber }).then(user => { if (user && user.mobile_number === mobileNumber) return Promise.reject(new Error('کاربر با این شماره موبایل از قبل وجود دارد')) else return true }) }), body('password') .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) .custom((value, { req }) => { if (value === req.body.password_confirmation) return true else return Promise.reject(_faSr.response.passwords_not_match) }) ], checkValidations(validationResult), async (req, res) => { try { const { first_name, last_name, national_code, province_id, province_name, city_id, city_name, address, postal_code, tel_number, mobile_number, email, store_name, password } = req.body const data = { last_name, province_id, province_name, city_id, city_name, address, postal_code, tel_number, store_name } data.first_name = nameOptimizer(first_name) data.national_code = national_code.trim() data.mobile_number = normalizeMobileNumber(mobile_number) data.email = email.trim().toLowerCase() // hash user password and done const salt = await bcrypt.genSalt(10) const hash = await bcrypt.hash(password, salt) data.password = hash const user = new User(data) await user.save() await sendConfirmationEmail(user._id) await sendConfirmationSMS(user._id) return res.json({ message: _faSr.response.success_save }) } catch (err) { const registerFailurMsg = 'مشکلی در ثبت نام پیش آمده، لطفا مجددا تلاش کنید' res500(res, registerFailurMsg) } } ] module.exports.update_user = [ [ body('first_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('last_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('national_code') .notEmpty() .withMessage(_faSr.required.national_code) .bail() .custom((value, { req }) => { if (checkNationalCode(value)) return true else return Promise.reject(_faSr.format.national_code) }) .custom((value, { req }) => { const nationalCode = value.trim() return User.findOne({ national_code: nationalCode }).then(user => { if (user && user.national_code === nationalCode && user._id.toString() !== req.body._id) return Promise.reject(new Error('کاربر با این کد ملی از قبل وجود دارد')) else return true }) }), body('province_id').notEmpty().withMessage(_faSr.required.province), body('city_id').notEmpty().withMessage(_faSr.required.city), body('address') .notEmpty() .withMessage(_faSr.required.address) .bail() .custom((value, { req }) => { if (!isPersianWithDigits(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('postal_code') .notEmpty() .withMessage(_faSr.required.postal_code) .bail() .isNumeric() .withMessage(_faSr.format.number), body('tel_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .isNumeric() .withMessage(_faSr.format.phone_number), body('mobile_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .custom((value, { req }) => { const mobileNStatus = checkMobileNumber(value) if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number) else return Promise.resolve() }) .bail() .custom((value, { req }) => { const mobileNumber = value.trim() return User.findOne({ mobile_number: mobileNumber }).then(user => { if (user && user.mobile_number === mobileNumber && user._id.toString() !== req.body._id) return Promise.reject(new Error('کاربر با این شماره موبایل از قبل وجود دارد')) else return true }) }), body('email') .notEmpty() .withMessage(_faSr.required.email) .bail() .isEmail() .withMessage(_faSr.format.email) .bail() .custom((value, { req }) => { const email = value.trim().toLowerCase() return User.findOne({ email }).then(user => { if (user && user.email === email && user._id.toString() !== req.body._id) return Promise.reject(_faSr.duplicated.email) else return true }) }) ], checkValidations(validationResult), async (req, res) => { const { first_name, last_name, national_code, province_id, province_name, city_id, city_name, address, postal_code, tel_number, mobile_number, email, store_name } = req.body const MongoData = { last_name, province_id, province_name, city_id, city_name, address, postal_code, tel_number, store_name } MongoData.first_name = nameOptimizer(first_name) MongoData.national_code = national_code.trim() MongoData.mobile_number = normalizeMobileNumber(mobile_number) MongoData.email = email.trim().toLowerCase() try { const { user_id } = req const user = await User.findById(user_id) if (!user) return res404(res, _faSr.not_found.user_id) const newEmail = user.email !== MongoData.email const newMobile = user.mobile_number !== MongoData.mobile_number if (newEmail) user.email_confirmed = false if (newMobile) user.mobile_confirmed = false await updateUserOnArpa(MongoData, user.verity_businessID, 'verity') await updateUserOnArpa(MongoData, user.panatech_businessID, 'panatech') user.first_name = MongoData.first_name user.last_name = MongoData.last_name user.national_code = MongoData.national_code user.province_id = MongoData.province_id user.province_name = MongoData.province_name user.city_id = MongoData.city_id user.city_name = MongoData.city_name user.address = MongoData.address user.postal_code = MongoData.postal_code user.tel_number = MongoData.tel_number user.store_name = MongoData.store_name user.mobile_number = MongoData.mobile_number user.email = MongoData.email await user.save() if (newEmail) await sendConfirmationEmail(user_id) if (newMobile) await sendConfirmationSMS(user_id) return res.json({ message: _faSr.response.success_save, data: user }) } catch (e) { console.log('Arpa Error during update user --', e) return res500(res, _faSr.response.problem) } } ] module.exports.update_user_password = [ [ body('password') .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) .custom((value, { req }) => { if (value === req.body.password_confirmation) return true else return Promise.reject(_faSr.response.passwords_not_match) }) ], checkValidations(validationResult), async (req, res) => { // hash user password and done try { const { _id, password } = req.body const { user_id } = req const salt = await bcrypt.genSalt(10) const hash = await bcrypt.hash(password, salt) const user = await User.findById(_id) if (!user) throw new Error(_faSr.not_found.user_id) user.password = hash if (user.scope.includes('admin')) user._creator = user_id await user.save() return res.json({ message: _faSr.response.success_save }) } catch (err) { if (err) return res500(res, err) } } ] /// // reset user password module.exports.generate_reset_user_password_link = [ [ body('type') .notEmpty() .withMessage('choose type') .bail() .custom((value, { req }) => { if (value === 'email' || value === 'mobile') return Promise.resolve() else return Promise.reject(new Error('invalid type')) }), body('email') .if((value, { req }) => req.body.type === 'email') .notEmpty() .withMessage(_faSr.required.email) .bail() .isEmail() .withMessage(_faSr.format.email), body('mobile') .if((value, { req }) => req.body.type === 'mobile') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .custom((value, { req }) => { const mobileNStatus = checkMobileNumber(value) if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number) else return Promise.resolve() }) ], checkValidations(validationResult), async (req, res) => { try { const { type, email, mobile } = req.body const query = {} if (type === 'email') query.email = email.toLowerCase().trim() if (type === 'mobile') query.mobile_number = normalizeMobileNumber(mobile) console.log('queryyyy', query) const user = await User.findOne(query) if (!user) throw new Error('err') const resetPassKey = crypto.randomBytes(5).toString('hex') user.resetPassToken = resetPassKey const hostURL = 'www.asan-service.com' if (type === 'email') { const emailHTMLTemplate = `

برای بازیابی کلمه عبور حساب کاربری خود روی لینک زیر کلیک کنید.

بازیابی کلمه عبور

چنانچه قادر به کلیلک روی لینک بالا نیستید آدرس زیر را در مرورگر خود باز کنید.

https://${hostURL}/auth/resetpass-newpass/${resetPassKey}

` // send mail with defined transport object await transporter.sendMail({ from: '"AsanService" ', // sender address to: email, // list of receivers subject: 'لینک بازیابی کلمه عبور', text: emailHTMLTemplate, html: emailHTMLTemplate }) } else { // const resetLink = `asan-service.com/auth/resetpass-newpass/${resetPassKey}` const resetLink = `${resetPassKey}` console.log(resetLink) await sms.auth.passwordReset(user.mobile_number, resetLink, new Date()) } await user.save() let responseMsg = 'لینک بازیابی برای شما ارسال شد' if (type === 'email') responseMsg = 'لینک بازیابی برای شما ارسال شد، چنانچه قادر به مشاهده ایمیل در صندوق ورودی خود نیستید پوشه spam را چک کنید.' return res.json({ message: responseMsg }) } catch (err) { let errMsg = _faSr.not_found.user_id if (err.code === 'EENVELOPE' || err.code === 400) errMsg = 'مشکلی در ارسال ایمیل پیش آمد لطفا دوباره تلاش کنید' return res404(res, errMsg) } } ] module.exports.reset_password_using_token = [ [ body('password') .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) .custom((value, { req }) => { if (value === req.body.password_confirmation) return true else return Promise.reject(_faSr.response.passwords_not_match) }) ], checkValidations(validationResult), async (req, res) => { try { const { token, password } = req.body if (!token) throw new Error('err') const user = await User.findOne({ resetPassToken: token }) if (!user) throw new Error('err') const salt = await bcrypt.genSalt(10) const hash = await bcrypt.hash(password, salt) user.password = hash user.token = null user.resetPassToken = undefined await user.save() return res.json({ message: _faSr.response.success_save }) } catch (err) { return res404(res, _faSr.response.expired_reset_link) } } ] /// // confirm email module.exports.changeEmail = [ [ body('email') .notEmpty() .withMessage(_faSr.required.email) .bail() .isEmail() .withMessage(_faSr.format.email) .bail() .custom((value, { req }) => { const email = value.toLowerCase() return User.findOne({ email }).then(user => { if (user && user.email === email) return Promise.reject(_faSr.duplicated.email) else return true }) }) ], checkValidations(validationResult), async (req, res) => { try { const { user_id } = req const { email } = req.body const user = await User.findById(user_id) if (!user) throw new Error('invalid id') user.email = email user.email_confirmed = false await user.save() await sendConfirmationEmail(user_id) return res.json({ message: 'کد تایید برای شما ارسال شد' }) } catch (err) { return res500(res, err) } } ] module.exports.resendEmailConfirmation = [ async (req, res) => { try { const { user_id } = req await sendConfirmationEmail(user_id) return res.json({ message: 'کد تایید برای شما ارسال شد' }) } catch (err) { return res500(res, err) } } ] module.exports.confirmEmail = [ async (req, res) => { try { const { user_id } = req const { verificationKey } = req.body const user = await User.findById(user_id) if (!user) throw new Error('err') if (verificationKey?.trim() !== user.emailConfirmationKey) throw new Error('404') if (!user.confirmed) { user.confirmed = true const verityID = await saveUserToArpa(user, 'verity') const panatechID = await saveUserToArpa(user, 'panatech') user.verity_businessID = verityID.businessID user.verity_businessCode = verityID.businessCode user.panatech_businessID = panatechID.businessID user.panatech_businessCode = panatechID.businessCode } // save new info to arpa await updateUserOnArpa(user, user.verity_businessID, 'verity') await updateUserOnArpa(user, user.panatech_businessID, 'panatech') // update user info in mongoDB user.email_confirmed = true user.emailConfirmationKey = undefined await user.save() return res.json({ message: 'ایمل شما تایید شد.' }) } catch (e) { if (e === '404') return res404(res, 'کد تایید صحیح نمیباشد.') else return res500(res, 'مشکلی در تایید ایمیل پیش آمده، لطفا دوباره تلاش کنید') } } ] /// // confirm mobile module.exports.changeMobile = [ [ body('mobile') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .custom((value, { req }) => { const mobileNStatus = checkMobileNumber(value) if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number) else return Promise.resolve() }) .bail() .custom((value, { req }) => { return User.findOne({ mobile_number: value }).then(user => { if (user && user.mobile_number === value) return Promise.reject(new Error('کاربر با این شماره موبایل از قبل وجود دارد')) else return true }) }) ], checkValidations(validationResult), async (req, res) => { try { const { user_id } = req const { mobile } = req.body const user = await User.findById(user_id) if (!user) throw new Error('invalid id') user.mobile_number = mobile user.mobile_confirmed = false await user.save() await sendConfirmationSMS(user_id) return res.json({ message: 'کد تایید برای شما پیامک شد' }) } catch (err) { return res500(res, err) } } ] module.exports.resendMobileConfirmation = [ async (req, res) => { try { const { user_id } = req await sendConfirmationSMS(user_id) return res.json({ message: 'کد تایید برای شما پیامک شد' }) } catch (err) { return res500(res, err) } } ] module.exports.confirmMobile = [ async (req, res) => { try { const { user_id } = req const { verificationKey } = req.body const user = await User.findById(user_id) if (!user) throw new Error('err') if (verificationKey?.trim() !== user.mobileConfirmationKey) throw new Error('404') if (!user.confirmed) { user.confirmed = true const verityID = await saveUserToArpa(user, 'verity') const panatechID = await saveUserToArpa(user, 'panatech') user.verity_businessID = verityID.businessID user.verity_businessCode = verityID.businessCode user.panatech_businessID = panatechID.businessID user.panatech_businessCode = panatechID.businessCode } // save new info to arpa await updateUserOnArpa(user, user.verity_businessID, 'verity') await updateUserOnArpa(user, user.panatech_businessID, 'panatech') // update user info in mongoDB user.mobile_confirmed = true user.mobileConfirmationKey = undefined await user.save() return res.json({ message: 'شماره شما تایید شد.' }) } catch (e) { if (e === '404') return res404(res, 'کد تایید صحیح نمیباشد.') else return res500(res, 'مشکلی در تایید شماره پیش آمده، لطفا دوباره تلاش کنید') } } ] /// //////////////////////////////////////////////////////////////////// user managemnet by admin module.exports.add_user = [ [ body('first_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('last_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('national_code') .notEmpty() .withMessage(_faSr.required.national_code) .bail() .custom((value, { req }) => { if (checkNationalCode(value)) return true else return Promise.reject(_faSr.format.national_code) }) .bail() .custom((value, { req }) => { const nationalCode = value.trim() return User.findOne({ national_code: nationalCode }).then(user => { if (user && user.national_code === nationalCode) return Promise.reject(new Error('کاربر با این کد ملی از قبل وجود دارد')) else return true }) }), body('province_id').notEmpty().withMessage(_faSr.required.province), body('city_id').notEmpty().withMessage(_faSr.required.city), body('address') .notEmpty() .withMessage(_faSr.required.address) .bail() .custom((value, { req }) => { if (!isPersianWithDigits(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('postal_code') .notEmpty() .withMessage(_faSr.required.postal_code) .bail() .isNumeric() .withMessage(_faSr.format.number), body('email') .notEmpty() .withMessage(_faSr.required.email) .bail() .isEmail() .withMessage(_faSr.format.email) .bail() .custom((value, { req }) => { const email = value.trim().toLowerCase() return User.findOne({ email }).then(user => { if (user && user.email === email) return Promise.reject(_faSr.duplicated.email) else return true }) }), body('tel_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .isNumeric() .withMessage(_faSr.format.phone_number), body('mobile_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .custom((value, { req }) => { const mobileNStatus = checkMobileNumber(value) if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number) else return Promise.resolve() }) .bail() .custom((value, { req }) => { const mobileNumber = value.trim() return User.findOne({ mobile_number: mobileNumber }).then(user => { if (user && user.mobile_number === mobileNumber) return Promise.reject(new Error('کاربر با این شماره موبایل از قبل وجود دارد')) else return true }) }), body('password') .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) .custom((value, { req }) => { if (value === req.body.password_confirmation) return true else return Promise.reject(_faSr.response.passwords_not_match) }) ], checkValidations(validationResult), async (req, res) => { try { const { first_name, last_name, national_code, province_id, province_name, city_id, city_name, address, postal_code, tel_number, mobile_number, email, store_name, password } = req.body const data = { last_name, province_id, province_name, city_id, city_name, address, postal_code, tel_number, store_name } data.first_name = nameOptimizer(first_name) data.national_code = national_code.trim() data.mobile_number = normalizeMobileNumber(mobile_number) data.email = email.trim().toLowerCase() data.email_confirmed = true data.mobile_confirmed = true data.confirmed = true // hash user password and done const salt = await bcrypt.genSalt(10) const hash = await bcrypt.hash(password, salt) data.password = hash const user = new User(data) await user.save() const verityID = await saveUserToArpa(user, 'verity') const panatechID = await saveUserToArpa(user, 'panatech') user.verity_businessID = verityID.businessID user.verity_businessCode = verityID.businessCode user.panatech_businessID = panatechID.businessID user.panatech_businessCode = panatechID.businessCode // save new info to arpa await updateUserOnArpa(user, user.verity_businessID, 'verity') await updateUserOnArpa(user, user.panatech_businessID, 'panatech') await user.save() return res.status(200).json({ message: _faSr.response.success_save, user }) } catch (err) { const registerFailurMsg = 'مشکلی در ثبت نام پیش آمده، لطفا مجددا تلاش کنید' res500(res, registerFailurMsg) } } ] module.exports.get_all_users_for_admin = [ (req, res) => { User.find( { scope: ['user'], confirmed: true, verity_businessID: { $ne: null }, verity_businessCode: { $ne: null } }, (err, data) => { if (err) return res500(res, err) return res.status(200).json(data) } ) } ] module.exports.get_one_user_for_admin = [ (req, res) => { User.findById(req.params.id, (err, user) => { if (err || !user) return res404(res, err) if (!user.seenByAdmin) { user.seenByAdmin = true user.save(err => { if (err) console.log('get one user for admin - save seenByAdmin error :', err) notifyAdmin('updateNewCustomers') }) } return res.json(user) }) } ] module.exports.change_verity_info_by_admin = [ [ body('verity_businessCode').notEmpty().withMessage('کد مشتری نباید خالی باشد'), body('verity_businessID').notEmpty().withMessage('آیدی مشتری نباید خالی باشد') ], checkValidations(validationResult), async (req, res) => { try { const user = await User.findById(req.params?.id) if (!user) return res404(res, 'invalid user id') if (!user.old_verity_businessID && !user.old_verity_businessCode) { user.old_verity_businessID = user.verity_businessID user.old_verity_businessCode = user.verity_businessCode await user.save() } user.verity_businessID = req.body.verity_businessID user.verity_businessCode = req.body.verity_businessCode await user.save() return res.json({ message: _faSr.response.success_save }) } catch (err) { return res500(res, err) } } ] module.exports.change_panatech_info_by_admin = [ [ body('panatech_businessCode').notEmpty().withMessage('کد مشتری نباید خالی باشد'), body('panatech_businessID').notEmpty().withMessage('آیدی مشتری نباید خالی باشد') ], checkValidations(validationResult), async (req, res) => { try { const user = await User.findById(req.params?.id) if (!user) return res404(res, 'invalid user id') if (!user.old_panatech_businessID && !user.old_panatech_businessCode) { user.old_panatech_businessID = user.panatech_businessID user.old_panatech_businessCode = user.panatech_businessCode await user.save() } user.panatech_businessID = req.body.panatech_businessID user.panatech_businessCode = req.body.panatech_businessCode await user.save() return res.json({ message: _faSr.response.success_save }) } catch (err) { return res500(res, err) } } ] module.exports.update_user_by_admin = [ [ body('first_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('last_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('national_code') .notEmpty() .withMessage(_faSr.required.national_code) .bail() .custom((value, { req }) => { if (checkNationalCode(value)) return true else return Promise.reject(_faSr.format.national_code) }) .custom((value, { req }) => { const nationalCode = value.trim() return User.findOne({ national_code: nationalCode }).then(user => { if (user && user.national_code === nationalCode && user._id.toString() !== req.body._id) return Promise.reject(new Error('کاربر با این کد ملی از قبل وجود دارد')) else return true }) }), body('province_id').notEmpty().withMessage(_faSr.required.province), body('city_id').notEmpty().withMessage(_faSr.required.city), body('address') .notEmpty() .withMessage(_faSr.required.address) .bail() .custom((value, { req }) => { if (!isPersianWithDigits(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('postal_code') .notEmpty() .withMessage(_faSr.required.postal_code) .bail() .isNumeric() .withMessage(_faSr.format.number), body('tel_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .isNumeric() .withMessage(_faSr.format.phone_number), body('mobile_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .custom((value, { req }) => { const mobileNStatus = checkMobileNumber(value) if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number) else return Promise.resolve() }) .bail() .custom((value, { req }) => { const mobileNumber = value.trim() return User.findOne({ mobile_number: mobileNumber }).then(user => { if (user && user.mobile_number === mobileNumber && user._id.toString() !== req.body._id) return Promise.reject(new Error('کاربر با این شماره موبایل از قبل وجود دارد')) else return true }) }), body('email') .notEmpty() .withMessage(_faSr.required.email) .bail() .isEmail() .withMessage(_faSr.format.email) .bail() .custom((value, { req }) => { const email = value.trim().toLowerCase() return User.findOne({ email }).then(user => { if (user && user.email === email && user._id.toString() !== req.body._id) return Promise.reject(_faSr.duplicated.email) else return true }) }) ], checkValidations(validationResult), async (req, res) => { const { first_name, last_name, national_code, province_id, province_name, city_id, city_name, address, postal_code, tel_number, mobile_number, email, store_name } = req.body const MongoData = { last_name, province_id, province_name, city_id, city_name, address, postal_code, tel_number, store_name } MongoData.first_name = nameOptimizer(first_name) MongoData.national_code = national_code.trim() MongoData.mobile_number = normalizeMobileNumber(mobile_number) MongoData.email = email.trim().toLowerCase() try { const user_id = req.params.id const user = await User.findById(user_id) if (!user) { return res404(res, _faSr.not_found.user_id) } // check if need to ferify email or mobile const newEmail = user.email !== MongoData.email const newMobile = user.mobile_number !== MongoData.mobile_number if (newEmail) user.email_confirmed = false if (newMobile) user.mobile_confirmed = false // save new info to arpa await updateUserOnArpa(MongoData, user.verity_businessID, 'verity') await updateUserOnArpa(MongoData, user.panatech_businessID, 'panatech') // save new info to user user.first_name = MongoData.first_name user.last_name = MongoData.last_name user.national_code = MongoData.national_code user.province_id = MongoData.province_id user.province_name = MongoData.province_name user.city_id = MongoData.city_id user.city_name = MongoData.city_name user.address = MongoData.address user.postal_code = MongoData.postal_code user.tel_number = MongoData.tel_number user.store_name = MongoData.store_name user.mobile_number = MongoData.mobile_number user.email = MongoData.email await user.save() // send confirmation key after user saved if (newEmail) await sendConfirmationEmail(user_id) if (newMobile) await sendConfirmationSMS(user_id) return res.json({ message: _faSr.response.success_save }) } catch (e) { console.log('Arpa Error during update user --', e) return res500(res, _faSr.response.problem) } } ] module.exports.updateUserByAdmin = [ [ body('first_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('last_name') .notEmpty() .withMessage(_faSr.required.first_name) .bail() .isLength({ min: 2 }) .withMessage(_faSr.min_char.min2) .bail() .custom((value, { req }) => { if (removeWhiteSpaces(value).length < 2) return Promise.reject(_faSr.min_char.min2) if (!isPersian(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('national_code') .notEmpty() .withMessage(_faSr.required.national_code) .bail() .custom((value, { req }) => { if (checkNationalCode(value)) return true else return Promise.reject(_faSr.format.national_code) }) .custom((value, { req }) => { const nationalCode = value.trim() return User.findOne({ national_code: nationalCode }).then(user => { if (user && user.national_code === nationalCode && user._id.toString() !== req.body._id) return Promise.reject(new Error('کاربر با این کد ملی از قبل وجود دارد')) else return true }) }), body('province_id').notEmpty().withMessage(_faSr.required.province), body('city_id').notEmpty().withMessage(_faSr.required.city), body('address') .notEmpty() .withMessage(_faSr.required.address) .bail() .custom((value, { req }) => { if (!isPersianWithDigits(value)) return Promise.reject(new Error('لطفا با حروف فارسی تایپ کنید')) else return true }), body('postal_code') .notEmpty() .withMessage(_faSr.required.postal_code) .bail() .isNumeric() .withMessage(_faSr.format.number), body('tel_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .isNumeric() .withMessage(_faSr.format.phone_number), body('mobile_number') .notEmpty() .withMessage(_faSr.required.phone_number) .bail() .custom((value, { req }) => { const mobileNStatus = checkMobileNumber(value) if (!mobileNStatus) return Promise.reject(_faSr.format.phone_number) else return Promise.resolve() }) .bail() .custom((value, { req }) => { const mobileNumber = value.trim() return User.findOne({ mobile_number: mobileNumber }).then(user => { if (user && user.mobile_number === mobileNumber && user._id.toString() !== req.body._id) return Promise.reject(new Error('کاربر با این شماره موبایل از قبل وجود دارد')) else return true }) }), body('email') .notEmpty() .withMessage(_faSr.required.email) .bail() .isEmail() .withMessage(_faSr.format.email) .bail() .custom((value, { req }) => { const email = value.trim().toLowerCase() return User.findOne({ email }).then(user => { if (user && user.email === email && user._id.toString() !== req.body._id) return Promise.reject(_faSr.duplicated.email) else return true }) }) ], checkValidations(validationResult), async (req, res) => { const { first_name, last_name, national_code, province_id, province_name, city_id, city_name, address, postal_code, tel_number, mobile_number, email, store_name } = req.body const MongoData = { last_name, province_id, province_name, city_id, city_name, address, postal_code, tel_number, store_name } MongoData.first_name = nameOptimizer(first_name) MongoData.national_code = national_code.trim() MongoData.mobile_number = normalizeMobileNumber(mobile_number) MongoData.email = email.trim().toLowerCase() try { const user_id = req.params.id const user = await User.findById(user_id) if (!user) return res404(res, _faSr.not_found.user_id) // check if need to ferify email or mobile const newEmail = user.email !== MongoData.email const newMobile = user.mobile_number !== MongoData.mobile_number if (newEmail) user.email_confirmed = false if (newMobile) user.mobile_confirmed = false // save new info to arpa await updateUserOnArpa(MongoData, user.verity_businessID, 'verity') await updateUserOnArpa(MongoData, user.panatech_businessID, 'panatech') // save new info to user user.first_name = MongoData.first_name user.last_name = MongoData.last_name user.national_code = MongoData.national_code user.province_id = MongoData.province_id user.province_name = MongoData.province_name user.city_id = MongoData.city_id user.city_name = MongoData.city_name user.address = MongoData.address user.postal_code = MongoData.postal_code user.tel_number = MongoData.tel_number user.store_name = MongoData.store_name user.mobile_number = MongoData.mobile_number user.email = MongoData.email await user.save() // send confirmation key after user saved if (newEmail) await sendConfirmationEmail(user_id) if (newMobile) await sendConfirmationSMS(user_id) return res.json({ message: _faSr.response.success_save }) } catch (e) { console.log('Arpa Error during update user --', e) return res500(res, _faSr.response.problem) } } ] /// //////////////////////////////////////////////////////////////////// authentication /// //////// Login module.exports.webLogin = [ [ body('username') .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) .bail() .if((value, { req }) => { return req.body.password && req.body.password.length >= 4 }) .custom((value, { req }) => { return User.findOne({ $or: [{ email: value.toLowerCase().trim() }, { national_code: value }, { username: value }] }).then(user => { if (!user) return Promise.reject(_faSr.not_found.password) else return true }) }), body('password') .notEmpty() .withMessage(_faSr.required.password) .bail() .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4), body('remember_me') .exists() .withMessage(_faSr.required.remember_me) .bail() .isBoolean() .withMessage(_faSr.format.boolean) ], checkValidations(validationResult), async (req, res) => { try { const { username, password } = req.body const user = await User.findOne({ $or: [{ email: username.toLowerCase().trim() }, { national_code: username }, { username }] }) if (!user) throw new Error('err') const passwordMatch = await bcrypt.compare(password, user.password) if (!passwordMatch) throw new Error('err') const token = await jwt.sign({ _id: user._id }, secretKey, { expiresIn: req.body.remember_me ? '30d' : '7d' }) user.token = token await user.save() return res.json({ token }) } catch (err) { return res.status(422).json({ validation: { username: { msg: _faSr.not_found.password } } }) } } ] module.exports.appLogin = [ [ body('username') .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) .bail() .if((value, { req }) => { return req.body.password && req.body.password.length >= 4 }) .custom((value, { req }) => { return User.findOne({ $or: [{ email: value.toLowerCase().trim() }, { national_code: value }] }).then(user => { if (!user) return Promise.reject(_faSr.not_found.password) else return true }) }), body('password') .notEmpty() .withMessage(_faSr.required.password) .bail() .isLength({ min: 4 }) .withMessage(_faSr.min_char.min4) ], checkValidations(validationResult), async (req, res) => { try { const { username, password } = req.body const user = await User.findOne({ $or: [{ email: username.toLowerCase().trim() }, { national_code: username }] }) if (!user) throw new Error('err') const passwordMatch = await bcrypt.compare(password, user.password) if (!passwordMatch) throw new Error('err') const token = await jwt.sign({ _id: user._id }, secretKey, { expiresIn: '60d' }) user.appToken = token await user.save() return res.json({ token }) } catch (err) { return res.status(422).json({ validation: { username: { msg: _faSr.not_found.password } } }) } } ] /// //////// logout module.exports.webLogout = [ async (req, res) => { try { const token = req.headers?.authorization if (!token) throw new Error('err') const user = await User.findOne({ token }) if (!user) throw new Error('err') user.token = null await user.save() return res.json({ message: _faSr.response.logged_out }) } catch (err) { return res.status(401).json({ message: _faSr.response.not_logged_in }) } } ] module.exports.appLogout = [ async (req, res) => { try { const token = req.headers?.authorization if (!token) throw new Error('err') const user = await User.findOne({ appToken: token }) if (!user) throw new Error('err') user.appToken = null await user.save() return res.json({ message: _faSr.response.logged_out }) } catch (err) { return res.status(401).json({ message: _faSr.response.not_logged_in }) } } ] /// //////// get user module.exports.getUser = [ async (req, res) => { try { const token = req.headers?.authorization if (!token) throw new Error('err') const decoded = jwt.verify(token, secretKey) if (!decoded) throw new Error('err') const user = await User.findById(decoded?._id).select( '-token -appToken -emailConfirmationKey -mobileConfirmationKey -password -resetPassToken' ) return res.status(200).json({ user }) } catch (err) { console.log('🚀 ~ file: userController.js ~ line 1580 ~ err', err) return res.status(401).json({ message: 'unauthenticated' }) } } ]