@abtnode/core 1.7.20 → 1.7.23
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/node.js +20 -0
- package/lib/api/team.js +40 -5
- package/lib/index.js +5 -2
- package/lib/states/audit-log.js +26 -6
- package/lib/states/node.js +23 -13
- package/lib/util/index.js +29 -0
- package/package.json +15 -14
package/lib/api/node.js
CHANGED
|
@@ -6,6 +6,7 @@ const isDocker = require('@abtnode/util/lib/is-docker');
|
|
|
6
6
|
const isGitpod = require('@abtnode/util/lib/is-gitpod');
|
|
7
7
|
const getFolderSize = require('@abtnode/util/lib/get-folder-size');
|
|
8
8
|
const canPackageReadWrite = require('@abtnode/util/lib/can-pkg-rw');
|
|
9
|
+
const { toDelegateAddress } = require('@arcblock/did-util');
|
|
9
10
|
|
|
10
11
|
const logger = require('@abtnode/logger')('@abtnode/core:api:node');
|
|
11
12
|
|
|
@@ -13,6 +14,7 @@ const IP = require('../util/ip');
|
|
|
13
14
|
const { validateNodeInfo, validateUpdateGateway } = require('../validators/node');
|
|
14
15
|
const BlockletRegistry = require('../blocklet/registry');
|
|
15
16
|
const { getAll } = require('../blocklet/manager/engine');
|
|
17
|
+
const { getDelegateState } = require('../util');
|
|
16
18
|
|
|
17
19
|
const sanitizeUrl = (url) => {
|
|
18
20
|
if (!url) {
|
|
@@ -160,6 +162,24 @@ class NodeAPI {
|
|
|
160
162
|
|
|
161
163
|
return this.state.updateGateway(data);
|
|
162
164
|
}
|
|
165
|
+
|
|
166
|
+
async getDelegationState() {
|
|
167
|
+
const info = await this.state.read();
|
|
168
|
+
|
|
169
|
+
const ownerNft = info.ownerNft || {};
|
|
170
|
+
if (!ownerNft.did) {
|
|
171
|
+
throw new Error('Invalid owner NFT');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const address = toDelegateAddress(ownerNft.holder, info.did);
|
|
175
|
+
const state = await getDelegateState(info.launcher.chainHost, address);
|
|
176
|
+
if (!state) {
|
|
177
|
+
return { delegated: false };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const transferV2Delegation = (state.ops || []).find(({ key }) => key === 'fg:t:transfer_v2');
|
|
181
|
+
return { delegated: !!transferV2Delegation };
|
|
182
|
+
}
|
|
163
183
|
}
|
|
164
184
|
|
|
165
185
|
module.exports = NodeAPI;
|
package/lib/api/team.js
CHANGED
|
@@ -25,7 +25,8 @@ class TeamAPI extends EventEmitter {
|
|
|
25
25
|
|
|
26
26
|
this.notification = states.notification;
|
|
27
27
|
this.node = states.node;
|
|
28
|
-
this.
|
|
28
|
+
this.memberInviteExpireTime = 1000 * 3600 * 24 * 30; // 30 days
|
|
29
|
+
this.serverInviteExpireTime = 1000 * 3600; // 1 hour
|
|
29
30
|
this.teamManager = teamManager;
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -224,9 +225,13 @@ class TeamAPI extends EventEmitter {
|
|
|
224
225
|
|
|
225
226
|
// Invite member
|
|
226
227
|
|
|
227
|
-
async
|
|
228
|
+
async createMemberInvitation({ teamDid, role, expireTime, remark }, context) {
|
|
228
229
|
await this.teamManager.checkEnablePassportIssuance(teamDid);
|
|
229
230
|
|
|
231
|
+
if (expireTime && expireTime <= 0) {
|
|
232
|
+
throw new Error('Expire time must be greater than 0');
|
|
233
|
+
}
|
|
234
|
+
|
|
230
235
|
if (!role) {
|
|
231
236
|
throw new Error('Role cannot be empty');
|
|
232
237
|
}
|
|
@@ -246,7 +251,7 @@ class TeamAPI extends EventEmitter {
|
|
|
246
251
|
throw new Error('Inviter does not exist');
|
|
247
252
|
}
|
|
248
253
|
|
|
249
|
-
const expireDate = Date.now() + this.
|
|
254
|
+
const expireDate = Date.now() + (expireTime || this.memberInviteExpireTime);
|
|
250
255
|
const state = await this.getSessionState(teamDid);
|
|
251
256
|
const { id: inviteId } = await state.start({
|
|
252
257
|
type: 'invite',
|
|
@@ -269,6 +274,36 @@ class TeamAPI extends EventEmitter {
|
|
|
269
274
|
};
|
|
270
275
|
}
|
|
271
276
|
|
|
277
|
+
async createTransferInvitation({ teamDid, remark }, context) {
|
|
278
|
+
return this.createMemberInvitation(
|
|
279
|
+
{ teamDid, expireTime: this.serverInviteExpireTime, remark, role: 'owner' },
|
|
280
|
+
context
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async getInvitation({ teamDid, inviteId }) {
|
|
285
|
+
if (!teamDid || !inviteId) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const state = await this.getSessionState(teamDid);
|
|
290
|
+
|
|
291
|
+
const invitation = await state.findOne({ _id: inviteId, type: 'invite' });
|
|
292
|
+
if (!invitation) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
298
|
+
inviteId: invitation._id,
|
|
299
|
+
role: invitation.role,
|
|
300
|
+
remark: invitation.remark,
|
|
301
|
+
expireDate: new Date(invitation.expireDate).toString(),
|
|
302
|
+
inviter: invitation.inviter,
|
|
303
|
+
teamDid: invitation.teamDid,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
272
307
|
async getInvitations({ teamDid }) {
|
|
273
308
|
const state = await this.getSessionState(teamDid);
|
|
274
309
|
|
|
@@ -350,7 +385,7 @@ class TeamAPI extends EventEmitter {
|
|
|
350
385
|
throw new Error(`Passport does not exist: ${name}`);
|
|
351
386
|
}
|
|
352
387
|
|
|
353
|
-
const expireDate = Date.now() + this.
|
|
388
|
+
const expireDate = Date.now() + this.memberInviteExpireTime;
|
|
354
389
|
|
|
355
390
|
const state = await this.getSessionState(teamDid);
|
|
356
391
|
const { id } = await state.start({
|
|
@@ -668,7 +703,7 @@ class TeamAPI extends EventEmitter {
|
|
|
668
703
|
// Just for test
|
|
669
704
|
// =============
|
|
670
705
|
setInviteExpireTime(ms) {
|
|
671
|
-
this.
|
|
706
|
+
this.memberInviteExpireTime = ms;
|
|
672
707
|
}
|
|
673
708
|
}
|
|
674
709
|
|
package/lib/index.js
CHANGED
|
@@ -247,9 +247,10 @@ function ABTNode(options) {
|
|
|
247
247
|
addBlockletStore: nodeAPI.addRegistry.bind(nodeAPI),
|
|
248
248
|
deleteBlockletStore: nodeAPI.deleteRegistry.bind(nodeAPI),
|
|
249
249
|
selectBlockletStore: nodeAPI.selectRegistry.bind(nodeAPI),
|
|
250
|
+
getDelegationState: nodeAPI.getDelegationState.bind(nodeAPI),
|
|
250
251
|
cleanupDirtyUpgradeState: states.node.cleanupDirtyUpgradeState.bind(states.node),
|
|
251
|
-
addNodeOwner: states.node.addNodeOwner.bind(states.node),
|
|
252
252
|
updateNodeOwner: states.node.updateNodeOwner.bind(states.node),
|
|
253
|
+
updateNftHolder: states.node.updateNftHolder.bind(states.node),
|
|
253
254
|
updateNodeRouting,
|
|
254
255
|
isInitialized,
|
|
255
256
|
resetNode: (params, context) =>
|
|
@@ -261,7 +262,9 @@ function ABTNode(options) {
|
|
|
261
262
|
// Team && Access control
|
|
262
263
|
|
|
263
264
|
// Invitation
|
|
264
|
-
|
|
265
|
+
createMemberInvitation: teamAPI.createMemberInvitation.bind(teamAPI),
|
|
266
|
+
createTransferInvitation: teamAPI.createTransferInvitation.bind(teamAPI),
|
|
267
|
+
getInvitation: teamAPI.getInvitation.bind(teamAPI),
|
|
265
268
|
getInvitations: teamAPI.getInvitations.bind(teamAPI),
|
|
266
269
|
processInvitation: teamAPI.processInvitation.bind(teamAPI),
|
|
267
270
|
deleteInvitation: teamAPI.deleteInvitation.bind(teamAPI),
|
package/lib/states/audit-log.js
CHANGED
|
@@ -9,7 +9,6 @@ const logger = require('@abtnode/logger')('@abtnode/core:states:audit-log');
|
|
|
9
9
|
const BaseState = require('./base');
|
|
10
10
|
|
|
11
11
|
const { parse } = require('../util/ua');
|
|
12
|
-
const { lookup } = require('../util/ip');
|
|
13
12
|
|
|
14
13
|
const getServerInfo = (info) => `[${info.name}](${joinUrl(info.routing.adminPath, '/settings/about')})`;
|
|
15
14
|
const getBlockletInfo = (blocklet, info) => `[${getDisplayName(blocklet)} v${blocklet.meta.version}](${joinUrl(info.routing.adminPath, '/blocklets/', blocklet.meta.did, '/overview')})`; // prettier-ignore
|
|
@@ -154,7 +153,7 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
154
153
|
return `${args.user.approved ? 'enabled' : 'disabled'} user ${user} for ${team}`;
|
|
155
154
|
case 'deletePassportIssuance':
|
|
156
155
|
return `removed passport issuance ${args.sessionId} from ${team}`;
|
|
157
|
-
case '
|
|
156
|
+
case 'createMemberInvitation':
|
|
158
157
|
return `created member invitation(${result.inviteId}: ${args.remark}) with **${args.role}** passport for ${team}`; // prettier-ignore
|
|
159
158
|
case 'deleteInvitation':
|
|
160
159
|
return `removed unused member invitation(${args.inviteId}) from ${team}`;
|
|
@@ -169,6 +168,8 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
169
168
|
return `removed all trusted passport issuers for ${team}`;
|
|
170
169
|
}
|
|
171
170
|
return `updated trusted passport issuers to following for ${team}: \n${args.trustedPassports.map(x => `- ${x.remark}: ${x.issuerDid}`).join('\n')}`; // prettier-ignore
|
|
171
|
+
case 'delegateTransferNFT':
|
|
172
|
+
return `${args.owner} ${args.reason}`;
|
|
172
173
|
|
|
173
174
|
// accessKeys
|
|
174
175
|
case 'createAccessKey':
|
|
@@ -258,12 +259,13 @@ const getLogCategory = (action) => {
|
|
|
258
259
|
case 'revokeUserPassport':
|
|
259
260
|
case 'enableUserPassport':
|
|
260
261
|
case 'updateUserApproval':
|
|
261
|
-
case '
|
|
262
|
+
case 'createMemberInvitation':
|
|
262
263
|
case 'deleteInvitation':
|
|
263
264
|
case 'createRole':
|
|
264
265
|
case 'updateRole':
|
|
265
266
|
case 'updatePermissionsForRole':
|
|
266
267
|
case 'configTrustedPassports':
|
|
268
|
+
case 'delegateTransferNFT':
|
|
267
269
|
return 'team';
|
|
268
270
|
|
|
269
271
|
// accessKeys
|
|
@@ -299,6 +301,25 @@ const getLogCategory = (action) => {
|
|
|
299
301
|
}
|
|
300
302
|
};
|
|
301
303
|
|
|
304
|
+
const getScope = (args = {}) => {
|
|
305
|
+
// this param usually means mutating an application (server or blocklet)
|
|
306
|
+
if (args.teamDid) {
|
|
307
|
+
return args.teamDid;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// this param usually means mutating a child component
|
|
311
|
+
if (args.rootDid) {
|
|
312
|
+
return args.rootDid;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// this param usually means mutating a nested child component
|
|
316
|
+
if (Array.isArray(args.did)) {
|
|
317
|
+
return args.did[0];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return null;
|
|
321
|
+
};
|
|
322
|
+
|
|
302
323
|
class AuditLogState extends BaseState {
|
|
303
324
|
constructor(baseDir, options = {}) {
|
|
304
325
|
super(baseDir, { filename: 'audit-log.db', ...options });
|
|
@@ -352,17 +373,16 @@ class AuditLogState extends BaseState {
|
|
|
352
373
|
|
|
353
374
|
try {
|
|
354
375
|
const { ip, ua, user } = context;
|
|
355
|
-
const [info,
|
|
376
|
+
const [info, uaInfo] = await Promise.all([node.states.node.read(), parse(ua)]);
|
|
356
377
|
|
|
357
378
|
const data = await this.asyncDB.insert({
|
|
358
|
-
scope: args
|
|
379
|
+
scope: getScope(args) || info.did, // server or blocklet did
|
|
359
380
|
action,
|
|
360
381
|
category: await getLogCategory(action, args, context, result, info, node),
|
|
361
382
|
content: (await getLogContent(action, args, context, result, info, node)).trim(),
|
|
362
383
|
actor: pick(user.actual || user, ['did', 'fullName', 'role']),
|
|
363
384
|
extra: args,
|
|
364
385
|
env: pick(uaInfo, ['browser', 'os', 'device']),
|
|
365
|
-
geo: pick(geoInfo || { country: '', city: '' }, ['country', 'city']),
|
|
366
386
|
ip,
|
|
367
387
|
ua,
|
|
368
388
|
});
|
package/lib/states/node.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-underscore-dangle */
|
|
2
2
|
const semver = require('semver');
|
|
3
3
|
const omit = require('lodash/omit');
|
|
4
|
-
const isEqual = require('lodash/isEqual');
|
|
5
4
|
const isEmpty = require('lodash/isEmpty');
|
|
6
5
|
const security = require('@abtnode/util/lib/security');
|
|
7
6
|
const { isFromPublicKey } = require('@arcblock/did');
|
|
@@ -215,32 +214,43 @@ class NodeState extends BaseState {
|
|
|
215
214
|
return updateResult;
|
|
216
215
|
}
|
|
217
216
|
|
|
218
|
-
async
|
|
219
|
-
if (!validateOwner(
|
|
217
|
+
async updateNodeOwner({ nodeOwner, ownerNft }) {
|
|
218
|
+
if (!validateOwner(nodeOwner)) {
|
|
220
219
|
throw new Error('Node owner is invalid');
|
|
221
220
|
}
|
|
222
221
|
|
|
223
222
|
const doc = await this.read();
|
|
224
223
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
224
|
+
const initialized = this.isInitialized({ nodeOwner });
|
|
225
|
+
|
|
226
|
+
if (this.notification && typeof this.notification.setDefaultReceiver === 'function') {
|
|
227
|
+
this.notification.setDefaultReceiver(nodeOwner.did);
|
|
228
|
+
}
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
const entities = { nodeOwner, initialized, initializedAt: initialized ? new Date() : null };
|
|
231
|
+
if (ownerNft) {
|
|
232
|
+
entities.ownerNft = ownerNft;
|
|
231
233
|
}
|
|
232
234
|
|
|
233
|
-
|
|
235
|
+
const updateResult = await this.update(doc._id, {
|
|
236
|
+
$set: entities,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
this.emit(EVENTS.NODE_ADDED_OWNER, updateResult);
|
|
240
|
+
|
|
241
|
+
return updateResult;
|
|
234
242
|
}
|
|
235
243
|
|
|
236
|
-
async
|
|
237
|
-
if (!
|
|
238
|
-
throw new Error('
|
|
244
|
+
async updateNftHolder(holder) {
|
|
245
|
+
if (!holder) {
|
|
246
|
+
throw new Error('NFT holder can not be empty');
|
|
239
247
|
}
|
|
240
248
|
|
|
241
249
|
const doc = await this.read();
|
|
242
250
|
|
|
243
|
-
return this.
|
|
251
|
+
return this.update(doc._id, {
|
|
252
|
+
$set: { 'ownerNft.holder': holder },
|
|
253
|
+
});
|
|
244
254
|
}
|
|
245
255
|
|
|
246
256
|
async enterMode(mode) {
|
package/lib/util/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const { Certificate } = require('@fidm/x509');
|
|
|
14
14
|
const getPortLib = require('get-port');
|
|
15
15
|
const v8 = require('v8');
|
|
16
16
|
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
17
|
+
const axios = require('@abtnode/util/lib/axios');
|
|
17
18
|
const parseBlockletMeta = require('@blocklet/meta/lib/parse');
|
|
18
19
|
const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
|
|
19
20
|
const { BlockletStatus, BLOCKLET_INTERFACE_WELLKNOWN } = require('@blocklet/meta/lib/constants');
|
|
@@ -503,6 +504,33 @@ const getStateCrons = (states) => [
|
|
|
503
504
|
},
|
|
504
505
|
];
|
|
505
506
|
|
|
507
|
+
const getDelegateState = async (chainHost, address) => {
|
|
508
|
+
const result = await axios.post(
|
|
509
|
+
joinUrl(chainHost, '/gql/'),
|
|
510
|
+
JSON.stringify({
|
|
511
|
+
query: `{
|
|
512
|
+
getDelegateState(address: "${address}") {
|
|
513
|
+
state {
|
|
514
|
+
address
|
|
515
|
+
ops {
|
|
516
|
+
key
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}`,
|
|
521
|
+
}),
|
|
522
|
+
{
|
|
523
|
+
headers: {
|
|
524
|
+
'Content-Type': 'application/json',
|
|
525
|
+
Accept: 'application/json',
|
|
526
|
+
},
|
|
527
|
+
timeout: 60 * 1000,
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
return get(result.data, 'data.getDelegateState.state');
|
|
532
|
+
};
|
|
533
|
+
|
|
506
534
|
const lib = {
|
|
507
535
|
validateOwner,
|
|
508
536
|
getProviderFromNodeInfo,
|
|
@@ -541,6 +569,7 @@ const lib = {
|
|
|
541
569
|
getSafeEnv,
|
|
542
570
|
memoizeAsync,
|
|
543
571
|
getStateCrons,
|
|
572
|
+
getDelegateState,
|
|
544
573
|
};
|
|
545
574
|
|
|
546
575
|
module.exports = lib;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.7.
|
|
6
|
+
"version": "1.7.23",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,23 +19,24 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/certificate-manager": "1.7.
|
|
23
|
-
"@abtnode/constant": "1.7.
|
|
24
|
-
"@abtnode/cron": "1.7.
|
|
25
|
-
"@abtnode/db": "1.7.
|
|
26
|
-
"@abtnode/logger": "1.7.
|
|
27
|
-
"@abtnode/queue": "1.7.
|
|
28
|
-
"@abtnode/rbac": "1.7.
|
|
29
|
-
"@abtnode/router-provider": "1.7.
|
|
30
|
-
"@abtnode/static-server": "1.7.
|
|
31
|
-
"@abtnode/timemachine": "1.7.
|
|
32
|
-
"@abtnode/util": "1.7.
|
|
22
|
+
"@abtnode/certificate-manager": "1.7.23",
|
|
23
|
+
"@abtnode/constant": "1.7.23",
|
|
24
|
+
"@abtnode/cron": "1.7.23",
|
|
25
|
+
"@abtnode/db": "1.7.23",
|
|
26
|
+
"@abtnode/logger": "1.7.23",
|
|
27
|
+
"@abtnode/queue": "1.7.23",
|
|
28
|
+
"@abtnode/rbac": "1.7.23",
|
|
29
|
+
"@abtnode/router-provider": "1.7.23",
|
|
30
|
+
"@abtnode/static-server": "1.7.23",
|
|
31
|
+
"@abtnode/timemachine": "1.7.23",
|
|
32
|
+
"@abtnode/util": "1.7.23",
|
|
33
33
|
"@arcblock/did": "^1.16.15",
|
|
34
34
|
"@arcblock/did-motif": "^1.1.9",
|
|
35
|
+
"@arcblock/did-util": "^1.16.15",
|
|
35
36
|
"@arcblock/event-hub": "1.16.15",
|
|
36
37
|
"@arcblock/pm2-events": "^0.0.5",
|
|
37
38
|
"@arcblock/vc": "^1.16.15",
|
|
38
|
-
"@blocklet/meta": "1.7.
|
|
39
|
+
"@blocklet/meta": "1.7.23",
|
|
39
40
|
"@fidm/x509": "^1.2.1",
|
|
40
41
|
"@nedb/core": "^1.2.2",
|
|
41
42
|
"@nedb/multi": "^1.2.2",
|
|
@@ -79,5 +80,5 @@
|
|
|
79
80
|
"express": "^4.17.1",
|
|
80
81
|
"jest": "^27.4.5"
|
|
81
82
|
},
|
|
82
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "aa13838b7597bce11851d0b902575f861aa1ab34"
|
|
83
84
|
}
|