Nide.js 在 Safari 浏览器上无法正常运行

问题描述 投票:0回答:0

我正在尝试开发 mern 电子商务应用程序并面临着非常奇怪的问题。在 chrome 上一切正常,但它总是在 safari 浏览器上给我“未经授权”的错误,从我的中间件检查用户令牌是否有效。(我在不同的计算机上尝试过,并得到相同的结果)但是在 chrome 上,一切都很完美。我更新了每个需要更新的包。并搜索了几乎所有内容,但没有成功。如果比我更有经验的人能解释发生了什么,我会非常高兴。提前谢谢你。

我将共享我的整个用户控制器,以防我在那里和我的中间件出现问题。


const asyncHandler =  require("express-async-handler");
const User =  require("../models/userModel");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");
const Token = require("../models/tokenModel");
const crypto = require("crypto");
const sendEmail = require("../util/sendMail");
const {fileSizeFormater} = require("../util/fileUpload");
const cloudinary = require("cloudinary").v2;
const Product = require("../models/productModel");
const stripe = require("stripe")(my stripe details
})




const generateToken =  (id)=> {
    return jwt.sign({id}, "secret", {expiresIn: "1d"})
}


// User Registration

const registerUser = asyncHandler(  async (req,res,next) => {
   
    const {name,email,password} = req.body;

    // check if user don't leaves any of required fields
    if(!name || !email || !password){
       res.status(400);
       throw new Error("Please enter all required fields")
    };

    // cheking user's password length
    if(password.length < 6 ){
        res.status(400);
        throw new Error("Your password is too short")
    };
    if(password.length > 23 ){
        res.status(400);
        throw new Error("Your password is too long");
    };

    // cheking if user with provided email adress already exists or not
    const existingUser = await User.findOne({email});

    if(existingUser){
        res.status(400);
        throw new Error(`User with email: ${email} already exists`);
    };

    let fileData = {}

    if(req.file){
        // Upload file to cloudinary 
        let uploadedFile;

        try {
          uploadedFile = await cloudinary.uploader.upload(req.file.path, {folder: "Shopper tools", resource_type: "image"})
            
        } catch (error) {
            res.status(500);
            throw new Error(error)
        }
        fileData = {
            fileName: req.file.originalname,
            filePath: uploadedFile.secure_url,
            fileType: req.file.mimetype,
            fileSize: fileSizeFormater(req.file.size,2)

        }

    }

    // creating new user

     const  user = await User.create({
        name,
        email,
        password,
        picture: fileData
      });

    //   Generate token for the user

     const token = generateToken(user._id)
    
    //  Sending cookie

    res.cookie("token", token, {
        path: "/",
        httpOnly:true,
       expires: new Date(Date.now() + 1000 * 86400), 
       sameSite: "none",
       secure: true
    });
  
    if(user){
        const {_id, name, password, email, picture, phone,bio} = user
        res.status(201).json({_id,name,email,password,picture,phone, bio, token})
    }else{
        res.status(400);
        throw new Error("User was not created")
    }

});


// User Login

const loginUser = asyncHandler( async (req,res,next)=> {
     const {email,password} = req.body;
  
    // Validate inputs

     if(!email || !password) {
        res.status(400);
        throw new Error("Please fiel empty fields");
     };

    //  Check if user  exists or not
    const user = await User.findOne({email});

    if(!user){
        res.status(400);
        throw new Error(`user with email: ${email} does not exist, please signup.`)
    };
    
    // user exists but now check the password
    const passwordIsCorrect = await bcrypt.compare(password,user.password);

     //   Generate token for the user

     const token = generateToken(user._id)
    
    //  Sending cookie

    if(passwordIsCorrect){
        res.cookie("token", token, {
            path: "/",
            httpOnly:true,
            expires: new Date(Date.now() + 1000 * 86400), 
            sameSite: "none",
            secure: true
        })
    }


    if(user && passwordIsCorrect){
        const {_id,name, email,password,bio,phone,picture} = user;
        res.status(200).json({ _id,name, email,password,bio,phone,picture,token});
    }else{
        res.status(400);
        throw new Error("Invalid email or password")
    }

});

// log out user


const logoutUser = asyncHandler(async (req,res,next)=>{
    res.cookie("token", "", {
        path: "/",
        httpOnly:true,
        expires: new Date(0), 
       sameSite: "none",
       secure: true
    })

    return res.status(200).json({message: "Successfully logged out"})

});

// Get the user

const getUser = asyncHandler(async (req,res, next)=> {
        
    const user = await User.findById(req.user._id);

    if(user ){
        const {name, email,bio,phone,picture,cart} = user;
        res.status(200).json({ name, email,bio,phone,picture,cart});
    }else{
        res.status(401);
        throw new Error("user was not found")
    }


});

// Get Loggedin Status

const getLoginStatus =  asyncHandler(async (req,res,next)=> {
      const token = req.cookies.token;

      if(!token){
        return res.json(false);
      };

    //   verfie token

    const verfiedToken = jwt.verify(token,"secret");

    if(verfiedToken){
        return res.json(true);
    };

    return res.json(false)

});

// Upadate User

const updateuser =  asyncHandler(async (req,res,next)=> {
     const user = await User.findById(req.user._id);

       // Handle image upload

    let fileData = {}

    if(req.file){
        // Upload file to cloudinary 
        let uploadedFile;

        try {
          uploadedFile = await cloudinary.uploader.upload(req.file.path, {folder: "Shopper tools", resource_type: "image"})
            
        } catch (error) {
            res.status(500);
            throw new Error(error)
        }
        fileData = {
            fileName: req.file.originalname,
            filePath: uploadedFile.secure_url,
            fileType: req.file.mimetype,
            fileSize: fileSizeFormater(req.file.size,2)

        }

    }


     
     if(user){
        const {name, picture,phone,email,bio} = user;
        user.email = email;
        user.name = req.body.name || name;
        user.picture = Object.keys(fileData).length === 0 ? picture : fileData
        user.phone = req.body.phone || phone;
        user.bio = req.body.bio || bio;

      const updateuser =  await user.save();

        res.status(200).json({
            _id: updateuser._id,
            name: updateuser.name,
            phone: updateuser.phone,
            bio: updateuser.bio,
            picture:updateuser.picture,
            email: updateuser.email,
        });

     } else {
        res.status(401);
        throw new Error("User not found")
     }
});

// Change password

const changePassword = asyncHandler(async (req,res, next) => {
    const user = await User.findById(req.user._id);

    const {oldPassword, newPassword} = req.body

    // validate parameters

    if(!user){
        res.status(400);
        throw new Error("User not found, please login or signup")
    };

    if(!oldPassword || !newPassword){
        res.status(400);
        throw new Error("Fields are required! please fiel")
    };

    // check if oldPassword matches the user's current password

    const passwordIsCorrect = await bcrypt.compare(oldPassword, user.password);

    if(!passwordIsCorrect){
        res.status(400);
        throw new Error("Passowrd is incorrect, please enter correct current password");
    };

    if(passwordIsCorrect && user){
        user.password = newPassword;
        
        await user.save();

        res.status(200).json({msg: `Password changed successfully. New Password is - ${newPassword}`})
    } else{
        res.status(400);
        throw new Error("An unknown error occured");
    }
});

// Forgot password

const forgotPassword =  asyncHandler(async (req,res,next)=> {
    const {email} = req.body;

    // Find user with provided email

    const user = await User.findOne({email});

    if(!user){
        res.status(404);
        throw new Error("No user was found with provided email")
    };

    // Delete token if it already exists in database

    let token = await Token.findOne({userId: user._id});

    if(token){
        await token.deleteOne()
    }

    // Create reset Token if user exists

    // let resetToken = crypto.randomBytes(32).toString("hex") + user._id;
    let resetToken = crypto.randomBytes(32).toString("hex") + user._id
     
    //  Hash the resetToken

    const hashedToken = crypto.createHash("sha256").update(resetToken).digest("hex");

    // save hashed Token to database

    await new Token(
        {
           userId: user._id,
           token: hashedToken,
           createdAt: Date.now(),
           expiresAt: Date.now() + 30 * (60 *1000)
        }
    ).save();

    // Constract reset Url

    const resetUrl = `http://localhost:3000/resetpassword/${resetToken}`;

    // constract message

    const message = `
            
      <h2>Hello ${user.name}</h2>
      <p>This is your reset token which please note expires in 30 minutes</p>

      <a href=${resetUrl} clicktracking=off>${resetUrl}</a>

    `;

    const subject = "password reset request";
    const send_to = user.email;
    const sent_from = `[email protected]`;

    try {
        await sendEmail(subject,message,send_to,sent_from);
        res.status(200).json({success: true, message: "reset email sent"})
        
    } catch (error) {
         res.status(400);
         throw new Error(error.message)
    }


});


const resetPassword = asyncHandler(async (req, res) => {
    const { password } = req.body;
    const { resetToken } = req.params;
  
    // Hash token, then compare to Token in DB
    const hashedToken = crypto
      .createHash("sha256")
      .update(resetToken)
      .digest("hex");
  
    // fIND tOKEN in DB
    const userToken = await Token.findOne({
      token: hashedToken,
      expiresAt: { $gt: Date.now() },
    });
  
    if (!userToken) {
      res.status(404);
      throw new Error("Invalid or Expired Token");
    }
  
    // Find user
    const user = await User.findOne({ _id: userToken.userId });
    user.password = password;
    await user.save();
    res.status(200).json({
      message: "Password Reset Successful, Please Login",
    });
  });
  



// add to cart

const addToCart = asyncHandler( async (req,res,next)=>{

    const {productId} =  req.params

    const user =  await User.findById(req.user._id);

    const product =  await Product.findById(productId);

    if(product.quantity === 0){
        res.status(400)
        throw new Error("This Product is out of stcok");
    }

    
    const cartProductIndex = user.cart.items.findIndex(cp=>{
        return cp.productId.toString()  === productId.toString();
    });
    let newQty = 1;
    const updatedCartItems = [...user.cart.items];
    if(product.quantity === 0){
        res.status(400)
        throw new Error("This Product is out of stcok");
    }


    if(cartProductIndex >= 0) {
        newQty = user.cart.items[cartProductIndex].quantity + 1;
        updatedCartItems[cartProductIndex].quantity = newQty
    }else {
              updatedCartItems.push({
                productId: productId,
                quantity: newQty
              });
    }

    if( newQty > product.quantity){
        res.status(400);
        throw new Error(`only ${product.quantity}  ${product.name} are avalable right now`)
      }

    const updatedCart = {
              items: updatedCartItems
            };

    user.cart = updatedCart;
    
    await user.save();

    res.status(200).json({user})
})


// remove from cart 

const removeFromCart = asyncHandler(async (req,res,next)=>{
    const {productId} =  req.params
    const user =  await User.findById(req.user._id);
    const updatedCartItems = user.cart.items.filter(item => {
              return item.productId.toString() !== productId.toString();
            });
            user.cart.items = updatedCartItems;
            await user.save();

            res.status(200).json("product is removed from cart")

})


// Clear cart 

const clearCart = asyncHandler(async (req,res,next)=>{
   
    const user =  await User.findById(req.user._id);
     user.cart = {items:[]};
     await user.save()

 res.status(200).json({user})

})


// Decrease cart item quantity

const decreaseCartItemquantity = asyncHandler(async (req,res,next)=>{
      const {productId} = req.params
      const user =  await User.findById(req.user._id);
      const product = await Product.findById(productId);

      const cartProductIndex = user.cart.items.findIndex(cp=>{
        return cp.productId.toString()  === productId.toString();
    });
    let newQty;
    const updatedCartItems = [...user.cart.items];

    if(product.quantity === 0){
        res.status(400)
        throw new Error("This Product is out of stcok");
    }

    if(cartProductIndex >= 0 && user.cart.items[cartProductIndex].quantity !== 1) {
        newQty = user.cart.items[cartProductIndex].quantity - 1;
        updatedCartItems[cartProductIndex].quantity = newQty
    }else {
        newQty = 1
        updatedCartItems[cartProductIndex].quantity = newQty
    }

    const updatedCart = {
              items: updatedCartItems
            };

    user.cart = updatedCart;
    
    await user.save();

    res.status(200).json({user})  

});

// increase cart item quantity

const increaseCartItemquantity = asyncHandler(async (req,res,next)=>{
    const {productId} = req.params
    const user =  await User.findById(req.user._id);
    const product = await Product.findById(productId);

    const cartProductIndex = user.cart.items.findIndex(cp=>{
      return cp.productId.toString()  === productId.toString();
  });
  let newQty = 0 ;
  const updatedCartItems = [...user.cart.items];

  if(product.quantity === 0){
      res.status(400)
      throw new Error("This Product is out of stcok");
  }

  if(cartProductIndex >=0 ) {
      newQty = user.cart.items[cartProductIndex].quantity + 1;
      updatedCartItems[cartProductIndex].quantity = newQty
  }else {
      newQty = 1
      updatedCartItems[cartProductIndex].quantity = newQty
  }

  if( newQty > product.quantity){
    res.status(400);
    throw new Error(`only ${product.quantity}  ${product.name} are avalable right now`)
  }

  const updatedCart = {
            items: updatedCartItems
          };

  user.cart = updatedCart;
  
  await user.save();

  res.status(200).json({user})  

});

const getCart = asyncHandler(async (req,res,next) => {


    const user = await User.findById(req.user._id);

    if(!user){
        res.status(400);
        throw new Error("No user was found. Please register ")
    };
     const cartItems = await user.populate('cart.items.productId')

    res.status(200).json(cartItems)

});

const getCheckout = asyncHandler(async (req, res, next) => {
  const user = await User.findById(req.user._id);
  if (!user) {
    res.status(400);
    throw new Error('No user was found. Please register.');
  }

  // Populate the cart items with their associated product information
  const cartItems = await User.findById(req.user._id).populate('cart.items.productId').lean();
  
  // Calculate the line items and total cart value
  let lineItems = [];
  let cartValue = 0;

  for (const item of cartItems.cart.items) {
    const { _id: id, name, description, price, image } = item.productId;

    // Calculate the total price of the item based on the quantity
    const quantity = item.quantity;
    const totalPrice = quantity * price;

    // Convert the total price to cents
    const unitAmount = Math.round(totalPrice * 100);

    // Add the line item to the array
    lineItems.push({
      price_data: {
        currency: 'usd',
        product_data: {
          name,
          description,
          images: [image?.filePath],
        },
        unit_amount: unitAmount,
      },
      quantity,
    });

    // Add the total price of the item to the cart value
    cartValue += totalPrice;
  }

  const session = await stripe.checkout.sessions.create({
    payment_method_types: ['card'],
    line_items: lineItems,
    success_url: req.protocol + '://' + req.get('host') + '//checkout/success',
    cancel_url: req.protocol + '://' + req.get('host') + '//checkout/cancel',
    customer_email: user.email,
    billing_address_collection: 'auto',
    metadata: {
      cartValue,
    },
    locale: 'auto',
    mode: 'payment',
  });

  res.status(200).json({ url: session.url });
});







 MIDDLEWARE:


const asyncHandler = require("express-async-handler");
const User = require("../models/userModel");
const jwt = require("jsonwebtoken");
const cookie = require("cookie");

const protectFromUnAuthorizedAttack = asyncHandler(async (req, res, next) => {
  try {
    const cookies = cookie.parse(req.headers.cookie || "");

    const token = cookies.token;

    if (!token) {
      res.status(401);
      throw new Error("Not authorized");
    }

    // check token validity
    const verifiedToken = jwt.verify(token, "secret");

    // Find User Id by verified token
    const user = await User.findById(verifiedToken.id).select("-password");

    if (!user) {
      res.status(401);
      throw new Error("User was not found");
    }

    req.user = user;
    next();
  } catch (err) {
    res.status(401);
    throw new Error(err.message);
  }
});










我几乎尝试了一切,包括 chatGPT

node.js authentication safari jwt mern
© www.soinside.com 2019 - 2024. All rights reserved.