const Category = require('../models/Category'); const Election = require('../models/Election'); const {body, validationResult} = require('express-validator'); const {_sr} = require('../plugins/serverResponses'); const { res404, res500, checkValidations, generateRandomDigits, hasWhiteSpaces, isLatinCharactersWithSymbol } = require('../plugins/controllersHelperFunctions'); const fs = require('fs'); const sharp = require('sharp'); ////// variables const profileWidth = 300; const profileHeight = 400; const profileQuality = 100; const limit = 20; //// functions const _checkSpecPermission = async (req, category) => { return !req.user.specialPermissions.includes(category._id) && //check parent id exist in special permissions or not !req.user.specialPermissions.some(item => category?.treeCat?.join().includes(item)); } const _generatePass = async () => { let pass = generateRandomDigits(8); let checkPass = await Election.findOne({'voters.password': pass}); if (checkPass) return await _generatePass(); return pass; } /////////////////////////////////////////////////////////////////////// admin election module.exports.addElection = [ [ body('title') .notEmpty().withMessage(_sr['fa'].required.field), body('catId') .isMongoId().withMessage(_sr['fa'].required.field) .if((value, {req}) => { return req.body.catId }) .custom((value) => { return Category.findOne({ _id: value }) .then(category => { if (!category) return Promise.reject(_sr['fa'].not_found.category); else return true }) }), body('startDate') .notEmpty().withMessage(`لطفا یک تاریخ شروع تعیین کنید`), body('endDate') .notEmpty().withMessage(`لطفا یک تاریخ پایان تعیین کنید`), body('publish') .isBoolean().withMessage(_sr['fa'].format.boolean), ], checkValidations(validationResult), async (req, res) => { try { const { title, catId, description, startDate, endDate, publish } = req.body; const data = { title: title.trim(), catId, description, startDate, endDate, publish, _creator: req.user._id, _updatedBy: req.user._id } const category = await Category.findOne({ _id: catId }).populate({ path: 'parent', populate: { path: 'parent', model: 'Category' } }); if (!category) return res404(res, 'دسته بندی مورد نظر پیدا نشد'); /** check special permission */ if (req.user.specialPermissions.length && await _checkSpecPermission(req, category)) { return res.status(403).json({ message: `شما نمیتوانید این دسته بندی را انتخاب کنید` }); } data.allCat = category.treeCat; data.rootParent = category.rootParent || category._id; const createElection = await Election.create(data); if (!createElection) return res500(res, `مشکلی در اجرای درخواست شما پیش آمده است`); return res.json(createElection); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.updateElection = [ [ body('title') .notEmpty().withMessage(_sr['fa'].required.field), body('catId') .isMongoId().withMessage(_sr['fa'].required.field) .if((value, {req}) => { return req.body.catId }) .custom((value) => { return Category.findOne({ _id: value }) .then(category => { if (!category) return Promise.reject(_sr['fa'].not_found.category); else return true }) }), body('startDate') .notEmpty().withMessage(`لطفا یک تاریخ شروع تعیین کنید`), body('endDate') .notEmpty().withMessage(`لطفا یک تاریخ پایان تعیین کنید`), body('publish') .isBoolean().withMessage(_sr['fa'].format.boolean), ], checkValidations(validationResult), async (req, res) => { try { const electionId = req.params.id; const { title, catId, description, startDate, endDate, publish } = req.body; //// check election const targetElection = await Election.findById(electionId); if (!targetElection) return res404(res, `انتخابات مورد نظر پیدا نشد`); if (catId && targetElection.catId !== catId) { const category = await Category.findOne({ _id: catId }).populate({ path: 'parent', populate: { path: 'parent', model: 'Category' } }); if (!category) return res404(res, 'دسته بندی مورد نظر پیدا نشد'); /** check special permission */ if (req.user.specialPermissions.length && await _checkSpecPermission(req, category)) { return res.status(403).json({ message: `شما نمیتوانید این دسته بندی را انتخاب کنید` }); } targetElection.catId = catId; targetElection.allCat = category.treeCat; targetElection.rootParent = category.rootParent || category._id; } targetElection.title = title; targetElection.description = description; targetElection.startDate = startDate; targetElection.endDate = endDate; targetElection.publish = publish; targetElection._updatedBy = req.user._id; await targetElection.save(); return res.json({message: _sr['fa'].response.success_save}); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.getAllElection = [ async (req, res) => { try { let findQuery = {}; if (req.user.specialPermissions.length) { findQuery.catId = {$in: req.user.specialPermissions} } const allElection = await Election.paginate(findQuery, { page: req.query.page || 1, populate: [ {path: 'catId'}, { path: '_creator', select: { firstName: 1, lastName: 1, profilePic: 1 } } ], limit, sort: {created_at: -1} }); if (!allElection) return res500(res, `مشکلی در گرفتن اطلاعات پیش آمده است`); return res.json(allElection); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.getOneElection = [ async (req, res) => { try { const electionId = req.params.id; const findQuery = { _id: electionId }; const election = await Election.findOne(findQuery).populate('catId'); if (!election) return res404(res, `اطلاعات مورد نظر پیدا نشد`); if (req.user.specialPermissions.length && await _checkSpecPermission(req, election.catId)) { return res.status(403).json({ message: `شما مجاز به دیدن اطلاعات این انتخابات نمی باشید` }); } return res.json(election); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.deletOneElection = [ async (req, res) => { try { const electionId = req.params.id; const deleteElection = await Election.findById(electionId).populate('catId'); if (!deleteElection) return res404(res, `انتخابات مورد نظر پیدا نشد`); if (req.user.specialPermissions.length && await _checkSpecPermission(req, deleteElection.catId)) { return res.status(403).json({ message: `شما اجازه حذف این انتخابات را ندارید` }); } if (deleteElection.candidates.length) { const targetDir = `./static/uploads/images/election/`; const getFiles = fs.readdirSync(targetDir); for (const file of getFiles) { if (file.includes(deleteElection._id)) { fs.unlink(`./static/uploads/images/election/${file}`, (err) => { if (err) console.log(err) }); } } } await deleteElection.deleteOne(); return res.json({message: _sr['fa'].response.success_remove}); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.search = [ async (req, res) => { try { let query = {}; let populate = [ { path: '_creator', select: {firstName: 1, lastName: 1, profilePic: 1} }, { path: 'catId' } ]; query['$and'] = []; const { term, catId, publish, date } = req.body; /** search by term */ if (term && !_.isEmpty(term)) { query['$and'].push({ $or: [{ title: { $regex: '.*' + term + '.*' } }] }); } if (req.user.specialPermissions.length) { query.catId = {$in: req.user.specialPermissions} } else if (catId && !_.isEmpty(catId)) { query['$and'].push({ catId }); } /** search by publish */ if (!_.isUndefined(publish) && _.isBoolean(publish)) { query['$and'].push({ publish }); } /** search by date */ if (date && !_.isEmpty(date)) { if (date.length === 1) { query['$and'].push({ startDate: {$lte: new Date(date[0])}, endDate: {$gte: new Date(date[0])} }); } else { query['$and'].push({ startDate: {$lte: new Date(date[0])}, endDate: {$gte: new Date(date[1])} }); } } _.isEmpty(query['$and']) && delete query['$and']; // _.isEmpty(input['$or']) && delete input['$or']; /** get elections */ const elections = await Election.paginate(query, { page: req.query.page || 1, limit, populate, sort: {_id: -1} }); /** show result */ return res.json(elections); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] ///////////////////////////////////////////////////////////////////// admin candidate module.exports.addCandidate = [ [ body('firstName') .notEmpty().withMessage(_sr['fa'].required.first_name), body('lastName') .notEmpty().withMessage(_sr['fa'].required.last_name), body('nationalCode') .optional().isNumeric().withMessage(_sr['fa'].required.national_code) .bail() .isLength({min: 10, max: 10}).withMessage('کد ملی 10 رقمی می باشد'), ], checkValidations(validationResult), async (req, res) => { try { const electionId = req.params.id; const { firstName, lastName, bornDate, education, nationalCode, resume } = req.body; const data = { firstName, lastName, bornDate, education, nationalCode, resume, _creator: req.user._id, _updatedBy: req.user._id } if (!req?.files?.profilePic) { return res.status(422).json({ validation: {profilePic: {msg: `لطفا یک فایل ارسال کنید`}} }); } const targetElection = await Election.findById(electionId); if (!targetElection) return res404(res, 'انتخابات مورد نظر پیدا نشد'); /////// add pic 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 = 'candidate_' + Date.now() + '_' + targetElection._id + '.jpg'; data.profilePic = imageName; const target = sharp(image.data) await target .resize(profileWidth, profileHeight, { fit: 'cover' }) .toFormat('jpg') .jpeg({ quality: profileQuality }) .toFile(`./static/uploads/images/election/${imageName}`) targetElection.candidates.push(data); await targetElection.save(); return res.json({message: _sr['fa'].response.success_save}); } catch (error) { console.log(error.message) return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.updateCandidate = [ [ body('candidateId') .notEmpty().withMessage(_sr['fa'].required.field), body('firstName') .notEmpty().withMessage(_sr['fa'].required.first_name), body('lastName') .notEmpty().withMessage(_sr['fa'].required.last_name), body('nationalCode') .optional().isNumeric().withMessage(_sr['fa'].required.national_code) .bail() .isLength({min: 10, max: 10}).withMessage('کد ملی 10 رقمی می باشد'), ], checkValidations(validationResult), async (req, res) => { try { const electionId = req.params.id; const { candidateId, firstName, lastName, bornDate, education, nationalCode, resume } = req.body; const targetElection = await Election.findById(electionId); if (!targetElection) return res404(res, 'انتخابات مورد نظر پیدا نشد'); let candidate = targetElection.candidates.id(candidateId); if (!candidate) return res404(res, `کاندید مورد نظر پیدا نشد`); if (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 = 'candidate_' + Date.now() + '_' + targetElection._id + '.jpg'; const target = sharp(image.data) await target .resize(profileWidth, profileHeight, { fit: 'cover' }) .toFormat('jpg') .jpeg({ quality: profileQuality }) .toFile(`./static/uploads/images/election/${imageName}`) /** * if exist old pic delete it from file */ if (candidate.profilePic) { const oldPic = `./static${candidate.profilePic}`; if (fs.existsSync(oldPic)) { fs.unlink(oldPic, (err) => { if (err) return res500(res, err?.message); }); } } candidate.profilePic = imageName; } candidate.firstName = firstName; candidate.lastName = lastName; candidate.bornDate = bornDate; candidate.education = education; candidate.nationalCode = nationalCode; candidate.resume = resume; candidate._updatedBy = req.user._id; await targetElection.save(); return res.json({message: _sr['fa'].response.success_save}); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.deleteCandidate = [ async (req, res) => { try { const electionId = req.params.id; const candidateId = req.params.canId; const targetElection = await Election.findById(electionId); if (!targetElection) return res404(res, 'انتخابات مورد نظر پیدا نشد'); let candidate = targetElection.candidates.id(candidateId); if (!candidate) return res404(res, `کاندید مورد نظر پیدا نشد`); if (candidate.profilePic) { const oldPic = `./static${candidate.profilePic}`; if (fs.existsSync(oldPic)) { fs.unlink(oldPic, (err) => { if (err) return res500(res, err?.message); }); } } await targetElection.candidates.id(candidateId).remove(); await targetElection.save(); return res.json({message: _sr['fa'].response.success_remove}); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] ///////////////////////////////////////////////////////////////////// admin voter module.exports.addVoter = [ [ body('firstName') .notEmpty().withMessage(_sr['fa'].required.first_name), body('lastName') .notEmpty().withMessage(_sr['fa'].required.last_name), body('nationalCode') .notEmpty().withMessage(_sr['fa'].required.national_code) .bail() .isLength({min: 10, max: 10}).withMessage('کد ملی 10 رقمی می باشد'), 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 Election.findOne({'voters.username': value}) .then(user => { if (user) return Promise.reject(_sr['fa'].duplicated.username) else return true }) }), ], checkValidations(validationResult), async (req, res) => { try { const electionId = req.params.id; const { firstName, lastName, username, nationalCode, mobileNumber, job } = req.body; //// generate password // async function generatePass(){ // let pass = generateRandomDigits(8); // let checkPass = Election.findOne({'voters.password' : pass}); // // console.log(checkPass) // // if (checkPass) return await generatePass(); // // return pass; // } const data = { firstName, lastName, username, password: await _generatePass(), mobileNumber, nationalCode, job, _creator: req.user._id, _updatedBy: req.user._id } const targetElection = await Election.findById(electionId); if (!targetElection) return res404(res, 'انتخابات مورد نظر پیدا نشد'); targetElection.voters.push(data); await targetElection.save(); return res.json({message: _sr['fa'].response.success_save}); } catch (error) { console.log(error.message) return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.updateVoter = [ [ body('voterId') .notEmpty().withMessage(_sr['fa'].required.field), body('firstName') .notEmpty().withMessage(_sr['fa'].required.first_name), body('lastName') .notEmpty().withMessage(_sr['fa'].required.last_name), body('nationalCode') .notEmpty().withMessage(_sr['fa'].required.national_code), ], checkValidations(validationResult), async (req, res) => { try { const electionId = req.params.id; const { voterId, firstName, lastName, nationalCode, mobileNumber, job } = req.body; const targetElection = await Election.findById(electionId); if (!targetElection) return res404(res, 'انتخابات مورد نظر پیدا نشد'); let voter = targetElection.voters.id(voterId); if (!voter) return res404(res, `رای دهنده مورد نظر پیدا نشد`); voter.firstName = firstName; voter.lastName = lastName; voter.mobileNumber = mobileNumber; voter.job = job; voter.nationalCode = nationalCode; voter._updatedBy = req.user._id; await targetElection.save(); return res.json({message: _sr['fa'].response.success_save}); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.deleteVoter = [ async (req, res) => { try { const electionId = req.params.id; const voterId = req.params.voterId; const targetElection = await Election.findById(electionId); if (!targetElection) return res404(res, 'انتخابات مورد نظر پیدا نشد'); let voter = targetElection.voters.id(voterId); if (!voter) return res404(res, `کاربر مورد نظر پیدا نشد`); await targetElection.voters.id(voterId).remove(); await targetElection.save(); return res.json({message: _sr['fa'].response.success_remove}); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] //////////////////////////////////////////////////////////////////// auth voter module.exports.voterLogin = [ [ body('username') .notEmpty().withMessage(_sr['fa'].required.username), body('password') .notEmpty().withMessage(_sr['fa'].required.password) ], checkValidations(validationResult), async (req, res) => { try { const {username, password} = req.body; const user = await Election.findOne({voters: {$elemMatch: {username, password, voted: false}}}) if (!user) return res.status(422).json({validation: {username: {msg: _sr['fa'].not_found.password}}}); return res.json({token: user.voters.find(item => item.password === password)._id}); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.getVoterUser = [ async (req, res) => { try { const token = req.headers.authorization?.replace(/^Bearer\s/, ''); const election = await Election.findOne({'voters._id': token}); if (!election) return res.status(422).json({validation: {username: {msg: _sr['fa'].not_found.password}}}); const user = await election.voters.id(token); return res.json({user}); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] /////////////////////////////////////////////////////////////////////// public module.exports.getElectionByUser = [ async (req, res) => { try { if (!req.voter) return res.status(403).json({message: 'شما اجازه دیدن این صفحه را ندارید'}); const targetElection = await Election.findOne({voters: {$elemMatch: {_id: req.voter._id, voted: false}}}) return res.json(targetElection); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ] module.exports.addVote = [ [ body('electionId') .notEmpty().withMessage(_sr['fa'].required.field), body('candidateId') .notEmpty().withMessage(_sr['fa'].required.field) ], checkValidations(validationResult), async (req, res) => { try { if (!req.voter) return res.status(403).json({message: 'شما اجازه رای دادن ندارید'}); const {electionId, candidateId} = req.body; const targetElection = await Election.findById(electionId); if (!targetElection) return res404(res, 'انتخابات مورد نظر پیدا نشد'); const voter = await targetElection.voters.id(req.voter._id); if (!voter) return res.status(403).json({message: 'شما اجازه رای دادن ندارید'}); voter.voted = true; const candidate = await targetElection.candidates.id(candidateId); if (!candidate) return res404(res, 'کاندیدای انتخاب شده وجود ندارد'); const voteObj = { voter: voter._id, candidate: candidate._id } targetElection.votes.push(voteObj); await targetElection.save(); return res.json({message: 'رای شما با موفقیت ثبت شد'}); } catch (error) { return res500(res, `مشکلی در اجرای درخواست شما بوجود آمده است`); } } ]