@abtnode/core 1.17.8-beta-20260109-075740-5f484e08 → 1.17.8-beta-20260111-112953-aed5ff39
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/access-key-manager.js +104 -0
- package/lib/api/team/invitation-manager.js +461 -0
- package/lib/api/team/notification-manager.js +189 -0
- package/lib/api/team/oauth-manager.js +60 -0
- package/lib/api/team/org-crud-manager.js +202 -0
- package/lib/api/team/org-manager.js +56 -0
- package/lib/api/team/org-member-manager.js +403 -0
- package/lib/api/team/org-query-manager.js +126 -0
- package/lib/api/team/org-resource-manager.js +186 -0
- package/lib/api/team/passport-manager.js +670 -0
- package/lib/api/team/rbac-manager.js +335 -0
- package/lib/api/team/session-manager.js +540 -0
- package/lib/api/team/store-manager.js +198 -0
- package/lib/api/team/tag-manager.js +230 -0
- package/lib/api/team/user-auth-manager.js +132 -0
- package/lib/api/team/user-manager.js +78 -0
- package/lib/api/team/user-query-manager.js +299 -0
- package/lib/api/team/user-social-manager.js +354 -0
- package/lib/api/team/user-update-manager.js +224 -0
- package/lib/api/team/verify-code-manager.js +161 -0
- package/lib/api/team.js +439 -3287
- package/lib/blocklet/manager/disk/auth-manager.js +68 -0
- package/lib/blocklet/manager/disk/backup-manager.js +288 -0
- package/lib/blocklet/manager/disk/cleanup-manager.js +157 -0
- package/lib/blocklet/manager/disk/component-manager.js +83 -0
- package/lib/blocklet/manager/disk/config-manager.js +191 -0
- package/lib/blocklet/manager/disk/controller-manager.js +64 -0
- package/lib/blocklet/manager/disk/delete-reset-manager.js +328 -0
- package/lib/blocklet/manager/disk/download-manager.js +96 -0
- package/lib/blocklet/manager/disk/env-config-manager.js +311 -0
- package/lib/blocklet/manager/disk/federated-manager.js +651 -0
- package/lib/blocklet/manager/disk/hook-manager.js +124 -0
- package/lib/blocklet/manager/disk/install-component-manager.js +95 -0
- package/lib/blocklet/manager/disk/install-core-manager.js +448 -0
- package/lib/blocklet/manager/disk/install-download-manager.js +313 -0
- package/lib/blocklet/manager/disk/install-manager.js +36 -0
- package/lib/blocklet/manager/disk/install-upgrade-manager.js +340 -0
- package/lib/blocklet/manager/disk/job-manager.js +467 -0
- package/lib/blocklet/manager/disk/lifecycle-manager.js +26 -0
- package/lib/blocklet/manager/disk/notification-manager.js +343 -0
- package/lib/blocklet/manager/disk/query-manager.js +562 -0
- package/lib/blocklet/manager/disk/settings-manager.js +507 -0
- package/lib/blocklet/manager/disk/start-manager.js +611 -0
- package/lib/blocklet/manager/disk/stop-restart-manager.js +292 -0
- package/lib/blocklet/manager/disk/update-manager.js +153 -0
- package/lib/blocklet/manager/disk.js +669 -5796
- package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +5 -0
- package/lib/blocklet/manager/lock.js +18 -0
- package/lib/event/index.js +28 -24
- package/lib/util/blocklet/app-utils.js +192 -0
- package/lib/util/blocklet/blocklet-loader.js +258 -0
- package/lib/util/blocklet/config-manager.js +232 -0
- package/lib/util/blocklet/did-document.js +240 -0
- package/lib/util/blocklet/environment.js +555 -0
- package/lib/util/blocklet/health-check.js +449 -0
- package/lib/util/blocklet/install-utils.js +365 -0
- package/lib/util/blocklet/logo.js +57 -0
- package/lib/util/blocklet/meta-utils.js +269 -0
- package/lib/util/blocklet/port-manager.js +141 -0
- package/lib/util/blocklet/process-manager.js +504 -0
- package/lib/util/blocklet/runtime-info.js +105 -0
- package/lib/util/blocklet/validation.js +418 -0
- package/lib/util/blocklet.js +98 -3066
- package/lib/util/wallet-app-notification.js +40 -0
- package/package.json +22 -22
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
const { joinURL } = require('ufo');
|
|
2
|
+
const get = require('lodash/get');
|
|
3
|
+
const omit = require('lodash/omit');
|
|
4
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:notification');
|
|
5
|
+
const { getDidDomainForBlocklet } = require('@abtnode/util/lib/get-domain-for-blocklet');
|
|
6
|
+
const { getBlockletInfo } = require('@blocklet/meta/lib/info');
|
|
7
|
+
const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
|
|
8
|
+
const {
|
|
9
|
+
WELLKNOWN_BLOCKLET_ADMIN_PATH,
|
|
10
|
+
NOTIFICATION_SEND_CHANNEL,
|
|
11
|
+
NOTIFICATION_SEND_STATUS,
|
|
12
|
+
ROLES,
|
|
13
|
+
SERVER_ROLES,
|
|
14
|
+
EVENTS,
|
|
15
|
+
} = require('@abtnode/constant');
|
|
16
|
+
|
|
17
|
+
const states = require('../../../states');
|
|
18
|
+
const { isCLI } = require('../../../util');
|
|
19
|
+
const { transformNotification } = require('../../../util/notification');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create notification for blocklet
|
|
23
|
+
* @param {Object} manager - BlockletManager instance
|
|
24
|
+
* @param {string} did - Blocklet DID
|
|
25
|
+
* @param {Object} notification - Notification data
|
|
26
|
+
* @param {Object} options
|
|
27
|
+
* @returns {Promise<Object|undefined>}
|
|
28
|
+
*/
|
|
29
|
+
async function _createNotification(manager, did, notification, { skipGetBlocklet = false } = {}) {
|
|
30
|
+
logger.info('[_createNotification] called', { did, title: notification?.title, skipGetBlocklet });
|
|
31
|
+
|
|
32
|
+
if (isCLI()) {
|
|
33
|
+
logger.info('[_createNotification] skipped in CLI mode');
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
let blockletUrl;
|
|
39
|
+
let blocklet;
|
|
40
|
+
try {
|
|
41
|
+
blocklet = skipGetBlocklet ? null : await manager.getBlocklet(did);
|
|
42
|
+
if (blocklet) {
|
|
43
|
+
const urls = blocklet.site?.domainAliases || [];
|
|
44
|
+
const customUrl = urls.find((x) => !x.isProtected)?.value;
|
|
45
|
+
blockletUrl = `http://${
|
|
46
|
+
customUrl || getDidDomainForBlocklet({ did: blocklet.appPid })
|
|
47
|
+
}${WELLKNOWN_BLOCKLET_ADMIN_PATH}`;
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
logger.error('[_createNotification] get blocklet url failed', { did, error });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// if blocklet is external, no need to create notification in server
|
|
54
|
+
const extra = await states.blockletExtras.getMeta(did);
|
|
55
|
+
const isExternal = !!extra?.controller;
|
|
56
|
+
|
|
57
|
+
if (!isExternal) {
|
|
58
|
+
// 需要处理 action, staging 环境中 path 要带有 admin/
|
|
59
|
+
const { action } = notification;
|
|
60
|
+
let actionPath = action;
|
|
61
|
+
if (actionPath) {
|
|
62
|
+
const nodeInfo = await states.node.read();
|
|
63
|
+
actionPath =
|
|
64
|
+
process.env.NODE_ENV === 'production' ? joinURL(nodeInfo.routing.adminPath, actionPath) : actionPath;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
logger.info('[_createNotification] creating server notification', { did, title: notification?.title });
|
|
68
|
+
await manager.teamManager.createNotification({
|
|
69
|
+
...notification,
|
|
70
|
+
action: actionPath,
|
|
71
|
+
blockletUrl,
|
|
72
|
+
sender: did,
|
|
73
|
+
source: 'system',
|
|
74
|
+
});
|
|
75
|
+
logger.info('[_createNotification] server notification created', { did, title: notification?.title });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (blocklet) {
|
|
79
|
+
const doc = await manager.teamManager.createNotification({
|
|
80
|
+
teamDid: did,
|
|
81
|
+
...notification,
|
|
82
|
+
action: notification.blockletDashboardAction || notification.action,
|
|
83
|
+
blockletUrl,
|
|
84
|
+
sender: did,
|
|
85
|
+
source: 'system',
|
|
86
|
+
});
|
|
87
|
+
return doc;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
logger.error('create notification failed', { did, error });
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get webhooks from webhook status
|
|
98
|
+
* @param {string} webhook - Webhook status JSON
|
|
99
|
+
* @param {Array<string>} urls - URLs to filter
|
|
100
|
+
* @param {boolean} resendFailedOnly - Only resend failed webhooks
|
|
101
|
+
* @returns {Array}
|
|
102
|
+
*/
|
|
103
|
+
function getWebhooks(webhook, urls = [], resendFailedOnly = true) {
|
|
104
|
+
const webhooks = [];
|
|
105
|
+
try {
|
|
106
|
+
const parsedWebhook = JSON.parse(webhook ?? '{}');
|
|
107
|
+
for (const item of Object.entries(parsedWebhook)) {
|
|
108
|
+
const [url, values] = item;
|
|
109
|
+
const value = values[0]; // 或者最新的状态
|
|
110
|
+
const isFailed = value.status === NOTIFICATION_SEND_STATUS.FAILED;
|
|
111
|
+
const canResend = isFailed || !resendFailedOnly;
|
|
112
|
+
if (!urls.length && canResend) {
|
|
113
|
+
webhooks.push({
|
|
114
|
+
url,
|
|
115
|
+
type: value.type || 'api',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (urls.length > 0 && urls.includes(url) && canResend) {
|
|
119
|
+
webhooks.push({
|
|
120
|
+
url,
|
|
121
|
+
type: value.type || 'api',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return webhooks;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
logger.error('Failed to get webhooks', { error });
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Handle resend notification job
|
|
134
|
+
* @param {Object} manager - BlockletManager instance
|
|
135
|
+
* @param {Object} job - Job data
|
|
136
|
+
* @returns {Promise<void>}
|
|
137
|
+
*/
|
|
138
|
+
async function _onResendNotification(manager, job) {
|
|
139
|
+
const { notification, user, sender, channels, webhookUrls, resendFailedOnly = true } = job;
|
|
140
|
+
logger.info('Start to resend notification', { notification: notification.id, user: user.did });
|
|
141
|
+
const { userInfo, sendStatus } = user;
|
|
142
|
+
try {
|
|
143
|
+
const walletEnabled = get(userInfo, 'extra.notifications.wallet', true);
|
|
144
|
+
// 需要向 wallet 重发消息
|
|
145
|
+
if (channels.includes(NOTIFICATION_SEND_CHANNEL.WALLET) && walletEnabled) {
|
|
146
|
+
const { walletSendStatus } = sendStatus;
|
|
147
|
+
if (walletSendStatus === NOTIFICATION_SEND_STATUS.FAILED || !resendFailedOnly) {
|
|
148
|
+
await sendToUser(userInfo.did, notification, sender, {
|
|
149
|
+
channels: [NOTIFICATION_SEND_CHANNEL.WALLET],
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} catch (error) {
|
|
154
|
+
logger.error('Failed to resend notification to wallet', { error });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const emailEnabled = get(userInfo, 'extra.notifications.email', true);
|
|
159
|
+
if (channels.includes(NOTIFICATION_SEND_CHANNEL.EMAIL) && emailEnabled && userInfo.email) {
|
|
160
|
+
const { emailSendStatus } = sendStatus;
|
|
161
|
+
if (emailSendStatus === NOTIFICATION_SEND_STATUS.FAILED || !resendFailedOnly) {
|
|
162
|
+
await sendToUser(userInfo.did, notification, sender, {
|
|
163
|
+
channels: [NOTIFICATION_SEND_CHANNEL.EMAIL],
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch (error) {
|
|
168
|
+
logger.error('Failed to resend notification to email', { error });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const pushEnabled = get(userInfo, 'extra.notifications.push', true);
|
|
173
|
+
if (channels.includes(NOTIFICATION_SEND_CHANNEL.PUSH) && pushEnabled) {
|
|
174
|
+
const { pushKitSendStatus } = sendStatus;
|
|
175
|
+
if (pushKitSendStatus === NOTIFICATION_SEND_STATUS.FAILED || !resendFailedOnly) {
|
|
176
|
+
await sendToUser(userInfo.did, notification, sender, {
|
|
177
|
+
channels: [NOTIFICATION_SEND_CHANNEL.PUSH],
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
logger.error('Failed to resend notification to push', { error });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const { webhook } = sendStatus;
|
|
187
|
+
const webhooks = getWebhooks(webhook, webhookUrls, resendFailedOnly);
|
|
188
|
+
if (webhooks.length) {
|
|
189
|
+
await sendToUser(
|
|
190
|
+
userInfo.did,
|
|
191
|
+
{
|
|
192
|
+
...notification,
|
|
193
|
+
appInfo: { webhooks },
|
|
194
|
+
},
|
|
195
|
+
sender,
|
|
196
|
+
{
|
|
197
|
+
channels: [NOTIFICATION_SEND_CHANNEL.WEBHOOK],
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
logger.error('Failed to resend notification to webhook', { error });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Resend notification
|
|
208
|
+
* @param {Object} manager - BlockletManager instance
|
|
209
|
+
* @param {Object} props - Props
|
|
210
|
+
* @param {string} props.teamDid - Blocklet DID
|
|
211
|
+
* @param {string} props.notificationId - Notification ID
|
|
212
|
+
* @param {Array<string>} props.receivers - Receiver DIDs
|
|
213
|
+
* @param {Array<string>} props.channels - Channels
|
|
214
|
+
* @param {Array<string>} props.webhookUrls - Webhook URLs
|
|
215
|
+
* @param {boolean} props.resendFailedOnly - Only resend failed
|
|
216
|
+
* @param {Object} context - Context
|
|
217
|
+
* @returns {Promise<void>}
|
|
218
|
+
*/
|
|
219
|
+
async function resendNotification(manager, props, context) {
|
|
220
|
+
const { teamDid, notificationId, receivers, channels, webhookUrls, resendFailedOnly = true } = props;
|
|
221
|
+
// 判断用户的角色
|
|
222
|
+
const { user } = context || {};
|
|
223
|
+
if (
|
|
224
|
+
!user?.role ||
|
|
225
|
+
![ROLES.ADMIN, ROLES.OWNER, SERVER_ROLES.BLOCKLET_ADMIN, SERVER_ROLES.BLOCKLET_OWNER].includes(user?.role)
|
|
226
|
+
) {
|
|
227
|
+
throw new Error('Forbidden. You do not have permission to access this resource.');
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
// get link origin
|
|
231
|
+
const { referrer } = context;
|
|
232
|
+
let origin = referrer ? new URL(referrer).origin : '';
|
|
233
|
+
const nodeInfo = await states.node.read();
|
|
234
|
+
const blocklet = await manager.getBlocklet(teamDid);
|
|
235
|
+
|
|
236
|
+
if (!origin || nodeInfo.did !== teamDid) {
|
|
237
|
+
origin = await manager.teamManager.getActionLinkOrigin(teamDid, blocklet);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const notificationState = await manager.teamAPI.getNotificationState(teamDid);
|
|
241
|
+
const { notification, users } = await notificationState.combineNotificationReceiver({
|
|
242
|
+
notificationId,
|
|
243
|
+
receivers,
|
|
244
|
+
channels,
|
|
245
|
+
webhookUrls,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const notificationFormat = transformNotification(notification, origin);
|
|
249
|
+
|
|
250
|
+
manager.teamManager.applyComponentMountPoint(
|
|
251
|
+
teamDid,
|
|
252
|
+
notificationFormat,
|
|
253
|
+
notification.componentDid,
|
|
254
|
+
origin,
|
|
255
|
+
blocklet
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
// const resendReceivers = users.map((u) => u.userInfo.did);
|
|
259
|
+
const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
260
|
+
const sender = Object.freeze({
|
|
261
|
+
appDid: wallet.address,
|
|
262
|
+
verified: true,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const receiverObject = {
|
|
266
|
+
receivers: users.map((u) => u.userInfo.did),
|
|
267
|
+
[NOTIFICATION_SEND_CHANNEL.WALLET]: [],
|
|
268
|
+
[NOTIFICATION_SEND_CHANNEL.EMAIL]: [],
|
|
269
|
+
[NOTIFICATION_SEND_CHANNEL.PUSH]: [],
|
|
270
|
+
[NOTIFICATION_SEND_CHANNEL.WEBHOOK]: [],
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
for (const _user of users) {
|
|
274
|
+
const { userInfo, sendStatus } = _user;
|
|
275
|
+
const { walletSendStatus, emailSendStatus, pushKitSendStatus, webhook } = sendStatus;
|
|
276
|
+
if (walletSendStatus === NOTIFICATION_SEND_STATUS.FAILED && channels.includes(NOTIFICATION_SEND_CHANNEL.WALLET)) {
|
|
277
|
+
receiverObject[NOTIFICATION_SEND_CHANNEL.WALLET].push(userInfo.did);
|
|
278
|
+
}
|
|
279
|
+
if (emailSendStatus === NOTIFICATION_SEND_STATUS.FAILED && channels.includes(NOTIFICATION_SEND_CHANNEL.EMAIL)) {
|
|
280
|
+
receiverObject[NOTIFICATION_SEND_CHANNEL.EMAIL].push(userInfo.did);
|
|
281
|
+
}
|
|
282
|
+
if (pushKitSendStatus === NOTIFICATION_SEND_STATUS.FAILED && channels.includes(NOTIFICATION_SEND_CHANNEL.PUSH)) {
|
|
283
|
+
receiverObject[NOTIFICATION_SEND_CHANNEL.PUSH].push(userInfo.did);
|
|
284
|
+
}
|
|
285
|
+
// TODO: webhook 需要根据 webhookUrls 进行重发
|
|
286
|
+
if (webhook === NOTIFICATION_SEND_STATUS.FAILED && channels.includes(NOTIFICATION_SEND_CHANNEL.WEBHOOK)) {
|
|
287
|
+
receiverObject[NOTIFICATION_SEND_CHANNEL.WEBHOOK].push(userInfo.did);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 获取第一次构建时的 options,但是要排除 channel 字段。在重发阶段会指定 channel
|
|
292
|
+
const options = omit(get(notification, 'options', {}), ['channel']);
|
|
293
|
+
|
|
294
|
+
for (const channel of channels) {
|
|
295
|
+
const resendReceivers = resendFailedOnly ? receiverObject[channel] : receiverObject.receivers;
|
|
296
|
+
|
|
297
|
+
manager.emit(EVENTS.NOTIFICATION_CREATE_QUEUED, {
|
|
298
|
+
channels: [channel],
|
|
299
|
+
notification: notificationFormat,
|
|
300
|
+
receivers: resendReceivers,
|
|
301
|
+
isServices: true,
|
|
302
|
+
teamDid,
|
|
303
|
+
sender,
|
|
304
|
+
pushOnly: true,
|
|
305
|
+
isExist: true,
|
|
306
|
+
options,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
} catch (error) {
|
|
310
|
+
logger.error('Failed to get notification state', {
|
|
311
|
+
teamDid,
|
|
312
|
+
notificationId,
|
|
313
|
+
});
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Send all messages to user
|
|
320
|
+
* @param {Object} manager - BlockletManager instance
|
|
321
|
+
* @param {Object} params
|
|
322
|
+
* @returns {void}
|
|
323
|
+
*/
|
|
324
|
+
function _sendAllMessageToUser(manager, { did, title, description, action, blockletDashboardAction }) {
|
|
325
|
+
// 这个方法会向用户发送消息
|
|
326
|
+
manager._createNotification(did, {
|
|
327
|
+
title: title.en,
|
|
328
|
+
description: description.en,
|
|
329
|
+
entityType: 'blocklet',
|
|
330
|
+
entityId: did,
|
|
331
|
+
severity: 'success',
|
|
332
|
+
action,
|
|
333
|
+
blockletDashboardAction,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
module.exports = {
|
|
338
|
+
_createNotification,
|
|
339
|
+
getWebhooks,
|
|
340
|
+
_onResendNotification,
|
|
341
|
+
resendNotification,
|
|
342
|
+
_sendAllMessageToUser,
|
|
343
|
+
};
|