Skip to content

Commit 01a8018

Browse files
committed
feat: add user role management and refactor migrations
- Add API endpoint to update user roles (/update-role/:id) - Add roleId field to User schema with Role reference - Refactor migrations to use constants for role names - Improve demo user creation with proper password hashing - Remove redundant migration 004 - Update resource logging to use identifiers instead of IDs
1 parent 0389f5d commit 01a8018

File tree

9 files changed

+74
-74
lines changed

9 files changed

+74
-74
lines changed

src/domains/user/api.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ const {
1212
followUser,
1313
deactivateUser,
1414
activateUser,
15+
updateUserRole,
1516
} = require('./service');
1617

1718
const {
1819
createSchema,
1920
updateSchema,
2021
idSchema,
2122
searchSchema,
23+
updateUserRoleSchema,
2224
} = require('./request');
2325
const { validateRequest } = require('../../middlewares/request-validate');
2426
const { logRequest } = require('../../middlewares/log');
@@ -146,6 +148,22 @@ const routes = () => {
146148
}
147149
);
148150

151+
// update user's role only
152+
router.put(
153+
'/update-role/:id',
154+
logRequest({}),
155+
isAuthorized,
156+
validateRequest({ schema: updateUserRoleSchema, isParam: false }),
157+
async (req, res, next) => {
158+
try {
159+
const updatedUser = await updateUserRole(req.params.id, req.body);
160+
res.status(200).json(updatedUser);
161+
} catch (error) {
162+
next(error);
163+
}
164+
}
165+
);
166+
149167
return router;
150168
};
151169

src/domains/user/request.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,8 @@ const searchSchema = Joi.object({
2929
order: Joi.string().valid('asc', 'desc'),
3030
});
3131

32-
module.exports = { createSchema, updateSchema, idSchema, searchSchema };
32+
const updateUserRoleSchema = Joi.object().keys({
33+
roleId: Joi.string().required(),
34+
});
35+
36+
module.exports = { createSchema, updateSchema, idSchema, searchSchema, updateUserRoleSchema };

src/domains/user/schema.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ const schema = new mongoose.Schema({
147147
required: true,
148148
default: 'visitor',
149149
},
150+
roleId: {
151+
type: mongoose.Schema.Types.ObjectId,
152+
ref: 'Role',
153+
},
150154
});
151155

152156
schema.add(baseSchema);

src/domains/user/service.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const logger = require('../../libraries/log/logger');
22
const Model = require('./schema');
3+
const Role = require('../role/schema');
34
const { AppError } = require('../../libraries/error-handling/AppError');
45
const crypto = require('crypto');
56

@@ -134,6 +135,21 @@ const activateUser = async (id) => {
134135
}
135136
};
136137

138+
const updateUserRole = async (id, payload) => {
139+
try {
140+
const item = await Model.findById(id);
141+
const role = await Role.findById(payload.roleId);
142+
item.role = role.name;
143+
item.roleId = role._id;
144+
await item.save();
145+
logger.info(`updateUserRole(): ${model} updated`, { id, role: item.role });
146+
return item;
147+
} catch (error) {
148+
logger.error(`updateUserRole(): Failed to update ${model}`, error);
149+
throw new AppError(`Failed to update ${model}`, error.message);
150+
}
151+
}
152+
137153
const getByGitHubId = async (githubId) => {
138154
return await Model.findOne({ 'github.id': githubId });
139155
};
@@ -298,4 +314,5 @@ module.exports = {
298314
getByGoogleId,
299315
findByVerificationToken,
300316
refreshVerificationToken,
317+
updateUserRole,
301318
};

src/migrations/001-add-initial-resources.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ async function insertResource(resource) {
160160
return;
161161
}
162162
const result = await Resource.create(resource);
163-
console.log(`Inserted resource: ${resource._id}`);
163+
console.log(`Inserted resource: ${resource.identifier}`);
164164
return result;
165165
} catch (error) {
166166
console.error(`Error inserting resource ${resource.id}:`, error);

src/migrations/002-add-roles-permissions.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
const Model = require('../domains/role/schema');
2+
const { ROLES } = require('./constants');
23

34
const data = {
45
roles: [
56
{
6-
name: 'Super admin',
7+
name: ROLES.SUPER_ADMIN,
78
identifier: 'superadmin',
89
isSystemManaged: true,
910
permissions: {
@@ -30,7 +31,7 @@ const data = {
3031
},
3132
},
3233
{
33-
name: 'Admin',
34+
name: ROLES.ADMIN,
3435
identifier: 'admin',
3536
isSystemManaged: true,
3637
permissions: {
@@ -39,11 +40,13 @@ const data = {
3940
'/api/v1/users/count',
4041
'/api/v1/users/detail/:id',
4142
],
42-
client: [],
43+
client: [
44+
'sidebar-users',
45+
],
4346
},
4447
},
4548
{
46-
name: 'Visitor',
49+
name: ROLES.VISITOR,
4750
identifier: 'visitor',
4851
isSystemManaged: true,
4952
permissions: {
@@ -76,10 +79,10 @@ async function insert(item) {
7679
return;
7780
}
7881
const result = await Model.create(item);
79-
console.log(`Inserted role: ${item._id}`);
82+
console.log(`Inserted role: ${item.identifier}`);
8083
return result;
8184
} catch (error) {
82-
console.error(`Error inserting role ${item._id}:`, error);
85+
console.error(`Error inserting role ${item.identifier}:`, error);
8386
throw error;
8487
}
8588
}

src/migrations/003-add-users.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
const User = require('../domains/user/schema');
2+
const Role = require('../domains/role/schema');
23
const bcrypt = require('bcrypt');
34
const config = require('../configs');
5+
const { ROLES } = require('./constants');
46

57
async function insert(user) {
68
try {
@@ -10,8 +12,10 @@ async function insert(user) {
1012
console.log(`${user.displayName} already exists: ${user.email}`);
1113
return;
1214
}
15+
16+
user.roleId = await Role.findOne({name: user.role});
1317
const result = await User.create(user);
14-
console.log(`Inserted ${user.displayName}: ${result._id}`);
18+
console.log(`Inserted ${user.displayName}`);
1519
return result;
1620
} catch (error) {
1721
console.error(`Error inserting ${user.displayName}:`, error);
@@ -54,12 +58,12 @@ async function runMigration() {
5458
authType: 'local',
5559
local: {
5660
username: 'admin@example.com',
57-
password: 'password', // sample password for demo purposes
61+
password: await bcrypt.hash('password', salt),
5862
},
59-
isDemo: false,
63+
isDemo: true,
6064
isVerified: true,
6165
isAdmin: true,
62-
role: 'admin'
66+
role: ROLES.ADMIN
6367
};
6468

6569
await insert(adminUser);
@@ -70,12 +74,12 @@ async function runMigration() {
7074
authType: 'local',
7175
local: {
7276
username: 'visitor@example.com',
73-
password: 'password', // sample password for demo purposes
77+
password: await bcrypt.hash('password', salt),
7478
},
75-
isDemo: false,
79+
isDemo: true,
7680
isVerified: true,
7781
isAdmin: false,
78-
role: 'visitor'
82+
role: ROLES.VISITOR
7983
};
8084

8185
await insert(visitorUser);

src/migrations/004-update-demo-users.js

Lines changed: 0 additions & 59 deletions
This file was deleted.

src/migrations/constants.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const ROLES = {
2+
SUPER_ADMIN: 'Super admin',
3+
ADMIN: 'Admin',
4+
USER: 'User',
5+
DEMO: 'Demo',
6+
VISITOR: 'Visitor',
7+
}
8+
9+
module.exports = { ROLES };

0 commit comments

Comments
 (0)