Skip to content

Commit 1613ea6

Browse files
committed
Merge branch 'feature/user-role' into dev
2 parents 49f085f + 6eb2155 commit 1613ea6

File tree

8 files changed

+119
-0
lines changed

8 files changed

+119
-0
lines changed

src/common/api/user.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export default (apiEngine) => ({
2+
list: ({ page }) => apiEngine.get('/api/users', { params: { page } }),
23
register: (user) => apiEngine.post('/api/users', { data: user }),
34
login: (user) => apiEngine.post('/api/users/login', { data: user }),
45
logout: () => apiEngine.get('/api/users/logout'),

src/common/constants/Roles.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default {
2+
// end user
3+
USER: 'USER',
4+
// system administrator
5+
ADMIN: 'ADMIN',
6+
// root
7+
ROOT: 'ROOT',
8+
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// ref:
2+
// - <https://github.com/ReactTraining/react-router/issues/3103>
3+
// - <https://github.com/baronswindle/react-router-compose-hooks/blob/master/index.js>
4+
5+
export default {
6+
parallel(...hooks) {
7+
let callbacksRequired = hooks.reduce((totalCallbacks, hook) => {
8+
if (hook.length >= 3) {
9+
totalCallbacks++;
10+
}
11+
return totalCallbacks;
12+
}, 0);
13+
14+
return function onEnter(nextState, replace, executeTransition) {
15+
let callbacksInvoked = 0;
16+
hooks.forEach((hook) => {
17+
hook.call(this, nextState, replace, () => {
18+
if (++callbacksInvoked === callbacksRequired) {
19+
executeTransition();
20+
}
21+
});
22+
});
23+
if (!callbacksRequired) {
24+
executeTransition();
25+
}
26+
};
27+
},
28+
29+
series(...hooks) {
30+
return function onEnter(nextState, replace, executeTransition) {
31+
(function executeHooksSynchronously(remainingHooks) {
32+
if (!remainingHooks.length) {
33+
return executeTransition();
34+
}
35+
let nextHook = remainingHooks[0];
36+
if (nextHook.length >= 3) {
37+
nextHook.call(this, nextState, replace, () => {
38+
executeHooksSynchronously(remainingHooks.slice(1));
39+
});
40+
} else {
41+
nextHook.call(this, nextState, replace);
42+
executeHooksSynchronously(remainingHooks.slice(1));
43+
}
44+
})(hooks);
45+
};
46+
},
47+
};

src/common/utils/roleRequired.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export default (store) => (requiredRoles) => (nextState, replace) => {
2+
let { user } = store.getState().cookies;
3+
user = (user && JSON.parse(user)) || {};
4+
5+
if (!((
6+
requiredRoles instanceof Array &&
7+
requiredRoles.indexOf(user.role) >= 0
8+
) || (
9+
user.role === requiredRoles
10+
))) {
11+
replace({
12+
pathname: '/',
13+
});
14+
}
15+
};

src/server/controllers/user.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@ import User from '../models/User';
33
import filterAttribute from '../utils/filterAttribute';
44

55
export default {
6+
list(req, res) {
7+
User.paginate({ page: req.query.page }, handleDbError(res)((page) => {
8+
User
9+
.find({})
10+
.sort({ createdAt: 'desc' })
11+
.limit(page.limit)
12+
.skip(page.skip)
13+
.exec(handleDbError(res)((users) => {
14+
res.json({
15+
users: users,
16+
page: page,
17+
});
18+
}));
19+
}));
20+
},
21+
622
create(req, res) {
723
const user = User({
824
name: req.body.name,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Errors from '../../common/constants/Errors';
2+
3+
const roleRequired = (requiredRoles) => (req, res, next) => {
4+
if ((
5+
requiredRoles instanceof Array &&
6+
requiredRoles.indexOf(req.user.role) >= 0
7+
) || (
8+
req.user.role === requiredRoles
9+
)) {
10+
next();
11+
} else {
12+
return res.errors([Errors.PERMISSION_DENIED]);
13+
}
14+
};
15+
16+
export default roleRequired;

src/server/models/User.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import crypto from 'crypto';
22
import mongoose from 'mongoose';
33
import jwt from 'jsonwebtoken';
44
import configs from '../../../configs/project/server';
5+
import Roles from '../../common/constants/Roles';
6+
import paginatePlugin from './plugins/paginate';
57

68
const hashPassword = (rawPassword = '') => {
79
let recursiveLevel = 5;
@@ -32,6 +34,11 @@ let UserSchema = new mongoose.Schema({
3234
required: true,
3335
set: hashPassword,
3436
},
37+
role: {
38+
type: String,
39+
enum: Object.keys(Roles).map(r => Roles[r]),
40+
default: Roles.USER,
41+
},
3542
avatarURL: String,
3643
}, {
3744
versionKey: false,
@@ -41,6 +48,8 @@ let UserSchema = new mongoose.Schema({
4148
},
4249
});
4350

51+
UserSchema.plugin(paginatePlugin);
52+
4453
UserSchema.path('email.value').validate(function(value, cb) {
4554
User.findOne({ 'email.value': value }, (err, user) => {
4655
cb(!err && !user);

src/server/routes/api.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import configs from '../../../configs/project/server';
2+
import Roles from '../../common/constants/Roles';
23
import bodyParser from '../middlewares/bodyParser';
34
import authRequired from '../middlewares/authRequired';
5+
import roleRequired from '../middlewares/roleRequired';
46
import fileUpload from '../middlewares/fileUpload';
57
import userController from '../controllers/user';
68
import formValidationController from '../controllers/formValidation';
@@ -9,6 +11,11 @@ import todoController from '../controllers/todo';
911

1012
export default ({ app }) => {
1113
// user
14+
app.get('/api/users',
15+
authRequired,
16+
roleRequired([Roles.ADMIN]),
17+
userController.list
18+
);
1219
app.post('/api/users', bodyParser.json, userController.create);
1320
app.post('/api/users/login', bodyParser.json, userController.login);
1421
app.get('/api/users/logout', userController.logout);

0 commit comments

Comments
 (0)