diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml deleted file mode 100644 index a6a5fdd..0000000 --- a/.github/workflows/deploy.yaml +++ /dev/null @@ -1,42 +0,0 @@ -name: Deploy Checker -on: - push: - branches: - - main -jobs: - deployment: - runs-on: ubuntu-latest - steps: - - name: Set up environment - run: | - echo "COMMIT_MESSAGE=$(echo '${{ github.event.head_commit.message }}' | jq -aRs .)" >> $GITHUB_ENV - - - name: Deploy Stage - uses: fjogeleit/http-request-action@v1 - with: - url: 'https://canary.discord.com/api/webhooks/1266738294226554953/xLG8rouRFIAJJGSgroDyjorzkh1vxn6b2QqlLM44Y1rONt8F0rGkAB5v5VKY1h_znQW6' - method: 'POST' - customHeaders: '{"Content-Type": "application/json"}' - data: '{ - "content": " Github push ${{ github.repository }} / ${{ github.event.head_commit.timestamp }} / ${{github.event.head_commit.author.name}}", - "embeds": [ - { - "title": "${{ github.repository }}", - "description": "Git checker", - "url": "http://github.com/${{ github.repository }}", - "color": 5814783, - "fields": [ - { - "name": "${{github.event.head_commit.author.name}}", - "value": ${{ env.COMMIT_MESSAGE }} - } - ], - "author": { - "name": "${{ github.actor }}" - }, - "timestamp": "${{ github.event.head_commit.timestamp }}" - } - ], - "username": "${{github.event.head_commit.author.name}}", - "attachments": [] - }' diff --git a/.github/workflows/liara.yaml b/.github/workflows/liara.yaml index 531ad93..59a5adb 100644 --- a/.github/workflows/liara.yaml +++ b/.github/workflows/liara.yaml @@ -7,14 +7,81 @@ jobs: deploy: runs-on: ubuntu-latest + env: + DISCORD_WEBHOOK_URL: 'https://canary.discord.com/api/webhooks/1266738294226554953/xLG8rouRFIAJJGSgroDyjorzkh1vxn6b2QqlLM44Y1rONt8F0rGkAB5v5VKY1h_znQW6' + LIARA_TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySUQiOiI2NGVkY2ZmYWJiODUyOWZmNWFmOTU3YmEiLCJpYXQiOjE3MjAyNzUyNDB9.WNsgMX03tD1rZaceKRgjQ_MCffvW8oOQ7CnT_NdYP30 + APP_NAME: asanservice + steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: "18" + - name: Set up environment + run: | + echo "COMMIT_MESSAGE=$(echo '${{ github.event.head_commit.message }}' | jq -aRs .)" >> $GITHUB_ENV - name: update-liara - env: - LIARA_TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySUQiOiI2NGVkY2ZmYWJiODUyOWZmNWFmOTU3YmEiLCJpYXQiOjE3MjAyNzUyNDB9.WNsgMX03tD1rZaceKRgjQ_MCffvW8oOQ7CnT_NdYP30 + run: | npm i -g @liara/cli@5 - liara deploy --app="asanservice" --api-token="$LIARA_TOKEN" --no-app-logs \ No newline at end of file + liara deploy --app="$APP_NAME" --api-token="$LIARA_TOKEN" --no-app-logs + - name: Deploy Stage + if: success() + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.DISCORD_WEBHOOK_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{ + "content": " Github push ${{ github.repository }} / ${{ github.event.head_commit.timestamp }} / ${{github.event.head_commit.author.name}}", + "embeds": [ + { + "title": "${{ github.repository }}", + "description": "Success deploy", + "url": "http://github.com/${{ github.repository }}", + "color": 32768, + "fields": [ + { + "name": "${{github.event.head_commit.author.name}}", + "value": ${{ env.COMMIT_MESSAGE }} + } + ], + "author": { + "name": "${{ github.actor }}" + }, + "timestamp": "${{ github.event.head_commit.timestamp }}" + } + ], + "username": "${{github.event.head_commit.author.name}}", + "attachments": [] + }' + - name: Deploy Stage + if: failure() + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.DISCORD_WEBHOOK_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{ + "content": " Failed to deploy ${{ github.repository }} / ${{ github.event.head_commit.timestamp }} / ${{github.event.head_commit.author.name}}", + "embeds": [ + { + "title": "${{ github.repository }}", + "description": "Failed to deploy", + "url": "http://github.com/${{ github.repository }}", + "color": 16711680, + "fields": [ + { + "name": "${{github.event.head_commit.author.name}}", + "value": ${{ env.COMMIT_MESSAGE }} + } + ], + "author": { + "name": "${{ github.actor }}" + }, + "timestamp": "${{ github.event.head_commit.timestamp }}" + } + ], + "username": "${{github.event.head_commit.author.name}}", + "attachments": [] + }' \ No newline at end of file diff --git a/bun.lockb b/bun.lockb new file mode 100644 index 0000000..55c24e2 Binary files /dev/null and b/bun.lockb differ diff --git a/package-lock.json b/package-lock.json index 07f9bd4..4e0da68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "nuxt-leaflet": "^0.0.27", "read-excel-file": "^5.2.2", "socket.io": "^4.4.1", + "validator": "^13.12.0", "vue-persian-datetime-picker": "^2.10.3" }, "devDependencies": { @@ -20668,9 +20669,9 @@ } }, "node_modules/validator": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", - "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", "engines": { "node": ">= 0.10" } diff --git a/package.json b/package.json index be9cd69..d835fa0 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "nuxt-leaflet": "^0.0.27", "read-excel-file": "^5.2.2", "socket.io": "^4.4.1", + "validator": "^13.12.0", "vue-persian-datetime-picker": "^2.10.3" }, "devDependencies": { diff --git a/server/WebSocket/controllers/admin_EventsController.js b/server/WebSocket/controllers/admin_EventsController.js index 458a01a..da300e8 100644 --- a/server/WebSocket/controllers/admin_EventsController.js +++ b/server/WebSocket/controllers/admin_EventsController.js @@ -7,11 +7,15 @@ const OldPieceRequest = require('../../models/OldPieceRequest') const BufferRequest = require('../../models/BufferRequest') const GuaranteeReport = require('../../models/GuaranteeReport') const Complaint = require('../../models/Complaint') - +const Withdraw = require('../../models/GPS.Withdraw'); +const RequestGPS = require('../../models/GPS.Request') +const AwardRequest = require('../../models/GPS.AwardRequest'); +const InstalledDevice = require('../../models/GPS.InstalledDevice'); module.exports.getAllNotifs = async socket => { try { const allData = await Promise.all([ + // unreadCuMsgs ContactPageMessage.countDocuments({ read: false }), @@ -36,7 +40,19 @@ module.exports.getAllNotifs = async socket => { // newGuaranteeReports GuaranteeReport.countDocuments({ status: 'sent', seen: false }), - Complaint.countDocuments({ read: false }) + Complaint.countDocuments({ read: false }), + + // unread withdraw request + Withdraw.countDocuments({ status: 0 }), + + // unread GPS request + RequestGPS.countDocuments({ status: 0 }), + + // unread award request + AwardRequest.countDocuments({ status: 0 }), + + // unread install device request + InstalledDevice.countDocuments({ status: 0 }) ]) const unreadCuMsgs = allData[0] @@ -48,6 +64,10 @@ module.exports.getAllNotifs = async socket => { const newBufferRequests = allData[6] const newGuaranteeReports = allData[7] const newComplaintReports = allData[8] + const newWithdrawRequest = allData[9] + const newGpsRequest = allData[10] + const newAwardRequest = allData[11] + const newInstallDeviceRequest = allData[12] socket.emit(`admin:allNotifs`, { @@ -59,7 +79,11 @@ module.exports.getAllNotifs = async socket => { newOldPieceRequests, newBufferRequests, newGuaranteeReports, - newComplaintReports + newComplaintReports, + newWithdrawRequest, + newGpsRequest, + newAwardRequest, + newInstallDeviceRequest }) } catch (e) { console.log(e) diff --git a/server/_env.js b/server/_env.js index f2fea60..1d75de1 100644 --- a/server/_env.js +++ b/server/_env.js @@ -1,6 +1,7 @@ const EnvirementVariables = { // node envirements - isDev: process.env.NODE_ENV === 'development', + // isDev: process.env.NODE_ENV === 'development', + isDev: true, isProduction: process.env.NODE_ENV === 'production', // server ports diff --git a/server/authentication.js b/server/authentication.js index 7bbf587..1289045 100644 --- a/server/authentication.js +++ b/server/authentication.js @@ -25,13 +25,31 @@ const authentication = { async isAdmin(req, res, next) { try { const token = req.headers?.authorization - if (!token) throw new Error('err') + if (!token) throw new Error('err1') const decoded = jwt.verify(token.replace(/^Bearer\s/, ''), authentication.secretKey) - if (!decoded) throw new Error('err') + if (!decoded) throw new Error('err2') const user = await User.findById(decoded._id) - if (!user || !user.scope.includes('admin')) throw new Error('err') + if (!user || !user.scope.includes('admin')) throw new Error('err3') + + req.user_id = decoded._id + return next() + } catch (err) { + return res401(res, 'unauthorized') + } + }, + // check if admin logged in (middleware) + async isGPS(req, res, next) { + try { + const token = req.headers?.authorization + if (!token) throw new Error('err1') + + const decoded = jwt.verify(token.replace(/^Bearer\s/, ''), authentication.secretKey) + if (!decoded) throw new Error('err2') + + const user = await User.findById(decoded._id) + if (!user || !user.scope.includes('gps')) throw new Error('err3') req.user_id = decoded._id return next() @@ -54,6 +72,7 @@ const authentication = { req.user_id = decoded._id return next() } catch (err) { + return res401(res, 'unauthorized') } }, diff --git a/server/constants.js b/server/constants.js new file mode 100644 index 0000000..6ba4bcb --- /dev/null +++ b/server/constants.js @@ -0,0 +1,10 @@ +exports.constants = { + VALIDATION_ERROR: 400, + UNAUTHORIZED: 401, + FORBIDOEN: 403, + NOT_FOUND: 404, + SERVER_ERROR: 500, + SITE_UNAUTHORIZED: 422, + TOO_MANY: 429, + Method_Not_Allowed:405 +}; diff --git a/server/controllers/GPS.Auth.js b/server/controllers/GPS.Auth.js new file mode 100644 index 0000000..16b1e3b --- /dev/null +++ b/server/controllers/GPS.Auth.js @@ -0,0 +1,64 @@ +const bcrypt = require('bcryptjs') +const jwt = require('jsonwebtoken') +const { body, validationResult } = require('express-validator') +const { secretKey } = require('../authentication') +const { _sr } = require('../plugins/serverResponses') +const { checkValidations } = require('../plugins/controllersHelperFunctions') +const User = require('../models/User') + +module.exports.login = [ + [ + body('username') + .isLength({ min: 4 }) + .withMessage(_sr.fa.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(_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) + ], + 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(_sr.fa.not_found.password) + const passwordMatch = await bcrypt.compare(password, user.password) + if (!passwordMatch) throw new Error(_sr.fa.not_found.password) + const token = await jwt.sign({ _id: user._id }, secretKey, { + expiresIn: req.body.remember_me ? '30d' : '7d' + }) + if(!user.scope.includes('gps')){ + throw new Error('شما به این بخش دسترسی ندارید') + } + user.token = token + await user.save() + return res.status(200).json({ token }) + } catch (err) { + return res.status(422).json({ + validation: { username: { msg: err.message } } + }) + } + } + ] \ No newline at end of file diff --git a/server/controllers/GPS.Award.js b/server/controllers/GPS.Award.js new file mode 100644 index 0000000..54c83b5 --- /dev/null +++ b/server/controllers/GPS.Award.js @@ -0,0 +1,84 @@ +const { body, validationResult, param } = require('express-validator'); +const { _sr } = require('../plugins/serverResponses'); +const { checkValidations } = require('../plugins/controllersHelperFunctions'); +const Award = require('../models/GPS.Award'); + + +module.exports.createAward = [ + [ + body('score').notEmpty().isInt().withMessage(_sr.fa.required.field), + body('title').notEmpty().isString().withMessage(_sr.fa.required.field), + body('desc').notEmpty().isString().withMessage(_sr.fa.required.field), + body('expireDate').notEmpty().withMessage(_sr.fa.required.field), + ], + checkValidations(validationResult), + async (req, res) => { + const { score, title, desc, expireDate } = req.body; + const fixDate = new Date(expireDate) + const award = await Award.create({ score, title, desc, expireDate:fixDate }) + res.status(201).json({ + msg: _sr.fa.response.success_save, + data: award + }) + + } +] + + +module.exports.findAllUser = async (req, res) => { + const toDay = new Date() + const data = await Award.find({expireDate: {$gt: toDay}}) + res.status(200).json(data) +} + +module.exports.findAllAdmin = async (req, res) => { + const data = await Award.find({}) + res.status(200).json(data) +} + +module.exports.findOne = [ + [ + param('id').notEmpty().isMongoId().withMessage('ایدی را وارد یا تصحیح کنید') + ], + checkValidations(validationResult), + async (req, res) => { + const data = await Award.findById(req.params.id) + if (!data) { + res.status(404).json({ msg: _sr.fa.not_found.item_id }) + } else { + res.status(200).json(data) + } + + } +] + +module.exports.update =[ + [ + param('id').notEmpty().isMongoId().withMessage('ایدی را وارد یا تصحیح کنید'), + body('score').notEmpty().isInt().withMessage(_sr.fa.required.field), + body('title').notEmpty().isString().withMessage(_sr.fa.required.field), + body('desc').notEmpty().isString().withMessage(_sr.fa.required.field), + body('expireDate').notEmpty().withMessage(_sr.fa.required.field), + ], + checkValidations(validationResult), + async (req, res) => { + const { score, title, desc, expireDate } = req.body; + const data = await Award.findById(req.params.id) + if (!data) { + res.status(404).json({ msg: _sr.fa.not_found.item_id }) + } else { + data.score = score; + data.title = title; + data.desc = desc; + data.expireDate = new Date(expireDate) + data.save() + res.status(200).json({ + msg: _sr.fa.response.success_save, + data + }) + } + + } +] + + diff --git a/server/controllers/GPS.AwardRequest.js b/server/controllers/GPS.AwardRequest.js new file mode 100644 index 0000000..cdeda43 --- /dev/null +++ b/server/controllers/GPS.AwardRequest.js @@ -0,0 +1,93 @@ +const { body, param, validationResult } = require('express-validator') +const { _sr } = require('../plugins/serverResponses') +const { checkValidations } = require('../plugins/controllersHelperFunctions') +const User = require('../models/User') +const Award = require('../models/GPS.Award'); +const AwardRequest = require('../models/GPS.AwardRequest'); + + +module.exports.sendRequest = [ + [ + body('awardId').notEmpty().isMongoId().withMessage(_sr.fa.required.field) + ], + checkValidations(validationResult), + async(req, res)=>{ + const award = await Award.findById(req.body.awardId) + if (!award) { + res.status(404).json({ msg: _sr.fa.not_found.item_id }) + }else if(award.expireDate < new Date()){ + res.status(404).json({ msg: _sr.fa.not_found.item_id }) + }else{ + const user = await User.findById(req.user_id) + if(award.score <= user.score){ + user.score -= award.score; + user.save() + const data = await AwardRequest.create({ + awardId:req.body.awardId, + userId:req.user_id + }) + res.status(200).json({ msg:_sr.fa.response.success_save, data}) + }else{ + res.status(400).json({ msg: "امتیاز شما برای این مورد کافی نیست" }) + + } + + } + + } +] + +module.exports.findAllUser = async(req, res)=>{ + const data = await AwardRequest.find({userId:req.user_id}).populate('awardId','-created_at -updated_at') + res.status(200).json(data) +} + +module.exports.findAllAdmin = async(req, res)=>{ + const data = await AwardRequest.find().populate('awardId','-created_at -updated_at').populate('userId', 'first_name last_name shopName shopNumber shopAddress national_code mobile_number') + res.status(200).json(data) +} + +module.exports.findOne = [ + [ + param('id').notEmpty().isMongoId().withMessage('ایدی را وارد یا تصحیح کنید') + ], + checkValidations(validationResult), + async (req, res) => { + const data = await AwardRequest.findById(req.params.id).populate('awardId','-created_at -updated_at').populate('userId', 'first_name last_name shopName shopNumber shopAddress national_code mobile_number') + if (!data) { + res.status(404).json({ msg: _sr.fa.not_found.item_id }) + } else { + res.status(200).json(data) + } + + } +] + + +module.exports.update = [ + [ + param('id').notEmpty().isMongoId().withMessage('ایدی را وارد یا تصحیح کنید'), + body('status').notEmpty().isInt().withMessage(_sr.fa.required.field), + body('desc').notEmpty().isString().withMessage(_sr.fa.required.field) + ], + checkValidations(validationResult), + async (req, res) => { + const { status, desc, metaData } = req.body; + const data = await AwardRequest.findById(req.params.id) + if (!data) { + res.status(404).json({ msg: _sr.fa.not_found.item_id }) + } else { + await AwardRequest.updateOne( + {id:req.params.id}, + { + status, + desc, + metaData + } + ) + res.status(200).json({ msg: _sr.fa.response.success_save }) + + } + + } +] diff --git a/server/controllers/GPS.BankAccount.js b/server/controllers/GPS.BankAccount.js new file mode 100644 index 0000000..56855c5 --- /dev/null +++ b/server/controllers/GPS.BankAccount.js @@ -0,0 +1,70 @@ +const { body, param, validationResult } = require('express-validator') +const { _sr } = require('../plugins/serverResponses') +const { checkValidations } = require('../plugins/controllersHelperFunctions') +const User = require('../models/User') + +module.exports.addCard = [ + [ + body('holderName').notEmpty().isString().withMessage(_sr.fa.required.name), + body('PAN').notEmpty().isInt().withMessage(_sr.fa.required.store_number), + body('IBAN').notEmpty().isIBAN().withMessage(_sr.fa.required.address) + ], + checkValidations(validationResult), + async (req, res) => { + try { + const { holderName, PAN, IBAN } = req.body; + const userData = await User.findById(req.user_id).select('id cardBank') + if (userData?.cardBank && Array.isArray(userData?.cardBank)) { + userData.cardBank.push({ + holderName, + PAN, + IBAN + }) + userData.save() + res.status(201).json({ msg: 'با موفقیت ثبت شد ' }) + } else { + userData.cardBank = [{ + holderName, + PAN, + IBAN + }]; + userData.save() + res.status(201).json({ msg: 'با موفقیت ثبت شد ' }) + } + } catch (error) { + res.status(500).json({ msg: error }) + + } + } +] + + +module.exports.getAll = [ + async (req, res) => { + const userData = await User.findById(req.user_id).select('id cardBank') + res.status(200).json(userData) + } +] + + +module.exports.deleteCard = [ + [ + param('id').notEmpty().isMongoId().withMessage('این ایدی اشتباه می باشد') + ], + checkValidations(validationResult), + async (req, res) => { + const userData = await User.findOneAndUpdate( + { + _id: req.user_id, + "cardBank._id": req.params.id + }, + { + $pull: { + cardBank: { _id: req.params.id } + } + }, + { new:true } + ).select('id cardBank') + res.status(200).json({ msg: "با موفقیت حذف شد ", data: userData }) + } +] \ No newline at end of file diff --git a/server/controllers/GPS.Device.js b/server/controllers/GPS.Device.js new file mode 100644 index 0000000..4167d01 --- /dev/null +++ b/server/controllers/GPS.Device.js @@ -0,0 +1,171 @@ +const readXlsxFile = require('read-excel-file/node') +const validator = require('validator') +const { body, param, validationResult } = require('express-validator'); +const { _sr } = require('../plugins/serverResponses'); +const { checkValidations, res500 } = require('../plugins/controllersHelperFunctions'); +const Device = require('../models/GPS.Device'); + +module.exports.addCode = [ + [ + body('model').notEmpty().isString().withMessage(_sr.fa.required.name), + body('IMEI').notEmpty().isIMEI().trim().withMessage('لطفا IMEI را وارد یا تصحیح کنید').custom(async (v) => { + const device = await Device.findOne({ IMEI: v }) + if (device) { + return Promise.reject(_sr.fa.duplicated.IMEI) + } else { + return true + } + }), + body('tomanPrice').notEmpty().isInt({ min: 0 }).withMessage("قیمت دستگاه را به تومتن وارد کنید"), + body('dollarProfit').notEmpty().isInt({ min: 0 }).withMessage("سود نصاب را به دلار وارد کنید"), + body('profit').notEmpty().isInt({ min: 0 }).withMessage("سود نصاب را به تومان وارد کنید"), + body('score').notEmpty().isInt({ min: 0 }).withMessage("امتیاز دریافتی نصاب را وارد کنید"), + body('renewalProfit').notEmpty().isInt({ min: 0 }).withMessage("سود دریافتی نصاب را از تمدید اشتراک وارد کنید"), + ], + checkValidations(validationResult), + async (req, res) => { + const data = await Device.create(req.body) + res.status(201).json({ msg: _sr.fa.response.success_save, data }) + } +] + + + +module.exports.importExel = async (req, res) => { + try { + if (!req.files?.file) return res.status(422).json({ validation: { file: { msg: 'فایل را اضافه کنید' } } }) + if (!req.files?.file.name.split('.').some(item => ['xlsx', 'xltx', 'xlsm', 'xlsb'].includes(item))) return res.status(422).json({ validation: { file: { msg: 'فرمت فایل درست نمیباشد' } } }) + const file = req.files.file + + const exelFile = await readXlsxFile(Buffer.from(file.data)); + const data = await creator(exelFile) + res.status(201).json({ msg: _sr.fa.response.success_save, data }) + + + } catch (error) { + console.log(error); + res500(res, error) + } + +} + +module.exports.getAll = [ + async (req, res) => { + let page; + let rows; + if (!req.query?.rows) { + rows = 10 + } else { + rows = req.query.rows + } + if (!req.query?.page) { + page = 0 + // eslint-disable-next-line eqeqeq + } else if (!req.query.page == 1) { + page = 0 + } else { + page = req.query.page + page = page * rows - rows + } + const data = await Device.find().sort({ created_at: -1 }).skip(page).limit(rows); + const total = Math.ceil((await Device.estimatedDocumentCount())) + res.status(200).json({ data, total }) + } +] + +module.exports.getOne = [ + [ + param('id').notEmpty().isMongoId().withMessage('ایدی اشتباه است') + ], + checkValidations(validationResult), + async (req, res) => { + const data = await Device.findById(req.params.id).sort({ created_at: -1 }) + if (!data) { + res.status(404).json(_sr.fa.not_found.item_id) + } else { + res.status(200).json(data) + } + + } +] + +module.exports.getOneByIMEI = [ + [ + param('imei').notEmpty().isString().withMessage('ایدی اشتباه است') + ], + checkValidations(validationResult), + async (req, res) => { + const data = await Device.findOne({IMEI:req.params.imei}).sort({ created_at: -1 }) + if (!data) { + res.status(404).json(_sr.fa.not_found.item_id) + } else { + res.status(200).json(data) + } + + } +] + + + +module.exports.updateDevice = [ + [ + param('id').notEmpty().isMongoId().withMessage('ایدی اشتباه است').custom(async (v) => { + const device = await Device.findById(v) + if (device) { + return true + } else { + return Promise.reject(_sr.fa.not_found.item_id) + + } + }), + body('model').notEmpty().isString().withMessage(_sr.fa.required.name), + body('tomanPrice').notEmpty().isInt({ min: 0 }).withMessage("قیمت دستگاه را به تومتن وارد کنید"), + body('dollarProfit').notEmpty().isInt({ min: 0 }).withMessage("سود نصاب را به دلار وارد کنید"), + body('profit').notEmpty().isInt({ min: 0 }).withMessage("سود نصاب را به تومان وارد کنید"), + body('score').notEmpty().isInt({ min: 0 }).withMessage("امتیاز دریافتی نصاب را وارد کنید"), + body('renewalProfit').notEmpty().isInt({ min: 0 }).withMessage("سود دریافتی نصاب را از تمدید اشتراک وارد کنید"), + + ], + checkValidations(validationResult), + async (req, res) => { + const { model, tomanPrice, dollarProfit, profit, score, renewalProfit } = req.body; + const data = await Device.findByIdAndUpdate( + req.params.id, + { + model, tomanPrice, dollarProfit, profit, score, renewalProfit + }, + { new: true } + ) + res.status(200).json({ msg: _sr.fa.response.success_save, data }) + } +] + + +const creator = (exel) => { + return new Promise(async (resolve, reject) => { + for (const row of exel) { + // eslint-disable-next-line no-constant-condition + if (validator.isIMEI(row[0].toString().trim()) || true) { + const device = await Device.findOne({ IMEI: row[0] }) + if (device) { + console.warn("Repeated IMEI ", row[0]) + } else { + await Device.create( + { + IMEI: row[0], + model: row[1], + tomanPrice: row[2], + dollarProfit: row[3] || 1, + profit: row[4] || 1, + score: row[5] || 1, + renewalProfit: row[6] || 1 + } + ) + } + } else { + console.warn("Wrong IMEI ", row[0]) + } + } + resolve("end") + }) +} \ No newline at end of file diff --git a/server/controllers/GPS.InstalledDevice.js b/server/controllers/GPS.InstalledDevice.js new file mode 100644 index 0000000..57b2118 --- /dev/null +++ b/server/controllers/GPS.InstalledDevice.js @@ -0,0 +1,160 @@ +const { body, param, validationResult } = require('express-validator'); +const { _sr } = require('../plugins/serverResponses'); +const { checkValidations } = require('../plugins/controllersHelperFunctions'); +const User = require('../models/User'); +const InstalledDevice = require('../models/GPS.InstalledDevice'); +const Device = require('../models/GPS.Device'); +const Score = require('../models/GPS.Score'); + +module.exports.deviceRegistration = [ + [ + body('IMEI').notEmpty().isString().withMessage('لطفا IMEI را وارد یا تصحیح کنید') + ], + checkValidations(validationResult), + async (req, res) => { + const device = await Device.findOne({ IMEI: req.body.IMEI }); + if (!device) { + throw res.status(404).json({ msg: 'این IMEI وجود ندارد' }) + // eslint-disable-next-line eqeqeq + } else if (device.status == 1) { + throw res.status(404).json({ msg: 'این IMEI قبلا ثبت شده' }) + + } else { + const data = { + deviceId: device.id, + status: 0, + registerDate: new Date(), + userId: req.user_id, + IMEI: req.body.IMEI + } + const newRequest = await InstalledDevice.create(data); + device.status = 1; + device.save() + res.status(201).json({ msg: _sr.fa.response.success_save, data: newRequest }) + } + } +] + + +module.exports.getAllForUser = async (req, res) => { + let page; + let rows; + if (!req.query?.rows || req.query?.rows <= 5) { + rows = 10 + } else { + rows = parseInt(req.query.rows) + } + + if (!req.query?.page) { + page = 0 + // eslint-disable-next-line eqeqeq + } else if (req.query.page <= 1) { + page = 0 + } else { + page = req.query.page + page = page * rows - rows + } + + const search = req.query?.search ? req.query.search : "" + + + const data = await InstalledDevice.find({ + userId: req.user_id, + $or: [ + { IMEI: { $regex: search, $options: 'i' } }, + ], + }).skip(page).limit(rows).sort({ created_at: -1 }).populate('deviceId') + + const total = Math.ceil((await InstalledDevice.countDocuments({ userId: req.user_id }))) + res.status(200).json({ data, total }) + +} + +module.exports.getAllForAdmin = async (req, res) => { + let page; + let rows; + if (!req.query?.rows || req.query?.rows <= 5) { + rows = 10 + } else { + rows = parseInt(req.query.rows) + } + + if (!req.query?.page) { + page = 0 + // eslint-disable-next-line eqeqeq + } else if (req.query.page <= 1) { + page = 0 + } else { + page = req.query.page + page = page * rows - rows + } + const search = req.query?.search ? req.query.search : "" + + const data = await InstalledDevice.find({ + $or: [ + { IMEI: { $regex: search, $options: 'i' } }, + ] + }).sort({ status: 1 }).skip(page).limit(rows) + .populate('deviceId') + .populate('userId', 'shopName shopNumber shopAddress birthDate score first_name last_name profilePic') + const total = Math.ceil((await InstalledDevice.estimatedDocumentCount())) + res.status(200).json({ data, total }) +} + + + +module.exports.updateInstalled = [ + [ + param('id').notEmpty().isMongoId().withMessage('لطفا ایدی را وارد یا تصحیح کنید'), + param('type').notEmpty().isString().withMessage('لطفا وضعیت را وارد یا تصحیح کنید'), + ], + checkValidations(validationResult), + async (req, res) => { + const installedDevice = await InstalledDevice.findById(req.params.id) + if (!installedDevice) { + throw res.status(404).json({ msg: 'این ایدی وجود ندارد' }) + } else { + const user = await User.findById(installedDevice.userId).select('walletBalance dollarBalance score') + const device = await Device.findById(installedDevice.deviceId) + switch (req.params.type) { + case "accept": { + + installedDevice.status = 1; + installedDevice.installationDate = new Date(); + + device.status = 1; + + user.walletBalance = device.profit + user.dollarBalance = device.dollarProfit + user.score = device.score + + + await Score.create({ + title:"ثبت دستگاه", + deviceId: device.id, + score: device.score, + userId: installedDevice.userId + }) + user.save() + device.save() + installedDevice.save() + res.status(201).json({ msg: _sr.fa.response.success_save, data: installedDevice }) + break; + } + case "reject": { + installedDevice.status = 2; + installedDevice.save() + device.status = 0; + device.save() + res.status(201).json({ msg: _sr.fa.response.success_save, data: installedDevice }) + break; + } + default: { + res.status(404).json({ msg: "استاتوس اشتباه است" }) + break; + } + } + } + } +] + diff --git a/server/controllers/GPS.Overview.js b/server/controllers/GPS.Overview.js new file mode 100644 index 0000000..225c675 --- /dev/null +++ b/server/controllers/GPS.Overview.js @@ -0,0 +1,23 @@ +const User = require('../models/User'); +const InstalledDevice = require('../models/GPS.InstalledDevice'); +const Withdraw = require('../models/GPS.Withdraw'); + + +module.exports.mainPage = async(req, res)=>{ + const device = await InstalledDevice.countDocuments({userId:req.user_id}) + const user = await User.findById(req.user_id).select('walletBalance dollarBalance score') + res.status(200).json({installedDevice:device, user}) +} + +module.exports.walletPage = async(req, res)=>{ + const withdraw = await Withdraw.findOne({userId: req.user_id, status:0}) + const user = await User.findById(req.user_id).select('walletBalance dollarBalance score') + let pending; + + if(withdraw){ + pending = true + }else{ + pending = false + } + res.status(200).json({user, pending }) +} \ No newline at end of file diff --git a/server/controllers/GPS.Request.js b/server/controllers/GPS.Request.js new file mode 100644 index 0000000..78ef4a6 --- /dev/null +++ b/server/controllers/GPS.Request.js @@ -0,0 +1,139 @@ +const { body, param, validationResult } = require('express-validator') +const { _sr } = require('../plugins/serverResponses') +const { checkValidations, res400 } = require('../plugins/controllersHelperFunctions') +const User = require('../models/User') +const RequestGPS = require('../models/GPS.Request') + +module.exports.gpsRequest = [ + [ + body('shopName').notEmpty().isString().withMessage(_sr.fa.required.name), + body('shopNumber').notEmpty().isString().withMessage(_sr.fa.required.store_number), + body('shopAddress').notEmpty().isString().withMessage(_sr.fa.required.address), + body('birthDate').notEmpty().isString().withMessage(_sr.fa.required.date), + ], + checkValidations(validationResult), + async (req, res) => { + const { shopName, shopNumber, shopAddress, birthDate, profilePic } = req.body; + const repeated = await RequestGPS.findOne({ userId: req.user_id }) + if (repeated) { + res400(res, "شما قبلا درخواست ثبت کرده اید") + } else { + const request = await RequestGPS.create({ + shopName, + shopNumber, + shopAddress, + birthDate, + profilePic, + userId: req.user_id, + }) + res.status(201).json({ msg: 'درخواست شما با موفقیت ثبت شد', date: request }) + } + + } +] + + +module.exports.getAllRequest = [ + async (req, res) => { + let page; + let rows; + if (!req.query?.rows) { + rows = 10 + } else { + rows = req.query.rows + } + if (!req.query?.page) { + page = 0 + // eslint-disable-next-line eqeqeq + } else if (!req.query.page == 1) { + page = 0 + } else { + page = req.query.page + page = page * rows - rows + } + const data = await RequestGPS.find({}).sort({ status: 1 }).skip(page).limit(rows) + .populate('userId', 'shopName shopNumber shopAddress birthDate score first_name last_name profilePic') + const total = Math.ceil((await RequestGPS.estimatedDocumentCount())) + res.status(200).json({ data, total }) + } +] + +module.exports.getOneRequest = [ + [ + param('id').notEmpty().isMongoId().withMessage('این ایدی اشتباه است') + ], + checkValidations(validationResult), + async (req, res) => { + const data = await RequestGPS.findById(req.params.id).populate('userId', 'shopName shopNumber shopAddress birthDate score first_name last_name profilePic') + res.status(200).json(data) + } +] + + +module.exports.changeStatus = [ + [ + param('status').notEmpty().isString().withMessage('این استاتوس اشتباه است'), + param('id').notEmpty().isMongoId().withMessage('این ایدی اشتباه است') + ], + checkValidations(validationResult), + async (req, res) => { + switch (req.params.status) { + case "accept": { + const data = await RequestGPS.findByIdAndUpdate(req.params.id, + { + status: 1 + }, + { new: true }) + const userPreData = await User.findById(data.userId).select('walletBalance dollarBalance score scope') + if(userPreData.scope.includes('gps')){ + const userData = await User.findByIdAndUpdate( + data.userId, + { + shopName: data.shopName, + shopNumber: data.shopNumber, + shopAddress: data.shopAddress, + birthDate: data.birthDate, + walletBalance: userPreData?.walletBalance | 0, + dollarBalance: userPreData?.dollarBalance | 0, + score: userPreData?.score | 0, + $push:{scope:'gps'} + }, + { new: true } + ).select('first_name last_name') + res.status(200).json({ msg: 'با موفقیت به روز شد', data: userData }) + + }else{ + const userData = await User.findByIdAndUpdate( + data.userId, + { + shopName: data.shopName, + shopNumber: data.shopNumber, + shopAddress: data.shopAddress, + birthDate: data.birthDate, + walletBalance: userPreData?.walletBalance | 0, + dollarBalance: userPreData?.dollarBalance | 0, + score: userPreData?.score | 0, + }, + { new: true } + ).select('first_name last_name') + res.status(200).json({ msg: 'با موفقیت به روز شد', data: userData }) + + } + break; + } + case "reject": { + const data = await RequestGPS.findByIdAndUpdate(req.params.id, + { + status: 2 + }, + { new: true }) + + res.status(200).json({ msg: 'با موفقیت به روز شد', data }) + break; + } + default: + res.status(400).json({ msg: 'این استاتوس اشتباه است' }) + break; + } + } +] diff --git a/server/controllers/GPS.Score.js b/server/controllers/GPS.Score.js new file mode 100644 index 0000000..ff9e2f5 --- /dev/null +++ b/server/controllers/GPS.Score.js @@ -0,0 +1,41 @@ +// const { body, param, validationResult } = require('express-validator') +// const { _sr } = require('../plugins/serverResponses') +// const { checkValidations } = require('../plugins/controllersHelperFunctions') +const Score = require('../models/GPS.Score') +const User = require('../models/User') + + + +module.exports.getAll = async (req, res) => { + let page; + let rows; + if (!req.query?.rows || req.query?.rows <= 5) { + rows = 10 + } else { + rows = parseInt(req.query.rows) + } + + if (!req.query?.page) { + page = 0 + // eslint-disable-next-line eqeqeq + } else if (req.query.page <= 1) { + page = 0 + } else { + page = req.query.page + page = page * rows - rows + } + const search = req.query?.search ? req.query.search : "" + + + const score = await Score.find({ + userId: req.user_id, + $or: [ + { title: { $regex: search, $options: 'i' } }, + { score: { $regex: search, $options: 'i' } }, + ], + }).skip(page).limit(rows); + const total = Math.ceil((await Score.countDocuments({ userId: req.user_id }))) + + const user = await User.findById(req.user_id).select('score') + res.status(200).json({ data: score, score: user.score, total }) +} diff --git a/server/controllers/GPS.Withdraw.js b/server/controllers/GPS.Withdraw.js new file mode 100644 index 0000000..5271ef5 --- /dev/null +++ b/server/controllers/GPS.Withdraw.js @@ -0,0 +1,134 @@ +const { body, param, validationResult } = require('express-validator'); +const { _sr } = require('../plugins/serverResponses'); +const { checkValidations } = require('../plugins/controllersHelperFunctions'); +const User = require('../models/User'); +const Withdraw = require('../models/GPS.Withdraw'); + + +module.exports.createRequest = [ + [ + body('card').notEmpty().isObject().withMessage('گارت ورودی خود را انتخاب کنید'), + ], + checkValidations(validationResult), + async (req, res) => { + try { + const withdraw = await Withdraw.findOne({userId: req.user_id, status:0}) + if(withdraw){ + res.status(400).json({ msg: 'شما درخواست درحال بررسی دارید' }) + }else{ + const user = await User.findById(req.user_id) + const data = { + card: req.body.card, + userId: req.user_id, + amount: user.walletBalance, + dollarAmount: user.dollarBalance, + status: 0 + } + + const request = await Withdraw.create(data) + user.dollarBalance = 0; + user.walletBalance = 0; + user.save() + res.status(201).json({ msg: _sr.fa.response.success_save, data: request }) + } + + } catch (error) { + res.status(500).json({ msg: error }) + + } + } +] + + +module.exports.getAllForUser = async (req, res) => { + let page; + let rows; + if (!req.query?.rows || req.query?.rows <= 5) { + rows = 10 + } else { + rows = parseInt(req.query.rows) + } + + if (!req.query?.page) { + page = 0 + // eslint-disable-next-line eqeqeq + } else if (req.query.page <= 1) { + page = 0 + } else { + page = req.query.page + page = page * rows - rows + } + const search = req.query?.search ? req.query.search : "" + + + const data = await Withdraw.find({ + userId: req.user_id, + $or: [ + { amount: { $regex: search, $options: 'i' } }, + { dollarAmount: { $regex: search, $options: 'i' } }, + { trackingNumber: { $regex: search, $options: 'i' } }, + ], + }).skip(page).limit(rows).sort({ created_at: -1 }); + const total = Math.ceil((await Withdraw.countDocuments({ userId: req.user_id }))) + res.status(201).json({ data, total }) +} + +module.exports.getAllForAdmin = async (req, res) => { + let page; + let rows; + if (!req.query?.rows || req.query?.rows <= 5) { + rows = 10 + } else { + rows = parseInt(req.query.rows) + } + + if (!req.query?.page) { + page = 0 + // eslint-disable-next-line eqeqeq + } else if (req.query.page <= 1) { + page = 0 + } else { + page = req.query.page + page = page * rows - rows + } + const data = await Withdraw.find().sort({ status: 1 }).skip(page).limit(rows) + .populate('userId', 'shopName shopNumber shopAddress birthDate score first_name last_name profilePic') + const total = Math.ceil((await Withdraw.estimatedDocumentCount())) + res.status(201).json({ data, total }) +} + + +module.exports.updateReq = [ + [ + param('id').notEmpty().isMongoId().withMessage('ایدی را باید وارد کنید'), + param('type').notEmpty().isString().withMessage('وضعیت را باید وارد کنید'), + ], + checkValidations(validationResult), + async (req, res) => { + const request = await Withdraw.findById(req.params.id) + if (!request) { + res.status(404).json({ msg: _sr.fa.not_found.item_id }) + } else { + switch (req.params.type) { + case "accept": { + request.status = 1; + request.save() + res.status(201).json({ msg: _sr.fa.response.success_save, data: request }) + break; + } + case "deposit": { + request.status = 2; + request.depositDate = new Date(); + request.trackingNumber = req.body.trackingNumber || 0; + request.save() + res.status(201).json({ msg: _sr.fa.response.success_save, data: request }) + break; + } + default: { + res.status(404).json({ msg: "استاتوس اشتباه است" }) + break; + } + } + } + } +] \ No newline at end of file diff --git a/server/controllers/userController.js b/server/controllers/userController.js index 49c8d9d..1cb7ac9 100644 --- a/server/controllers/userController.js +++ b/server/controllers/userController.js @@ -24,7 +24,18 @@ const { 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: 'awesome_bell_ncc1hi', + pass: '509f8938-db95-4b8d-83e7-9ea8eccfd28b' + }, + tls: { + rejectUnauthorized: false + } +}) /// ///////////////////////////////////////////////////////////////// verify account functions function sendConfirmationEmail(userId) { return new Promise(async (resolve, reject) => { @@ -38,18 +49,7 @@ function sendConfirmationEmail(userId) { /// //// email configs // const hostURL = 'www.asan-service.com' - const transporter = nodemailer.createTransport({ - host: 'smtp.c1.liara.email', - port: 587, - secure: false, // true for 465, false for other ports - auth: { - user: 'awesome_bell_ncc1hi', - pass: '509f8938-db95-4b8d-83e7-9ea8eccfd28b' - }, - tls: { - rejectUnauthorized: false - } - }) + const emailHTMLTemplate = `
@@ -250,7 +250,7 @@ module.exports.delete_admin = [ module.exports.permissions = [ (req, res) => { - return res.json(permissions) + return res.status(200).json(permissions) } ] @@ -687,18 +687,7 @@ module.exports.generate_reset_user_password_link = [ const hostURL = 'www.asan-service.com' if (type === 'email') { - const transporter = nodemailer.createTransport({ - host: 'mail.asan-service.com', - port: 587, - secure: false, // true for 465, false for other ports - auth: { - user: 'resetpassword@asan-service.com', - pass: '3e9atXQV+7a&' - }, - tls: { - rejectUnauthorized: false - } - }) + const emailHTMLTemplate = `
@@ -1238,179 +1227,6 @@ module.exports.update_user_by_admin = [ } ] - -/// //////////////////////////////////////////////////////////////////// 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.json({ user }) - } catch (err) { - console.log('🚀 ~ file: userController.js ~ line 1070 ~ err', err) - return res.status(401).json({ message: 'unauthenticated' }) - } - } -] - - module.exports.updateUserByAdmin = [ [ body('first_name') @@ -1595,3 +1411,177 @@ module.exports.updateUserByAdmin = [ } } ] + +/// //////////////////////////////////////////////////////////////////// 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.json({ user }) + } catch (err) { + console.log('🚀 ~ file: userController.js ~ line 1070 ~ err', err) + return res.status(401).json({ message: 'unauthenticated' }) + } + } +] + +/// ////////////////////////////////////////////////////////////////////////// GPS Section + diff --git a/server/database.js b/server/database.js index 0c3f7b5..d59a6d9 100644 --- a/server/database.js +++ b/server/database.js @@ -2,9 +2,10 @@ const mongoose = require('mongoose') // mongodb database connection string. change it as per your needs. here "mydb" is the name of the database. You don't need to create DB from mongodb terminal. mongoose create the database automatically. const inHostUrl = 'mongodb://root:KPTt76tImNBBMm4Kor8QA9gB@asan-service:27017/asanserv_database?authSource=admin' +const outHostUrl = 'mongodb://root:KPTt76tImNBBMm4Kor8QA9gB@hotaka.liara.cloud:33794/asanserv_database?authSource=admin' const init = ()=>{ try { - mongoose.connect(inHostUrl, { + mongoose.connect(outHostUrl, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false, diff --git a/server/errorHandler.js b/server/errorHandler.js new file mode 100644 index 0000000..5d52dd0 --- /dev/null +++ b/server/errorHandler.js @@ -0,0 +1,73 @@ +const { constants } = require("./constants"); +const errorHandler = (err, req, res, next) => { + const statusCode = res.statusCode ? res.statusCode : 500; + + switch (statusCode) { + case constants.VALIDATION_ERROR: + res.status(statusCode).json({ + title: "Validation Failed ", + msg: err.message, + stackTrace: err.stack, + }); + break; + case constants.Method_Not_Allowed: + res.status(statusCode).json({ + title: "Method Not Allowed ", + msg: err.message, + stackTrace: err.stack, + }); + break; + case constants.NOT_FOUND: + res.status(statusCode).json({ + title: "Not found ", + msg: err.message, + stackTrace: err.stack, + }); + break; + case constants.FORBIDOEN: + res.status(statusCode).json({ + title: "Forbidoen ", + msg: err.message, + stackTrace: err.stack, + }); + break; + case constants.SERVER_ERROR: + res.status(statusCode).json({ + title: "Server Error ", + msg: err.message, + stackTrace: err.stack, + }); + break; + case constants.UNAUTHORIZED: + res.status(statusCode).json({ + title: "Unauthorized ", + msg: err.message, + stackTrace: err.stack, + }); + break; + case constants.SITE_UNAUTHORIZED: + res.status(statusCode).json({ + title: "Site Unauthorized ", + msg: err.message, + stackTrace: err.stack, + }); + break; + case constants.TOO_MANY: + res.status(statusCode).json({ + title: "Too many request", + msg: err.message, + stackTrace: err.stack, + }); + break; + default: + console.log(err); + res.status(500).json({ + title: "Unhandled error", + msg: err.message, + stackTrace: err.stack + }); + break; + } +}; + +module.exports = { name: "errorHandler", run: errorHandler }; diff --git a/server/index.js b/server/index.js index f2dc882..6e0dd63 100644 --- a/server/index.js +++ b/server/index.js @@ -1,5 +1,6 @@ /// / develop environment config const { isDev, isProduction, DevServerPort } = require('./_env') +const errorHandler = require('./errorHandler') const wss = require('./WebSocket/wss') /// / init express app (wrapping all functions into init() to prevent running in development mode by nuxt dev mode) function init() { @@ -36,6 +37,7 @@ function init() { const admin = require('./routes/admin') const user = require('./routes/user') const agent = require('./routes/agent') + const gps = require('./routes/gps') const auth = require('./routes/auth') const cross = require('./routes/cross') const thirdParty = require('./routes/thirdParty') @@ -45,12 +47,14 @@ function init() { router.use('/admin', authentication.isAdmin, admin) router.use('/user', authentication.isUser, user) router.use('/agent', authentication.isAgent, agent) + router.use('/gps', authentication.isGPS, gps) router.use('/auth', auth) router.use('/cross', cross) router.use('/thirdParty', checkThirdPartyAccess, thirdParty) app.use(isDev ? '/api' : '', router) + app.use(errorHandler.run) return app } diff --git a/server/models/GPS.Award.js b/server/models/GPS.Award.js new file mode 100644 index 0000000..7df2283 --- /dev/null +++ b/server/models/GPS.Award.js @@ -0,0 +1,12 @@ +const mongoose = require('mongoose') + +const AwairdSchema = mongoose.Schema({ + + score:Number, + title:String, + desc:String, + expireDate:Date + +}) + +module.exports = mongoose.model('AwardGPS', AwairdSchema) diff --git a/server/models/GPS.AwardRequest.js b/server/models/GPS.AwardRequest.js new file mode 100644 index 0000000..4689cfc --- /dev/null +++ b/server/models/GPS.AwardRequest.js @@ -0,0 +1,24 @@ +const mongoose = require('mongoose') + +const AwardRequestSchema = mongoose.Schema({ + awardId:{ + type:mongoose.Types.ObjectId, + ref:'AwardGPS' + }, + userId:{ + type:String, + ref:'User' + }, + desc:String, + metaData:Object, + status:{ + type:Number, + enum:[ + 0, // Pending + 1 // Done + ], + default:0 + } +}) + +module.exports = mongoose.model('AwardRequestGPS', AwardRequestSchema) diff --git a/server/models/GPS.Device.js b/server/models/GPS.Device.js new file mode 100644 index 0000000..cb7dbad --- /dev/null +++ b/server/models/GPS.Device.js @@ -0,0 +1,42 @@ +const mongoose = require('mongoose') + +const deviceSchema = mongoose.Schema({ + model: String, + IMEI: { + type: String, + unique: [true, "این ایدی در سامانه موجود است"] + }, + tomanPrice: { + type: Number, + min: 0, + default: 0 + }, + dollarProfit: { + type: Number, + min: 0, + default: 0 + }, + profit: { + type: Number, + min: 0, + default: 0 + }, + score: { + type: Number, + min: 0 + }, + renewalProfit: { + type: Number, + min: 0 + }, + status: { + type: Number, + enum: [ + 0, // not installed + 1 // installed + ], + default: 0 + } +}) + +module.exports = mongoose.model('Device', deviceSchema) diff --git a/server/models/GPS.InstalledDevice.js b/server/models/GPS.InstalledDevice.js new file mode 100644 index 0000000..583964b --- /dev/null +++ b/server/models/GPS.InstalledDevice.js @@ -0,0 +1,26 @@ +const mongoose = require('mongoose') + +const InstalledDeviceSchema = mongoose.Schema({ + deviceId: { + type: String, + ref: "Device" + }, + IMEI:String, + status: { + type: Number, + enum: [ + 0, // Pending + 1, // Accepted + 2, // Rejected + ], + default: 0 + }, + registerDate: Date, + installationDate: Date, + userId: { + type: String, + ref: 'User' + }, +}) + +module.exports = mongoose.model('InstalledDevice', InstalledDeviceSchema) diff --git a/server/models/GPS.Renewal.js b/server/models/GPS.Renewal.js new file mode 100644 index 0000000..a181274 --- /dev/null +++ b/server/models/GPS.Renewal.js @@ -0,0 +1,33 @@ +const mongoose = require('mongoose') + +const RenewalSchema = mongoose.Schema({ + deviceId: { + type: String, + ref: 'Device' + }, + renewDate: Date, + renewType: { + type: String, + }, + price: { + type: Number, + min: 0 + }, + profit: { + type: Number, + min: 0, + unique: true, + default: 0, + required: true + }, + dollarProfit: { + type: Number, + min: 0 + }, + userId: { + type: String, + ref: 'User' + }, +}) + +module.exports = mongoose.model('Renewal', RenewalSchema) diff --git a/server/models/GPS.Request.js b/server/models/GPS.Request.js new file mode 100644 index 0000000..d755842 --- /dev/null +++ b/server/models/GPS.Request.js @@ -0,0 +1,23 @@ +const mongoose = require('mongoose') + +const RequestSchema = mongoose.Schema({ + userId: { + type: String, + ref: 'User' + }, + shopName: String, + shopNumber: String, + shopAddress: String, + birthDate: String, + status: { + type: Number, + enum: [ + 0, // Pending + 1, // Accepted + 2 // Rejected + ], + default: 0 + } +}) + +module.exports = mongoose.model('RequestGPS', RequestSchema) diff --git a/server/models/GPS.Score.js b/server/models/GPS.Score.js new file mode 100644 index 0000000..5e40807 --- /dev/null +++ b/server/models/GPS.Score.js @@ -0,0 +1,19 @@ +const mongoose = require('mongoose') + +const ScoreSchema = mongoose.Schema({ + deviceId: { + type: String, + ref: 'Device' + }, + title:String, + score: { + type: Number, + min: 0 + }, + userId: { + type: String, + ref: 'User' + }, +}) + +module.exports = mongoose.model('Score', ScoreSchema) diff --git a/server/models/GPS.Withdraw.js b/server/models/GPS.Withdraw.js new file mode 100644 index 0000000..3c13415 --- /dev/null +++ b/server/models/GPS.Withdraw.js @@ -0,0 +1,41 @@ +const mongoose = require('mongoose') + + +const BankSchema = mongoose.Schema({ + holderName: String, + PAN: { + type: Number, + }, + IBAN: { + type: String, + } +}) + +/// ///////////////////////////////////////////////////// +const WithdrawSchema = mongoose.Schema({ + card: BankSchema, + amount: { + type: Number, + min: 0 + }, + dollarAmount: { + type: Number, + min: 0 + }, + userId: { + type: String, + ref: 'User' + }, + status: { + type: Number, + enum: [ + 0, // Pending + 1, // Accepted + 2, // deposited + ] + }, + depositDate: Date, + trackingNumber: String +}) + +module.exports = mongoose.model('Withdraw', WithdrawSchema) diff --git a/server/models/User.js b/server/models/User.js index 43cac88..a398b0a 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -1,5 +1,16 @@ const mongoose = require('mongoose') +const BankSchema = mongoose.Schema({ + holderName:String, + PAN:{ + type:Number, + }, + IBAN:{ + type:String, + } +}) + + const UserSchema = mongoose.Schema({ // for admins _creator: { type: mongoose.ObjectId, ref: 'User' }, @@ -7,6 +18,7 @@ const UserSchema = mongoose.Schema({ username: String, permissions: Array, personalCode:String, + // for user national_code: String, province_id: String, @@ -42,6 +54,33 @@ const UserSchema = mongoose.Schema({ last_name: String, password: String, + // for GPS + profilePic:{ + type:String, + default:"noPic" + }, + shopName:String, + shopNumber:String, + shopAddress:String, + birthDate:String, + walletBalance:{ + type:Number, + min:0, + default:0 + }, + dollarBalance:{ + type:Number, + min:0, + default:0 + }, + score:{ + type:Number, + min:0, + default:0 + }, + cardBank:[BankSchema], + + /// //////////////////////////////// user confirmations confirmed: { type: Boolean, default: false }, email_confirmed: { type: Boolean, default: false }, diff --git a/server/plugins/asanServiceSerialChecker.js b/server/plugins/asanServiceSerialChecker.js index a903dc8..0ba4f73 100644 --- a/server/plugins/asanServiceSerialChecker.js +++ b/server/plugins/asanServiceSerialChecker.js @@ -1,6 +1,5 @@ const fs = require('fs') const readXlsxFile = require('read-excel-file/node') - const guaranteeSerialsPath = './static/uploads/serials' const productSerialsPath = './static/uploads/serials_products' diff --git a/server/plugins/controllersHelperFunctions.js b/server/plugins/controllersHelperFunctions.js index aa64209..bda09fd 100644 --- a/server/plugins/controllersHelperFunctions.js +++ b/server/plugins/controllersHelperFunctions.js @@ -2,7 +2,9 @@ module.exports.res404 = (res, err) => { return res.status(404).json({ message: err }) } - +module.exports.res400 = (res, err) => { + return res.status(404).json({ message: err }) +} module.exports.res406 = (res, err) => { return res.status(406).json({ message: err }) } diff --git a/server/plugins/permissions.js b/server/plugins/permissions.js index 01b3b03..3662d5b 100644 --- a/server/plugins/permissions.js +++ b/server/plugins/permissions.js @@ -114,5 +114,9 @@ module.exports = [ { name: 'برند ها', value: 'brands' + }, + { + name: 'جی پی اس', + value: 'gps' } ] diff --git a/server/plugins/serverResponses.js b/server/plugins/serverResponses.js index 8900a12..378de2b 100644 --- a/server/plugins/serverResponses.js +++ b/server/plugins/serverResponses.js @@ -16,6 +16,7 @@ module.exports._sr = { company_name: 'درصورتی که شخص حقوقی هستید، نام کمپانی را وارد کنید', username: 'نام کاربری را وارد کنید', phone_number: 'شماره تماس را وارد کنید', + store_number: 'شماره تماس مغازه را وارد کنید', password: 'پسورد را وارد کنید', birthdate: 'تاریخ تولد را وارد کنید', // addressing validations @@ -107,7 +108,8 @@ module.exports._sr = { email: 'این ایمیل از قبل وجود دارد', name: 'نام تکراری است', title: 'عنوان تکراری است', - phone_number: 'این شماره تماس از قبل وجود دارد' + phone_number: 'این شماره تماس از قبل وجود دارد', + IMEI:'این IMEI تکراری است' }, response: { logged_in: 'شما وارد سیستم شدید', diff --git a/server/routes/admin.js b/server/routes/admin.js index c40dc20..0ae205b 100644 --- a/server/routes/admin.js +++ b/server/routes/admin.js @@ -26,9 +26,52 @@ const smsBroadcastController = require('../controllers/smsBroadcastController') const surveyController = require('../controllers/surveyController') const complaintController = require('../controllers/complaintController') const brandController = require('../controllers/brandController') +const requestController = require('../controllers/GPS.Request') +const withdraw = require('../controllers/GPS.Withdraw') +const installedDevice = require('../controllers/GPS.InstalledDevice') +const device = require('../controllers/GPS.Device') +const award = require('../controllers/GPS.Award') +const awardRequest = require('../controllers/GPS.AwardRequest') /// //////////////////////////////////////////////////////// routes + + +/// ///////////////////////////// GPS + +/// ////////////// Award +router.post('/gps/award',hasPermission('gps'), award.createAward) +router.get('/gps/award',hasPermission('gps'), award.findAllAdmin) +router.get('/gps/award/:id',hasPermission('gps'), award.findOne) +router.put('/gps/award/:id',hasPermission('gps'), award.update) + +/// ////////////// Award request +router.get('/gps/award-request',hasPermission('gps'), awardRequest.findAllAdmin) +router.get('/gps/award-request/:id',hasPermission('gps'), awardRequest.findOne) +router.put('/gps/award-request/:id',hasPermission('gps'), awardRequest.update) + +/// ////////////// request for access to gps +router.get('/gps/request',hasPermission('gps'), requestController.getAllRequest) +router.get('/gps/request/:id',hasPermission('gps'), requestController.getOneRequest) +router.patch('/gps/request/:id/:status',hasPermission('gps'), requestController.changeStatus) + +/// ////////////// Withdraw +router.get('/gps/withdraw',hasPermission('gps'), withdraw.getAllForAdmin) +router.patch('/gps/withdraw/:id/:type',hasPermission('gps'), withdraw.updateReq) + +/// ////////////// Installed device +router.patch('/gps/register-device/:id/:type', installedDevice.updateInstalled) +router.get('/gps/register-device', installedDevice.getAllForAdmin) + +/// ////////////// Device +router.post('/gps/device',hasPermission('gps'), device.addCode) +router.post('/gps/device/exel',hasPermission('gps'), device.importExel) +router.get('/gps/device',hasPermission('gps'), device.getAll) +router.get('/gps/device/imei/:imei',hasPermission('gps'), device.getOneByIMEI) +router.get('/gps/device/id/:id',hasPermission('gps'), device.getOne) +router.put('/gps/device/:id',hasPermission('gps'), device.updateDevice) + +/// ///////////////////////////////////////////////////////// /// ///////////// survey router.get('/surveys',hasPermission('surveys'), surveyController.getAllForAdmin) router.get('/surveys/:id',hasPermission('surveys'), surveyController.getOneForAdmin) diff --git a/server/routes/agent.js b/server/routes/agent.js index 82711c5..ee33d3e 100644 --- a/server/routes/agent.js +++ b/server/routes/agent.js @@ -5,7 +5,6 @@ const pieceController = require('../controllers/pieceController') const pieceRequestController = require('../controllers/pieceRequestController') const oldPieceRequestController = require('../controllers/oldPieceRequestController') const bufferRequestController = require('../controllers/bufferRequestController') - const guaranteeReportController = require('../controllers/guaranteeReportController') /// // inbox diff --git a/server/routes/auth.js b/server/routes/auth.js index bf7de62..83c1eb5 100644 --- a/server/routes/auth.js +++ b/server/routes/auth.js @@ -1,8 +1,13 @@ const { Router } = require('express') const router = Router() const userController = require('../controllers/userController') +const gpsAuthController = require('../controllers/GPS.Auth') -/// /////////////////// admin routes +/// /////////////////// gps routes +router.post('/gps/login', gpsAuthController.login) + + +/// /////////////////// // router.post('/register/admin', userController.register_admin) router.post('/register/user', userController.register_user) // router.post('/activation/:key', userController.activation) @@ -13,4 +18,6 @@ router.post('/login', userController.webLogin) router.post('/logout', userController.webLogout) router.get('/user', userController.getUser) + + module.exports = router diff --git a/server/routes/gps.js b/server/routes/gps.js new file mode 100644 index 0000000..47dbe98 --- /dev/null +++ b/server/routes/gps.js @@ -0,0 +1,38 @@ +const { Router } = require('express') +const router = Router() +const bankAccount = require('../controllers/GPS.BankAccount') +const withdraw = require('../controllers/GPS.Withdraw') +const installedDevice = require('../controllers/GPS.InstalledDevice') +const score = require('../controllers/GPS.Score') +const award = require('../controllers/GPS.Award') +const awardRequest = require('../controllers/GPS.AwardRequest') +const overview = require('../controllers/GPS.Overview') + +/// /////////////// Overview page +router.get('/overview/main', overview.mainPage) +router.get('/overview/wallet', overview.walletPage) + + +/// ////////////// Award request +router.post('/award-request', awardRequest.sendRequest) +router.get('/award-request', awardRequest.findAllUser) + +/// ////////////// Award +router.get('/award', award.findAllUser) + +/// ///////////////// Bank card +router.post('/bank-card', bankAccount.addCard) +router.get('/bank-card', bankAccount.getAll) +router.delete('/bank-card/:id', bankAccount.deleteCard) +/// ///////////////// Withdraw request +router.post('/withdraw', withdraw.createRequest) +router.get('/withdraw', withdraw.getAllForUser) + +/// //////////////// Installed device +router.post('/register-device', installedDevice.deviceRegistration) +router.get('/register-device', installedDevice.getAllForUser) + +/// ///////////////// Score +router.get('/score', score.getAll) + +module.exports = router diff --git a/server/routes/user.js b/server/routes/user.js index a896171..663ee7c 100644 --- a/server/routes/user.js +++ b/server/routes/user.js @@ -6,6 +6,10 @@ const representationController = require('../controllers/representationControlle const transactionDraftsController = require('../controllers/transactionDraftsController') const transactionController = require('../controllers/transactionController') const surveyController = require('../controllers/surveyController') +const requestController = require('../controllers/GPS.Request') + +/// // GPS +router.post('/gps/request', requestController.gpsRequest) /// // profile router.put('/update_profile', userController.update_user)