@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,651 @@
|
|
|
1
|
+
const pick = require('lodash/pick');
|
|
2
|
+
const remove = require('lodash/remove');
|
|
3
|
+
const isNil = require('lodash/isNil');
|
|
4
|
+
const uniqBy = require('lodash/uniqBy');
|
|
5
|
+
const { joinURL } = require('ufo');
|
|
6
|
+
const pLimit = require('p-limit');
|
|
7
|
+
const pRetry = require('p-retry');
|
|
8
|
+
const pMap = require('p-map');
|
|
9
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:federated');
|
|
10
|
+
const { getBlockletInfo } = require('@blocklet/meta/lib/info');
|
|
11
|
+
const { FEDERATED, USER_PROFILE_SYNC_FIELDS, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
12
|
+
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
13
|
+
const {
|
|
14
|
+
callFederated,
|
|
15
|
+
syncFederated: _syncFederated,
|
|
16
|
+
getFederatedMaster,
|
|
17
|
+
generateSiteInfo,
|
|
18
|
+
findFederatedSite,
|
|
19
|
+
safeGetFederated,
|
|
20
|
+
shouldSyncFederated,
|
|
21
|
+
getUserAvatarUrl,
|
|
22
|
+
} = require('@abtnode/auth/lib/util/federated');
|
|
23
|
+
const { BlockletEvents, BlockletInternalEvents } = require('@blocklet/constant');
|
|
24
|
+
|
|
25
|
+
const states = require('../../../states');
|
|
26
|
+
const request = require('../../../util/request');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Join federated login
|
|
30
|
+
* @param {Object} manager - BlockletManager instance
|
|
31
|
+
* @param {Object} params
|
|
32
|
+
* @param {Object} context
|
|
33
|
+
* @returns {Promise<Object>}
|
|
34
|
+
*/
|
|
35
|
+
async function joinFederatedLogin(manager, { appUrl, did }, context) {
|
|
36
|
+
const url = new URL(appUrl);
|
|
37
|
+
// master service api 的地址
|
|
38
|
+
url.pathname = joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/join');
|
|
39
|
+
|
|
40
|
+
const blocklet = await manager.getBlocklet(did);
|
|
41
|
+
const nodeInfo = await states.node.read();
|
|
42
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
43
|
+
const domainAliases = await manager.getDomainAliases({ blocklet, nodeInfo });
|
|
44
|
+
const { permanentWallet } = blockletInfo;
|
|
45
|
+
const memberSite = {
|
|
46
|
+
appId: blocklet.appDid,
|
|
47
|
+
appPid: blocklet.appPid,
|
|
48
|
+
aliasDid: (blocklet.migratedFrom || []).map((x) => x.appDid),
|
|
49
|
+
appName: blockletInfo.name,
|
|
50
|
+
appDescription: blockletInfo.description,
|
|
51
|
+
appUrl: blockletInfo.appUrl,
|
|
52
|
+
aliasDomain: domainAliases.map((x) => x.value),
|
|
53
|
+
appLogo:
|
|
54
|
+
blocklet.environmentObj.BLOCKLET_APP_LOGO ||
|
|
55
|
+
normalizePathPrefix(joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, '/blocklet/logo')) ||
|
|
56
|
+
'/',
|
|
57
|
+
appLogoRect: blocklet.environmentObj.BLOCKLET_APP_LOGO_RECT,
|
|
58
|
+
did: permanentWallet.address,
|
|
59
|
+
pk: permanentWallet.publicKey,
|
|
60
|
+
serverId: nodeInfo.did,
|
|
61
|
+
serverVersion: nodeInfo.version,
|
|
62
|
+
version: blockletInfo.version,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
logger.info('Apply to join federated login', {
|
|
66
|
+
memberSite: pick(memberSite, ['appId', 'appPid', 'appName', 'appDescription', 'appUrl']),
|
|
67
|
+
masterAppUrl: appUrl,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
let data;
|
|
71
|
+
try {
|
|
72
|
+
const result = await pRetry(
|
|
73
|
+
() =>
|
|
74
|
+
request.post(url.href, {
|
|
75
|
+
// 初次申请时,member 不在站点群中,不需要对数据进行加密
|
|
76
|
+
site: memberSite,
|
|
77
|
+
}),
|
|
78
|
+
{ retries: 3 }
|
|
79
|
+
);
|
|
80
|
+
data = result.data;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
const errorMsg = error?.response?.data;
|
|
83
|
+
logger.error('Failed to join federated login', { error, errorMsg, did, url: url.href });
|
|
84
|
+
throw new Error(errorMsg || 'Failed to join federated login');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await states.blockletExtras.setSettings(blocklet.appPid, {
|
|
88
|
+
federated: {
|
|
89
|
+
config: {
|
|
90
|
+
appId: blocklet.appDid,
|
|
91
|
+
appPid: blocklet.appPid,
|
|
92
|
+
isMaster: false,
|
|
93
|
+
},
|
|
94
|
+
sites: data.sites,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const newState = await manager.getBlocklet(did);
|
|
99
|
+
manager.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
100
|
+
manager.emit(BlockletEvents.updated, { ...newState, context });
|
|
101
|
+
return newState;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Quit federated login
|
|
106
|
+
* member 退出统一登录站点群
|
|
107
|
+
* @param {Object} manager - BlockletManager instance
|
|
108
|
+
* @param {Object} params
|
|
109
|
+
* @param {string} params.did - blocklet pid
|
|
110
|
+
* @param {string} [params.targetDid] - 要退出的目标 blocklet pid, 如果未设置,则代表退出自身
|
|
111
|
+
* @param {Object} context
|
|
112
|
+
* @returns {Promise<Object>}
|
|
113
|
+
*/
|
|
114
|
+
async function quitFederatedLogin(manager, { did, targetDid }, context) {
|
|
115
|
+
const blocklet = await manager.getBlocklet(did);
|
|
116
|
+
const masterSite = getFederatedMaster(blocklet);
|
|
117
|
+
if (masterSite) {
|
|
118
|
+
// master 可以指定删除一个 member
|
|
119
|
+
if (masterSite.appPid === did && targetDid && targetDid !== did) {
|
|
120
|
+
const memberSite = findFederatedSite(blocklet, targetDid);
|
|
121
|
+
|
|
122
|
+
if (memberSite) {
|
|
123
|
+
await manager.syncFederated({
|
|
124
|
+
did,
|
|
125
|
+
data: {
|
|
126
|
+
sites: [
|
|
127
|
+
{
|
|
128
|
+
...memberSite,
|
|
129
|
+
action: 'delete',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
try {
|
|
135
|
+
const nodeInfo = await states.node.read();
|
|
136
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
137
|
+
const { permanentWallet } = blockletInfo;
|
|
138
|
+
await callFederated({
|
|
139
|
+
action: 'disband',
|
|
140
|
+
permanentWallet,
|
|
141
|
+
site: memberSite,
|
|
142
|
+
data: {},
|
|
143
|
+
});
|
|
144
|
+
} catch (error) {
|
|
145
|
+
logger.error('Failed to disband memberSite', {
|
|
146
|
+
error,
|
|
147
|
+
did,
|
|
148
|
+
memberSite,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const federated = safeGetFederated(blocklet, { isMaster: true });
|
|
152
|
+
remove(federated.sites, (item) => item.appPid === targetDid);
|
|
153
|
+
await states.blockletExtras.setSettings(blocklet.appPid, { federated });
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
// member 向 mater 申请退出
|
|
157
|
+
const nodeInfo = await states.node.read();
|
|
158
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
159
|
+
const { permanentWallet } = blockletInfo;
|
|
160
|
+
logger.info('Quit federated login', {
|
|
161
|
+
memberSite: {
|
|
162
|
+
appId: blocklet.appDid,
|
|
163
|
+
appPid: blocklet.appPid,
|
|
164
|
+
appName: blockletInfo.name,
|
|
165
|
+
appDescription: blockletInfo.description,
|
|
166
|
+
appUrl: blockletInfo.appUrl,
|
|
167
|
+
},
|
|
168
|
+
masterAppUrl: masterSite.appUrl,
|
|
169
|
+
});
|
|
170
|
+
try {
|
|
171
|
+
await callFederated({
|
|
172
|
+
action: 'quit',
|
|
173
|
+
site: masterSite,
|
|
174
|
+
permanentWallet,
|
|
175
|
+
data: {
|
|
176
|
+
memberPid: blocklet.appPid,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
} catch (error) {
|
|
180
|
+
logger.error('Failed to quit blocklet, will still quit federated by itself', { error, did });
|
|
181
|
+
}
|
|
182
|
+
await states.blockletExtras.setSettings(blocklet.appPid, {
|
|
183
|
+
federated: null,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
await states.blockletExtras.setSettings(blocklet.appPid, {
|
|
188
|
+
federated: null,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const newState = await manager.getBlocklet(did);
|
|
193
|
+
manager.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
194
|
+
manager.emit(BlockletEvents.updated, { ...newState, context });
|
|
195
|
+
return newState;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Disband federated login
|
|
200
|
+
* master 解散统一登录站点群
|
|
201
|
+
* @param {Object} manager - BlockletManager instance
|
|
202
|
+
* @param {Object} params
|
|
203
|
+
* @param {string} params.did - blocklet pid
|
|
204
|
+
* @param {Object} context
|
|
205
|
+
* @returns {Promise<Object>}
|
|
206
|
+
*/
|
|
207
|
+
async function disbandFederatedLogin(manager, { did }, context) {
|
|
208
|
+
const blocklet = await manager.getBlocklet(did);
|
|
209
|
+
const federated = safeGetFederated(blocklet, { isMaster: true });
|
|
210
|
+
const masterSite = getFederatedMaster(blocklet);
|
|
211
|
+
// 只有 Master 可以调用这个逻辑
|
|
212
|
+
if (masterSite?.appPid === did) {
|
|
213
|
+
const nodeInfo = await states.node.read();
|
|
214
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
215
|
+
const { permanentWallet } = blockletInfo;
|
|
216
|
+
logger.info('Disband federated login', {
|
|
217
|
+
memberSite: {
|
|
218
|
+
appId: blocklet.appDid,
|
|
219
|
+
appPid: blocklet.appPid,
|
|
220
|
+
appName: blockletInfo.name,
|
|
221
|
+
appDescription: blockletInfo.description,
|
|
222
|
+
appUrl: blockletInfo.appUrl,
|
|
223
|
+
},
|
|
224
|
+
masterAppUrl: masterSite.appUrl,
|
|
225
|
+
});
|
|
226
|
+
const limitSync = pLimit(FEDERATED.SYNC_LIMIT);
|
|
227
|
+
|
|
228
|
+
const disbandQueue = federated.sites
|
|
229
|
+
.filter((item) => item.appPid !== did)
|
|
230
|
+
.map((item) => {
|
|
231
|
+
return limitSync(async () => {
|
|
232
|
+
try {
|
|
233
|
+
await callFederated({
|
|
234
|
+
action: 'disband',
|
|
235
|
+
permanentWallet,
|
|
236
|
+
site: item,
|
|
237
|
+
data: {},
|
|
238
|
+
});
|
|
239
|
+
} catch (error) {
|
|
240
|
+
logger.error('Failed to notify member disband', {
|
|
241
|
+
error,
|
|
242
|
+
did,
|
|
243
|
+
memberSite: item,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
await Promise.all(disbandQueue);
|
|
249
|
+
await states.blockletExtras.setSettings(blocklet.appPid, {
|
|
250
|
+
federated: null,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const newState = await manager.getBlocklet(did);
|
|
254
|
+
manager.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
255
|
+
manager.emit(BlockletEvents.updated, { ...newState, context });
|
|
256
|
+
return newState;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Set federated config
|
|
261
|
+
* @param {Object} manager - BlockletManager instance
|
|
262
|
+
* @param {Object} params
|
|
263
|
+
* @param {string} params.did - blocklet pid
|
|
264
|
+
* @param {Object} params.config - federated 配置内容
|
|
265
|
+
* @param {Object} context
|
|
266
|
+
* @returns {Promise<Object>}
|
|
267
|
+
*/
|
|
268
|
+
async function setFederated(manager, { did, config }, context) {
|
|
269
|
+
await states.blockletExtras.setSettings(did, { federated: config });
|
|
270
|
+
|
|
271
|
+
const newBlockletState = await manager.getBlocklet(did);
|
|
272
|
+
manager.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
273
|
+
manager.emit(BlockletEvents.updated, { ...newBlockletState, context });
|
|
274
|
+
return newBlockletState;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Audit federated login
|
|
279
|
+
* @param {Object} manager - BlockletManager instance
|
|
280
|
+
* @param {Object} params
|
|
281
|
+
* @param {string} params.did - master blocklet pid
|
|
282
|
+
* @param {string} params.memberPid - member blocklet pid
|
|
283
|
+
* @param {'approved'|'revoked'|'rejected'} params.status - member blocklet status
|
|
284
|
+
* @returns {Promise<Object>}
|
|
285
|
+
*/
|
|
286
|
+
async function auditFederatedLogin(manager, { memberPid, did, status }) {
|
|
287
|
+
const blocklet = await manager.getBlocklet(did);
|
|
288
|
+
const teamDid = blocklet.appPid;
|
|
289
|
+
|
|
290
|
+
const federated = safeGetFederated(blocklet, { isMaster: true });
|
|
291
|
+
const memberSite = federated.sites.find((item) => item.appPid === memberPid);
|
|
292
|
+
memberSite.status = status;
|
|
293
|
+
if (isNil(federated.config.isMaster)) {
|
|
294
|
+
const masterSite = federated.sites.find((item) => item.appPid === teamDid);
|
|
295
|
+
|
|
296
|
+
masterSite.isMaster = true;
|
|
297
|
+
federated.config.isMaster = true;
|
|
298
|
+
}
|
|
299
|
+
// 有审批操作的一方,自动成为 master
|
|
300
|
+
const newState = await setFederated(manager, {
|
|
301
|
+
did: teamDid,
|
|
302
|
+
config: federated,
|
|
303
|
+
});
|
|
304
|
+
logger.info('Audit member join federated login', {
|
|
305
|
+
memberSite: pick(memberSite, ['appId', 'appPid', 'appName', 'appDescription', 'appUrl']),
|
|
306
|
+
status,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const nodeInfo = await states.node.read();
|
|
310
|
+
const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
311
|
+
|
|
312
|
+
if (status === 'approved') {
|
|
313
|
+
// 必须先通知所有成员站点,该站点已经成功加入,后续该站点才能成功拉取所有站点的信息
|
|
314
|
+
await manager.syncFederated({
|
|
315
|
+
did,
|
|
316
|
+
data: {
|
|
317
|
+
sites: [
|
|
318
|
+
{
|
|
319
|
+
...memberSite,
|
|
320
|
+
action: 'upsert',
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
await callFederated({
|
|
329
|
+
action: 'audit-res',
|
|
330
|
+
permanentWallet,
|
|
331
|
+
site: memberSite,
|
|
332
|
+
data: {
|
|
333
|
+
masterPid: teamDid,
|
|
334
|
+
status,
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
} catch (error) {
|
|
338
|
+
logger.error('Failed to post audit result to member site', { error, did });
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
341
|
+
return newState;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Update user info and sync federated
|
|
346
|
+
* @param {Object} manager - BlockletManager instance
|
|
347
|
+
* @param {Object} params
|
|
348
|
+
* @param {string} params.teamDid - master blocklet pid
|
|
349
|
+
* @param {Object} params.updated - 更新后的用户信息
|
|
350
|
+
* @returns {Promise<void>}
|
|
351
|
+
*/
|
|
352
|
+
async function updateUserInfoAndSyncFederated(manager, { teamDid, updated }) {
|
|
353
|
+
try {
|
|
354
|
+
const { sourceAppPid } = updated;
|
|
355
|
+
const blocklet = await manager.getBlocklet(teamDid);
|
|
356
|
+
|
|
357
|
+
const masterSite = getFederatedMaster(blocklet);
|
|
358
|
+
if (shouldSyncFederated(blocklet, sourceAppPid)) {
|
|
359
|
+
const data = pick(updated, USER_PROFILE_SYNC_FIELDS);
|
|
360
|
+
if (data.avatar) {
|
|
361
|
+
data.avatar = getUserAvatarUrl(data.avatar, blocklet);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
await manager.syncFederated({
|
|
365
|
+
did: blocklet.appPid,
|
|
366
|
+
userFields: USER_PROFILE_SYNC_FIELDS,
|
|
367
|
+
data: {
|
|
368
|
+
users: [
|
|
369
|
+
{
|
|
370
|
+
...data,
|
|
371
|
+
action: 'syncProfile',
|
|
372
|
+
sourceAppPid: sourceAppPid || masterSite.appPid,
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
} catch (error) {
|
|
379
|
+
logger.error('Failed to update user info and sync federated', {
|
|
380
|
+
teamDid,
|
|
381
|
+
sourceAppPid: updated.sourceAppPid,
|
|
382
|
+
userDid: updated.did,
|
|
383
|
+
error,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Update user extra
|
|
390
|
+
* @param {Object} manager - BlockletManager instance
|
|
391
|
+
* @param {Object} args
|
|
392
|
+
* @returns {Promise<Object>}
|
|
393
|
+
*/
|
|
394
|
+
async function updateUserExtra(manager, args) {
|
|
395
|
+
try {
|
|
396
|
+
if (args.extra) {
|
|
397
|
+
try {
|
|
398
|
+
args.extra = JSON.parse(args.extra);
|
|
399
|
+
} catch (err) {
|
|
400
|
+
throw new Error('extra should be a valid json string');
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const updated = await manager.teamAPI.updateUser({
|
|
405
|
+
teamDid: args.teamDid,
|
|
406
|
+
user: pick(args, ['did', 'remark', 'extra']),
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// 异步更新站点群其他站点的用户信息
|
|
410
|
+
updateUserInfoAndSyncFederated(manager, { teamDid: args.teamDid, updated });
|
|
411
|
+
|
|
412
|
+
return updated;
|
|
413
|
+
} catch (err) {
|
|
414
|
+
logger.error('Failed to update user extra', { err });
|
|
415
|
+
throw err;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Update user info and sync
|
|
421
|
+
* @param {Object} manager - BlockletManager instance
|
|
422
|
+
* @param {Object} params
|
|
423
|
+
* @param {string} params.teamDid
|
|
424
|
+
* @param {Object} params.user
|
|
425
|
+
* @returns {Promise<Object>}
|
|
426
|
+
*/
|
|
427
|
+
async function updateUserInfoAndSync(manager, { teamDid, user }, { generateUserUpdateData }) {
|
|
428
|
+
try {
|
|
429
|
+
const existingUser = await manager.teamAPI.getUser({ teamDid, user: { did: user.did } });
|
|
430
|
+
if (!existingUser) {
|
|
431
|
+
throw new Error('User not found');
|
|
432
|
+
}
|
|
433
|
+
const updateData = generateUserUpdateData(user, existingUser);
|
|
434
|
+
const updated = await manager.teamAPI.updateUser({ teamDid, user: updateData });
|
|
435
|
+
|
|
436
|
+
// 异步更新站点群其他站点的用户信息
|
|
437
|
+
updateUserInfoAndSyncFederated(manager, { teamDid, updated });
|
|
438
|
+
|
|
439
|
+
return updated;
|
|
440
|
+
} catch (err) {
|
|
441
|
+
logger.error('user profile sync failed', { err });
|
|
442
|
+
throw err;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Sync federated
|
|
448
|
+
* @param {Object} manager - BlockletManager instance
|
|
449
|
+
* @param {Object} params
|
|
450
|
+
* @returns {Promise<Array>}
|
|
451
|
+
*/
|
|
452
|
+
async function syncFederated(manager, { did, data, syncSites, allowStatus, userFields, siteFields } = {}) {
|
|
453
|
+
if (!did) {
|
|
454
|
+
logger.error('SyncFederated failed: did is required');
|
|
455
|
+
return [];
|
|
456
|
+
}
|
|
457
|
+
const blocklet = await manager.getBlocklet(did);
|
|
458
|
+
if (!blocklet) {
|
|
459
|
+
logger.error(`SyncFederated failed: Blocklet not found for did: ${did}`);
|
|
460
|
+
return [];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const nodeInfo = await states.node.read();
|
|
464
|
+
const result = await _syncFederated({
|
|
465
|
+
blocklet,
|
|
466
|
+
data,
|
|
467
|
+
syncSites,
|
|
468
|
+
allowStatus,
|
|
469
|
+
userFields,
|
|
470
|
+
siteFields,
|
|
471
|
+
nodeInfo,
|
|
472
|
+
});
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Login federated
|
|
478
|
+
* @param {Object} manager - BlockletManager instance
|
|
479
|
+
* @param {Object} params
|
|
480
|
+
* @returns {Promise<Object>}
|
|
481
|
+
*/
|
|
482
|
+
async function loginFederated(manager, { did, site, data }) {
|
|
483
|
+
const blocklet = await manager.getBlocklet(did);
|
|
484
|
+
const nodeInfo = await states.node.read();
|
|
485
|
+
const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
486
|
+
const result = await callFederated({
|
|
487
|
+
action: 'loginByMaster',
|
|
488
|
+
data,
|
|
489
|
+
permanentWallet,
|
|
490
|
+
site,
|
|
491
|
+
});
|
|
492
|
+
return result;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Sync master authorization
|
|
497
|
+
* @param {Object} manager - BlockletManager instance
|
|
498
|
+
* @param {Object} params
|
|
499
|
+
* @returns {Promise<Object>}
|
|
500
|
+
*/
|
|
501
|
+
async function syncMasterAuthorization(manager, { did }) {
|
|
502
|
+
// 1. 获取 master 的站点信息
|
|
503
|
+
// 2. 向 master 请求 Authorization 数据
|
|
504
|
+
// 3. 更新 delegation 和 roles 映射
|
|
505
|
+
const blocklet = await manager.getBlocklet(did);
|
|
506
|
+
const teamDid = blocklet.appPid;
|
|
507
|
+
const masterSite = getFederatedMaster(blocklet);
|
|
508
|
+
const nodeInfo = await states.node.read();
|
|
509
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
510
|
+
const { permanentWallet } = blockletInfo;
|
|
511
|
+
const result = await callFederated({
|
|
512
|
+
action: 'getMasterAuthorization',
|
|
513
|
+
permanentWallet,
|
|
514
|
+
site: masterSite,
|
|
515
|
+
});
|
|
516
|
+
const { delegation, roles } = result;
|
|
517
|
+
|
|
518
|
+
const trustedPassports = blocklet.trustedPassports || [];
|
|
519
|
+
// NOTICE: 当前仅在可信列表中不包含 master 站点时执行添加操作
|
|
520
|
+
const hasTrustedPassport = trustedPassports.find((item) => item.issuerDid === masterSite.appPid);
|
|
521
|
+
if (!hasTrustedPassport) {
|
|
522
|
+
// NOTICE: teamAPI.configTrustedPassports 和 teamManager.configTrustedPassports 传参的格式是不一样的
|
|
523
|
+
await manager.teamAPI.configTrustedPassports({
|
|
524
|
+
teamDid,
|
|
525
|
+
trustedPassports: [
|
|
526
|
+
...trustedPassports,
|
|
527
|
+
{
|
|
528
|
+
issuerDid: masterSite.appPid,
|
|
529
|
+
remark: 'Generated on join federated login',
|
|
530
|
+
mappings: roles.map((item) => {
|
|
531
|
+
return {
|
|
532
|
+
from: { passport: item.name },
|
|
533
|
+
to: { role: 'guest' },
|
|
534
|
+
};
|
|
535
|
+
}),
|
|
536
|
+
},
|
|
537
|
+
],
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const federated = safeGetFederated(blocklet);
|
|
542
|
+
federated.config.delegation = delegation;
|
|
543
|
+
|
|
544
|
+
const newBlockletState = await setFederated(manager, {
|
|
545
|
+
did: teamDid,
|
|
546
|
+
config: federated,
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
return newBlockletState;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Sync federated config
|
|
554
|
+
* @param {Object} manager - BlockletManager instance
|
|
555
|
+
* @param {Object} params
|
|
556
|
+
* @returns {Promise<Object>}
|
|
557
|
+
*/
|
|
558
|
+
async function syncFederatedConfig(manager, { did }) {
|
|
559
|
+
const blocklet = await manager.getBlocklet(did);
|
|
560
|
+
const teamDid = blocklet.appPid;
|
|
561
|
+
const masterSite = getFederatedMaster(blocklet);
|
|
562
|
+
const federated = safeGetFederated(blocklet);
|
|
563
|
+
const nodeInfo = await states.node.read();
|
|
564
|
+
const domainAliases = await manager.getDomainAliases({ blocklet, nodeInfo });
|
|
565
|
+
const selfConfig = await generateSiteInfo({ nodeInfo, blocklet, domainAliases });
|
|
566
|
+
const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
567
|
+
|
|
568
|
+
const isSelfMaster = masterSite?.appPid === teamDid;
|
|
569
|
+
|
|
570
|
+
let siteInfoList = [];
|
|
571
|
+
if (isSelfMaster) {
|
|
572
|
+
siteInfoList = await pMap(
|
|
573
|
+
// FIXME: @zhanghan 需要对 sites 做一次去重处理
|
|
574
|
+
uniqBy(federated.sites, 'appPid'),
|
|
575
|
+
async (item) => {
|
|
576
|
+
if (item.appPid === teamDid) {
|
|
577
|
+
return {
|
|
578
|
+
...selfConfig,
|
|
579
|
+
status: item.status,
|
|
580
|
+
isMaster: isSelfMaster ? true : item.isMaster,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
const siteItemConfig = await callFederated({
|
|
586
|
+
action: 'getConfig',
|
|
587
|
+
permanentWallet,
|
|
588
|
+
site: item,
|
|
589
|
+
data: {},
|
|
590
|
+
});
|
|
591
|
+
return {
|
|
592
|
+
...siteItemConfig,
|
|
593
|
+
status: item.status,
|
|
594
|
+
isMaster: isSelfMaster ? false : item.isMaster,
|
|
595
|
+
};
|
|
596
|
+
} catch (error) {
|
|
597
|
+
logger.error('Failed to get site info, use outdate site-info', {
|
|
598
|
+
error,
|
|
599
|
+
site: item,
|
|
600
|
+
});
|
|
601
|
+
return item;
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
{ concurrency: FEDERATED.SYNC_LIMIT }
|
|
605
|
+
);
|
|
606
|
+
} else {
|
|
607
|
+
siteInfoList = await callFederated({
|
|
608
|
+
action: 'pullFederatedSites',
|
|
609
|
+
permanentWallet,
|
|
610
|
+
site: masterSite,
|
|
611
|
+
data: {},
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
federated.sites = siteInfoList;
|
|
615
|
+
|
|
616
|
+
const newBlockletState = await setFederated(manager, {
|
|
617
|
+
did: teamDid,
|
|
618
|
+
config: federated,
|
|
619
|
+
});
|
|
620
|
+
if (isSelfMaster) {
|
|
621
|
+
await syncFederated(manager, {
|
|
622
|
+
did,
|
|
623
|
+
data: {
|
|
624
|
+
sites: siteInfoList.filter((x) => !['pending'].includes(x.status)).map((x) => ({ ...x, action: 'upsert' })),
|
|
625
|
+
},
|
|
626
|
+
});
|
|
627
|
+
} else {
|
|
628
|
+
await syncFederated(manager, {
|
|
629
|
+
did,
|
|
630
|
+
data: {
|
|
631
|
+
sites: [{ ...selfConfig, action: 'upsert' }],
|
|
632
|
+
},
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
return newBlockletState;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
module.exports = {
|
|
639
|
+
joinFederatedLogin,
|
|
640
|
+
quitFederatedLogin,
|
|
641
|
+
disbandFederatedLogin,
|
|
642
|
+
setFederated,
|
|
643
|
+
auditFederatedLogin,
|
|
644
|
+
updateUserInfoAndSyncFederated,
|
|
645
|
+
updateUserExtra,
|
|
646
|
+
updateUserInfoAndSync,
|
|
647
|
+
syncFederated,
|
|
648
|
+
loginFederated,
|
|
649
|
+
syncMasterAuthorization,
|
|
650
|
+
syncFederatedConfig,
|
|
651
|
+
};
|