@abtnode/core 1.17.8-beta-20260109-075740-5f484e08 → 1.17.8-beta-20260113-015027-32a1cec4
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/lib/api/team/access-key-manager.js +104 -0
- package/lib/api/team/invitation-manager.js +461 -0
- package/lib/api/team/notification-manager.js +189 -0
- package/lib/api/team/oauth-manager.js +60 -0
- package/lib/api/team/org-crud-manager.js +202 -0
- package/lib/api/team/org-manager.js +56 -0
- package/lib/api/team/org-member-manager.js +403 -0
- package/lib/api/team/org-query-manager.js +126 -0
- package/lib/api/team/org-resource-manager.js +186 -0
- package/lib/api/team/passport-manager.js +670 -0
- package/lib/api/team/rbac-manager.js +335 -0
- package/lib/api/team/session-manager.js +540 -0
- package/lib/api/team/store-manager.js +198 -0
- package/lib/api/team/tag-manager.js +230 -0
- package/lib/api/team/user-auth-manager.js +132 -0
- package/lib/api/team/user-manager.js +78 -0
- package/lib/api/team/user-query-manager.js +299 -0
- package/lib/api/team/user-social-manager.js +354 -0
- package/lib/api/team/user-update-manager.js +224 -0
- package/lib/api/team/verify-code-manager.js +161 -0
- package/lib/api/team.js +439 -3287
- package/lib/blocklet/manager/disk/auth-manager.js +68 -0
- package/lib/blocklet/manager/disk/backup-manager.js +288 -0
- package/lib/blocklet/manager/disk/cleanup-manager.js +157 -0
- package/lib/blocklet/manager/disk/component-manager.js +83 -0
- package/lib/blocklet/manager/disk/config-manager.js +191 -0
- package/lib/blocklet/manager/disk/controller-manager.js +64 -0
- package/lib/blocklet/manager/disk/delete-reset-manager.js +328 -0
- package/lib/blocklet/manager/disk/download-manager.js +96 -0
- package/lib/blocklet/manager/disk/env-config-manager.js +311 -0
- package/lib/blocklet/manager/disk/federated-manager.js +651 -0
- package/lib/blocklet/manager/disk/hook-manager.js +124 -0
- package/lib/blocklet/manager/disk/install-component-manager.js +95 -0
- package/lib/blocklet/manager/disk/install-core-manager.js +448 -0
- package/lib/blocklet/manager/disk/install-download-manager.js +313 -0
- package/lib/blocklet/manager/disk/install-manager.js +36 -0
- package/lib/blocklet/manager/disk/install-upgrade-manager.js +340 -0
- package/lib/blocklet/manager/disk/job-manager.js +467 -0
- package/lib/blocklet/manager/disk/lifecycle-manager.js +26 -0
- package/lib/blocklet/manager/disk/notification-manager.js +343 -0
- package/lib/blocklet/manager/disk/query-manager.js +562 -0
- package/lib/blocklet/manager/disk/settings-manager.js +507 -0
- package/lib/blocklet/manager/disk/start-manager.js +611 -0
- package/lib/blocklet/manager/disk/stop-restart-manager.js +292 -0
- package/lib/blocklet/manager/disk/update-manager.js +153 -0
- package/lib/blocklet/manager/disk.js +669 -5796
- package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +5 -0
- package/lib/blocklet/manager/lock.js +18 -0
- package/lib/event/index.js +28 -24
- package/lib/util/blocklet/app-utils.js +192 -0
- package/lib/util/blocklet/blocklet-loader.js +258 -0
- package/lib/util/blocklet/config-manager.js +232 -0
- package/lib/util/blocklet/did-document.js +240 -0
- package/lib/util/blocklet/environment.js +555 -0
- package/lib/util/blocklet/health-check.js +449 -0
- package/lib/util/blocklet/install-utils.js +365 -0
- package/lib/util/blocklet/logo.js +57 -0
- package/lib/util/blocklet/meta-utils.js +269 -0
- package/lib/util/blocklet/port-manager.js +141 -0
- package/lib/util/blocklet/process-manager.js +504 -0
- package/lib/util/blocklet/runtime-info.js +105 -0
- package/lib/util/blocklet/validation.js +418 -0
- package/lib/util/blocklet.js +98 -3066
- package/lib/util/wallet-app-notification.js +40 -0
- package/package.json +22 -22
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
const { joinURL } = require('ufo');
|
|
2
|
+
const logger = require('@abtnode/logger')('@abtnode/core:api:team:org-member');
|
|
3
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
4
|
+
const { CustomError } = require('@blocklet/error');
|
|
5
|
+
const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
|
|
6
|
+
const { getBlockletInfo } = require('@blocklet/meta/lib/info');
|
|
7
|
+
const { getWalletDid } = require('@blocklet/meta/lib/did-utils');
|
|
8
|
+
const { Joi } = require('@arcblock/validator');
|
|
9
|
+
const { getEmailServiceProvider } = require('@abtnode/auth/lib/email');
|
|
10
|
+
|
|
11
|
+
const { getOrgInviteLink, isOrgOwner, isAdmingPath } = require('../../util/org');
|
|
12
|
+
const { getBlocklet } = require('../../util/blocklet');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Add org member
|
|
16
|
+
* @param {Object} api - TeamAPI instance
|
|
17
|
+
* @param {Object} params
|
|
18
|
+
* @param {string} params.teamDid - Team DID
|
|
19
|
+
* @param {string} params.orgId - Org ID
|
|
20
|
+
* @param {string} params.userDid - User DID
|
|
21
|
+
* @param {Object} context
|
|
22
|
+
* @returns {Promise<Object>}
|
|
23
|
+
*/
|
|
24
|
+
async function addOrgMember(api, { teamDid, orgId, userDid }, context) {
|
|
25
|
+
try {
|
|
26
|
+
const state = await api.getOrgState(teamDid);
|
|
27
|
+
return state.addOrgMember({ orgId, userDid }, context);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
logger.error('Add member to org failed', { err, teamDid, orgId, userDid });
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Update org member
|
|
36
|
+
* @param {Object} api - TeamAPI instance
|
|
37
|
+
* @param {Object} params
|
|
38
|
+
* @param {string} params.teamDid - Team DID
|
|
39
|
+
* @param {string} params.orgId - Org ID
|
|
40
|
+
* @param {string} params.userDid - User DID
|
|
41
|
+
* @param {string} params.status - Status
|
|
42
|
+
* @param {Object} context
|
|
43
|
+
* @returns {Promise<Object>}
|
|
44
|
+
*/
|
|
45
|
+
async function updateOrgMember(api, { teamDid, orgId, userDid, status }, context) {
|
|
46
|
+
try {
|
|
47
|
+
const state = await api.getOrgState(teamDid);
|
|
48
|
+
return state.updateOrgMember({ orgId, userDid, status }, context);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
logger.error('Update member in org failed', { err, teamDid, orgId, userDid, status });
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Send invitation notification
|
|
57
|
+
* @param {Object} api - TeamAPI instance
|
|
58
|
+
* @param {Object} params
|
|
59
|
+
* @returns {Promise<void>}
|
|
60
|
+
*/
|
|
61
|
+
async function sendInvitationNotification(
|
|
62
|
+
api,
|
|
63
|
+
{ teamDid, invitor, org, role, successUserDids, inviteLink, email, inviteType, blocklet }
|
|
64
|
+
) {
|
|
65
|
+
try {
|
|
66
|
+
const userInfo = await api.getUser({
|
|
67
|
+
teamDid,
|
|
68
|
+
user: { did: invitor.did },
|
|
69
|
+
options: { enableConnectedAccount: true },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// 检测是否开启了 email 服务
|
|
73
|
+
const provider = getEmailServiceProvider(blocklet);
|
|
74
|
+
|
|
75
|
+
const translate = {
|
|
76
|
+
en: {
|
|
77
|
+
title: 'Inviting you to join the organization',
|
|
78
|
+
description: `<${userInfo.fullName}(did:abt:${userInfo.did})> invites you to join the ${org.name} organization, role is ${role}.<br/>After accepting, you will be able to access the resources and collaboration content of the organization.<br/><br/>Please click the button below to handle the invitation.<br/><br/>If you don't want to join, you can ignore this notification.`,
|
|
79
|
+
accept: 'Accept',
|
|
80
|
+
},
|
|
81
|
+
zh: {
|
|
82
|
+
title: '邀请您加入组织',
|
|
83
|
+
description: `<${userInfo.fullName}(did:abt:${userInfo.did})> 邀请您加入 ${org.name} 组织,角色为 ${role}。<br/>接受后,你将能够访问该组织的资源和协作内容。<br/><br/>请点击下方按钮处理邀请。<br/><br/>如果你不想加入,可以忽略此通知。`,
|
|
84
|
+
accept: '接受',
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
const content = translate[userInfo.locale || 'en'] || translate.en;
|
|
88
|
+
|
|
89
|
+
const message = {
|
|
90
|
+
title: content.title,
|
|
91
|
+
body: content.description,
|
|
92
|
+
actions: [
|
|
93
|
+
{
|
|
94
|
+
name: content.accept,
|
|
95
|
+
title: content.accept,
|
|
96
|
+
link: inviteLink,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
if (inviteType === 'internal') {
|
|
102
|
+
await api.createNotification({
|
|
103
|
+
teamDid,
|
|
104
|
+
receiver: successUserDids,
|
|
105
|
+
entityId: teamDid,
|
|
106
|
+
source: 'system',
|
|
107
|
+
severity: 'info',
|
|
108
|
+
...message,
|
|
109
|
+
});
|
|
110
|
+
logger.info('Invite notification sent successfully', {
|
|
111
|
+
teamDid,
|
|
112
|
+
orgId: org.id,
|
|
113
|
+
sentToUsers: successUserDids,
|
|
114
|
+
sentCount: successUserDids.length,
|
|
115
|
+
});
|
|
116
|
+
} else if (inviteType === 'external' && provider && email) {
|
|
117
|
+
// 当 service 开启 email 服务时才会发送邮件通知
|
|
118
|
+
const emailInputSchema = Joi.string().email().required();
|
|
119
|
+
const { error } = emailInputSchema.validate(email);
|
|
120
|
+
if (error) {
|
|
121
|
+
throw new CustomError(400, error.message);
|
|
122
|
+
}
|
|
123
|
+
const nodeInfo = await api.node.read();
|
|
124
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
125
|
+
const sender = {
|
|
126
|
+
appDid: blockletInfo.wallet.address,
|
|
127
|
+
appSk: blockletInfo.wallet.secretKey,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
await sendToUser(email, message, sender, undefined, 'send-to-mail');
|
|
131
|
+
logger.info('Send invitation notification to email completed', {
|
|
132
|
+
teamDid,
|
|
133
|
+
orgId: org.id,
|
|
134
|
+
email,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
} catch (notificationErr) {
|
|
138
|
+
// 通知发送失败不影响邀请的成功,只记录警告
|
|
139
|
+
logger.warn('Failed to send invitation notification, but invitations were created successfully', {
|
|
140
|
+
notificationErr,
|
|
141
|
+
teamDid,
|
|
142
|
+
orgId: org.id,
|
|
143
|
+
successUserDids,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get federated master blocklet info
|
|
150
|
+
* @param {Object} params
|
|
151
|
+
* @param {Object} params.blocklet - Blocklet
|
|
152
|
+
* @returns {Object|undefined}
|
|
153
|
+
*/
|
|
154
|
+
function getFederatedMasterBlockletInfo({ blocklet }) {
|
|
155
|
+
const sites = blocklet.settings?.federated?.sites || [];
|
|
156
|
+
const federatedMaster = sites.find((item) => item.isMaster !== false);
|
|
157
|
+
const federatedCurrent = sites.find((item) => item.appId === blocklet.appDid);
|
|
158
|
+
const isFederated = !!federatedMaster || !!federatedCurrent;
|
|
159
|
+
if (!isFederated) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const formatBlockletInfo = (federateBlocklet) => ({
|
|
164
|
+
appId: federateBlocklet?.appId,
|
|
165
|
+
appName: federateBlocklet?.appName,
|
|
166
|
+
appDescription: federateBlocklet?.appDescription,
|
|
167
|
+
appLogo: federateBlocklet?.appLogo,
|
|
168
|
+
appPid: federateBlocklet?.appPid,
|
|
169
|
+
appUrl: federateBlocklet?.appUrl,
|
|
170
|
+
version: federateBlocklet?.version,
|
|
171
|
+
sourceAppPid: federateBlocklet?.appPid,
|
|
172
|
+
provider: 'wallet',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const blocklets = [];
|
|
176
|
+
if (federatedCurrent?.status === 'approved') {
|
|
177
|
+
blocklets.push(formatBlockletInfo(federatedMaster));
|
|
178
|
+
}
|
|
179
|
+
if (federatedCurrent) {
|
|
180
|
+
blocklets.push({
|
|
181
|
+
...formatBlockletInfo(federatedCurrent),
|
|
182
|
+
sourceAppPid: null,
|
|
183
|
+
});
|
|
184
|
+
} else {
|
|
185
|
+
const blockletInfo = getBlockletInfo(blocklet, undefined, { returnWallet: false });
|
|
186
|
+
blocklets.push({
|
|
187
|
+
...formatBlockletInfo(blockletInfo),
|
|
188
|
+
appPid: blocklet?.appPid,
|
|
189
|
+
appLogo: joinURL(blockletInfo.appUrl, WELLKNOWN_SERVICE_PATH_PREFIX, '/blocklet/logo'),
|
|
190
|
+
sourceAppPid: null,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return blocklets[0];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get org (internal helper - imported from org-query-manager to avoid circular dependency)
|
|
199
|
+
* @param {Object} api - TeamAPI instance
|
|
200
|
+
* @param {Object} params
|
|
201
|
+
* @param {string} params.teamDid - Team DID
|
|
202
|
+
* @param {string} params.id - Org ID
|
|
203
|
+
* @param {Object} context
|
|
204
|
+
* @returns {Promise<Object>}
|
|
205
|
+
*/
|
|
206
|
+
async function _getOrg(api, { teamDid, id }, context) {
|
|
207
|
+
const state = await api.getOrgState(teamDid);
|
|
208
|
+
return state.get({ id }, context);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Invite members to org
|
|
213
|
+
* @param {Object} api - TeamAPI instance
|
|
214
|
+
* @param {Object} params
|
|
215
|
+
* @param {string} params.teamDid - Team DID
|
|
216
|
+
* @param {string} params.orgId - Org ID
|
|
217
|
+
* @param {Array} params.userDids - User DIDs
|
|
218
|
+
* @param {string} params.role - Role
|
|
219
|
+
* @param {string} params.inviteType - Invite type
|
|
220
|
+
* @param {string} params.email - Email
|
|
221
|
+
* @param {Object} context
|
|
222
|
+
* @returns {Promise<Object>}
|
|
223
|
+
*/
|
|
224
|
+
async function inviteMembersToOrg(api, { teamDid, orgId, userDids, role, inviteType = 'internal', email }, context) {
|
|
225
|
+
try {
|
|
226
|
+
const state = await api.getOrgState(teamDid);
|
|
227
|
+
const { user } = context || {};
|
|
228
|
+
if (!user) {
|
|
229
|
+
throw new CustomError(400, 'User is required');
|
|
230
|
+
}
|
|
231
|
+
const org = await _getOrg(api, { teamDid, id: orgId }, context);
|
|
232
|
+
if (!org) {
|
|
233
|
+
throw new CustomError(400, 'Org not found');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// dashboard 或者非 org owner 无法邀请成员
|
|
237
|
+
if (isAdmingPath(context) || !isOrgOwner(user, org)) {
|
|
238
|
+
throw new CustomError(403, "You cannot invite members to other users' org");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (inviteType === 'internal' && userDids.length === 0) {
|
|
242
|
+
throw new CustomError(400, 'You must invite at least one user');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Step 1: 批量添加成员到组织 - 记录添加成功和添加失败的用户 DID
|
|
246
|
+
const successUserDids = [];
|
|
247
|
+
const failedUserDids = [];
|
|
248
|
+
const skipInviteUserDids = [];
|
|
249
|
+
|
|
250
|
+
// 内部邀请
|
|
251
|
+
if (inviteType === 'internal') {
|
|
252
|
+
for (const userDid of userDids) {
|
|
253
|
+
try {
|
|
254
|
+
// eslint-disable-next-line no-await-in-loop
|
|
255
|
+
const currentUser = await api.getUser({
|
|
256
|
+
teamDid,
|
|
257
|
+
user: { did: userDid },
|
|
258
|
+
});
|
|
259
|
+
const walletDid = getWalletDid(currentUser);
|
|
260
|
+
// 如果当前用户不是钱包用户,则跳过邀请 (参考颁发通行证逻辑)
|
|
261
|
+
const skipInvite = walletDid !== userDid;
|
|
262
|
+
if (skipInvite) {
|
|
263
|
+
skipInviteUserDids.push(userDid);
|
|
264
|
+
}
|
|
265
|
+
const status = skipInvite ? 'active' : 'inviting';
|
|
266
|
+
// eslint-disable-next-line no-await-in-loop
|
|
267
|
+
await state.addOrgMember({ orgId, userDid, status }, context);
|
|
268
|
+
successUserDids.push(userDid);
|
|
269
|
+
} catch (addErr) {
|
|
270
|
+
failedUserDids.push(userDid);
|
|
271
|
+
logger.warn('Failed to add user to org', { userDid, orgId, error: addErr.message });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
logger.info('Batch add members to org completed', {
|
|
276
|
+
teamDid,
|
|
277
|
+
orgId,
|
|
278
|
+
totalUsers: userDids.length,
|
|
279
|
+
successCount: successUserDids.length,
|
|
280
|
+
failedCount: failedUserDids.length,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// 如果没有成功添加的用户,直接返回结果
|
|
284
|
+
if (successUserDids.length === 0) {
|
|
285
|
+
logger.warn('No users were successfully added to org', { teamDid, orgId, failedUserDids });
|
|
286
|
+
throw new CustomError(500, 'No users were successfully added to org');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// 要排除 OAuth 用户
|
|
291
|
+
const inviteUserDids = successUserDids.filter((did) => !skipInviteUserDids.includes(did));
|
|
292
|
+
|
|
293
|
+
if (skipInviteUserDids.length > 0) {
|
|
294
|
+
logger.info('OAuth users were successfully added to org', { teamDid, orgId, skipInviteUserDids });
|
|
295
|
+
// 向 OAuth 用户颁发通行证
|
|
296
|
+
for (const userDid of skipInviteUserDids) {
|
|
297
|
+
// eslint-disable-next-line no-await-in-loop
|
|
298
|
+
await api.issuePassportToUser({
|
|
299
|
+
teamDid,
|
|
300
|
+
userDid,
|
|
301
|
+
role,
|
|
302
|
+
notification: {},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// 如果没有其他用户,那么直接返回即可
|
|
308
|
+
if (inviteType === 'internal' && inviteUserDids.length === 0) {
|
|
309
|
+
return {
|
|
310
|
+
successDids: successUserDids,
|
|
311
|
+
failedDids: failedUserDids,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Previous Step 2: 要判断站点是否是站点群内,如果是站点群中,需要使用 master 的 appPid 作为 sourceAppPid 创建邀请链接
|
|
316
|
+
|
|
317
|
+
const blocklet = await getBlocklet({ did: teamDid, states: api.states, dataDirs: api.dataDirs });
|
|
318
|
+
const masterBlockletInfo = getFederatedMasterBlockletInfo({ blocklet });
|
|
319
|
+
|
|
320
|
+
// Step 2: 创建邀请链接,只创建添加成功的用户邀请链接
|
|
321
|
+
|
|
322
|
+
const inviteInfo = await api.createMemberInvitation(
|
|
323
|
+
{
|
|
324
|
+
teamDid,
|
|
325
|
+
role,
|
|
326
|
+
expireTime: api.memberInvitationExpireTime,
|
|
327
|
+
orgId,
|
|
328
|
+
inviteUserDids: inviteType === 'internal' ? inviteUserDids : [],
|
|
329
|
+
sourceAppPid: masterBlockletInfo?.appPid,
|
|
330
|
+
},
|
|
331
|
+
context
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
const inviteLink = getOrgInviteLink(inviteInfo, blocklet);
|
|
335
|
+
|
|
336
|
+
logger.info('Invite link created for successful users', {
|
|
337
|
+
teamDid,
|
|
338
|
+
orgId,
|
|
339
|
+
inviteId: inviteInfo.inviteId,
|
|
340
|
+
userCount: successUserDids.length,
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Step 3: 发送邀请通知,只发送添加成功的用户邀请通知
|
|
344
|
+
|
|
345
|
+
sendInvitationNotification(api, {
|
|
346
|
+
teamDid,
|
|
347
|
+
invitor: user,
|
|
348
|
+
org,
|
|
349
|
+
role,
|
|
350
|
+
successUserDids,
|
|
351
|
+
email,
|
|
352
|
+
inviteType,
|
|
353
|
+
inviteLink,
|
|
354
|
+
blocklet,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// 返回成功和失败的用户列表
|
|
358
|
+
return {
|
|
359
|
+
successDids: successUserDids,
|
|
360
|
+
failedDids: failedUserDids,
|
|
361
|
+
inviteLink,
|
|
362
|
+
};
|
|
363
|
+
} catch (err) {
|
|
364
|
+
logger.error('Invite users to org failed', { err, teamDid, orgId, userDids, role });
|
|
365
|
+
throw err;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Remove org member
|
|
371
|
+
* @param {Object} api - TeamAPI instance
|
|
372
|
+
* @param {Object} params
|
|
373
|
+
* @param {string} params.teamDid - Team DID
|
|
374
|
+
* @param {string} params.orgId - Org ID
|
|
375
|
+
* @param {string} params.userDid - User DID
|
|
376
|
+
* @param {Object} context
|
|
377
|
+
* @returns {Promise<Object>}
|
|
378
|
+
*/
|
|
379
|
+
async function removeOrgMember(api, { teamDid, orgId, userDid }, context) {
|
|
380
|
+
try {
|
|
381
|
+
const state = await api.getOrgState(teamDid);
|
|
382
|
+
const roles = await api.getRoles({ teamDid, orgId });
|
|
383
|
+
const result = await state.removeOrgMember({ orgId, userDid }, context);
|
|
384
|
+
try {
|
|
385
|
+
await state.removeOrgRelatedData({ roles, orgId, userDid });
|
|
386
|
+
} catch (err) {
|
|
387
|
+
logger.error('Failed to remove user related passports', { err, teamDid, roles, userDid });
|
|
388
|
+
}
|
|
389
|
+
return result;
|
|
390
|
+
} catch (err) {
|
|
391
|
+
logger.error('Remove member from org failed', { err, teamDid, orgId, userDid });
|
|
392
|
+
throw err;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
module.exports = {
|
|
397
|
+
addOrgMember,
|
|
398
|
+
updateOrgMember,
|
|
399
|
+
sendInvitationNotification,
|
|
400
|
+
getFederatedMasterBlockletInfo,
|
|
401
|
+
inviteMembersToOrg,
|
|
402
|
+
removeOrgMember,
|
|
403
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const logger = require('@abtnode/logger')('@abtnode/core:api:team:org-query');
|
|
2
|
+
const { getUserAvatarUrl } = require('@abtnode/util/lib/user');
|
|
3
|
+
|
|
4
|
+
const { getBlocklet } = require('../../util/blocklet');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get orgs
|
|
8
|
+
* @param {Object} api - TeamAPI instance
|
|
9
|
+
* @param {Object} params
|
|
10
|
+
* @param {string} params.teamDid - Team DID
|
|
11
|
+
* @param {Object} context
|
|
12
|
+
* @returns {Promise<Object>}
|
|
13
|
+
*/
|
|
14
|
+
async function getOrgs(api, { teamDid, ...payload }, context) {
|
|
15
|
+
try {
|
|
16
|
+
const state = await api.getOrgState(teamDid);
|
|
17
|
+
const { passports, orgs, ...rest } = await state.list(payload, context);
|
|
18
|
+
const { includePassports = true } = payload.options || {};
|
|
19
|
+
if (includePassports) {
|
|
20
|
+
// 获取每个组织的 passports
|
|
21
|
+
const orgPassports = await Promise.all(orgs.map((o) => api.getRoles({ teamDid, orgId: o.id })));
|
|
22
|
+
|
|
23
|
+
orgs.forEach((o, index) => {
|
|
24
|
+
const roles = orgPassports[index]; // 获取每个组织的角色
|
|
25
|
+
// 过滤 passports
|
|
26
|
+
o.passports = passports.filter((p) => roles.some((r) => r.name === p.name));
|
|
27
|
+
});
|
|
28
|
+
} else {
|
|
29
|
+
orgs.forEach((o) => {
|
|
30
|
+
o.passports = [];
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
...rest,
|
|
36
|
+
orgs,
|
|
37
|
+
};
|
|
38
|
+
} catch (err) {
|
|
39
|
+
logger.error('Failed to get orgs', { err, teamDid });
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get org
|
|
46
|
+
* @param {Object} api - TeamAPI instance
|
|
47
|
+
* @param {Object} params
|
|
48
|
+
* @param {string} params.teamDid - Team DID
|
|
49
|
+
* @param {string} params.id - Org ID
|
|
50
|
+
* @param {Object} context
|
|
51
|
+
* @returns {Promise<Object>}
|
|
52
|
+
*/
|
|
53
|
+
async function getOrg(api, { teamDid, id }, context) {
|
|
54
|
+
try {
|
|
55
|
+
const state = await api.getOrgState(teamDid);
|
|
56
|
+
return state.get({ id }, context);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
logger.error('Failed to get org', { err, teamDid, id });
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get org members
|
|
65
|
+
* @param {Object} api - TeamAPI instance
|
|
66
|
+
* @param {Object} params
|
|
67
|
+
* @param {string} params.teamDid - Team DID
|
|
68
|
+
* @param {string} params.orgId - Org ID
|
|
69
|
+
* @param {Object} params.paging - Paging
|
|
70
|
+
* @param {Object} context
|
|
71
|
+
* @returns {Promise<Object>}
|
|
72
|
+
*/
|
|
73
|
+
async function getOrgMembers(api, { teamDid, orgId, paging }, context) {
|
|
74
|
+
try {
|
|
75
|
+
const state = await api.getOrgState(teamDid);
|
|
76
|
+
const result = await state.getOrgMembers({ orgId, paging, options: { includePassport: true } }, context);
|
|
77
|
+
const info = await api.node.read();
|
|
78
|
+
const isServer = api.teamManager.isNodeTeam(teamDid);
|
|
79
|
+
const blocklet = await getBlocklet({ did: teamDid, states: api.states, dataDirs: api.dataDirs });
|
|
80
|
+
const baseUrl = blocklet.environmentObj.BLOCKLET_APP_URL;
|
|
81
|
+
const roles = await api.getRoles({ teamDid, orgId });
|
|
82
|
+
result.users.forEach((item) => {
|
|
83
|
+
if (item?.user?.avatar) {
|
|
84
|
+
item.user.avatar = getUserAvatarUrl(baseUrl, item.user.avatar, info, isServer);
|
|
85
|
+
}
|
|
86
|
+
if (item?.user?.passports?.length > 0) {
|
|
87
|
+
item.user.passports = item.user.passports.filter((passport) =>
|
|
88
|
+
roles.some((roleItem) => roleItem.name === passport.name)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
} catch (err) {
|
|
95
|
+
logger.error('Get org members failed', { err, teamDid, orgId });
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get org invitable users
|
|
102
|
+
* @param {Object} api - TeamAPI instance
|
|
103
|
+
* @param {Object} params
|
|
104
|
+
* @param {string} params.teamDid - Team DID
|
|
105
|
+
* @param {string} params.id - Org ID
|
|
106
|
+
* @param {string} params.query - Query
|
|
107
|
+
* @param {Object} params.paging - Paging
|
|
108
|
+
* @param {Object} context
|
|
109
|
+
* @returns {Promise<Object>}
|
|
110
|
+
*/
|
|
111
|
+
async function getOrgInvitableUsers(api, { teamDid, id, query, paging }, context) {
|
|
112
|
+
try {
|
|
113
|
+
const state = await api.getOrgState(teamDid);
|
|
114
|
+
return state.getOrgInvitableUsers({ id, query, paging }, context);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
logger.error('Get org invitable users failed', { err, teamDid, id });
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
getOrgs,
|
|
123
|
+
getOrg,
|
|
124
|
+
getOrgMembers,
|
|
125
|
+
getOrgInvitableUsers,
|
|
126
|
+
};
|