@kne/fastify-account 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -6
- package/libs/controllers/admin-user.js +91 -91
- package/libs/models/user.js +27 -51
- package/libs/services/account.js +1 -1
- package/libs/services/user.js +10 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
|
|
2
|
-
# fastify-
|
|
2
|
+
# fastify-account
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
### 描述
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
用于用户注册登录认证.
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
### 安装
|
|
11
11
|
|
|
12
12
|
```shell
|
|
13
|
-
npm i --save @kne/fastify-
|
|
13
|
+
npm i --save @kne/fastify-account
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
|
|
17
|
+
### 概述
|
|
18
|
+
|
|
19
|
+
#### 功能模块
|
|
20
|
+
- **账号管理**:提供用户注册、登录、密码修改、忘记密码等功能。
|
|
21
|
+
- **用户信息管理**:支持用户信息的获取和更新。
|
|
22
|
+
- **验证码服务**:支持邮箱和短信验证码的发送与验证。
|
|
23
|
+
|
|
24
|
+
#### 技术栈
|
|
25
|
+
- **后端框架**:Fastify
|
|
26
|
+
- **数据库**:Sequelize(支持多种数据库)
|
|
27
|
+
- **认证**:JWT(JSON Web Token)
|
|
28
|
+
- **密码加密**:bcryptjs
|
|
29
|
+
- **工具库**:Lodash、Day.js
|
|
30
|
+
|
|
31
|
+
#### 核心模块
|
|
32
|
+
- **models**:定义数据模型(用户、账号、验证码等)。
|
|
33
|
+
- **services**:业务逻辑层,处理核心功能。
|
|
34
|
+
- **controllers**:API接口层,定义路由和请求处理。
|
|
35
|
+
|
|
36
|
+
#### 项目特点
|
|
37
|
+
- 模块化设计,易于扩展和维护。
|
|
38
|
+
- 支持多租户功能。
|
|
39
|
+
- 提供详细的错误处理和日志记录。
|
|
40
|
+
|
|
16
41
|
### 示例
|
|
17
42
|
|
|
18
43
|
#### 示例代码
|
|
@@ -21,7 +46,24 @@ npm i --save @kne/fastify-user
|
|
|
21
46
|
|
|
22
47
|
### API
|
|
23
48
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
|
49
|
+
#### 账号管理
|
|
50
|
+
|
|
51
|
+
| 路径 | 方法 | 描述 | 参数 |
|
|
52
|
+
|------|------|------|------|
|
|
53
|
+
| `/api/account/v1.0.0/account/sendEmailCode` | POST | 发送登录邮箱验证码 | `email` (string), `type` (number), `options` (object) |
|
|
54
|
+
| `/api/account/v1.0.0/account/sendSMSCode` | POST | 发送登录短信验证码 | `phone` (string), `type` (number), `options` (object) |
|
|
55
|
+
| `/api/account/v1.0.0/account/validateCode` | POST | 验证码验证 | `name` (string), `type` (number), `code` (string) |
|
|
56
|
+
| `/api/account/v1.0.0/account/accountIsExists` | POST | 账号是否已存在 | `phone` (string) 或 `email` (string) |
|
|
57
|
+
| `/api/account/v1.0.0/account/register` | POST | 注册账号 | `phone` (string) 或 `email` (string), `password` (string), `code` (string), 其他可选字段 |
|
|
58
|
+
| `/api/account/v1.0.0/account/login` | POST | 登录 | `type` (string), `email` (string) 或 `phone` (string), `password` (string) |
|
|
59
|
+
| `/api/account/v1.0.0/account/modifyPassword` | POST | 修改密码 | `email` (string) 或 `phone` (string), `newPwd` (string), `oldPwd` (string) |
|
|
60
|
+
| `/api/account/v1.0.0/account/resetPassword` | POST | 重置密码 | `newPwd` (string), `token` (string) |
|
|
61
|
+
| `/api/account/v1.0.0/account/forgetPwd` | POST | 忘记密码 | `email` (string) 或 `phone` (string) |
|
|
62
|
+
| `/api/account/v1.0.0/account/parseResetToken` | POST | 通过token获取name | `token` (string) |
|
|
63
|
+
|
|
64
|
+
#### 用户信息管理
|
|
27
65
|
|
|
66
|
+
| 路径 | 方法 | 描述 | 参数 |
|
|
67
|
+
|------|------|------|------|
|
|
68
|
+
| `/api/account/v1.0.0/user/getUserInfo` | GET | 获取用户信息 | 无 |
|
|
69
|
+
| `/api/account/v1.0.0/user/saveUserInfo` | POST | 更新用户信息 | `avatar` (string), `nickname` (string), `email` (string), `phone` (string), `gender` (string), `birthday` (string), `description` (string) |
|
|
@@ -1,110 +1,110 @@
|
|
|
1
1
|
const fp = require('fastify-plugin');
|
|
2
2
|
|
|
3
3
|
const adminUserController = fp(async (fastify, options) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
}
|
|
4
|
+
const { services, authenticate } = fastify[options.name];
|
|
5
|
+
fastify.get(`${options.prefix}/admin/getUserList`, {
|
|
6
|
+
onRequest: [authenticate.user, authenticate.admin], schema: {
|
|
7
|
+
tags: ['管理后台'], summary: '获取用户列表', query: {
|
|
8
|
+
type: 'object', properties: {
|
|
9
|
+
perPage: { type: 'number' }, currentPage: { type: 'number' }
|
|
12
10
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}, async request => {
|
|
14
|
+
const { filter, perPage, currentPage } = Object.assign({
|
|
15
|
+
perPage: 20, currentPage: 1
|
|
16
|
+
}, request.query);
|
|
17
|
+
return await services.user.getUserList({
|
|
18
|
+
filter, perPage, currentPage
|
|
20
19
|
});
|
|
20
|
+
});
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
22
|
+
fastify.post(`${options.prefix}/admin/resetUserPassword`, {
|
|
23
|
+
onRequest: [authenticate.user, authenticate.admin], schema: {
|
|
24
|
+
tags: ['管理后台'], summary: '重置用户账号密码', body: {
|
|
25
|
+
type: 'object', required: ['userId', 'password'], properties: {
|
|
26
|
+
password: { type: 'string' }, userId: { type: 'string' }
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}, async request => {
|
|
31
|
+
await services.account.resetPassword(request.body);
|
|
32
|
+
return {};
|
|
33
|
+
});
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
fastify.post(`${options.prefix}/admin/addUser`, {
|
|
36
|
+
onRequest: [authenticate.user, authenticate.admin], schema: {
|
|
37
|
+
tags: ['管理后台'], summary: '添加用户', body: {}
|
|
38
|
+
}
|
|
39
|
+
}, async request => {
|
|
40
|
+
const userInfo = request.body;
|
|
41
|
+
await services.user.addUser(Object.assign({}, userInfo, {
|
|
42
|
+
status: 10, password: services.account.md5(options.defaultPassword)
|
|
43
|
+
}));
|
|
44
|
+
return {};
|
|
45
|
+
});
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
}
|
|
47
|
+
fastify.post(`${options.prefix}/admin/saveUser`, {
|
|
48
|
+
onRequest: [authenticate.user, authenticate.admin], schema: {
|
|
49
|
+
tags: ['管理后台'], summary: '修改用户信息', body: {
|
|
50
|
+
type: 'object', required: ['id'], properties: {
|
|
51
|
+
id: { type: 'string' },
|
|
52
|
+
avatar: { type: 'string' },
|
|
53
|
+
nickname: { type: 'string' },
|
|
54
|
+
phone: { type: 'string', default: '' },
|
|
55
|
+
email: { type: 'string', default: '' },
|
|
56
|
+
description: { type: 'string', default: '' }
|
|
59
57
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}, async request => {
|
|
61
|
+
const user = request.body;
|
|
62
|
+
await services.user.saveUser(user);
|
|
63
|
+
return {};
|
|
64
|
+
});
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
}
|
|
66
|
+
fastify.post(`${options.prefix}/admin/setSuperAdmin`, {
|
|
67
|
+
onRequest: [authenticate.user, authenticate.admin], schema: {
|
|
68
|
+
tags: ['管理后台'], summary: '设置用户为超级管理员', body: {
|
|
69
|
+
type: 'object', required: ['status', 'userId'], properties: {
|
|
70
|
+
status: { type: 'boolean', description: 'true:将用户设置为超级管理员,false:取消用户超级管理员' },
|
|
71
|
+
userId: { type: 'string', description: '用户id' }
|
|
74
72
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}, async request => {
|
|
76
|
+
const { status, userId } = request.body;
|
|
77
|
+
await services.user.setSuperAdmin({ userId, status });
|
|
78
|
+
return {};
|
|
79
|
+
});
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
}
|
|
81
|
+
fastify.post(`${options.prefix}/admin/setUserClose`, {
|
|
82
|
+
onRequest: [authenticate.user, authenticate.admin], schema: {
|
|
83
|
+
tags: ['管理后台'], summary: '关闭用户', body: {
|
|
84
|
+
type: 'object', required: ['id'], properties: {
|
|
85
|
+
id: { type: 'string' }
|
|
88
86
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}, async request => {
|
|
90
|
+
const { id } = request.body;
|
|
91
|
+
await services.user.setUserStatus({ userId: id, status: 12 });
|
|
92
|
+
return {};
|
|
93
|
+
});
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
}
|
|
95
|
+
fastify.post(`${options.prefix}/admin/setUserNormal`, {
|
|
96
|
+
onRequest: [authenticate.user, authenticate.admin], schema: {
|
|
97
|
+
tags: ['管理后台'], summary: '设置用户状态为正常', body: {
|
|
98
|
+
type: 'object', required: ['id'], properties: {
|
|
99
|
+
id: { type: 'string' }
|
|
102
100
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}, async request => {
|
|
104
|
+
const { id } = request.body;
|
|
105
|
+
await services.user.setUserStatus({ userId: id, status: 0 });
|
|
106
|
+
return {};
|
|
107
|
+
});
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
module.exports = adminUserController;
|
package/libs/models/user.js
CHANGED
|
@@ -2,58 +2,34 @@ const user = ({ DataTypes, definePrimaryType }) => {
|
|
|
2
2
|
return {
|
|
3
3
|
model: {
|
|
4
4
|
nickname: {
|
|
5
|
-
type: DataTypes.STRING,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
type: DataTypes.STRING,
|
|
10
|
-
comment: '用户邮箱'
|
|
11
|
-
},
|
|
12
|
-
phone: {
|
|
13
|
-
type: DataTypes.STRING,
|
|
14
|
-
comment: '用户手机号'
|
|
15
|
-
},
|
|
16
|
-
userAccountId: definePrimaryType('userAccountId', {
|
|
17
|
-
allowNull: false,
|
|
18
|
-
comment: '当前账号id'
|
|
19
|
-
}),
|
|
20
|
-
status: {
|
|
21
|
-
type: DataTypes.INTEGER,
|
|
22
|
-
defaultValue: 0,
|
|
23
|
-
comment: '0:正常,10:初始化未激活,需要用户设置密码后使用,11:已禁用,12:已关闭'
|
|
24
|
-
},
|
|
25
|
-
avatar: {
|
|
26
|
-
type: DataTypes.STRING,
|
|
27
|
-
comment: '头像fileId'
|
|
28
|
-
},
|
|
29
|
-
gender: {
|
|
30
|
-
type: DataTypes.STRING,
|
|
31
|
-
comment: 'F:女,M:男'
|
|
32
|
-
},
|
|
33
|
-
birthday: {
|
|
34
|
-
type: DataTypes.DATE,
|
|
35
|
-
comment: '出生日期'
|
|
36
|
-
},
|
|
37
|
-
description: {
|
|
38
|
-
type: DataTypes.TEXT,
|
|
39
|
-
comment: '个人描述'
|
|
40
|
-
},
|
|
41
|
-
isSuperAdmin: {
|
|
42
|
-
type: DataTypes.BOOLEAN,
|
|
43
|
-
comment: '是否是平台超级管理员'
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
options: {
|
|
47
|
-
indexes: [
|
|
48
|
-
{
|
|
49
|
-
unique: true,
|
|
50
|
-
fields: ['email', 'deleted_at']
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
unique: true,
|
|
54
|
-
fields: ['phone', 'deleted_at']
|
|
5
|
+
type: DataTypes.STRING, comment: '用户昵称'
|
|
6
|
+
}, email: {
|
|
7
|
+
type: DataTypes.STRING, comment: '用户邮箱', set(value) {
|
|
8
|
+
this.setDataValue('email', value ? value.toLowerCase() : value);
|
|
55
9
|
}
|
|
56
|
-
|
|
10
|
+
}, phone: {
|
|
11
|
+
type: DataTypes.STRING, comment: '用户手机号'
|
|
12
|
+
}, userAccountId: definePrimaryType('userAccountId', {
|
|
13
|
+
allowNull: false, comment: '当前账号id'
|
|
14
|
+
}), status: {
|
|
15
|
+
type: DataTypes.INTEGER, defaultValue: 0, comment: '0:正常,10:初始化未激活,需要用户设置密码后使用,11:已禁用,12:已关闭'
|
|
16
|
+
}, avatar: {
|
|
17
|
+
type: DataTypes.STRING, comment: '头像fileId'
|
|
18
|
+
}, gender: {
|
|
19
|
+
type: DataTypes.STRING, comment: 'F:女,M:男'
|
|
20
|
+
}, birthday: {
|
|
21
|
+
type: DataTypes.DATE, comment: '出生日期'
|
|
22
|
+
}, description: {
|
|
23
|
+
type: DataTypes.TEXT, comment: '个人描述'
|
|
24
|
+
}, isSuperAdmin: {
|
|
25
|
+
type: DataTypes.BOOLEAN, comment: '是否是平台超级管理员'
|
|
26
|
+
}
|
|
27
|
+
}, options: {
|
|
28
|
+
indexes: [{
|
|
29
|
+
unique: true, fields: ['email', 'deleted_at']
|
|
30
|
+
}, {
|
|
31
|
+
unique: true, fields: ['phone', 'deleted_at']
|
|
32
|
+
}]
|
|
57
33
|
}
|
|
58
34
|
};
|
|
59
35
|
};
|
package/libs/services/account.js
CHANGED
package/libs/services/user.js
CHANGED
|
@@ -36,19 +36,17 @@ const userService = fp(async (fastify, options) => {
|
|
|
36
36
|
const accountIsExists = async ({ email, phone }, currentUser) => {
|
|
37
37
|
const query = [];
|
|
38
38
|
if (email && email !== get(currentUser, 'email')) {
|
|
39
|
-
query.push({ email });
|
|
39
|
+
query.push({ email: email.toLowerCase() });
|
|
40
40
|
}
|
|
41
41
|
if (phone && phone !== get(currentUser, 'phone')) {
|
|
42
42
|
query.push({ phone });
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
return (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})) > 0
|
|
51
|
-
);
|
|
45
|
+
return ((await models.user.count({
|
|
46
|
+
where: {
|
|
47
|
+
[Op.or]: query
|
|
48
|
+
}
|
|
49
|
+
})) > 0);
|
|
52
50
|
};
|
|
53
51
|
|
|
54
52
|
const addUser = async ({ avatar, nickname, gender, birthday, description, phone, email, password, status }) => {
|
|
@@ -60,15 +58,7 @@ const userService = fp(async (fastify, options) => {
|
|
|
60
58
|
}
|
|
61
59
|
const account = await models.userAccount.create(await services.account.passwordEncryption(password));
|
|
62
60
|
const user = await models.user.create({
|
|
63
|
-
avatar,
|
|
64
|
-
nickname,
|
|
65
|
-
gender,
|
|
66
|
-
birthday,
|
|
67
|
-
description,
|
|
68
|
-
phone,
|
|
69
|
-
email,
|
|
70
|
-
status,
|
|
71
|
-
userAccountId: account.id
|
|
61
|
+
avatar, nickname, gender, birthday, description, phone, email, status, userAccountId: account.id
|
|
72
62
|
});
|
|
73
63
|
await account.update({ belongToUserId: user.id });
|
|
74
64
|
|
|
@@ -109,13 +99,12 @@ const userService = fp(async (fastify, options) => {
|
|
|
109
99
|
return {
|
|
110
100
|
pageData: rows.map(item => {
|
|
111
101
|
return Object.assign({}, item.get({ pain: true }));
|
|
112
|
-
}),
|
|
113
|
-
totalCount: count
|
|
102
|
+
}), totalCount: count
|
|
114
103
|
};
|
|
115
104
|
};
|
|
116
105
|
|
|
117
106
|
const saveUser = async ({ id, ...otherInfo }) => {
|
|
118
|
-
const user = await getUserInstance({id});
|
|
107
|
+
const user = await getUserInstance({ id });
|
|
119
108
|
|
|
120
109
|
if ((await accountIsExists({ phone: otherInfo.phone, email: otherInfo.email }, user)) > 0) {
|
|
121
110
|
throw new Error('手机号或者邮箱都不能重复');
|
|
@@ -143,14 +132,7 @@ const userService = fp(async (fastify, options) => {
|
|
|
143
132
|
};
|
|
144
133
|
|
|
145
134
|
services.user = {
|
|
146
|
-
getUserInstance,
|
|
147
|
-
getUser,
|
|
148
|
-
addUser,
|
|
149
|
-
saveUser,
|
|
150
|
-
accountIsExists,
|
|
151
|
-
getUserList,
|
|
152
|
-
setSuperAdmin,
|
|
153
|
-
setUserStatus
|
|
135
|
+
getUserInstance, getUser, addUser, saveUser, accountIsExists, getUserList, setSuperAdmin, setUserStatus
|
|
154
136
|
};
|
|
155
137
|
});
|
|
156
138
|
|