@abtnode/core 1.16.52-beta-20251003-083412-fdfc4e36 → 1.16.52-beta-20251005-235515-42ad5caf

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.
@@ -0,0 +1,661 @@
1
+ const { CustomError } = require('@blocklet/error');
2
+ const { SERVER_ROLES } = require('@abtnode/constant');
3
+ const { Sequelize, Op } = require('sequelize');
4
+ const logger = require('@abtnode/logger')('@abtnode/core:states:orgs');
5
+ const { PASSPORT_STATUS } = require('@abtnode/constant');
6
+
7
+ const BaseState = require('./base');
8
+ const { formatPaging, isAdminUser, isAdmingPath, isOrgOwner } = require('../util/org');
9
+
10
+ class OrgState extends BaseState {
11
+ constructor(baseDir, config = {}, models) {
12
+ super(baseDir, { filename: 'orgs.db', ...config });
13
+ this.userOrgs = new BaseState(models.UserOrg, config);
14
+ this.user = new BaseState(models.User, config);
15
+ this.session = new BaseState(models.Session, config);
16
+ this.passport = new BaseState(models.Passport, config);
17
+ this.orgResource = new BaseState(models.OrgResource, config);
18
+ }
19
+
20
+ async create(payload, context) {
21
+ const { user } = context || {};
22
+ // 非 SDK 环境,只能创建自己的 org
23
+ if (user.role !== SERVER_ROLES.BLOCKLET_SDK && payload.ownerDid && payload.ownerDid !== user.did) {
24
+ throw new CustomError(403, 'You cannot create org for other users');
25
+ }
26
+
27
+ const orgData = {
28
+ ...payload,
29
+ ownerDid: payload.ownerDid || context.user.did,
30
+ };
31
+
32
+ if (!orgData.ownerDid) {
33
+ throw new CustomError(400, 'Owner did is required');
34
+ }
35
+
36
+ const doc = await this.insert(orgData);
37
+
38
+ // 创建成功后默认要向组织添加创建者
39
+ await this.userOrgs.insert({ orgId: doc.id, userDid: orgData.ownerDid, status: 'active' });
40
+
41
+ return doc;
42
+ }
43
+
44
+ async updateOrg({ org }, context) {
45
+ const result = await this.findOne({ where: { id: org.id } });
46
+ const { user } = context || {};
47
+ // dashboard 或者非 org owner 无法更新 org
48
+ if (isAdmingPath(context) || !isOrgOwner(user, result)) {
49
+ throw new CustomError(403, "You cannot edit other user's org");
50
+ }
51
+ const [affectedRows, docs] = await this.update({ id: result.id }, { $set: org }, { returnUpdatedDocs: true });
52
+ if (affectedRows !== 1) {
53
+ throw new CustomError(500, 'Update org failed, please try again later');
54
+ }
55
+ return docs[0];
56
+ }
57
+
58
+ async deleteOrg({ id }, context) {
59
+ const result = await this.findOne({ where: { id } });
60
+ const { user } = context || {};
61
+ // dashboard 或者非 org owner 无法更新 org
62
+ if (isAdmingPath(context) || !isOrgOwner(user, result)) {
63
+ throw new CustomError(403, "You cannot delete other user's org");
64
+ }
65
+ return this.remove({ id });
66
+ }
67
+
68
+ /**
69
+ * 用户是否存在组织中
70
+ */
71
+ canAccessOrg({ userDid, orgId }) {
72
+ return this.userOrgs.findOne({ where: { userDid, orgId } });
73
+ }
74
+
75
+ async get({ id }, context) {
76
+ const result = await this.getOrg(id);
77
+ if (!result) {
78
+ throw new CustomError(404, 'Org not found', { id });
79
+ }
80
+ const { user } = context || {};
81
+ const userInOrg = await this.canAccessOrg({ userDid: user.did, orgId: id });
82
+ const isAdmin = (isAdmingPath(context) && isAdminUser(user)) || user.role === SERVER_ROLES.BLOCKLET_SDK;
83
+ if (!isAdmin && !isOrgOwner(user, result) && !userInOrg) {
84
+ throw new CustomError(403, "You cannot access other user's org");
85
+ }
86
+
87
+ return result;
88
+ }
89
+
90
+ async list(payload = {}, context) {
91
+ const paging = formatPaging(payload.paging);
92
+ const { user } = context || {};
93
+ if (!user) {
94
+ throw new CustomError(403, 'You are not logged in');
95
+ }
96
+
97
+ // 非 SDK 环境,只能查询自己的 org
98
+ if (user.role !== SERVER_ROLES.BLOCKLET_SDK && payload.userDid && payload.userDid !== user.did) {
99
+ throw new CustomError(403, "You cannot access other user's org");
100
+ }
101
+
102
+ const where = {};
103
+ const replacements = {};
104
+
105
+ const orgConditions = [];
106
+ const likeOp = this.model.sequelize.getDialect() === 'postgres' ? Op.iLike : Op.like;
107
+
108
+ Object.keys(payload.org || {}).forEach((key) => {
109
+ if (key !== 'id' && Object.prototype.hasOwnProperty.call(payload.org, key) && !!payload.org[key]) {
110
+ // 使用 LIKE 模糊查询
111
+ orgConditions.push({ [key]: { [likeOp]: `%${payload.org[key]}%` } });
112
+ }
113
+ });
114
+
115
+ const isAdmin = isAdminUser(user) && isAdmingPath(context);
116
+
117
+ // 如果是在 dashboard 查询,忽略 type 参数
118
+ if (isAdmin) {
119
+ payload.type = '';
120
+ }
121
+
122
+ const quertUserDid = payload.userDid || user.did;
123
+
124
+ // 根据不同的 type 构建查询条件,同时合并 org 条件
125
+ if (payload.type === 'owned') {
126
+ where.ownerDid = quertUserDid;
127
+ // 添加 org 条件作为 AND 条件
128
+ if (orgConditions.length > 0) {
129
+ where[Op.or] = orgConditions;
130
+ }
131
+ } else if (payload.type === 'joined') {
132
+ where.id = {
133
+ [Op.in]: Sequelize.literal(
134
+ '(SELECT DISTINCT "orgId" FROM user_orgs WHERE userDid = :userDid and status = "active")'
135
+ ),
136
+ };
137
+ where.ownerDid = {
138
+ [Op.ne]: quertUserDid,
139
+ };
140
+ replacements.userDid = quertUserDid;
141
+ // 添加 org 条件作为 AND 条件
142
+ if (orgConditions.length > 0) {
143
+ where[Op.or] = orgConditions;
144
+ }
145
+ } else if (!isAdmin && !payload.type) {
146
+ // 查询所有的(包括我创建的和我加入的)
147
+ const typeConditions = [
148
+ { ownerDid: quertUserDid },
149
+ {
150
+ id: {
151
+ [Op.in]: Sequelize.literal(
152
+ '(SELECT DISTINCT "orgId" FROM user_orgs WHERE userDid = :userDid and status = "active")'
153
+ ),
154
+ },
155
+ },
156
+ ];
157
+
158
+ if (orgConditions.length > 0) {
159
+ where[Op.and] = [{ [Op.or]: typeConditions }, { [Op.or]: orgConditions }];
160
+ } else {
161
+ where[Op.or] = typeConditions;
162
+ }
163
+ replacements.userDid = quertUserDid;
164
+ } else if (orgConditions.length > 0) {
165
+ where[Op.or] = orgConditions;
166
+ }
167
+
168
+ // @FIXME: LiuShuang 这里没必要返回 owner 的信息, 可以直接通过 ownerDid 查询 owner 的信息
169
+ const result = await this.paginate({ where, replacements }, { updatedAt: -1 }, paging);
170
+
171
+ // 查询每个 org 中的用户信息, 在org 列表页没必要查询全部
172
+ const orgMembers = await Promise.all(
173
+ result.list.map((org) => this.getOrgMembers({ orgId: org.id, paging: { page: 1, pageSize: 5 } }, context))
174
+ );
175
+
176
+ result.list.forEach((org, index) => {
177
+ const itme = orgMembers[index];
178
+ org.members = itme.users;
179
+ org.membersCount = itme.paging.total;
180
+ });
181
+
182
+ // 查询用户的 passport
183
+ let passports = [];
184
+ if (['joined', 'all'].includes(payload.type)) {
185
+ passports = await this.passport.find({
186
+ where: { userDid: quertUserDid },
187
+ });
188
+ }
189
+
190
+ return {
191
+ orgs: result.list,
192
+ passports,
193
+ paging: result.paging,
194
+ };
195
+ }
196
+
197
+ getOrg(id) {
198
+ return this.findOne({ where: { id } });
199
+ }
200
+
201
+ async orgIsExist(id) {
202
+ try {
203
+ const doc = await this.getOrg(id);
204
+ return !!doc;
205
+ } catch (err) {
206
+ logger.error('Org not found', { err, id });
207
+ return false;
208
+ }
209
+ }
210
+
211
+ async getUser(did) {
212
+ const attributes = ['did'];
213
+ let user = await this.user.findOne({ where: { did }, attributes });
214
+ if (!user) {
215
+ const connectedAccount = await this.connectedAccount.findOne({ did });
216
+ if (connectedAccount) {
217
+ user = await this.user.findOne({ where: { did: connectedAccount.userDid }, attributes });
218
+ }
219
+ }
220
+ return user;
221
+ }
222
+
223
+ /**
224
+ * 获取用户创建的 org 数量
225
+ * @param {*} userDid
226
+ * @returns
227
+ */
228
+ async getOrgCountByUser(userDid) {
229
+ const count = await this.count({ where: { ownerDid: userDid } });
230
+ return count;
231
+ }
232
+
233
+ /**
234
+ * 获取一个 org 下的成员数量
235
+ * @param {*} userDid
236
+ * @returns
237
+ */
238
+ async getOrgMemberCount(orgId) {
239
+ const count = await this.userOrgs.count({ where: { orgId } });
240
+ return count;
241
+ }
242
+
243
+ async userIsExist(userDid) {
244
+ try {
245
+ const doc = await this.getUser(userDid);
246
+ return !!doc;
247
+ } catch (err) {
248
+ logger.error('User not found', { err, userDid });
249
+ return false;
250
+ }
251
+ }
252
+
253
+ // org members
254
+ async addOrgMember({ orgId, userDid, status }) {
255
+ const [orgIsExist, userIsExist] = await Promise.all([this.orgIsExist(orgId), this.userIsExist(userDid)]);
256
+ if (!orgIsExist) {
257
+ throw new CustomError(404, 'Org not found', { orgId });
258
+ }
259
+
260
+ if (!userIsExist) {
261
+ throw new CustomError(404, 'User not found', { userDid });
262
+ }
263
+
264
+ const userOrg = await this.userOrgs.findOne({ where: { orgId, userDid } });
265
+ if (userOrg) {
266
+ throw new CustomError(400, 'User already in the org, cannot add again', { orgId, userDid });
267
+ }
268
+
269
+ return this.userOrgs.insert({ orgId, userDid, status: status || 'active' });
270
+ }
271
+
272
+ async removeOrgMember({ orgId, userDid }, context) {
273
+ const [org, userIsExist] = await Promise.all([this.getOrg(orgId), this.userIsExist(userDid)]);
274
+ if (!org || !userIsExist) {
275
+ throw new CustomError(404, 'Org or user not found', { orgId, userDid });
276
+ }
277
+
278
+ const { user } = context || {};
279
+ if (isAdmingPath(context) || !isOrgOwner(user, org)) {
280
+ throw new CustomError(403, "You cannot remove members from other users' org");
281
+ }
282
+
283
+ if (org.ownerDid === userDid) {
284
+ throw new CustomError(400, 'Owner cannot be removed from the org', { orgId, userDid });
285
+ }
286
+
287
+ const userOrg = await this.userOrgs.findOne({ where: { orgId, userDid } });
288
+ if (!userOrg) {
289
+ throw new CustomError(400, 'User not in the org, cannot remove', { orgId, userDid });
290
+ }
291
+
292
+ return this.userOrgs.remove({ orgId, userDid });
293
+ }
294
+
295
+ async getOrgMembers({ orgId, paging, options = {} }, context) {
296
+ const { includePassport = false } = options || {};
297
+ const org = await this.getOrg(orgId);
298
+ if (!org) {
299
+ throw new CustomError(404, 'Org not found', { orgId });
300
+ }
301
+
302
+ const { user } = context || {};
303
+ const userInOrg = await this.canAccessOrg({ userDid: user.did, orgId });
304
+ const isAdmin = (isAdmingPath(context) && isAdminUser(user)) || user.role === SERVER_ROLES.BLOCKLET_SDK;
305
+ if (!isAdmin && !isOrgOwner(user, org) && !userInOrg) {
306
+ throw new CustomError(403, "You cannot access other user's org");
307
+ }
308
+
309
+ const newPaging = formatPaging(paging);
310
+
311
+ const include = [
312
+ {
313
+ model: this.user.model,
314
+ as: 'user',
315
+ attributes: ['did', 'avatar', 'fullName'],
316
+ },
317
+ ];
318
+
319
+ const doc = await this.userOrgs.paginate({ where: { orgId }, include }, { updatedAt: -1 }, newPaging);
320
+
321
+ if (includePassport) {
322
+ // 获取每个用户的 passport
323
+ const passports = await Promise.all(
324
+ doc.list.map((item) => this.passport.find({ where: { userDid: item.userDid, status: PASSPORT_STATUS.VALID } }))
325
+ );
326
+
327
+ doc.list.forEach((item, index) => {
328
+ if (item.user) {
329
+ item.user.passports = passports[index];
330
+ }
331
+ });
332
+ }
333
+
334
+ return {
335
+ users: doc.list,
336
+ paging: doc.paging,
337
+ };
338
+ }
339
+
340
+ async updateOrgMember({ orgId, userDid, status = 'active' }) {
341
+ const [orgIsExist, userIsExist] = await Promise.all([this.orgIsExist(orgId), this.userIsExist(userDid)]);
342
+ if (!orgIsExist) {
343
+ throw new CustomError(404, 'Org not found', { orgId });
344
+ }
345
+ if (!userIsExist) {
346
+ throw new CustomError(404, 'User not found', { userDid });
347
+ }
348
+ const userOrg = await this.userOrgs.findOne({ where: { orgId, userDid } });
349
+ if (!userOrg || status === userOrg.status) {
350
+ throw new CustomError(400, 'User not in the org, cannot update', { orgId, userDid });
351
+ }
352
+ return this.userOrgs.update({ orgId, userDid }, { $set: { status: status || 'active' } });
353
+ }
354
+
355
+ /**
356
+ * 移除正在邀请的用户
357
+ * @param {string} userDid - 用户DID:这里表示是的在邀请链接中的用户列表移除用户ID
358
+ * @param {string} orgId - 组织ID
359
+ * @returns {Promise}
360
+ */
361
+ async removeInvitation({ userDid, orgId }) {
362
+ if (!orgId) {
363
+ throw new CustomError(400, 'orgId is required');
364
+ }
365
+
366
+ try {
367
+ // 查找包含该orgId的邀请记录
368
+ const invitations = await this.session.find({
369
+ where: {
370
+ '__data.orgId': orgId,
371
+ type: 'invite',
372
+ },
373
+ });
374
+
375
+ if (!invitations || invitations.length === 0) {
376
+ logger.info('No invitations found for org', { orgId, userDid });
377
+ return;
378
+ }
379
+
380
+ // 如果没有传入 userDid,删除所有相关邀请
381
+ if (!userDid) {
382
+ logger.info('Removing all invitations for org', { orgId, count: invitations.length });
383
+
384
+ const removePromises = invitations.map((invitation) => this.session.remove({ id: invitation.id }));
385
+
386
+ await Promise.all(removePromises);
387
+ logger.info('Successfully removed all invitations for org', { orgId });
388
+ return;
389
+ }
390
+
391
+ // 如果有 userDid,按原逻辑处理:从邀请中移除特定用户
392
+ const invitation = invitations.find((item) => {
393
+ return item?.__data?.inviteUserDids?.length > 0 && item.__data.inviteUserDids.includes(userDid);
394
+ });
395
+
396
+ if (!invitation) {
397
+ throw new CustomError(404, 'Invitation not found for user', { userDid, orgId });
398
+ }
399
+
400
+ const otherUsers = invitation.__data.inviteUserDids.filter((item) => item !== userDid);
401
+
402
+ if (otherUsers.length > 0) {
403
+ // 还有其他用户,只移除当前用户
404
+ await this.session.update(invitation.id, {
405
+ __data: {
406
+ ...invitation.__data,
407
+ inviteUserDids: otherUsers,
408
+ },
409
+ });
410
+ logger.info('Removed user from invitation', { userDid, orgId, remainingUsers: otherUsers.length });
411
+ } else {
412
+ // 没有其他用户了,删除整个邀请
413
+ await this.session.remove({ id: invitation.id });
414
+ logger.info('Removed entire invitation (no remaining users)', { userDid, orgId, invitationId: invitation.id });
415
+ }
416
+ } catch (error) {
417
+ logger.error('Remove invitation failed', { error, userDid, orgId });
418
+ throw error;
419
+ }
420
+ }
421
+
422
+ /**
423
+ * 获取一个 org 下可邀请的用户列表
424
+ */
425
+ async getOrgInvitableUsers({ id, query = {}, paging = { page: 1, pageSize: 10 } }, context) {
426
+ const org = await this.getOrg(id);
427
+ if (!org) {
428
+ throw new CustomError(404, 'Org not found', { id });
429
+ }
430
+ const { user } = context || {};
431
+ // dashboard 或者非 org owner 无法获取可邀请的用户列表
432
+ if (isAdmingPath(context) || !isOrgOwner(user, org)) {
433
+ throw new CustomError(403, "You cannot access other user's org");
434
+ }
435
+ const { search } = query || {};
436
+
437
+ const newPaging = formatPaging(paging);
438
+
439
+ const where = { approved: true };
440
+
441
+ if (search) {
442
+ if (search.length > 50) {
443
+ throw new CustomError(400, 'the length of search text should not more than 50');
444
+ } else {
445
+ const likeOp = this.model.sequelize.getDialect() === 'postgres' ? Op.iLike : Op.like;
446
+ where[Op.or] = [{ fullName: { [likeOp]: `%${search}%` } }, { email: { [likeOp]: `%${search}%` } }];
447
+ }
448
+ }
449
+
450
+ const members = await this.userOrgs.find({ where: { orgId: id } });
451
+ const memberDids = members.map((item) => item.userDid);
452
+
453
+ if (memberDids.length) {
454
+ where.did = {
455
+ [Op.notIn]: memberDids,
456
+ };
457
+ }
458
+
459
+ const result = await this.user.paginate(
460
+ {
461
+ where,
462
+ attributes: ['did', 'avatar', 'fullName'],
463
+ },
464
+ { updatedAt: -1 },
465
+ newPaging
466
+ );
467
+
468
+ return {
469
+ users: result.list,
470
+ paging: result.paging,
471
+ };
472
+ }
473
+
474
+ /**
475
+ * 检测 resource 是否已经与 org 绑定
476
+ * @param {*} param0
477
+ * @returns
478
+ */
479
+ async getOrgResource({ orgId, resourceId }, context) {
480
+ const org = await this.getOrg(orgId);
481
+ if (!org) {
482
+ throw new CustomError(400, 'Org not found');
483
+ }
484
+
485
+ const { user } = context || {};
486
+ const userInOrg = await this.canAccessOrg({ userDid: user.did, orgId });
487
+ const isAdmin = (isAdmingPath(context) && isAdminUser(user)) || user.role === SERVER_ROLES.BLOCKLET_SDK;
488
+ if (!isAdmin && !isOrgOwner(user, org) && !userInOrg) {
489
+ throw new CustomError(403, "You cannot access resources under other users' org");
490
+ }
491
+
492
+ const where = { orgId };
493
+ if (resourceId) {
494
+ where.resourceId = resourceId;
495
+ }
496
+ return this.orgResource.find({ where });
497
+ }
498
+
499
+ /**
500
+ * 添加组织资源
501
+ */
502
+ async addOrgResource({ orgId, resourceIds = [], type, metadata = {} }, context) {
503
+ const org = await this.getOrg(orgId);
504
+ if (!org) {
505
+ throw new CustomError(404, 'Org not found', { orgId });
506
+ }
507
+ const { user } = context || {};
508
+ // dashboard 或者非 org owner 无法向 org 中添加资源
509
+ // blocklet-sdk 可以添加资源
510
+ if (user.role !== SERVER_ROLES.BLOCKLET_SDK && (isAdmingPath(context) || !isOrgOwner(user, org))) {
511
+ throw new CustomError(403, "You cannot add resources to other users' org");
512
+ }
513
+
514
+ const success = [];
515
+ const failed = [];
516
+
517
+ for (const resourceId of resourceIds) {
518
+ try {
519
+ // 检查资源是否已经存在
520
+ // eslint-disable-next-line no-await-in-loop
521
+ const resources = await this.getOrgResource({ orgId, resourceId }, context);
522
+
523
+ if (resources.length > 0) {
524
+ logger.warn('Resource already exists in org', { orgId, resourceId });
525
+ failed.push(resourceId);
526
+ continue;
527
+ }
528
+
529
+ const instance = {
530
+ orgId,
531
+ resourceId,
532
+ type,
533
+ metadata,
534
+ };
535
+ // eslint-disable-next-line no-await-in-loop
536
+ await this.orgResource.insert(instance);
537
+ success.push(resourceId);
538
+ } catch (error) {
539
+ logger.error('Failed to add resource to the org', { error, orgId, resourceId });
540
+ failed.push(resourceId);
541
+ }
542
+ }
543
+ return {
544
+ success,
545
+ failed,
546
+ };
547
+ }
548
+
549
+ /**
550
+ * 将资源从 org 中移除
551
+ */
552
+ async removeOrgResource({ orgId, resourceIds = [] }, context) {
553
+ const org = await this.getOrg(orgId);
554
+ if (!org) {
555
+ throw new CustomError(404, 'Org not found', { orgId });
556
+ }
557
+ const { user } = context || {};
558
+ // dashboard 或者非 org owner 无法向 org 中添加资源
559
+ // blocklet-sdk 可以移除资源
560
+ if (user.role !== SERVER_ROLES.BLOCKLET_SDK && (isAdmingPath(context) || !isOrgOwner(user, org))) {
561
+ throw new CustomError(403, "You cannot remove resources from other users' org");
562
+ }
563
+ return this.orgResource.remove({ where: { orgId, resourceId: { [Op.in]: resourceIds } } });
564
+ }
565
+
566
+ /**
567
+ * 迁移资源到新的 org
568
+ */
569
+ async migrateOrgResource({ from, to, resourceIds = [] }, context) {
570
+ // 资源与 org 的关系时多对多: 一个资源可以属于多个 org, 一个 org 可以有多个资源
571
+ // 如果没有 from,无法确定从哪里迁出
572
+ if (!from || !to) {
573
+ throw new CustomError(400, 'Both from and to parameters are required', { from, to });
574
+ }
575
+ const org = await this.getOrg(to);
576
+ if (!org) {
577
+ throw new CustomError(404, 'Org not found', { to });
578
+ }
579
+
580
+ const { user } = context || {};
581
+ // dashboard 或者非 org owner 无法迁移资源
582
+ // blocklet-sdk 可以迁移资源
583
+ if (user.role !== SERVER_ROLES.BLOCKLET_SDK && (isAdmingPath(context) || !isOrgOwner(user, org))) {
584
+ throw new CustomError(403, "You cannot migrate resources to other users' org");
585
+ }
586
+
587
+ if (!Array.isArray(resourceIds) || resourceIds.length === 0) {
588
+ return { success: [], failed: [] };
589
+ }
590
+
591
+ const success = [];
592
+ const failed = [];
593
+
594
+ for (const resourceId of resourceIds) {
595
+ try {
596
+ // 如果资源已经绑定到源组织,更新到目标组织
597
+ // eslint-disable-next-line no-await-in-loop
598
+ const resources = await this.getOrgResource({ orgId: from, resourceId }, context);
599
+
600
+ if (resources.length > 0) {
601
+ // eslint-disable-next-line no-await-in-loop
602
+ await this.orgResource.update(
603
+ { orgId: from, resourceId },
604
+ {
605
+ $set: {
606
+ orgId: to,
607
+ },
608
+ }
609
+ );
610
+ } else {
611
+ // 如果完全没有绑定,创建新的绑定
612
+ // eslint-disable-next-line no-await-in-loop
613
+ await this.orgResource.insert({
614
+ orgId: to,
615
+ resourceId,
616
+ });
617
+ }
618
+
619
+ success.push(resourceId);
620
+ } catch (error) {
621
+ logger.error('Failed to migrate resource to org', { error, from, to, resourceId });
622
+ failed.push(resourceId);
623
+ }
624
+ }
625
+
626
+ return {
627
+ success,
628
+ failed,
629
+ };
630
+ }
631
+
632
+ revokeUserPassportsByRoles({ roles = [], userDid = '' }) {
633
+ if (roles.length === 0) {
634
+ return [];
635
+ }
636
+ const names = new Set(roles.map((x) => x.name));
637
+ const where = { name: { [Op.in]: Array.from(names) } };
638
+ if (userDid) {
639
+ where.userDid = userDid;
640
+ }
641
+ return this.passport.update({ where }, { $set: { status: PASSPORT_STATUS.REVOKED } });
642
+ }
643
+
644
+ /**
645
+ * 移除组织相关的数据
646
+ */
647
+ async removeOrgRelatedData({ roles = [], orgId, userDid = '' }) {
648
+ try {
649
+ const result = await Promise.allSettled([
650
+ this.revokeUserPassportsByRoles({ roles, userDid }),
651
+ this.removeInvitation({ orgId, userDid }),
652
+ ]);
653
+ return result;
654
+ } catch (error) {
655
+ logger.error('Failed to remove org related data', { error, roles, orgId });
656
+ throw error;
657
+ }
658
+ }
659
+ }
660
+
661
+ module.exports = OrgState;
@@ -58,6 +58,7 @@ const States = {
58
58
  OauthCode: require('../states/oauth-code'),
59
59
 
60
60
  AccessKey: require('../states/access-key'),
61
+ Org: require('../states/org'),
61
62
  };
62
63
 
63
64
  const getDefaultTeamState = () => ({
@@ -83,6 +84,7 @@ const getDefaultTeamState = () => ({
83
84
  webhookEvent: null,
84
85
 
85
86
  accessKey: null,
87
+ org: null,
86
88
  });
87
89
 
88
90
  class TeamManager extends EventEmitter {
@@ -158,6 +160,7 @@ class TeamManager extends EventEmitter {
158
160
  webhookEvent: await this.createState(this.nodeDid, 'WebhookEvent'),
159
161
 
160
162
  accessKey: await this.createState(this.nodeDid, 'AccessKey'),
163
+ Org: await this.createState(this.nodeDid, 'Org'),
161
164
  };
162
165
  }
163
166
 
@@ -197,6 +200,10 @@ class TeamManager extends EventEmitter {
197
200
  return this.getState(teamDid, 'accessKey');
198
201
  }
199
202
 
203
+ getOrgState(teamDid) {
204
+ return this.getState(teamDid, 'org');
205
+ }
206
+
200
207
  async getNotificationState(teamDid) {
201
208
  const state = await this.getState(teamDid ?? this.nodeDid, 'notification');
202
209
  state.setDefaultSender(this.nodeDid);
@@ -805,10 +812,10 @@ class TeamManager extends EventEmitter {
805
812
  return owner;
806
813
  }
807
814
 
808
- async getRoles(did) {
815
+ async getRoles(did, orgId) {
809
816
  const rbac = await this.getRBAC(did);
810
- const roles = await rbac.getRoles();
811
- return roles.map((d) => pick(d, ['name', 'grants', 'title', 'description', 'extra']));
817
+ const roles = await rbac.getRoles(orgId);
818
+ return roles.map((d) => pick(d, ['name', 'grants', 'title', 'description', 'extra', 'orgId']));
812
819
  }
813
820
 
814
821
  async initTeam(did) {