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%> +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +