@kne/fastify-account 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (54) hide show
  1. package/README.md +2506 -803
  2. package/index.js +61 -8
  3. package/libs/controllers/account.js +221 -37
  4. package/libs/controllers/admin.js +92 -17
  5. package/libs/controllers/adminPermission.js +188 -38
  6. package/libs/controllers/adminTenant.js +44 -257
  7. package/libs/controllers/adminTenantCompany.js +53 -0
  8. package/libs/controllers/adminTenantOrg.js +71 -0
  9. package/libs/controllers/{adminRole.js → adminTenantRole.js} +15 -12
  10. package/libs/controllers/adminTenantUser.js +169 -0
  11. package/libs/controllers/requestLog.js +77 -0
  12. package/libs/controllers/tenant.js +4 -14
  13. package/libs/controllers/tenantCompany.js +54 -0
  14. package/libs/controllers/tenantOrg.js +65 -0
  15. package/libs/controllers/tenantRole.js +219 -0
  16. package/libs/controllers/tenantUser.js +169 -0
  17. package/libs/controllers/user.js +4 -3
  18. package/libs/models/admin-role.js +4 -8
  19. package/libs/models/application.js +16 -10
  20. package/libs/models/company-info.js +25 -0
  21. package/libs/models/login-log.js +11 -9
  22. package/libs/models/permission.js +7 -9
  23. package/libs/models/request-log.js +43 -0
  24. package/libs/models/tenant-application.js +8 -10
  25. package/libs/models/tenant-org.js +5 -9
  26. package/libs/models/tenant-permission.js +7 -9
  27. package/libs/models/tenant-role-application.js +13 -13
  28. package/libs/models/tenant-role-permission.js +9 -14
  29. package/libs/models/tenant-role.js +5 -9
  30. package/libs/models/tenant-share-group-permission.js +5 -9
  31. package/libs/models/tenant-share-group.js +5 -9
  32. package/libs/models/tenant-source-user-share-group.js +5 -9
  33. package/libs/models/tenant-token.js +7 -9
  34. package/libs/models/tenant-user-org.js +11 -10
  35. package/libs/models/tenant-user-role.js +11 -10
  36. package/libs/models/tenant-user-share-group.js +6 -10
  37. package/libs/models/tenant-user.js +35 -16
  38. package/libs/models/tenant.js +17 -9
  39. package/libs/models/user-account.js +17 -9
  40. package/libs/models/user.js +27 -17
  41. package/libs/models/verification-code.js +8 -10
  42. package/libs/services/account.js +95 -71
  43. package/libs/services/admin.js +38 -122
  44. package/libs/services/application.js +170 -0
  45. package/libs/services/permission.js +161 -163
  46. package/libs/services/request-log.js +37 -0
  47. package/libs/services/tenant-company.js +54 -0
  48. package/libs/services/tenant-invite.js +62 -0
  49. package/libs/services/tenant-org.js +118 -0
  50. package/libs/services/tenant-role.js +109 -0
  51. package/libs/services/tenant-user.js +578 -0
  52. package/libs/services/tenant.js +69 -670
  53. package/libs/services/user.js +109 -33
  54. package/package.json +3 -3
package/index.js CHANGED
@@ -2,6 +2,8 @@ const fp = require('fastify-plugin');
2
2
  const packageJson = require('./package.json');
3
3
  const path = require('path');
4
4
  const merge = require('lodash/merge');
5
+ const get = require('lodash/get');
6
+ const { Unauthorized, Forbidden } = require('http-errors');
5
7
 
6
8
  module.exports = fp(
7
9
  async function (fastify, options) {
@@ -9,21 +11,27 @@ module.exports = fp(
9
11
  options = merge(
10
12
  {
11
13
  prefix: `/api/v${packageJson.version.split('.')[0]}/account`, //如果为true,发送邮件和短信将不调用,验证码随response返回
14
+ dbTableNamePrefix: 't_account_',
12
15
  isTest: false,
13
16
  jwt: {
14
- secret: 'super-secret'
17
+ secret: 'super-secret',
18
+ expires: null
15
19
  },
16
- defaultPassword: 'Aa000000!'
20
+ sendMessage: async () => {}
17
21
  },
18
22
  options
19
23
  );
20
- fastify.register(require('fastify-ip'));
21
24
  fastify.register(require('@fastify/jwt'), options.jwt);
22
25
  fastify.register(require('@kne/fastify-namespace'), {
23
26
  options,
24
27
  name: 'account',
25
28
  modules: [
26
- ['models', await fastify.sequelize.addModels(path.resolve(__dirname, './libs/models'))],
29
+ [
30
+ 'models',
31
+ await fastify.sequelize.addModels(path.resolve(__dirname, './libs/models'), {
32
+ prefix: options.dbTableNamePrefix
33
+ })
34
+ ],
27
35
  ['services', path.resolve(__dirname, './libs/services')],
28
36
  ['controllers', path.resolve(__dirname, './libs/controllers')],
29
37
  [
@@ -32,15 +40,60 @@ module.exports = fp(
32
40
  user: async request => {
33
41
  const info = await request.jwtVerify();
34
42
  //这里判断失效时间
35
- //info.iat
43
+ if (options.jwt.expires && Date.now() - info.iat * 1000 > options.jwt.expires) {
44
+ throw Unauthorized('身份认证超时');
45
+ }
36
46
  request.authenticatePayload = info.payload;
37
- request.userInfo = await fastify.account.services.user.getUserInfo(request.authenticatePayload);
47
+ request.userInfo = await fastify.account.services.user.getUser(request.authenticatePayload);
48
+ request.appName = request.headers['x-app-name'];
49
+ await fastify.account.services.requestLog.addRequestLog({
50
+ userInfo: request.userInfo,
51
+ tenantId: request.userInfo.currentTenantId,
52
+ appName: request.appName,
53
+ type: 'user',
54
+ action: `${request.routerPath}:${request.method}`,
55
+ summary: request.routeSchema?.summary
56
+ });
38
57
  },
39
58
  tenant: async request => {
40
- request.tenantInfo = await fastify.account.services.tenant.tenantUserAuthenticate(request.userInfo);
59
+ request.appName = request.headers['x-app-name'];
60
+ request.tenantInfo = await fastify.account.services.tenantUser.getTenantUserByUserId({
61
+ userInfo: request.userInfo,
62
+ appName: request.appName
63
+ });
64
+ request.tenantInfo.companyInfo = await fastify.account.services.tenantCompany.getTenantCompanyInfo({
65
+ tenantId: request.tenantInfo.tenant.id
66
+ });
67
+ await fastify.account.services.requestLog.addRequestLog({
68
+ userInfo: request.userInfo,
69
+ tenantId: request.tenantInfo.tenant.id,
70
+ appName: request.appName,
71
+ type: 'tenant',
72
+ action: `${request.routerPath}:${request.method}`,
73
+ summary: request.routeSchema?.summary
74
+ });
41
75
  },
42
76
  admin: async request => {
43
- request.adminInfo = await fastify.account.services.admin.superAdminAuthenticate(request.userInfo);
77
+ if (!(await fastify.account.services.admin.checkIsSuperAdmin(request.userInfo))) {
78
+ throw Unauthorized('不能执行该操作,需要超级管理员权限');
79
+ }
80
+ request.appName = request.headers['x-app-name'];
81
+ await fastify.account.services.requestLog.addRequestLog({
82
+ userInfo: request.userInfo,
83
+ appName: request.appName,
84
+ type: 'admin',
85
+ action: `${request.routerPath}:${request.method}`,
86
+ summary: request.routeSchema?.summary
87
+ });
88
+ },
89
+ createPermission: permission => async request => {
90
+ const permissions = get(request.tenantInfo, 'tenantUser.permissions');
91
+ if (!permissions) {
92
+ throw Forbidden('未获取到权限信息');
93
+ }
94
+ if (!(permission && permissions.indexOf(permission) > -1)) {
95
+ throw Forbidden('用户没有权限执行该操作');
96
+ }
44
97
  }
45
98
  }
46
99
  ]
@@ -1,9 +1,12 @@
1
1
  const fp = require('fastify-plugin');
2
2
  module.exports = fp(async (fastify, options) => {
3
+ const { services } = fastify.account;
3
4
  fastify.post(
4
5
  `${options.prefix}/sendEmailCode`,
5
6
  {
6
7
  schema: {
8
+ tags: ['账号'],
9
+ summary: '发送登录邮箱验证码',
7
10
  body: {
8
11
  type: 'object',
9
12
  required: ['email'],
@@ -18,14 +21,7 @@ module.exports = fp(async (fastify, options) => {
18
21
  schema: {
19
22
  type: 'object',
20
23
  properties: {
21
- code: { type: 'number' },
22
- data: {
23
- type: 'object',
24
- properties: {
25
- code: { type: 'string', description: '验证码' }
26
- }
27
- },
28
- msg: { type: 'string' }
24
+ code: { type: 'string', description: '验证码' }
29
25
  }
30
26
  }
31
27
  }
@@ -36,7 +32,7 @@ module.exports = fp(async (fastify, options) => {
36
32
  },
37
33
  async request => {
38
34
  const { email } = request.body;
39
- const code = await fastify.account.services.account.sendEmailCode({ email });
35
+ const code = await services.account.sendVerificationCode({ name: email, type: 0 });
40
36
  return options.isTest ? { code } : {};
41
37
  }
42
38
  );
@@ -45,18 +41,34 @@ module.exports = fp(async (fastify, options) => {
45
41
  `${options.prefix}/sendSMSCode`,
46
42
  {
47
43
  schema: {
44
+ tags: ['账号'],
45
+ summary: '发送登录短信验证码',
48
46
  body: {
49
47
  type: 'object',
50
48
  required: ['phone'],
51
49
  properties: {
52
50
  phone: { type: 'string', description: '电话' }
53
51
  }
52
+ },
53
+ response: {
54
+ 200: {
55
+ content: {
56
+ 'application/json': {
57
+ schema: {
58
+ type: 'object',
59
+ properties: {
60
+ code: { type: 'string', description: '验证码' }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
54
66
  }
55
67
  }
56
68
  },
57
69
  async request => {
58
70
  const { phone } = request.body;
59
- const code = await fastify.account.services.account.sendSMSCode({ phone });
71
+ const code = await services.account.sendVerificationCode({ name: phone, type: 0 });
60
72
  return options.isTest ? { code } : {};
61
73
  }
62
74
  );
@@ -65,20 +77,31 @@ module.exports = fp(async (fastify, options) => {
65
77
  `${options.prefix}/validateCode`,
66
78
  {
67
79
  schema: {
80
+ tags: ['账号'],
81
+ summary: '验证码验证',
68
82
  body: {
69
83
  type: 'object',
70
84
  required: ['name', 'type', 'code'],
71
85
  properties: {
72
86
  name: { type: 'string', description: '被验证的账号,手机或邮箱' },
73
- type: { type: 'number', description: '0:手机注册,1:邮箱注册,2:手机登录,3:邮箱登录,4:验证租户管理员' },
87
+ type: { type: 'number', description: '0:注册,2:登录,4:验证租户管理员,5:忘记密码' },
74
88
  code: { type: 'string', description: '接受到的验证码' }
75
89
  }
90
+ },
91
+ response: {
92
+ 200: {
93
+ content: {
94
+ 'application/json': {
95
+ schema: {}
96
+ }
97
+ }
98
+ }
76
99
  }
77
100
  }
78
101
  },
79
102
  async request => {
80
103
  const { name, type, code } = request.body;
81
- const isPass = await fastify.account.services.account.verificationCodeValidate({
104
+ const isPass = await services.account.verificationCodeValidate({
82
105
  name,
83
106
  type,
84
107
  code
@@ -94,29 +117,45 @@ module.exports = fp(async (fastify, options) => {
94
117
  `${options.prefix}/accountIsExists`,
95
118
  {
96
119
  schema: {
120
+ tags: ['账号'],
121
+ summary: '账号是否已存在',
97
122
  body: {
98
123
  oneOf: [
99
124
  {
100
125
  type: 'object',
101
126
  required: ['phone'],
102
127
  properties: {
103
- phone: { type: 'string' }
128
+ phone: { type: 'string', description: '电话' }
104
129
  }
105
130
  },
106
131
  {
107
132
  type: 'object',
108
133
  required: ['email'],
109
134
  properties: {
110
- email: { type: 'string' }
135
+ email: { type: 'string', description: '邮箱' }
111
136
  }
112
137
  }
113
138
  ]
139
+ },
140
+ response: {
141
+ 200: {
142
+ content: {
143
+ 'application/json': {
144
+ schema: {
145
+ type: 'object',
146
+ properties: {
147
+ isExists: { type: 'boolean', description: 'true已存在,false不存在' }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
114
153
  }
115
154
  }
116
155
  },
117
156
  async request => {
118
157
  const { phone, email } = request.body;
119
- return { isExists: await fastify.account.services.user.accountIsExists({ phone, email }) };
158
+ return { isExists: await services.user.accountIsExists({ phone, email }) };
120
159
  }
121
160
  );
122
161
 
@@ -124,45 +163,56 @@ module.exports = fp(async (fastify, options) => {
124
163
  `${options.prefix}/register`,
125
164
  {
126
165
  schema: {
166
+ tags: ['账号'],
167
+ summary: '注册账号',
127
168
  body: {
128
169
  oneOf: [
129
170
  {
130
171
  type: 'object',
131
172
  required: ['phone', 'password', 'code'],
132
173
  properties: {
133
- avatar: { type: 'string' },
134
- phone: { type: 'string' },
135
- code: { type: 'string' },
136
- password: { type: 'string' },
137
- invitationCode: { type: 'string' },
138
- nickname: { type: 'string' },
139
- gender: { type: 'string' },
140
- birthday: { type: 'string', format: 'date' },
141
- description: { type: 'string' }
174
+ avatar: { type: 'string', description: '头像图片id' },
175
+ phone: { type: 'string', description: '电话' },
176
+ code: { type: 'string', description: '验证码' },
177
+ password: { type: 'string', description: '密码(需要md5加密)' },
178
+ invitationCode: { type: 'string', description: '邀请码,用来默认加入租户' },
179
+ nickname: { type: 'string', description: '昵称' },
180
+ gender: { type: 'string', description: '性别' },
181
+ birthday: { type: 'string', format: 'date', description: '出生日期' },
182
+ description: { type: 'string', description: '个人简介' }
142
183
  }
143
184
  },
144
185
  {
145
186
  type: 'object',
146
187
  required: ['email', 'password', 'code'],
147
188
  properties: {
148
- avatar: { type: 'string' },
149
- email: { type: 'string' },
150
- code: { type: 'string' },
151
- password: { type: 'string' },
152
- invitationCode: { type: 'string' },
153
- nickname: { type: 'string' },
154
- gender: { type: 'string' },
155
- birthday: { type: 'string', format: 'date' },
156
- description: { type: 'string' }
189
+ avatar: { type: 'string', description: '头像图片id' },
190
+ email: { type: 'string', description: '邮箱' },
191
+ code: { type: 'string', description: '验证码' },
192
+ password: { type: 'string', description: '密码(需要md5加密)' },
193
+ invitationCode: { type: 'string', description: '邀请码,用来默认加入租户' },
194
+ nickname: { type: 'string', description: '昵称' },
195
+ gender: { type: 'string', description: '性别' },
196
+ birthday: { type: 'string', format: 'date', description: '出生日期' },
197
+ description: { type: 'string', description: '个人简介' }
157
198
  }
158
199
  }
159
200
  ]
201
+ },
202
+ response: {
203
+ 200: {
204
+ content: {
205
+ 'application/json': {
206
+ schema: {}
207
+ }
208
+ }
209
+ }
160
210
  }
161
211
  }
162
212
  },
163
213
  async request => {
164
214
  const account = request.body;
165
- return await fastify.account.services.account.register(account);
215
+ return await services.account.register(account);
166
216
  }
167
217
  );
168
218
 
@@ -170,20 +220,154 @@ module.exports = fp(async (fastify, options) => {
170
220
  `${options.prefix}/login`,
171
221
  {
172
222
  schema: {
223
+ tags: ['账号'],
224
+ summary: '登录',
173
225
  body: {
174
226
  type: 'object',
175
227
  required: ['username', 'password'],
176
228
  properties: {
177
- username: { type: 'string' },
178
- password: { type: 'string' }
229
+ username: { type: 'string', description: '用户名' },
230
+ password: { type: 'string', description: '密码' }
231
+ }
232
+ },
233
+ response: {
234
+ 200: {
235
+ content: {
236
+ 'application/json': {
237
+ schema: {
238
+ type: 'object',
239
+ properties: {
240
+ token: { type: 'string', description: '用户token' },
241
+ currentTenantId: { type: 'string', description: '当前租户id' }
242
+ }
243
+ }
244
+ }
245
+ }
179
246
  }
180
247
  }
181
248
  }
182
249
  },
183
250
  async request => {
184
251
  const { username, password } = request.body;
185
- const { token, user } = await fastify.account.services.account.login({ username, password, ip: request.ip });
252
+ const appName = request.headers['x-app-name'];
253
+ const { token, user } = await services.account.login({ username, password, ip: request.ip, appName });
186
254
  return { token, currentTenantId: user.currentTenantId };
187
255
  }
188
256
  );
257
+
258
+ fastify.post(
259
+ `${options.prefix}/modifyPassword`,
260
+ {
261
+ schema: {
262
+ tags: ['账号'],
263
+ summary: '新用户重置新密码',
264
+ body: {
265
+ oneOf: [
266
+ {
267
+ type: 'object',
268
+ required: ['email', 'newPwd', 'oldPwd'],
269
+ properties: {
270
+ email: { type: 'string', description: '邮箱' },
271
+ newPwd: { type: 'string', description: '新密码' },
272
+ oldPwd: { type: 'string', description: '原密码' }
273
+ }
274
+ },
275
+ {
276
+ type: 'object',
277
+ required: ['phone', 'newPwd', 'oldPwd'],
278
+ properties: {
279
+ phone: { type: 'string', description: '手机号' },
280
+ newPwd: { type: 'string', description: '新密码' },
281
+ oldPwd: { type: 'string', description: '原密码' }
282
+ }
283
+ }
284
+ ]
285
+ }
286
+ }
287
+ },
288
+ async request => {
289
+ await services.account.modifyPassword(request.body);
290
+ return {};
291
+ }
292
+ );
293
+
294
+ fastify.post(
295
+ `${options.prefix}/resetPassword`,
296
+ {
297
+ schema: {
298
+ tags: ['账号'],
299
+ summary: '用户重置密码',
300
+ body: {
301
+ type: 'object',
302
+ required: ['newPwd', 'token'],
303
+ properties: {
304
+ newPwd: { type: 'string', description: '新密码' },
305
+ token: { type: 'string', description: '验证token' }
306
+ }
307
+ }
308
+ }
309
+ },
310
+ async request => {
311
+ await services.account.resetPasswordByToken({
312
+ password: request.body.newPwd,
313
+ token: request.body.token
314
+ });
315
+
316
+ return {};
317
+ }
318
+ );
319
+
320
+ fastify.post(
321
+ `${options.prefix}/forgetPwd`,
322
+ {
323
+ schema: {
324
+ tags: ['账号'],
325
+ summary: '忘记密码',
326
+ body: {
327
+ oneOf: [
328
+ {
329
+ type: 'object',
330
+ required: ['email'],
331
+ properties: {
332
+ email: { type: 'string', description: '邮箱' }
333
+ }
334
+ },
335
+ {
336
+ type: 'object',
337
+ required: ['phone'],
338
+ properties: {
339
+ phone: { type: 'string', description: '手机号' }
340
+ }
341
+ }
342
+ ]
343
+ }
344
+ }
345
+ },
346
+ async request => {
347
+ const name = request.body.email || request.body.phone;
348
+ const token = await services.account.sendJWTVerificationCode({ name, type: 5 });
349
+ return options.isTest ? { token } : {};
350
+ }
351
+ );
352
+
353
+ fastify.post(
354
+ `${options.prefix}/parseResetToken`,
355
+ {
356
+ schema: {
357
+ tags: ['账号'],
358
+ summary: '通过token获取name',
359
+ body: {
360
+ type: 'object',
361
+ required: ['token'],
362
+ properties: {
363
+ token: { type: 'string' }
364
+ }
365
+ }
366
+ }
367
+ },
368
+ async request => {
369
+ const { name } = await services.account.verificationJWTCodeValidate(request.body);
370
+ return { name };
371
+ }
372
+ );
189
373
  });