我正在尝试开发 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