From e5f604303be853b13c77ff1bc40381f89d3ad9fc Mon Sep 17 00:00:00 2001 From: tejaswitandel Date: Mon, 14 Jun 2021 13:37:39 +0000 Subject: [PATCH] Code Synced by DhiWise --- .env | 4 + .env.PRODUCTION | 2 + .env.QA | 2 + .eslintrc.js | 35 + .gitignore | 1 + README.md | 121 +++ app.js | 37 + config/adminPassportStrategy.js | 43 + config/authConstant.js | 90 ++ config/db.js | 17 + constants/account.js | 8 + constants/user.js | 5 + controller/admin/NiraliController.js | 236 +++++ controller/admin/accountController.js | 235 +++++ controller/admin/authController.js | 172 ++++ controller/admin/fileUploadController.js | 220 +++++ controller/admin/testController.js | 87 ++ controller/admin/userController.js | 249 +++++ jobs/index.js | 2 + jobs/jobConfiguration.js | 6 + middleware/auth.js | 49 + middleware/loginUser.js | 29 + model/Nirali.js | 60 ++ model/account.js | 58 ++ model/user.js | 83 ++ package.json | 34 + postman/postman-collection.json | 1053 ++++++++++++++++++++++ routes/admin/NiraliRoutes.js | 16 + routes/admin/accountRoutes.js | 16 + routes/admin/auth.js | 11 + routes/admin/index.js | 12 + routes/admin/testRoutes.js | 15 + routes/admin/uploadRoutes.js | 11 + routes/admin/userRoutes.js | 17 + routes/index.js | 6 + services/admin/testService.js | 36 + services/auth.js | 237 +++++ services/customQueryService.js | 77 ++ services/email/emailService.js | 33 + services/jobs/jobConfiguration.js | 3 + services/sms/smsService.js | 48 + utils/common.js | 25 + utils/dbService.js | 314 +++++++ utils/deleteDependent.js | 41 + utils/messages.js | 89 ++ utils/responseCode.js | 6 + utils/validateRequest.js | 27 + utils/validation/NiraliValidation.js | 19 + utils/validation/accountValidation.js | 21 + utils/validation/userValidation.js | 33 + views/emailTemplate/html.ejs | 44 + views/resetPassword/html.ejs | 51 ++ views/sendOTP/html.ejs | 53 ++ views/successfullyResetPassword/html.ejs | 37 + 54 files changed, 4236 insertions(+) create mode 100644 .env create mode 100644 .env.PRODUCTION create mode 100644 .env.QA create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app.js create mode 100644 config/adminPassportStrategy.js create mode 100644 config/authConstant.js create mode 100644 config/db.js create mode 100644 constants/account.js create mode 100644 constants/user.js create mode 100644 controller/admin/NiraliController.js create mode 100644 controller/admin/accountController.js create mode 100644 controller/admin/authController.js create mode 100644 controller/admin/fileUploadController.js create mode 100644 controller/admin/testController.js create mode 100644 controller/admin/userController.js create mode 100644 jobs/index.js create mode 100644 jobs/jobConfiguration.js create mode 100644 middleware/auth.js create mode 100644 middleware/loginUser.js create mode 100644 model/Nirali.js create mode 100644 model/account.js create mode 100644 model/user.js create mode 100644 package.json create mode 100644 postman/postman-collection.json create mode 100644 routes/admin/NiraliRoutes.js create mode 100644 routes/admin/accountRoutes.js create mode 100644 routes/admin/auth.js create mode 100644 routes/admin/index.js create mode 100644 routes/admin/testRoutes.js create mode 100644 routes/admin/uploadRoutes.js create mode 100644 routes/admin/userRoutes.js create mode 100644 routes/index.js create mode 100644 services/admin/testService.js create mode 100644 services/auth.js create mode 100644 services/customQueryService.js create mode 100644 services/email/emailService.js create mode 100644 services/jobs/jobConfiguration.js create mode 100644 services/sms/smsService.js create mode 100644 utils/common.js create mode 100644 utils/dbService.js create mode 100644 utils/deleteDependent.js create mode 100644 utils/messages.js create mode 100644 utils/responseCode.js create mode 100644 utils/validateRequest.js create mode 100644 utils/validation/NiraliValidation.js create mode 100644 utils/validation/accountValidation.js create mode 100644 utils/validation/userValidation.js create mode 100644 views/emailTemplate/html.ejs create mode 100644 views/resetPassword/html.ejs create mode 100644 views/sendOTP/html.ejs create mode 100644 views/successfullyResetPassword/html.ejs diff --git a/.env b/.env new file mode 100644 index 0000000..aa4ba87 --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +PORT=3000 +DB_URL = 'mongodb://localhost:27017/node_test' +TEST=test +data=tabws diff --git a/.env.PRODUCTION b/.env.PRODUCTION new file mode 100644 index 0000000..ea61ab4 --- /dev/null +++ b/.env.PRODUCTION @@ -0,0 +1,2 @@ +TEST=da +data=tab diff --git a/.env.QA b/.env.QA new file mode 100644 index 0000000..546815c --- /dev/null +++ b/.env.QA @@ -0,0 +1,2 @@ +TEST=test +data=tab diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..7bc4e72 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,35 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + parserOptions: { + ecmaVersion: 12, + sourceType: 'module', + }, + rules: { + 'multiline-comment-style': ['error', 'starred-block'], + 'object-property-newline': ['error', { + allowAllPropertiesOnSameLine: false, + allowMultiplePropertiesPerLine: false, + }], + 'object-curly-newline': ['error', { + minProperties: 2, + multiline: true, + }], + 'no-multiple-empty-lines': ['error', { + max: 1, + maxEOF: 0, + }], + semi: ['error', 'always'], + indent: ['error', 2], + 'no-param-reassign': 'off', + 'no-underscore-dangle': 'off', + 'class-methods-use-this': 'off', + 'max-len': [2, { + code: 1000, + ignorePattern: '^import .*', + }], + 'linebreak-style': ['error', process.platform === 'win32' ? 'windows' : 'unix'], + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b5a89d --- /dev/null +++ b/README.md @@ -0,0 +1,121 @@ +# MVC NodeJS,Mongoose,Express Project + + +supported version of nodejs-15.13.0 +supported version of mongoose-4.0 + +This is a template of Web application, developed using MVC pattern with Node.js, ExpressJS, and Mongoose ODM. +Basic boilerplate for web applications, built on Express.js using the Model–View–Controller architectural pattern. +The views are created with the Embedded JavaScript template (EJS) view engine. +A MongoDB database is used for data storage, with object modeling provided by Mongoose. + +# initial +- Configure a basic server in app.js. +- Organize the routes with Express Router. +- Use the mainRoutes in app as middleware. +- Set a final use after the routes, to display a 404 message for the unhandled requests. +1.Install needed Node.js modules: + ```$ npm install``` +2.execute server: + ```$ npm start``` +# folder structure: + + --project + --project_folder + --config + --controller + --jobs + --logs + --middleware + --model + --postman + --public + --routes + --services + --utils + --views + --app.js + --.env + --.gitignore + --.eslintrc.js + --project_folder.zip +# app.js +- entry point of application. +# config +- passport strategy for all platforms. +- based on Auth Model - authentication files has been generated. +- Auth constant File that has authentication configuration constants +- Used .env file and configure the db connection string to use in the project. +# controller +- includes controller files per model +- Controllers are separated per Platform + + -controller + -admin + -modelController.js + -device + -modelController.js + -desktop + -modelController.js + -client + -modelController.js + +# jobs +- Cron jobs related Files and configuration +# logs +- Log file +# middleware +- User authentication Middleware based on Roles and permission for Routes' access +- Custom Policy files +# models +- Mongoose Models , as per user defined schema +# postman +- Postman collection File for Platform based APIs that are generated. +- Import this JSON in Postman to test the APIs. +# public +- You can add static files like like images, pdf etc. +# routes +- based on platform,separate folder is generated,within those folders model wise route files are that has model crud APIs' routes. +- index.js file, main file which includes all platform routes. +- added index files in app.js to access the routes of the application. +# services + -jobs + -cron job services + -auth.js + -Logic for JWT Tokenization for user to login into Application using username and password along with otp if required. +# utils + -validation + -joi validations files. + -files are separated by models. + -common.js + -converted object to enum function. + -dbService.js + -common Database functionalities + -getAllDocuments(find all documents) + -updateDocuments(update single documents in db) + -deleteDocuments(delete single documents in db) + -createDocuments(create single documents in db) + -getDocumentByQuery(find single document) + -getSingleDocumentById(find by id) + -softDelete + -findExistData + -bulkInsert(insert multiple documents in db) + -bulkUpdate(update multiple documents in db) + -countDocument + -Aggregation + -messages.js + -static messages that are sent with response - contains status and Data + -responseCode.js + -codes for responses + -validateRequest.js + -validate schema based on joi validation +# views +- add ejs files + + + + + + + + diff --git a/app.js b/app.js new file mode 100644 index 0000000..a815427 --- /dev/null +++ b/app.js @@ -0,0 +1,37 @@ +const express = require('express'); +const path = require('path'); +const dotenv = require('dotenv'); +dotenv.config(); +global.__basedir = __dirname; +const ejs = require("ejs"); +let cookieParser = require('cookie-parser'); +let logger = require('morgan'); +const passport = require("passport") + +const {adminPassportStrategy} = require("./config/adminPassportStrategy"); + +const app = express(); + +//template engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +//all routes +const routes = require("./routes/index") + +//jobs configuration +require('./jobs/index'); + +app.use(logger('dev')); +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); +app.use(routes) + +adminPassportStrategy(passport); + + +app.listen(process.env.PORT,()=>{ + console.log(`your application is running on ${process.env.PORT}`) +}); diff --git a/config/adminPassportStrategy.js b/config/adminPassportStrategy.js new file mode 100644 index 0000000..fc65136 --- /dev/null +++ b/config/adminPassportStrategy.js @@ -0,0 +1,43 @@ +/* + * admin authentication - with passport + */ + +const { Strategy, ExtractJwt } = require("passport-jwt") +const { JWT } = require("./authConstant") +const user = require("../model/user") + +let token = null; +const jwtExtractor = (req) => { + if (req && req.headers) { + let tokenParts = req.headers.authorization.split(' '); + if (/^Bearer$/i.test(tokenParts[0])) { + token = tokenParts[1]; + } + } + return token; +}; + +module.exports = { + adminPassportStrategy: passport => { + const options = {}; + options.jwtFromRequest =jwtExtractor; options.secretOrKey = JWT.ADMIN_SECRET; + passport.use('admin-rule', + new Strategy(options, (payload, done) => { + user.findOne({ username: payload.username }, (err, user) => { + if (err) { + // console.log(err) + return done(err, false); + } + if (user) { + if(user.tokens.includes(token)){ + return done(null, { + ...user.toJSON() + }); + } + } + return done('No User Found', {}); + }); + }) + ); + } +} \ No newline at end of file diff --git a/config/authConstant.js b/config/authConstant.js new file mode 100644 index 0000000..7b0d407 --- /dev/null +++ b/config/authConstant.js @@ -0,0 +1,90 @@ +/* + * constants + */ + +const JWT={ + ADMIN_SECRET:"myjwtadminsecret", + EXPIRES_IN: 10000 +} + +const USER_ROLE ={ + User:1, + Admin:2, +} + +const PLATFORM = { + ADMIN:1, +} + +let LOGIN_ACCESS ={ + [USER_ROLE.User]:[PLATFORM.ADMIN], + [USER_ROLE.Admin]:[PLATFORM.ADMIN], +} + +const DEFAULT_ROLE= 1 + +const ROLE_RIGHTS={ + + [USER_ROLE.User] : [ + "getAllByUserInAdminPlatform", + "getByUserInAdminPlatform", + "aggregateByUserInAdminPlatform", + "getCountByUserInAdminPlatform", + "createByUserInAdminPlatform", + "addBulkByUserInAdminPlatform", + "updateByUserInAdminPlatform", + "updateBulkByUserInAdminPlatform", + "partialUpdateByUserInAdminPlatform", + "deleteByUserInAdminPlatform", + "softDeleteByUserInAdminPlatform", + "upsertByUserInAdminPlatform", + "fileUploadByUserInAdminPlatform", + "changePasswordByUserInAdminPlatform" +], + + [USER_ROLE.Admin] : [ + "getAllByAdminInAdminPlatform", + "getByAdminInAdminPlatform", + "aggregateByAdminInAdminPlatform", + "getCountByAdminInAdminPlatform", + "createByAdminInAdminPlatform", + "addBulkByAdminInAdminPlatform", + "updateByAdminInAdminPlatform", + "updateBulkByAdminInAdminPlatform", + "partialUpdateByAdminInAdminPlatform", + "deleteByAdminInAdminPlatform", + "softDeleteByAdminInAdminPlatform", + "upsertByAdminInAdminPlatform", + "fileUploadByAdminInAdminPlatform", + "changePasswordByAdminInAdminPlatform" +], + +} +const MAX_LOGIN_RETRY_LIMIT = 3; + +const SEND_LOGIN_OTP = { + SMS:1, + EMAIL:2, +} +const DEFAULT_SEND_LOGIN_OTP=SEND_LOGIN_OTP.SMS + +const FORGOT_PASSWORD_WITH = { + LINK: { + sms: true, + email: false + }, + EXPIRETIME: 23 +} + +module.exports = { + JWT, + USER_ROLE, + DEFAULT_ROLE, + ROLE_RIGHTS, + PLATFORM, + MAX_LOGIN_RETRY_LIMIT, + SEND_LOGIN_OTP, + DEFAULT_SEND_LOGIN_OTP, + FORGOT_PASSWORD_WITH, + LOGIN_ACCESS +} \ No newline at end of file diff --git a/config/db.js b/config/db.js new file mode 100644 index 0000000..e45b0d3 --- /dev/null +++ b/config/db.js @@ -0,0 +1,17 @@ +/* + * Database connection file. + */ +const mongoose = require("mongoose") +const uri = process.env.DB_URL; +mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false }) +var db = mongoose.connection + +db.once("open", () => { + console.log("Connection Successful") +}) + +db.on("error", () => { + console.log("Error in Connect Mongo") +}) + +module.exports = mongoose \ No newline at end of file diff --git a/constants/account.js b/constants/account.js new file mode 100644 index 0000000..dcd99e5 --- /dev/null +++ b/constants/account.js @@ -0,0 +1,8 @@ +/* + * constants + */ + +module.exports={ + type: { data: 1, data_2: 2 }, + type_type: { type: { data: 1, data_2: 2 } } +} diff --git a/constants/user.js b/constants/user.js new file mode 100644 index 0000000..16bfb63 --- /dev/null +++ b/constants/user.js @@ -0,0 +1,5 @@ +/* + * constants + */ + +module.exports={ type: 1 } diff --git a/controller/admin/NiraliController.js b/controller/admin/NiraliController.js new file mode 100644 index 0000000..528a407 --- /dev/null +++ b/controller/admin/NiraliController.js @@ -0,0 +1,236 @@ +const Nirali = require("../../model/Nirali") +const utils = require("../../utils/messages") +const NiraliSchemaKey = require("../../utils/validation/NiraliValidation"); +const validation = require("../../utils/validateRequest"); +const dbService = require("../../utils/dbService"); + +const addNirali = async(req, res) => { + try { + let isValid = validation.validateParamsWithJoi(req.body,NiraliSchemaKey.schemaKeys); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + const data = new Nirali({ + ...req.body, + }) + let result = await dbService.createDocument(Nirali,data); + result = (({test,id}) => ({test,id}))(result); + + + return utils.successResponse(result, res); + } catch (error) { + if(error.name === "ValidationError"){ + return utils.validationError(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse(error.message,res); + } +} +const findAllNirali = async(req, res) => { + try { + let options = {} + let query={} + let result; + if(req.body.isCountOnly){ + if (req.body.query !== undefined) { + query = { ...req.body.query } + } + result = await dbService.countDocument(Nirali, query); + if (result) { + result = { totalRecords: result } + return utils.successResponse(result, res); + } + return utils.recordNotFound([], res) + } + else { + if (req.body.options !== undefined) { + if(req.body.options.populate){ + delete req.body.options.populate; + } + options = { ...req.body.options }; + + if(req.body.query !==undefined){ + query={...req.body.query} + } + result = await dbService.getAllDocuments( Nirali,query,options); + + if(!result){ + return utils.recordNotFound([],res); + } + return utils.successResponse(result, res); + } + } + } + catch(error){ + return utils.failureResponse(error.message,res); + } +} + +const getNirali = async(req, res) => { + try { + let query={}; + query.id = req.params.id; + let result = await dbService.getDocumentByQuery(Nirali,query); + + if(result){ + return utils.successResponse(result, res); + } + return utils.recordNotFound([],res); + } + catch(error){ + return utils.failureResponse(error.message,res) + } +} +const getNiraliCount = async(req, res) => { + try { + let where ={}; + if(req.body.where){ + where = req.body.where; + } + let result = await dbService.countDocument(Nirali,where); + if(result){ + result = {totalRecords:result} + return utils.successResponse(result, res); + } + return utils.recordNotFound([],res); + } + catch(error){ + return utils.failureResponse(error.message,res); + } +} + +const getNiraliByAggregate = async (req,res)=>{ + try{ + let result=await dbService.getDocumentByAggregation(Nirali,req.body); + if(result){ + return utils.successResponse(result, res); + } + return utils.recordNotFound([],res); + }catch(error){ + return utils.failureResponse(error.message,res) + } +} +const updateNirali = async(req, res) => { + try { + const data = { + ...req.body, + id:req.params.id + } + let isValid = validation.validateParamsWithJoi( + data, + NiraliSchemaKey.schemaKeys + ); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + let result = await dbService.findOneAndUpdateDocument(Nirali,{_id:req.params.id},data,{new:true}); + if(!result){ + return utils.failureResponse("something is wrong",res) + } + + return utils.successResponse(result, res); + } + catch(error){ + if(error.name === "ValidationError"){ + return utils.isDuplicate(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse(error.message,res); + } +} +const partialUpdateNirali = async (req, res) => { + try { + const data = { + ...req.body, + id: req.params.id + } + let isValid = validation.validateParamsWithJoi( + data, + NiraliSchemaKey.updateSchemaKeys + ); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + let result = await dbService.updateDocument(Nirali, req.params.id, data); + if (!result) { + return utils.failureResponse("something is wrong", res) + } + + return utils.successResponse(result, res); + } + catch(error){ + return utils.failureResponse(error.message, res) + } +} +const softDeleteNirali = async (req, res) => { + try{ + let result = await dbService.updateDocument(Nirali, { _id: req.params.id }, { isDeleted: true }); + if(!result){ + return utils.failedSoftDelete(res); + } + return utils.successResponse(result, res); + }catch(error){ + return utils.failureResponse(error.message,res); + } +} +const bulkInsertNirali = async(req,res)=>{ + try{ + let data; + if(req.body.data !== undefined && req.body.data.length){ + data = req.body.data; + let result =await dbService.bulkInsert(Nirali,data); + return utils.successResponse(result, res); + }else{ + return utils.failureResponse('Invalid Data',res) + } + }catch(error){ + if(error.name === "ValidationError"){ + return utils.validationError(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse(error.message,res); + } +} +const bulkUpdateNirali=async(req,res)=>{ + try { + let filter={}; + let data; + if(req.body.filter !== undefined){ + filter = req.body.filter + } + if(req.body.data !== undefined){ + data = req.body.data; + let result = await dbService.bulkUpdate(Nirali,filter,data); + if(!result){ + return utils.failureResponse("something is wrong.",res) + } + return utils.successResponse(result, res); + } + else{ + return utils.failureResponse("Invalid Data", res) + } + } + catch(error){ + return utils.failureResponse(error.message,res); + } +} + + +module.exports = { + addNirali, + findAllNirali, + getNirali, + getNiraliCount, + getNiraliByAggregate, + updateNirali, + partialUpdateNirali, + softDeleteNirali, + bulkInsertNirali, + bulkUpdateNirali, +} diff --git a/controller/admin/accountController.js b/controller/admin/accountController.js new file mode 100644 index 0000000..2814e57 --- /dev/null +++ b/controller/admin/accountController.js @@ -0,0 +1,235 @@ +const Account = require("../../model/account") +const utils = require("../../utils/messages") +const accountSchemaKey = require("../../utils/validation/accountValidation"); +const validation = require("../../utils/validateRequest"); +const dbService = require("../../utils/dbService"); + +const addAccount = async(req, res) => { + try { + let isValid = validation.validateParamsWithJoi(req.body,accountSchemaKey.schemaKeys); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + const data = new Account({ + ...req.body, + }) + let result = await dbService.createDocument(Account,data); + + + return utils.successResponse(result, res); + } catch (error) { + if(error.name === "ValidationError"){ + return utils.validationError(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse(error.message,res); + } +} +const findAllAccount = async(req, res) => { + try { + let options = {} + let query={} + let result; + if(req.body.isCountOnly){ + if (req.body.query !== undefined) { + query = { ...req.body.query } + } + result = await dbService.countDocument(Account, query); + if (result) { + result = { totalRecords: result } + return utils.successResponse(result, res); + } + return utils.recordNotFound([], res) + } + else { + if (req.body.options !== undefined) { + if(req.body.options.populate){ + delete req.body.options.populate; + } + options = { ...req.body.options }; + + if(req.body.query !==undefined){ + query={...req.body.query} + } + result = await dbService.getAllDocuments( Account,query,options); + + if(!result){ + return utils.recordNotFound([],res); + } + return utils.successResponse(result, res); + } + } + } + catch(error){ + return utils.failureResponse(error.message,res); + } +} + +const getAccount = async(req, res) => { + try { + let query={}; + query.id = req.params.id; + let result = await dbService.getDocumentByQuery(Account,query); + + if(result){ + return utils.successResponse(result, res); + } + return utils.recordNotFound([],res); + } + catch(error){ + return utils.failureResponse(error.message,res) + } +} +const getAccountCount = async(req, res) => { + try { + let where ={}; + if(req.body.where){ + where = req.body.where; + } + let result = await dbService.countDocument(Account,where); + if(result){ + result = {totalRecords:result} + return utils.successResponse(result, res); + } + return utils.recordNotFound([],res); + } + catch(error){ + return utils.failureResponse(error.message,res); + } +} + +const getAccountByAggregate = async (req,res)=>{ + try{ + let result=await dbService.getDocumentByAggregation(Account,req.body); + if(result){ + return utils.successResponse(result, res); + } + return utils.recordNotFound([],res); + }catch(error){ + return utils.failureResponse(error.message,res) + } +} +const updateAccount = async(req, res) => { + try { + const data = { + ...req.body, + id:req.params.id + } + let isValid = validation.validateParamsWithJoi( + data, + accountSchemaKey.schemaKeys + ); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + let result = await dbService.findOneAndUpdateDocument(Account,{_id:req.params.id},data,{new:true}); + if(!result){ + return utils.failureResponse("something is wrong",res) + } + + return utils.successResponse(result, res); + } + catch(error){ + if(error.name === "ValidationError"){ + return utils.isDuplicate(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse(error.message,res); + } +} +const partialUpdateAccount = async (req, res) => { + try { + const data = { + ...req.body, + id: req.params.id + } + let isValid = validation.validateParamsWithJoi( + data, + accountSchemaKey.updateSchemaKeys + ); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + let result = await dbService.updateDocument(Account, req.params.id, data); + if (!result) { + return utils.failureResponse("something is wrong", res) + } + + return utils.successResponse(result, res); + } + catch(error){ + return utils.failureResponse(error.message, res) + } +} +const softDeleteAccount = async (req, res) => { + try{ + let result = await dbService.updateDocument(Account, { _id: req.params.id }, { isDeleted: true }); + if(!result){ + return utils.failedSoftDelete(res); + } + return utils.successResponse(result, res); + }catch(error){ + return utils.failureResponse(error.message,res); + } +} +const bulkInsertAccount = async(req,res)=>{ + try{ + let data; + if(req.body.data !== undefined && req.body.data.length){ + data = req.body.data; + let result =await dbService.bulkInsert(Account,data); + return utils.successResponse(result, res); + }else{ + return utils.failureResponse('Invalid Data',res) + } + }catch(error){ + if(error.name === "ValidationError"){ + return utils.validationError(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse(error.message,res); + } +} +const bulkUpdateAccount=async(req,res)=>{ + try { + let filter={}; + let data; + if(req.body.filter !== undefined){ + filter = req.body.filter + } + if(req.body.data !== undefined){ + data = req.body.data; + let result = await dbService.bulkUpdate(Account,filter,data); + if(!result){ + return utils.failureResponse("something is wrong.",res) + } + return utils.successResponse(result, res); + } + else{ + return utils.failureResponse("Invalid Data", res) + } + } + catch(error){ + return utils.failureResponse(error.message,res); + } +} + + +module.exports = { + addAccount, + findAllAccount, + getAccount, + getAccountCount, + getAccountByAggregate, + updateAccount, + partialUpdateAccount, + softDeleteAccount, + bulkInsertAccount, + bulkUpdateAccount, +} diff --git a/controller/admin/authController.js b/controller/admin/authController.js new file mode 100644 index 0000000..af820a8 --- /dev/null +++ b/controller/admin/authController.js @@ -0,0 +1,172 @@ +const authService = require("../../services/auth") +const utils = require("../../utils/messages"); +const User = require("../../model/user"); +const dbService = require("../../utils/dbService"); +const moment = require("moment"); +const userSchemaKey = require("../../utils/validation/userValidation"); +const validation = require("../../utils/validateRequest"); + +module.exports = { + /* + * api: user register + * description : first time user registration. + */ + register : async(req, res) => { + try { + let isValid = validation.validateParamsWithJoi( + req.body, + userSchemaKey.schemaKeys + ); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + const data = new User({ + ...req.body + }) + const result = await dbService.createDocument(User,data); + return utils.successResponse(result, res); + } catch (error) { + if(error.name === "ValidationError"){ + return utils.validationError(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse({},res); + } + }, + /* + * api : forgot password + * description : send email or sms to user for forgot password. + */ + forgotPassword: async (req, res) => { + const params = req.body; + try { + if (!params.email) { + return utils.insufficientParameters(res); + } + let where = {email: params.email}; + params.email = params.email.toString().toLowerCase(); + let isUser = await dbService.getDocumentByQuery(User,where); + if (isUser) { + let {resultOfEmail,resultOfSMS} = await authService.sendResetPasswordNotification(isUser); + if(resultOfEmail && resultOfSMS){ + return utils.successResponse("otp successfully send.", res); + }else if(resultOfEmail && !resultOfSMS) { + return utils.successResponse("otp successfully send to your email.", res); + } else if (!resultOfEmail && resultOfSMS) { + return utils.successResponse("otp successfully send to your mobile number.", res); + }else{ + return utils.failureResponse("otp can not be sent due to some issue try again later", res); + } + } else { + return utils.recordNotFound("user not found", res); + } + } catch (error) { + //console.log(error); + return utils.failureResponse(error, res); + } + }, + /* + * api : validate forgot password otp + * description : after successfully sent mail or sms for forgot password validate otp + */ + validateResetPasswordOtp: async (req, res) => { + const params = req.body; + try { + if (!params || !params.otp) { + return utils.insufficientParameters(res); + } + let isUser = await dbService.getDocumentByQuery(User, { 'resetPasswordLink.code': params.otp }); + if (!isUser || !isUser.resetPasswordLink.expireTime) { + return utils.successResponse("Invalid OTP", res); + } + // link expire + if (moment(new Date()).isAfter(moment(isUser.resetPasswordLink.expireTime))) { + return utils.successResponse("Your reset password link is expired or invalid", res); + } + // await dbService.updateDocument(User, isUser.id, { resetPasswordLink: {} }) + return utils.successResponse('Otp verified', res); + } catch (error) { + //console.log(error); + return utils.failureResponse(error.message, res); + } + }, + /* + * api : reset password + * description : after successfully sent email or sms for forgot password, + * validate otp or link and reset password + */ + resetPassword : async (req, res) => { + const params = req.body; + try { + if (!params.code || !params.newPassword) { + return utils.insufficientParameters(res); + } + let isUser = await dbService.getDocumentByQuery(User, { 'resetPasswordLink.code': params.code }); + if (isUser && isUser.resetPasswordLink.expireTime) { + if (moment(new Date()).isAfter(moment(isUser.resetPasswordLink.expireTime))) {// link expire + return utils.failureResponse("Your reset password link is expired on invalid", res); + } + } else { + // invalid token + return utils.failureResponse("Invalid Code", res); + } + let response = await authService.resetPassword(isUser, params.newPassword); + return utils.successResponse(response.data, res); + } catch (error) { + //console.log(error); + return utils.failureResponse(error.message, res); + } + }, + /* + * api : send email or sms + * description : send otp to user for two-factor authentication + */ + sendOtpForLogin:async(req,res)=>{ + try { + let params = req.body; + if(!params.username || !params.password){ + return utils.insufficientParameters(res); + } + let result = await authService.sendLoginOTP(params.username,params.password); + return utils.successResponse(result.data, res); + } catch (error) { + return utils.failureResponse(error.message,res); + } + }, + /* + * api : two-factor authentication + * description : after successfully sent otp to user, user can login with otp. + */ + loginWithOTP: async (req, res) => { + const params = req.body; + let url = req.originalUrl + try { + if (!params.code || !params.username || !params.password) { + return utils.insufficientParameters(res); + } + let where = {$or:[{username:params.username},{password:params.username}]} + let user = await dbService.getDocumentByQuery(User,where); + if (user && user.loginOTP.expireTime) { + if (moment(new Date()).isAfter(moment(user.loginOTP.expireTime))) {// link expire + return utils.successResponse("Your reset password link is expired", res); + } + if(user.loginOTP.code!==params.code){ + return utils.successResponse("Invalid Code", res); + } + } else { + // invalid token + return utils.successResponse("Invalid Code", res); + } + let result = await authService.loginWithOTP(params.username,params.password,url); + if(!result.flag){ + return utils.loginSuccess(result.data,res); + } + return utils.loginFailed(result.data,res); + } catch (error) { + //console.log(error); + return utils.failureResponse(err.message, res); + } + } +} diff --git a/controller/admin/fileUploadController.js b/controller/admin/fileUploadController.js new file mode 100644 index 0000000..9cff18d --- /dev/null +++ b/controller/admin/fileUploadController.js @@ -0,0 +1,220 @@ +const fs = require('fs'); +const path = require('path'); +const formidable = require('formidable'); +const validUrl = require('valid-url'); + +const utils = require("../../utils/messages") + +let defaultDirectory = 'uploads' +let allowedFileTypes = [ 'png', 'jpeg' ]; +let maxFileSize = 23; //In Megabyte + +const upload = async (req, res) => { + try { + // Create Directory if not exist. + await makeDirectory(defaultDirectory); + + // Setting up formidable options. + const form = new formidable.IncomingForm(); + form.multiples = true; + form.maxFileSize = 300 * 1024 * 1024; //300 MB + form.maxFieldsSize = 100 * 1024 * 1024; //50 MB + + //Upload File one by one + const uploadFileRes = await new Promise(async (resolve, reject) => { + + form.parse(req, async function (err, fields, files) { + + let filePaths = []; + let fileCount = 1; + + let fileArr = []; + if(files["file[]"].size == 0){ + resolve({ + "err": "Please Select any one File", + "status": false + }) + } + if (!Array.isArray(files["file[]"])) { + fileArr.push(files["file[]"]); + files["file[]"] = fileArr; + } + + for (let file of files["file[]"]) { + + let response = await uploadFile(file, fields, fileCount++); + + if (response.status == false) { + filePaths.push({ + "name": file.name, + "err": response.message, + "status": false + }) + } else { + let url = response.data; + if (!validUrl.isUri(response.data)) { + url = req.protocol + "://" + req.headers.host + response.data; + } + filePaths.push({ + "path": url, + "status": true + }) + } + } + resolve(filePaths); + }); + }); + + return utils.successResponse(uploadFileRes,res); + + } catch (error) { + return utils.failureResponse(error.message,res); + } +} + +/** + * + * Function used to create directory. + * + * @param {} dirPath + */ +const makeDirectory = async (directoryPath) => { + + if (!fs.existsSync(directoryPath)){ + fs.promises.mkdir(directoryPath, { recursive: true }, (err) => { + if (err) { + return false; + }; + return true; + }); + } + return true; +} + + +/** + * + * Function used to upload file + * + * @param {} files + * @param {} fields + */ +async function uploadFile(file, fields, fileCount) { + + let tempPath = file.path; + let unlink; + + let extension = path.extname(file.name); + extension = extension.split('.').pop(); + + fileType = file.type; + + if(allowedFileTypes.length){ + //Check allowed extension; + if (!allowedFileTypes.includes(extension)) { + return { + status: false, + message: "Not Allowed file." + } + } + } + + // Check File Size + const fileSize = ((file.size / 1024) / 1024); + if (maxFileSize < fileSize) { + return { + status : false, + message : `Allow file size upto ${maxFileSize} MB.` + } + } + + //Check Mime types of file. + let isValidMimeType = await checkExtAndFileMimeType(file, extension); + + if (!isValidMimeType) { + return { + status: false, + message: "File Invalid.", + } + } + + //Create New path + let newPath = defaultDirectory + "/" + new Date().getTime() + path.extname(file.name); + + //Create Requested Directory,if given in request parameter. + if (fields && fields.folder) { + let newDir = defaultDirectory + "/" + fields.folder; + const createDir = await makeDirectory(newDir); + if(createDir){ + if (fields.fileName) { + newPath = newDir + "/" + fields.fileName + "-" + fileCount + path.extname(file.name); + } + } + } + else if (fields && fields.fileName) { + newPath = defaultDirectory + "/" + fields.fileName + "-" + fileCount + path.extname(file.name); + } + + const response = await new Promise(async (resolve, reject) => { + fs.readFile(tempPath, function (err, data) { + fs.writeFile(newPath, data, async function (err) { + + //Remove file from temp + unlink = await unlinkFile(tempPath); + + if (unlink.status == false) { + reject(unlink); + } else { + resolve({ + status: true, + message: "File upload successfully.", + data: "/" + newPath + }); + } + }); + }); + }); + + + + + return response; +} + +/** + * + * Function used to unlink file. + * + * @param {} path + */ +async function unlinkFile(path) { + + fs.unlink(path, function (err) { + if (err) { + return { + status: false, + message: err.message + } + } + }); + + return { + status: true + }; +} + + +/** + * + * Function used to check mimetype with extension. + * + * @param {} path + */ +async function checkExtAndFileMimeType(file, extension) { + return true; + } + + + + +module.exports = {upload}; \ No newline at end of file diff --git a/controller/admin/testController.js b/controller/admin/testController.js new file mode 100644 index 0000000..ab755a4 --- /dev/null +++ b/controller/admin/testController.js @@ -0,0 +1,87 @@ +const user = require("../../model/user") +const account = require("../../model/account") +const customQueryService = require("../../services/customQueryService.js") +const testService = require("../../services/admin/testService"); +const utils = require("../../utils/messages") +/* +* dfdf +*/ +const test=async (req,res)=>{ +try { + if(combinedOutput){ + return utils.successResponse(combinedOutput,res); + } + } catch (error) { + throw error; + } +} +/* +* test +*/ +const test=async (req,res)=>{ +try { + // let result = testService.test(); + + let isValid={}; + let combinedOutput={}; + const validation = require("../../utils/validateRequest"); + const query_6198 = {} + query_6198.filter = {"username":{"$exists":false}} + + let list = await customQueryService.find(user,query_6198) + combinedOutput.list = list + const query_1312 = {} + query_1312.filter = "{\"username\":{\"$exists\":false,\"$eq\":\"test\"},\"name\":{\"$exists\":true}}" + + let data = await customQueryService.find(user,query_1312) + combinedOutput.data = data + if(combinedOutput){ + return utils.successResponse(combinedOutput,res); + } + } catch (error) { + throw error; + } +} +/* +* sdsd +*/ +const test=async (req,res)=>{ +try { + // let result = testService.test(); + + let isValid={}; + let combinedOutput={}; + const validation = require("../../utils/validateRequest"); + const query_6626 = {} + let test = await customQueryService.find(account,query_6626) + combinedOutput.test = test + const query_1009 = {} + query_1009.filter = "{\"username\":\"sdas\",\"name\":{\"$exists\":false},\"password\":\"csdsds\"}" + + let count = await customQueryService.find(user,query_1009) + combinedOutput.count = count + const query_6096 = {} + query_6096.filter = "\"{\\\"name\\\":{\\\"$exists\\\":false}}\"" + + let account = await customQueryService.find(account,query_6096) + combinedOutput.account = account + const query_7212 = {} + query_7212.filter = "\"{\\\"name\\\":{\\\"$exists\\\":true}}\"" + + let accountCount = await customQueryService.find(account,query_7212) + combinedOutput.accountCount = accountCount + if(combinedOutput){ + return utils.successResponse(combinedOutput,res); + } + } catch (error) { + throw error; + } +} +module.exports={ + test, + test, + test, +} + + + diff --git a/controller/admin/userController.js b/controller/admin/userController.js new file mode 100644 index 0000000..70e690b --- /dev/null +++ b/controller/admin/userController.js @@ -0,0 +1,249 @@ +const User = require("../../model/user") +const utils = require("../../utils/messages") +const userSchemaKey = require("../../utils/validation/userValidation"); +const validation = require("../../utils/validateRequest"); +const dbService = require("../../utils/dbService"); +const auth = require("../../services/auth"); + +const addUser = async(req, res) => { + try { + let isValid = validation.validateParamsWithJoi(req.body,userSchemaKey.schemaKeys); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + const data = new User({ + ...req.body, + }) + let result = await dbService.createDocument(User,data); + + + return utils.successResponse(result, res); + } catch (error) { + if(error.name === "ValidationError"){ + return utils.validationError(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse(error.message,res); + } +} +const findAllUser = async(req, res) => { + try { + let options = {} + let query={} + let result; + if(req.body.isCountOnly){ + if (req.body.query !== undefined) { + query = { ...req.body.query } + } + result = await dbService.countDocument(User, query); + if (result) { + result = { totalRecords: result } + return utils.successResponse(result, res); + } + return utils.recordNotFound([], res) + } + else { + if (req.body.options !== undefined) { + if(req.body.options.populate){ + delete req.body.options.populate; + } + options = { ...req.body.options }; + + if(req.body.query !==undefined){ + query={...req.body.query} + } + result = await dbService.getAllDocuments( User,query,options); + + if(!result){ + return utils.recordNotFound([],res); + } + return utils.successResponse(result, res); + } + } + } + catch(error){ + return utils.failureResponse(error.message,res); + } +} + +const getUser = async(req, res) => { + try { + let query={}; + query.id = req.params.id; + let result = await dbService.getDocumentByQuery(User,query); + + if(result){ + return utils.successResponse(result, res); + } + return utils.recordNotFound([],res); + } + catch(error){ + return utils.failureResponse(error.message,res) + } +} +const getUserCount = async(req, res) => { + try { + let where ={}; + if(req.body.where){ + where = req.body.where; + } + let result = await dbService.countDocument(User,where); + if(result){ + result = {totalRecords:result} + return utils.successResponse(result, res); + } + return utils.recordNotFound([],res); + } + catch(error){ + return utils.failureResponse(error.message,res); + } +} + +const getUserByAggregate = async (req,res)=>{ + try{ + let result=await dbService.getDocumentByAggregation(User,req.body); + if(result){ + return utils.successResponse(result, res); + } + return utils.recordNotFound([],res); + }catch(error){ + return utils.failureResponse(error.message,res) + } +} +const updateUser = async(req, res) => { + try { + const data = { + ...req.body, + id:req.params.id + } + let isValid = validation.validateParamsWithJoi( + data, + userSchemaKey.schemaKeys + ); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + let result = await dbService.findOneAndUpdateDocument(User,{_id:req.params.id},data,{new:true}); + if(!result){ + return utils.failureResponse("something is wrong",res) + } + + return utils.successResponse(result, res); + } + catch(error){ + if(error.name === "ValidationError"){ + return utils.isDuplicate(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse(error.message,res); + } +} +const partialUpdateUser = async (req, res) => { + try { + const data = { + ...req.body, + id: req.params.id + } + let isValid = validation.validateParamsWithJoi( + data, + userSchemaKey.updateSchemaKeys + ); + if (isValid.error) { + return utils.inValidParam(isValid.details, res); + } + let result = await dbService.updateDocument(User, req.params.id, data); + if (!result) { + return utils.failureResponse("something is wrong", res) + } + + return utils.successResponse(result, res); + } + catch(error){ + return utils.failureResponse(error.message, res) + } +} +const softDeleteUser = async (req, res) => { + try{ + let result = await dbService.updateDocument(User, { _id: req.params.id }, { isDeleted: true }); + if(!result){ + return utils.failedSoftDelete(res); + } + return utils.successResponse(result, res); + }catch(error){ + return utils.failureResponse(error.message,res); + } +} +const bulkInsertUser = async(req,res)=>{ + try{ + let data; + if(req.body.data !== undefined && req.body.data.length){ + data = req.body.data; + let result =await dbService.bulkInsert(User,data); + return utils.successResponse(result, res); + }else{ + return utils.failureResponse('Invalid Data',res) + } + }catch(error){ + if(error.name === "ValidationError"){ + return utils.validationError(error.message, res); + } + if(error.code && error.code == 11000){ + return utils.isDuplicate(error.message, res); + } + return utils.failureResponse(error.message,res); + } +} +const bulkUpdateUser=async(req,res)=>{ + try { + let filter={}; + let data; + if(req.body.filter !== undefined){ + filter = req.body.filter + } + if(req.body.data !== undefined){ + data = req.body.data; + let result = await dbService.bulkUpdate(User,filter,data); + if(!result){ + return utils.failureResponse("something is wrong.",res) + } + return utils.successResponse(result, res); + } + else{ + return utils.failureResponse("Invalid Data", res) + } + } + catch(error){ + return utils.failureResponse(error.message,res); + } +} + + const changePassword = async (req, res) => { + try { + let params = req.body; + if (!params.newPassword || !params.userId) { + return utils.inValidParam({}, res); + } + let result = await auth.changePassword(params); + return utils.successResponse(result.data, res); + } catch (error) { + return utils.failureResponse(error, res); + } + } + +module.exports = { + addUser, + findAllUser, + getUser, + getUserCount, + getUserByAggregate, + updateUser, + partialUpdateUser, + softDeleteUser, + bulkInsertUser, + bulkUpdateUser, + changePassword +} diff --git a/jobs/index.js b/jobs/index.js new file mode 100644 index 0000000..a439463 --- /dev/null +++ b/jobs/index.js @@ -0,0 +1,2 @@ +const jobConfiguration = require("./jobConfiguration"); + diff --git a/jobs/jobConfiguration.js b/jobs/jobConfiguration.js new file mode 100644 index 0000000..97ae48b --- /dev/null +++ b/jobs/jobConfiguration.js @@ -0,0 +1,6 @@ +const cron = require('node-cron'); +let jobService = require("../services/jobs/jobConfiguration"); + + +module.exports={ +}; \ No newline at end of file diff --git a/middleware/auth.js b/middleware/auth.js new file mode 100644 index 0000000..35dca07 --- /dev/null +++ b/middleware/auth.js @@ -0,0 +1,49 @@ +const passport = require('passport'); +const { ROLE_RIGHTS, USER_ROLE } = require('../config/authConstant'); +const util = require("../utils/messages"); + +const verifyCallback = (req, resolve, reject, requiredRights) => async (err, user, info) => { + if (err || info || !user) { + return reject("Unauthorized User"); + } + req.user = user; + if (!user.isActive) { + return reject("User is deactivated"); + } + if (requiredRights.length) { + for(role in USER_ROLE){ + if(USER_ROLE[role]===user.role){ + const userRights = ROLE_RIGHTS[user.role]; + const hasRequiredRights = requiredRights.some((requiredRight) => userRights.includes(requiredRight)); + if (!hasRequiredRights || !user.id) { + return reject('Unauthorized user'); + } + } + } + } + resolve(); +}; + +/* +* policy : authentication & authorization policy for platform wise check, +* whether user is authenticated and authorized or not +*/ +const auth = (...requiredRights) => async (req, res, next) => { + +let url =req.originalUrl; + if(url.includes('admin')){ + return new Promise((resolve, reject) => { + passport.authenticate('admin-rule', { session: false }, verifyCallback(req, resolve, reject, requiredRights))( + req, + res, + next + ); + }) + .then(() => next()) + .catch((err) => { + return util.unAuthorizedRequest(err,res); + }); + } +}; + +module.exports = auth; diff --git a/middleware/loginUser.js b/middleware/loginUser.js new file mode 100644 index 0000000..d4c1381 --- /dev/null +++ b/middleware/loginUser.js @@ -0,0 +1,29 @@ +const jwt = require("jsonwebtoken"); +const util = require("../utils/messages"); +const adminSecret = require("../config/authConstant").JWT.ADMIN_SECRET; + +/* +* policy : only authentication policy for platform wise check, +* whether user is authenticated or not +*/ +const authenticateJWT = (req, res, next) => { + const authHeader = req.headers.authorization; + if (authHeader) { + const token = authHeader.split(' ')[1]; + let url = req.originalUrl; + let secret = ''; + if(url.includes('admin')){ + secret = adminSecret; + } + jwt.verify(token,secret, (err, user) => { + if (err) { + return util.unAuthorizedRequest(err,res); + } + req.user = user; + next(); + }); + } else { + return util.unAuthorizedRequest(err,res); + } +}; +module.exports = authenticateJWT \ No newline at end of file diff --git a/model/Nirali.js b/model/Nirali.js new file mode 100644 index 0000000..7ad6b99 --- /dev/null +++ b/model/Nirali.js @@ -0,0 +1,60 @@ +const mongoose = require("../config/db"); +const mongoosePaginate = require('mongoose-paginate-v2'); +var idValidator = require('mongoose-id-validator'); + +const myCustomLabels = { + totalDocs: 'itemCount', + docs: 'data', + limit: 'perPage', + page: 'currentPage', + nextPage: 'next', + prevPage: 'prev', + totalPages: 'pageCount', + pagingCounter: 'slNo', + meta: 'paginator', +}; + +mongoosePaginate.paginate.options = { + customLabels: myCustomLabels +}; + +const Schema = mongoose.Schema; +const schema = new Schema( +{ + + addedBy:{type:Schema.Types.ObjectId,ref:"user"}, + + isActive:Boolean, + + isDeleted:Boolean, + + test:{type:String} + }, + { timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } } +); + + +schema.pre('save',async function(next){ + this.slug = this.name.toLowerCase() + next(); +}); +schema.pre('save', async function(next) { + this.isDeleted = false; + this.isActive = true; + next(); +}); + + + +schema.method("toJSON", function () { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; +}); +schema.plugin(mongoosePaginate); +schema.plugin(idValidator); + + + +const Nirali = mongoose.model("Nirali",schema,"Nirali"); +module.exports = Nirali \ No newline at end of file diff --git a/model/account.js b/model/account.js new file mode 100644 index 0000000..8696288 --- /dev/null +++ b/model/account.js @@ -0,0 +1,58 @@ +const mongoose = require("../config/db"); +const mongoosePaginate = require('mongoose-paginate-v2'); +var idValidator = require('mongoose-id-validator'); + +const myCustomLabels = { + totalDocs: 'itemCount', + docs: 'data', + limit: 'perPage', + page: 'currentPage', + nextPage: 'next', + prevPage: 'prev', + totalPages: 'pageCount', + pagingCounter: 'slNo', + meta: 'paginator', +}; + +mongoosePaginate.paginate.options = { + customLabels: myCustomLabels +}; + +const Schema = mongoose.Schema; +const schema = new Schema( +{ + + addedBy:{type:Schema.Types.ObjectId,ref:"user"}, + + data:{type:String}, + + isActive:Boolean, + + isDeleted:Boolean, + + name:{type:Number} + }, + { timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } } +); + + +schema.pre('save', async function(next) { + this.isDeleted = false; + this.isActive = true; + next(); +}); + + + +schema.method("toJSON", function () { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; +}); +schema.plugin(mongoosePaginate); +schema.plugin(idValidator); + + + +const account = mongoose.model("account",schema,"account"); +module.exports = account \ No newline at end of file diff --git a/model/user.js b/model/user.js new file mode 100644 index 0000000..06fba5d --- /dev/null +++ b/model/user.js @@ -0,0 +1,83 @@ +const mongoose = require("../config/db"); +const mongoosePaginate = require('mongoose-paginate-v2'); +var idValidator = require('mongoose-id-validator'); +const bcrypt = require("bcrypt"); +const{USER_ROLE,DEFAULT_ROLE} = require("../config/authConstant"); +const {convertObjectToEnum} = require("../utils/common") + +const myCustomLabels = { + totalDocs: 'itemCount', + docs: 'data', + limit: 'perPage', + page: 'currentPage', + nextPage: 'next', + prevPage: 'prev', + totalPages: 'pageCount', + pagingCounter: 'slNo', + meta: 'paginator', +}; + +mongoosePaginate.paginate.options = { + customLabels: myCustomLabels +}; + +const Schema = mongoose.Schema; +const schema = new Schema( +{ + + addedBy:{type:Schema.Types.ObjectId,ref:"user"}, + + emails:[{email:{type:String}}], + + isActive:Boolean, + + isDeleted:Boolean, + + loginOTP:{code:String,expireTime:Date}, + + loginTry:{type:Number,default:0}, + + name:{type:String}, + + password:{type:String}, + + resetPasswordLink:{code:String,expireTime:Date}, + + role:{type:Number,enum:convertObjectToEnum(USER_ROLE)}, + + test:{type:Array}, + + username:{type:String} + }, + { timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } } +); + + +schema.pre('save', async function(next) { + this.isDeleted = false; + this.isActive = true; + if(this.password){ + this.password = await bcrypt.hash(this.password, 8); + } + next(); +}); + + + +schema.methods.isPasswordMatch = async function (password) { + const user = this; + return bcrypt.compare(password, user.password); +}; + +schema.method("toJSON", function () { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; +}); +schema.plugin(mongoosePaginate); +schema.plugin(idValidator); + + + +const user = mongoose.model("user",schema,"user"); +module.exports = user \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..e1481fa --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "node-test", + "version": "0.0.1", + "private": true, + "scripts": { + "start": "node app.js" + }, + "dependencies": { + "axios": "~0.21.1", + "bcrypt": "~5.0.0", + "cookie-parser": "~1.4.4", + "cors": "~2.8.5", + "debug": "~2.6.9", + "dotenv": "~8.2.0", + "ejs": "~3.1.6", + "express": "~4.16.1", + "express-rate-limit": "~5.2.6", + "formidable": "~1.2.2", + "joi": "~17.3.0", + "jsonwebtoken": "~8.5.1", + "moment": "~2.29.1", + "mongoose": "~5.11.8", + "mongoose-id-validator": "~0.6.0", + "mongoose-paginate-v2": "~1.3.12", + "mongoose-unique-validator": "~2.0.3", + "morgan": "~1.9.1", + "node-cron": "~3.0.0", + "nodemailer": "~6.5.0", + "passport": "~0.4.1", + "passport-jwt": "~4.0.0", + "uuid": "~8.3.2", + "valid-url": "~1.0.9" + } +} diff --git a/postman/postman-collection.json b/postman/postman-collection.json new file mode 100644 index 0000000..8bf2a6b --- /dev/null +++ b/postman/postman-collection.json @@ -0,0 +1,1053 @@ +{ + "info": { + "name": "Node Test", + "_postman_id": "8f91d0dc-c2a4-48b9-aaf9-caf88461216a", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" + }, + "item": [ + { + "name": "admin", + "description": "sdsd", + "item": [ + { + "name": "adminRoutes", + "description": "", + "item": [ + { + "name": "/", + "request": { + "url": "http://localhost:3000/admin/", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "http://localhost:3000/users", + "request": { + "url": "http://localhost:3000/adminhttp://localhost:3000/users", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "TEST", + "request": { + "url": "http://localhost:3000/adminTEST", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "description": "" + }, + "_postman_isSubFolder": true + } + ] + } + ] + }, + { + "name": "Admin", + "description": "Admin APIs", + "item": [ + { + "name": "Nirali", + "description": "Nirali Model APIs", + "item": [ + { + "name": "addNirali", + "request": { + "url": "http://localhost:3000/admin/nirali/create", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"test\": \"intuitive\",\n \"isDeleted\": true,\n \"isActive\": true\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "findAllNirali", + "request": { + "url": "http://localhost:3000/admin/nirali/list", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"query\": {},\n \"options\": {\n \"select\": [\n \"field 1\",\n \"field 2\"\n ],\n \"collation\": \"\",\n \"sort\": \"\",\n \"populate\": \"\",\n \"projection\": \"\",\n \"lean\": false,\n \"leanWithId\": true,\n \"offset\": 0,\n \"page\": 1,\n \"limit\": 10,\n \"customLabels\": {},\n \"pagination\": true,\n \"useEstimatedCount\": false,\n \"useCustomCountFn\": false,\n \"forceCountFn\": false,\n \"read\": {},\n \"options\": {}\n },\n \"isCountOnly\": false\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "getNirali", + "request": { + "url": "http://localhost:3000/admin/nirali/60c75b9d067fbe00e453a35b", + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "getNiraliCount", + "request": { + "url": "http://localhost:3000/admin/nirali/count", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"where\": {\n \"isActive\": true\n }\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "aggregateNirali", + "request": { + "url": "http://localhost:3000/admin/nirali/aggregate", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"group\": {\n \"avg\": {\n \"key\": {\n \"_id\": [\n \"field1\",\n \"field2\"\n ]\n }\n },\n \"sum\": {\n \"key\": {\n \"_id\": \"field\"\n }\n },\n \"min\": {\n \"key\": {\n \"_id\": \"field\"\n }\n },\n \"max\": {\n \"key\": {\n \"_id\": \"field\"\n }\n },\n \"addToSet\": {\n \"key\": {\n \"_id\": \"field\"\n }\n }\n },\n \"match\": {\n \"key\": {\n \"field\": \"value\"\n }\n },\n \"project\": {\n \"gt\": {\n \"key\": {\n \"field\": \"value\"\n }\n }\n }\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "updateNirali", + "request": { + "url": "http://localhost:3000/admin/nirali/update/60c75b9d067fbe00e453a35b", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"test\": \"intuitive\",\n \"isDeleted\": true,\n \"isActive\": true\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "partialupdateNirali", + "request": { + "url": "http://localhost:3000/admin/nirali/partial-update/60c75b9d067fbe00e453a35b", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"isActive\": true,\n \"isDeleted\": false\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "softDeleteNirali", + "request": { + "url": "http://localhost:3000/admin/nirali/softDelete/60c75b9d067fbe00e453a35b", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "insertBulkNirali", + "request": { + "url": "http://localhost:3000/admin/nirali/addBulk", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"data\": [\n {\n \"test\": \"intuitive\",\n \"isDeleted\": true,\n \"isActive\": true\n }\n ]\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "updateBulkNirali", + "request": { + "url": "http://localhost:3000/admin/nirali/updateBulk", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"filter\": {\n \"isActive\": true\n },\n \"data\": {\n \"isDeleted\": false\n }\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + } + ] + }, + { + "name": "File Upload", + "description": "Upload Files", + "item": [ + { + "name": "File upload in admin", + "request": { + "url": "http://localhost:3000/admin/upload", + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b3B0YWwuY29tIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwOi8vdG9wdGFsLmNvbS9qd3RfY2xhaW1zL2lzX2FkbWluIjp0cnVlLCJjb21wYW55IjoiVG9wdGFsIiwiYXdlc29tZSI6dHJ1ZX0.yRQYnWzskCZUxPwaQupWkiUzKELZ49eM7oWxAQK_ZXw", + "type": "text" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "file[]", + "type": "file", + "description": "Select file to upload" + }, + { + "key": "file[]", + "type": "file", + "disabled": true, + "description": "Select Another file to upload multiple files" + }, + { + "key": "folder", + "value": "Enter foldername", + "type": "text", + "disabled": true, + "description": "Optional, enable to upload file to specific folder" + }, + { + "key": "fileName", + "value": "Enter fileName", + "type": "text", + "disabled": true, + "description": "Optional, enable to give Specific file name to uploaded File" + } + ] + }, + "description": "" + }, + "_postman_isSubFolder": true + } + ] + }, + { + "name": "account", + "description": "account Model APIs", + "item": [ + { + "name": "addaccount", + "request": { + "url": "http://localhost:3000/admin/account/create", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"data\": \"Communications\",\n \"name\": 164,\n \"isDeleted\": false,\n \"isActive\": false\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "findAllaccount", + "request": { + "url": "http://localhost:3000/admin/account/list", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"query\": {},\n \"options\": {\n \"select\": [\n \"field 1\",\n \"field 2\"\n ],\n \"collation\": \"\",\n \"sort\": \"\",\n \"populate\": \"\",\n \"projection\": \"\",\n \"lean\": false,\n \"leanWithId\": true,\n \"offset\": 0,\n \"page\": 1,\n \"limit\": 10,\n \"customLabels\": {},\n \"pagination\": true,\n \"useEstimatedCount\": false,\n \"useCustomCountFn\": false,\n \"forceCountFn\": false,\n \"read\": {},\n \"options\": {}\n },\n \"isCountOnly\": false\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "getaccount", + "request": { + "url": "http://localhost:3000/admin/account/60c75b9d067fbe00e453a35c", + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "getaccountCount", + "request": { + "url": "http://localhost:3000/admin/account/count", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"where\": {\n \"isActive\": true\n }\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "aggregateaccount", + "request": { + "url": "http://localhost:3000/admin/account/aggregate", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"group\": {\n \"avg\": {\n \"key\": {\n \"_id\": [\n \"field1\",\n \"field2\"\n ]\n }\n },\n \"sum\": {\n \"key\": {\n \"_id\": \"field\"\n }\n },\n \"min\": {\n \"key\": {\n \"_id\": \"field\"\n }\n },\n \"max\": {\n \"key\": {\n \"_id\": \"field\"\n }\n },\n \"addToSet\": {\n \"key\": {\n \"_id\": \"field\"\n }\n }\n },\n \"match\": {\n \"key\": {\n \"field\": \"value\"\n }\n },\n \"project\": {\n \"gt\": {\n \"key\": {\n \"field\": \"value\"\n }\n }\n }\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "updateaccount", + "request": { + "url": "http://localhost:3000/admin/account/update/60c75b9d067fbe00e453a35c", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"data\": \"Communications\",\n \"name\": 164,\n \"isDeleted\": false,\n \"isActive\": false\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "partialupdateaccount", + "request": { + "url": "http://localhost:3000/admin/account/partial-update/60c75b9d067fbe00e453a35c", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"isActive\": true,\n \"isDeleted\": false\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "softDeleteaccount", + "request": { + "url": "http://localhost:3000/admin/account/softDelete/60c75b9d067fbe00e453a35c", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "insertBulkaccount", + "request": { + "url": "http://localhost:3000/admin/account/addBulk", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"data\": [\n {\n \"data\": \"Communications\",\n \"name\": 164,\n \"isDeleted\": false,\n \"isActive\": false\n }\n ]\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "updateBulkaccount", + "request": { + "url": "http://localhost:3000/admin/account/updateBulk", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"filter\": {\n \"isActive\": true\n },\n \"data\": {\n \"isDeleted\": false\n }\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + } + ] + }, + { + "name": "user", + "description": "user Model APIs", + "item": [ + { + "name": "adduser", + "request": { + "url": "http://localhost:3000/admin/user/create", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"Cecil Skiles\",\n \"password\": \"JqYt6Ji66pgnV7T\",\n \"emails\": [\n {\n \"email\": \"Lina.Nolan93@yahoo.com\"\n }\n ],\n \"name\": \"Brandon Borer\",\n \"isDeleted\": false,\n \"isActive\": true,\n \"role\": 1\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "findAlluser", + "request": { + "url": "http://localhost:3000/admin/user/list", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"query\": {},\n \"options\": {\n \"select\": [\n \"field 1\",\n \"field 2\"\n ],\n \"collation\": \"\",\n \"sort\": \"\",\n \"populate\": \"\",\n \"projection\": \"\",\n \"lean\": false,\n \"leanWithId\": true,\n \"offset\": 0,\n \"page\": 1,\n \"limit\": 10,\n \"customLabels\": {},\n \"pagination\": true,\n \"useEstimatedCount\": false,\n \"useCustomCountFn\": false,\n \"forceCountFn\": false,\n \"read\": {},\n \"options\": {}\n },\n \"isCountOnly\": false\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "getuser", + "request": { + "url": "http://localhost:3000/admin/user/60c75b9d067fbe00e453a35d", + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "getuserCount", + "request": { + "url": "http://localhost:3000/admin/user/count", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"where\": {\n \"isActive\": true\n }\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "aggregateuser", + "request": { + "url": "http://localhost:3000/admin/user/aggregate", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"group\": {\n \"avg\": {\n \"key\": {\n \"_id\": [\n \"field1\",\n \"field2\"\n ]\n }\n },\n \"sum\": {\n \"key\": {\n \"_id\": \"field\"\n }\n },\n \"min\": {\n \"key\": {\n \"_id\": \"field\"\n }\n },\n \"max\": {\n \"key\": {\n \"_id\": \"field\"\n }\n },\n \"addToSet\": {\n \"key\": {\n \"_id\": \"field\"\n }\n }\n },\n \"match\": {\n \"key\": {\n \"field\": \"value\"\n }\n },\n \"project\": {\n \"gt\": {\n \"key\": {\n \"field\": \"value\"\n }\n }\n }\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "updateuser", + "request": { + "url": "http://localhost:3000/admin/user/update/60c75b9d067fbe00e453a35d", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"Cecil Skiles\",\n \"password\": \"JqYt6Ji66pgnV7T\",\n \"emails\": [\n {\n \"email\": \"Lina.Nolan93@yahoo.com\"\n }\n ],\n \"name\": \"Brandon Borer\",\n \"isDeleted\": false,\n \"isActive\": true,\n \"role\": 1\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "partialupdateuser", + "request": { + "url": "http://localhost:3000/admin/user/partial-update/60c75b9d067fbe00e453a35d", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"isActive\": true,\n \"isDeleted\": false\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "softDeleteuser", + "request": { + "url": "http://localhost:3000/admin/user/softDelete/60c75b9d067fbe00e453a35d", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "insertBulkuser", + "request": { + "url": "http://localhost:3000/admin/user/addBulk", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"data\": [\n {\n \"username\": \"Cecil Skiles\",\n \"password\": \"JqYt6Ji66pgnV7T\",\n \"emails\": [\n {\n \"email\": \"Lina.Nolan93@yahoo.com\"\n }\n ],\n \"name\": \"Brandon Borer\",\n \"isDeleted\": false,\n \"isActive\": true,\n \"role\": 1\n }\n ]\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "updateBulkuser", + "request": { + "url": "http://localhost:3000/admin/user/updateBulk", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + }, + { + "key": "Authorization", + "value": "Bearer Your-token", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"filter\": {\n \"isActive\": true\n },\n \"data\": {\n \"isDeleted\": false\n }\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + } + ] + }, + { + "name": "login", + "description": "Admin Login", + "item": [ + { + "name": "Send login OTP in Admin", + "request": { + "url": "http://localhost:3000/admin/auth/send_login_otp", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"Your username\",\n \"password\": \"YourPassword\"\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "Login With OTP in Admin", + "request": { + "url": "http://localhost:3000/admin/auth/login_with_otp", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"Your username\",\n \"password\": \"YourPassword\",\n \"code\": \"Your OTP\"\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "Change Password in Admin", + "request": { + "url": "http://localhost:3000/admin/user/change-password", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"60c75b9d067fbe00e453a35e\",\n \"newPassword\": \"YourNewPassword\"\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "Forgot Password in Admin", + "request": { + "url": "http://localhost:3000/admin/auth/forgot-password", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"yourmail@gmail.com\"\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "Validate OTP in Admin", + "request": { + "url": "http://localhost:3000/admin/auth/validate-otp", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"otp\": \"5898\"\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "Reset password in Admin", + "request": { + "url": "http://localhost:3000/admin/auth/reset-password", + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"code\": \"5898\",\n \"newPassword\": \"yourPassword\"\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + }, + { + "name": "Register User in Admin", + "request": { + "url": "http://localhost:3000/admin/auth/register", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"Cecil Skiles\",\n \"password\": \"JqYt6Ji66pgnV7T\",\n \"emails\": [\n {\n \"email\": \"Lina.Nolan93@yahoo.com\"\n }\n ],\n \"name\": \"Brandon Borer\",\n \"isDeleted\": false,\n \"isActive\": true,\n \"role\": 1\n}" + }, + "description": "" + }, + "_postman_isSubFolder": true + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/routes/admin/NiraliRoutes.js b/routes/admin/NiraliRoutes.js new file mode 100644 index 0000000..1b4efbb --- /dev/null +++ b/routes/admin/NiraliRoutes.js @@ -0,0 +1,16 @@ +const express = require('express'); +const router = express.Router(); +const NiraliController = require("../../controller/admin/NiraliController") +const auth = require("../../middleware/auth"); + +router.route("/create").post(auth(...[ 'createByAdminInAdminPlatform' ]),NiraliController.addNirali); +router.route("/list").post(auth(...[ 'getAllByAdminInAdminPlatform' ]),NiraliController.findAllNirali); +router.route("/:id").get(auth(...[ 'getByAdminInAdminPlatform' ]),NiraliController.getNirali); +router.route("/count").post(auth(...[ 'getCountByAdminInAdminPlatform' ]),NiraliController.getNiraliCount); +router.route("/aggregate").post(auth(...[ 'aggregateByAdminInAdminPlatform' ]),NiraliController.getNiraliByAggregate); +router.route("/update/:id").put(auth(...[ 'updateByAdminInAdminPlatform' ]),NiraliController.updateNirali); +router.route("/partial-update/:id").put(auth(...[ 'partialUpdateByAdminInAdminPlatform' ]),NiraliController.partialUpdateNirali); +router.route("/softDelete/:id").put(auth(...[ 'softDeleteByAdminInAdminPlatform' ]),NiraliController.softDeleteNirali); +router.route("/addBulk").post(auth(...[ 'addBulkByAdminInAdminPlatform' ]),NiraliController.bulkInsertNirali); +router.route("/updateBulk").put(auth(...[ 'updateBulkByAdminInAdminPlatform' ]),NiraliController.bulkUpdateNirali); +module.exports = router; diff --git a/routes/admin/accountRoutes.js b/routes/admin/accountRoutes.js new file mode 100644 index 0000000..3835635 --- /dev/null +++ b/routes/admin/accountRoutes.js @@ -0,0 +1,16 @@ +const express = require('express'); +const router = express.Router(); +const accountController = require("../../controller/admin/accountController") +const auth = require("../../middleware/auth"); + +router.route("/create").post(auth(...[ 'createByAdminInAdminPlatform' ]),accountController.addAccount); +router.route("/list").post(auth(...[ 'getAllByAdminInAdminPlatform' ]),accountController.findAllAccount); +router.route("/:id").get(auth(...[ 'getByAdminInAdminPlatform' ]),accountController.getAccount); +router.route("/count").post(auth(...[ 'getCountByAdminInAdminPlatform' ]),accountController.getAccountCount); +router.route("/aggregate").post(auth(...[ 'aggregateByAdminInAdminPlatform' ]),accountController.getAccountByAggregate); +router.route("/update/:id").put(auth(...[ 'updateByAdminInAdminPlatform' ]),accountController.updateAccount); +router.route("/partial-update/:id").put(auth(...[ 'partialUpdateByAdminInAdminPlatform' ]),accountController.partialUpdateAccount); +router.route("/softDelete/:id").put(auth(...[ 'softDeleteByAdminInAdminPlatform' ]),accountController.softDeleteAccount); +router.route("/addBulk").post(auth(...[ 'addBulkByAdminInAdminPlatform' ]),accountController.bulkInsertAccount); +router.route("/updateBulk").put(auth(...[ 'updateBulkByAdminInAdminPlatform' ]),accountController.bulkUpdateAccount); +module.exports = router; diff --git a/routes/admin/auth.js b/routes/admin/auth.js new file mode 100644 index 0000000..7a97d89 --- /dev/null +++ b/routes/admin/auth.js @@ -0,0 +1,11 @@ +const express = require("express"); +const routes = express.Router(); +const authController = require("../../controller/admin/authController") +routes.route("/register").post(authController.register); +routes.post("/send_login_otp",authController.sendOtpForLogin); +routes.post("/login_with_otp",authController.loginWithOTP); +routes.route("/forgot-password").post(authController.forgotPassword); +routes.route("/validate-otp").post(authController.validateResetPasswordOtp); +routes.route("/reset-password").put(authController.resetPassword); + +module.exports = routes; \ No newline at end of file diff --git a/routes/admin/index.js b/routes/admin/index.js new file mode 100644 index 0000000..ca1f1a8 --- /dev/null +++ b/routes/admin/index.js @@ -0,0 +1,12 @@ +const express = require("express") +const router = express.Router() +router.use("/auth",require("./auth")); +router.use("/nirali",require("./NiraliRoutes")); +router.use("/account",require("./accountRoutes")); +router.use("/user",require("./userRoutes")); +router.use("/upload",require("./uploadRoutes")); +router.use("/",require("./testRoutes")); +router.use("/",require("./testRoutes")); +router.use("/",require("./testRoutes")); + +module.exports = router; diff --git a/routes/admin/testRoutes.js b/routes/admin/testRoutes.js new file mode 100644 index 0000000..a597762 --- /dev/null +++ b/routes/admin/testRoutes.js @@ -0,0 +1,15 @@ +const express = require("express"); +const router = express.Router() +const testController = require("../../controller/admin/testController"); + +router.post("/", (req, res) => { + testController.test(req,res); +}) +router.post("http://localhost:3000/users", (req, res) => { + testController.test(req,res); +}) +router.post("TEST", (req, res) => { + testController.test(req,res); +}) + +module.exports = router; \ No newline at end of file diff --git a/routes/admin/uploadRoutes.js b/routes/admin/uploadRoutes.js new file mode 100644 index 0000000..ccf8c75 --- /dev/null +++ b/routes/admin/uploadRoutes.js @@ -0,0 +1,11 @@ +var express = require('express'); +var router = express.Router(); + +const fileUploadController = require("../../controller/admin/fileUploadController"); +const auth = require("../../middleware/auth"); +router.post("/",auth(...[ + 'fileUploadByUserInAdminPlatform', + 'fileUploadByAdminInAdminPlatform' +]),fileUploadController.upload); + +module.exports = router; \ No newline at end of file diff --git a/routes/admin/userRoutes.js b/routes/admin/userRoutes.js new file mode 100644 index 0000000..df61b02 --- /dev/null +++ b/routes/admin/userRoutes.js @@ -0,0 +1,17 @@ +const express = require('express'); +const router = express.Router(); +const userController = require("../../controller/admin/userController") +const auth = require("../../middleware/auth"); + +router.route("/create").post(userController.addUser); +router.route("/list").post(auth(...[ 'getAllByAdminInAdminPlatform' ]),userController.findAllUser); +router.route("/:id").get(auth(...[ 'getByAdminInAdminPlatform' ]),userController.getUser); +router.route("/count").post(auth(...[ 'getCountByAdminInAdminPlatform' ]),userController.getUserCount); +router.route("/aggregate").post(auth(...[ 'aggregateByAdminInAdminPlatform' ]),userController.getUserByAggregate); +router.route("/update/:id").put(auth(...[ 'updateByAdminInAdminPlatform' ]),userController.updateUser); +router.route("/partial-update/:id").put(auth(...[ 'partialUpdateByAdminInAdminPlatform' ]),userController.partialUpdateUser); +router.route("/softDelete/:id").put(auth(...[ 'softDeleteByAdminInAdminPlatform' ]),userController.softDeleteUser); +router.route("/addBulk").post(userController.bulkInsertUser); +router.route("/updateBulk").put(auth(...[ 'updateBulkByAdminInAdminPlatform' ]),userController.bulkUpdateUser); +router.route("/change-password").put(auth(...[ 'changePasswordByAdminInAdminPlatform' ]),userController.changePassword); +module.exports = router; diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..c0c656e --- /dev/null +++ b/routes/index.js @@ -0,0 +1,6 @@ +const express = require("express") +const router = express.Router() + +router.use("/admin",require("./admin/index")); + +module.exports =router \ No newline at end of file diff --git a/services/admin/testService.js b/services/admin/testService.js new file mode 100644 index 0000000..03efcfe --- /dev/null +++ b/services/admin/testService.js @@ -0,0 +1,36 @@ +/* +* dfdf +*/ +const test=()=>{ + try { + return true; + } catch (error) { + throw error; + } +} +/* +* test +*/ +const test=()=>{ + try { + return true; + } catch (error) { + throw error; + } +} +/* +* sdsd +*/ +const test=()=>{ + try { + return true; + } catch (error) { + throw error; + } +} +module.exports={ + test, + test, + test, +} + diff --git a/services/auth.js b/services/auth.js new file mode 100644 index 0000000..8ccba53 --- /dev/null +++ b/services/auth.js @@ -0,0 +1,237 @@ +const User = require("../model/user") +const dbService = require("../utils/dbService"); +const { JWT,LOGIN_ACCESS, + PLATFORM,MAX_LOGIN_RETRY_LIMIT,DEFAULT_SEND_LOGIN_OTP,SEND_LOGIN_OTP,FORGOT_PASSWORD_WITH,NO_OF_DEVICE_ALLOWED} = require("../config/authConstant"); +const jwt = require("jsonwebtoken"); +const common = require("../utils/common"); +const moment = require("moment"); +const bcrypt = require("bcrypt"); +const emailService = require("./email/emailService"); +const sendSMS = require("./sms/smsService"); +const uuid = require("uuid").v4; + +async function generateToken(user,secret){ + return jwt.sign( {id:user.id,username:user.username}, secret, { + expiresIn: JWT.EXPIRES_IN * 60 + }); +} +async function sendEmailForResetPasswordLink(user) { + try { + let token = uuid(); + let expires = moment(); + expires = expires.add(FORGOT_PASSWORD_WITH.EXPIRETIME, "minutes").toISOString(); + await dbService.updateDocument(User,user.id, + { resetPasswordLink: { code: token, expireTime: expires } }); + let viewType = "/reset-password/"; + let msg = "Click on the link below to reset your password."; + let mailObj = { + subject: "Reset Password", + to: user.email, + template: "/views/resetPassword", + data: { + link: "http://localhost:3000" + viewType + token, + linkText: "Reset Password", + message:msg + } + }; + await emailService.sendEmail(mailObj); + return true; + } catch (e) { + return false; + } +} +async function sendSMSForResetPasswordLink(user) { + try { + let token = uuid(); + let expires = moment(); + expires = expires.add(FORGOT_PASSWORD_WITH.EXPIRETIME, "minutes").toISOString(); + await dbService.updateDocument(User,user.id, + { resetPasswordLink: { code: token, expireTime: expires } }); + let viewType = "/reset-password/"; + let msg = `Click on the link to reset your password. + http://localhost:3000${viewType + token}`; + let smsObj = { + to:user.mobileNo, + message:msg + } + await sendSMS(smsObj); + return true; + } catch (error) { + return false; + } +} +async function sendSMSForLoginOtp(user) { + try { + let otp = common.randomNumber(); + let expires = moment(); + expires = expires.add(6, "hours").toISOString(); + await dbService.updateDocument(User, user.id, { loginOTP: { code: otp, expireTime: expires } }); + let message = `OTP code for Login `; + let otpMsg = `${message}: ${otp}`; + let smsObj = { + to:user.mobileNo, + message:otpMsg + } + await sendSMS(smsObj); + return true; + } catch (error) { + return false; + } +} +let auth = module.exports = {} + auth.loginUser=async(username,password,url) => { + try { + let where = {$or:[{username:username},{password:username}]} + const user = await dbService.getDocumentByQuery(User,where); + if (user) { + if(user.loginTry >= MAX_LOGIN_RETRY_LIMIT){ + return { flag:true, message:"you have exceed the number of limit.you have to reset the password"} + } + const isPasswordMatched = await user.isPasswordMatch(password); + if (isPasswordMatched) { + const {password,...userData}=user.toJSON() + let token; + if(!user.role){ + return {flag:true, data:'You have no assigned any role'} + } + if(url.includes('admin')){ + if(!LOGIN_ACCESS[user.role].includes(PLATFORM.ADMIN)){ + return {flag:true, data:'you are unable to access this platform'} + } + token = await generateToken(userData,JWT.ADMIN_SECRET) + } + if(user.loginRetryLimit){ + await dbService.updateDocument(User,user.id,{loginTry:0}); + } + let tokens = user.tokens; + if(user.tokens.length == NO_OF_DEVICE_ALLOWED){ + tokens.pop(); + } + tokens.unshift(token); + await dbService.updateDocument(User,user.id,{tokens}); + delete userData.tokens; + const userToReturn = { ...userData, ...{ token } }; + return {flag:false,data:userToReturn} + } else { + await dbService.updateDocument(User,user.id,{loginTry:user.loginTry+1}); + return {flag:true,data:'Incorrect Password'} + } + } else { + return {flag:true,data:'User not exists'} + } + } catch (error) { + throw new Error(error.message) + } + }, + auth.changePassword=async(params)=>{ + try { + let password = params.newPassword; + let where = {_id:params.userId}; + let user = await dbService.getDocumentByQuery(User,where); + if (user && user.id) { + password = await bcrypt.hash(password, 8); + let updatedUser = dbService.updateDocument(User,user.id,{password}); + if (updatedUser) { + return {flag:false,data:user}; + } + return {flag:true,data:'password can not changed due to some error.please try again'} + } + return {flag:true,data:'User not found'} + } catch (error) { + throw new Error(error.message) + } + }, + auth.sendResetPasswordNotification=async (user) => { + let resultOfEmail=false; + let resultOfSMS=false; + try { + if(FORGOT_PASSWORD_WITH.LINK.email){ + resultOfEmail = await sendEmailForResetPasswordLink(user); + } + if(FORGOT_PASSWORD_WITH.LINK.sms){ + resultOfSMS = await sendSMSForResetPasswordLink(user); + } + return {resultOfEmail,resultOfSMS}; + } catch (error) { + throw new Error(error.message) + } + }, + auth.resetPassword=async (user, newPassword) => { + try { + let where = { _id: user.id }; + const dbUser = await dbService.getDocumentByQuery(User,where); + if (!dbUser) { + return { + flag: false, + data: "User not found", + }; + } + newPassword = await bcrypt.hash(newPassword, 8); + await dbService.updateDocument(User, user.id, { + password: newPassword, + resetPasswordLink: null, + loginTry:0 + }); + let mailObj = { + subject: 'Reset Password', + to: user.email, + template: '/views/successfullyResetPassword', + data: { + isWidth: true, + email: user.email || '-', + message: "Password Successfully Reset" + } + }; + await emailService.sendEmail(mailObj); + return { + flag: false, + data: "Password reset successfully", + }; + } catch (error) { + throw new Error(error.message) + } + }, + auth.sendLoginOTP = async (username,password) => { + try { + let where = {$or:[{username:username},{password:username}]} + let user = await dbService.getDocumentByQuery(User,where); + if(!user){ + return {flag:true, data:'User not found'} + } + if(user.loginTry >= MAX_LOGIN_RETRY_LIMIT){ + return {flag:true,data:'you have exceed the number of limit.you have to reset the password'} + } + const isPasswordMatched = await user.isPasswordMatch(password); + if (!isPasswordMatched) { + await dbService.updateDocument(User,user.id,{loginTry:user.loginTry+1}); + return {flag:true,data:'Incorrect Password'} + } + let res; + if(DEFAULT_SEND_LOGIN_OTP===SEND_LOGIN_OTP.EMAIL){ + // send Email here + }else if(DEFAULT_SEND_LOGIN_OTP===SEND_LOGIN_OTP.SMS){ + // send SMS here + res = await sendSMSForLoginOtp(user); + } + if(!res){ + return {flag:false,data:"otp can not be sent due to some issue try again later."}; + } + return {flag:false,data:"Please check your email for OTP"}; + } catch (error) { + throw new Error(error.message) + } + }, + auth.loginWithOTP = async (username, password, url) => { + try { + let result = await auth.loginUser(username, password, url); + if (!result.flag) { + result.loginOTP=null; + await dbService.updateDocument(User,{_id:result.data.id},{ loginOTP: null }); + return {flag:false,data:result} + } + return {flag:true,data:result.data} + + } catch (error) { + throw new Error(error.message) + } + } \ No newline at end of file diff --git a/services/customQueryService.js b/services/customQueryService.js new file mode 100644 index 0000000..713615f --- /dev/null +++ b/services/customQueryService.js @@ -0,0 +1,77 @@ + +const find = async (model, { filter = {}, populate, skip, limit, select, sort }) => { + let query = model.find(filter) + if (select) { + query = query.select(select) + } + if (populate) { + query = query.populate(populate) + } + if (skip) { + query = query.skip(skip) + } + if (limit) { + query = query.limit(limit) + } + if (sort) { + query = query.sort(sort) + } + return await query.exec() +} +const create = async(model,data,options={})=>{ + try { + if(data && data.length){ + return await model.create(data,options) + }else{ + return await model.create([data],options) + } + } catch (error) { + throw new Error(error.message) + } +} +const findOneAndUpdate = async(model,filter,data,options={new:true})=>{ + try { + return await model.findOneAndUpdate(filter,data,options) + } catch (error) { + throw new Error(error.message) + } +} +const findOneAndDelete = async(model,filter,options={})=>{ + try { + return await model.findOneAndDelete(filter,options) + } catch (error) { + throw new Error(error.message) + } +} +const updateMany = async(model,filter,data,options={})=>{ + try { + await model.updateMany(filter,data,options) + return await model.find(filter); + } catch (error) { + throw new Error(error.message) + } +} +const deleteMany = async(model,filter,options={})=>{ + try { + await model.deleteMany(filter,data,options) + return await model.find(filter); + } catch (error) { + throw new Error(error.message) + } +} +const aggregate = async(model, queries)=>{ + try { + return await model.aggregate(queries) + } catch (error) { + throw new Error(error.message) + } +} +module.exports = { + find, + findOneAndUpdate, + findOneAndDelete, + updateMany, + deleteMany, + create, + aggregate +} diff --git a/services/email/emailService.js b/services/email/emailService.js new file mode 100644 index 0000000..fa063ca --- /dev/null +++ b/services/email/emailService.js @@ -0,0 +1,33 @@ +const nodemailer = require('nodemailer'); +const ejs = require("ejs") +module.exports = { + sendEmail: async (obj) => { + let transporter = nodemailer.createTransport({ + service: 'Mailgun', + auth: { + user: '', + pass: '' + } + }); + if (!Array.isArray(obj.to)) { + obj.to = [obj.to]; + } + const htmlText = await ejs.renderFile(`${__basedir}${obj.template}/html.ejs`, obj.data); + + return await Promise.all(obj.to.map((emailId) => { + var mailOpts = { + from: obj.from || "noreply@yoyo.co", + to: emailId, + subject: obj.subject, + html: htmlText + }; + transporter.sendMail(mailOpts, function (err, response) { + if (err) { + //ret.message = "Mail error."; + } else { + //ret.message = "Mail send."; + } + }); + })); + } +}; diff --git a/services/jobs/jobConfiguration.js b/services/jobs/jobConfiguration.js new file mode 100644 index 0000000..6e7ae98 --- /dev/null +++ b/services/jobs/jobConfiguration.js @@ -0,0 +1,3 @@ + +module.exports={ +}; \ No newline at end of file diff --git a/services/sms/smsService.js b/services/sms/smsService.js new file mode 100644 index 0000000..b6e95ff --- /dev/null +++ b/services/sms/smsService.js @@ -0,0 +1,48 @@ +const axios = require('axios') +const sendSms = async (obj) => { + console.log('SMS---', obj); + if (obj.to) { + obj.mobiles = obj.to; + } + let mobiles; + if (Array.isArray(obj.mobiles)) { + obj.mobiles = obj.mobiles.map((m) => { + let tmpNo = m.split('+'); + return tmpNo[1] ? tmpNo[1] : tmpNo[0]; + }); + mobiles = obj.mobiles.join(','); + } + else { + let tmpNo = obj.mobiles.split('+'); + mobiles = tmpNo[1] ? tmpNo[1] : tmpNo[0]; + } + const message = obj.message + const userid = "" + const password = escape("You Password") + const v = 1.1 + const method = "sendMessage" + const msg_type = "text" + const send_to = mobiles + return await new Promise((resolve, reject) => { + axios( + { + url: `http://enterprise.smsgupshup.com/GatewayAPI/rest?msg=${message}&v=${v}&userid=${userid}&password=${password}&method=${method}&send_to=${send_to}&msg_type=${msg_type}`, + method: 'GET', + }) + .then(function (response) { + let response1 = response.data.split('|'); + console.log(response.data) + if (response1.length) { + if (response1[0].trim() === 'error') { + reject(response) + } else { + resolve(response) + } + } + }) + .catch(function (error) { + reject(error) + }); + }); +} +module.exports = sendSms \ No newline at end of file diff --git a/utils/common.js b/utils/common.js new file mode 100644 index 0000000..b985fa4 --- /dev/null +++ b/utils/common.js @@ -0,0 +1,25 @@ +module.exports = { + + /* + * convertObjectToEnum : convert object to enum + * @param obj : {} + */ + convertObjectToEnum: (obj) => { + const enumArr = []; + Object.values(obj).map((val) => enumArr.push(val)); + return enumArr; + }, + + /* + * randomNumber : generate random numbers. + * @param length : number *default 4 + */ + randomNumber: (length = 4) => { + const numbers = '12345678901234567890'; + let result = ''; + for (let i = length; i > 0; i -= 1) { + result += numbers[Math.round(Math.random() * (numbers.length - 1))]; + } + return result; + }, +}; diff --git a/utils/dbService.js b/utils/dbService.js new file mode 100644 index 0000000..d79e23d --- /dev/null +++ b/utils/dbService.js @@ -0,0 +1,314 @@ +/* +* createDocument : create any mongoose document +* @param model : mongoose model +* @param data : {} +*/ +const createDocument = (model, data) => new Promise((resolve, reject) => { + model.create(data, (err, result) => { + if (err) reject(err); + else resolve(result); + }); +}); + +/* +* updateDocument : update any existing mongoose document +* @param model : mongoose model +* @param id : mongoose document's _id +* @param data : {} +*/ +const updateDocument = (model, id, data) => new Promise((resolve, reject) => { + model.updateOne({ _id: id }, data, { runValidators: true, context: 'query' }, (err, result) => { + if (err) reject(err); + else resolve(result); + }); +}); + +/* +* deleteDocument : delete any existing mongoose document +* @param model : mongoose model +* @param id : mongoose document's _id +*/ +const deleteDocument = (model, id) => new Promise((resolve, reject) => { + model.deleteOne({ _id: id }, (err, data) => { + if (err) reject(err); + else resolve(data); + }); +}); + +/* +* getAllDocuments : find all the mongoose document +* @param model : mongoose model +* @param query : {} +* @param options : {} +*/ +const getAllDocuments = (model, query, options) => new Promise((resolve, reject) => { + model.paginate(query, options, (err, data) => { + if (err) reject(err); + else resolve(data); + }); +}); + +/* +* getSingleDocumentById : find single mongoose document +* @param model : mongoose model +* @param id : mongoose document's _id +* @param select : [] *optional +*/ +const getSingleDocumentById = (model, id, select = []) => new Promise((resolve, reject) => { + model.findOne({ _id: id }, select, (err, data) => { + if (err) reject(err); + else resolve(data); + }); +}); + +/* +* findExistsData : find existing mongoose document +* @param model : mongoose model +* @params data : { +* "query":{ +* "and":[{"Name":"Dhiraj"},{"Salary":300}], +* "or":[{"Name":"Dhiraj"},{"Salary":300}] +* } +* } +*/ +const findExistsData = (model, data) => { + // let { model } = data; + const { query } = data; + const { and } = query; + const { or } = query; + const q = {}; + + if (and) { + q.$and = []; + for (let index = 0; index < and.length; index += 1) { + q.$and.push(and[index]); + } + } + if (or) { + q.$or = []; + for (let index = 0; index < or.length; index += 1) { + q.$or.push(or[index]); + } + } + + return new Promise((resolve, reject) => { + model.find(q, (err, result) => { + if (err) reject(err); + else resolve(result); + }); + }); +}; +/* +* getDocumentByAggregation : find mongoose document by aggregation +* @param model : mongoose model +* @param query : {} +*/ +const getDocumentByAggregation = (model, query) => { + let keyInJson, valuesOfAggregate; + let valuesOfFields, keysOfFields; + let input = {}, finalInput = {}, aggregate = {}; + let array = []; + for (const [keys, values] of Object.entries(query)) { + for (const [key, value] of Object.entries(values)) { + switch (keys) { + case 'group': + keyInJson = 'key' in value; + if (keyInJson) { + valuesOfAggregate = Object.values(value); + valuesOfFields = Object.values(valuesOfAggregate[0]); + keysOfFields = Object.keys(valuesOfAggregate[0]); + for (const [nestKey, nestValue] of Object.entries(valuesOfFields)) { + if (Array.isArray(nestValue)) { + input._id = `$${keysOfFields[nestKey]}`; + for (const [i, j] of Object.entries(nestValue)) { + finalInput[`$${key}`]=''; + finalInput[`$${key}`]+= `$${j}`; + input[j] = finalInput; + finalInput = {}; + } + aggregate.$group = input; + array.push(aggregate); + } else { + input._id = `$${keysOfFields[nestKey]}`; + finalInput[`$${key}`]=''; + finalInput[`$${key}`] = `$${nestValue}`; + input[nestValue] = finalInput; + aggregate.$group = input; + array.push(aggregate); + } + } + } + aggregate = {}; + finalInput = {}; + input = {}; + break; + + case 'match': + valuesOfFields = Object.values(value).flat(); + keysOfFields = Object.keys(value); + if (Array.isArray(valuesOfFields) && valuesOfFields.length > 1) { + finalInput.$in = valuesOfFields; + input[keysOfFields[0]] = finalInput; + } else { + input[keysOfFields[0]] = valuesOfFields[0]; + } + aggregate.$match = input; + array.push(aggregate); + aggregate = {}; + input = {}; + finalInput = {}; + break; + + case 'project': + valuesOfFields = Object.values(value); + if (valuesOfFields.length === 1) { + const projectValues = Object.values(valuesOfFields[0]).toString(); + const projectKeys = Object.keys(valuesOfFields[0]).toString(); + const projectArr = []; + + if (isNaN(projectValues)) { + projectArr.push(`$${projectKeys}`); + projectArr.push(`$${projectValues}`); + } else { + projectArr.push(`$${projectKeys}`); + projectArr.push(projectValues); + } + finalInput[`$${key}`] = projectArr; + input[projectKeys]=finalInput; + aggregate.$project = input; + array.push(aggregate); + } + aggregate = {}; + input = {}; + finalInput = {}; + break; + } + } + } + return new Promise((resolve, reject) => { + model.aggregate(array, (err, data) => { + if (err) { + reject(err); + } + resolve(data); + }); + }); +}; + +/* +* softDeleteDocument : soft delete ( partially delete ) mongoose document +* @param model : mongoose model +* @param id : mongoose document's _id +*/ +const softDeleteDocument = (model, id) => new Promise(async (resolve, reject) => { + const result = await getSingleDocumentById(model, id); + result.isDeleted = true; + result.isActive = false; + model.updateOne({ _id: id }, result, (err, data) => { + if (err) reject(err); + else resolve(data); + }); +}); + +/* +* bulkInsert : create document in bulk mongoose document +* @param model : mongoose model +* @param data : {} +*/ +const bulkInsert = (model, data) => new Promise((resolve, reject) => { + model.insertMany(data, (err, result) => { + if (result !== undefined && result.length > 0) { + resolve(result); + } else { + reject(err); + } + }); +}); + +/* +* bulkInsert : update existing document in bulk mongoose document +* @param model : mongoose model +* @param filter : {} +* @param data : {} +*/ +const bulkUpdate = (model, filter, data) => new Promise((resolve, reject) => { + model.updateMany(filter, data, (err, result) => { + if (result !== undefined) { + resolve(result); + } else { + reject(err); + } + }); +}); + +/* +* countDocument : count total number of records in particular model +* @param model : mongoose model +* @param where : {} +*/ +const countDocument = (model, where) => new Promise((resolve, reject) => { + model.where(where).countDocuments((err, result) => { + if (result !== undefined) { + resolve(result); + } else { + reject(err); + } + }); +}); + +/* +* getDocumentByQuery : find document by dynamic query +* @param model : mongoose model +* @param where : {} +* @param select : [] *optional +*/ +const getDocumentByQuery = (model, where, select = []) => new Promise((resolve, reject) => { + model.findOne(where, select, (err, data) => { + if (err) reject(err); + else resolve(data); + }); +}); + +/* +* findOneAndUpdateDocument : find existing document and update mongoose document +* @param model : mongoose model +* @param filter : {} +* @param data : {} +* @param options : {} *optional +*/ +const findOneAndUpdateDocument = (model, filter, data, options = {}) => new Promise((resolve, reject) => { + model.findOneAndUpdate(filter, data, options, (err, result) => { + if (err) reject(err); + else resolve(result); + }); +}); + +/* +* findOneAndDeleteDocument : find existing document and delete mongoose document +* @param model : mongoose model +* @param filter : {} +* @param options : {} *optional +*/ +const findOneAndDeleteDocument = (model, filter, options = {}) => new Promise((resolve, reject) => { + model.findOneAndDelete(filter, options, (err, data) => { + if (err) reject(err); + else resolve(data); + }); +}); + +module.exports = { + createDocument, + getAllDocuments, + updateDocument, + deleteDocument, + getSingleDocumentById, + findExistsData, + softDeleteDocument, + bulkInsert, + bulkUpdate, + countDocument, + getDocumentByQuery, + getDocumentByAggregation, + findOneAndUpdateDocument, + findOneAndDeleteDocument, +}; diff --git a/utils/deleteDependent.js b/utils/deleteDependent.js new file mode 100644 index 0000000..9d4bf62 --- /dev/null +++ b/utils/deleteDependent.js @@ -0,0 +1,41 @@ +const mongoose = require('../config/db'); +const dbService = require('./dbService'); + +const deleteDependentWarning = async (possibleDependent, refId) => Promise.all(possibleDependent.map(async (e) => { + try { + const allRecords = {}; + const where = {}; + where[e.refId] = refId; + const query = await dbService.countDocument(mongoose.model(e.model), where); + allRecords.model = e.model; + allRecords.count = query; + return allRecords; + } catch (error) { + throw new Error(error.message); + } +})); + +const deleteDependent = async (possibleDependent, refId) => Promise.all(possibleDependent.map(async (e) => { + try { + const where = {}; + where[e.refId] = refId; + return await mongoose.model(e.model).deleteMany(where); + } catch (error) { + throw new Error(error.message); + } +})); + +const softDeleteDependent = (possibleDependent, refId) => Promise.all(possibleDependent.map(async (e) => { + try { + const where = {}; + where[e.refId] = refId; + const query = await mongoose.model(e.model).updateMany(where, { isDeleted: true }); + return query; + } catch (error) { + throw new Error(error.message); + } +})); + +module.exports = { + deleteDependentWarning, deleteDependent, softDeleteDependent, +}; diff --git a/utils/messages.js b/utils/messages.js new file mode 100644 index 0000000..8651e71 --- /dev/null +++ b/utils/messages.js @@ -0,0 +1,89 @@ +const responseStatusCode = require('./responseCode'); + +exports.successResponse = (data, res) => res.status(responseStatusCode.success).json({ + STATUS: 'Success', + MESSAGE: 'Your request is successfully executed', + DATA: data, +}); + +exports.failureResponse = (data, res) => res.status(responseStatusCode.internalServerError).json({ + STATUS: 'Failure', + MESSAGE: 'Internal Server Error', + DATA: data, +}); + +exports.badRequest = (data, res) => res.status(responseStatusCode.badRequest).json({ + STATUS: 'Bad Request', + MESSAGE: 'The request cannot be fulfilled due to bad syntax', + DATA: data, +}); + +exports.validationError = (data, res) => res.status(responseStatusCode.validationError).json({ + STATUS: 'Validation Error', + MESSAGE: 'Invalid Data, Validation Failed', + DATA: data, +}); + +exports.isDuplicate = (data, res) => res.status(responseStatusCode.validationError).json({ + STATUS: 'Validation Error', + MESSAGE: 'Data Duplication Found', + DATA: data, +}); + +exports.recordNotFound = (data, res) => res.status(responseStatusCode.success).json({ + STATUS: 'Success', + MESSAGE: 'Record not found with specified criteria.', + DATA: data, +}); + +exports.insufficientParameters = (res) => res.status(responseStatusCode.badRequest).json({ + STATUS: 'FAILURE', + MESSAGE: 'Insufficient parameters', + DATA: {} +}); + +exports.notFound = (err, res) => res.status(responseStatusCode.notFound).json({ + STATUS: 'Not Found', + MESSAGE: 'The requested resource could not be found but may be available again in the future', + DATA: {} +}); + +exports.mongoError = (err, res) => res.status(responseStatusCode.internalServerError).json({ + STATUS: 'Failure', + MESSAGE: 'Mongo db related error', + DATA: err, +}); + +exports.inValidParam = (err, res) => res.status(responseStatusCode.validationError).json({ + STATUS: 'Validation Error', + MESSAGE: 'Invalid values in parameters', + DATA: err, +}); + +exports.unAuthorizedRequest = (err, res) => res.status(responseStatusCode.unAuthorizedRequest).json({ + STATUS: 'Unauthorized', + MESSAGE: 'You are not authorized to access the request', + ERROR: err, +}); + +exports.loginSuccess = (data, res) => res.status(responseStatusCode.success).json({ + STATUS: 'Success', + MESSAGE: 'Login Successful', + DATA: data, +}); + +exports.passwordEmailWrong = (res) => res.status(responseStatusCode.unAuthorizedRequest).json({ + STATUS: 'Unauthorized', + MESSAGE: 'email or password may be wrong', + DATA: {}, +}); +exports.loginFailed = (data, res) => res.status(responseStatusCode.unAuthorizedRequest).json({ + STATUS: 'Unauthorized', + MESSAGE: 'Login Failed', + DATA: data, +}); +exports.failedSoftDelete = (res) => res.status(responseStatusCode.internalServerError).json({ + STATUS: 'Failure', + MESSAGE: 'Data can not be deleted due to internal server error', + DATA: {}, +}); diff --git a/utils/responseCode.js b/utils/responseCode.js new file mode 100644 index 0000000..b3f0fb8 --- /dev/null +++ b/utils/responseCode.js @@ -0,0 +1,6 @@ +exports.success = 200; +exports.badRequest = 400; +exports.internalServerError = 500; +exports.unAuthorizedRequest = 401; +exports.notFound = 404; +exports.validationError = 422; diff --git a/utils/validateRequest.js b/utils/validateRequest.js new file mode 100644 index 0000000..cfd4abd --- /dev/null +++ b/utils/validateRequest.js @@ -0,0 +1,27 @@ +const joi = require('joi'); + +exports.validateParamsWithJoi = (body, schemaKeys) => { + const schema = joi.object(schemaKeys); + try { + const { + error, value, + } = schema.validate(body, { + abortEarly: false, + }); + + if (error && error.details) { + const data = { + error: true, + details: error.details, + }; + return data; + } + return value; + } catch (err) { + const data = { + error: true, + details: err, + }; + return data; + } +}; diff --git a/utils/validation/NiraliValidation.js b/utils/validation/NiraliValidation.js new file mode 100644 index 0000000..a27cc67 --- /dev/null +++ b/utils/validation/NiraliValidation.js @@ -0,0 +1,19 @@ +/* + * modelValidation.js + * purpose : request validation + * description : validate each post and put request as per mongoose model + **/ + +const joi = require("joi") +exports.schemaKeys = { + id: joi.string().regex(/^[0-9a-fA-F]{24}$/), + test: joi.string(), + isActive: joi.boolean(), + isDeleted: joi.boolean() +}; +exports.updateSchemaKeys = { + id: joi.string().regex(/^[0-9a-fA-F]{24}$/), + test: joi.string(), + isActive: joi.boolean(), + isDeleted: joi.boolean() +}; \ No newline at end of file diff --git a/utils/validation/accountValidation.js b/utils/validation/accountValidation.js new file mode 100644 index 0000000..54d3a54 --- /dev/null +++ b/utils/validation/accountValidation.js @@ -0,0 +1,21 @@ +/* + * modelValidation.js + * purpose : request validation + * description : validate each post and put request as per mongoose model + **/ + +const joi = require("joi") +exports.schemaKeys = { + id: joi.string().regex(/^[0-9a-fA-F]{24}$/), + data: joi.string(), + name: joi.number().integer(), + isActive: joi.boolean(), + isDeleted: joi.boolean() +}; +exports.updateSchemaKeys = { + id: joi.string().regex(/^[0-9a-fA-F]{24}$/), + data: joi.string(), + name: joi.number().integer(), + isActive: joi.boolean(), + isDeleted: joi.boolean() +}; \ No newline at end of file diff --git a/utils/validation/userValidation.js b/utils/validation/userValidation.js new file mode 100644 index 0000000..9466cee --- /dev/null +++ b/utils/validation/userValidation.js @@ -0,0 +1,33 @@ +/* + * modelValidation.js + * purpose : request validation + * description : validate each post and put request as per mongoose model + **/ + +const {USER_ROLE} = require("../../config/authConstant"); +const {convertObjectToEnum} = require("../common"); +const joi = require("joi") +exports.schemaKeys = { + id: joi.string().regex(/^[0-9a-fA-F]{24}$/), + username: joi.string(), + password: joi.string(), + emails: joi.array().items(), + name: joi.string(), + role: joi.number().integer().valid(...convertObjectToEnum(USER_ROLE)), + resetPasswordLink: joi.object({code:joi.string(),expireTime:joi.date()}), + loginTry: joi.number().integer(), + isActive: joi.boolean(), + isDeleted: joi.boolean() +}; +exports.updateSchemaKeys = { + id: joi.string().regex(/^[0-9a-fA-F]{24}$/), + username: joi.string(), + password: joi.string(), + emails: joi.array().items(), + name: joi.string(), + role: joi.number().integer().valid(...convertObjectToEnum(USER_ROLE)), + resetPasswordLink: joi.object({code:joi.string(),expireTime:joi.date()}), + loginTry: joi.number().integer(), + isActive: joi.boolean(), + isDeleted: joi.boolean() +}; \ No newline at end of file diff --git a/views/emailTemplate/html.ejs b/views/emailTemplate/html.ejs new file mode 100644 index 0000000..2b1b7ac --- /dev/null +++ b/views/emailTemplate/html.ejs @@ -0,0 +1,44 @@ + + + + + + + + + +
+
+
+ +
+
+
+

+ Dear , +

+
+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/views/resetPassword/html.ejs b/views/resetPassword/html.ejs new file mode 100644 index 0000000..a22e881 --- /dev/null +++ b/views/resetPassword/html.ejs @@ -0,0 +1,51 @@ + + + + + + + + + + + + +
+
+
+ Reset Password Link +
+
+
+

+ Dear User,<%-message%> +

+
+

+ <%-linkText%> +

+
+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/views/sendOTP/html.ejs b/views/sendOTP/html.ejs new file mode 100644 index 0000000..6e39993 --- /dev/null +++ b/views/sendOTP/html.ejs @@ -0,0 +1,53 @@ + + + + + + + + + <% if(otp) { %> + + + + <% } %> + +
+
+
+ OTP +
+
+
+

+ Dear User, +

+
+

+ Your OTP code is <%- otp %> +

+
+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/views/successfullyResetPassword/html.ejs b/views/successfullyResetPassword/html.ejs new file mode 100644 index 0000000..11b6b29 --- /dev/null +++ b/views/successfullyResetPassword/html.ejs @@ -0,0 +1,37 @@ + + + + + + +
+
+
+ <%-message%> +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +