@abtnode/core 1.16.17-beta-2679e686 → 1.16.17-beta-6f0c7674
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.js +150 -0
- package/lib/blocklet/manager/disk.js +54 -60
- package/lib/index.js +8 -0
- package/lib/router/manager.js +38 -6
- package/lib/states/node.js +1 -0
- package/lib/states/user-session.js +8 -0
- package/lib/states/user.js +10 -0
- package/lib/team/manager.js +8 -0
- package/lib/util/blocklet.js +2 -0
- package/lib/util/federated.js +24 -0
- package/lib/util/router.js +73 -0
- package/lib/validators/node.js +9 -0
- package/package.json +20 -20
package/lib/api/team.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const { EventEmitter } = require('events');
|
|
2
2
|
const pick = require('lodash/pick');
|
|
3
|
+
const defaults = require('lodash/defaults');
|
|
4
|
+
const cloneDeep = require('lodash/cloneDeep');
|
|
3
5
|
const joinUrl = require('url-join');
|
|
4
6
|
|
|
5
7
|
const logger = require('@abtnode/logger')('@abtnode/core:api:team');
|
|
@@ -40,6 +42,7 @@ const { validateCreatePermission, validateUpdatePermission } = require('../valid
|
|
|
40
42
|
const { getBlocklet } = require('../util/blocklet');
|
|
41
43
|
const StoreUtil = require('../util/store');
|
|
42
44
|
const { profileSchema } = require('../validators/user');
|
|
45
|
+
const { callFederated } = require('../util/federated');
|
|
43
46
|
|
|
44
47
|
const sanitizeUrl = (url) => {
|
|
45
48
|
if (!url) {
|
|
@@ -1000,6 +1003,7 @@ class TeamAPI extends EventEmitter {
|
|
|
1000
1003
|
async getPassportIssuance({ teamDid, sessionId }) {
|
|
1001
1004
|
const state = await this.getSessionState(teamDid);
|
|
1002
1005
|
const doc = await state.read(sessionId);
|
|
1006
|
+
// FIXME: @zhanghan 指定被邀请者的邀请需要查询被邀请者的详细信息
|
|
1003
1007
|
return doc;
|
|
1004
1008
|
}
|
|
1005
1009
|
|
|
@@ -1315,6 +1319,10 @@ class TeamAPI extends EventEmitter {
|
|
|
1315
1319
|
return this.teamManager.getUserState(did);
|
|
1316
1320
|
}
|
|
1317
1321
|
|
|
1322
|
+
getUserSessionState(did) {
|
|
1323
|
+
return this.teamManager.getUserSessionState(did);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1318
1326
|
getTagState(did) {
|
|
1319
1327
|
return this.teamManager.getTagState(did);
|
|
1320
1328
|
}
|
|
@@ -1329,6 +1337,148 @@ class TeamAPI extends EventEmitter {
|
|
|
1329
1337
|
setInviteExpireTime(ms) {
|
|
1330
1338
|
this.memberInviteExpireTime = ms;
|
|
1331
1339
|
}
|
|
1340
|
+
|
|
1341
|
+
// user-session management
|
|
1342
|
+
async getUserSession({ teamDid, userDid, visitorId }) {
|
|
1343
|
+
const state = await this.getUserSessionState(teamDid);
|
|
1344
|
+
const userState = await this.getUserState(teamDid);
|
|
1345
|
+
|
|
1346
|
+
const where = {
|
|
1347
|
+
status: 'online',
|
|
1348
|
+
};
|
|
1349
|
+
if (userDid) where.userDid = userDid;
|
|
1350
|
+
if (visitorId) where.visitorId = visitorId;
|
|
1351
|
+
|
|
1352
|
+
const userSessions = await state.model.findAll({
|
|
1353
|
+
where,
|
|
1354
|
+
attributes: ['id', 'appPid', 'userDid', 'visitorId', 'passportId'],
|
|
1355
|
+
include: {
|
|
1356
|
+
model: userState.model,
|
|
1357
|
+
as: 'user',
|
|
1358
|
+
attributes: ['did', 'sourceProvider', 'fullName', 'email', 'avatar', 'remark', 'sourceAppPid'],
|
|
1359
|
+
},
|
|
1360
|
+
});
|
|
1361
|
+
return userSessions.map((x) => x.toJSON());
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
async getPassportById({ teamDid, passportId }) {
|
|
1365
|
+
const userState = await this.getUserState(teamDid);
|
|
1366
|
+
const passport = await userState.getPassportById(passportId);
|
|
1367
|
+
return passport;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
async getPassportFromFederated({ site, passportId, teamDid }) {
|
|
1371
|
+
const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
|
|
1372
|
+
const nodeInfo = await this.node.read();
|
|
1373
|
+
const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
1374
|
+
|
|
1375
|
+
const result = await callFederated({
|
|
1376
|
+
action: 'getPassport',
|
|
1377
|
+
data: {
|
|
1378
|
+
passportId,
|
|
1379
|
+
},
|
|
1380
|
+
permanentWallet,
|
|
1381
|
+
site,
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
return result;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
async upsertUserSession({ teamDid, appPid, userDid, visitorId, ua, lastLoginIp, sourceAppPid, passportId, status }) {
|
|
1388
|
+
if (!userDid) {
|
|
1389
|
+
throw new Error('userDid are required');
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
const state = await this.getUserSessionState(teamDid);
|
|
1393
|
+
let data;
|
|
1394
|
+
if (!visitorId) {
|
|
1395
|
+
data = await state.insert({
|
|
1396
|
+
userDid,
|
|
1397
|
+
ua,
|
|
1398
|
+
lastLoginIp,
|
|
1399
|
+
appPid,
|
|
1400
|
+
passportId,
|
|
1401
|
+
});
|
|
1402
|
+
} else {
|
|
1403
|
+
const exist = await state.findOne({ userDid, visitorId, appPid });
|
|
1404
|
+
if (exist) {
|
|
1405
|
+
[, [data]] = await state.update(exist.id, { ua, lastLoginIp, passportId, status });
|
|
1406
|
+
} else {
|
|
1407
|
+
data = await state.insert({
|
|
1408
|
+
visitorId,
|
|
1409
|
+
userDid,
|
|
1410
|
+
ua,
|
|
1411
|
+
lastLoginIp,
|
|
1412
|
+
appPid,
|
|
1413
|
+
passportId,
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
if (sourceAppPid) {
|
|
1419
|
+
const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
|
|
1420
|
+
const nodeInfo = await this.node.read();
|
|
1421
|
+
const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
1422
|
+
const federated = defaults(cloneDeep(blocklet.settings.federated || {}), {
|
|
1423
|
+
config: {},
|
|
1424
|
+
sites: [],
|
|
1425
|
+
});
|
|
1426
|
+
const masterSite = federated.sites[0];
|
|
1427
|
+
if (masterSite && masterSite.isMaster !== false && masterSite.appPid === sourceAppPid) {
|
|
1428
|
+
await callFederated({
|
|
1429
|
+
action: 'sync',
|
|
1430
|
+
permanentWallet,
|
|
1431
|
+
site: masterSite,
|
|
1432
|
+
data: {
|
|
1433
|
+
userSessions: [
|
|
1434
|
+
{
|
|
1435
|
+
action: 'login',
|
|
1436
|
+
userDid,
|
|
1437
|
+
visitorId: data.visitorId,
|
|
1438
|
+
ua,
|
|
1439
|
+
lastLoginIp,
|
|
1440
|
+
appPid: teamDid,
|
|
1441
|
+
passportId,
|
|
1442
|
+
},
|
|
1443
|
+
],
|
|
1444
|
+
},
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
return data;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
async logoutUser({ teamDid, userDid, visitorId, appPid }) {
|
|
1453
|
+
const state = await this.getUserSessionState(teamDid);
|
|
1454
|
+
const data = await state.update({ userDid, visitorId, appPid }, { status: 'offline' });
|
|
1455
|
+
const nodeInfo = await this.node.read();
|
|
1456
|
+
const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
|
|
1457
|
+
const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
1458
|
+
const federated = defaults(cloneDeep(blocklet.settings.federated || {}), {
|
|
1459
|
+
config: {},
|
|
1460
|
+
sites: [],
|
|
1461
|
+
});
|
|
1462
|
+
const masterSite = federated.sites[0];
|
|
1463
|
+
if (masterSite && masterSite.isMaster !== false && masterSite.appPid !== teamDid) {
|
|
1464
|
+
callFederated({
|
|
1465
|
+
action: 'sync',
|
|
1466
|
+
permanentWallet,
|
|
1467
|
+
site: masterSite,
|
|
1468
|
+
data: {
|
|
1469
|
+
userSessions: [
|
|
1470
|
+
{
|
|
1471
|
+
action: 'logout',
|
|
1472
|
+
userDid,
|
|
1473
|
+
visitorId,
|
|
1474
|
+
appPid: teamDid,
|
|
1475
|
+
},
|
|
1476
|
+
],
|
|
1477
|
+
},
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
return data;
|
|
1481
|
+
}
|
|
1332
1482
|
}
|
|
1333
1483
|
|
|
1334
1484
|
module.exports = TeamAPI;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/* eslint-disable max-classes-per-file */
|
|
2
|
-
/* eslint-disable no-underscore-dangle */
|
|
3
2
|
/* eslint-disable no-await-in-loop */
|
|
4
3
|
const fs = require('fs-extra');
|
|
5
4
|
const path = require('path');
|
|
@@ -182,6 +181,7 @@ const {
|
|
|
182
181
|
getSelectedResources,
|
|
183
182
|
updateSelectedResources,
|
|
184
183
|
} = require('../project');
|
|
184
|
+
const { callFederated } = require('../../util/federated');
|
|
185
185
|
|
|
186
186
|
const { formatEnvironments, getBlockletMeta, validateOwner, isCLI } = util;
|
|
187
187
|
|
|
@@ -3744,7 +3744,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
3744
3744
|
* @param {string} options.appUrl - 申请加入统一登录的 master appUrl
|
|
3745
3745
|
* @returns {Promise<any>} 更新后的 blocklet 数据
|
|
3746
3746
|
*/
|
|
3747
|
-
async joinFederatedLogin({ appUrl, did }) {
|
|
3747
|
+
async joinFederatedLogin({ appUrl, did }, context) {
|
|
3748
3748
|
const url = new URL(appUrl);
|
|
3749
3749
|
// master service api 的地址
|
|
3750
3750
|
url.pathname = joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/join');
|
|
@@ -3818,7 +3818,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
3818
3818
|
* @param {string} options.did - blocklet pid
|
|
3819
3819
|
* @returns {Promise<any>} 更新后的 blocklet 数据
|
|
3820
3820
|
*/
|
|
3821
|
-
async quitFederatedLogin({ did }) {
|
|
3821
|
+
async quitFederatedLogin({ did }, context) {
|
|
3822
3822
|
const blocklet = await this.getBlocklet(did);
|
|
3823
3823
|
const federated = defaults(cloneDeep(blocklet.settings.federated || {}), {
|
|
3824
3824
|
config: {},
|
|
@@ -3839,21 +3839,17 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
3839
3839
|
},
|
|
3840
3840
|
masterAppUrl: masterSite.appUrl,
|
|
3841
3841
|
});
|
|
3842
|
-
const url = new URL(masterSite.appUrl);
|
|
3843
|
-
url.pathname = joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/quit');
|
|
3844
3842
|
try {
|
|
3845
|
-
await
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
{ retries: 3 }
|
|
3854
|
-
);
|
|
3843
|
+
await callFederated({
|
|
3844
|
+
action: 'quit',
|
|
3845
|
+
site: masterSite,
|
|
3846
|
+
permanentWallet,
|
|
3847
|
+
data: {
|
|
3848
|
+
memberPid: blocklet.appPid,
|
|
3849
|
+
},
|
|
3850
|
+
});
|
|
3855
3851
|
} catch (error) {
|
|
3856
|
-
logger.error('Failed to quit blocklet, will still quit federated by itself', { error, did
|
|
3852
|
+
logger.error('Failed to quit blocklet, will still quit federated by itself', { error, did });
|
|
3857
3853
|
}
|
|
3858
3854
|
}
|
|
3859
3855
|
|
|
@@ -3872,7 +3868,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
3872
3868
|
* @param {string} options.did - blocklet pid
|
|
3873
3869
|
* @returns {Promise<any>} 更新后的 blocklet 数据
|
|
3874
3870
|
*/
|
|
3875
|
-
async disbandFederatedLogin({ did }) {
|
|
3871
|
+
async disbandFederatedLogin({ did }, context) {
|
|
3876
3872
|
const blocklet = await this.getBlocklet(did);
|
|
3877
3873
|
const federated = defaults(cloneDeep(blocklet.settings.federated || {}), {
|
|
3878
3874
|
config: {},
|
|
@@ -3899,22 +3895,17 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
3899
3895
|
.filter((item) => item.appPid !== did)
|
|
3900
3896
|
.map((item) => {
|
|
3901
3897
|
return limitSync(async () => {
|
|
3902
|
-
const url = new URL(item.appUrl);
|
|
3903
|
-
url.pathname = joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/disband');
|
|
3904
3898
|
try {
|
|
3905
|
-
await
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
{ retries: 1 }
|
|
3912
|
-
);
|
|
3899
|
+
await callFederated({
|
|
3900
|
+
action: 'disband',
|
|
3901
|
+
permanentWallet,
|
|
3902
|
+
site: item,
|
|
3903
|
+
data: {},
|
|
3904
|
+
});
|
|
3913
3905
|
} catch (error) {
|
|
3914
3906
|
logger.error('Failed to disband blocklet, will still disband federated by itself', {
|
|
3915
3907
|
error,
|
|
3916
3908
|
did,
|
|
3917
|
-
url: url.href,
|
|
3918
3909
|
});
|
|
3919
3910
|
}
|
|
3920
3911
|
});
|
|
@@ -3937,7 +3928,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
3937
3928
|
* @param {object} param.config federated 配置内容
|
|
3938
3929
|
* @returns {Promise<any>} 更新后的 blocklet 数据
|
|
3939
3930
|
*/
|
|
3940
|
-
async setFederated({ did, config }) {
|
|
3931
|
+
async setFederated({ did, config }, context) {
|
|
3941
3932
|
await states.blockletExtras.setSettings(did, { federated: config });
|
|
3942
3933
|
|
|
3943
3934
|
const newState = await this.getBlocklet(did);
|
|
@@ -3953,7 +3944,8 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
3953
3944
|
* @param {boolean} param.autoLogin 是否自动登录
|
|
3954
3945
|
* @returns {Promise<any>} 更新后的 blocklet 数据
|
|
3955
3946
|
*/
|
|
3956
|
-
|
|
3947
|
+
// FIXME: @zhanghan deprecated 不再需要配置自动登录
|
|
3948
|
+
async configFederated({ did, autoLogin = false }, context) {
|
|
3957
3949
|
const blocklet = await this.getBlocklet(did);
|
|
3958
3950
|
const federated = cloneDeep(blocklet.settings.federated || {});
|
|
3959
3951
|
federated.config.autoLogin = autoLogin;
|
|
@@ -3970,7 +3962,6 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
3970
3962
|
* @param {object} param
|
|
3971
3963
|
* @param {string} param.did master blocklet pid
|
|
3972
3964
|
* @param {string} param.memberPid member blocklet pid
|
|
3973
|
-
* @param {boolean} param.autoLogin 是否自动登录
|
|
3974
3965
|
* @returns {Promise<any>} 更新后的 blocklet 数据
|
|
3975
3966
|
*/
|
|
3976
3967
|
async auditFederatedLogin({ memberPid, did, status }) {
|
|
@@ -4028,31 +4019,24 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
4028
4019
|
roles = await this.teamManager.getRoles(blocklet.appPid);
|
|
4029
4020
|
}
|
|
4030
4021
|
|
|
4031
|
-
const postUrl = joinUrl(memberSite.appUrl, WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/audit-res');
|
|
4032
|
-
|
|
4033
4022
|
logger.info('Audit member join federated login', {
|
|
4034
4023
|
status,
|
|
4035
|
-
postUrl,
|
|
4036
4024
|
});
|
|
4037
4025
|
try {
|
|
4038
4026
|
const roleList = roles.map((item) => pick(item, ['name', 'title', 'description']));
|
|
4039
|
-
await
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
{
|
|
4051
|
-
retries: 3,
|
|
4052
|
-
}
|
|
4053
|
-
);
|
|
4027
|
+
await callFederated({
|
|
4028
|
+
action: 'audit-res',
|
|
4029
|
+
permanentWallet,
|
|
4030
|
+
site: memberSite,
|
|
4031
|
+
data: {
|
|
4032
|
+
masterPid: blocklet.appPid,
|
|
4033
|
+
status,
|
|
4034
|
+
delegation,
|
|
4035
|
+
roles: roleList,
|
|
4036
|
+
},
|
|
4037
|
+
});
|
|
4054
4038
|
} catch (error) {
|
|
4055
|
-
logger.error('Failed to post audit res to member-site', { error, did
|
|
4039
|
+
logger.error('Failed to post audit res to member-site', { error, did });
|
|
4056
4040
|
throw error;
|
|
4057
4041
|
}
|
|
4058
4042
|
await this.syncFederated({
|
|
@@ -4119,24 +4103,34 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
4119
4103
|
.filter((item) => item.appId !== federated.config.appId)
|
|
4120
4104
|
.map((item) => {
|
|
4121
4105
|
return limitSync(async () => {
|
|
4122
|
-
const url = joinUrl(item.appUrl, WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/sync');
|
|
4123
4106
|
try {
|
|
4124
4107
|
// NOTICE: 即使通知某个站点失败了,也不影响其他站点接收同步结果
|
|
4125
|
-
await
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
{ retries: 3 }
|
|
4132
|
-
);
|
|
4108
|
+
await callFederated({
|
|
4109
|
+
action: 'sync',
|
|
4110
|
+
permanentWallet,
|
|
4111
|
+
site: item,
|
|
4112
|
+
data: safeData,
|
|
4113
|
+
});
|
|
4133
4114
|
} catch (error) {
|
|
4134
|
-
logger.warn('Failed to sync federated sites', { error, did,
|
|
4115
|
+
logger.warn('Failed to sync federated sites', { error, did, action: 'sync' });
|
|
4135
4116
|
}
|
|
4136
4117
|
});
|
|
4137
4118
|
});
|
|
4138
4119
|
await Promise.all(waitingList);
|
|
4139
4120
|
}
|
|
4121
|
+
|
|
4122
|
+
async loginFederated({ did, site, data }) {
|
|
4123
|
+
const blocklet = await this.getBlocklet(did);
|
|
4124
|
+
const nodeInfo = await states.node.read();
|
|
4125
|
+
const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
4126
|
+
const result = await callFederated({
|
|
4127
|
+
action: 'loginByMaster',
|
|
4128
|
+
data,
|
|
4129
|
+
permanentWallet,
|
|
4130
|
+
site,
|
|
4131
|
+
});
|
|
4132
|
+
return result;
|
|
4133
|
+
}
|
|
4140
4134
|
}
|
|
4141
4135
|
|
|
4142
4136
|
class BlockletManager extends FederatedBlockletManager {}
|
package/lib/index.js
CHANGED
|
@@ -269,6 +269,7 @@ function ABTNode(options) {
|
|
|
269
269
|
configFederated: blockletManager.configFederated.bind(blockletManager),
|
|
270
270
|
setFederated: blockletManager.setFederated.bind(blockletManager),
|
|
271
271
|
syncFederated: blockletManager.syncFederated.bind(blockletManager),
|
|
272
|
+
loginFederated: blockletManager.loginFederated.bind(blockletManager),
|
|
272
273
|
configNotification: blockletManager.configNotification.bind(blockletManager),
|
|
273
274
|
updateWhoCanAccess: blockletManager.updateWhoCanAccess.bind(blockletManager),
|
|
274
275
|
updateAppSessionConfig: blockletManager.updateAppSessionConfig.bind(blockletManager),
|
|
@@ -381,6 +382,13 @@ function ABTNode(options) {
|
|
|
381
382
|
isConnectedAccount: teamAPI.isConnectedAccount.bind(teamAPI),
|
|
382
383
|
switchProfile: teamAPI.switchProfile.bind(teamAPI),
|
|
383
384
|
|
|
385
|
+
// user-session
|
|
386
|
+
getUserSession: teamAPI.getUserSession.bind(teamAPI),
|
|
387
|
+
upsertUserSession: teamAPI.upsertUserSession.bind(teamAPI),
|
|
388
|
+
logoutUser: teamAPI.logoutUser.bind(teamAPI),
|
|
389
|
+
getPassportById: teamAPI.getPassportById.bind(teamAPI),
|
|
390
|
+
getPassportFromFederated: teamAPI.getPassportFromFederated.bind(teamAPI),
|
|
391
|
+
|
|
384
392
|
// Tagging
|
|
385
393
|
createTag: teamAPI.createTag.bind(teamAPI),
|
|
386
394
|
updateTag: teamAPI.updateTag.bind(teamAPI),
|
package/lib/router/manager.js
CHANGED
|
@@ -18,6 +18,7 @@ const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
|
18
18
|
const logger = require('@abtnode/logger')('@abtnode/core:router:manager');
|
|
19
19
|
const { getProvider } = require('@abtnode/router-provider');
|
|
20
20
|
const checkDomainMatch = require('@abtnode/util/lib/check-domain-match');
|
|
21
|
+
const { isDidDomain } = require('@abtnode/util/lib/url-evaluation');
|
|
21
22
|
const {
|
|
22
23
|
DOMAIN_FOR_IP_SITE,
|
|
23
24
|
DOMAIN_FOR_DEFAULT_SITE,
|
|
@@ -44,6 +45,7 @@ const { findWebInterface } = require('../util/blocklet');
|
|
|
44
45
|
const { attachRuntimeDomainAliases, ensureLatestInfo } = require('./helper');
|
|
45
46
|
const Router = require('./index');
|
|
46
47
|
const states = require('../states');
|
|
48
|
+
const { getDidFromDomainGroupName, updateNFTDomainRecord, revokeAndDeleteNFTDomainRecord } = require('../util/router');
|
|
47
49
|
|
|
48
50
|
const checkPathPrefixInBlackList = (pathPrefix, extraBlackList = []) => {
|
|
49
51
|
const blacklist = [
|
|
@@ -184,7 +186,7 @@ class RouterManager extends EventEmitter {
|
|
|
184
186
|
return dbSite;
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
async addDomainAlias({ id, domainAlias: tmpAlias, force }, context = {}) {
|
|
189
|
+
async addDomainAlias({ id, domainAlias: tmpAlias, force, type, nftDid, chainHost }, context = {}) {
|
|
188
190
|
const domainAlias = await validateAddDomainAlias(tmpAlias, context);
|
|
189
191
|
const dbSite = await states.site.findOne({ id });
|
|
190
192
|
if (!dbSite) {
|
|
@@ -215,12 +217,26 @@ class RouterManager extends EventEmitter {
|
|
|
215
217
|
}
|
|
216
218
|
|
|
217
219
|
const doc = await states.site.findOne({ id });
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
const item = { value: domainAlias, isProtected: false };
|
|
221
|
+
if (type === 'nft-domain') {
|
|
222
|
+
item.type = type;
|
|
223
|
+
item.nftDid = nftDid;
|
|
224
|
+
item.chainHost = chainHost;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const updateResult = await states.site.update({ id }, { $set: { domainAliases: [...doc.domainAliases, item] } });
|
|
222
228
|
logger.debug('add domain alias update result', { id, updateResult, domainAlias });
|
|
223
229
|
|
|
230
|
+
if (type === 'nft-domain') {
|
|
231
|
+
const did = getDidFromDomainGroupName(doc.domain); // TODO: 是不是可靠?
|
|
232
|
+
const didDomain = doc.domainAliases.find((x) => isDidDomain(x.value));
|
|
233
|
+
const blocklet = await states.blocklet.getBlocklet(did);
|
|
234
|
+
const nodeInfo = await states.node.read();
|
|
235
|
+
|
|
236
|
+
await updateNFTDomainRecord({ name: domainAlias, value: didDomain.value, blocklet, nodeInfo });
|
|
237
|
+
logger.info('update nft domain record', { domain: domainAlias, didDomain: '', nftDid, id });
|
|
238
|
+
}
|
|
239
|
+
|
|
224
240
|
const newSite = await states.site.findOne({ id });
|
|
225
241
|
await attachRuntimeDomainAliases({ sites: newSite, context, node: states.node });
|
|
226
242
|
|
|
@@ -228,12 +244,16 @@ class RouterManager extends EventEmitter {
|
|
|
228
244
|
}
|
|
229
245
|
|
|
230
246
|
async deleteDomainAlias({ id, domainAlias: tmpAlias }, context = {}) {
|
|
231
|
-
|
|
247
|
+
let domainAlias = await validateAddDomainAlias(tmpAlias, context);
|
|
248
|
+
domainAlias = toLower(domainAlias);
|
|
249
|
+
|
|
232
250
|
const dbSite = await states.site.findOne({ id });
|
|
233
251
|
if (!dbSite) {
|
|
234
252
|
throw new Error(`site ${id} does not exist`);
|
|
235
253
|
}
|
|
236
254
|
|
|
255
|
+
const toDelete = dbSite.domainAliases.find((x) => toLower(x.value) === domainAlias);
|
|
256
|
+
|
|
237
257
|
dbSite.domainAliases = dbSite.domainAliases.filter((x) => {
|
|
238
258
|
if (typeof x === 'string') {
|
|
239
259
|
return toLower(x) !== domainAlias;
|
|
@@ -247,6 +267,18 @@ class RouterManager extends EventEmitter {
|
|
|
247
267
|
|
|
248
268
|
await attachRuntimeDomainAliases({ sites: dbSite, context, node: states.node });
|
|
249
269
|
|
|
270
|
+
if (toDelete?.type === 'nft-domain') {
|
|
271
|
+
const blockletDid = getDidFromDomainGroupName(dbSite.domain); // TODO: 是不是可靠?
|
|
272
|
+
const blocklet = await states.blocklet.getBlocklet(blockletDid);
|
|
273
|
+
const nodeInfo = await states.node.read();
|
|
274
|
+
|
|
275
|
+
revokeAndDeleteNFTDomainRecord({ name: domainAlias, blocklet, nodeInfo })
|
|
276
|
+
.then(() => logger.info('revoke and delete nft domain record', { domain: domainAlias, blockletDid, id }))
|
|
277
|
+
.catch((error) =>
|
|
278
|
+
logger.error('revoke and delete nft domain record failed', { error, domain: domainAlias, blockletDid, id })
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
250
282
|
return dbSite;
|
|
251
283
|
}
|
|
252
284
|
|
package/lib/states/node.js
CHANGED
package/lib/states/user.js
CHANGED
|
@@ -423,6 +423,16 @@ class User extends ExtendBase {
|
|
|
423
423
|
|
|
424
424
|
return result;
|
|
425
425
|
}
|
|
426
|
+
|
|
427
|
+
async getPassports(did) {
|
|
428
|
+
const passports = await this.passport.find({ userDid: did });
|
|
429
|
+
return passports;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async getPassportById(passportId) {
|
|
433
|
+
const passport = await this.passport.findOne({ id: passportId });
|
|
434
|
+
return passport;
|
|
435
|
+
}
|
|
426
436
|
}
|
|
427
437
|
|
|
428
438
|
module.exports = User;
|
package/lib/team/manager.js
CHANGED
|
@@ -28,6 +28,7 @@ const rbacCreationLock = new Lock('rbac-creation-lock');
|
|
|
28
28
|
|
|
29
29
|
const States = {
|
|
30
30
|
User: require('../states/user'),
|
|
31
|
+
UserSession: require('../states/user-session'),
|
|
31
32
|
Passport: require('../states/passport'),
|
|
32
33
|
ConnectedAccount: require('../states/connect-account'),
|
|
33
34
|
Session: require('../states/session'),
|
|
@@ -96,6 +97,7 @@ class TeamManager extends EventEmitter {
|
|
|
96
97
|
this.cache[this.nodeDid] = {
|
|
97
98
|
rbac: await createRBAC({ storage: new MemoryStorage(), data: RBAC_CONFIG }),
|
|
98
99
|
user: await this.createState(this.nodeDid, 'User'),
|
|
100
|
+
userSession: await this.createState(this.nodeDid, 'UserSession'),
|
|
99
101
|
tag: await this.createState(this.nodeDid, 'Tag'),
|
|
100
102
|
passport: await this.createState(this.nodeDid, 'Passport'),
|
|
101
103
|
connectedAccount: await this.createState(this.nodeDid, 'ConnectedAccount'),
|
|
@@ -107,6 +109,10 @@ class TeamManager extends EventEmitter {
|
|
|
107
109
|
return this.getState(teamDid, 'user');
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
getUserSessionState(teamDid) {
|
|
113
|
+
return this.getState(teamDid, 'userSession');
|
|
114
|
+
}
|
|
115
|
+
|
|
110
116
|
getTagState(teamDid) {
|
|
111
117
|
return this.getState(teamDid, 'tag');
|
|
112
118
|
}
|
|
@@ -339,10 +345,12 @@ class TeamManager extends EventEmitter {
|
|
|
339
345
|
const passport = await this.createState(did, 'Passport');
|
|
340
346
|
const connectedAccount = await this.createState(did, 'ConnectedAccount');
|
|
341
347
|
const session = await this.createState(did, 'Session');
|
|
348
|
+
const userSession = await this.createState(did, 'UserSession');
|
|
342
349
|
|
|
343
350
|
this.cache[did] = {
|
|
344
351
|
rbac,
|
|
345
352
|
user,
|
|
353
|
+
userSession,
|
|
346
354
|
tag,
|
|
347
355
|
session,
|
|
348
356
|
passport,
|
package/lib/util/blocklet.js
CHANGED
|
@@ -274,6 +274,7 @@ const getAppSystemEnvironments = (blocklet, nodeInfo, dataDirs) => {
|
|
|
274
274
|
|
|
275
275
|
const { wallet } = result;
|
|
276
276
|
const appSk = toHex(wallet.secretKey);
|
|
277
|
+
const appPk = toHex(wallet.publicKey);
|
|
277
278
|
const appId = wallet.address;
|
|
278
279
|
const appName = title || name || result.name;
|
|
279
280
|
const appDescription = description || result.description;
|
|
@@ -302,6 +303,7 @@ const getAppSystemEnvironments = (blocklet, nodeInfo, dataDirs) => {
|
|
|
302
303
|
|
|
303
304
|
return {
|
|
304
305
|
BLOCKLET_DID: did, // BLOCKLET_DID is always same as BLOCKLET_APP_PID in structV2 application
|
|
306
|
+
BLOCKLET_APP_PK: appPk,
|
|
305
307
|
BLOCKLET_APP_SK: appSk,
|
|
306
308
|
BLOCKLET_APP_ID: appId,
|
|
307
309
|
BLOCKLET_APP_PSK: appPsk, // permanent sk even the blocklet has been migrated
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
2
|
+
const pRetry = require('p-retry');
|
|
3
|
+
const { signV2 } = require('@arcblock/jwt');
|
|
4
|
+
const joinUrl = require('url-join');
|
|
5
|
+
|
|
6
|
+
const request = require('./request');
|
|
7
|
+
|
|
8
|
+
async function callFederated({ site, permanentWallet, data, action }) {
|
|
9
|
+
const url = new URL(site.appUrl);
|
|
10
|
+
url.pathname = joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, `/api/federated/${action}`);
|
|
11
|
+
const result = await pRetry(
|
|
12
|
+
() =>
|
|
13
|
+
request.post(url.href, {
|
|
14
|
+
signer: permanentWallet.address,
|
|
15
|
+
data: signV2(permanentWallet.address, permanentWallet.secretKey, data),
|
|
16
|
+
}),
|
|
17
|
+
{ retries: 3 }
|
|
18
|
+
);
|
|
19
|
+
return result.data;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
callFederated,
|
|
24
|
+
};
|
package/lib/util/router.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
const { BLOCKLET_SITE_GROUP_SUFFIX } = require('@abtnode/constant');
|
|
2
|
+
const axios = require('@abtnode/util/lib/axios');
|
|
3
|
+
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
4
|
+
const { stableStringify } = require('@arcblock/vc');
|
|
5
|
+
const { toBase58 } = require('@ocap/util');
|
|
6
|
+
const joinURL = require('url-join');
|
|
7
|
+
const logger = require('@abtnode/logger')('@abtnode/core:router:util');
|
|
2
8
|
|
|
3
9
|
const getBlockletDomainGroupName = (did) => `${did}${BLOCKLET_SITE_GROUP_SUFFIX}`;
|
|
4
10
|
|
|
@@ -7,7 +13,74 @@ const getDidFromDomainGroupName = (name) => {
|
|
|
7
13
|
return did;
|
|
8
14
|
};
|
|
9
15
|
|
|
16
|
+
const getNFTDomainHeaders = ({ wallet, payload }) => ({
|
|
17
|
+
'x-blocklet-sig': toBase58(wallet.sign(stableStringify(payload))),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const updateNFTDomainRecord = async ({ name, value, blocklet, nodeInfo }) => {
|
|
21
|
+
const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
22
|
+
|
|
23
|
+
const payload = {
|
|
24
|
+
delegatee: blocklet.appPid,
|
|
25
|
+
domain: name,
|
|
26
|
+
record: {
|
|
27
|
+
name,
|
|
28
|
+
type: 'CNAME',
|
|
29
|
+
value,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
try {
|
|
33
|
+
const { data } = await axios({
|
|
34
|
+
method: 'POST',
|
|
35
|
+
url: joinURL(nodeInfo.nftDomainUrl, '/api/domains'), // TODO: 替换为真实地址
|
|
36
|
+
data: payload,
|
|
37
|
+
headers: getNFTDomainHeaders({ wallet, payload }),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return data;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logger.error('updateNFTDomainRecord error', {
|
|
43
|
+
error,
|
|
44
|
+
name,
|
|
45
|
+
value,
|
|
46
|
+
delegatee: blocklet.appPid,
|
|
47
|
+
appPid: blocklet.appPid,
|
|
48
|
+
resp: error.response?.data,
|
|
49
|
+
});
|
|
50
|
+
throw new Error('update nft domain record failed');
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const revokeAndDeleteNFTDomainRecord = async ({ name, blocklet, nodeInfo }) => {
|
|
55
|
+
const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
56
|
+
|
|
57
|
+
const payload = {
|
|
58
|
+
delegatee: wallet.address,
|
|
59
|
+
domain: name,
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
const { data } = await axios({
|
|
63
|
+
method: 'DELETE',
|
|
64
|
+
url: joinURL(nodeInfo.nftDomainUrl, '/api/domains'),
|
|
65
|
+
data: payload,
|
|
66
|
+
headers: getNFTDomainHeaders({ wallet, payload }),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return data;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
logger.error('revokeAndDeleteNFTDomainRecord error', {
|
|
72
|
+
error,
|
|
73
|
+
name,
|
|
74
|
+
appPid: blocklet.appPid,
|
|
75
|
+
resp: error.response?.data,
|
|
76
|
+
});
|
|
77
|
+
throw new Error('revoke nft domain record failed');
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
10
81
|
module.exports = {
|
|
11
82
|
getBlockletDomainGroupName,
|
|
12
83
|
getDidFromDomainGroupName,
|
|
84
|
+
updateNFTDomainRecord,
|
|
85
|
+
revokeAndDeleteNFTDomainRecord,
|
|
13
86
|
};
|
package/lib/validators/node.js
CHANGED
|
@@ -28,6 +28,15 @@ const nodeInfoSchema = Joi.object({
|
|
|
28
28
|
zh: { 'string.uriCustomScheme': 'Web Wallet 必须是合法的 URL' },
|
|
29
29
|
en: { 'string.uriCustomScheme': 'Web Wallet must be a valid URL' },
|
|
30
30
|
}),
|
|
31
|
+
nftDomainUrl: Joi.string()
|
|
32
|
+
.uri({ scheme: [/https?/] })
|
|
33
|
+
.label('web wallet url')
|
|
34
|
+
.allow('')
|
|
35
|
+
.optional()
|
|
36
|
+
.messages({
|
|
37
|
+
zh: { 'string.uriCustomScheme': 'NFT Domain 必须是合法的 URL' },
|
|
38
|
+
en: { 'string.uriCustomScheme': 'NFT Domain must be a valid URL' },
|
|
39
|
+
}),
|
|
31
40
|
autoUpgrade: Joi.boolean(),
|
|
32
41
|
enableWelcomePage: Joi.boolean(),
|
|
33
42
|
diskAlertThreshold: Joi.number()
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.17-beta-
|
|
6
|
+
"version": "1.16.17-beta-6f0c7674",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,19 +19,19 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/analytics": "1.16.17-beta-
|
|
23
|
-
"@abtnode/auth": "1.16.17-beta-
|
|
24
|
-
"@abtnode/certificate-manager": "1.16.17-beta-
|
|
25
|
-
"@abtnode/constant": "1.16.17-beta-
|
|
26
|
-
"@abtnode/cron": "1.16.17-beta-
|
|
27
|
-
"@abtnode/logger": "1.16.17-beta-
|
|
28
|
-
"@abtnode/models": "1.16.17-beta-
|
|
29
|
-
"@abtnode/queue": "1.16.17-beta-
|
|
30
|
-
"@abtnode/rbac": "1.16.17-beta-
|
|
31
|
-
"@abtnode/router-provider": "1.16.17-beta-
|
|
32
|
-
"@abtnode/static-server": "1.16.17-beta-
|
|
33
|
-
"@abtnode/timemachine": "1.16.17-beta-
|
|
34
|
-
"@abtnode/util": "1.16.17-beta-
|
|
22
|
+
"@abtnode/analytics": "1.16.17-beta-6f0c7674",
|
|
23
|
+
"@abtnode/auth": "1.16.17-beta-6f0c7674",
|
|
24
|
+
"@abtnode/certificate-manager": "1.16.17-beta-6f0c7674",
|
|
25
|
+
"@abtnode/constant": "1.16.17-beta-6f0c7674",
|
|
26
|
+
"@abtnode/cron": "1.16.17-beta-6f0c7674",
|
|
27
|
+
"@abtnode/logger": "1.16.17-beta-6f0c7674",
|
|
28
|
+
"@abtnode/models": "1.16.17-beta-6f0c7674",
|
|
29
|
+
"@abtnode/queue": "1.16.17-beta-6f0c7674",
|
|
30
|
+
"@abtnode/rbac": "1.16.17-beta-6f0c7674",
|
|
31
|
+
"@abtnode/router-provider": "1.16.17-beta-6f0c7674",
|
|
32
|
+
"@abtnode/static-server": "1.16.17-beta-6f0c7674",
|
|
33
|
+
"@abtnode/timemachine": "1.16.17-beta-6f0c7674",
|
|
34
|
+
"@abtnode/util": "1.16.17-beta-6f0c7674",
|
|
35
35
|
"@arcblock/did": "1.18.92",
|
|
36
36
|
"@arcblock/did-auth": "1.18.92",
|
|
37
37
|
"@arcblock/did-ext": "^1.18.92",
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
"@arcblock/pm2-events": "^0.0.5",
|
|
43
43
|
"@arcblock/validator": "^1.18.92",
|
|
44
44
|
"@arcblock/vc": "1.18.92",
|
|
45
|
-
"@blocklet/constant": "1.16.17-beta-
|
|
46
|
-
"@blocklet/env": "1.16.17-beta-
|
|
47
|
-
"@blocklet/meta": "1.16.17-beta-
|
|
48
|
-
"@blocklet/resolver": "1.16.17-beta-
|
|
49
|
-
"@blocklet/sdk": "1.16.17-beta-
|
|
45
|
+
"@blocklet/constant": "1.16.17-beta-6f0c7674",
|
|
46
|
+
"@blocklet/env": "1.16.17-beta-6f0c7674",
|
|
47
|
+
"@blocklet/meta": "1.16.17-beta-6f0c7674",
|
|
48
|
+
"@blocklet/resolver": "1.16.17-beta-6f0c7674",
|
|
49
|
+
"@blocklet/sdk": "1.16.17-beta-6f0c7674",
|
|
50
50
|
"@did-space/client": "^0.3.11",
|
|
51
51
|
"@fidm/x509": "^1.2.1",
|
|
52
52
|
"@ocap/mcrypto": "1.18.92",
|
|
@@ -101,5 +101,5 @@
|
|
|
101
101
|
"jest": "^27.5.1",
|
|
102
102
|
"unzipper": "^0.10.11"
|
|
103
103
|
},
|
|
104
|
-
"gitHead": "
|
|
104
|
+
"gitHead": "8123f54b0f572c47f1ea704607aae113576c2a76"
|
|
105
105
|
}
|