diff --git a/components/belonging/belongingModel.js b/components/belonging/belongingModel.js index 5ac887e..b8d9a8c 100644 --- a/components/belonging/belongingModel.js +++ b/components/belonging/belongingModel.js @@ -1,5 +1,6 @@ const mongoose = require('mongoose') const ObjectID = mongoose.Schema.Types.ObjectId +const AutoIncrement = require('mongoose-sequence')(mongoose) const belongingSchema = new mongoose.Schema( { @@ -10,14 +11,14 @@ const belongingSchema = new mongoose.Schema( }, belongingCategory: { type: ObjectID, - ref: 'Category' + ref: 'Category', }, projectCode: { type: String, required: true, default: false, }, - relatedProjects : [String], + relatedProjects: [String], location: { type: { type: String, @@ -29,12 +30,27 @@ const belongingSchema = new mongoose.Schema( required: true, }, }, - documents: [String] + documents: [String], + numId: { + type: Number, + unique: true, + }, + isActive: { + type: Boolean, + default: true, + select: false, + }, }, - { timestamps: true } + { + toJSON: { virtuals: true }, + toObject: { virtuals: true }, + timestamps: true, + } ) -belongingSchema.index({ location: '2dsphere' }); // Enable geospatial queries +belongingSchema.plugin(AutoIncrement, { inc_field: 'numId' }) + +belongingSchema.index({ location: '2dsphere' }) // Enable geospatial queries const Belonging = mongoose.model('Belonging', belongingSchema) module.exports = Belonging diff --git a/components/belonging/belongingRoutes.js b/components/belonging/belongingRoutes.js index 0e8fc7e..e3715e1 100644 --- a/components/belonging/belongingRoutes.js +++ b/components/belonging/belongingRoutes.js @@ -13,7 +13,7 @@ router.post('/admin/create', perm.requires('createabelonging'), belongingControl router.get('/admin/get-one/:belongingId', perm.requires('getabelonging'), belongingController.getBelonging) -router.put('/admin/update/:belongingId', perm.requires('updateabelonging'), belongingController.updateBelonging) +router.patch('/admin/update/:belongingId', perm.requires('updateabelonging'), belongingController.updateBelonging) router.delete('/admin/delete/:belongingId', perm.requires('deleteabelonging'), belongingController.deleteBelonging) diff --git a/components/category/categoryController.js b/components/category/categoryController.js new file mode 100644 index 0000000..6aef053 --- /dev/null +++ b/components/category/categoryController.js @@ -0,0 +1,57 @@ +const catchAsync = require('../../globalErrorHandler/catchAsync') +const categoryService = require('./categoryService') + +exports.getCategoryTree = catchAsync(async (req, res, next) => { + const categoryTree = await categoryService.getCategoryTree() + + res.status(200).json({ + status: 'success', + data: categoryTree, + }) +}) + +exports.getAllCategories = catchAsync(async (req, res, next) => { + const categories = await categoryService.getAllCategories() + + res.status(200).json({ + status: 'success', + results: categories.length, + data: categories, + }) +}) + +exports.createCategory = catchAsync(async (req, res, next) => { + const category = await categoryService.createCategory(req.body) + + res.status(201).json({ + status: 'success', + data: category, + }) +}) + +exports.getCategory = catchAsync(async (req, res, next) => { + const category = await categoryService.getCategory(req.params.categoryId) + + res.status(200).json({ + status: 'success', + data: category, + }) +}) + +exports.updateCategory = catchAsync(async (req, res, next) => { + const category = await categoryService.updateCategory(req.params.categoryId, req.body) + + res.status(200).json({ + status: 'success', + data: category, + }) +}) + +exports.deleteCategory = catchAsync(async (req, res, next) => { + await categoryService.deleteCategory(req.params.categoryId) + + res.status(204).json({ + status: 'success', + data: null, + }) +}) diff --git a/components/category/categoryModel.js b/components/category/categoryModel.js new file mode 100644 index 0000000..d4d0fb5 --- /dev/null +++ b/components/category/categoryModel.js @@ -0,0 +1,28 @@ +const mongoose = require('mongoose') +const ObjectID = mongoose.Schema.Types.ObjectId + +const categorySchema = new mongoose.Schema( + { + nativeName: { + type: String, + required: true, + }, + englishName: { + type: String, + required: true, + unique: true, + }, + parentId: { + type: ObjectID, + ref: 'Category', + }, + }, + { + toJSON: { virtuals: true }, + toObject: { virtuals: true }, + timestamps: true, + } +) + +const Category = mongoose.model('Category', categorySchema) +module.exports = Category diff --git a/components/category/categoryRoutes.js b/components/category/categoryRoutes.js new file mode 100644 index 0000000..ba29d6a --- /dev/null +++ b/components/category/categoryRoutes.js @@ -0,0 +1,22 @@ +const express = require('express') +const categoryController = require('./categoryController') +const perm = require('../../middlewares/permissionMiddleware') +const loginCheck = require('../../middlewares/loginCheck') + +const router = new express.Router() + +router.use(loginCheck.loginProtect) + +router.get('/admin/get-tree', perm.requires('getcategorytree'), categoryController.getCategoryTree) + +router.get('/admin/get-all', perm.requires('getallcategories'), categoryController.getAllCategories) + +router.post('/admin/create', perm.requires('createcategory'), categoryController.createCategory) + +router.get('/admin/get-one/:categoryId', perm.requires('getcategory'), categoryController.getCategory) + +router.patch('/admin/update/:categoryId', perm.requires('updatecategory'), categoryController.updateCategory) + +router.delete('/admin/delete/:categoryId', perm.requires('deletecategory'), categoryController.deleteCategory) + +module.exports = router diff --git a/components/category/categoryService.js b/components/category/categoryService.js new file mode 100644 index 0000000..f22ed30 --- /dev/null +++ b/components/category/categoryService.js @@ -0,0 +1,95 @@ +const Category = require('./categoryModel') +const AppError = require('../../globalErrorHandler/AppError') + +module.exports.getCategoryTree = async () => { + const nodes = await Category.find({}) + let tree = [] + for (let i = 0; i < nodes.length; i++) { + if (!nodes[i].parentId) { + const label = nodes[i].nativeName + const children = [] + const key = nodes[i]._id.toString() + const href = '/categories/admin/update/' + nodes[i]._id + const a_attr = { href } + const root = { label, children, key, a_attr } + tree.push(root) + } + } + + tree = recursiveFunction(tree, nodes) + return tree +} + +const recursiveFunction = (tree, categories) => { + if (tree.length > 0) { + for (let i = 0; i < tree.length; i++) { + let subTree = [] + for (let j = 0; j < categories.length; j++) { + if (tree[i].key === categories[j].parentId?.toString()) { + const label = categories[j].nativeName.toString() + const children = [] + const key = categories[j]._id.toString() + const href = '/categories/admin/update/' + categories[j]._id + const a_attr = { href } + subTree.push({ label, children, key, a_attr }) + } + let index = findCategoryIndex(tree, tree[i].label) + tree[index].children = subTree + } + } + for (let i = 0; i < tree.length; i++) { + let index = findCategoryIndex(tree, tree[i].label) + recursiveFunction(tree[index].children, categories) + } + } + return tree +} + +function findCategoryIndex(array, label) { + let index + for (let i = 0; i < array.length; i++) { + if (array[i].label === label) { + index = i + return index + } + } +} + +module.exports.getAllCategories = async () => { + return Category.find().populate('parentId') +} + +module.exports.createCategory = async (data) => { + const category = Category.create(data, null) + return category +} + +module.exports.getCategory = async (categoryId) => { + const category = await Category.findById(categoryId) + + if (!category) { + throw new AppError('NoCategoryFound', 404, {}) + } + + return category +} + +module.exports.updateCategory = async (categoryId, data) => { + const category = await Category.findOneAndUpdate({ _id: categoryId }, data, { new: true }) + + if (!category) { + throw new AppError('NoCategoryFound', 404, {}) + } + + return category +} + +module.exports.deleteCategory = async (categoryId) => { + const category = await Category.findByIdAndDelete(categoryId) + + if (!category) { + throw new AppError('NoCategoryFound', 404, {}) + } + + return null +} diff --git a/components/permission/permissionController.js b/components/permission/permissionController.js index a8ef0e6..7f2ae4b 100644 --- a/components/permission/permissionController.js +++ b/components/permission/permissionController.js @@ -14,10 +14,7 @@ exports.getAllPermissions = catchAsync(async (req, res, next) => { }) exports.createPermission = catchAsync(async (req, res, next) => { - const permission = await permissionService.createPermission( - req.body.englishName, - req.body.persianName - ) + const permission = await permissionService.createPermission(req.body) res.status(201).json({ status: 'success', @@ -35,10 +32,10 @@ exports.getPermission = catchAsync(async (req, res, next) => { }) exports.updatePermission = catchAsync(async (req, res, next) => { - const updatePermission = await permissionService.updatePermission(data) + const updatePermission = await permissionService.updatePermission(req.params.id, req.body) res.status(200).json({ status: 'success', - data: permission, + data: updatePermission, }) }) diff --git a/components/permission/permissionRoutes.js b/components/permission/permissionRoutes.js index 41bcff0..8d02c2c 100644 --- a/components/permission/permissionRoutes.js +++ b/components/permission/permissionRoutes.js @@ -7,34 +7,14 @@ const router = new express.Router() router.use(loginCheck.loginProtect) -router.get( - '/', - perm.requires('getallpermissions'), - permissionController.getAllPermissions -) +router.get('/', perm.requires('getallpermissions'), permissionController.getAllPermissions) -router.post( - '/', - perm.requires('createpermission'), - permissionController.createPermission -) +router.post('/', perm.requires('createpermission'), permissionController.createPermission) -router.get( - '/:id', - perm.requires('getpermission'), - permissionController.getPermission -) +router.get('/:id', perm.requires('getpermission'), permissionController.getPermission) -router.put( - '/:id', - perm.requires('updatepermission'), - permissionController.updatePermission -) +router.patch('/:id', perm.requires('updatepermission'), permissionController.updatePermission) -router.delete( - '/:id', - perm.requires('deletepermission'), - permissionController.deletePermission -) +router.delete('/:id', perm.requires('deletepermission'), permissionController.deletePermission) module.exports = router diff --git a/components/permission/permissionService.js b/components/permission/permissionService.js index c928d90..69a71ad 100644 --- a/components/permission/permissionService.js +++ b/components/permission/permissionService.js @@ -5,11 +5,8 @@ module.exports.getAllPermissions = async () => { return Permission.find({}) } -module.exports.createPermission = async (englishName, persianName) => { - return await Permission.create({ - englishName, - persianName, - }) +module.exports.createPermission = async (data) => { + return await Permission.create(data) } module.exports.getPermission = async (permissionId) => { @@ -22,8 +19,8 @@ module.exports.getPermission = async (permissionId) => { return permission } -module.exports.updatePermission = async (data) => { - const permission = await Permission.findOneAndUpdate(data) +module.exports.updatePermission = async (permissionId, data) => { + const permission = await Permission.findOneAndUpdate({ _id: permissionId }, data, { new: true }) if (!permission) { throw AppError('NoPermissionFound', 404, {}) @@ -33,7 +30,11 @@ module.exports.updatePermission = async (data) => { } module.exports.deletePermission = async (permissionId) => { - await Permission.findOneAndDelete({ _id: permissionId }) + const permission = await Permission.findOneAndDelete({ _id: permissionId }) + + if (!permission) { + throw AppError('NoPermissionFound', 404, {}) + } return null } diff --git a/components/role/roleController.js b/components/role/roleController.js index 3aed3e7..42e165e 100644 --- a/components/role/roleController.js +++ b/components/role/roleController.js @@ -14,10 +14,7 @@ exports.getAllRoles = catchAsync(async (req, res, next) => { }) exports.createRole = catchAsync(async (req, res, next) => { - const role = await roleService.createRole( - req.body.englishName, - req.body.persianName - ) + const role = await roleService.createRole(req.body) res.status(201).json({ status: 'success', @@ -26,7 +23,7 @@ exports.createRole = catchAsync(async (req, res, next) => { }) exports.getRole = catchAsync(async (req, res, next) => { - const role = await roleService.getRole(req.body.id) + const role = await roleService.getRole(req.params.roleId) res.status(200).json({ status: 'success', @@ -35,15 +32,15 @@ exports.getRole = catchAsync(async (req, res, next) => { }) exports.updateRole = catchAsync(async (req, res, next) => { - const updateRole = await roleService.updateRole(data) + const updateRole = await roleService.updateRole(req.params.roleId, req.body) res.status(200).json({ status: 'success', - data: role, + data: updateRole, }) }) exports.deleteRole = catchAsync(async (req, res, next) => { - await roleService.deleteRole(req.body.id) + await roleService.deleteRole(req.params.roleId) res.status(204).json({ status: 'success', diff --git a/components/role/roleRoutes.js b/components/role/roleRoutes.js index b824ad5..b836ca0 100644 --- a/components/role/roleRoutes.js +++ b/components/role/roleRoutes.js @@ -13,7 +13,7 @@ router.post('/admin/create', perm.requires('createarole'), roleController.create router.get('/admin/get-one/:roleId', perm.requires('getarole'), roleController.getRole) -router.put('/admin/update/:roleId', perm.requires('updatearole'), roleController.updateRole) +router.patch('/admin/update/:roleId', perm.requires('updatearole'), roleController.updateRole) router.delete('/admin/delete/:roleId', perm.requires('deletearole'), roleController.deleteRole) diff --git a/components/role/roleService.js b/components/role/roleService.js index 917db0b..8b6ab87 100644 --- a/components/role/roleService.js +++ b/components/role/roleService.js @@ -6,7 +6,7 @@ module.exports.getAllRoles = async () => { } module.exports.createRole = async (data) => { - return Role.create(data, null) + return await Role.create(data) } module.exports.getRole = async (roleId) => { @@ -20,7 +20,7 @@ module.exports.getRole = async (roleId) => { } module.exports.updateRole = async (roleId, data) => { - const role = await Role.findOneAndUpdate({ _id: roleId},data, null) + const role = await Role.findOneAndUpdate({ _id: roleId }, data, { new: true }) if (!role) { throw new AppError('NoRoleFound', 404, {}) @@ -30,7 +30,7 @@ module.exports.updateRole = async (roleId, data) => { } module.exports.deleteRole = async (roleId) => { - const role = await Role.findByIdAndUpdate(roleId, { isActive: false }, null) + const role = await Role.findByIdAndUpdate(roleId, { isActive: false }, { new: true }) if (!role) { throw new AppError('NoRoleFound', 404, {}) diff --git a/components/user/userController.js b/components/user/userController.js index 0af2808..2918287 100644 --- a/components/user/userController.js +++ b/components/user/userController.js @@ -2,11 +2,25 @@ const catchAsync = require('../../globalErrorHandler/catchAsync') const userService = require('./userService') +exports.signup = catchAsync(async (req, res, next) => { + const { user, token, cookieOptions } = await userService.signup( + req.body.name, + req.body.email, + req.body.password, + req.body.passwordConfirm + ) + res.cookie('jwt', token, cookieOptions) + res.status(200).json({ status: 'success ', data: { user, token } }) +}) + exports.loginUser = catchAsync(async (req, res, next) => { - const user = await userService.loginUser(req.body.rawPassword) + const { email, password } = req.body + const { token, user } = await userService.loginUser(email, password) const maxAge = 60 * 60 * 24 * 90 - res.cookie('raw-password', user.rawPassword, { + res.setHeader('Authorization', `Bearer ${token}`) + + res.cookie('belonging_token', token, { httpOnly: true, maxAge: maxAge * 1000, path: '/', @@ -14,12 +28,12 @@ exports.loginUser = catchAsync(async (req, res, next) => { res.status(200).json({ status: 'success', - data: user, + data: { user, token }, }) }) exports.logoutUser = catchAsync(async (req, res, next) => { - res.cookie('raw-password', '', { + res.cookie('belonging_token', '', { httpOnly: true, maxAge: 0, path: '/', diff --git a/components/user/userModel.js b/components/user/userModel.js index 2cb84f1..1fb5820 100644 --- a/components/user/userModel.js +++ b/components/user/userModel.js @@ -1,21 +1,39 @@ const mongoose = require('mongoose') const ObjectID = mongoose.Schema.Types.ObjectId +const validator = require('validator') const userSchema = new mongoose.Schema( { name: String, + email: { + type: String, + lowercase: true, + required: true, + validate: [validator.isEmail, 'Please provide a valid email'], + }, + emailUnique: { + type: String, + default: function () { + return this.email || `no-email-${this._id}` + }, + }, password: { type: String, required: true, trim: true, + min: 8, + select: false, }, + passwordChangedAt: Date, + passwordResetToken: String, + passwordResetExpires: Date, isActive: { type: Boolean, default: true, }, role: { type: ObjectID, - ref: 'Role' + ref: 'Role', }, isAdmin: { type: Boolean, diff --git a/components/user/userRoutes.js b/components/user/userRoutes.js index 0013c05..da0800f 100644 --- a/components/user/userRoutes.js +++ b/components/user/userRoutes.js @@ -5,6 +5,7 @@ const loginCheck = require('../../middlewares/loginCheck') const router = new express.Router() +router.post('/signup', userController.signup) router.post('/login', userController.loginUser) router.post('/logout', userController.logoutUser) @@ -17,7 +18,7 @@ router.get('/:userId', userController.getUser) router.post('/', userController.createUser) -router.put('/:userId', userController.updateUser) +router.patch('/:userId', userController.updateUser) router.delete('/:userId', userController.deleteUser) diff --git a/components/user/userService.js b/components/user/userService.js index 09c3ee9..7625d89 100644 --- a/components/user/userService.js +++ b/components/user/userService.js @@ -1,21 +1,106 @@ // userService.js const User = require('./userModel') const AppError = require('../../globalErrorHandler/AppError') -const loginUser = async (rawPassword) => { - const user = await User.findOne({ rawPassword }, null, null) +const redisClient = require('../../setup/redis') +const jwt = require('jsonwebtoken') +const bcrypt = require('bcryptjs') +const maxAge = 60 * 60 * 24 * 90 - if (!user) { - throw new AppError('WrongPassword', 404, {}) +const signToken = (id) => { + return jwt.sign({ id }, process.env.JWT_SECRET, { + expiresIn: maxAge, + }) +} + +const signup = async (name, email, password, passwordConfirm) => { + let userExist = await User.findOne({ email }, null, null) + + if (userExist) { + throw new AppError('UserAlreadyExist', 500, {}) } - return user + if (password != passwordConfirm) throw new AppError('PasswordIncorrect', 401, {}) + + const hashedPassword = bcrypt.hashSync(password, 10) + + const user = await createUser({ + name, + email, + password: hashedPassword, + passwordConfirm: undefined, + }) + + const token = signToken(user._id) + const cookieOptions = { + expires: new Date(Date.now() + maxAge * 24 * 60 * 60 * 1000), + httpOnly: true, + } + + if (process.env.NODE_ENV === 'production') cookieOptions.secure = true + + // set user password to undefined before sending data + user.password = undefined + return { token, user, cookieOptions } +} + +const loginAttempt = async (phrase) => { + const attemptExist = await redisClient.exists(phrase + '#attempts') + + if (attemptExist === 0) { + await redisClient.set( + phrase + '#attempts', + 0, + { + EX: 3600, + }, + null + ) + } + + // if user phrase(email or phone) exist return error with expire time left for login + if (parseInt(await redisClient.get(phrase + '#attempts')) >= parseInt(process.env.LOGIN_ATTEMPT_LIMIT)) { + const limitTime = await redisClient.ttl(phrase + '#attempts') + const minutes = (limitTime / 60).toFixed(0) + throw new AppError('MaximumLoginReached', 500, { + minutes, + }) + } + + if (attemptExist === 1) { + const attempts = parseInt(await redisClient.get(phrase + '#attempts')) + await redisClient.set( + phrase + '#attempts', + attempts + 1 + '', + { + EX: 3600, + }, + null + ) + } +} + +const loginUser = async (email, password) => { + if (!email || !password) { + throw new AppError('ProvideEmailAndPassword', 400) + } + + const user = await User.findOne({ email }, null, null).select('+password') + + if (email) { + await loginAttempt(email) + } + + if (!user || !(await bcrypt.compare(password, user.password))) { + throw new AppError('IncorrectEmailOrPassword', 401, {}) + } + + // set user password to undefined before sending data + user.password = undefined + return { token: signToken(user._id), user } } const getAllUsers = async () => { const users = await User.find({}, null, null) - for (let user of users) { - user.activatedCodes = await codeService.getCodeCount(user._id) - } return users } @@ -61,4 +146,5 @@ module.exports = { deleteUser, updateUser, createUser, + signup, } diff --git a/config/.env b/config/.env index 55ad1db..4642e61 100644 --- a/config/.env +++ b/config/.env @@ -1,3 +1,7 @@ MONGODB_URL=mongodb://admin:password@mongodb:27018/marbm?authSource=admin NODE_ENV=development -PORT=3000 \ No newline at end of file +PORT=3000 +JWT_SECRET=Y@RJ$WT!@K0NS@MPL! +REDIS_URL= redis://:135792468@redis:6379/0 +LOGIN_ATTEMPT_LIMIT=5 +SUPER_ADMIN_ROLE_ID=676afd6d076b286f9395b05f \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2d0aab4..bf85114 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: - MONGODB_URL=mongodb://admin:*135792468*@mongo:27017/marbm?authSource=admin - NODE_ENV=development - PORT=3000 + - REDIS_URL=redis://:135792468@redis:6379/0 minio-server: image: minio/minio ports: diff --git a/middlewares/loginCheck.js b/middlewares/loginCheck.js index 1a0e439..2ce6ab2 100644 --- a/middlewares/loginCheck.js +++ b/middlewares/loginCheck.js @@ -28,7 +28,7 @@ exports.loginProtect = async (req, res, next) => { } // 3) check if user still exist - const loggedInUser = await User.findById(decodedToken.id).populate('role addresses') + const loggedInUser = await User.findById(decodedToken.id).populate('role') if (!loggedInUser) { return next(new AppError('TokenUserNotExist', 401, {})) diff --git a/package-lock.json b/package-lock.json index d450703..7d63afc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "bcryptjs": "^2.4.3", "body-parser": "^1.20.3", "chalk": "^5.3.0", "compression": "^1.7.5", @@ -17,7 +18,10 @@ "dotenv": "^16.4.7", "express": "^4.21.2", "jsonwebtoken": "^9.0.2", - "mongoose": "^8.9.0" + "mongoose": "^8.9.0", + "mongoose-sequence": "^6.0.1", + "redis": "^4.7.0", + "validator": "^13.12.0" }, "devDependencies": { "nodemon": "^3.1.9" @@ -31,6 +35,59 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", + "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -82,12 +139,22 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -228,6 +295,14 @@ "fsevents": "~2.3.2" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -548,6 +623,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, "node_modules/get-intrinsic": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", @@ -765,6 +848,11 @@ "node": ">=12.0.0" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -962,6 +1050,18 @@ "url": "https://opencollective.com/mongoose" } }, + "node_modules/mongoose-sequence": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/mongoose-sequence/-/mongoose-sequence-6.0.1.tgz", + "integrity": "sha512-uXnLCW9pu2V49Xw8BmdXdeRugd2mv+ntu3nT2Bbm33pNRmmvHE2GKA+8BASKoQt960McLX4VL78wkb492f6MoQ==", + "dependencies": { + "async": "^3.2.5", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "mongoose": ">=5" + } + }, "node_modules/mongoose/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1217,6 +1317,19 @@ "node": ">=8.10.0" } }, + "node_modules/redis": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", + "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.0", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1494,6 +1607,14 @@ "node": ">= 0.4.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1521,6 +1642,11 @@ "engines": { "node": ">=16" } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index 3769713..36fa04b 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "license": "ISC", "description": "", "dependencies": { + "bcryptjs": "^2.4.3", "body-parser": "^1.20.3", "chalk": "^5.3.0", "compression": "^1.7.5", @@ -19,7 +20,10 @@ "dotenv": "^16.4.7", "express": "^4.21.2", "jsonwebtoken": "^9.0.2", - "mongoose": "^8.9.0" + "mongoose": "^8.9.0", + "mongoose-sequence": "^6.0.1", + "redis": "^4.7.0", + "validator": "^13.12.0" }, "devDependencies": { "nodemon": "^3.1.9" diff --git a/setup/databaseConnection.js b/setup/databaseConnection.js index bda7ab6..b6d412e 100644 --- a/setup/databaseConnection.js +++ b/setup/databaseConnection.js @@ -1,4 +1,5 @@ const mongoose = require('mongoose') +const redisClient = require('./redis') const port = process.env.PORT || 3000 const env = process.env.NODE_ENV || 'development' module.exports = (app) => { @@ -14,13 +15,17 @@ module.exports = (app) => { .connect(connectionString, {}) .then(async function () { console.log('MongoDB Database Connected') - const MyModel = mongoose.model('MyCollection', new mongoose.Schema({name: String})); + const MyModel = mongoose.model('MyCollection', new mongoose.Schema({ name: String })) // Create a document to force MongoDB to actually create the DB and collection - await MyModel.create({name: 'test'}); + await MyModel.create({ name: 'test' }) - app.listen(port, function () { - console.log('Server Started On Port ' + port) + redisClient.connect().then((r) => { + console.log('Redis connection Established') + + app.listen(port, function () { + console.log('Server Started On Port ' + port) + }) }) }) .catch(function (err) { diff --git a/setup/redis.js b/setup/redis.js new file mode 100644 index 0000000..e3919cc --- /dev/null +++ b/setup/redis.js @@ -0,0 +1,18 @@ +const redis = require('redis') +const env = process.env.NODE_ENV || 'development' + +let connectionString +if (env === 'development') { + connectionString = process.env.REDIS_URL +} else if (env === 'test') { + connectionString = process.env.TEST_REDIS_URL +} else if (env === 'production') { + connectionString = process.env.REDIS_URL +} +const client = redis.createClient({ url: connectionString }) + +client.on('error', function (err) { + console.log(err) +}) + +module.exports = client diff --git a/setup/routerEndpoints.js b/setup/routerEndpoints.js index 3f274f4..dab970b 100644 --- a/setup/routerEndpoints.js +++ b/setup/routerEndpoints.js @@ -5,6 +5,7 @@ const userRoutes = require('../components/user/userRoutes') const roleRoutes = require('../components/role/roleRoutes') const permissionRoutes = require('../components/permission/permissionRoutes') const belongingRoutes = require('../components/belonging/belongingRoutes') +const categoryRoutes = require('../components/category/categoryRoutes') // const codeRoutes = require('../components/code/codeRoutes') // const CRMRoutes = require('../components/pages/crmRoutes') // const LoginRoute = require('../components/pages/loginRoutes') @@ -15,5 +16,6 @@ routerEndpoints.use('/users', userRoutes) routerEndpoints.use('/permissions', permissionRoutes) routerEndpoints.use('/belongings', belongingRoutes) routerEndpoints.use('/roles', roleRoutes) +routerEndpoints.use('/categories', categoryRoutes) module.exports = routerEndpoints