У меня есть проблема: при регистрации меня кидать на страцницу сайта и сразу же выкидывает на страницу логина, я так понимаю потому что jwt токена при регистрации не дают. Исправим это. SignUp.jsx import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import logo from '../../../img/LogoSpring.svg';
import style from './AuthUp.module.css';
import { loginSuccess } from '../../Redux/actions';
import classNames from 'classnames';

const InputField = ({ name, type, placeholder, value, error, onChange }) => (
    <>
        <input
            className={classNames(style.form_input, { [style.error]: error })}
            type={type}
            name={name}
            placeholder={placeholder}
            value={value}
            onChange={onChange}
        />
        {error && <span className={style.error_message}>{error}</span>}
    </>
);

export const SignUpPage = () => {
    const [formData, setFormData] = useState({
        username: '',
        password: '',
        repeatPassword: '',
        firstName: '',
        lastName: '',
        age: '',
    });

    const [errors, setErrors] = useState({});
    const navigate = useNavigate();
    const dispatch = useDispatch();

    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData({ ...formData, [name]: value });
        setErrors((prevErrors) => ({ ...prevErrors, [name]: '' }));
    };

    const handleSubmit = async (e) => {
        e.preventDefault();

        try {
            const response = await fetch('http://localhost:5000/signup', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(formData),
            });

            if (response.ok) {
                alert('Signup successful!');
                dispatch(loginSuccess());
                navigate('/');
            } else {
                const data = await response.json();
                if (data.errors) {
                    setErrors(data.errors);
                } else {
                    alert(data.message || 'Signup failed. Please try again.');
                }
            }
        } catch (error) {
            alert('Signup failed. Please try again.');
        }
    };

    return (
        <div className={style.form_wrapper}>
            <img className={style.header__logo} alt="Spring Framework Logo" src={logo} />
            <h1 className={style.form_title}>Create your account</h1>
            <form onSubmit={handleSubmit} className={style.form}>
                <InputField
                    name="username"
                    type="text"
                    placeholder="Username"
                    value={formData.username}
                    error={errors.username}
                    onChange={handleChange}
                />
                <InputField
                    name="password"
                    type="password"
                    placeholder="Password"
                    value={formData.password}
                    error={errors.password}
                    onChange={handleChange}
                />
                <InputField
                    name="repeatPassword"
                    type="password"
                    placeholder="Repeat Password"
                    value={formData.repeatPassword}
                    error={errors.repeatPassword}
                    onChange={handleChange}
                />
                <InputField
                    name="firstName"
                    type="text"
                    placeholder="First Name"
                    value={formData.firstName}
                    error={errors.firstName}
                    onChange={handleChange}
                />
                <InputField
                    name="lastName"
                    type="text"
                    placeholder="Last Name"
                    value={formData.lastName}
                    error={errors.lastName}
                    onChange={handleChange}
                />
                <InputField
                    name="age"
                    type="number"
                    placeholder="Age"
                    value={formData.age}
                    error={errors.age}
                    onChange={handleChange}
                />

                <button type="submit" className={style.form_button_submit}>
                    Sign Up
                </button>
            </form>
            <button onClick={() => navigate('/login')} className={style.switch_form_button}>
                Already have an account?
            </button>
        </div>
    );
};
api.js export const fetchWithAuth = async (url, options = {}) => {
    let accessToken = localStorage.getItem('accessToken');
    let refreshToken = localStorage.getItem('refreshToken');

    // Helper function to redirect to login
    const redirectToLogin = () => {
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');
        window.location.href = '/login';
    };

    // Add Authorization header with the access token
    options.headers = {
        ...options.headers,
        'Authorization': `Bearer ${accessToken}`,
    };

    let response = await fetch(url, options);

    if (response.status === 401) {
        // Access token might be expired
        console.log('Access token expired. Attempting to refresh...');

        // Attempt to refresh the access token
        const refreshResponse = await fetch('http://localhost:5000/refresh', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ refreshToken })
        });

        if (refreshResponse.status === 200) {
            const data = await refreshResponse.json();
            accessToken = data.accessToken;
            localStorage.setItem('accessToken', accessToken);

            // Log console message about access token refresh
            console.log('Access token refreshed.');

            // Retry the original request with new access token
            options.headers['Authorization'] = `Bearer ${accessToken}`;
            response = await fetch(url, options);

            // If the retried request fails, handle accordingly
            if (response.status === 401 || response.status === 403) {
                console.log('Retried request failed after token refresh. Redirecting to login.');
                redirectToLogin();
                throw new Error('Session expired. Please log in again.');
            }
        } else {
            // Refresh token is invalid or expired
            console.log('Refresh token expired or invalid. Redirecting to login.');
            redirectToLogin();
            throw new Error('Session expired. Please log in again.');
        }
    } else if (response.status === 403) {
        // Access forbidden, redirect to login
        console.log('Access forbidden. Redirecting to login.');
        redirectToLogin();
        throw new Error('Access forbidden. Please log in again.');
    }

    return response;
}; actions.js import { fetchWithAuth } from './api.js';

export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_FAILURE = 'LOGIN_FAILURE';
export const FETCH_CARDS_SUCCESS = 'FETCH_CARDS_SUCCESS';
export const FETCH_CARDS_FAILURE = 'FETCH_CARDS_FAILURE';

export const loginSuccess = (username) => ({
    type: LOGIN_SUCCESS,
    payload: username
});

export const loginFailure = (error) => ({
    type: LOGIN_FAILURE,
    payload: error
});


export const loginThunk = (username, password) => {
    return async (dispatch) => {
        try {
            const response = await fetch('http://localhost:5000/login', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ username, password })
            });

            if (response.status === 200) {
                const data = await response.json();
                localStorage.setItem('accessToken', data.accessToken);
                localStorage.setItem('refreshToken', data.refreshToken);
                dispatch(loginSuccess(data.username));
            } else {
                const { message } = await response.json();
                dispatch(loginFailure(message));
            }
        } catch {
            dispatch(loginFailure('Network error'));
        }
    };
};
export const fetchCardsSuccess = (cards) => ({
    type: FETCH_CARDS_SUCCESS,
    payload: cards
});

export const fetchCardsFailure = (error) => ({
    type: FETCH_CARDS_FAILURE,
    payload: error
});

export const fetchCards = (searchTerm = '') => {
    return async (dispatch) => {
        try {
            const response = await fetchWithAuth(
                `http://localhost:5000/api/cards?search=${encodeURIComponent(searchTerm)}`
            );

            if (!response.ok) {
                throw new Error('Failed to fetch cards');
            }

            const data = await response.json();
            dispatch(fetchCardsSuccess(data));
        } catch (error) {
            console.error(error);
            dispatch(fetchCardsFailure(error.message));
        }
    };
};

export const logout = () => {
    return (dispatch) => {
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');
        localStorage.removeItem('username');
        dispatch({ type: 'LOGOUT' });
    };
}; authReducer.js import { LOGIN_SUCCESS, LOGIN_FAILURE } from './actions';

const initialState = {
    isAuthenticated: !!localStorage.getItem('accessToken'),
    error: null,
    username: localStorage.getItem('username') || ''
};
export const authReducer = (state = initialState, action) => {
    switch (action.type) {
        case LOGIN_SUCCESS:
            localStorage.setItem('username', action.payload);
            return { ...state, isAuthenticated: true, error: null, username: action.payload };
        case LOGIN_FAILURE:
            return { ...state, isAuthenticated: false, error: action.payload, username: '' };
        case 'LOGOUT':
            localStorage.removeItem('username');
            return { ...state, isAuthenticated: false, error: null, username: '' };
        default:
            return state;
    }
}; reducer.js import { FETCH_CARDS_SUCCESS, FETCH_CARDS_FAILURE } from './actions';

const initialState = {
    cards: [],
    error: null
};

export const cardsReducer = (state = initialState, action) => {
    switch (action.type) {
        case FETCH_CARDS_SUCCESS:
            return {
                ...state,
                cards: action.payload,
                error: null
            };
        case FETCH_CARDS_FAILURE:
            return {
                ...state,
                error: action.payload
            };
        default:
            return state;
    }
};
store.js import { createStore, combineReducers, applyMiddleware } from 'redux';
import { thunkMiddleware } from './thunk';
import { cardsReducer } from './reducer';
import { authReducer } from './authReducer';

const rootReducer = combineReducers({
    cards: cardsReducer,
    auth: authReducer
});

export const store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
thunk.js export const thunkMiddleware =
    ({ dispatch, getState }) =>
    (next) =>
    (action) => {
        if (typeof action === 'function') {
            return action(dispatch, getState);
        }
        return next(action);
    }; index. js import 'dotenv/config';
import express from 'express';
import cors from 'cors';
import { pool } from './db.js';
import 'dotenv/config';
import jwt from 'jsonwebtoken';
import { authenticateToken } from './authenticateToken.js';

const app = express();
const PORT = 5000;

app.use(cors());
app.use(express.json());
app.use('/img', express.static('img'));

app.post('/login', async (req, res) => {
    const { username, password } = req.body;

    try {
        const userCheck = await pool.query('SELECT * FROM users WHERE username = $1', [username]);
        if (userCheck.rows.length === 0) {
            return res.status(401).json({ message: 'Invalid username or password' });
        }

        const user = userCheck.rows[0];

        if (user.password !== password) {
            return res.status(401).json({ message: 'Invalid username or password' });
        }

        // Generate Access and Refresh Tokens
        const userId = user.id;

        const accessToken = jwt.sign({ userId }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '1s' });
        const refreshToken = jwt.sign({ userId }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '3m' });

        // Send tokens and username to client
        res.status(200).json({ accessToken, refreshToken, username });
    } catch (error) {
        console.error(error);
        res.status(500).json({ message: 'Server error' });
    }
});

app.get('/api/cards', authenticateToken, async (req, res) => {
    const { search } = req.query;

    try {
        let queryText = 'SELECT * FROM cards';
        let queryParams = [];

        if (search) {
            queryText += ' WHERE LOWER(heading) LIKE $1 OR LOWER(description) LIKE $1';
            queryParams.push(`%${search.toLowerCase()}%`);
        }

        const result = await pool.query(queryText, queryParams);

        const cards = result.rows.map((row) => ({
            id: row.id,
            card: {
                image: {
                    alt: row.image_alt,
                    src: row.image_src,
                },
                text: {
                    heading: row.heading,
                    description: row.description,
                },
            },
        }));

        return res.status(200).json(cards);
    } catch (error) {
        console.error('Error fetching cards:', error);
        res.status(500).json({ message: 'Server error' });
    }
});

app.post('/refresh', (req, res) => {
    const { refreshToken } = req.body;

    if (!refreshToken) return res.sendStatus(401);

    jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
        if (err) {
            console.error('Error verifying refresh token:', err);
            return res.sendStatus(403);
        }
        const userId = user.userId;
        const newAccessToken = jwt.sign({ userId }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '2s' });
        res.json({ accessToken: newAccessToken });
    });
});

app.post('/signup', async (req, res) => {
    const { username, password, repeatPassword, firstName, lastName, age } = req.body;

    const errors = {};

    if (username.length < 3) {
        errors.username = 'Username must contain 3 symbols or more';
    }
    if (password.length < 4  || !/\d/.test(password) || !/[a-zA-Z]/.test(password)) {
        errors.password = 'Password must contain at least 1 number, 1 letter, and be 4 symbols or more';
    }
    if (password !== repeatPassword) {
        errors.repeatPassword = 'Passwords do not match';
    }
    if (firstName.length < 3) {
        errors.firstName = 'First name must contain 3 symbols or more';
    }
    if (lastName.length < 3) {
        errors.lastName = 'Last name must contain 3 symbols or more';
    }
    if (isNaN(age) || age <= 0) {
        errors.age = 'Age must be a number and cannot be zero or negative';
    }

    if (Object.keys(errors).length > 0) {
        return res.status(400).json({ errors });
    }

    try {
        const userCheck = await pool.query('SELECT * FROM users WHERE username = $1', [username]);
        if (userCheck.rows.length > 0) {
            return res.status(400).json({ errors: { username: 'Username already exists' } });
        }

        const newUser = await pool.query(
            'INSERT INTO users (username, password, first_name, last_name, age) VALUES ($1, $2, $3, $4, $5) RETURNING *',
            [username, password, firstName, lastName, age]
        );

        res.status(201).json({ message: 'User registered successfully', user: newUser.rows[0] });
    } catch (error) {
        console.error(error);
        res.status(500).json({ message: 'Server error' });
    }
});
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});