@kne/fastify-account 1.0.0-alpha.9 → 2.0.0-alpha.1

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.
Files changed (44) hide show
  1. package/README.md +6 -2046
  2. package/index.js +34 -21
  3. package/libs/controllers/account.js +246 -173
  4. package/libs/controllers/admin-user.js +110 -0
  5. package/libs/controllers/admin.js +55 -146
  6. package/libs/controllers/user.js +7 -24
  7. package/libs/models/example._js +13 -0
  8. package/libs/models/user-account.js +13 -32
  9. package/libs/models/user.js +37 -36
  10. package/libs/models/verification-code.js +16 -21
  11. package/libs/services/account.js +124 -149
  12. package/libs/services/admin.js +21 -73
  13. package/libs/services/user.js +52 -58
  14. package/package.json +24 -28
  15. package/libs/controllers/adminPermission.js +0 -237
  16. package/libs/controllers/adminRole.js +0 -146
  17. package/libs/controllers/adminTenant.js +0 -464
  18. package/libs/controllers/tenant.js +0 -34
  19. package/libs/models/admin-role.js +0 -15
  20. package/libs/models/application.js +0 -42
  21. package/libs/models/login-log.js +0 -11
  22. package/libs/models/permission.js +0 -51
  23. package/libs/models/tenant-application.js +0 -26
  24. package/libs/models/tenant-org.js +0 -26
  25. package/libs/models/tenant-permission.js +0 -26
  26. package/libs/models/tenant-role-application.js +0 -37
  27. package/libs/models/tenant-role-permission.js +0 -34
  28. package/libs/models/tenant-role.js +0 -23
  29. package/libs/models/tenant-share-group-permission.js +0 -18
  30. package/libs/models/tenant-share-group.js +0 -18
  31. package/libs/models/tenant-source-user-share-group.js +0 -18
  32. package/libs/models/tenant-token.js +0 -30
  33. package/libs/models/tenant-user-org.js +0 -23
  34. package/libs/models/tenant-user-role.js +0 -23
  35. package/libs/models/tenant-user-share-group.js +0 -18
  36. package/libs/models/tenant-user.js +0 -75
  37. package/libs/models/tenant.js +0 -46
  38. package/libs/services/application.js +0 -151
  39. package/libs/services/permission.js +0 -367
  40. package/libs/services/tenant-invite.js +0 -62
  41. package/libs/services/tenant-org.js +0 -97
  42. package/libs/services/tenant-role.js +0 -108
  43. package/libs/services/tenant-user.js +0 -555
  44. package/libs/services/tenant.js +0 -132
package/index.js CHANGED
@@ -1,29 +1,39 @@
1
1
  const fp = require('fastify-plugin');
2
- const packageJson = require('./package.json');
3
- const path = require('path');
4
- const merge = require('lodash/merge');
5
- const { Unauthorized } = require('http-errors');
2
+ const path = require('node:path');
3
+ const {merge } = require('lodash');
4
+ const jwt = require('@fastify/jwt');
5
+ const namespace = require('@kne/fastify-namespace');
6
+ const httpErrors = require('http-errors');
6
7
 
7
- module.exports = fp(
8
+ const { Unauthorized } = httpErrors;
9
+
10
+ const user = fp(
8
11
  async function (fastify, options) {
9
- await fastify.sequelize.addModels(path.resolve(__dirname, './models'));
10
12
  options = merge(
11
13
  {
12
- prefix: `/api/v${packageJson.version.split('.')[0]}/account`, //如果为true,发送邮件和短信将不调用,验证码随response返回
14
+ name: 'account',
15
+ version: '1.0.0',
13
16
  dbTableNamePrefix: 't_account_',
14
17
  isTest: false,
15
18
  jwt: {
16
- secret: 'super-secret'
19
+ secret: 'super-secret',
20
+ expires: null,
21
+ verify: {
22
+ extractToken: request => request.headers['x-user-token']
23
+ }
17
24
  },
18
- defaultPassword: 'Aa000000!'
25
+ defaultPassword: 'Aa000000!',
26
+ sendMessage: async () => {}
19
27
  },
20
28
  options
21
29
  );
22
- fastify.register(require('fastify-ip'));
23
- fastify.register(require('@fastify/jwt'), options.jwt);
24
- fastify.register(require('@kne/fastify-namespace'), {
30
+ if (!options.prefix) {
31
+ options.prefix = `/api/${options.name}/v${options.version}`;
32
+ }
33
+ fastify.register(jwt, options.jwt);
34
+ fastify.register(namespace, {
25
35
  options,
26
- name: 'account',
36
+ name: options.name,
27
37
  modules: [
28
38
  [
29
39
  'models',
@@ -37,17 +47,19 @@ module.exports = fp(
37
47
  'authenticate',
38
48
  {
39
49
  user: async request => {
50
+ const { services } = fastify[options.name];
40
51
  const info = await request.jwtVerify();
41
52
  //这里判断失效时间
42
- //info.iat
53
+ if (options.jwt.expires && Date.now() - info.iat * 1000 > options.jwt.expires) {
54
+ throw Unauthorized('身份认证超时');
55
+ }
43
56
  request.authenticatePayload = info.payload;
44
- request.userInfo = await fastify.account.services.user.getUser(request.authenticatePayload);
45
- },
46
- tenant: async request => {
47
- request.tenantInfo = await fastify.account.services.tenantUser.getTenantUserByUserId(request.userInfo);
57
+ request.userInfo = await services.user.getUser(request.authenticatePayload);
58
+ request.appName = request.headers['x-app-name'];
48
59
  },
49
60
  admin: async request => {
50
- if (!(await fastify.account.services.admin.checkIsSuperAdmin(request.userInfo))) {
61
+ const { services } = fastify[options.name];
62
+ if (!(await services.admin.checkIsSuperAdmin(request.userInfo))) {
51
63
  throw Unauthorized('不能执行该操作,需要超级管理员权限');
52
64
  }
53
65
  }
@@ -57,7 +69,8 @@ module.exports = fp(
57
69
  });
58
70
  },
59
71
  {
60
- name: 'fastify-account',
61
- dependencies: ['fastify-sequelize']
72
+ name: 'fastify-user'
62
73
  }
63
74
  );
75
+
76
+ module.exports = user;
@@ -1,190 +1,263 @@
1
1
  const fp = require('fastify-plugin');
2
- module.exports = fp(async (fastify, options) => {
3
- const { services } = fastify.account;
4
- fastify.post(
5
- `${options.prefix}/sendEmailCode`,
6
- {
7
- schema: {
8
- body: {
9
- type: 'object',
10
- required: ['email'],
11
- properties: {
12
- email: { type: 'string', description: '邮箱' }
13
- }
14
- },
15
- response: {
16
- 200: {
17
- content: {
18
- 'application/json': {
19
- schema: {
20
- type: 'object',
21
- properties: {
22
- code: { type: 'number' },
23
- data: {
24
- type: 'object',
25
- properties: {
26
- code: { type: 'string', description: '验证码' }
27
- }
28
- },
29
- msg: { type: 'string' }
30
- }
2
+
3
+ const accountController = fp(async (fastify, options) => {
4
+ const {services} = fastify[options.name];
5
+ fastify.post(`${options.prefix}/account/sendEmailCode`, {
6
+ schema: {
7
+ tags: ['账号'], summary: '发送登录邮箱验证码', body: {
8
+ type: 'object', required: ['email'], properties: {
9
+ email: {type: 'string', description: '邮箱'}
10
+ }
11
+ }, response: {
12
+ 200: {
13
+ content: {
14
+ 'application/json': {
15
+ schema: {
16
+ type: 'object', properties: {
17
+ code: {type: 'string', description: '验证码'}
18
+ }
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }, async request => {
26
+ const {email} = request.body;
27
+ const code = await services.account.sendVerificationCode({name: email, type: 0});
28
+ return options.isTest ? {code} : {};
29
+ });
30
+
31
+ fastify.post(`${options.prefix}/account/sendSMSCode`, {
32
+ schema: {
33
+ tags: ['账号'], summary: '发送登录短信验证码', body: {
34
+ type: 'object', required: ['phone'], properties: {
35
+ phone: {type: 'string', description: '电话'}
36
+ }
37
+ }, response: {
38
+ 200: {
39
+ content: {
40
+ 'application/json': {
41
+ schema: {
42
+ type: 'object', properties: {
43
+ code: {type: 'string', description: '验证码'}
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }, async request => {
52
+ const {phone} = request.body;
53
+ const code = await services.account.sendVerificationCode({name: phone, type: 0});
54
+ return options.isTest ? {code} : {};
55
+ });
56
+
57
+ fastify.post(`${options.prefix}/account/validateCode`, {
58
+ schema: {
59
+ tags: ['账号'], summary: '验证码验证', body: {
60
+ type: 'object', required: ['name', 'type', 'code'], properties: {
61
+ name: {type: 'string', description: '被验证的账号,手机或邮箱'},
62
+ type: {type: 'number', description: '0:注册,2:登录,4:验证租户管理员,5:忘记密码'},
63
+ code: {type: 'string', description: '接受到的验证码'}
64
+ }
65
+ }, response: {
66
+ 200: {
67
+ content: {
68
+ 'application/json': {
69
+ schema: {}
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }, async request => {
76
+ const {name, type, code} = request.body;
77
+ const isPass = await services.account.verificationCodeValidate({
78
+ name, type, code
79
+ });
80
+ if (!isPass) {
81
+ throw new Error('验证码错误');
82
+ }
83
+ return {};
84
+ });
85
+
86
+ fastify.post(`${options.prefix}/account/accountIsExists`, {
87
+ schema: {
88
+ tags: ['账号'], summary: '账号是否已存在', body: {
89
+ oneOf: [{
90
+ type: 'object', required: ['phone'], properties: {
91
+ phone: {type: 'string', description: '电话'}
92
+ }
93
+ }, {
94
+ type: 'object', required: ['email'], properties: {
95
+ email: {type: 'string', description: '邮箱'}
96
+ }
97
+ }]
98
+ }, response: {
99
+ 200: {
100
+ content: {
101
+ 'application/json': {
102
+ schema: {
103
+ type: 'object', properties: {
104
+ isExists: {type: 'boolean', description: 'true已存在,false不存在'}
105
+ }
106
+ }
107
+ }
108
+ }
31
109
  }
32
- }
33
110
  }
34
- }
35
111
  }
36
- }
37
- },
38
- async request => {
39
- const { email } = request.body;
40
- const code = await services.account.sendEmailCode({ email });
41
- return options.isTest ? { code } : {};
42
- }
43
- );
112
+ }, async request => {
113
+ const {phone, email} = request.body;
114
+ return {isExists: await services.user.accountIsExists({phone, email})};
115
+ });
44
116
 
45
- fastify.post(
46
- `${options.prefix}/sendSMSCode`,
47
- {
48
- schema: {
49
- body: {
50
- type: 'object',
51
- required: ['phone'],
52
- properties: {
53
- phone: { type: 'string', description: '电话' }
54
- }
117
+ fastify.post(`${options.prefix}/account/register`, {
118
+ schema: {
119
+ tags: ['账号'], summary: '注册账号', body: {
120
+ oneOf: [{
121
+ type: 'object', required: ['phone', 'password', 'code'], properties: {
122
+ avatar: {type: 'string', description: '头像图片id'},
123
+ phone: {type: 'string', description: '电话'},
124
+ code: {type: 'string', description: '验证码'},
125
+ password: {type: 'string', description: '密码(需要md5加密)'},
126
+ invitationCode: {type: 'string', description: '邀请码,用来默认加入租户'},
127
+ nickname: {type: 'string', description: '昵称'},
128
+ gender: {type: 'string', description: '性别'},
129
+ birthday: {type: 'string', format: 'date', description: '出生日期'},
130
+ description: {type: 'string', description: '个人简介'}
131
+ }
132
+ }, {
133
+ type: 'object', required: ['email', 'password', 'code'], properties: {
134
+ avatar: {type: 'string', description: '头像图片id'},
135
+ email: {type: 'string', description: '邮箱'},
136
+ code: {type: 'string', description: '验证码'},
137
+ password: {type: 'string', description: '密码(需要md5加密)'},
138
+ invitationCode: {type: 'string', description: '邀请码,用来默认加入租户'},
139
+ nickname: {type: 'string', description: '昵称'},
140
+ gender: {type: 'string', description: '性别'},
141
+ birthday: {type: 'string', format: 'date', description: '出生日期'},
142
+ description: {type: 'string', description: '个人简介'}
143
+ }
144
+ }]
145
+ }, response: {
146
+ 200: {
147
+ content: {
148
+ 'application/json': {
149
+ schema: {}
150
+ }
151
+ }
152
+ }
153
+ }
55
154
  }
56
- }
57
- },
58
- async request => {
59
- const { phone } = request.body;
60
- const code = await services.account.sendSMSCode({ phone });
61
- return options.isTest ? { code } : {};
62
- }
63
- );
155
+ }, async request => {
156
+ const account = request.body;
157
+ return await services.account.register(account);
158
+ });
64
159
 
65
- fastify.post(
66
- `${options.prefix}/validateCode`,
67
- {
68
- schema: {
69
- body: {
70
- type: 'object',
71
- required: ['name', 'type', 'code'],
72
- properties: {
73
- name: { type: 'string', description: '被验证的账号,手机或邮箱' },
74
- type: { type: 'number', description: '0:手机注册,1:邮箱注册,2:手机登录,3:邮箱登录,4:验证租户管理员' },
75
- code: { type: 'string', description: '接受到的验证码' }
76
- }
160
+ fastify.post(`${options.prefix}/account/login`, {
161
+ schema: {
162
+ tags: ['账号'], summary: '登录', body: {
163
+ type: 'object', required: ['password'], properties: {
164
+ type: {type: 'string', default: 'email', description: '登录类型'},
165
+ email: {type: 'string', description: '邮箱'},
166
+ phone: {type: 'string', description: '手机号'},
167
+ password: {type: 'string', description: '密码'}
168
+ }
169
+ }, response: {
170
+ 200: {
171
+ content: {
172
+ 'application/json': {
173
+ schema: {
174
+ type: 'object', properties: {
175
+ token: {type: 'string', description: '用户token'},
176
+ currentTenantId: {type: 'string', description: '当前租户id'}
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
77
183
  }
78
- }
79
- },
80
- async request => {
81
- const { name, type, code } = request.body;
82
- const isPass = await services.account.verificationCodeValidate({
83
- name,
84
- type,
85
- code
86
- });
87
- if (!isPass) {
88
- throw new Error('验证码错误');
89
- }
90
- return {};
91
- }
92
- );
184
+ }, async request => {
185
+ const appName = request.headers['x-app-name'];
186
+ const {token, user} = await services.account.login(Object.assign({}, request.body, {appName}));
187
+ return {token, currentTenantId: user.currentTenantId};
188
+ });
93
189
 
94
- fastify.post(
95
- `${options.prefix}/accountIsExists`,
96
- {
97
- schema: {
98
- body: {
99
- oneOf: [
100
- {
101
- type: 'object',
102
- required: ['phone'],
103
- properties: {
104
- phone: { type: 'string' }
105
- }
106
- },
107
- {
108
- type: 'object',
109
- required: ['email'],
110
- properties: {
111
- email: { type: 'string' }
112
- }
190
+ fastify.post(`${options.prefix}/account/modifyPassword`, {
191
+ schema: {
192
+ tags: ['账号'], summary: '新用户重置新密码', body: {
193
+ oneOf: [{
194
+ type: 'object', required: ['email', 'newPwd', 'oldPwd'], properties: {
195
+ email: {type: 'string', description: '邮箱'},
196
+ newPwd: {type: 'string', description: '新密码'},
197
+ oldPwd: {type: 'string', description: '原密码'}
198
+ }
199
+ }, {
200
+ type: 'object', required: ['phone', 'newPwd', 'oldPwd'], properties: {
201
+ phone: {type: 'string', description: '手机号'},
202
+ newPwd: {type: 'string', description: '新密码'},
203
+ oldPwd: {type: 'string', description: '原密码'}
204
+ }
205
+ }]
113
206
  }
114
- ]
115
207
  }
116
- }
117
- },
118
- async request => {
119
- const { phone, email } = request.body;
120
- return { isExists: await services.user.accountIsExists({ phone, email }) };
121
- }
122
- );
208
+ }, async request => {
209
+ await services.account.modifyPassword(request.body);
210
+ return {};
211
+ });
123
212
 
124
- fastify.post(
125
- `${options.prefix}/register`,
126
- {
127
- schema: {
128
- body: {
129
- oneOf: [
130
- {
131
- type: 'object',
132
- required: ['phone', 'password', 'code'],
133
- properties: {
134
- avatar: { type: 'string' },
135
- phone: { type: 'string' },
136
- code: { type: 'string' },
137
- password: { type: 'string' },
138
- invitationCode: { type: 'string' },
139
- nickname: { type: 'string' },
140
- gender: { type: 'string' },
141
- birthday: { type: 'string', format: 'date' },
142
- description: { type: 'string' }
143
- }
144
- },
145
- {
146
- type: 'object',
147
- required: ['email', 'password', 'code'],
148
- properties: {
149
- avatar: { type: 'string' },
150
- email: { type: 'string' },
151
- code: { type: 'string' },
152
- password: { type: 'string' },
153
- invitationCode: { type: 'string' },
154
- nickname: { type: 'string' },
155
- gender: { type: 'string' },
156
- birthday: { type: 'string', format: 'date' },
157
- description: { type: 'string' }
158
- }
213
+ fastify.post(`${options.prefix}/account/resetPassword`, {
214
+ schema: {
215
+ tags: ['账号'], summary: '用户重置密码', body: {
216
+ type: 'object', required: ['newPwd', 'token'], properties: {
217
+ newPwd: {type: 'string', description: '新密码'}, token: {type: 'string', description: '验证token'}
218
+ }
159
219
  }
160
- ]
161
220
  }
162
- }
163
- },
164
- async request => {
165
- const account = request.body;
166
- return await services.account.register(account);
167
- }
168
- );
221
+ }, async request => {
222
+ await services.account.resetPasswordByToken({
223
+ password: request.body.newPwd, token: request.body.token
224
+ });
169
225
 
170
- fastify.post(
171
- `${options.prefix}/login`,
172
- {
173
- schema: {
174
- body: {
175
- type: 'object',
176
- required: ['username', 'password'],
177
- properties: {
178
- username: { type: 'string' },
179
- password: { type: 'string' }
180
- }
226
+ return {};
227
+ });
228
+
229
+ fastify.post(`${options.prefix}/account/forgetPwd`, {
230
+ schema: {
231
+ tags: ['账号'], summary: '忘记密码', body: {
232
+ oneOf: [{
233
+ type: 'object', required: ['email'], properties: {
234
+ email: {type: 'string', description: '邮箱'}
235
+ }
236
+ }, {
237
+ type: 'object', required: ['phone'], properties: {
238
+ phone: {type: 'string', description: '手机号'}
239
+ }
240
+ }]
241
+ }
181
242
  }
182
- }
183
- },
184
- async request => {
185
- const { username, password } = request.body;
186
- const { token, user } = await services.account.login({ username, password, ip: request.ip });
187
- return { token, currentTenantId: user.currentTenantId };
188
- }
189
- );
243
+ }, async request => {
244
+ const name = request.body.email || request.body.phone;
245
+ const token = await services.account.sendJWTVerificationCode({name, type: 5});
246
+ return options.isTest ? {token} : {};
247
+ });
248
+
249
+ fastify.post(`${options.prefix}/account/parseResetToken`, {
250
+ schema: {
251
+ tags: ['账号'], summary: '通过token获取name', body: {
252
+ type: 'object', required: ['token'], properties: {
253
+ token: {type: 'string'}
254
+ }
255
+ }
256
+ }
257
+ }, async request => {
258
+ const {name} = await services.account.verificationJWTCodeValidate(request.body);
259
+ return {name};
260
+ });
190
261
  });
262
+
263
+ module.exports = accountController;
@@ -0,0 +1,110 @@
1
+ const fp = require('fastify-plugin');
2
+
3
+ const adminUserController = fp(async (fastify, options) => {
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'}
10
+ }
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
19
+ });
20
+ });
21
+
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'}
27
+ }
28
+ }
29
+ }
30
+ }, async request => {
31
+ await services.account.resetPassword(request.body);
32
+ return {};
33
+ });
34
+
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
+
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'},
55
+ email: {type: 'string'},
56
+ description: {type: 'string'}
57
+ }
58
+ }
59
+ }
60
+ }, async request => {
61
+ const user = request.body;
62
+ await services.user.saveUser(user);
63
+ return {};
64
+ });
65
+
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'}
72
+ }
73
+ }
74
+ }
75
+ }, async request => {
76
+ const {status, userId} = request.body;
77
+ await services.user.setSuperAdmin({userId, status});
78
+ return {};
79
+ });
80
+
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'}
86
+ }
87
+ }
88
+ }
89
+ }, async request => {
90
+ const {id} = request.body;
91
+ await services.user.setUserStatus({userId: id, status: 12});
92
+ return {};
93
+ });
94
+
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'}
100
+ }
101
+ }
102
+ }
103
+ }, async request => {
104
+ const {id} = request.body;
105
+ await services.user.setUserStatus({userId: id, status: 0});
106
+ return {};
107
+ });
108
+ });
109
+
110
+ module.exports = adminUserController;