i'm pretty new in MEAN STACK.
I make my API REST under Nodejs Express and mongoodb.
I use Angular 14 as client.
I made a little project where user can register / login / display user informations.
Now i'd like to add an update functionality. A simple form where user can update his name or his role. I'm stuck at this point and i'd like to know why my code don't work and how can i fix this. It's been 2 days i'm on it, that's why i'm there to have some informations.
This is my config.json
{
"development": {
"PORT": 3000,
"MONGODB_URI": "mongodb://localhost:27017/PangolinDB",
"JWT_SECRET": "SECRET#123",
"JWT_EXP": "10m"
},
"production": {
"PORT": 80,
"MONGODB_URI": "mongodb://xxxxx/DB_Name",
"JWT_SECRET": "SECRET#123",
"JWT_EXP": "2m"
}
}
user.controller.js
const mongoose = require('mongoose');
const passport = require('passport');
const _ = require('lodash');
const { rest } = require('lodash');
const User = mongoose.model('User');
// updated crud code
ObjectId = require('mongoose').Types.ObjectId; //catch collection Id
// end updated crud code
module.exports.register = (req, res, next) => {
//console.log('inside register function');
var user = new User();
user.fullName = req.body.fullName;
user.email = req.body.email;
user.password = req.body.password;
user.role = req.body.role;
user.followers = req.body.followers;
user.following = req.body.following;
user.save((err, doc) => {
if (!err)
res.send(doc);
else {
if (err.code = 1100)
res.status(422).send(['L\'adresse email existe déja.']);
else
return next(err);
}
});
}
module.exports.authenticate = (req, res, next) => {
//call for passport authentication
passport.authenticate('local', (err, user, info) => {
//error from passport middleware
if (err) return res.status(404).json(err);
//registered user
else if(user) return res.status(200).json({ "token" : user.generateJwt() });
//unknow user or wrong password
else return res.status(401).json(info);
})(req,res);
}
module.exports.userProfile = (req, res, next) => {
User.findOne({ _id: req._id },
(err, user) => {
if (!user)
return res.status(404).json({ status: false, message: 'User record not found'});
else
return res.status(200).json({ status: true, user: _.pick(user,['fullName',
'email', 'role', 'friends','followers','following']) });
})
}
module.exports.getAllUser = async (req, res) => {
const users = await User.find().select('-password');
res.status(200).json(users);
};
module.exports.userInfo = (req, res) => {
if (!ObjectId.isValid(req.params.id))
return res.status(400).send('ID unknown :' + req.params.id)
User.findById(req.params.id, (err,docs) => {
if(!err) res.send(docs);
else console.log('ID unknown ; ' . err);
}) .select('-password');
};
module.exports.updateUser = async (req, res) => {
if (!ObjectId.isValid(req.params.id))
return res.status(400).send('ID unknown :' + req.params.id)
try {
await User.findOneAndUpdate(
{_id: req.params.id},
{
$set: {
role : req.body.role
},
},
{ new: true, upsert: true, setDefaultsOnInsert: true},
)
.then((docs) => res.send(docs))
.catch((err) => res.status(500).send({ message: err }));
} catch (err){
return res.status(500).json({ message: err});
}
}
module.exports.deleteUser = async (req, res) => {
if (!ObjectId.isValid(req.params.id))
return res.status(400).send('ID unknown :' + req.params.id)
try {
await User.remove({_id: req.params.id}).exec();
res.status(200).json({ message: "Successfully deleted. "});
} catch (err) {
return res.status(500).json({ message: err });
}
}
module.exports.follow = async (req, res) => {
/* if (!ObjectId.isValid(req.params.id) || !ObjectId.isValid(req.params.idToFollow))
return res.status(400).send('ID unknown :' + req.params.id) */
try {
// add to the follower list
await User.findByIdAndUpdate(
req.params.id, // id wanted to being followed
{ $addToSet: { following: req.body.idToFollow } }, // id being followed
{ new : true, upsert: true },
);
// add to following list
await User.findByIdAndUpdate(
req.body.idToFollow,
{ $addToSet: { followers: req.params.id}},
{ new: true, upsert: true },
)
return res.status(201).json(docs);
}catch (err) {
return res.status(500).json({ message: err });
}
};
module.exports.unfollow = async (req, res) => {
/* if (!ObjectId.isValid(req.params.id) || !ObjectId.isValid(req.params.idToFollow))
return res.status(400).send('ID unknown :' + req.params.id) */
try {
// add to the follower list
await User.findByIdAndUpdate(
req.params.id, // id wanted to being followed
{ $pull: { following: req.body.idToUnFollow } }, // id being followed
{ new : true, upsert: true },
);
// add to following list
await User.findByIdAndUpdate(
req.body.idToUnFollow,
{ $pull: { followers: req.params.id}},
{ new: true, upsert: true },
)
return res.status(201).json(docs);
}catch (err) {
return res.status(500).json({ message: err });
}
};
user.model.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
var userSchema = new mongoose.Schema({
fullName: {
type: String,
required: 'Full name can\'t be empty'
},
email: {
type: String,
required: 'Email can\'t be empty',
unique: true
},
password: {
type: String,
required: 'Password can\'t be empty',
minlength: [4, 'Password must be atleast 4 character long']
},
role: {
type: String,
required: 'Role can\'t be empty',
},
followers: {
type: String,
},
following: {
type: String,
},
saltSecret: String
});
// Custom validation for email
userSchema.path('email').validate((val) => {
emailRegex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return emailRegex.test(val);
}, 'Invalid e-mail.');
// Events encrypt password
userSchema.pre('save', function (next) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(this.password, salt, (err, hash) => {
this.password = hash;
this.saltSecret = salt;
next();
});
});
});
// Methods verify password
userSchema.methods.verifyPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
userSchema.methods.generateJwt = function () {
return jwt.sign({ _id: this._id},
process.env.JWT_SECRET,
{
expiresIn: process.env.JWT_EXP
});
}
mongoose.model('User', userSchema);
routes.js
const express = require('express');
const router = express.Router();
const ctrlUser = require('../controllers/user.controller');
const jwtHelper = require('../config/jwtHelper');
//auth
router.post('/register', ctrlUser.register,);
router.post('/authenticate',ctrlUser.authenticate);
// user display
router.get('/userProfile',jwtHelper.verifyJwtToken, ctrlUser.userProfile);
router.get('/list', ctrlUser.getAllUser);
router.get('/user/:id', ctrlUser.userInfo);
router.put("/user/:id",ctrlUser.updateUser);
router.delete("/user/:id",ctrlUser.deleteUser);
//follow
router.patch('/user/follow/:id', ctrlUser.follow);
router.patch('/user/unfollow/:id', ctrlUser.unfollow);
module.exports = router;
index.js
require('./config/config');
require('./models/db');
require('./config/passportConfig');
require('./routes/index.router')
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const passport = require('passport');
//updated code
// end updtated code
const rtsIndex = require('./routes/index.router');
var app = express();
// middleware
app.use(bodyParser.json());
app.use(cors());
app.use(passport.initialize());
app.use('/api', rtsIndex);
//
/* app.use('api',routeList); */
//
// error handler
app.use((err, req, res, next) => {
if (err.name === 'ValidationError') {
var valErrors = [];
Object.keys(err.errors).forEach(key => valErrors.push(err.errors[key].message));
res.status(422).send(valErrors)
}
else{
console.log(err);
}
});
// start server
app.listen(process.env.PORT, () => console.log(`Server started at port :
${process.env.PORT}`));
user.service.ts (angular 14)
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse, } from '#angular/common/http';
import { User } from './user.model';
import { Observable, ObservableInput, throwError} from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
#Injectable({
providedIn: 'root'
})
export class UserService {
selectedUser: User = {
id:'',
fullName: '',
email: '',
password: '',
role:'',
friends:'',
followers:'',
following:'',
};
noAuthHeader = { headers: new HttpHeaders({ 'NoAuth': 'True'}) };
errorMgmt: (err: any, caught: Observable<any>) => ObservableInput<any>;
HttpHeaders: HttpHeaders | { [header: string]: string | string[]; };
constructor(private http: HttpClient) { }
//httpMethods
//user form
//register ()
postUser(user: User){
return this.http.post( environment.apiBaseUrl+'/register',user,this.noAuthHeader);
}
//login ()
login(authCredentials: any){
return this.http.post(environment.apiBaseUrl + '/authenticate', authCredentials,
this.noAuthHeader);
}
//user profile
//user infos
getUserProfile(){
return this.http.get(environment.apiBaseUrl + '/userProfile');
} // need jwt in header
getOne(){
const token = this.getToken();
const headers = new HttpHeaders({'Authorization ':'bearer'+ token});
return this.http.get(environment.apiBaseUrl+'/userProfile',{headers});
}
updateUser(_id: any, value: any){
return this.http.put(environment.apiBaseUrl + `/user/${_id}`, value);
}
edit(_id: any, data: any){
return this.http.put(environment.apiBaseUrl + `/user/${_id}`, data);
}
//get all users
getAllUsers(){
return this.http.get(environment.apiBaseUrl + '/list');
}
//Helper Methods
setToken(token: string) {
localStorage.setItem('token', token);
}
getToken() {
return localStorage.getItem('token');
}
deleteToken() {
localStorage.removeItem('token');
}
getUserPayload(){
var token = this.getToken();
if (token) {
var getUserPayload = atob(token.split('.')[1]);
return JSON.parse(getUserPayload);
}
else
return null;
}
isLoggedIn() {
var userPayload = this.getUserPayload();
if (userPayload)
return userPayload.exp > Date.now() / 1000;
else
return false;
}
// Error
handleError(error: HttpErrorResponse) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Handle client error
errorMessage = error.error.message;
} else {
// Handle server error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(() => {
errorMessage;
});
}
}
edit.component.ts
import { UserService } from './../shared/user.service';
import { Component, OnInit, NgZone } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { FormGroup, FormControl, Validator, Validators, SelectControlValueAccessor,
FormBuilder } from "#angular/forms";
#Component({
selector: 'app-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.css']
})
/* export class EditComponent implements OnInit{
editUsr = new FormGroup({
fullName: new FormControl(null, [Validators.required]),
email: new FormControl(null, [Validators.required]),
role: new FormControl(null, [Validators.required])
});
loading: boolean =true;
id: any;
constructor (
private userService : UserService,
private activateRoute : ActivatedRoute,
private route: Router
){}
ngOnInit(): void {
this.activateRoute.params.subscribe((data: any) =>{
this.id = data['id'];
this.userService.getOne(this.id).subscribe((res: any)=>{
if (res.user != undefined){
this.editUsr.get('fullName')?.setValue(res.user.fullName);
this.editUsr.get('email')?.setValue(res.user.email);
this.editUsr.get('role')?.setValue(res.user.role);
}
});
this.loading = false;
});
}
addUser(){
const data: any = {
fullName : this.editUsr.get('fullName')?.value,
email : this.editUsr.get('email')?.value,
role: this.editUsr.get('role')?.value
}
this.userService.edit(this.id, data).subscribe(()=>this.route.navigateByUrl('edit'));
}
}
*/
export class EditComponent implements OnInit {
getId:any;
updateForm: FormGroup;
constructor(
public formBuilder: FormBuilder,
private router: Router,
private ngZone: NgZone,
private activatedRoute: ActivatedRoute,
private UserService: UserService
) {
this.getId = this.activatedRoute.snapshot.paramMap.get('id');
this.UserService.getOne().subscribe(res=>{
this.updateForm.patchValue({
fullName: res['fullName'],
email: res['email'],
role: res['role']
});
});
this.updateForm = this.formBuilder.group({
fullName:[''],
email:[''],
role:[''],
})
}
ngOnInit(): void {}
onUpdate(): any{
this.UserService.updateUser(this.getId, this.updateForm.value).subscribe(()=>{
console.log('mise à jouer effectuée avec succès')
this.ngZone.run(()=> this.router.navigateByUrl('/userprofile'))
},(err)=> {
console.log(err);
});
}
}
edit.component.html
<div class="row justify-content-center mt-5">
<div class="col-md-4">
<form [formGroup]="updateForm" (ngSubmit)="onUpdate()">
<h2>Edit User</h2>
<div class="form-group">
<label for="fullName">FullName</label>
<input class="form-control" type="text" formControlName="fullName" id="fullName" >
</div>
<div class="form-group">
<label for="email">Email</label>
<input class="form-control" type="text" formControlName="email" id="email">
</div>
<div class="form-group">
<label for="role">Role</label>
<input class="form-control" type="text" formControlName="role" id="role">
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" type="submit" >Enregistrer</button>
<button class="btn btn-info btn-block" [routerLink]="['/userprofile']"
type="submit">Retour</button>
</div>
</form>
</div>
</div>
<p>edit works!</p>
environment.ts
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
apiBaseUrl: 'http://localhost:3000/api'
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative
impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
routes.ts
import { EditComponent } from './edit/edit.component';
import { HomeComponent } from './home/home.component';
import { Routes } from "#angular/router";
import { UserComponent } from "./user/user.component";
import { SignUpComponent } from "./sign-up/sign-up.component";
import { SignInComponent } from "./user/sign-in/sign-in.component";
import { UserProfileComponent } from "./user-profile/user-profile.component";
import { AuthGuard } from "./auth/auth.guard";
export const appRoutes: Routes = [
{path:'home', component: HomeComponent},
{path:'edit', component:EditComponent},
{
path: 'signup', component: UserComponent,
children: [{ path:'', component: SignUpComponent}]
},
{
path: 'login', component: UserComponent,
children: [{ path:'', component: SignInComponent}]
},
{
path: 'userprofile', component: UserProfileComponent,canActivate:[AuthGuard]
},
{
path: '', redirectTo: '/login', pathMatch: 'full'
}
];
thanks in advance for the help :)
Related
Scenario : When I create/register user1 ,the verification mail is sent to that email id and he(user1) is being verified successfully and I am able to change password for user1. After creating user1 , I am creating/registering user2 ,where the verification email is sent to the account .After clicking the link , it's becomes INVALID Overall , I am only able to create one user
Languages used : MERN stack
Backend => route.js :
const express = require("express");
const router = express.Router();
const User = require("../models/userModel");
const Doctor = require("../models/doctorModel");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const authMiddleware = require("../middlewares/authMiddleware");
const sendEmail = require("../utils/sendMail");
const Token = require("../models/tokenModel");
const Appointment = require("../models/appointmentModel");
const moment = require("moment");
router.post("/register", async (req, res) => {
try {
const userExists = await User.findOne({ email: req.body.email });
if (userExists) {
return res
.status(200)
.send({ message: "User already exists", success: false });
}
const password = req.body.password;
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
req.body.password = hashedPassword;
const newuser = new User(req.body);
const result = await newuser.save();
await sendEmail(result, "verifyemail");
res
.status(200)
.send({ message: "User created successfully", success: true });
} catch (error) {
console.log(error);
res
.status(500)
.send({ message: "Error creating user", success: false, error });
}
});
router.post("/login", async (req, res) => {
try {
const result = await User.findOne({ data: req.body.userId });
console.log(result);
const user = await User.findOne({ email: req.body.email });
if (!user) {
return res
.status(200)
.send({ message: "User does not exist", success: false });
}
if (user.isVerified === false) {
return res
.status(200)
.send({ message: "User not Verified", success: false });
}
const isMatch = await bcrypt.compare(req.body.password, user.password);
if (!isMatch) {
return res
.status(200)
.send({ message: "Password is incorrect", success: false });
} else {
const dataToBeSentToFrontend = {
id: user._id,
email: user.email,
name: user.name,
};
const token = jwt.sign(dataToBeSentToFrontend, process.env.JWT_SECRET, {
expiresIn: "1d",
});
res
.status(200)
.send({ message: "Login successful", success: true, data: token });
}
} catch (error) {
console.log(error);
res
.status(500)
.send({ message: "Error logging in", success: false, error });
}
});
router.post("/get-user-info-by-id", authMiddleware, async (req, res) => {
try {
const user = await User.findOne({ _id: req.body.userId });
user.password = undefined;
if (!user) {
return res
.status(200)
.send({ message: "User does not exist", success: false });
} else {
res.status(200).send({
success: true,
data: user,
});
}
} catch (error) {
res
.status(500)
.send({ message: "Error getting user info", success: false, error });
}
});
router.post("/send-password-reset-link", async (req, res) => {
try {
const result = await User.findOne({ email: req.body.email });
await sendEmail(result, "resetpassword");
res.send({
success: true,
message: "Password reset link sent to your email successfully",
});
} catch (error) {
res.status(500).send(error);
}
});
router.post("/resetpassword", async (req, res) => {
try {
const tokenData = await Token.findOne({ token: req.body.token });
if (tokenData) {
const password = req.body.password;
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
await User.findOneAndUpdate({
_id: tokenData.userid,
password: hashedPassword,
});
await Token.findOneAndDelete({ token: req.body.token });
res.send({ success: true, message: "Password reset successfull" });
} else {
res.send({ success: false, message: "Invalid token" });
}
} catch (error) {
res.status(500).send(error);
}
});
router.post("/verifyemail", async (req, res) => {
try {
const tokenData = await Token.findOne({ token: req.body.token });
if (tokenData) {
await User.findOneAndUpdate({ _id: tokenData.userid, isVerified: true });
await Token.findOneAndDelete({ token: req.body.token });
res.send({ success: true, message: "Email Verified Successlly" });
} else {
res.send({ success: false, message: "Invalid token" });
}
} catch (error) {
res.status(500).send(error);
}
});
Backend => sendEmail.js :
const nodemailer = require("nodemailer");
const bcrypt = require("bcrypt");
const Token = require("../models/tokenModel");
module.exports = async (user, mailType) => {
try {
const transporter = nodemailer.createTransport({
service: "gmail",
host: "smtp.gmail.com",
port: 587,
secure: true,
auth: {
user: "sh***********th#gmail.com",
pass: "e**************l",
},
});
const encryptedToken = bcrypt
.hashSync(user._id.toString(), 10)
.replaceAll("/", "");
const token = new Token({
userid: user._id,
token: encryptedToken,
});
await token.save();
let mailOptions, emailContent;
if (mailType === "verifyemail") {
emailContent = `<div><h1>Please click on the below link to verify your email address</h1> ${encryptedToken} </div>`;
mailOptions = {
from: "sh************th#gmail.com",
to: user.email,
subject: "Verify Email For MERN Auth",
html: emailContent,
};
} else {
emailContent = `<div><h1>Please click on the below link to reset your password</h1> ${encryptedToken} </div>`;
mailOptions = {
from: "shanshangeeth#gmail.com",
to: user.email,
subject: "Reset Password",
html: emailContent,
};
}
await transporter.sendMail(mailOptions);
} catch (error) {
console.log(error);
}
};
Backend => authMiddleware.js :
const jwt = require("jsonwebtoken");
module.exports = async (req, res, next) => {
try {
const token = req.headers["authorization"].split(" ")[1];
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
return res.status(401).send({
message: "Auth failed",
success: false,
});
} else {
req.body.userId = decoded.id;
next();
}
});
} catch (error) {
return res.status(401).send({
message: "Auth failed",
success: false,
});
}
};
Backend => tokenmodel.js :
const mongoose = require("mongoose");
const tokenSchema = new mongoose.Schema(
{
userid: {
type: String,
required: true,
},
token: {
type: String,
required: true,
},
},
{ timestamps: true }
);
const tokenModel = mongoose.model("tokens", tokenSchema);
module.exports = tokenModel;
Backend => userModel.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
isVerified: {
type: Boolean,
default: false,
},
isDoctor: {
type: Boolean,
default: false,
},
isDoctor: {
type: Boolean,
default: false,
},
isAdmin: {
type: Boolean,
default: false,
},
seenNotifications: {
type: Array,
default: [],
},
unseenNotifications: {
type: Array,
default: [],
},
},
{
timestamps: true,
}
);
const userModel = mongoose.model("users", userSchema);
module.exports = userModel;
Frontend => VerifyEmail.js
path="/verifyemail/:token" in app.js inside PublicRoute
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { toast } from "react-hot-toast";
import axios from "axios";
function VerifyEmail() {
const [emailVerified, setEmailVerified] = useState("");
const params = useParams();
const verifyToken = async () => {
try {
toast.loading();
const response = await axios.post("/api/user/verifyemail", {
token: params.token,
});
if (response.data.success) {
setEmailVerified("true");
} else {
setEmailVerified("false");
}
toast.dismiss();
} catch (error) {
toast.dismiss();
setEmailVerified("false");
}
};
useEffect(() => {
verifyToken();
}, []);
console.log(emailVerified);
return (
<div className="flex min-h-screen p-5 justify-center items-center">
{emailVerified == "" && (
<h1 className="text-primary text-4xl">
Please wait we are verifying your email
</h1>
)}
{emailVerified == "true" && (
<h1 className="text-primary text-4xl">
Your email verified successfully
</h1>
)}
{emailVerified == "false" && (
<h1 className="text-primary text-4xl">
Invalid or Expired Token
{console.log(emailVerified)}
</h1>
)}
</div>
);
}
export default VerifyEmail;
Frontend =>ProtectedRoute.js
import React from "react";
import { Navigate, useNavigate } from "react-router-dom";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { setUser } from "../redux/userSlice";
import { showLoading, hideLoading } from "../redux/alertsSlice";
function ProtectedRoute(props) {
const { user } = useSelector((state) => state.user);
const dispatch = useDispatch();
const navigate = useNavigate();
const getUser = async () => {
try {
dispatch(showLoading());
const response = await axios.post(
"/api/user/get-user-info-by-id",
{ token: localStorage.getItem("token") },
{
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
}
);
console.log('TOKEN ("token") : ', localStorage.getItem("token"));
dispatch(hideLoading());
if (response.data.success) {
dispatch(setUser(response.data.data));
console.log(response.data.data);
} else {
localStorage.clear();
navigate("/login");
}
} catch (error) {
dispatch(hideLoading());
localStorage.clear();
navigate("/login");
}
};
useEffect(() => {
if (!user) {
getUser();
}
}, [user]);
if (localStorage.getItem("token")) {
return props.children;
} else {
return <Navigate to="/login" />;
}
}
export default ProtectedRoute;
Frontend=> VerifyEmail.js
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { toast } from "react-hot-toast";
import axios from "axios";
function VerifyEmail() {
const [emailVerified, setEmailVerified] = useState("");
const params = useParams();
const verifyToken = async () => {
try {
toast.loading();
const response = await axios.post("/api/user/verifyemail", {
token: params.token,
});
if (response.data.success) {
setEmailVerified("true");
} else {
setEmailVerified("false");
}
toast.dismiss();
} catch (error) {
toast.dismiss();
setEmailVerified("false");
}
};
useEffect(() => {
verifyToken();
}, []);
console.log(emailVerified);
return (
<div className="flex min-h-screen p-5 justify-center items-center">
{emailVerified == "" && (
<h1 className="text-primary text-4xl">
Please wait we are verifying your email
</h1>
)}
{emailVerified == "true" && (
<h1 className="text-primary text-4xl">
Your email verified successfully
</h1>
)}
{emailVerified == "false" && (
<h1 className="text-primary text-4xl">
Invalid or Expired Token
{console.log(emailVerified)}
</h1>
)}
</div>
);
}
export default VerifyEmail;
Frontend => userSlice.js
import { createSlice } from "#reduxjs/toolkit";
export const userSlice = createSlice({
name: "user",
initialState: {
user: null
},
reducers: {
setUser: (state , action) => {
state.user = action.payload;
}
},
});
export const { setUser , reloadUserData } = userSlice.actions;
When I create/register user1 , the verification mail is sent to that email id and he(user1) is being verified successfully and I am able to change password for user1. After creating user1 , I am creating/registering user2 ,where the verification email is sent to the account .After clicking the link , it's becomes INVALID Overall , I am only able to create one user who's being verified
This webpage is in a perpetual state of loading and I don't know why. It's almost as if it's awaiting the data. I tested the backend and the user exists and should be being sent but it's just waiting. I also tried isolating the the code so that it's just the bare minimum needed and there's still the perpetual loading. The This is what it looks like: Edit user Error Below is the code:
UserEditScreens.js
import axios from "axios";
import React, { useContext, useEffect, useReducer, useState } from "react";
import Container from "react-bootstrap/Container";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import { Helmet } from "react-helmet-async";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "react-toastify";
import LoadingBox from "../components/LoadingBox";
import MessageBox from "../components/MessageBox";
import { getError } from "../utils";
import { Store } from "./Store";
const reducer = (state, action) => {
switch (action.value) {
case "FETCH_REQUEST":
return { ...state, loading: true };
case "FETCH_SUCCESS":
return { ...state, loading: false };
case "FETCH_FAIL":
return { ...state, loading: false, error: action.payload };
case "UPDATE_REQUEST":
return { ...state, loadingUpdate: true };
case "UPDATE_SUCCESS":
return { ...state, loadingUpdate: false };
case "UPDATE_FAIL":
return { ...state, loadingUpdate: false };
default:
return state;
}
};
export default function UserEditScreen() {
const [{ loading, error, loadingUpdate }, dispatch] = useReducer(reducer, {
loading: true,
error: "",
});
const { state } = useContext(Store);
const { userInfo } = state;
const params = useParams();
const { id: userId } = params;
const navigate = useNavigate();
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [email, setEmail] = useState("");
const [isAdmin, setIsAdmin] = useState(false);
useEffect(() => {
const fetchData = async () => {
try {
dispatch({ type: "FETCH_REQUEST" });
const { data } = await axios.get(`/api/users/${userId}`, {
headers: { authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: "FETCH_SUCCESS", payload: data });
} catch (err) {
dispatch({ type: "FETCH_FAIL", payload: getError(err) });
}
};
fetchData();
}, [userInfo, userId]);
const submitHandler = async (e) => {
e.preventDefault();
try {
dispatch({ type: "UPDATE_REQUEST" });
await axios.put(
`/api/users/${userId}`,
{ _id: userId, firstName, lastName, email, isAdmin },
{ headers: { Authorization: `Bearer ${userInfo.token}` } }
);
dispatch({ type: "UPDATE_SUCCESS" });
toast.success("User updated successfully");
navigate("/admin/users");
} catch (error) {
toast.error(getError(error));
dispatch({ type: "UPDATE_FAIL" });
}
};
return (
<Container>
<Helmet>Edit User #{userId}</Helmet>
<h1>Edit User #{userId}</h1>
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="firstName">
<Form.Label>First Name</Form.Label>
<Form.Control
value={firstName}
type="name"
onChange={(e) => setFirstName(e.target.value)}
required
></Form.Control>
</Form.Group>
<Form.Group>
<Form.Label>Last Name</Form.Label>
<Form.Control
value={lastName}
type="name"
onChange={(e) => setLastName(e.target.value)}
required
></Form.Control>
</Form.Group>
<Form.Group className="mb-3" controlId="lastName">
<Form.Label>Email</Form.Label>
<Form.Control
value={email}
type="email"
onChange={(e) => setEmail(e.target.value)}
required
></Form.Control>
</Form.Group>
<Form.Check
className="mb-3"
type="checkbox"
id="isAdmin"
label="isAdmin"
checked={isAdmin}
onChange={(e) => setIsAdmin(e.target.checked)}
></Form.Check>
<div>
<Button disabled={loadingUpdate} type="submit">
Update
</Button>
{loadingUpdate && <LoadingBox></LoadingBox>}
</div>
</Form>
)}
</Container>
);
}
UserRoutes.js
import express from "express";
import User from "../models/userModel.js";
import bcrypt from "bcryptjs";
import { generateToken, isAuth } from "../utils.js";
import expressAsyncHandler from "express-async-handler";
const userRouter = express.Router();
userRouter.get(
"/",
isAuth,
expressAsyncHandler(async (req, res) => {
const users = await User.find();
res.send(users);
})
);
userRouter.get(
"/:id",
isAuth,
expressAsyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (user) {
console.log(user);
res.send(user);
} else {
res.status(404).send({ message: "User Not Found" });
}
})
);
userRouter.put(
"/:id",
isAuth,
expressAsyncHandler(async (req, res) => {
if (user) {
const user = await User.findById(req.params.id);
user.firstName = req.body.firstName || user.firstName;
user.lastName = req.body.lastName || user.lastName;
user.email = req.body.email || user.email;
user.isAdmin = Boolean(req.body.isAdmin);
const updatedUser = await user.save();
res.send({ message: "User Updated", user: updatedUser });
} else {
res.status(404).send({ message: "User Not Found" });
}
})
);
userRouter.post(
"/signin",
expressAsyncHandler(async (req, res) => {
const user = await User.findOne({ email: req.body.email });
if (user) {
if (bcrypt.compareSync(req.body.password, user.password)) {
res.send({
_id: user._id,
name: user.firstName,
email: user.email,
token: generateToken(user),
});
return;
}
}
res.status(401).send({ message: "Invalid email or password" });
})
);
userRouter.post(
"/signup",
expressAsyncHandler(async (req, res) => {
const newUser = new User({
firstName: req.body.firstName,
lastName: req.body.firstName,
email: req.body.email,
password: bcrypt.hashSync(req.body.password),
});
const user = await newUser.save();
res.send({
_id: user._id,
firstName: user.firstName,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user),
});
})
);
userRouter.put(
"/profile",
isAuth,
expressAsyncHandler(async (req, res) => {
const user = await User.findById(req.user._id);
if (user) {
user.name = req.body.name || user.name;
user.email = req.body.email || user.email;
if (req.body.password) {
user.password = bcrypt.hashSync(req.body.password, 8);
}
const updatedUser = await user.save();
res.send({
_id: updatedUser._id,
name: updatedUser.name,
email: updatedUser.email,
isAdmin: updatedUser.isAdmin,
token: generateToken(updatedUser),
});
} else {
res.status(404).send({ message: "User not found" });
}
})
);
export default userRouter;
I'm creating new app(e-commerce) using react and react-redux with axios. For back-end using Mongo-DB(MERN stack).
There is no problem when saving data to database, but when i get that data from database middleware can not reach token from headers. But postman work fine both get and save data.
I use the same middleware when saving data to database, it work fine. But when i try to fetch data from database i get unauthorized error.
here is my code.
FOR BACK-END;
Server file;
import express from "express";
import connectDB from "./config/db.js";
import dotenv from "dotenv";
import colors from "colors";
import productRoutes from "./routes/productRoutes.js";
import userRouters from "./routes/userRoutes.js";
import shippingAddressRoutes from "./routes/shippingAddressRoutes.js";
import { notFound, errorHandler } from "./middleware/errorMiddleware.js";
dotenv.config();
connectDB();
const app = express();
// body parser
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// API route
app.get("/", (req, res) => {
res.send("API is running....");
});
// route for products
app.use("/api/products", productRoutes);
// route for users
app.use("/api/users", userRouters);
// route for shipping address
app.use("/api/shippingaddress", shippingAddressRoutes);
app.use(notFound);
app.use(errorHandler);
const PORT = process.env.PORT || 5000;
app.listen(
PORT,
console.log(
`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`.yellow.bold
)
);
shippingAddressRoute file;
import express from "express";
import {
saveAddress,
changeAddress,
getAddress,
} from "../controllers/shippingAddressController.js";
import protect from "../middleware/authMiddleware.js";
const router = express.Router();
router
.route("/")
.get(protect, getAddress)
.post(protect, saveAddress)
.put(protect, changeAddress);
export default router;
shippingAddressController file;
import asyncHandler from "express-async-handler";
import ShippingAddress from "../models/shippingAddressModel.js";
import User from "../models/userModel.js";
// #desc get address
// #route GET /api/shippingaddress
// #access Private
const getAddress = asyncHandler(async (req, res) => {
const user = User.findById(req.user._id);
if (user) {
const { email } = req.body;
const shippingAddress = await ShippingAddress.findOne({
userEmail: email,
});
if (shippingAddress) {
res.status(200);
res.json({
address: shippingAddress.address,
city: shippingAddress.city,
postalCode: shippingAddress.postalCode,
county: shippingAddress.country,
email: shippingAddress.userEmail,
});
} else {
res.status(404);
throw new Error("Address not found");
}
} else {
res.status(404);
throw new Error("User not found");
}
});
// #desc save address
// #route POST /api/shippingaddress
// #access Private
const saveAddress = asyncHandler(async (req, res) => {
const user = User.findById(req.user._id);
if (user) {
const { address, city, postalCode, country, email } = req.body;
const existAddress = await ShippingAddress.findOne({ userEmail: email });
if (existAddress) {
res.status(400);
throw new Error(
"User already has an address. If you want to change address, try CHANGE ADDRESS."
);
} else {
const createdAddress = await ShippingAddress.create({
user: req.user._id,
address,
city,
postalCode,
country,
userEmail: email,
});
if (createdAddress) {
res.status(201);
res.json({
address: createdAddress.address,
city: createdAddress.city,
postalCode: createdAddress.postalCode,
county: createdAddress.country,
email: createdAddress.userEmail,
});
} else {
res.status(400);
throw new Error("Address did not save");
}
}
} else {
res.status(404);
throw new Error("User not found");
}
});
// #desc change address
// #route PUT /api/shippingaddress
// #access Private
const changeAddress = asyncHandler(async (req, res) => {
const user = await User.findById(req.user._id);
if (user) {
const { email } = req.body;
const shippingAddress = await ShippingAddress.findOne({
userEmail: email,
});
if (shippingAddress) {
shippingAddress.address = req.body.address || shippingAddress.address;
shippingAddress.city = req.body.city || shippingAddress.city;
shippingAddress.postalCode =
req.body.postalCode || shippingAddress.postalCode;
shippingAddress.country = req.body.country || shippingAddress.country;
const updatedShippingAddress = await shippingAddress.save();
res.status(200);
res.json({
address: updatedShippingAddress.address,
city: updatedShippingAddress.city,
postalCode: updatedShippingAddress.postalCode,
county: updatedShippingAddress.country,
email: updatedShippingAddress.userEmail,
});
} else {
res.status(404);
throw new Error("Address not found");
}
} else {
res.status(404);
throw new Error("User not found");
}
});
export { getAddress, saveAddress, changeAddress };
authMiddleware file;
import jwt from "jsonwebtoken";
import User from "../models/userModel.js";
import asyncHandler from "express-async-handler";
const protect = asyncHandler(async (req, res, next) => {
let token = req.headers.token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
) {
try {
// get token from req
token = req.headers.authorization.split(" ")[1];
// verify token
const decode = jwt.verify(token, process.env.JWT_SECRET);
// get user from token
req.user = await User.findById(decode.id).select("-password");
next();
} catch (error) {
res.status(401);
throw new Error("Not1 Authorized");
}
}
if (!token) {
res.status(401);
throw new Error("Not2 Authorized");
}
});
export default protect;
FOR FRONT-END;
shippingAction file;
import { createAsyncThunk } from "#reduxjs/toolkit";
import shippingServices from "./shippingServices";
// get address
export const getAddress = createAsyncThunk(
"shipping/get",
async (userInfo, thunkAPI) => {
try {
return await shippingServices.get(userInfo);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
// save address
export const saveAddress = createAsyncThunk(
"shipping/save",
async (addressInfo, thunkAPI) => {
try {
return await shippingServices.save(addressInfo);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
// update address
export const updateAddress = createAsyncThunk(
"shipping/update",
async (addressInfo, thunkAPI) => {
try {
return await shippingServices.update(addressInfo);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
shippingServices file;
import axios from "axios";
const SHIPPING_ADDRESS_URL = "/api/shippingaddress";
// save address
const save = async (addressInfo) => {
const config = {
headers: {
Authorization: `Bearer ${addressInfo.token}`,
},
};
const { data } = await axios.post(SHIPPING_ADDRESS_URL, addressInfo, config);
return data;
};
// get address
const get = async (userInfo) => {
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
};
const { data } = await axios.get(SHIPPING_ADDRESS_URL, userInfo, config);
return data;
};
// update address
const update = async (addressInfo) => {
const config = {
headers: {
Authorization: `Bearer ${addressInfo.token}`,
},
};
const { data } = await axios.put(SHIPPING_ADDRESS_URL, addressInfo, config);
return data;
};
const shippingServices = {
save,
get,
update,
};
export default shippingServices;
shippigPage file;
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Form, Button } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import FormContainer from "../components/FormContainer";
import Message from "../components/Message";
import Loader from "../components/Loader";
import { shippingSliceAction } from "../features/shipping/shippingSlice";
import { saveAddress, getAddress } from "../features/shipping/shippingAction";
function ShippingPage() {
const { user } = useSelector((state) => state.auth);
const { shippingAddress, isLoading, isSuccess, isError, message } =
useSelector((state) => state.shipping);
const [address, setAddress] = useState("");
const [city, setCity] = useState("");
const [postalCode, setPostalCode] = useState("");
const [country, setCountry] = useState("");
const [comMessage, setComMessage] = useState(null);
const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => {
if (shippingAddress) {
setAddress(shippingAddress.address);
setCity(shippingAddress.city);
setPostalCode(shippingAddress.postalCode);
setCountry(shippingAddress.country);
} else {
const userInfo = {
token: user.token,
email: user.email,
};
dispatch(getAddress(userInfo));
}
}, []);
const submitHandler = (e) => {
e.preventDefault();
if (!user) {
navigate("/login");
} else {
if (!address || !city || !postalCode || !country) {
setComMessage("Please fill all fields");
setTimeout(() => setComMessage(null), 3000);
} else {
dispatch(
shippingSliceAction.takeAddress({
email: user.email,
address,
city,
postalCode,
country,
})
);
localStorage.setItem(
"userAddress",
JSON.stringify({
email: user.email,
address,
city,
postalCode,
country,
})
);
}
}
};
const saveContinue = () => {
if (!user) {
navigate("/login");
} else {
dispatch(
saveAddress({
email: user.email,
address,
city,
postalCode,
country,
token: user.token,
})
);
if (isSuccess) {
setComMessage("Your Address Saved");
setTimeout(() => dispatch(shippingSliceAction.reset()), 3000);
setTimeout(() => setComMessage(null), 3000);
setTimeout(() => navigate("/payment"), 4000);
}
}
};
return (
<FormContainer>
<h1>Shipping</h1>
{comMessage && <Message variant="danger">{comMessage}</Message>}
<Form onSubmit={submitHandler}>
<Form.Group controlId="address">
<Form.Label>Address</Form.Label>
<Form.Control
type="text"
placeholder="Enter Address"
value={address}
onChange={(e) => setAddress(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="city">
<Form.Label>City</Form.Label>
<Form.Control
type="text"
placeholder="Enter City"
value={city}
onChange={(e) => setCity(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="postalCode">
<Form.Label>Postal Code</Form.Label>
<Form.Control
type="number"
placeholder="Enter Postal Code"
value={postalCode}
onChange={(e) => setPostalCode(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="country">
<Form.Label>Country</Form.Label>
<Form.Control
type="text"
placeholder="Enter Country"
value={country}
onChange={(e) => setCountry(e.target.value)}
></Form.Control>
</Form.Group>
<Button className="shippingButton" type="submit" variant="primary">
Continue without saving
</Button>
{isLoading && <Loader />}
{isSuccess && <Message variant="success"></Message>}
{isError && <Message variant="danger">{message}</Message>}
<Button
className="shippingButton save"
type="button"
variant="secondary"
onClick={saveContinue}
>
Save my address and continue
</Button>
</Form>
</FormContainer>
);
}
export default ShippingPage;
// shippingServices.js
const { data } = await axios.get(SHIPPING_ADDRESS_URL, userInfo, config);
this should be
const { data } = await axios.get(SHIPPING_ADDRESS_URL, config);
Also stop using unnecessary packages like express-async-handler,
just cover entire middle-ware function body with try/catch block, call next(error) in catch block & it will work just fine.
I am using socket.io with express and typescript, when want to emit to particular logged in user it is not working, the rest is working fine, when new user join and other things, in App.ts in backend it looks like:
httpServer.listen(8000, () => {
console.log(`app is running on: http://localhost:8000`);
});
//SocketIO
const io = new Server(httpServer, {
cors: {
credentials: true,
},
});
app.set("socketio", io);
io.use((socket: SocketWithUser, next: any) => {
const token: any = socket.handshake.query.token;
if (token) {
try {
const payload = jwt.verify(
token,
<string>process.env.JWT_TOKEN
) as DataStoredInToken;
socket.userId = payload._id;
return next();
} catch (err) {
next(err);
}
} else {
return next(new Error("Access Denied"));
}
});
io.on("connection", (socket: SocketWithUser) => {
if (socket.userId) {
socket.join(socket.userId);
socket.emit("joined", `user ${socket.userId} joined`);
}
socket.on("disconnect", () => {
console.log("disconnect");
});
});
and in another route sockethandler
import { Request } from "express";
import { NotificationProps } from "types/notification";
export const sendNotification = (
req: Request,
notification: NotificationProps
) => {
const io = req.app.get("socketio");
io.sockets
.in(String(`${notification.receiver}`))
.emit("newNotification", notification);
};
the like post route looks like
export const likePost = async (req: RequestWithUser, res: Response) => {
const { postId } = req.body;
const post = await PostModel.findById(postId);
if (!post) return res.status(400).send({ msg: `post does not exist` });
const checkIfLiked = post.likes.find(
(item: any) => String(item.user._id) === String(req.user_id)
);
if (!checkIfLiked) {
await post.updateOne(
{
$push: { likes: { user: req.user_id } },
},
{ new: true }
);
const notification = new Notification({
sender: req.user_id,
receiver: post.user,
notificaitonType: "like",
});
await notification.save();
sendNotification(req, notification);
return res.status(200).send({ success: true });
}
const postWithOutLike = await post.updateOne(
{
$pull: { likes: { user: req.user_id } },
},
{ new: true }
);
return res.status(200).send({ postWithOutLike });
};
in the frontend react app just calling it like:
socketIo().on("newNotification", (data) => {
console.log({ data });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
any help please?
I believe you need want io.in
// to all clients in room
io.in(notification.receiver).emit('newNotification', notification);
Ref
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 };