@abtnode/core 1.17.3-beta-20251118-061144-335cd35d → 1.17.3-beta-20251119-034511-f26047c0
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 +49 -0
- package/lib/index.js +1 -0
- package/lib/states/notification.js +47 -0
- package/lib/util/notification.js +185 -1
- package/package.json +24 -24
package/lib/api/team.js
CHANGED
|
@@ -68,6 +68,7 @@ const { passportDisplaySchema } = require('../validators/util');
|
|
|
68
68
|
const { validateUserRolePassport } = require('../util/validate-user-role-passport');
|
|
69
69
|
const { getOrgInviteLink, createOrgValidators, isOrgOwner, isAdmingPath } = require('../util/org');
|
|
70
70
|
const { createOrgInputSchema, updateOrgInputSchema } = require('../validators/org');
|
|
71
|
+
const { checkPushChannelAvailable, getNotificationPushState } = require('../util/notification');
|
|
71
72
|
|
|
72
73
|
const sanitizeUrl = (url) => {
|
|
73
74
|
if (!url) {
|
|
@@ -3410,6 +3411,54 @@ class TeamAPI extends EventEmitter {
|
|
|
3410
3411
|
throw err;
|
|
3411
3412
|
}
|
|
3412
3413
|
}
|
|
3414
|
+
|
|
3415
|
+
async getNotificationStats({ teamDid, since = '1h' }) {
|
|
3416
|
+
let startTime = dayjs().subtract(1, 'hours').toDate();
|
|
3417
|
+
|
|
3418
|
+
if (since && typeof since === 'string') {
|
|
3419
|
+
const sinceMatch = since.match(/^(\d+)h$/);
|
|
3420
|
+
if (sinceMatch) {
|
|
3421
|
+
const hours = parseInt(sinceMatch[1], 10);
|
|
3422
|
+
if (hours >= 1 && hours <= 24) {
|
|
3423
|
+
startTime = dayjs().subtract(hours, 'hours').toDate();
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
try {
|
|
3429
|
+
const state = await this.getNotificationState(teamDid);
|
|
3430
|
+
const isServer = this.teamManager.isNodeTeam(teamDid);
|
|
3431
|
+
const blocklet = isServer
|
|
3432
|
+
? {}
|
|
3433
|
+
: await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
|
|
3434
|
+
const channelsAvailable = checkPushChannelAvailable(blocklet, isServer);
|
|
3435
|
+
const results = await state.getNotificationsBySince({ since });
|
|
3436
|
+
|
|
3437
|
+
if (results.length === 0) {
|
|
3438
|
+
return {
|
|
3439
|
+
healthy: true,
|
|
3440
|
+
message: `There have been no push records since ${startTime}. Please choose another time range`,
|
|
3441
|
+
since: startTime,
|
|
3442
|
+
channels: channelsAvailable,
|
|
3443
|
+
};
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
const pushState = getNotificationPushState(results, channelsAvailable, isServer);
|
|
3447
|
+
|
|
3448
|
+
return {
|
|
3449
|
+
healthy: true,
|
|
3450
|
+
since: startTime,
|
|
3451
|
+
channels: pushState,
|
|
3452
|
+
};
|
|
3453
|
+
} catch (err) {
|
|
3454
|
+
logger.error('Get notification service health failed', err, { teamDid });
|
|
3455
|
+
return {
|
|
3456
|
+
healthy: false,
|
|
3457
|
+
error: err.message,
|
|
3458
|
+
since: startTime,
|
|
3459
|
+
};
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3413
3462
|
}
|
|
3414
3463
|
|
|
3415
3464
|
module.exports = TeamAPI;
|
package/lib/index.js
CHANGED
|
@@ -669,6 +669,7 @@ function ABTNode(options) {
|
|
|
669
669
|
getNotificationComponents: teamAPI.getNotificationComponents.bind(teamAPI),
|
|
670
670
|
resendNotification: blockletManager.resendNotification.bind(blockletManager),
|
|
671
671
|
getReceivers: teamAPI.getReceivers.bind(teamAPI),
|
|
672
|
+
getNotificationStats: teamAPI.getNotificationStats.bind(teamAPI),
|
|
672
673
|
|
|
673
674
|
// AuditLog
|
|
674
675
|
createAuditLog: async (params) => {
|
|
@@ -4,6 +4,7 @@ const { Sequelize, Op } = require('sequelize');
|
|
|
4
4
|
const { isValid } = require('@arcblock/did');
|
|
5
5
|
const { Joi } = require('@arcblock/validator');
|
|
6
6
|
const { ROLES, SERVER_ROLES, NOTIFICATION_SEND_CHANNEL, NOTIFICATION_SEND_STATUS } = require('@abtnode/constant');
|
|
7
|
+
const dayjs = require('@abtnode/util/lib/dayjs');
|
|
7
8
|
const BaseState = require('./base');
|
|
8
9
|
const { getReceiversStatistics } = require('../util/notification');
|
|
9
10
|
|
|
@@ -954,6 +955,52 @@ class NotificationState extends BaseState {
|
|
|
954
955
|
});
|
|
955
956
|
return Number(countResult.total);
|
|
956
957
|
}
|
|
958
|
+
|
|
959
|
+
async getNotificationsBySince({ since = '1h' }) {
|
|
960
|
+
// 解析 since 参数,格式为 "数字h",例如 "1h", "24h"
|
|
961
|
+
const sinceMatch = since.match(/^(\d+)h$/);
|
|
962
|
+
if (!sinceMatch) {
|
|
963
|
+
throw new Error('Invalid since format. Expected format: "1h", "2h", "24h", etc.');
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const hours = parseInt(sinceMatch[1], 10);
|
|
967
|
+
|
|
968
|
+
// 验证范围:最小 1h,最大 24h
|
|
969
|
+
if (hours < 1 || hours > 24) {
|
|
970
|
+
throw new Error('The since parameter must be between 1h and 24h.');
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// 计算时间范围
|
|
974
|
+
const startTime = dayjs().subtract(hours, 'hours').toDate();
|
|
975
|
+
|
|
976
|
+
// 第一步:在 notifications 表中查询符合时间范围的通知 ID
|
|
977
|
+
const notifications = await this.model.findAll({
|
|
978
|
+
where: {
|
|
979
|
+
createdAt: {
|
|
980
|
+
[Op.gte]: startTime,
|
|
981
|
+
},
|
|
982
|
+
},
|
|
983
|
+
attributes: ['id'],
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
// 如果没有找到符合条件的通知,直接返回空数组
|
|
987
|
+
if (notifications.length === 0) {
|
|
988
|
+
return [];
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// 提取通知 ID 列表
|
|
992
|
+
const notificationIds = notifications.map((n) => n.id);
|
|
993
|
+
|
|
994
|
+
// 第二步:根据通知 ID 在 notification_receivers 表中查询数据
|
|
995
|
+
return this.notificationReceivers.model.findAll({
|
|
996
|
+
where: {
|
|
997
|
+
notificationId: {
|
|
998
|
+
[Op.in]: notificationIds,
|
|
999
|
+
},
|
|
1000
|
+
},
|
|
1001
|
+
order: [['createdAt', 'DESC']],
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
957
1004
|
}
|
|
958
1005
|
|
|
959
1006
|
module.exports = NotificationState;
|
package/lib/util/notification.js
CHANGED
|
@@ -2,7 +2,11 @@ const { joinURL } = require('ufo');
|
|
|
2
2
|
const isUrl = require('is-url');
|
|
3
3
|
const omit = require('lodash/omit');
|
|
4
4
|
const groupBy = require('lodash/groupBy');
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
NOTIFICATION_SEND_STATUS,
|
|
7
|
+
NOTIFICATION_SEND_CHANNEL,
|
|
8
|
+
NOTIFICATION_SEND_FAILED_REASON,
|
|
9
|
+
} = require('@abtnode/constant');
|
|
6
10
|
|
|
7
11
|
const REMOVE_FIELDS = [
|
|
8
12
|
'description',
|
|
@@ -131,9 +135,189 @@ function getReceiversStatistics(receivers) {
|
|
|
131
135
|
};
|
|
132
136
|
}
|
|
133
137
|
|
|
138
|
+
/**
|
|
139
|
+
* 检测消息推送渠道是否开启
|
|
140
|
+
*/
|
|
141
|
+
function checkPushChannelAvailable(blocklet = {}, isServer = false) {
|
|
142
|
+
const config = blocklet.settings?.notification || {};
|
|
143
|
+
|
|
144
|
+
const pushKitEnabled = config.pushKit?.enabled && config.pushKit?.endpoint;
|
|
145
|
+
const emailEnabled = config.email?.enabled;
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
[NOTIFICATION_SEND_CHANNEL.WALLET]: {
|
|
149
|
+
enabled: true,
|
|
150
|
+
},
|
|
151
|
+
...(isServer
|
|
152
|
+
? {}
|
|
153
|
+
: {
|
|
154
|
+
[NOTIFICATION_SEND_CHANNEL.PUSH]: {
|
|
155
|
+
enabled: !!pushKitEnabled,
|
|
156
|
+
},
|
|
157
|
+
[NOTIFICATION_SEND_CHANNEL.EMAIL]: {
|
|
158
|
+
enabled: emailEnabled || false,
|
|
159
|
+
},
|
|
160
|
+
}),
|
|
161
|
+
[NOTIFICATION_SEND_CHANNEL.WEBHOOK]: {
|
|
162
|
+
enabled: true,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function getColumnField(channel, suffix) {
|
|
168
|
+
switch (channel) {
|
|
169
|
+
case NOTIFICATION_SEND_CHANNEL.WALLET:
|
|
170
|
+
return `wallet${suffix}`;
|
|
171
|
+
case NOTIFICATION_SEND_CHANNEL.PUSH:
|
|
172
|
+
return `pushKit${suffix}`;
|
|
173
|
+
case NOTIFICATION_SEND_CHANNEL.EMAIL:
|
|
174
|
+
return `email${suffix}`;
|
|
175
|
+
default:
|
|
176
|
+
return '';
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const SEND_STATUS_MAP = {
|
|
181
|
+
[NOTIFICATION_SEND_STATUS.PENDING]: 'pending',
|
|
182
|
+
[NOTIFICATION_SEND_STATUS.SENT]: 'success',
|
|
183
|
+
[NOTIFICATION_SEND_STATUS.FAILED]: 'failed',
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
function getFailedReasonMessage(reason, channel) {
|
|
187
|
+
if (!reason || !channel) {
|
|
188
|
+
return reason;
|
|
189
|
+
}
|
|
190
|
+
switch (reason) {
|
|
191
|
+
case NOTIFICATION_SEND_FAILED_REASON.USER_DISABLED:
|
|
192
|
+
return `The user has disabled the notification for the "${channel}" channel.`;
|
|
193
|
+
case NOTIFICATION_SEND_FAILED_REASON.CHANNEL_UNAVAILABLE:
|
|
194
|
+
return `The "${channel}" channel is not available.`;
|
|
195
|
+
case NOTIFICATION_SEND_FAILED_REASON.CHANNEL_DISABLED:
|
|
196
|
+
return `The "${channel}" channel was not selected for this notification.`;
|
|
197
|
+
case NOTIFICATION_SEND_FAILED_REASON.NOT_ONLINE:
|
|
198
|
+
return 'The user is not online.';
|
|
199
|
+
default:
|
|
200
|
+
return reason;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function getStatisticsState(results, channel = NOTIFICATION_SEND_CHANNEL.WALLET) {
|
|
205
|
+
const last = results[0];
|
|
206
|
+
|
|
207
|
+
const getIgnoredAndFailedCount = (data) => {
|
|
208
|
+
const notSuccessCount = data.filter(
|
|
209
|
+
(item) => item[getColumnField(channel, 'SendStatus')] !== NOTIFICATION_SEND_STATUS.SENT
|
|
210
|
+
).length;
|
|
211
|
+
const ignoredCount = data.filter(
|
|
212
|
+
(item) =>
|
|
213
|
+
item[getColumnField(channel, 'SendStatus')] !== NOTIFICATION_SEND_STATUS.SENT &&
|
|
214
|
+
(item[getColumnField(channel, 'SendStatus')] === NOTIFICATION_SEND_STATUS.PENDING ||
|
|
215
|
+
[
|
|
216
|
+
...Object.values(NOTIFICATION_SEND_FAILED_REASON),
|
|
217
|
+
'Email Service is not available.',
|
|
218
|
+
'Push Kit Service is not Enabled.',
|
|
219
|
+
].includes(item[getColumnField(channel, 'SendFailedReason')]))
|
|
220
|
+
).length;
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
ignored: ignoredCount,
|
|
224
|
+
failed: notSuccessCount - ignoredCount,
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
last: {
|
|
230
|
+
sendAt: last[getColumnField(channel, 'SendAt')],
|
|
231
|
+
sendStatus: SEND_STATUS_MAP[last[getColumnField(channel, 'SendStatus')]],
|
|
232
|
+
reason: getFailedReasonMessage(last[getColumnField(channel, 'SendFailedReason')], channel),
|
|
233
|
+
},
|
|
234
|
+
state: {
|
|
235
|
+
total: results.length,
|
|
236
|
+
success: results.filter((item) => item[getColumnField(channel, 'SendStatus')] === NOTIFICATION_SEND_STATUS.SENT)
|
|
237
|
+
.length,
|
|
238
|
+
...getIgnoredAndFailedCount(results),
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function getWebhookStatisticsState(results) {
|
|
244
|
+
// 收集所有 URL 的最新记录
|
|
245
|
+
const latestRecords = [];
|
|
246
|
+
|
|
247
|
+
const state = results.reduce(
|
|
248
|
+
(acc, item) => {
|
|
249
|
+
const webhook = item.webhook ?? {};
|
|
250
|
+
Object.values(webhook).forEach((records) => {
|
|
251
|
+
// 对每个 URL 的记录数组按 sendAt 倒序排列,取最新的
|
|
252
|
+
const latestRecord = records.sort((a, b) => new Date(b.sendAt) - new Date(a.sendAt))[0];
|
|
253
|
+
if (latestRecord) {
|
|
254
|
+
latestRecords.push(latestRecord);
|
|
255
|
+
acc.total++;
|
|
256
|
+
if (latestRecord.status === NOTIFICATION_SEND_STATUS.SENT) {
|
|
257
|
+
acc.success++;
|
|
258
|
+
} else if (latestRecord.status === NOTIFICATION_SEND_STATUS.FAILED) {
|
|
259
|
+
acc.failed++;
|
|
260
|
+
} else if (latestRecord.status === NOTIFICATION_SEND_STATUS.PENDING) {
|
|
261
|
+
acc.ignored++;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
return acc;
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
total: 0,
|
|
269
|
+
success: 0,
|
|
270
|
+
failed: 0,
|
|
271
|
+
ignored: 0,
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// 从所有 URL 的最新记录中,找到最新的那个
|
|
276
|
+
const lastWebhook = latestRecords.sort((a, b) => new Date(b.sendAt) - new Date(a.sendAt))[0];
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
last: lastWebhook
|
|
280
|
+
? {
|
|
281
|
+
sendAt: lastWebhook.sendAt,
|
|
282
|
+
type: lastWebhook.type,
|
|
283
|
+
sendStatus: SEND_STATUS_MAP[lastWebhook.status],
|
|
284
|
+
reason: lastWebhook.failedReason,
|
|
285
|
+
}
|
|
286
|
+
: null,
|
|
287
|
+
state,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function getNotificationPushState(results, channelsAvailable, isServer = false) {
|
|
292
|
+
return {
|
|
293
|
+
wallet: {
|
|
294
|
+
...channelsAvailable[NOTIFICATION_SEND_CHANNEL.WALLET],
|
|
295
|
+
...getStatisticsState(results, NOTIFICATION_SEND_CHANNEL.WALLET),
|
|
296
|
+
},
|
|
297
|
+
...(isServer
|
|
298
|
+
? {}
|
|
299
|
+
: {
|
|
300
|
+
pushKit: {
|
|
301
|
+
...channelsAvailable[NOTIFICATION_SEND_CHANNEL.PUSH],
|
|
302
|
+
...getStatisticsState(results, NOTIFICATION_SEND_CHANNEL.PUSH),
|
|
303
|
+
},
|
|
304
|
+
email: {
|
|
305
|
+
...channelsAvailable[NOTIFICATION_SEND_CHANNEL.EMAIL],
|
|
306
|
+
...getStatisticsState(results, NOTIFICATION_SEND_CHANNEL.EMAIL),
|
|
307
|
+
},
|
|
308
|
+
}),
|
|
309
|
+
webhook: {
|
|
310
|
+
...channelsAvailable[NOTIFICATION_SEND_CHANNEL.WEBHOOK],
|
|
311
|
+
...getWebhookStatisticsState(results),
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
134
316
|
module.exports = {
|
|
135
317
|
transformNotification,
|
|
136
318
|
getStatusCounts,
|
|
137
319
|
getWebhookStatusCounts,
|
|
138
320
|
getReceiversStatistics,
|
|
321
|
+
checkPushChannelAvailable,
|
|
322
|
+
getNotificationPushState,
|
|
139
323
|
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.17.3-beta-
|
|
6
|
+
"version": "1.17.3-beta-20251119-034511-f26047c0",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -17,21 +17,21 @@
|
|
|
17
17
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
18
18
|
"license": "Apache-2.0",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@abtnode/analytics": "1.17.3-beta-
|
|
21
|
-
"@abtnode/auth": "1.17.3-beta-
|
|
22
|
-
"@abtnode/certificate-manager": "1.17.3-beta-
|
|
23
|
-
"@abtnode/constant": "1.17.3-beta-
|
|
24
|
-
"@abtnode/cron": "1.17.3-beta-
|
|
25
|
-
"@abtnode/db-cache": "1.17.3-beta-
|
|
26
|
-
"@abtnode/docker-utils": "1.17.3-beta-
|
|
27
|
-
"@abtnode/logger": "1.17.3-beta-
|
|
28
|
-
"@abtnode/models": "1.17.3-beta-
|
|
29
|
-
"@abtnode/queue": "1.17.3-beta-
|
|
30
|
-
"@abtnode/rbac": "1.17.3-beta-
|
|
31
|
-
"@abtnode/router-provider": "1.17.3-beta-
|
|
32
|
-
"@abtnode/static-server": "1.17.3-beta-
|
|
33
|
-
"@abtnode/timemachine": "1.17.3-beta-
|
|
34
|
-
"@abtnode/util": "1.17.3-beta-
|
|
20
|
+
"@abtnode/analytics": "1.17.3-beta-20251119-034511-f26047c0",
|
|
21
|
+
"@abtnode/auth": "1.17.3-beta-20251119-034511-f26047c0",
|
|
22
|
+
"@abtnode/certificate-manager": "1.17.3-beta-20251119-034511-f26047c0",
|
|
23
|
+
"@abtnode/constant": "1.17.3-beta-20251119-034511-f26047c0",
|
|
24
|
+
"@abtnode/cron": "1.17.3-beta-20251119-034511-f26047c0",
|
|
25
|
+
"@abtnode/db-cache": "1.17.3-beta-20251119-034511-f26047c0",
|
|
26
|
+
"@abtnode/docker-utils": "1.17.3-beta-20251119-034511-f26047c0",
|
|
27
|
+
"@abtnode/logger": "1.17.3-beta-20251119-034511-f26047c0",
|
|
28
|
+
"@abtnode/models": "1.17.3-beta-20251119-034511-f26047c0",
|
|
29
|
+
"@abtnode/queue": "1.17.3-beta-20251119-034511-f26047c0",
|
|
30
|
+
"@abtnode/rbac": "1.17.3-beta-20251119-034511-f26047c0",
|
|
31
|
+
"@abtnode/router-provider": "1.17.3-beta-20251119-034511-f26047c0",
|
|
32
|
+
"@abtnode/static-server": "1.17.3-beta-20251119-034511-f26047c0",
|
|
33
|
+
"@abtnode/timemachine": "1.17.3-beta-20251119-034511-f26047c0",
|
|
34
|
+
"@abtnode/util": "1.17.3-beta-20251119-034511-f26047c0",
|
|
35
35
|
"@aigne/aigne-hub": "^0.10.9",
|
|
36
36
|
"@arcblock/did": "^1.27.7",
|
|
37
37
|
"@arcblock/did-connect-js": "^1.27.7",
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
"@arcblock/pm2-events": "^0.0.5",
|
|
44
44
|
"@arcblock/validator": "^1.27.7",
|
|
45
45
|
"@arcblock/vc": "^1.27.7",
|
|
46
|
-
"@blocklet/constant": "1.17.3-beta-
|
|
46
|
+
"@blocklet/constant": "1.17.3-beta-20251119-034511-f26047c0",
|
|
47
47
|
"@blocklet/did-space-js": "^1.2.4",
|
|
48
|
-
"@blocklet/env": "1.17.3-beta-
|
|
48
|
+
"@blocklet/env": "1.17.3-beta-20251119-034511-f26047c0",
|
|
49
49
|
"@blocklet/error": "^0.3.3",
|
|
50
|
-
"@blocklet/meta": "1.17.3-beta-
|
|
51
|
-
"@blocklet/resolver": "1.17.3-beta-
|
|
52
|
-
"@blocklet/sdk": "1.17.3-beta-
|
|
53
|
-
"@blocklet/server-js": "1.17.3-beta-
|
|
54
|
-
"@blocklet/store": "1.17.3-beta-
|
|
50
|
+
"@blocklet/meta": "1.17.3-beta-20251119-034511-f26047c0",
|
|
51
|
+
"@blocklet/resolver": "1.17.3-beta-20251119-034511-f26047c0",
|
|
52
|
+
"@blocklet/sdk": "1.17.3-beta-20251119-034511-f26047c0",
|
|
53
|
+
"@blocklet/server-js": "1.17.3-beta-20251119-034511-f26047c0",
|
|
54
|
+
"@blocklet/store": "1.17.3-beta-20251119-034511-f26047c0",
|
|
55
55
|
"@blocklet/theme": "^3.2.6",
|
|
56
56
|
"@fidm/x509": "^1.2.1",
|
|
57
57
|
"@ocap/mcrypto": "^1.27.7",
|
|
@@ -116,5 +116,5 @@
|
|
|
116
116
|
"express": "^4.18.2",
|
|
117
117
|
"unzipper": "^0.10.11"
|
|
118
118
|
},
|
|
119
|
-
"gitHead": "
|
|
119
|
+
"gitHead": "7ab331f3b29e171a1e02aca80e73f35b6a161b86"
|
|
120
120
|
}
|