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

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 CHANGED
@@ -22,7 +22,7 @@ npm i --save @kne/fastify-account
22
22
  ### API
23
23
 
24
24
  ---
25
- title: "@kne/fastify-account v1.0.0-alpha.0"
25
+ title: "@kne/fastify-account v1.0.0-alpha.1"
26
26
  language_tabs:
27
27
  - shell: Shell
28
28
  - http: HTTP
@@ -42,7 +42,7 @@ headingLevel: 2
42
42
 
43
43
  <!-- Generator: Widdershins v4.0.1 -->
44
44
 
45
- <h1 id="-kne-fastify-account">@kne/fastify-account v1.0.0-alpha.0</h1>
45
+ <h1 id="-kne-fastify-account">@kne/fastify-account v1.0.0-alpha.1</h1>
46
46
 
47
47
  > Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.
48
48
 
@@ -1938,11 +1938,25 @@ This operation does not require authentication
1938
1938
  This operation does not require authentication
1939
1939
  </aside>
1940
1940
 
1941
- ## get__api_v1_account_tenant_getUserCurrentTenant
1941
+ ## get__api_v1_account_tenant_getTenantUserInfo
1942
1942
 
1943
- `GET /api/v1/account/tenant/getUserCurrentTenant`
1943
+ `GET /api/v1/account/tenant/getTenantUserInfo`
1944
1944
 
1945
- <h3 id="get__api_v1_account_tenant_getusercurrenttenant-responses">Responses</h3>
1945
+ <h3 id="get__api_v1_account_tenant_gettenantuserinfo-responses">Responses</h3>
1946
+
1947
+ |Status|Meaning|Description|Schema|
1948
+ |---|---|---|---|
1949
+ |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Default Response|None|
1950
+
1951
+ <aside class="success">
1952
+ This operation does not require authentication
1953
+ </aside>
1954
+
1955
+ ## get__api_v1_account_tenant_orgList
1956
+
1957
+ `GET /api/v1/account/tenant/orgList`
1958
+
1959
+ <h3 id="get__api_v1_account_tenant_orglist-responses">Responses</h3>
1946
1960
 
1947
1961
  |Status|Meaning|Description|Schema|
1948
1962
  |---|---|---|---|
@@ -1966,6 +1980,43 @@ This operation does not require authentication
1966
1980
  This operation does not require authentication
1967
1981
  </aside>
1968
1982
 
1983
+ ## post__api_v1_account_setCurrentTenantId
1984
+
1985
+ `POST /api/v1/account/setCurrentTenantId`
1986
+
1987
+ > Body parameter
1988
+
1989
+ ```json
1990
+ {
1991
+ "type": "object",
1992
+ "required": [
1993
+ "tenantId"
1994
+ ],
1995
+ "properties": {
1996
+ "tenantId": {
1997
+ "type": "string"
1998
+ }
1999
+ }
2000
+ }
2001
+ ```
2002
+
2003
+ <h3 id="post__api_v1_account_setcurrenttenantid-parameters">Parameters</h3>
2004
+
2005
+ |Name|In|Type|Required|Description|
2006
+ |---|---|---|---|---|
2007
+ |body|body|object|true|none|
2008
+ |» tenantId|body|string|true|none|
2009
+
2010
+ <h3 id="post__api_v1_account_setcurrenttenantid-responses">Responses</h3>
2011
+
2012
+ |Status|Meaning|Description|Schema|
2013
+ |---|---|---|---|
2014
+ |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Default Response|None|
2015
+
2016
+ <aside class="success">
2017
+ This operation does not require authentication
2018
+ </aside>
2019
+
1969
2020
  # Schemas
1970
2021
 
1971
2022
 
@@ -182,8 +182,8 @@ module.exports = fp(async (fastify, options) => {
182
182
  },
183
183
  async request => {
184
184
  const { username, password } = request.body;
185
- const token = await fastify.account.services.account.login({ username, password, ip: request.ip });
186
- return { token };
185
+ const { token, user } = await fastify.account.services.account.login({ username, password, ip: request.ip });
186
+ return { token, currentTenantId: user.currentTenantId };
187
187
  }
188
188
  );
189
189
  });
@@ -11,7 +11,7 @@ module.exports = fp(async (fastify, options) => {
11
11
  );
12
12
 
13
13
  fastify.get(
14
- `${options.prefix}/tenant/getUserCurrentTenant`,
14
+ `${options.prefix}/tenant/getTenantUserInfo`,
15
15
  {
16
16
  onRequest: [fastify.account.authenticate.user, fastify.account.authenticate.tenant]
17
17
  },
@@ -19,4 +19,15 @@ module.exports = fp(async (fastify, options) => {
19
19
  return request.tenantInfo;
20
20
  }
21
21
  );
22
+
23
+ fastify.get(
24
+ `${options.prefix}/tenant/orgList`,
25
+ {
26
+ onRequest: [fastify.account.authenticate.user, fastify.account.authenticate.tenant]
27
+ },
28
+ async request => {
29
+ const { tenantId } = request.tenantInfo;
30
+ return await fastify.account.services.tenant.getTenantOrgList({ tenantId });
31
+ }
32
+ );
22
33
  });
@@ -9,4 +9,25 @@ module.exports = fp(async (fastify, options) => {
9
9
  return { userInfo: request.userInfo };
10
10
  }
11
11
  );
12
+
13
+ fastify.post(
14
+ `${options.prefix}/setCurrentTenantId`,
15
+ {
16
+ onRequest: [fastify.account.authenticate.user],
17
+ schema: {
18
+ body: {
19
+ type: 'object',
20
+ required: ['tenantId'],
21
+ properties: {
22
+ tenantId: { type: 'string' }
23
+ }
24
+ }
25
+ }
26
+ },
27
+ async request => {
28
+ const { tenantId } = request.body;
29
+ await fastify.account.services.user.setCurrentTenantId({ id: request.userInfo.id, tenantId });
30
+ return {};
31
+ }
32
+ );
12
33
  });
@@ -1,5 +1,5 @@
1
1
  module.exports = (sequelize, DataTypes) => {
2
- return sequelize.define(
2
+ const tenantRoleApplication = sequelize.define(
3
3
  'tenantRoleApplication',
4
4
  {
5
5
  tenantId: {
@@ -30,4 +30,8 @@ module.exports = (sequelize, DataTypes) => {
30
30
  ]
31
31
  }
32
32
  );
33
+ tenantRoleApplication.associate = ({ tenantRoleApplication, application }) => {
34
+ tenantRoleApplication.belongsTo(application);
35
+ };
36
+ return tenantRoleApplication;
33
37
  };
@@ -1,5 +1,5 @@
1
1
  module.exports = (sequelize, DataTypes) => {
2
- return sequelize.define(
2
+ const tenantRolePermission = sequelize.define(
3
3
  'tenantRolePermission',
4
4
  {
5
5
  tenantId: {
@@ -30,4 +30,10 @@ module.exports = (sequelize, DataTypes) => {
30
30
  ]
31
31
  }
32
32
  );
33
+
34
+ tenantRolePermission.associate = ({ tenantRolePermission, permission }) => {
35
+ tenantRolePermission.belongsTo(permission);
36
+ };
37
+
38
+ return tenantRolePermission;
33
39
  };
@@ -43,7 +43,7 @@ module.exports = fp(async (fastify, options) => {
43
43
  ip
44
44
  });
45
45
 
46
- return fastify.jwt.sign({ payload: { id: user.id } });
46
+ return { token: fastify.jwt.sign({ payload: { id: user.id } }), user };
47
47
  };
48
48
 
49
49
  const passwordAuthentication = async ({ accountId, password }) => {
@@ -1,5 +1,9 @@
1
1
  const fp = require('fastify-plugin');
2
2
  const isNil = require('lodash/isNil');
3
+ const { Unauthorized } = require('http-errors');
4
+ const transform = require('lodash/transform');
5
+ const groupBy = require('lodash/groupBy');
6
+ const pick = require('lodash/pick');
3
7
  module.exports = fp(async (fastify, options) => {
4
8
  const getUserTenant = async authenticatePayload => {
5
9
  const user = await fastify.account.services.user.getUserInfo(authenticatePayload);
@@ -115,9 +119,129 @@ module.exports = fp(async (fastify, options) => {
115
119
  await tenantRole.destroy();
116
120
  };
117
121
 
122
+ const getTenantUserPermissionList = async ({ tenantRoleIds }) => {
123
+ const tenantRoleApplication = await fastify.account.models.tenantRoleApplication.findAll({
124
+ attributes: ['applicationId'],
125
+ where: {
126
+ roleId: {
127
+ [fastify.sequelize.Sequelize.Op.in]: tenantRoleIds
128
+ }
129
+ }
130
+ });
131
+
132
+ const applications = await fastify.account.models.application.findAll({
133
+ attributes: ['id', 'code', 'name'],
134
+ where: {
135
+ id: {
136
+ [fastify.sequelize.Sequelize.Op.in]: tenantRoleApplication.map(({ applicationId }) => applicationId)
137
+ }
138
+ }
139
+ });
140
+
141
+ const tenantRolePermission = await fastify.account.models.tenantRolePermission.findAll({
142
+ attributes: ['permissionId'],
143
+ include: {
144
+ attributes: ['code', 'name', 'isModule', 'paths'],
145
+ model: fastify.account.models.permission
146
+ },
147
+ where: {
148
+ roleId: {
149
+ [fastify.sequelize.Sequelize.Op.in]: tenantRoleIds
150
+ }
151
+ }
152
+ });
153
+
154
+ const permissions = await fastify.account.models.permission.findAll({
155
+ attributes: ['id', 'code', 'name', 'isModule', 'pid', 'applicationId', 'paths'],
156
+ where: {
157
+ [fastify.sequelize.Sequelize.Op.or]: [
158
+ {
159
+ id: {
160
+ [fastify.sequelize.Sequelize.Op.in]: tenantRolePermission.map(({ permissionId }) => permissionId)
161
+ }
162
+ },
163
+ {
164
+ isMust: true
165
+ }
166
+ ]
167
+ }
168
+ });
169
+
170
+ const permissionMapping = transform(
171
+ await fastify.account.models.permission.findAll({
172
+ where: {
173
+ id: {
174
+ [fastify.sequelize.Sequelize.Op.in]: permissions.map(({ paths }) => paths).reduce((list, item) => [...list, ...(item || [])], [])
175
+ }
176
+ }
177
+ }),
178
+ (result, value) => {
179
+ result[value.id] = { code: value.code, name: value.name };
180
+ },
181
+ {}
182
+ );
183
+
184
+ const applicationsMapping = transform(
185
+ applications,
186
+ (result, value) => {
187
+ result[value.id] = value;
188
+ },
189
+ {}
190
+ );
191
+
192
+ const findEndChildren = permissions => {
193
+ const output = [];
194
+ const core = (list, node) => {
195
+ const { children, other } = groupBy(list, item => (item.pid === node.id ? 'children' : 'other'));
196
+ if (!(other && other.length > 0)) {
197
+ return;
198
+ }
199
+ if (!(children && children.length > 0)) {
200
+ node.id !== 0 && output.push(node);
201
+ return;
202
+ }
203
+
204
+ children.forEach(node => {
205
+ core(other, node);
206
+ });
207
+ return output;
208
+ };
209
+ core(permissions, { id: 0 });
210
+ return output;
211
+ };
212
+
213
+ const userPermissionList = findEndChildren(permissions).map(({ code, applicationId, paths }) => {
214
+ return `${applicationsMapping[applicationId].code}${
215
+ paths && paths.length > 0
216
+ ? `:${paths
217
+ .map(id => {
218
+ return permissionMapping[id].code;
219
+ })
220
+ .join(':')}`
221
+ : ''
222
+ }:${code}`;
223
+ });
224
+
225
+ return {
226
+ applications: applications,
227
+ permissions: permissions.map(item =>
228
+ Object.assign(
229
+ {},
230
+ {
231
+ code: item.code,
232
+ name: item.name,
233
+ isModule: item.isModule,
234
+ paths: (item.paths || []).map(id => permissionMapping[id])
235
+ }
236
+ )
237
+ ),
238
+ userPermissionList
239
+ };
240
+ };
241
+
118
242
  const tenantUserAuthenticate = async user => {
119
243
  if (!user.currentTenantId) {
120
- throw new Error('没有找到当前绑定租户');
244
+ throw new Unauthorized('没有找到当前绑定租户');
121
245
  }
122
246
  const tenant = await fastify.account.models.tenant.findByPk(user.currentTenantId, {
123
247
  where: {
@@ -129,6 +253,17 @@ module.exports = fp(async (fastify, options) => {
129
253
  }
130
254
 
131
255
  const tenantUser = await fastify.account.models.tenantUser.findOne({
256
+ attributes: ['id', 'avatar', 'description', 'phone', 'email'],
257
+ include: [
258
+ {
259
+ attributes: ['id', 'name'],
260
+ model: fastify.account.models.tenantOrg
261
+ },
262
+ {
263
+ attributes: ['id', 'name'],
264
+ model: fastify.account.models.tenantRole
265
+ }
266
+ ],
132
267
  where: {
133
268
  tenantId: tenant.id,
134
269
  userId: user.id,
@@ -136,13 +271,35 @@ module.exports = fp(async (fastify, options) => {
136
271
  }
137
272
  });
138
273
 
274
+ // 获取当前租户默认角色
275
+ const defaultTenant = await fastify.account.models.tenantRole.findOne({
276
+ where: {
277
+ type: 1,
278
+ tenantId: tenant.id
279
+ }
280
+ });
281
+
282
+ if (!defaultTenant) {
283
+ throw new Error('租户默认角色未设置,请联系管理员');
284
+ }
285
+
286
+ const tenantRoleIds = tenantUser.tenantRoles.map(({ id }) => id);
287
+ tenantRoleIds.push(defaultTenant.id);
288
+
289
+ const { userPermissionList } = await getTenantUserPermissionList({ tenantRoleIds });
139
290
  if (!tenantUser) {
140
291
  throw new Error('当前租户用户不存在或者已经被关闭');
141
292
  }
142
293
 
294
+ const outputTenantUser = Object.assign({}, tenantUser.get({ plain: true }));
295
+ outputTenantUser.tenantOrgs = outputTenantUser?.tenantOrgs.map(({ id, name }) => ({ id, name }));
296
+ outputTenantUser.tenantRoles = outputTenantUser?.tenantRoles.map(({ id, name }) => ({ id, name }));
143
297
  return {
144
- tenant,
145
- tenantUser
298
+ tenant: pick(tenant, ['id', 'name', 'description']),
299
+ tenantUser: Object.assign({}, outputTenantUser, {
300
+ permissions: userPermissionList
301
+ }),
302
+ user
146
303
  };
147
304
  };
148
305
 
@@ -1,6 +1,7 @@
1
1
  const fp = require('fastify-plugin');
2
2
  const { Unauthorized } = require('http-errors');
3
3
  const get = require('lodash/get');
4
+ const pick = require('lodash/pick');
4
5
  module.exports = fp(async (fastify, options) => {
5
6
  const getUserInfo = async authenticatePayload => {
6
7
  if (!(authenticatePayload && authenticatePayload.id)) {
@@ -10,10 +11,7 @@ module.exports = fp(async (fastify, options) => {
10
11
  if (!user) {
11
12
  throw new Unauthorized();
12
13
  }
13
- const userInfo = user.get({ plain: true });
14
- delete userInfo['userAccountId'];
15
-
16
- return userInfo;
14
+ return pick(user, ['id', 'avatar', 'nickname', 'phone', 'email', 'gender', 'status', 'birthday', 'description', 'currentTenantId']);
17
15
  };
18
16
 
19
17
  const accountIsExists = async ({ email, phone }, currentUser) => {
@@ -97,12 +95,23 @@ module.exports = fp(async (fastify, options) => {
97
95
  await user.save();
98
96
  };
99
97
 
98
+ const setCurrentTenantId = async ({ id, tenantId }) => {
99
+ await fastify.account.services.tenant.getTenantInfo({ id: tenantId });
100
+ const user = await fastify.account.models.user.findByPk(id);
101
+
102
+ if (!user) {
103
+ throw new Error('用户不存在');
104
+ }
105
+ user.currentTenantId = tenantId;
106
+ await user.save();
107
+ };
100
108
  fastify.account.services.user = {
101
109
  getUserInfo,
102
110
  saveUser,
103
111
  accountIsExists,
104
112
  addUser,
105
113
  closeUser,
106
- openUser
114
+ openUser,
115
+ setCurrentTenantId
107
116
  };
108
117
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kne/fastify-account",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.2",
4
4
  "description": "fastify的用户管理账号等实现",
5
5
  "main": "index.js",
6
6
  "scripts": {