I am creating a multi-user dictionary, where several users can add words. I don't want that the same word could have dublicates. I was able to prevent dublicates when a word is added, but struggle with a functionality to prevent dublicates when a word is updated. For example, there is a word "apple" in a database, let's imagine someone wanted to update a word "apply" and accidentally wrote "apple", in such a case, updated word "apple" should not go to database, since there is already such word there. Please help me.
Words API
const express = require('express');
const router = express.Router();
const Word = require('../../models/Word');
const validateWordInput = require('../../validation/word');
const passport = require('passport');
// #route GET api/words/test
// #desc tests words route
// #access Public
router.get('/test', (req, res) => res.json({ msg: 'Words works' }));
// #route POST api/words
// #desc Add words to profile
// #access Private
router.post(
'/',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateWordInput(req.body);
// Check validation
if (!isValid) {
// Return any errors
return res.status(400).json(errors);
}
Word.find({}).then(word => {
if (
word.filter(
wrd =>
wrd.ugrWordCyr.toString().toLowerCase() ===
req.body.ugrWordCyr.toLowerCase()
).length !== 0
) {
return res
.status(404)
.json({ wordalreadyexists: 'Word already exists' });
} else {
const newWord = new Word({
user: req.user.id,
ugrWordCyr: req.body.ugrWordCyr,
rusTranslation: req.body.rusTranslation,
example: req.body.example,
exampleTranslation: req.body.exampleTranslation,
origin: req.body.origin,
sphere: req.body.sphere,
lexis: req.body.lexis,
grammar: req.body.grammar,
partOfSpeech: req.body.partOfSpeech,
style: req.body.style
});
newWord.save().then(word => res.json(word));
}
});
}
);
// #route Put api/words/:id
// #desc Update a word by id
// #access Private
router.put(
'/:id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateWordInput(req.body);
// Check validation
if (!isValid) {
// Return any errors
return res.status(400).json(errors);
}
Profile.findOne({ user: req.user.id }).then(profile => {
Word.findById(req.params.id)
.then(word => {
// Check for word owner
if (word.user.toString() !== req.user.id) {
return res
.status(401)
.json({ notauthorized: 'User not authorized' });
}
const wordID = req.params.id;
const wordInput = req.body;
// Update
Word.findByIdAndUpdate(
{ _id: wordID },
{ $set: wordInput },
{ returnOriginal: false },
(err, word) => {
if (err) {
console.log(err);
}
}
).then(word => {
res.json(word);
});
})
.catch(err => res.status(404).json({ nowordfound: 'No word found' }));
});
}
);
// #route GET api/words
// #desc Dislay all words
// #access Public
router.get('/', (req, res) => {
Word.find()
.sort({ date: -1 })
.then(words => res.json(words))
.catch(err => res.status(404).json({ nonwordsfound: 'No words found' }));
});
//#route Get api/words/:id
//#desc Get word by id
//#access Public
router.get('/:id', (req, res) => {
Word.findById(req.params.id)
.then(word => res.json(word))
.catch(err =>
res.status(404).json({ nonwordfound: 'No word found with that ID' })
);
});
//#route DELETE api/words/:id
//#desc DELETE word
//#access Private
router.delete(
'/:id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
Profile.findOne({ user: req.user.id }).then(profile => {
Word.findById(req.params.id)
.then(word => {
// Check for post owner
if (word.user.toString() !== req.user.id) {
return res
.status(401)
.json({ notauthorized: 'User not authorized' });
}
// Delete
word.remove().then(() => res.json({ success: true }));
})
.catch(err => res.status(404).json({ postnotfound: 'No post found' }));
});
}
);
module.exports = router;
I used the following code to prevent dublicated on addition.
if (
word.filter(
wrd =>
wrd.ugrWordCyr.toString().toLowerCase() ===
req.body.ugrWordCyr.toLowerCase()
).length !== 0
) {
return res
.status(404)
.json({ wordalreadyexists: 'Word already exists' });
} else {
const newWord = new Word({
user: req.user.id,
ugrWordCyr: req.body.ugrWordCyr,
rusTranslation: req.body.rusTranslation,
example: req.body.example,
exampleTranslation: req.body.exampleTranslation,
origin: req.body.origin,
sphere: req.body.sphere,
lexis: req.body.lexis,
grammar: req.body.grammar,
partOfSpeech: req.body.partOfSpeech,
style: req.body.style
});
What code should I write to do the same on update?
Before executing Word.findByIdAndUpdate(), you can do a check to see if the text in req.body matches any existing words in your database.
// #route Put api/words/:id
// #desc Update a word by id
// #access Private
router.put(
'/:id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateWordInput(req.body);
// Check validation
if (!isValid) {
// Return any errors
return res.status(400).json(errors);
}
Profile.findOne({ user: req.user.id }).then(profile => {
Word.findById(req.params.id)
.then(word => {
// Check for word owner
if (word.user.toString() !== req.user.id) {
return res
.status(401)
.json({ notauthorized: 'User not authorized' });
}
const wordID = req.params.id;
const wordInput = req.body;
//find all words
Word.find()
.then((allWords) => {
//create an array of strings using each word ("apple", "apricot", ...etc)
const wordStrings = allWords.map((word) => word.ugrWordCyr) //not sure if thats the property that has the word-spelling
//check if user input already exists in all words
if(wordStrings.includes(req.body.ugrWordCyr)){
return res.status(400).json({ error : "word already exists" })
}
// Update
Word.findByIdAndUpdate(
{ _id: wordID },
{ $set: wordInput },
{ returnOriginal: false },
(err, word) => {
if (err) {
console.log(err);
}
}).then(word => {
res.json(word);
});
})
.catch((errors) => {
return res.status(400).json({ errors: "could not find any words" })
})
})
.catch(err => res.status(404).json({ nowordfound: 'No word found' }));
});
}
);
Alternatively, you could also update your Word model and use the unique property when setting up your mongoose schema. I imagine your schema looks something like this:
const mongoose = require("mongoose")
const wordSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
ugrWordCyr: { type: String, unique: true}, <-- set unique to true
rusTranslation: { type: String },
example: { type: String },
exampleTranslation: { type: String },
origin: { type: String },
sphere: { type: String },
lexis: { type: String },
grammar: { type: String },
partOfSpeech: { type: String },
style: { type: String }
})
const Word = mongoose.model("Word", userSchema)
module.exports = Word
Now when you use findByIdAndUpdate(), it will not complete unless you pass in a new/unique string to ugrWordCyr or whatever you use as the explicit word.
Word.findByIdAndUpdate(
{ _id: wordID },
{ $set: wordInput },
{ returnOriginal: false },
(err, word) => {
if (err) {
console.log(err);
}
}
).then(word => {
res.json(word);
});
.catch(err => {
return res.status(400).json({ error: "could not update word" })
})
})
Related
i have an express app with a put request that updates a phonebook if i found that the person's name already exists in this phone book (i'm using the "mongoose-unique-validator" that have the unique: true option in validation)
but i have the problem with this put request only when i set findByIdAndUpdate's runValidators to true
here is my code
the schema
const personSchema = new mongoose.Schema({
name: { type: String, required: true, minlength: 3, unique: true },
number: {
type: String,
required: true,
validate: {
validator: function (str) {
//the function to validate the number
},
message: "phone number must contain at least 8 digits",
},
},
});
personSchema.plugin(uniqueValidator);
personSchema.set("toJSON", {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString();
delete returnedObject._id;
delete returnedObject.__v;
},
});
const Person = mongoose.model("Person", personSchema);
the put request
app.put("/api/persons/:id", (req, res, next) => {
const id = req.params.id;
console.log(id);
const body = req.body;
const person = {
name: body.name,
number: body.number,
};
// opts is supposed to be true; and this is where i have the problem
const opts = { runValidators: false };
Person.findByIdAndUpdate(id, person, opts)
.then((updatedPerson) => {
res.json(updatedPerson);
})
.catch((error) => next(error));
});
the error handler
const errorHandler = (error, req, res, next) => {
console.error(error.message);
if (error.name === "CastError" && error.kind == "ObjectId") {
return res.status(400).send({ error: "malformatted id" });
} else if (error.name === "ValidationError") {
return res.status(400).json({ error: error.message });
}
next(error);
};
app.use(errorHandler);
the error i'm getting is
error: "Validation failed: name: Cannot read property 'ownerDocument' of null"
#oussama ghrib If you're not updating the name and you're only updating the number (as you indicated), the update object can just have the phone number, like below:
app.put('/api/persons/:id', (req, res, next) => {
const { number } = req.body
Person.findByIdAndUpdate(req.params.id, { number }, {
new: true,
runValidators: true,
context: 'query'
})
.then((updatedPeep) => {
res.json(updatedPeep)
})
.catch((err) => next(err))
})
If you're using runValidators: true you also need context: 'query' as explained here: https://github.com/blakehaswell/mongoose-unique-validator#find--updates
findOneAndUpdate works like findByIdAndUpdate for this purpose.
The issue is with your usage of: findByIdAndUpdate(id, update, opts)
The update object should be properties you want to update otherwise it will also to try update the id.
Solution:
app.put("/api/persons/:id", (req, res, next) => {
const { name, number } = req.body;
Person.findByIdAndUpdate(req.params.id, { name, number }, opts )
.then((updatedPerson) => {
res.json(updatedPerson);
})
.catch((err) => next(err));
}
https://mongoosejs.com/docs/api/model.html#model_Model.findByIdAndUpdate
I have created 2 Users(Admin and Users) and also i have created many ToDos for a User but here my Todo array is empty in my User Schema. Unable to understand why todo task are not assigned to the User Schema.
UserSchema
var userSchema = new Schema({
name: {
type: String,
required: true,
maxlength: 30,
trim: true
},
role: {
type: Number,
default: 0
},
todos: [{
type: Schema.Types.ObjectId,
ref:"Todo"
}]
});
module.exports = mongoose.model("User", userSchema)
Todo Schema
let Todo = new Schema({
todo_heading: {
type: String
},
todo_desc: {
type: String
},
todo_priority: {
type: String
},
todo_completed: {
type: Boolean
},
user: {
type: Schema.Types.ObjectId,
ref:"User"
}
})
module.exports = mongoose.model('Todo', Todo);
here are my routes
User Route
router.get("/user/:userId/todos", isSignedIn, isAuthenticated, getToDos)
Todo Route
router.get("/", getTodos)
router.get("/:id", getUsertodos);
router.post("/user/:userId/add", addUsertodos);
User Controllers
exports.getToDos = (req, res) => {
User.find({ _id: req.params._id })
.populate("todos")
.exec((err, toDo) => {
if (err) {
res.json(err)
}
res.json(toDo)
})
}
ToDo Controllers
exports.addUsertodos = (req, res) => {
let todo = new Todo(req.body)
todo.save((err, todo) => {
if (err) {
return res.status(400).json({
error: "not saved"
})
}
else {
return res.json(todo)
}
})
}
it should work as expected if you add the objectId of newly created todo to the todos property when you create a user.
//routers/todo.js
var express = require('express');
var router = express.Router();
const Todo = require('../models/Todo');
const User = require('../models/User');
/* GET home page. */
router.get('/', async function (req, res) {
let todos = await Todo.find();
res.json({
todos
});
});
router.post('/todos', async function (req, res) {
//add todos
let {
todo_desc,
todo_heading,
todo_priority,
todo_completed
} = req.body;
try {
//NOTE: for simplicity assigning first user but you can grab it from the params
let user = await User.findOne();
let todo = await Todo.create({
todo_desc,
todo_completed,
todo_priority,
todo_heading,
user: user._id
})
res.json({
message: 'todo created successfully',
todo
});
} catch (err) {
return res.status(500).json({
message: 'Unable to create a todo',
err: JSON.stringify(err)
})
}
});
module.exports = router;
Here is the user route where post route get the string id of created ID and converts it to ObjectId(), assign it to the todos.
var express = require('express');
var router = express.Router();
let _ = require('lodash');
var mongoose = require('mongoose');
const User = require('../models/User');
/* GET users listing. */
router.post("/", async function (req, res) {
let {
name,
todos
} = req.body;
try {
let user = new User();
user.name = name;
let objectIds = todos.split(',').map(id => mongoose.Types.ObjectId(id));
user.todos.push(...objectIds)
await user.save()
console.log("user: ", JSON.stringify(user));
if (_.isEmpty(user)) {
res.status(500).json({
message: 'unable to create user'
})
}
res.json(user);
} catch (err) {
res.status(500).json({
message: 'unable to create user',
err: JSON.stringify(err)
})
}
});
router.get("/", async function (req, res) {
try {
let user = await User.find().populate('todos');
console.log("user: ", JSON.stringify(user));
if (_.isEmpty(user)) {
res.status(500).json({
message: 'unable to find user'
})
}
res.json(user);
} catch (err) {
res.status(500).json({
message: 'unable to find user',
err: JSON.stringify(err)
})
}
});
module.exports = router;
Check out the attached screenshot, the user record now contains the todos assigned to it.
If you want checkout the working code, please visit this repo that i created!!.
Hope this help.Cheers!!
I have been attempting to use Express Validator, however I have come across a problem.
Currently, I am using one route with a variable (which is the action, e.g update name, email, password etc.) to send the updated user data to the server.
I am using a switch statement that looks at the action, and does the relevant update to the user data.
I'd like one validation function that also has a switch statement, which will determine what it needs to validate.
Currently I have this so far...
this validation rules:
const validationRules = () => {
console.log("validating...");
return [
// names must be 1 or more
body("firstName").isLength({ min: 1 }),
body("lastName").isLength({ min: 1 }),
];
};
the post request:
app.post(
"/api/user/update/:action",
validationRules(),
validate,
(req, res) => {
const { id } = req.body;
const { action } = req.params;
const updateDatabase = (id, updateObject) => {
UserModel.findOneAndUpdate(
{ _id: id },
{ $set: updateObject },
{
useFindAndModify: false,
}
)
.then((user) => {
console.log(user);
res.sendStatus(200);
})
.catch((err) => {
console.log(err);
res.status(403).send({ error: err });
});
};
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
} else {
let update = {};
switch (action) {
case "name":
const { firstName, lastName } = req.body;
update = {
"userInfo.firstName": firstName,
"userInfo.lastName": lastName,
};
break;
case "email":
const { email } = req.body;
console.log(email);
update = {
"userInfo.email": email,
};
break;
case "mobile":
const { mobile } = req.body;
update = {
"userInfo.mobile": mobile,
};
break;
default:
console.log("nothing matched so nothing updated");
break;
}
updateDatabase(id, update);
}
}
);
You can make a simple middlware and the switch case there:
Here is an example:
const app = require('express')();
const validatorMiddleware = (req, res, next)=>{
switch(req.params.action){
case 'foo':
//here you will `return` the rules
console.log('foo');
break;
case 'bar':
//here you will `return` the rules
console.log('bar');
break;
default: console.log("default");
}
next();
}
app.get('/:action', validatorMiddleware, (req, res)=>{
res.send(req.params);
})
app.listen(8080, ()=>{
console.log('started');
})
For your case it will be something like this:
const validationRules = (req, res, next) => {
console.log("validating...");
switch (req.params.action) {
case "name":
return [
// names must be 1 or more
body("firstName").isLength({ min: 1 }),
body("lastName").isLength({ min: 1 }),
];
}
next();
};
I know this question gets asked a lot but I cannot tell where I am sending multiple headers. The data is getting stored in the database and then it crashes. I am fairly new to Node/Express and I think I might be missing something fundamental here.
I have tried reading what I could find on stackoverflow and figured out the reason I am getting this error is because it is sending multiple header requests. Tried updating the code with little tweaks but nothing has worked so far.
Thanks for the help.
Dashboard Controller -
exports.getGymOwnerMembersAdd = (req, res, next) => {
let message = req.flash('error');
if(message.length > 0) {
message = message[0];
} else {
message = null;
}
const oldInput = {
...
};
Membership
.find()
.then(memberships => {
res.render('gym-owner/members-add', {
memberships: memberships,
oldInput: oldInput,
errorMessage: message,
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
validationErrors: []
});
})
.catch(err => {
console.log(err);
});
}
exports.postGymOwnerMembersAdd = (req, res, next) => {
const membershipId = req.body.membershipLevel;
const errors = validationResult(req);
let message = req.flash('error');
if(message.length > 0) {
message = message[0];
} else {
message = null;
}
if(!errors.isEmpty()) {
Membership
.find()
.then(memberships => {
return res.status(422).render('gym-owner/members-add', {
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
errorMessage: errors.array()[0].msg,
message: message,
memberships: memberships,
oldInput: {
...
},
validationErrors: errors.array()
});
})
.catch(next);
}
bcrypt
.hash(password, 12)
.then(hashedPassword => {
const user = new User({
...
});
return user.save();
})
.then(result => {
res.redirect('/gym-owner-dashboard/members');
})
.catch(err=> {
console.log(err);
});
}
Dashboard Routes And Validation-
router.get('/gym-owner-dashboard/members-add', isAuth, isGymOwner, dashboardController.getGymOwnerMembersAdd);
router.post(
'/gym-owner-dashboard/members-add',
isAuth, isGymOwner,
[
check('name')
.isAlpha().withMessage('Names can only contain letters.')
.isLength({ min: 2 }).withMessage('Please enter a valid name')
.trim(),
check('email')
.isEmail().withMessage('Please enter a valid email.')
.custom((value, { req }) => {
return User.findOne({
email: value
}).then(userDoc => {
console.log('Made it here!');
if(userDoc) {
return Promise.reject('E-mail already exists, please pick a different one.');
};
});
})
.normalizeEmail(),
...
check(
'password',
'Please enter a password at least 5 characters.'
)
.isLength({ min: 5 })
.trim(),
check('confirmPassword')
.trim()
.custom((value, { req }) => {
if(value !== req.body.password) {
throw new Error('Passwords have to match!');
}
return true;
})
],
dashboardController.postGymOwnerMembersAdd
);
Expected Results
Create a new user while passing validation.
Actual Results
A new user is created and saved to Mongodb. The user gets redirected back to the user creation page with an error that the user is undefined. The server crashes with the error "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client"
I understand you have a bug in "postGymOwnerMembersAdd".
if(!errors.isEmpty()) {
Membership
.find()
.then(memberships => {
return res.status(422).render('gym-owner/members-add', { // this return refers to cb but not to middleware
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
errorMessage: errors.array()[0].msg,
message: message,
memberships: memberships,
oldInput: {
...
},
validationErrors: errors.array()
});
})
.catch(next);
}
bcrypt
.hash(password, 12)
.then(hashedPassword => {
const user = new User({
...
});
return user.save();
})
.then(result => {
res.redirect('/gym-owner-dashboard/members');
})
.catch(err=> {
console.log(err);
});
Thus, both the "return res.status(422).render()" and the "res.redirect('/gym-owner-dashboard/members')" will be executed, and this trigger error (header set after they are sent).
I mean two solutions to the problem
First: use async/await
exports.postGymOwnerMembersAdd = async (req, res, next) => {
const membershipId = req.body.membershipLevel;
const errors = validationResult(req);
let message = req.flash('error');
if(message.length > 0) {
message = message[0];
} else {
message = null;
}
if(!errors.isEmpty()) {
try {
const memberships = await Membership.find();
return res.status(422).render('gym-owner/members-add', {
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
errorMessage: errors.array()[0].msg,
message: message,
memberships: memberships,
oldInput: {
...
},
validationErrors: errors.array()
};
} catch (err) {
next(err);
}
}
const hashedPassword = await bcrypt.hash(password, 12);
const user = new User({
...
});
await user.save();
return res.redirect('/gym-owner-dashboard/members');
};
Second: use else
exports.postGymOwnerMembersAdd = (req, res, next) => {
const membershipId = req.body.membershipLevel;
const errors = validationResult(req);
let message = req.flash('error');
if(message.length > 0) {
message = message[0];
} else {
message = null;
}
if(!errors.isEmpty()) {
Membership
.find()
.then(memberships => {
return res.status(422).render('gym-owner/members-add', {
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
errorMessage: errors.array()[0].msg,
message: message,
memberships: memberships,
oldInput: {
...
},
validationErrors: errors.array()
});
})
.catch(next);
} else {
bcrypt
.hash(password, 12)
.then(hashedPassword => {
const user = new User({
...
});
return user.save();
})
.then(result => {
res.redirect('/gym-owner-dashboard/members');
})
.catch(err=> {
console.log(err);
});
}
}
I am trying to incorporate the express-jwt library and I do not quite understand how it's error handling works.
The documentation says:
Error handling
The default behavior is to throw an error when the token is invalid, so you can >add your custom logic to manage unauthorized access as follows:
app.use(function (err, req, res, next) {
if (err.name === 'UnauthorizedError') {
res.status(401).send('invalid token...');
}
});
But I am confused how that works. If I have a simple req res situation, and I want to call next if the token is valid, or call next with an error if it is not, where to I put that app.use function?
For instance, here is my code:
router.post('/', expressJwt({
secret: jwtSecret,
credentialsRequired: false
}), (req, res, next) => {
databaseController.findUser(req.user.email, (err, user) => {
if (err) {
return next(err)
}
res.json(user)
})
})
The err here would come from my DB call, not from the express-jwt validation.
Any help is appreciated.
Another way is you could place the middleware with app.use to scan all the routes for a valid jwt in the header or the query string.
Any public endpoints can be exempted using the unless keyword.
Ex:
app.use(expressjwt({credentialsRequired: true, secret: config.TOKEN_SECRET, requestProperty: 'user'}).unless({path: config.PUBLIC_URLs}));
app.use(function(err, req, res, next) {
if(err.name === 'UnauthorizedError') {
res.status(err.status).send({message:err.message});
logger.error(err);
return;
}
next();
});
this is my solution for individual routes.
function UseJwt(){
return [
jwtExpress({ secret: configuration.jwtSecret, algorithms: ['HS256'] }),
function(err, req, res, next){
res.status(err.status).json(err);
}
]
}
usage...
app.get(`/${prefix}/:user_id`,
...UseJwt(),
async function (req, res) {
// handle your code here.
}
)
You can create an express middleware just before the code you use to start express server.
// Global error handler that takes 4 arguments and ExpressJS knows that
app.use((err, req, res, next) => {
res.status(err.status).json(err);
});
app.listen(3000);
I use that for apps use REST but you can use the same approach and modify what should happen based on your needs. If you use Jade template for instance then you need to populate the template with the data you want to show to end user and log the rest on your log file.
app.use((err, req, res, next) => {
res.locals.status = status;
res.render('error')
});
Express-jwt is just a method that returns a RequestHandler (a function that takes in req, res, and next). This RequestHandler can be wrapped in a closure that replaces its next input with one you design that handles or formats errors. For example:
/**
* Wraps an Express request handler in an error handler
* #param method RequestHandler to wrap
* #returns RequestHandler
*/
export function formatError<T extends RequestHandler>(method: T): RequestHandler {
return async (req, res, next) => {
const wrapError = (err: any) => {
return (err)
? res.status(err.status).send({ message: err.message })
: next();
};
await method(req, res, wrapError);
};
}
const jwtMiddleware = formatError(expressjwt(...));
Then just use this closure instead of using expressjwt() directly:
router.post('/', jwtMiddleware, ...);
import { Schema, model } from "mongoose";
export const ROLES = ["Admin", "Estudiante","Docente","Secretario","Vicerrector","Inpector"];
const roleSchema = new Schema(
{
name: String,
},
{
versionKey: false,
}
);
export default model("Role", roleSchema);
//
import { Schema, model } from "mongoose";
import bcrypt from "bcryptjs";
const productSchema = new Schema(
{
username: {
type: String,
unique: true,
},
email: {
type: String,
unique: true,
},
password: {
type: String,
required: true,
},
//********************************NUEVOS CAMPOS PARA USUARIOS ADMINISTRADORES
nombres: {
type: String,
required: true,
},
apellidos: {
type: String,
required: true,
},
cedula: {
type: String,
unique: true,
},
foto: {
type: String,
required: true,
},
status: {
type: String,
required: true,
},
telefono: {
type: String,
required: true,
},
//---------------TIPO DE DOCUMENTOS
typo:{
type: String,
},
//---------------TIPO MAS DATOS
roles: [
{
type: Schema.Types.ObjectId,
ref: "Role",
},
],
},
{
timestamps: true,
versionKey: false,
}
);
productSchema.statics.encryptPassword = async (password) => {
const salt = await bcrypt.genSalt(10);
return await bcrypt.hash(password, salt);
};
productSchema.statics.comparePassword = async (password, receivedPassword) => {
return await bcrypt.compare(password, receivedPassword)
}
export default model("User", productSchema);
//
import Role from "../models/Role";
import User from "../models/User";
import bcrypt from "bcryptjs";
export const createRoles = async () => {
try {
// Count Documents
const count = await Role.estimatedDocumentCount();
// check for existing roles
if (count > 0) return;
// Create default Roles
const values = await Promise.all([
new Role({ name: "Estudiante" }).save(),//user
new Role({ name: "Docente" }).save(),//moderator
new Role({ name: "Admin" }).save(),//admin
new Role({ name: "Secretario" }).save(),//-------+++
new Role({ name: "Vicerrector" }).save(),//-------+++
new Role({ name: "Inpector" }).save(),//-------+++
]);
console.log(values);
} catch (error) {
console.error(error);
}
};
export const createAdmin = async () => {
// check for an existing admin user
const user = await User.findOne({ email: "10004095632w#gmailcom" });
// get roles _id
const roles = await Role.find({ name: { $in: ["Admin", "Estudiante","Docente","Secretario","Vicerrector","Inpector"] } });
if (!user) {
// create a new admin user
await User.create({
username: "admin",
email: "10004095632w#gmail.com",
password: await bcrypt.hash("Imperio 789.", 10),
roles: roles.map((role) => role._id),
nombres: "ad",
apellidos: "ad",
cedula: "123456789",
foto: "profile.jpg",
status: "Activo",
telefono: "+570995283857",
});
console.log('Admin User Created!')
}
};
//
import jwt from "jsonwebtoken";
import config from "../config";
import User from "../models/User";
import Role from "../models/Role";
export const verifyToken = async (req, res, next) => {
let token = req.headers["x-access-token"];
if (!token) return res.status(403).json({ message: "No token provided" });
try {
const decoded = jwt.verify(token, config.SECRET);
req.userId = decoded.id;
const user = await User.findById(req.userId, { password: 0 });
if (!user) return res.status(404).json({ message: "No user found" });
next();
} catch (error) {
return res.status(401).json({ message: "Unauthorized!" });
}
};
export const isSecretario = async (req, res, next) => {
try {
const user = await User.findById(req.userId);
const roles = await Role.find({ _id: { $in: user.roles } });
for (let i = 0; i < roles.length; i++) {
if (roles[i].name === "Secretario") {
next();
return;
}
}
return res.status(403).json({ message: "Require Moderator Role!" });
} catch (error) {
console.log(error)
return res.status(500).send({ message: error });
}
};
export const isAdmin = async (req, res, next) => {
try {
const user = await User.findById(req.userId);
const roles = await Role.find({ _id: { $in: user.roles } });
for (let i = 0; i < roles.length; i++) {
if (roles[i].name === "Admin"||roles[i].name === "Secretario") {
next();
return;
}
}
return res.status(403).json({ message: "Require Admin Role!" });
} catch (error) {
console.log(error)
return res.status(500).send({ message: error });
}
};
//
import User from "../models/User";
import { ROLES } from "../models/Role";
const checkDuplicateUsernameOrEmail = async (req, res, next) => {
try {
const user = await User.findOne({ username: req.body.username });
if (user)
return res.status(400).json({ message: "El numero de cédula ya existe" });
const email = await User.findOne({ email: req.body.email });
if (email)
return res.status(400).json({ message: "El correo electrónico ya existe" });
next();
} catch (error) {
res.status(500).json({ message: error });
}
};
const checkRolesExisted = (req, res, next) => {
if (req.body.roles) {
for (let i = 0; i < req.body.roles.length; i++) {
if (!ROLES.includes(req.body.roles[i])) {
return res.status(400).json({
message: `Role ${req.body.roles[i]} does not exist`,
});
}
}
}
next();
};
export { checkDuplicateUsernameOrEmail, checkRolesExisted };
//
import * as authJwt from "./authJwt";
import * as verifySignup from "./verifySignup";
export { authJwt, verifySignup };