Проблемы с роутингом react-admin

При входе в аккаунт админа, происходит редирект на страницу react-admin, а потом на /users (http://localhost:3000/admin#/users). Не понимаю, что конфликтует и почему так происходит, никаких ошибок нет. Помогите пожалуйста. (ip изменил на ...)

Back-end часть

main.js:

const express = require('express');
const mongoose = require('mongoose');
const swaggerUi = require('swagger-ui-express');
const swaggerDocs = require('./Other/SwaggerDoc/swaggerDoc');
const bookRoutes = require('./Books/booksRoutes');
const userRoutes = require('./User/userRoutes');
const authRoutes = require('./User/authRoutes')
const passport = require('./User/passportConfig');
const cors = require('cors');
const corsConfig = require('./Other/corsConfig');
const session = require('express-session');
const app = express();
 
app.use(express.json());
app.use(session({
  secret: '',
  resave: false,
  saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors(corsConfig));
 
 
async function connectToDatabase() {
  const uris = [
    'mongodb://.../bookapi',
    'mongodb://.../bookapi'
  ];
  
  for (let uri of uris) {
    try {
      await mongoose.connect(uri, {useNewUrlParser: true, useUnifiedTopology: true});
      console.log(`Connected to ${uri}`);
      break;
    } catch (err) {
      console.log(`Failed to connect to ${uri}`);
    }
  }
}
 
connectToDatabase();
 
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
app.use('/api/books', bookRoutes);
app.use('/api/users', userRoutes);
app.use('/auth', authRoutes);
 
app.listen(3001, () => console.log('Listening on port 3001...'));

user.js:

const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
 
let userId = 0;
 
const userSchema = new mongoose.Schema({
  id: Number,
  email: {
    type: String,
    required: true,
    maxlength: 100,
    validate: {
      validator: function(v) {
        return /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/.test(v);
      },
      message: props => `${props.value} invalid email`
    }
  },
  password: {
    type: String,
    required: true,
    validate: {
      validator: function(v) {
        return v.length >= 8 && v.length <= 32;
      },
      message: 'password length should be 8 - 32 symbols'
    }
  },
  role: {
    type: String,
    enum: ['user', 'admin'],
    default: 'user'
  }
});
 
 
userSchema.pre('save', async function(next) {
  let user = this;
  if (user.isNew) {
    const maxUser = await User.find().sort('-id').limit(1);
    const maxId = maxUser.length > 0 ? maxUser[0].id : 0;
    user.id = maxId + 1;
  }
  if (user.isModified('password')) {
    user.password = await bcrypt.hash(user.password, 8);
  }
  next();
});
 
const User = mongoose.model('User', userSchema);
 
module.exports = User;

userRoutes.js:

const express = require('express');
const User = require('./user');
const router = express.Router();
 
router.post('/', async (req, res) => {
    try {
      const user = new User(req.body);
      await user.save();
      const userResponse = await User.findOne({id: user.id}).select('-_id -__v');
      res.status(201).send(userResponse);
    } catch (error) {
      res.status(400).send(error);
    }
  })  
 
router.get('/:id', async (req, res) => {
  try {
    const user = await User.findOne({id: req.params.id}).select('-_id -__v');
    if (!user) {
      return res.status(404).send();
    }
    res.send(user);
  } catch (error) {
    res.status(500).send(error);
  }
});
 
router.put('/', async (req, res) => {
  try {
    const user = await User.findOneAndUpdate({id: req.body.id}, req.body, { new: true, runValidators: true }).select('-_id -__v');
    if (!user) {
      return res.status(404).send();
    }
    res.send(user);
  } catch (error) {
    res.status(400).send(error);
  }
});
 
router.delete('/:id', async (req, res) => {
  try {
    const user = await User.findOneAndDelete({id: req.params.id}).select('-_id -__v');
    if (!user) {
      return res.status(404).send();
    }
    res.send(user);
  } catch (error) {
    res.status(500).send(error);
  }
});
 
router.get('/users', async (req, res) => {
  const users = await User.find().select('-_id -__v');
  res.send(users);
});
 
module.exports = router;

passportConfig.js:

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const User = require('./user');
const bcrypt = require('bcrypt');
 
passport.use(new LocalStrategy({ usernameField: 'email' },
  async (email, password, done) => {
    try {
      const user = await User.findOne({ email });
      if (!user) {
        return done(null, false, { message: 'Incorrect email.' });
      }
      if (!await bcrypt.compare(password, user.password)) {
        return done(null, false, { message: 'Incorrect password.' });
      }
      return done(null, user);
    } catch (error) {
      return done(error);
    }
  }
));
 
passport.serializeUser((user, done) => {
    done(null, user.id);
  });
  
  passport.deserializeUser((id, done) => {
    User.findById(id, (err, user) => {
      done(err, user);
    });
  });  
 
module.exports = passport;

authRoutes.js:

const express = require('express');
const passport = require('./passportConfig');
const User = require('./user');
const router = express.Router();
 
router.post('/login', passport.authenticate('local'), async (req, res) => {
    const user = await User.findOne({ email: req.user.email }).select('-_id -__v');
    res.send(user);
  });
 
router.get('/logout', (req, res) => {
  req.logout();
  res.send('Logged out');
});
 
module.exports = router;

corsConfig.js:

const corsConfig = {
  origin: 'http://localhost:3000',
  methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
  credentials: true,
  allowedHeaders: ['Content-Type', 'Authorization'],
  exposedHeaders: ['X-Total-Count']
};
 
module.exports = corsConfig;

Front-end часть

App.js:

import AppRouter from './components/AppRouter';
import './index.css';
import {HashRouter} from "react-router-dom";
function App() {
  return (
    <HashRouter>
      <div className="App">
        <AppRouter/>
      </div>
    </HashRouter>
  );
}
 
export default App;

AppRouter.jsx:

import React, { useEffect } from 'react'
import { Routes, Route, useNavigate, Outlet} from "react-router-dom";
import { Links } from '../router'
function AppRouter() {
  const navigate = useNavigate();
  useEffect(() => {
    navigate('/login')
  }, []);
  return (
    <Routes>
      <Route path="/*" element={<Outlet />} />
      {Links.map(route => {
        return (
          <Route
            element={React.createElement(route.component)}
            path={route.path}
            exact={route.exact}
          />
        );
      })}
    </Routes>
  )
}
export default AppRouter

router/index.js:

import AdminInt from "../pages/Admin/AdminInt";
import Login from "../pages/Login/Login";
export const Links = [
    {path: '/login', component: Login, exact: true},
    {path: '/admin/*', component: AdminInt , exact: true}
]

Login.jsx:

import React, { useState } from 'react';
import { useLogin, useNotify } from 'react-admin';
import { useNavigate } from 'react-router-dom';
 
const Login = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const login = useLogin();
  const notify = useNotify();
  const navigate = useNavigate();
 
  const handleSubmit = async (event) => {
    event.preventDefault();
    login({ email, password })
      .then(() => {
        notify('Successfully logged in', 'info');
        navigate('/admin');
      })
      .catch((error) => {
        notify('Invalid email or password', 'warning');
      });
  };  
 
  return (
    <form onSubmit={handleSubmit}>
      <label>
        Email:
        <input type="email" value={email} onChange={e => setEmail(e.target.value)} required />
      </label>
      <label>
        Password:
        <input type="password" value={password} onChange={e => setPassword(e.target.value)} required />
      </label>
      <button type="submit">Log in</button>
    </form>
  );
};
 
export default Login;

AdminInt.jsx:

import React from "react";
import { Admin, Resource } from 'react-admin';
import dataProvider from "../../components/Admin/dataProvider";
import { UserList, UserEdit, UserCreate } from '../../components/Admin/Tools';
const AdminInt = () => (
    <Admin dataProvider={dataProvider}>
        <Resource name="users" list={UserList} edit={UserEdit} create={UserCreate} />
    </Admin>
);
 
export default AdminInt;

Tools.jsx:

import React from "react";
import { List, Datagrid, TextField, EmailField, Edit, SimpleForm, TextInput, SelectInput, Create  } from 'react-admin';
 
export const UserList = props => (
    <List {...props}>
        <Datagrid rowClick="edit">
            <TextField source="id" />
            <TextField source="role" />
            <EmailField source="email" />
        </Datagrid>
    </List>
);
 
export const UserEdit = props => (
    <Edit {...props}>
        <SimpleForm>
            <TextInput disabled source="id" />
            <TextInput source="email" />
            <TextInput source="password" type="password" />
            <SelectInput source="role" choices={[
                { id: 'user', name: 'user' },
                { id: 'admin', name: 'admin' }
            ]} />
        </SimpleForm>
    </Edit>
);
 
export const UserCreate = props => (
    <Create {...props}>
        <SimpleForm>
            <TextInput source="email" />
            <TextInput source="password" type="password" />
            <SelectInput source="role" choices={[
                { id: 'user', name: 'user' },
                { id: 'admin', name: 'admin' }
            ]} />
        </SimpleForm>
    </Create>
);

dataProvider.js:

import { fetchUtils } from 'react-admin';
import { stringify } from 'query-string';
 
const apiUrl = 'http://.../api';
const httpClient = (url, options = {}) => {
  if (!options.headers) {
    options.headers = new Headers({ Accept: 'application/json' });
  }
  const token = localStorage.getItem('token');
  options.headers.set('Authorization', `Bearer ${token}`);
  return fetchUtils.fetchJson(url, options);
}
 
 
export default {
  getList: (resource, params) => {
      const { page, perPage } = params.pagination;
      const { field, order } = params.sort;
      const query = {
        sort: JSON.stringify([field, order]),
        range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
        filter: JSON.stringify(params.filter),
      };
      const url = `${apiUrl}/${resource}?${stringify(query)}`;
  
      return httpClient(url).then(({ headers, json }) => {
        headers.set('X-Total-Count', json.length);
        return {
          data: json,
          total: parseInt(headers.get('x-total-count').split('/').pop(), 10),
        };
      });
    },
 
  getOne: (resource, params) =>
    httpClient(`${apiUrl}/${resource}/${params.id}`).then(({ json }) => ({
      data: json,
    })),
 
  getMany: (resource, params) => {
    const query = {
      filter: JSON.stringify({ id: params.ids }),
    };
    const url = `${apiUrl}/${resource}?${stringify(query)}`;
    return httpClient(url).then(({ json }) => ({ data: json }));
  },
 
  getManyReference: (resource, params) => {
    const { page, perPage } = params.pagination;
    const { field, order } = params.sort;
    const query = {
      sort: JSON.stringify([field, order]),
      range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
      filter: JSON.stringify({ ...params.filter, [params.target]: params.id }),
    };
    const url = `${apiUrl}/${resource}?${stringify(query)}`;
 
    return httpClient(url).then(({ headers, json }) => ({
      data: json,
      total: parseInt(headers.get('x-total-count').split('/').pop(), 10),
    }));
  },
 
  update: (resource, params) =>
    httpClient(`${apiUrl}/${resource}/${params.id}`, {
      method: 'PUT',
      body: JSON.stringify(params.data),
    }).then(({ json }) => ({ data: json })),
 
  updateMany: (resource, params) => {
    const query = {
      filter: JSON.stringify({ id: params.ids }),
    };
    return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
      method: 'PUT',
      body: JSON.stringify(params.data),
    }).then(({ json }) => ({ data: json }));
  },
 
  create: (resource, params) =>
    httpClient(`${apiUrl}/${resource}`, {
      method: 'POST',
      body: JSON.stringify(params.data),
    }).then(({ json }) => ({
      data: { ...params.data, id: json.id },
    })),
 
  delete: (resource, params) =>
    httpClient(`${apiUrl}/${resource}/${params.id}`, {
      method: 'DELETE',
    }).then(({ json }) => ({ data: json })),
 
  deleteMany: (resource, params) => {
    const query = {
      filter: JSON.stringify({ id: params.ids }),
    };
    return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
      method: 'DELETE',
    }).then(({ json }) => ({ data: json }));
  },
};


Ответы (0 шт):