import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import dotenv from "dotenv";
import { findUserByUsername, createUser } from "../model/userModel.js";

dotenv.config();

const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET;
const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET;

let refreshTokens = [];

export const login = async (req, res) => {
  const { username, password } = req.body;

  try {
    const user = await findUserByUsername(username);

    if (!user) {
      return res.status(401).json({ message: "Неверные учетные данные" });
    }

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

    if (!isPasswordValid) {
      return res.status(401).json({ message: "Неверные учетные данные" });
    }

    const userPayload = {
      userId: user.id,
      username: user.username,
    };

    const accessToken = jwt.sign(userPayload, ACCESS_TOKEN_SECRET, {
      expiresIn: "30s",
    });

    const refreshToken = jwt.sign(userPayload, REFRESH_TOKEN_SECRET, {
      expiresIn: "2m",
    });
    console.log("Generated Refresh Token:", refreshToken);
    refreshTokens.push(refreshToken);
    console.log("Current Refresh Tokens:", refreshTokens);

    res.status(200).json({
      accessToken,
      refreshToken,
      user: {
        userId: user.id,
        username: user.username,
      },
    });
  } catch (error) {
    console.error("Ошибка входа:", error);
  }
};

export const refreshToken = (req, res) => {
  const { refreshToken } = req.body;

  console.log("Received Refresh Token for Refresh:", refreshToken);
  console.log("Valid Refresh Tokens:", refreshTokens.includes(refreshToken));
  if (!refreshToken)
    return res.status(401).json({ message: "Токен не предоставлен" });
  if (!refreshTokens.includes(refreshToken))
    return res.status(403).json({ message: "Недействительный токен" });

  jwt.verify(refreshToken, REFRESH_TOKEN_SECRET, (err, user) => {
    if (err) return res.status(403).json({ message: "Недействительный токен" });

    const userPayload = {
      userId: user.userId,
      username: user.username,
    };

    const newAccessToken = jwt.sign(userPayload, ACCESS_TOKEN_SECRET, {
      expiresIn: "30s",
    });

    res.json({ accessToken: newAccessToken });
  });
};

export const logout = (req, res) => {
  const { refreshToken } = req.body;
  refreshTokens = refreshTokens.filter((token) => token !== refreshToken);
  res.status(200).json({ message: "Выход из системы выполнен" });
};

export const signup = async (req, res) => {
  const { firstName, lastName, username, password, age } = req.body;

  try {
    const existingUser = await findUserByUsername(username);

    if (existingUser) {
      return res
        .status(400)
        .json({ errors: { username: "Имя пользователя уже занято." } });
    }

    const hashedPassword = await bcrypt.hash(password, 12);

    await createUser({
      firstName,
      lastName,
      username,
      password: hashedPassword,
      age,
    });

    res.status(201).json({ message: "Регистрация прошла успешно" });
  } catch (error) {
    console.error("Ошибка во время регистрации:", error);
    res.status(500).json({ message: "Ошибка сервера" });
  }
};



Это код сервера, на клиента происходит ошибка:

Server is connected to frontend
jwtFunctions.js:3 
        
        
       POST https://server-ancient-grass-9030.fly.dev/api/login 401 (Unauthorized)
fetchWithAuth @ jwtFunctions.js:3
(anonymous) @ actions.js:12
(anonymous) @ redux-thunk.js?v=9bc83e65:7
(anonymous) @ App.jsx:43
commitHookEffectListMount @ chunk-M7F2YF5A.js?v=9bc83e65:16915
commitPassiveMountOnFiber @ chunk-M7F2YF5A.js?v=9bc83e65:18156
commitPassiveMountEffects_complete @ chunk-M7F2YF5A.js?v=9bc83e65:18129
commitPassiveMountEffects_begin @ chunk-M7F2YF5A.js?v=9bc83e65:18119
commitPassiveMountEffects @ chunk-M7F2YF5A.js?v=9bc83e65:18109
flushPassiveEffectsImpl @ chunk-M7F2YF5A.js?v=9bc83e65:19490
flushPassiveEffects @ chunk-M7F2YF5A.js?v=9bc83e65:19447
(anonymous) @ chunk-M7F2YF5A.js?v=9bc83e65:19328
workLoop @ chunk-M7F2YF5A.js?v=9bc83e65:197
flushWork @ chunk-M7F2YF5A.js?v=9bc83e65:176
performWorkUntilDeadline @ chunk-M7F2YF5A.js?v=9bc83e65:384
Show 12 more frames
Show less
jwtFunctions.js:31 Refresh Access Token start
jwtFunctions.js:33 Retrieved Refresh Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpYXQiOjE3Mjg1Njk2OTQsImV4cCI6MTcyODU2OTc0NH0.kCFmD-42OX1aLJPzvl8b18-rLR7E12yURXtHXhrJlFo
actions.js:122 
        
        
       GET https://server-ancient-grass-9030.fly.dev/api/cards 403 (Forbidden)
(anonymous) @ actions.js:122
(anonymous) @ redux-thunk.js?v=9bc83e65:7
(anonymous) @ App.jsx:28
commitHookEffectListMount @ chunk-M7F2YF5A.js?v=9bc83e65:16915
commitPassiveMountOnFiber @ chunk-M7F2YF5A.js?v=9bc83e65:18156
commitPassiveMountEffects_complete @ chunk-M7F2YF5A.js?v=9bc83e65:18129
commitPassiveMountEffects_begin @ chunk-M7F2YF5A.js?v=9bc83e65:18119
commitPassiveMountEffects @ chunk-M7F2YF5A.js?v=9bc83e65:18109
flushPassiveEffectsImpl @ chunk-M7F2YF5A.js?v=9bc83e65:19490
flushPassiveEffects @ chunk-M7F2YF5A.js?v=9bc83e65:19447
(anonymous) @ chunk-M7F2YF5A.js?v=9bc83e65:19328
workLoop @ chunk-M7F2YF5A.js?v=9bc83e65:197
flushWork @ chunk-M7F2YF5A.js?v=9bc83e65:176
performWorkUntilDeadline @ chunk-M7F2YF5A.js?v=9bc83e65:384
Show 12 more frames
Show less
Cards.jsx:5 {message: 'Invalid token'}
Cards.jsx:5 {message: 'Invalid token'}

jwtFunctions.js:40 
 POST https://server-ancient-grass-9030.fly.dev/api/token 403 (Forbidden)
refreshAccessToken	@	jwtFunctions.js:40
fetchWithAuth	@	jwtFunctions.js:12
await in fetchWithAuth		
(anonymous)	@	actions.js:12
(anonymous)	@	App.jsx:43
Show 12 more frames
jwtFunctions.js:57 Failed to refresh access token