@abtnode/core 1.16.52-beta-20251003-083412-fdfc4e36 → 1.16.52-beta-20251005-235515-42ad5caf
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/team.js +579 -47
- package/lib/blocklet/downloader/blocklet-downloader.js +2 -2
- package/lib/blocklet/downloader/bundle-downloader.js +13 -38
- package/lib/blocklet/manager/disk.js +200 -84
- package/lib/blocklet/manager/ensure-blocklet-running.js +3 -2
- package/lib/blocklet/manager/helper/blue-green-get-componentids.js +59 -0
- package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +292 -0
- package/lib/blocklet/manager/helper/blue-green-update-blocklet-status.js +18 -0
- package/lib/blocklet/manager/helper/blue-green-upgrade-blocklet.js +191 -0
- package/lib/blocklet/manager/helper/upgrade-components.js +2 -9
- package/lib/blocklet/migration-dist/migration.cjs +458 -456
- package/lib/blocklet/passport/index.js +8 -2
- package/lib/index.js +18 -0
- package/lib/monitor/blocklet-runtime-monitor.js +12 -7
- package/lib/states/audit-log.js +54 -2
- package/lib/states/blocklet.js +23 -8
- package/lib/states/index.js +3 -0
- package/lib/states/org.js +661 -0
- package/lib/team/manager.js +10 -3
- package/lib/util/blocklet.js +190 -115
- package/lib/util/docker/is-docker-only-single-instances.js +17 -0
- package/lib/util/org.js +99 -0
- package/lib/validators/org.js +19 -0
- package/package.json +26 -26
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { EventEmitter } = require('events');
|
|
2
2
|
const getRequestIP = require('@abtnode/util/lib/get-request-ip');
|
|
3
|
+
const { Op } = require('sequelize');
|
|
3
4
|
const logger = require('@abtnode/logger')('@abtnode/core:blocklet-passport');
|
|
4
5
|
|
|
5
6
|
const Cron = require('@abtnode/cron');
|
|
@@ -72,8 +73,13 @@ class PassportAPI extends EventEmitter {
|
|
|
72
73
|
const { page, pageSize } = validatePaging(paging);
|
|
73
74
|
|
|
74
75
|
const where = query;
|
|
75
|
-
if (query.role === '$all') {
|
|
76
|
-
|
|
76
|
+
if (query.role === '$all' || !query.role) {
|
|
77
|
+
// 只展示非 Org passport
|
|
78
|
+
const roles = await this.teamManager.getRoles(teamDid);
|
|
79
|
+
const queryRoles = roles.filter((x) => !x.orgId).map((x) => x.name);
|
|
80
|
+
where.role = {
|
|
81
|
+
[Op.in]: queryRoles,
|
|
82
|
+
};
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
const result = await passportState.passports(where, { issuanceDate: -1 }, { pageSize, page });
|
package/lib/index.js
CHANGED
|
@@ -555,6 +555,24 @@ function ABTNode(options) {
|
|
|
555
555
|
|
|
556
556
|
createTagging: teamAPI.createTagging.bind(teamAPI),
|
|
557
557
|
deleteTagging: teamAPI.deleteTagging.bind(teamAPI),
|
|
558
|
+
// Org
|
|
559
|
+
createOrg: teamAPI.createOrg.bind(teamAPI),
|
|
560
|
+
updateOrg: teamAPI.updateOrg.bind(teamAPI),
|
|
561
|
+
deleteOrg: teamAPI.deleteOrg.bind(teamAPI),
|
|
562
|
+
getOrgs: teamAPI.getOrgs.bind(teamAPI),
|
|
563
|
+
getOrg: teamAPI.getOrg.bind(teamAPI),
|
|
564
|
+
// Org Member
|
|
565
|
+
getOrgMembers: teamAPI.getOrgMembers.bind(teamAPI),
|
|
566
|
+
addOrgMember: teamAPI.addOrgMember.bind(teamAPI), // 加入的方式是通过邀请,因此不能批量添加
|
|
567
|
+
updateOrgMember: teamAPI.updateOrgMember.bind(teamAPI), // 加入的方式是通过邀请,因此不能批量添加
|
|
568
|
+
removeOrgMember: teamAPI.removeOrgMember.bind(teamAPI),
|
|
569
|
+
getOrgInvitableUsers: teamAPI.getOrgInvitableUsers.bind(teamAPI),
|
|
570
|
+
inviteMembersToOrg: teamAPI.inviteMembersToOrg.bind(teamAPI), // TODO: 批量邀请用户到组织 与 addOrgMember 可能重复
|
|
571
|
+
|
|
572
|
+
// org resource
|
|
573
|
+
getOrgResource: teamAPI.getOrgResource.bind(teamAPI),
|
|
574
|
+
addOrgResource: teamAPI.addOrgResource.bind(teamAPI),
|
|
575
|
+
migrateOrgResource: teamAPI.migrateOrgResource.bind(teamAPI),
|
|
558
576
|
|
|
559
577
|
// Access Control
|
|
560
578
|
getRBAC: (did = options.nodeDid) => teamManager.getRBAC(did),
|
|
@@ -11,7 +11,7 @@ const defaultLogger = require('@abtnode/logger')('blocklet-runtime-monitor');
|
|
|
11
11
|
|
|
12
12
|
const { Op } = require('sequelize');
|
|
13
13
|
const { isInstanceWorker } = require('@abtnode/util/lib/pm2/is-instence-worker');
|
|
14
|
-
const { getRuntimeInfo
|
|
14
|
+
const { getRuntimeInfo } = require('../util/blocklet');
|
|
15
15
|
|
|
16
16
|
const insertThrottleMap = new Map();
|
|
17
17
|
|
|
@@ -73,9 +73,10 @@ class BlockletRuntimeMonitor extends EventEmitter {
|
|
|
73
73
|
const {
|
|
74
74
|
meta: { did: blockletDid },
|
|
75
75
|
status,
|
|
76
|
+
greenStatus,
|
|
76
77
|
} = blocklet;
|
|
77
78
|
|
|
78
|
-
if (status !== BlockletStatus.running) {
|
|
79
|
+
if (status !== BlockletStatus.running && greenStatus !== BlockletStatus.running) {
|
|
79
80
|
if (this.data[blockletDid]) {
|
|
80
81
|
Object.keys(this.data[blockletDid]).forEach((key) => {
|
|
81
82
|
this.data[blockletDid][key].runtimeInfo = {};
|
|
@@ -105,14 +106,18 @@ class BlockletRuntimeMonitor extends EventEmitter {
|
|
|
105
106
|
blocklet,
|
|
106
107
|
async (component, { id: componentId, ancestors }) => {
|
|
107
108
|
const { meta } = component;
|
|
108
|
-
if (
|
|
109
|
-
|
|
109
|
+
if (
|
|
110
|
+
!isGatewayBlocklet(meta) &&
|
|
111
|
+
hasStartEngine(meta) &&
|
|
112
|
+
(component.status === BlockletStatus.running || component.greenStatus === BlockletStatus.running)
|
|
113
|
+
) {
|
|
114
|
+
const _processId = getComponentProcessId(component, ancestors);
|
|
115
|
+
const processId = component.greenStatus === BlockletStatus.running ? `${_processId}-green` : _processId;
|
|
116
|
+
|
|
110
117
|
try {
|
|
111
118
|
const runtimeInfo = await getRuntimeInfo(processId);
|
|
112
|
-
const dockerName = component.environments?.find((x) => x.key === 'BLOCKLET_DOCKER_NAME')?.value;
|
|
113
|
-
const dockerRuntimeInfo = dockerName ? await getDockerRuntimeInfoByDockerName(dockerName) : {};
|
|
114
119
|
|
|
115
|
-
this.data[blockletDid][componentId] = { runtimeInfo
|
|
120
|
+
this.data[blockletDid][componentId] = { runtimeInfo };
|
|
116
121
|
|
|
117
122
|
if (!component.mountPoint || component.mountPoint === '/') {
|
|
118
123
|
this.data[blockletDid].app.runtimeInfo = cloneDeep(runtimeInfo);
|
package/lib/states/audit-log.js
CHANGED
|
@@ -10,6 +10,7 @@ const isArray = require('lodash/isArray');
|
|
|
10
10
|
const isString = require('lodash/isString');
|
|
11
11
|
const mapValues = require('lodash/mapValues');
|
|
12
12
|
const map = require('lodash/map');
|
|
13
|
+
const omit = require('lodash/omit');
|
|
13
14
|
const { joinURL } = require('ufo');
|
|
14
15
|
const { Op } = require('sequelize');
|
|
15
16
|
const { getDisplayName } = require('@blocklet/meta/lib/util');
|
|
@@ -273,13 +274,15 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
273
274
|
expandSite(args.id, info, node),
|
|
274
275
|
expandUser(
|
|
275
276
|
args.teamDid,
|
|
276
|
-
args.userDid || get(args, 'user.did') || args.ownerDid || args.did,
|
|
277
|
+
args.userDid || get(args, 'user.did') || args.ownerDid || args.did || context.user.did,
|
|
277
278
|
args.passportId,
|
|
278
279
|
info,
|
|
279
280
|
node
|
|
280
281
|
),
|
|
281
282
|
]);
|
|
282
283
|
|
|
284
|
+
const prefix = process.env.NODE_ENV === 'production' ? info.routing.adminPath : '';
|
|
285
|
+
|
|
283
286
|
switch (action) {
|
|
284
287
|
// blocklets
|
|
285
288
|
case 'installBlocklet':
|
|
@@ -684,7 +687,6 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
684
687
|
case 'followUser':
|
|
685
688
|
case 'unfollowUser':
|
|
686
689
|
const followerUser = await node.getUser({ teamDid: args.teamDid, user: { did: args.followerDid } });
|
|
687
|
-
const prefix = process.env.NODE_ENV === 'production' ? info.routing.adminPath : '';
|
|
688
690
|
return `[${followerUser.fullName}](${joinURL(prefix, '/team/members')}) ${action === 'followUser' ? 'followed' : 'unfollowed'} user ${user}`;
|
|
689
691
|
|
|
690
692
|
// connect to aigne
|
|
@@ -693,6 +695,49 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
693
695
|
case 'disconnectToAigne':
|
|
694
696
|
return `Disconnect to Aigne(${args.url})`;
|
|
695
697
|
|
|
698
|
+
// org 相关
|
|
699
|
+
case 'createOrg':
|
|
700
|
+
return `${user} created an org(${args.name})`;
|
|
701
|
+
case 'deleteOrg':
|
|
702
|
+
return `${user} deleted the org(${args.id})`;
|
|
703
|
+
case 'updateOrg':
|
|
704
|
+
return `${user} updated the org(${args.org.name}): \n${Object.keys(omit(args.org, ['id']))
|
|
705
|
+
.map((x) => `- ${x}: ${args.org[x]}`)
|
|
706
|
+
.join('\n')}`;
|
|
707
|
+
case 'removeOrgMember': {
|
|
708
|
+
const [member, org, operator] = await Promise.all([
|
|
709
|
+
node.getUser({ teamDid: args.teamDid, user: { did: args.userDid } }),
|
|
710
|
+
node.getOrg({ teamDid: args.teamDid, id: args.orgId }, context),
|
|
711
|
+
node.getUser({ teamDid: args.teamDid, user: { did: context.user.did } }),
|
|
712
|
+
]);
|
|
713
|
+
return `[${operator.fullName}](${joinURL(prefix, '/team/members')}) removed a member([${member.fullName}](${joinURL(prefix, '/team/members')})) from the org(${org.name})`;
|
|
714
|
+
}
|
|
715
|
+
case 'inviteMembersToOrg': {
|
|
716
|
+
const [operator, joinedOrg] = await Promise.all([
|
|
717
|
+
node.getUser({ teamDid: args.teamDid, user: { did: context.user.did } }),
|
|
718
|
+
node.getOrg({ teamDid: args.teamDid, id: args.orgId }, context),
|
|
719
|
+
]);
|
|
720
|
+
if (args.inviteType === 'internal') {
|
|
721
|
+
const dids = args.userDids || [];
|
|
722
|
+
const { users } = await node.getUsers({ teamDid: args.teamDid, dids: dids.slice(0, 5) });
|
|
723
|
+
const invitedMembers = users.length < dids.length ? [...users, '...'] : users;
|
|
724
|
+
return `[${operator.fullName}](${joinURL(prefix, '/team/members')}) invited ${dids.length} ${dids.length > 1 ? 'members' : 'member'} to join the org(${joinedOrg.name}): \n${invitedMembers.map((x) => (x.fullName ? `- [${x.fullName}](${joinURL(prefix, '/team/members')})` : `- ${x}`)).join('\n')}`;
|
|
725
|
+
}
|
|
726
|
+
return `[${operator.fullName}](${joinURL(prefix, '/team/members')}) invited members to join the org(${joinedOrg.name})`;
|
|
727
|
+
}
|
|
728
|
+
case 'addOrgResource': {
|
|
729
|
+
const org = await node.getOrg({ teamDid: args.teamDid, id: args.orgId }, context);
|
|
730
|
+
const resourceIds = (args.resourceIds || []).slice(0, 5);
|
|
731
|
+
const displayResourceIds = resourceIds.length < args.resourceIds.length ? [...resourceIds, '...'] : resourceIds;
|
|
732
|
+
return `${user} added ${resourceIds.length} ${resourceIds.length > 1 ? 'resources' : 'resource'} to the org(${org.name}): \n${displayResourceIds.map((x) => `- ${x}`).join('\n')}`;
|
|
733
|
+
}
|
|
734
|
+
case 'migrateOrgResource': {
|
|
735
|
+
const org = await node.getOrg({ teamDid: args.teamDid, id: args.to }, context);
|
|
736
|
+
const resourceIds = (args.resourceIds || []).slice(0, 5);
|
|
737
|
+
const displayResourceIds = resourceIds.length < args.resourceIds.length ? [...resourceIds, '...'] : resourceIds;
|
|
738
|
+
return `${user} migrated ${resourceIds.length} ${resourceIds.length > 1 ? 'resources' : 'resource'} to the org(${org.name}): \n${displayResourceIds.map((x) => `- ${x}`).join('\n')}`;
|
|
739
|
+
}
|
|
740
|
+
|
|
696
741
|
default:
|
|
697
742
|
return action;
|
|
698
743
|
}
|
|
@@ -797,6 +842,13 @@ const getLogCategory = (action) => {
|
|
|
797
842
|
case 'updateUserInfo':
|
|
798
843
|
case 'followUser':
|
|
799
844
|
case 'unfollowUser':
|
|
845
|
+
case 'createOrg':
|
|
846
|
+
case 'updateOrg':
|
|
847
|
+
case 'deleteOrg':
|
|
848
|
+
case 'removeOrgMember':
|
|
849
|
+
case 'inviteMembersToOrg':
|
|
850
|
+
case 'addOrgResource':
|
|
851
|
+
case 'migrateOrgResource':
|
|
800
852
|
return 'team';
|
|
801
853
|
|
|
802
854
|
// accessKeys
|
package/lib/states/blocklet.js
CHANGED
|
@@ -198,7 +198,10 @@ class BlockletState extends BaseState {
|
|
|
198
198
|
|
|
199
199
|
async getBlockletStatus(did) {
|
|
200
200
|
const doc = await this.getBlocklet(did);
|
|
201
|
-
|
|
201
|
+
if (!doc) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
return doc.status;
|
|
202
205
|
}
|
|
203
206
|
|
|
204
207
|
async hasBlocklet(did) {
|
|
@@ -469,7 +472,7 @@ class BlockletState extends BaseState {
|
|
|
469
472
|
/**
|
|
470
473
|
* refresh ports for blocklet if occupied during starting workflow
|
|
471
474
|
*/
|
|
472
|
-
async refreshBlockletPorts(did, componentDids = []) {
|
|
475
|
+
async refreshBlockletPorts(did, componentDids = [], isGreen = false) {
|
|
473
476
|
const blocklet = await this.getBlocklet(did);
|
|
474
477
|
if (!blocklet) {
|
|
475
478
|
throw new CustomError(404, `Blocklet does not exist on refresh: ${did}`);
|
|
@@ -479,9 +482,12 @@ class BlockletState extends BaseState {
|
|
|
479
482
|
|
|
480
483
|
await forEachComponentV2(blocklet, async (component) => {
|
|
481
484
|
if (!shouldSkipComponent(component.meta.did, componentDids)) {
|
|
482
|
-
component
|
|
483
|
-
|
|
484
|
-
|
|
485
|
+
component[isGreen ? 'greenPorts' : 'ports'] = await refreshPorts(
|
|
486
|
+
component[isGreen ? 'greenPorts' : 'ports'] || component.ports,
|
|
487
|
+
{
|
|
488
|
+
blackList: [...occupiedExternalPorts.keys(), ...occupiedInternalPorts.keys()],
|
|
489
|
+
}
|
|
490
|
+
);
|
|
485
491
|
}
|
|
486
492
|
});
|
|
487
493
|
|
|
@@ -556,7 +562,11 @@ class BlockletState extends BaseState {
|
|
|
556
562
|
* componentDids?: Array<Did>
|
|
557
563
|
* }}
|
|
558
564
|
*/
|
|
559
|
-
async setBlockletStatus(
|
|
565
|
+
async setBlockletStatus(
|
|
566
|
+
did,
|
|
567
|
+
status,
|
|
568
|
+
{ componentDids, operator = 'daemon', isGreen = false, isGreenAndBlue = false } = {}
|
|
569
|
+
) {
|
|
560
570
|
logger.info('setBlockletStatus', { did, status, componentDids, operator });
|
|
561
571
|
if (typeof status === 'undefined') {
|
|
562
572
|
throw new Error('Unsupported blocklet status');
|
|
@@ -589,7 +599,11 @@ class BlockletState extends BaseState {
|
|
|
589
599
|
return;
|
|
590
600
|
}
|
|
591
601
|
|
|
592
|
-
component
|
|
602
|
+
component[isGreen ? 'greenStatus' : 'status'] = status;
|
|
603
|
+
if (isGreenAndBlue) {
|
|
604
|
+
component.greenStatus = status;
|
|
605
|
+
component.status = status;
|
|
606
|
+
}
|
|
593
607
|
if (status === BlockletStatus.running) {
|
|
594
608
|
component.startedAt = new Date();
|
|
595
609
|
component.stoppedAt = null;
|
|
@@ -602,7 +616,8 @@ class BlockletState extends BaseState {
|
|
|
602
616
|
component.inProgressStart = Date.now();
|
|
603
617
|
});
|
|
604
618
|
|
|
605
|
-
const
|
|
619
|
+
const isSetStatus = status === BlockletStatus.downloading || status === BlockletStatus.waiting;
|
|
620
|
+
const updateData = isSetStatus ? pick(doc, ['status', 'children']) : pick(doc, ['children']);
|
|
606
621
|
updateData.operator = operator;
|
|
607
622
|
|
|
608
623
|
const res = await this.updateBlocklet(did, updateData);
|
package/lib/states/index.js
CHANGED
|
@@ -18,6 +18,7 @@ const BackupState = require('./backup');
|
|
|
18
18
|
const TrafficInsightState = require('./traffic-insight');
|
|
19
19
|
const RuntimeInsightState = require('./runtime-insight');
|
|
20
20
|
const BlacklistState = require('./blacklist');
|
|
21
|
+
const OrgState = require('./org');
|
|
21
22
|
|
|
22
23
|
const { getDbFilePath } = require('../util');
|
|
23
24
|
|
|
@@ -46,6 +47,7 @@ const init = (dataDirs, config = {}) => {
|
|
|
46
47
|
const trafficInsight = new TrafficInsightState(models.TrafficInsight, config);
|
|
47
48
|
const runtimeInsight = new RuntimeInsightState(models.RuntimeInsight, { ...config, maxPageSize: 8640 });
|
|
48
49
|
const blacklistState = new BlacklistState(models.Blacklist, config);
|
|
50
|
+
const orgState = new OrgState(models.Org, config, models);
|
|
49
51
|
|
|
50
52
|
return {
|
|
51
53
|
node: nodeState,
|
|
@@ -64,6 +66,7 @@ const init = (dataDirs, config = {}) => {
|
|
|
64
66
|
trafficInsight,
|
|
65
67
|
runtimeInsight,
|
|
66
68
|
blacklist: blacklistState,
|
|
69
|
+
org: orgState,
|
|
67
70
|
};
|
|
68
71
|
};
|
|
69
72
|
|