When you log in to the admin account, a redirect occurs to the react-admin page, and then to /users (http://localhost:3000/admin#/users). There is nothing on this page. I don’t understand what is conflicting and why this is happening, there are no errors. Help me please. (ip changed to ...)
Back-end part
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 part
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 }));
},
};
In your userRoutes.js file you have this setup:
In your main.js file you have this setup:
The endpoint would actually be
/api/users/usersin this arrangement. In yourAdminInt.jsxfile you are importing the<UserList/>which could be hitting some apis in yourdataProvider.jswhere you have the setupso you could be hitting
/api/users/usersinstead of/api/userswhich might be causing the issue.Also check the network tab in chrome to see if your api requests are successful.