@abtnode/core 1.16.49-beta-20250826-112154-8ca981fa → 1.16.49-beta-20250827-025603-2bb1a7ee
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 +59 -18
- package/lib/blocklet/manager/disk.js +54 -24
- package/lib/blocklet/manager/ensure-blocklet-running.js +48 -1
- package/lib/blocklet/migration-dist/migration.cjs +1 -1
- package/lib/index.js +1 -1
- package/lib/states/blocklet.js +12 -5
- package/lib/states/user.js +184 -63
- package/lib/util/blocklet.js +3 -3
- package/package.json +26 -26
package/lib/api/team.js
CHANGED
|
@@ -188,9 +188,8 @@ const getUserSessionWhere = ({ status, blocklet }) => {
|
|
|
188
188
|
|
|
189
189
|
// 关注取消关注相关接口
|
|
190
190
|
const USER_RELATION_ACTIONS = {
|
|
191
|
-
follow: (state, followerDid, userDid) => state.followUser(followerDid, userDid),
|
|
192
|
-
unfollow: (state, followerDid, userDid) => state.unfollowUser(followerDid, userDid),
|
|
193
|
-
isFollowing: (state, followerDid, userDid) => state.isFollowing(followerDid, userDid),
|
|
191
|
+
follow: (state, followerDid, userDid, options) => state.followUser(followerDid, userDid, options),
|
|
192
|
+
unfollow: (state, followerDid, userDid, options) => state.unfollowUser(followerDid, userDid, options),
|
|
194
193
|
};
|
|
195
194
|
|
|
196
195
|
// 获取用户相关接口
|
|
@@ -400,7 +399,7 @@ class TeamAPI extends EventEmitter {
|
|
|
400
399
|
return doc;
|
|
401
400
|
}
|
|
402
401
|
|
|
403
|
-
async getUsers({ teamDid, query, paging: inputPaging, sort, dids }) {
|
|
402
|
+
async getUsers({ teamDid, query, paging: inputPaging, sort, dids }, context = {}) {
|
|
404
403
|
const state = await this.getUserState(teamDid);
|
|
405
404
|
const nodeInfo = await this.node.read();
|
|
406
405
|
|
|
@@ -423,7 +422,7 @@ class TeamAPI extends EventEmitter {
|
|
|
423
422
|
let paging;
|
|
424
423
|
|
|
425
424
|
if (dids) {
|
|
426
|
-
list = await state.getUsersByDids({ query, dids });
|
|
425
|
+
list = await state.getUsersByDids({ query, dids }, context);
|
|
427
426
|
paging = {
|
|
428
427
|
total: list.length,
|
|
429
428
|
pageSize: dids.length,
|
|
@@ -431,7 +430,7 @@ class TeamAPI extends EventEmitter {
|
|
|
431
430
|
page: 1,
|
|
432
431
|
};
|
|
433
432
|
} else {
|
|
434
|
-
const doc = await state.getUsers({ query, sort, paging: { pageSize: 20, ...inputPaging } });
|
|
433
|
+
const doc = await state.getUsers({ query, sort, paging: { pageSize: 20, ...inputPaging } }, context);
|
|
435
434
|
list = doc.list;
|
|
436
435
|
paging = doc.paging;
|
|
437
436
|
}
|
|
@@ -484,6 +483,7 @@ class TeamAPI extends EventEmitter {
|
|
|
484
483
|
'connectedAccounts',
|
|
485
484
|
|
|
486
485
|
'metadata',
|
|
486
|
+
'isFollowing',
|
|
487
487
|
]);
|
|
488
488
|
|
|
489
489
|
return {
|
|
@@ -590,8 +590,8 @@ class TeamAPI extends EventEmitter {
|
|
|
590
590
|
return full || owner;
|
|
591
591
|
}
|
|
592
592
|
|
|
593
|
-
async userFollowAction({ teamDid, userDid, action = 'follow' }, context = {}) {
|
|
594
|
-
const
|
|
593
|
+
async userFollowAction({ teamDid, userDid, followerDid, action = 'follow', options = {} }, context = {}) {
|
|
594
|
+
const targetDid = followerDid || context.userDid;
|
|
595
595
|
|
|
596
596
|
if (!USER_RELATION_ACTIONS[action]) {
|
|
597
597
|
throw new CustomError(
|
|
@@ -599,12 +599,49 @@ class TeamAPI extends EventEmitter {
|
|
|
599
599
|
`Invalid action: ${action}. Supported: ${Object.keys(USER_RELATION_ACTIONS).join(', ')}`
|
|
600
600
|
);
|
|
601
601
|
}
|
|
602
|
+
let result;
|
|
603
|
+
try {
|
|
604
|
+
const state = await this.getUserState(teamDid);
|
|
605
|
+
result = await USER_RELATION_ACTIONS[action](state, targetDid, userDid, options);
|
|
606
|
+
} catch (error) {
|
|
607
|
+
logger.error(`Failed to ${action} user`, {
|
|
608
|
+
followerDid: targetDid,
|
|
609
|
+
userDid,
|
|
610
|
+
error,
|
|
611
|
+
});
|
|
612
|
+
throw error;
|
|
613
|
+
}
|
|
602
614
|
|
|
603
|
-
|
|
604
|
-
|
|
615
|
+
try {
|
|
616
|
+
if (action === 'follow' && !options.skipNotification) {
|
|
617
|
+
const currentUser = await this.getUser({ teamDid, user: { did: targetDid } });
|
|
618
|
+
await this.createNotification({
|
|
619
|
+
teamDid,
|
|
620
|
+
receiver: userDid,
|
|
621
|
+
title: 'Followed',
|
|
622
|
+
description: `<${currentUser.fullName}(did:abt:${currentUser.did})> followed you`,
|
|
623
|
+
activity: {
|
|
624
|
+
type: 'follow',
|
|
625
|
+
actor: followerDid,
|
|
626
|
+
target: {
|
|
627
|
+
type: 'user',
|
|
628
|
+
id: followerDid,
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
entityId: teamDid,
|
|
632
|
+
source: 'system',
|
|
633
|
+
severity: 'success',
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
} catch (error) {
|
|
637
|
+
// 消息创建失败,不影响 follow 操作
|
|
638
|
+
logger.error('Failed to create notification', { error });
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return result;
|
|
605
642
|
}
|
|
606
643
|
|
|
607
|
-
async getUserFollows({ teamDid, userDid, type = 'following', paging, sort }, context = {}) {
|
|
644
|
+
async getUserFollows({ teamDid, userDid, type = 'following', paging, sort, options = {} }, context = {}) {
|
|
608
645
|
const state = await this.getUserState(teamDid);
|
|
609
646
|
|
|
610
647
|
const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
|
|
@@ -624,13 +661,14 @@ class TeamAPI extends EventEmitter {
|
|
|
624
661
|
{
|
|
625
662
|
paging,
|
|
626
663
|
sort,
|
|
664
|
+
...options,
|
|
627
665
|
},
|
|
628
666
|
context
|
|
629
667
|
);
|
|
630
668
|
|
|
631
669
|
list.forEach((item) => {
|
|
632
670
|
const { user } = item;
|
|
633
|
-
if (user?.avatar && user?.avatar?.startsWith(USER_AVATAR_URL_PREFIX)) {
|
|
671
|
+
if (user && user?.avatar && user?.avatar?.startsWith(USER_AVATAR_URL_PREFIX)) {
|
|
634
672
|
user.avatar = `${getFederatedUserAvatarUrl(user.avatar, blocklet)}?imageFilter=resize&w=48&h=48`;
|
|
635
673
|
}
|
|
636
674
|
});
|
|
@@ -645,14 +683,17 @@ class TeamAPI extends EventEmitter {
|
|
|
645
683
|
}
|
|
646
684
|
}
|
|
647
685
|
|
|
648
|
-
async getUserFollowStats({ teamDid,
|
|
686
|
+
async getUserFollowStats({ teamDid, userDids }) {
|
|
649
687
|
const state = await this.getUserState(teamDid);
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
688
|
+
return state.getFollowStats(userDids);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
async checkFollowing({ teamDid, userDids = [], followerDid }) {
|
|
692
|
+
if (!followerDid) {
|
|
693
|
+
throw new CustomError(400, 'Follower DID is required');
|
|
655
694
|
}
|
|
695
|
+
const state = await this.getUserState(teamDid);
|
|
696
|
+
return state.isFollowing(followerDid, userDids);
|
|
656
697
|
}
|
|
657
698
|
|
|
658
699
|
async updateUser({ teamDid, user }) {
|
|
@@ -439,9 +439,6 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
439
439
|
restart: async (params) => {
|
|
440
440
|
await this.restart(params);
|
|
441
441
|
},
|
|
442
|
-
stop: async (params) => {
|
|
443
|
-
await this.stop(params);
|
|
444
|
-
},
|
|
445
442
|
createAuditLog: (params) => this.createAuditLog(params),
|
|
446
443
|
notification: (did, title, description, severity) => {
|
|
447
444
|
try {
|
|
@@ -793,7 +790,15 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
793
790
|
}
|
|
794
791
|
|
|
795
792
|
async start(
|
|
796
|
-
{
|
|
793
|
+
{
|
|
794
|
+
did,
|
|
795
|
+
throwOnError,
|
|
796
|
+
checkHealthImmediately = false,
|
|
797
|
+
e2eMode = false,
|
|
798
|
+
componentDids: inputComponentDids,
|
|
799
|
+
atomic,
|
|
800
|
+
operator,
|
|
801
|
+
},
|
|
797
802
|
context
|
|
798
803
|
) {
|
|
799
804
|
const blocklet = await this.ensureBlocklet(did, { e2eMode });
|
|
@@ -823,29 +828,50 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
823
828
|
});
|
|
824
829
|
|
|
825
830
|
if (atomic || !blocklet.structVersion) {
|
|
826
|
-
return this._start({ blocklet, throwOnError, checkHealthImmediately, e2eMode, componentDids }, context);
|
|
831
|
+
return this._start({ blocklet, throwOnError, checkHealthImmediately, e2eMode, componentDids, operator }, context);
|
|
827
832
|
}
|
|
828
833
|
|
|
829
834
|
const tasks = componentDids.map(
|
|
830
835
|
(componentDid) => () =>
|
|
831
|
-
this._start(
|
|
836
|
+
this._start(
|
|
837
|
+
{
|
|
838
|
+
blocklet,
|
|
839
|
+
throwOnError,
|
|
840
|
+
checkHealthImmediately,
|
|
841
|
+
e2eMode,
|
|
842
|
+
componentDids: [componentDid],
|
|
843
|
+
operator,
|
|
844
|
+
},
|
|
845
|
+
context
|
|
846
|
+
)
|
|
832
847
|
);
|
|
833
848
|
|
|
834
|
-
const rest = await pAll(tasks, { concurrency:
|
|
849
|
+
const rest = await pAll(tasks, { concurrency: 6 });
|
|
835
850
|
|
|
836
851
|
return rest[0];
|
|
837
852
|
}
|
|
838
853
|
|
|
839
854
|
async _start(
|
|
840
|
-
{
|
|
855
|
+
{
|
|
856
|
+
blocklet: inputBlocklet,
|
|
857
|
+
did,
|
|
858
|
+
throwOnError,
|
|
859
|
+
checkHealthImmediately = false,
|
|
860
|
+
e2eMode = false,
|
|
861
|
+
componentDids,
|
|
862
|
+
operator: _operator,
|
|
863
|
+
},
|
|
841
864
|
context
|
|
842
865
|
) {
|
|
866
|
+
const operator = _operator || context?.user?.did;
|
|
867
|
+
|
|
843
868
|
logger.info('start blocklet', {
|
|
844
869
|
did: did || inputBlocklet.meta.did,
|
|
845
870
|
componentDids,
|
|
846
871
|
throwOnError,
|
|
847
872
|
checkHealthImmediately,
|
|
848
873
|
e2eMode,
|
|
874
|
+
operator,
|
|
849
875
|
});
|
|
850
876
|
// should check blocklet integrity
|
|
851
877
|
const blocklet1 = inputBlocklet || (await this.ensureBlocklet(did, { e2eMode }));
|
|
@@ -875,11 +901,9 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
875
901
|
}
|
|
876
902
|
}
|
|
877
903
|
|
|
878
|
-
// blocklet may be manually stopped durning starting
|
|
879
|
-
// so error message would not be sent if blocklet is stopped
|
|
880
|
-
// so we need update status first
|
|
881
904
|
const doc1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.starting, {
|
|
882
905
|
componentDids,
|
|
906
|
+
operator,
|
|
883
907
|
});
|
|
884
908
|
blocklet1.status = BlockletStatus.starting;
|
|
885
909
|
this.emit(BlockletEvents.statusChange, doc1);
|
|
@@ -990,7 +1014,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
990
1014
|
});
|
|
991
1015
|
|
|
992
1016
|
await this.deleteProcess({ did, componentDids });
|
|
993
|
-
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
|
|
1017
|
+
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids, operator });
|
|
994
1018
|
this.emit(BlockletEvents.startFailed, { ...res, componentDids, error: { message: error.message } });
|
|
995
1019
|
this.emit(BlockletEvents.statusChange, { ...res, error: { message: error.message } });
|
|
996
1020
|
|
|
@@ -1002,14 +1026,15 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1002
1026
|
}
|
|
1003
1027
|
}
|
|
1004
1028
|
|
|
1005
|
-
async stop({ did, updateStatus = true, silent = false, componentDids, reason }, context) {
|
|
1006
|
-
|
|
1029
|
+
async stop({ did, updateStatus = true, silent = false, componentDids, reason, operator: _operator }, context) {
|
|
1030
|
+
const operator = _operator || context?.user?.did;
|
|
1031
|
+
logger.info('stop blocklet', { did, componentDids, updateStatus, silent, operator });
|
|
1007
1032
|
|
|
1008
1033
|
const blocklet = await this.getBlocklet(did);
|
|
1009
1034
|
const { processId } = blocklet.env;
|
|
1010
1035
|
|
|
1011
1036
|
if (updateStatus) {
|
|
1012
|
-
const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, { componentDids });
|
|
1037
|
+
const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, { componentDids, operator });
|
|
1013
1038
|
blocklet.status = BlockletStatus.stopping;
|
|
1014
1039
|
this.emit(BlockletEvents.statusChange, doc);
|
|
1015
1040
|
}
|
|
@@ -1055,7 +1080,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1055
1080
|
} catch (error) {
|
|
1056
1081
|
logger.error('Failed to stop blocklet', { error, did });
|
|
1057
1082
|
if (updateStatus) {
|
|
1058
|
-
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
|
|
1083
|
+
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids, operator });
|
|
1059
1084
|
this.emit(BlockletEvents.statusChange, res);
|
|
1060
1085
|
}
|
|
1061
1086
|
throw error;
|
|
@@ -1066,7 +1091,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1066
1091
|
launcher.notifyBlockletUpdated(blocklet);
|
|
1067
1092
|
|
|
1068
1093
|
if (updateStatus) {
|
|
1069
|
-
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids });
|
|
1094
|
+
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids, operator });
|
|
1070
1095
|
// send notification to websocket channel
|
|
1071
1096
|
this.emit(BlockletEvents.statusChange, res);
|
|
1072
1097
|
|
|
@@ -1173,11 +1198,15 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1173
1198
|
* @param {Record<string, any>} context
|
|
1174
1199
|
* @returns {import('@blocklet/server-js').BlockletState}
|
|
1175
1200
|
*/
|
|
1176
|
-
async restart({ did, componentDids }, context) {
|
|
1177
|
-
|
|
1201
|
+
async restart({ did, componentDids, operator: _operator }, context) {
|
|
1202
|
+
const operator = _operator || context?.user?.did;
|
|
1203
|
+
logger.info('restart blocklet', { did, operator });
|
|
1178
1204
|
const blocklet = await this.getBlocklet(did);
|
|
1179
1205
|
await this.checkControllerStatus(blocklet, 'restart');
|
|
1180
|
-
await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, {
|
|
1206
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, {
|
|
1207
|
+
componentDids,
|
|
1208
|
+
operator,
|
|
1209
|
+
});
|
|
1181
1210
|
const result = await states.blocklet.getBlocklet(did);
|
|
1182
1211
|
this.emit(BlockletEvents.statusChange, result);
|
|
1183
1212
|
|
|
@@ -1187,12 +1216,13 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1187
1216
|
id: `${did}/${(componentDids || []).join(',')}`,
|
|
1188
1217
|
did,
|
|
1189
1218
|
componentDids,
|
|
1219
|
+
operator,
|
|
1190
1220
|
context,
|
|
1191
1221
|
});
|
|
1192
1222
|
ticket.on('failed', async (err) => {
|
|
1193
1223
|
logger.error('failed to restart blocklet', { did, error: err });
|
|
1194
1224
|
|
|
1195
|
-
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids });
|
|
1225
|
+
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids, operator });
|
|
1196
1226
|
this.emit(BlockletEvents.statusChange, state);
|
|
1197
1227
|
|
|
1198
1228
|
const description = `${getComponentNamesWithVersion(result, componentDids)} restart failed for ${getDisplayName(
|
|
@@ -3276,9 +3306,9 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3276
3306
|
}
|
|
3277
3307
|
}
|
|
3278
3308
|
|
|
3279
|
-
async _onRestart({ did, componentDids, context }) {
|
|
3280
|
-
await this.stop({ did, componentDids, updateStatus: false }, context);
|
|
3281
|
-
await this.start({ did, componentDids }, context);
|
|
3309
|
+
async _onRestart({ did, componentDids, context, operator }) {
|
|
3310
|
+
await this.stop({ did, componentDids, updateStatus: false, operator }, context);
|
|
3311
|
+
await this.start({ did, componentDids, operator }, context);
|
|
3282
3312
|
}
|
|
3283
3313
|
|
|
3284
3314
|
async _onCheckIfStarted(jobInfo, { throwOnError } = {}) {
|
|
@@ -3,6 +3,7 @@ const pAll = require('p-all');
|
|
|
3
3
|
const { BlockletStatus } = require('@blocklet/constant');
|
|
4
4
|
const sleep = require('@abtnode/util/lib/sleep');
|
|
5
5
|
const { getDisplayName } = require('@blocklet/meta/lib/util');
|
|
6
|
+
const { isValid } = require('@arcblock/did');
|
|
6
7
|
|
|
7
8
|
const states = require('../../states');
|
|
8
9
|
const { isBlockletPortHealthy, shouldCheckHealthy } = require('../../util/blocklet');
|
|
@@ -31,6 +32,9 @@ class EnsureBlockletRunning {
|
|
|
31
32
|
|
|
32
33
|
highLoadDisk = +process.env.ABT_NODE_ENSURE_RUNNING_HIGH_LOAD_DISK || 0.95;
|
|
33
34
|
|
|
35
|
+
// 进行中状态超时时间(毫秒)
|
|
36
|
+
inProgressTimeout = +process.env.ABT_NODE_ENSURE_RUNNING_IN_PROGRESS_TIMEOUT || 5 * 60 * 1000;
|
|
37
|
+
|
|
34
38
|
runningBlocklets = {};
|
|
35
39
|
|
|
36
40
|
runningRootBlocklets = {};
|
|
@@ -211,6 +215,43 @@ class EnsureBlockletRunning {
|
|
|
211
215
|
return;
|
|
212
216
|
}
|
|
213
217
|
|
|
218
|
+
// Skip health check if blocklet is in progress status
|
|
219
|
+
if (inProgressStatuses.includes(blocklet.status)) {
|
|
220
|
+
// Check if it's user-initiated operation
|
|
221
|
+
// 如果 operator 是 z 开头的字符串,表示是 did, 跳过健康检查
|
|
222
|
+
if (blocklet.operator && isValid(blocklet.operator)) {
|
|
223
|
+
logger.info('Skip ensure running check for user-initiated operation', {
|
|
224
|
+
did,
|
|
225
|
+
componentDid: blocklet.meta.did,
|
|
226
|
+
status: blocklet.status,
|
|
227
|
+
operator: blocklet.operator,
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Check timeout for daemon-initiated or no operator recorded operations
|
|
233
|
+
if (blocklet.inProgressStart) {
|
|
234
|
+
const elapsedTime = Date.now() - new Date(blocklet.inProgressStart).getTime();
|
|
235
|
+
if (elapsedTime < this.inProgressTimeout) {
|
|
236
|
+
logger.info('Skip ensure running check due to timeout not reached', {
|
|
237
|
+
did,
|
|
238
|
+
componentDid: blocklet.meta.did,
|
|
239
|
+
status: blocklet.status,
|
|
240
|
+
elapsedTime,
|
|
241
|
+
timeout: this.inProgressTimeout,
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
logger.warn('InProgress timeout reached, proceeding with health check', {
|
|
246
|
+
did,
|
|
247
|
+
componentDid: blocklet.meta.did,
|
|
248
|
+
status: blocklet.status,
|
|
249
|
+
elapsedTime,
|
|
250
|
+
timeout: this.inProgressTimeout,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
214
255
|
const health = await this.isBlockletPortHealthyWithRetries(
|
|
215
256
|
blocklet,
|
|
216
257
|
inProgressStatuses.includes(blocklet.status)
|
|
@@ -266,7 +307,13 @@ class EnsureBlockletRunning {
|
|
|
266
307
|
|
|
267
308
|
try {
|
|
268
309
|
logger.info('restart blocklet:', did, componentDids);
|
|
269
|
-
await this.restart({
|
|
310
|
+
await this.restart({
|
|
311
|
+
did,
|
|
312
|
+
componentDids,
|
|
313
|
+
checkHealthImmediately: true,
|
|
314
|
+
atomic: true,
|
|
315
|
+
operator: 'ensure-blocklet-running',
|
|
316
|
+
});
|
|
270
317
|
if (this.whenCycleCheck) {
|
|
271
318
|
this.createAuditLog({
|
|
272
319
|
action: 'ensureBlockletRunning',
|
|
@@ -38923,7 +38923,7 @@ module.exports = require("zlib");
|
|
|
38923
38923
|
/***/ ((module) => {
|
|
38924
38924
|
|
|
38925
38925
|
"use strict";
|
|
38926
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.48","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib","test":"node tools/jest.js","coverage":"npm run test -- --coverage"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.16.48","@abtnode/auth":"1.16.48","@abtnode/certificate-manager":"1.16.48","@abtnode/constant":"1.16.48","@abtnode/cron":"1.16.48","@abtnode/db-cache":"1.16.48","@abtnode/docker-utils":"1.16.48","@abtnode/logger":"1.16.48","@abtnode/models":"1.16.48","@abtnode/queue":"1.16.48","@abtnode/rbac":"1.16.48","@abtnode/router-provider":"1.16.48","@abtnode/static-server":"1.16.48","@abtnode/timemachine":"1.16.48","@abtnode/util":"1.16.48","@aigne/aigne-hub":"^0.6.
|
|
38926
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.48","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib","test":"node tools/jest.js","coverage":"npm run test -- --coverage"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.16.48","@abtnode/auth":"1.16.48","@abtnode/certificate-manager":"1.16.48","@abtnode/constant":"1.16.48","@abtnode/cron":"1.16.48","@abtnode/db-cache":"1.16.48","@abtnode/docker-utils":"1.16.48","@abtnode/logger":"1.16.48","@abtnode/models":"1.16.48","@abtnode/queue":"1.16.48","@abtnode/rbac":"1.16.48","@abtnode/router-provider":"1.16.48","@abtnode/static-server":"1.16.48","@abtnode/timemachine":"1.16.48","@abtnode/util":"1.16.48","@aigne/aigne-hub":"^0.6.9","@arcblock/did":"1.22.2","@arcblock/did-connect-js":"1.22.2","@arcblock/did-ext":"1.22.2","@arcblock/did-motif":"^1.1.14","@arcblock/did-util":"1.22.2","@arcblock/event-hub":"1.22.2","@arcblock/jwt":"1.22.2","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"1.22.2","@arcblock/vc":"1.22.2","@blocklet/constant":"1.16.48","@blocklet/did-space-js":"^1.1.18","@blocklet/env":"1.16.48","@blocklet/error":"^0.2.5","@blocklet/meta":"1.16.48","@blocklet/resolver":"1.16.48","@blocklet/sdk":"1.16.48","@blocklet/server-js":"1.16.48","@blocklet/store":"1.16.48","@blocklet/theme":"^3.1.30","@fidm/x509":"^1.2.1","@ocap/mcrypto":"1.22.2","@ocap/util":"1.22.2","@ocap/wallet":"1.22.2","@slack/webhook":"^5.0.4","archiver":"^7.0.1","axios":"^1.7.9","axon":"^2.0.3","chalk":"^4.1.2","cross-spawn":"^7.0.3","dayjs":"^1.11.13","deep-diff":"^1.0.2","detect-port":"^1.5.1","envfile":"^7.1.0","escape-string-regexp":"^4.0.0","fast-glob":"^3.3.2","filesize":"^10.1.1","flat":"^5.0.2","fs-extra":"^11.2.0","get-port":"^5.1.1","hasha":"^5.2.2","is-base64":"^1.1.0","is-cidr":"4","is-ip":"3","is-url":"^1.2.4","joi":"17.12.2","joi-extension-semver":"^5.0.0","js-yaml":"^4.1.0","kill-port":"^2.0.1","lodash":"^4.17.21","node-stream-zip":"^1.15.0","p-all":"^3.0.0","p-limit":"^3.1.0","p-map":"^4.0.0","p-retry":"^4.6.2","p-wait-for":"^3.2.0","private-ip":"^2.3.4","rate-limiter-flexible":"^5.0.5","read-last-lines":"^1.8.0","semver":"^7.6.3","sequelize":"^6.35.0","shelljs":"^0.8.5","slugify":"^1.6.6","ssri":"^8.0.1","stream-throttle":"^0.1.3","stream-to-promise":"^3.0.0","systeminformation":"^5.23.3","tail":"^2.2.4","tar":"^6.1.11","transliteration":"^2.3.5","ua-parser-js":"^1.0.2","ufo":"^1.5.3","uuid":"^11.1.0","valid-url":"^1.0.9","which":"^2.0.2","xbytes":"^1.8.0"},"devDependencies":{"expand-tilde":"^2.0.2","express":"^4.18.2","jest":"^29.7.0","unzipper":"^0.10.11"},"gitHead":"e5764f753181ed6a7c615cd4fc6682aacf0cb7cd"}');
|
|
38927
38927
|
|
|
38928
38928
|
/***/ }),
|
|
38929
38929
|
|
package/lib/index.js
CHANGED
|
@@ -536,11 +536,11 @@ function ABTNode(options) {
|
|
|
536
536
|
// user follow
|
|
537
537
|
followUser: (params, context) => teamAPI.userFollowAction({ ...params, action: 'follow' }, context),
|
|
538
538
|
unfollowUser: (params, context) => teamAPI.userFollowAction({ ...params, action: 'unfollow' }, context),
|
|
539
|
-
isFollowing: (params, context) => teamAPI.userFollowAction({ ...params, action: 'isFollowing' }, context),
|
|
540
539
|
|
|
541
540
|
getUserFollowers: (params, context) => teamAPI.getUserFollows({ ...params, type: 'followers' }, context),
|
|
542
541
|
getUserFollowing: (params, context) => teamAPI.getUserFollows({ ...params, type: 'following' }, context),
|
|
543
542
|
getUserFollowStats: teamAPI.getUserFollowStats.bind(teamAPI),
|
|
543
|
+
checkFollowing: teamAPI.checkFollowing.bind(teamAPI),
|
|
544
544
|
|
|
545
545
|
// Tagging
|
|
546
546
|
createTag: teamAPI.createTag.bind(teamAPI),
|
package/lib/states/blocklet.js
CHANGED
|
@@ -555,8 +555,8 @@ class BlockletState extends BaseState {
|
|
|
555
555
|
* componentDids?: Array<Did>
|
|
556
556
|
* }}
|
|
557
557
|
*/
|
|
558
|
-
async setBlockletStatus(did, status, { componentDids } = {}) {
|
|
559
|
-
logger.info('setBlockletStatus', { did, status, componentDids });
|
|
558
|
+
async setBlockletStatus(did, status, { componentDids, operator = 'daemon' } = {}) {
|
|
559
|
+
logger.info('setBlockletStatus', { did, status, componentDids, operator });
|
|
560
560
|
if (typeof status === 'undefined') {
|
|
561
561
|
throw new Error('Unsupported blocklet status');
|
|
562
562
|
}
|
|
@@ -568,14 +568,16 @@ class BlockletState extends BaseState {
|
|
|
568
568
|
const doc = await this.getBlocklet(did);
|
|
569
569
|
|
|
570
570
|
if (doc.meta?.group === BlockletGroup.gateway && !doc.children?.length) {
|
|
571
|
-
const
|
|
571
|
+
const updateData = { status, operator };
|
|
572
|
+
const res = await this.updateBlocklet(did, updateData);
|
|
572
573
|
statusLock.release();
|
|
573
574
|
return res;
|
|
574
575
|
}
|
|
575
576
|
|
|
576
577
|
// for backward compatibility
|
|
577
578
|
if (!doc.structVersion && !doc.children?.length) {
|
|
578
|
-
const
|
|
579
|
+
const updateData = { status, operator };
|
|
580
|
+
const res = await this.updateBlocklet(did, updateData);
|
|
579
581
|
statusLock.release();
|
|
580
582
|
return res;
|
|
581
583
|
}
|
|
@@ -599,9 +601,14 @@ class BlockletState extends BaseState {
|
|
|
599
601
|
component.startedAt = null;
|
|
600
602
|
component.stoppedAt = new Date();
|
|
601
603
|
}
|
|
604
|
+
component.operator = operator;
|
|
605
|
+
component.inProgressStart = Date.now();
|
|
602
606
|
});
|
|
603
607
|
|
|
604
|
-
const
|
|
608
|
+
const updateData = pick(doc, ['status', 'children']);
|
|
609
|
+
updateData.operator = operator;
|
|
610
|
+
|
|
611
|
+
const res = await this.updateBlocklet(did, updateData);
|
|
605
612
|
statusLock.release();
|
|
606
613
|
return res;
|
|
607
614
|
} catch (error) {
|
package/lib/states/user.js
CHANGED
|
@@ -253,7 +253,7 @@ class User extends ExtendBase {
|
|
|
253
253
|
/**
|
|
254
254
|
* Get blocklet service user list
|
|
255
255
|
*/
|
|
256
|
-
async getUsers({ query, sort, paging } = {}) {
|
|
256
|
+
async getUsers({ query, sort, paging } = {}, context = {}) {
|
|
257
257
|
const where = {};
|
|
258
258
|
const replacements = {};
|
|
259
259
|
|
|
@@ -268,7 +268,11 @@ class User extends ExtendBase {
|
|
|
268
268
|
includeTags = false,
|
|
269
269
|
includePassports = false,
|
|
270
270
|
includeConnectedAccounts = false,
|
|
271
|
+
includeFollowStatus = false,
|
|
271
272
|
} = query || {};
|
|
273
|
+
|
|
274
|
+
const { user } = context || {};
|
|
275
|
+
|
|
272
276
|
const shouldIncludeTag = (tags && tags.length) || includeTags;
|
|
273
277
|
|
|
274
278
|
if (isNullOrUndefined(approved) === false) {
|
|
@@ -417,18 +421,27 @@ SELECT did,inviter,generation FROM UserTree`.trim();
|
|
|
417
421
|
}
|
|
418
422
|
|
|
419
423
|
const result = await this.paginate({ where, include, replacements }, sorting, paging);
|
|
424
|
+
|
|
425
|
+
if (includeFollowStatus && user?.did) {
|
|
426
|
+
result.list = await this._enrichWithFollowStatus(result.list, user.did);
|
|
427
|
+
}
|
|
428
|
+
|
|
420
429
|
return { list: result.list, paging: { ...result.paging, total: total || result.paging.total } };
|
|
421
430
|
}
|
|
422
431
|
|
|
423
432
|
// eslint-disable-next-line require-await
|
|
424
|
-
async getUsersByDids({ dids, query }) {
|
|
433
|
+
async getUsersByDids({ dids, query }, context = {}) {
|
|
425
434
|
const {
|
|
426
435
|
approved,
|
|
427
436
|
includeTags = false,
|
|
428
437
|
includePassports = false,
|
|
429
438
|
includeConnectedAccounts = false,
|
|
439
|
+
includeFollowStatus = false,
|
|
430
440
|
selection = {},
|
|
431
441
|
} = query || {};
|
|
442
|
+
|
|
443
|
+
const { user } = context || {};
|
|
444
|
+
|
|
432
445
|
// 如果 dids 包含 *,则查询全部
|
|
433
446
|
const condition = dids.includes('*') ? {} : { did: dids };
|
|
434
447
|
if (isNullOrUndefined(approved) === false) {
|
|
@@ -448,7 +461,12 @@ SELECT did,inviter,generation FROM UserTree`.trim();
|
|
|
448
461
|
include.push(this.getConnectedInclude());
|
|
449
462
|
}
|
|
450
463
|
|
|
451
|
-
|
|
464
|
+
let result = await this.find({ where: condition, include }, selection);
|
|
465
|
+
|
|
466
|
+
if (includeFollowStatus && user?.did) {
|
|
467
|
+
result = await this._enrichWithFollowStatus(result, user.did, dids);
|
|
468
|
+
}
|
|
469
|
+
|
|
452
470
|
return result;
|
|
453
471
|
}
|
|
454
472
|
|
|
@@ -837,18 +855,40 @@ SELECT did,inviter,generation FROM UserTree`.trim();
|
|
|
837
855
|
dids.forEach((did) => {
|
|
838
856
|
const { error } = Joi.DID().validate(did);
|
|
839
857
|
if (error) {
|
|
840
|
-
throw new CustomError(400, `Invalid did: ${did}`);
|
|
858
|
+
throw new CustomError(400, JSON.stringify({ code: 'INVALID_USER', message: `Invalid did: ${did}` }));
|
|
841
859
|
}
|
|
842
860
|
});
|
|
843
861
|
|
|
844
862
|
const users = await Promise.all(dids.map((did) => this.getUserByDid(did)));
|
|
845
863
|
dids.forEach((did, index) => {
|
|
846
864
|
if (!users[index]) {
|
|
847
|
-
throw new CustomError(
|
|
865
|
+
throw new CustomError(400, JSON.stringify({ code: 'USER_NOT_FOUND', message: `User does not exist: ${did}` }));
|
|
848
866
|
}
|
|
849
867
|
});
|
|
850
868
|
}
|
|
851
869
|
|
|
870
|
+
/**
|
|
871
|
+
* 为用户列表添加关注状态
|
|
872
|
+
* @private
|
|
873
|
+
* @param {Array} users - 用户列表
|
|
874
|
+
* @param {string} currentUserDid - 当前用户的 DID
|
|
875
|
+
* @param {Array} userDids - 用户DID列表,如果未提供则从 users 中提取
|
|
876
|
+
* @returns {Promise<Array>} - 添加了 isFollowing 字段的用户列表
|
|
877
|
+
*/
|
|
878
|
+
async _enrichWithFollowStatus(users, currentUserDid, userDids = null) {
|
|
879
|
+
if (!currentUserDid || !users || users.length === 0) {
|
|
880
|
+
return users;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const targetDids = userDids || users.map((item) => item.did);
|
|
884
|
+
const followerMap = await this.isFollowing(currentUserDid, targetDids);
|
|
885
|
+
|
|
886
|
+
return users.map((item) => ({
|
|
887
|
+
...item,
|
|
888
|
+
isFollowing: followerMap[item.did],
|
|
889
|
+
}));
|
|
890
|
+
}
|
|
891
|
+
|
|
852
892
|
/**
|
|
853
893
|
* 关注用户
|
|
854
894
|
* @param {string} followerDid - 关注者的 DID
|
|
@@ -858,7 +898,7 @@ SELECT did,inviter,generation FROM UserTree`.trim();
|
|
|
858
898
|
checkUserFollowers(this.userFollowers);
|
|
859
899
|
|
|
860
900
|
if (followerDid === userDid) {
|
|
861
|
-
throw new CustomError(400, 'Cannot follow yourself');
|
|
901
|
+
throw new CustomError(400, JSON.stringify({ code: 'SELF_FOLLOW', message: 'Cannot follow yourself' }));
|
|
862
902
|
}
|
|
863
903
|
|
|
864
904
|
// 检查两个用户是否存在 + 检查是否已经关注(并行执行)
|
|
@@ -868,7 +908,7 @@ SELECT did,inviter,generation FROM UserTree`.trim();
|
|
|
868
908
|
]);
|
|
869
909
|
|
|
870
910
|
if (existingFollow) {
|
|
871
|
-
throw new CustomError(400, 'Already following this user');
|
|
911
|
+
throw new CustomError(400, JSON.stringify({ code: 'ALREADY_FOLLOWING', message: 'Already following this user' }));
|
|
872
912
|
}
|
|
873
913
|
|
|
874
914
|
// 创建关注关系
|
|
@@ -895,9 +935,22 @@ SELECT did,inviter,generation FROM UserTree`.trim();
|
|
|
895
935
|
|
|
896
936
|
/**
|
|
897
937
|
* 通用的关注关系查询函数
|
|
938
|
+
* @param {string} targetDid - 目标用户DID
|
|
939
|
+
* @param {string} type - 查询类型 'followers' 或 'following'
|
|
940
|
+
* @param {object} options - 选项
|
|
941
|
+
* @param {object} options.paging - 分页参数
|
|
942
|
+
* @param {object} options.sort - 排序参数
|
|
943
|
+
* @param {boolean} options.includeUserInfo - 是否包含用户详细信息,默认true
|
|
944
|
+
* @param {boolean} options.includeFollowStatus - 是否包含当前用户的关注状态,默认 true
|
|
945
|
+
* @param {object} context - 上下文,包含当前用户信息
|
|
898
946
|
* @private
|
|
899
947
|
*/
|
|
900
|
-
async _getFollowRelations(
|
|
948
|
+
async _getFollowRelations(
|
|
949
|
+
targetDid,
|
|
950
|
+
type,
|
|
951
|
+
{ paging, sort, includeUserInfo = true, includeFollowStatus = true } = {},
|
|
952
|
+
context = {}
|
|
953
|
+
) {
|
|
901
954
|
checkUserFollowers(this.userFollowers);
|
|
902
955
|
|
|
903
956
|
const { user: contextUser } = context;
|
|
@@ -911,44 +964,63 @@ SELECT did,inviter,generation FROM UserTree`.trim();
|
|
|
911
964
|
const sorting = [['createdAt', sort?.createdAt ? order : 'DESC']];
|
|
912
965
|
const result = await this.userFollowers.paginate({ where }, sorting, paging);
|
|
913
966
|
|
|
914
|
-
//
|
|
967
|
+
// 准备用户信息映射
|
|
968
|
+
let userMap = new Map();
|
|
915
969
|
if (includeUserInfo && this.models.User) {
|
|
916
970
|
const userDids = [...new Set(result.list.map((follow) => (isFollowers ? follow.followerDid : follow.userDid)))];
|
|
917
971
|
const users = await this.find({
|
|
918
972
|
where: { did: { [Op.in]: userDids } },
|
|
919
973
|
attributes: ['did', 'fullName', 'avatar', 'email'],
|
|
920
974
|
});
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const enrichedList = await Promise.all(
|
|
924
|
-
result.list.map(async (follow) => {
|
|
925
|
-
const userDid = isFollowers ? follow.followerDid : follow.userDid;
|
|
926
|
-
const user = userMap.get(userDid);
|
|
927
|
-
const enrichedFollow = {
|
|
928
|
-
...follow,
|
|
929
|
-
user: user ? { ...user } : null,
|
|
930
|
-
};
|
|
975
|
+
userMap = new Map(users.map((user) => [user.did, user]));
|
|
976
|
+
}
|
|
931
977
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
978
|
+
// 准备关注状态映射
|
|
979
|
+
let followingStatusMap = {};
|
|
980
|
+
if (includeFollowStatus && contextUser?.did) {
|
|
981
|
+
if (contextUser.did !== targetDid) {
|
|
982
|
+
// 查询当前用户对列表中所有用户的关注状态
|
|
983
|
+
const targetUserDids = result.list.map((follow) => (isFollowers ? follow.followerDid : follow.userDid));
|
|
984
|
+
followingStatusMap = await this.isFollowing(contextUser.did, targetUserDids);
|
|
985
|
+
} else if (isFollowers && targetDid) {
|
|
986
|
+
// 查询目标用户对所有粉丝的关注状态
|
|
987
|
+
const followerDids = result.list.map((follow) => follow.followerDid);
|
|
988
|
+
followingStatusMap = await this.isFollowing(targetDid, followerDids);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
943
991
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
992
|
+
// 构建增强的列表
|
|
993
|
+
const enrichedList = result.list.map((follow) => {
|
|
994
|
+
const userDid = isFollowers ? follow.followerDid : follow.userDid;
|
|
995
|
+
const enrichedFollow = { ...follow };
|
|
947
996
|
|
|
948
|
-
|
|
949
|
-
|
|
997
|
+
// 添加用户信息
|
|
998
|
+
if (includeUserInfo && userMap.size > 0) {
|
|
999
|
+
const user = userMap.get(userDid);
|
|
1000
|
+
enrichedFollow.user = user ? { ...user } : null;
|
|
1001
|
+
}
|
|
950
1002
|
|
|
951
|
-
|
|
1003
|
+
// 添加关注状态
|
|
1004
|
+
if (includeFollowStatus && contextUser?.did) {
|
|
1005
|
+
if (contextUser.did !== targetDid) {
|
|
1006
|
+
// 如果查看的是别人的关注列表,需要添加是否已关注的信息
|
|
1007
|
+
enrichedFollow.isFollowing = followingStatusMap[userDid] || false;
|
|
1008
|
+
} else if (isFollowers) {
|
|
1009
|
+
// 如果是查询粉丝,需要添加是否已关注的信息
|
|
1010
|
+
enrichedFollow.isFollowing = followingStatusMap[follow.followerDid] || false;
|
|
1011
|
+
} else {
|
|
1012
|
+
// 查看自己的关注列表,默认都是已关注状态
|
|
1013
|
+
enrichedFollow.isFollowing = true;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return enrichedFollow;
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
result.list = enrichedList;
|
|
1021
|
+
|
|
1022
|
+
// 如果不需要用户信息且不需要关注状态,只返回用户DID
|
|
1023
|
+
if (!includeUserInfo && !includeFollowStatus) {
|
|
952
1024
|
result.list = result.list.map((follow) => (isFollowers ? follow.followerDid : follow.userDid));
|
|
953
1025
|
}
|
|
954
1026
|
|
|
@@ -970,53 +1042,102 @@ SELECT did,inviter,generation FROM UserTree`.trim();
|
|
|
970
1042
|
}
|
|
971
1043
|
|
|
972
1044
|
/**
|
|
973
|
-
*
|
|
1045
|
+
* 批量检查用户A是否关注了多个用户
|
|
974
1046
|
* @param {string} followerDid - 关注者的 DID
|
|
975
|
-
* @param {string}
|
|
1047
|
+
* @param {string[]} userDids - 被关注用户的 DID 数组
|
|
1048
|
+
* @returns {Promise<{[userDid: string]: boolean}>} - 返回一个对象,键为用户DID,值为是否关注的布尔值
|
|
976
1049
|
*/
|
|
977
|
-
async isFollowing(followerDid,
|
|
978
|
-
if (!this.userFollowers) {
|
|
979
|
-
return false;
|
|
1050
|
+
async isFollowing(followerDid, userDids) {
|
|
1051
|
+
if (!this.userFollowers || !followerDid || !Array.isArray(userDids) || userDids.length === 0) {
|
|
1052
|
+
return userDids.reduce((acc, userDid) => ({ ...acc, [userDid]: false }), {});
|
|
980
1053
|
}
|
|
981
1054
|
|
|
982
1055
|
try {
|
|
983
|
-
|
|
984
|
-
|
|
1056
|
+
// 一次性查询所有关注关系
|
|
1057
|
+
const follows = await this.userFollowers.find({
|
|
985
1058
|
followerDid,
|
|
1059
|
+
userDid: { [Op.in]: userDids },
|
|
1060
|
+
});
|
|
1061
|
+
// 构建已关注用户的 Set,提高查找效率
|
|
1062
|
+
const followingSet = new Set(follows.map((follow) => follow.userDid));
|
|
1063
|
+
|
|
1064
|
+
// 构建结果对象
|
|
1065
|
+
const result = {};
|
|
1066
|
+
userDids.forEach((userDid) => {
|
|
1067
|
+
result[userDid] = followingSet.has(userDid);
|
|
986
1068
|
});
|
|
987
1069
|
|
|
988
|
-
return
|
|
1070
|
+
return result;
|
|
989
1071
|
} catch (error) {
|
|
990
|
-
logger.error('Failed to check following status:', error);
|
|
991
|
-
|
|
1072
|
+
logger.error('Failed to check multiple following status:', error);
|
|
1073
|
+
// 出错时返回全部为 false 的结果
|
|
1074
|
+
return userDids.reduce((acc, userDid) => ({ ...acc, [userDid]: false }), {});
|
|
992
1075
|
}
|
|
993
1076
|
}
|
|
994
1077
|
|
|
995
1078
|
/**
|
|
996
|
-
*
|
|
997
|
-
* @param {string}
|
|
1079
|
+
* 批量获取多个用户的关注统计信息
|
|
1080
|
+
* @param {string[]} userDids - 用户的 DID 数组
|
|
1081
|
+
* @returns {Promise<{[userDid: string]: {followers: number, following: number}}>} - 返回对象,键为用户DID,值为统计信息
|
|
998
1082
|
*/
|
|
999
|
-
async getFollowStats(
|
|
1000
|
-
if (!this.userFollowers) {
|
|
1001
|
-
return
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1083
|
+
async getFollowStats(userDids = []) {
|
|
1084
|
+
if (!this.userFollowers || !Array.isArray(userDids) || userDids.length === 0) {
|
|
1085
|
+
return userDids.reduce(
|
|
1086
|
+
(acc, userDid) => ({
|
|
1087
|
+
...acc,
|
|
1088
|
+
[userDid]: { followers: 0, following: 0 },
|
|
1089
|
+
}),
|
|
1090
|
+
{}
|
|
1091
|
+
);
|
|
1005
1092
|
}
|
|
1006
1093
|
|
|
1007
1094
|
try {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1095
|
+
// 使用原生 SQL 查询进行批量统计,提高性能
|
|
1096
|
+
const followersQuery = `
|
|
1097
|
+
SELECT "userDid", COUNT(*) as count
|
|
1098
|
+
FROM user_followers
|
|
1099
|
+
WHERE "userDid" IN (:userDids)
|
|
1100
|
+
GROUP BY "userDid"
|
|
1101
|
+
`;
|
|
1102
|
+
|
|
1103
|
+
const followingQuery = `
|
|
1104
|
+
SELECT "followerDid", COUNT(*) as count
|
|
1105
|
+
FROM user_followers
|
|
1106
|
+
WHERE "followerDid" IN (:userDids)
|
|
1107
|
+
GROUP BY "followerDid"
|
|
1108
|
+
`;
|
|
1109
|
+
|
|
1110
|
+
const [followersResults, followingResults] = await Promise.all([
|
|
1111
|
+
this.model.sequelize.query(followersQuery, { replacements: { userDids }, type: Sequelize.QueryTypes.SELECT }),
|
|
1112
|
+
this.model.sequelize.query(followingQuery, { replacements: { userDids }, type: Sequelize.QueryTypes.SELECT }),
|
|
1011
1113
|
]);
|
|
1012
1114
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1115
|
+
// 使用 Map 构造函数和数组映射优化数据结构构建
|
|
1116
|
+
const followersMap = new Map(followersResults.map((row) => [row.userDid, Number(row.count)]));
|
|
1117
|
+
const followingMap = new Map(followingResults.map((row) => [row.followerDid, Number(row.count)]));
|
|
1118
|
+
|
|
1119
|
+
// 使用 Object.fromEntries 优化结果对象构建
|
|
1120
|
+
const result = Object.fromEntries(
|
|
1121
|
+
userDids.map((userDid) => [
|
|
1122
|
+
userDid,
|
|
1123
|
+
{
|
|
1124
|
+
followers: followersMap.get(userDid) || 0,
|
|
1125
|
+
following: followingMap.get(userDid) || 0,
|
|
1126
|
+
},
|
|
1127
|
+
])
|
|
1128
|
+
);
|
|
1129
|
+
|
|
1130
|
+
return result;
|
|
1017
1131
|
} catch (error) {
|
|
1018
|
-
logger.error('Failed to get follow stats:', error);
|
|
1019
|
-
|
|
1132
|
+
logger.error('Failed to get multiple follow stats:', error);
|
|
1133
|
+
// 出错时返回全部为 0 的结果
|
|
1134
|
+
return userDids.reduce(
|
|
1135
|
+
(acc, userDid) => ({
|
|
1136
|
+
...acc,
|
|
1137
|
+
[userDid]: { followers: 0, following: 0 },
|
|
1138
|
+
}),
|
|
1139
|
+
{}
|
|
1140
|
+
);
|
|
1020
1141
|
}
|
|
1021
1142
|
}
|
|
1022
1143
|
}
|
package/lib/util/blocklet.js
CHANGED
|
@@ -925,8 +925,8 @@ const getProcessState = async (processId) => {
|
|
|
925
925
|
return statusMap[info.pm2_env.status];
|
|
926
926
|
};
|
|
927
927
|
|
|
928
|
-
const getProcessInfo = (processId, { throwOnNotExist = true } = {}) =>
|
|
929
|
-
getPm2ProcessInfo(processId, { printError: logger.error.bind(logger), throwOnNotExist });
|
|
928
|
+
const getProcessInfo = (processId, { throwOnNotExist = true, timeout = 10_000 } = {}) =>
|
|
929
|
+
getPm2ProcessInfo(processId, { printError: logger.error.bind(logger), throwOnNotExist, timeout });
|
|
930
930
|
|
|
931
931
|
const deleteProcess = (processId) => {
|
|
932
932
|
return new Promise((resolve, reject) => {
|
|
@@ -1027,7 +1027,7 @@ const _checkProcessHealthy = async (blocklet, { minConsecutiveTime, timeout, log
|
|
|
1027
1027
|
// ensure pm2 status is 'online'
|
|
1028
1028
|
const getStatus = async () => {
|
|
1029
1029
|
try {
|
|
1030
|
-
const info = await getProcessInfo(processId);
|
|
1030
|
+
const info = await getProcessInfo(processId, { timeout: 3_000 });
|
|
1031
1031
|
return info.pm2_env.status;
|
|
1032
1032
|
} catch (err) {
|
|
1033
1033
|
logger.error('blocklet checkStart error', { error: err, processId, name });
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.49-beta-
|
|
6
|
+
"version": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,22 +19,22 @@
|
|
|
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.49-beta-
|
|
23
|
-
"@abtnode/auth": "1.16.49-beta-
|
|
24
|
-
"@abtnode/certificate-manager": "1.16.49-beta-
|
|
25
|
-
"@abtnode/constant": "1.16.49-beta-
|
|
26
|
-
"@abtnode/cron": "1.16.49-beta-
|
|
27
|
-
"@abtnode/db-cache": "1.16.49-beta-
|
|
28
|
-
"@abtnode/docker-utils": "1.16.49-beta-
|
|
29
|
-
"@abtnode/logger": "1.16.49-beta-
|
|
30
|
-
"@abtnode/models": "1.16.49-beta-
|
|
31
|
-
"@abtnode/queue": "1.16.49-beta-
|
|
32
|
-
"@abtnode/rbac": "1.16.49-beta-
|
|
33
|
-
"@abtnode/router-provider": "1.16.49-beta-
|
|
34
|
-
"@abtnode/static-server": "1.16.49-beta-
|
|
35
|
-
"@abtnode/timemachine": "1.16.49-beta-
|
|
36
|
-
"@abtnode/util": "1.16.49-beta-
|
|
37
|
-
"@aigne/aigne-hub": "^0.6.
|
|
22
|
+
"@abtnode/analytics": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
23
|
+
"@abtnode/auth": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
24
|
+
"@abtnode/certificate-manager": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
25
|
+
"@abtnode/constant": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
26
|
+
"@abtnode/cron": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
27
|
+
"@abtnode/db-cache": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
28
|
+
"@abtnode/docker-utils": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
29
|
+
"@abtnode/logger": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
30
|
+
"@abtnode/models": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
31
|
+
"@abtnode/queue": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
32
|
+
"@abtnode/rbac": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
33
|
+
"@abtnode/router-provider": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
34
|
+
"@abtnode/static-server": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
35
|
+
"@abtnode/timemachine": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
36
|
+
"@abtnode/util": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
37
|
+
"@aigne/aigne-hub": "^0.6.9",
|
|
38
38
|
"@arcblock/did": "1.22.2",
|
|
39
39
|
"@arcblock/did-connect-js": "1.22.2",
|
|
40
40
|
"@arcblock/did-ext": "1.22.2",
|
|
@@ -45,16 +45,16 @@
|
|
|
45
45
|
"@arcblock/pm2-events": "^0.0.5",
|
|
46
46
|
"@arcblock/validator": "1.22.2",
|
|
47
47
|
"@arcblock/vc": "1.22.2",
|
|
48
|
-
"@blocklet/constant": "1.16.49-beta-
|
|
48
|
+
"@blocklet/constant": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
49
49
|
"@blocklet/did-space-js": "^1.1.18",
|
|
50
|
-
"@blocklet/env": "1.16.49-beta-
|
|
50
|
+
"@blocklet/env": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
51
51
|
"@blocklet/error": "^0.2.5",
|
|
52
|
-
"@blocklet/meta": "1.16.49-beta-
|
|
53
|
-
"@blocklet/resolver": "1.16.49-beta-
|
|
54
|
-
"@blocklet/sdk": "1.16.49-beta-
|
|
55
|
-
"@blocklet/server-js": "1.16.49-beta-
|
|
56
|
-
"@blocklet/store": "1.16.49-beta-
|
|
57
|
-
"@blocklet/theme": "^3.1.
|
|
52
|
+
"@blocklet/meta": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
53
|
+
"@blocklet/resolver": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
54
|
+
"@blocklet/sdk": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
55
|
+
"@blocklet/server-js": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
56
|
+
"@blocklet/store": "1.16.49-beta-20250827-025603-2bb1a7ee",
|
|
57
|
+
"@blocklet/theme": "^3.1.30",
|
|
58
58
|
"@fidm/x509": "^1.2.1",
|
|
59
59
|
"@ocap/mcrypto": "1.22.2",
|
|
60
60
|
"@ocap/util": "1.22.2",
|
|
@@ -118,5 +118,5 @@
|
|
|
118
118
|
"jest": "^29.7.0",
|
|
119
119
|
"unzipper": "^0.10.11"
|
|
120
120
|
},
|
|
121
|
-
"gitHead": "
|
|
121
|
+
"gitHead": "2b70eea34e8bc8546abb09d38935e8906d9586fd"
|
|
122
122
|
}
|