@abtnode/core 1.17.8-beta-20260109-075740-5f484e08 → 1.17.8-beta-20260113-015027-32a1cec4

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.
Files changed (65) hide show
  1. package/lib/api/team/access-key-manager.js +104 -0
  2. package/lib/api/team/invitation-manager.js +461 -0
  3. package/lib/api/team/notification-manager.js +189 -0
  4. package/lib/api/team/oauth-manager.js +60 -0
  5. package/lib/api/team/org-crud-manager.js +202 -0
  6. package/lib/api/team/org-manager.js +56 -0
  7. package/lib/api/team/org-member-manager.js +403 -0
  8. package/lib/api/team/org-query-manager.js +126 -0
  9. package/lib/api/team/org-resource-manager.js +186 -0
  10. package/lib/api/team/passport-manager.js +670 -0
  11. package/lib/api/team/rbac-manager.js +335 -0
  12. package/lib/api/team/session-manager.js +540 -0
  13. package/lib/api/team/store-manager.js +198 -0
  14. package/lib/api/team/tag-manager.js +230 -0
  15. package/lib/api/team/user-auth-manager.js +132 -0
  16. package/lib/api/team/user-manager.js +78 -0
  17. package/lib/api/team/user-query-manager.js +299 -0
  18. package/lib/api/team/user-social-manager.js +354 -0
  19. package/lib/api/team/user-update-manager.js +224 -0
  20. package/lib/api/team/verify-code-manager.js +161 -0
  21. package/lib/api/team.js +439 -3287
  22. package/lib/blocklet/manager/disk/auth-manager.js +68 -0
  23. package/lib/blocklet/manager/disk/backup-manager.js +288 -0
  24. package/lib/blocklet/manager/disk/cleanup-manager.js +157 -0
  25. package/lib/blocklet/manager/disk/component-manager.js +83 -0
  26. package/lib/blocklet/manager/disk/config-manager.js +191 -0
  27. package/lib/blocklet/manager/disk/controller-manager.js +64 -0
  28. package/lib/blocklet/manager/disk/delete-reset-manager.js +328 -0
  29. package/lib/blocklet/manager/disk/download-manager.js +96 -0
  30. package/lib/blocklet/manager/disk/env-config-manager.js +311 -0
  31. package/lib/blocklet/manager/disk/federated-manager.js +651 -0
  32. package/lib/blocklet/manager/disk/hook-manager.js +124 -0
  33. package/lib/blocklet/manager/disk/install-component-manager.js +95 -0
  34. package/lib/blocklet/manager/disk/install-core-manager.js +448 -0
  35. package/lib/blocklet/manager/disk/install-download-manager.js +313 -0
  36. package/lib/blocklet/manager/disk/install-manager.js +36 -0
  37. package/lib/blocklet/manager/disk/install-upgrade-manager.js +340 -0
  38. package/lib/blocklet/manager/disk/job-manager.js +467 -0
  39. package/lib/blocklet/manager/disk/lifecycle-manager.js +26 -0
  40. package/lib/blocklet/manager/disk/notification-manager.js +343 -0
  41. package/lib/blocklet/manager/disk/query-manager.js +562 -0
  42. package/lib/blocklet/manager/disk/settings-manager.js +507 -0
  43. package/lib/blocklet/manager/disk/start-manager.js +611 -0
  44. package/lib/blocklet/manager/disk/stop-restart-manager.js +292 -0
  45. package/lib/blocklet/manager/disk/update-manager.js +153 -0
  46. package/lib/blocklet/manager/disk.js +669 -5796
  47. package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +5 -0
  48. package/lib/blocklet/manager/lock.js +18 -0
  49. package/lib/event/index.js +28 -24
  50. package/lib/util/blocklet/app-utils.js +192 -0
  51. package/lib/util/blocklet/blocklet-loader.js +258 -0
  52. package/lib/util/blocklet/config-manager.js +232 -0
  53. package/lib/util/blocklet/did-document.js +240 -0
  54. package/lib/util/blocklet/environment.js +555 -0
  55. package/lib/util/blocklet/health-check.js +449 -0
  56. package/lib/util/blocklet/install-utils.js +365 -0
  57. package/lib/util/blocklet/logo.js +57 -0
  58. package/lib/util/blocklet/meta-utils.js +269 -0
  59. package/lib/util/blocklet/port-manager.js +141 -0
  60. package/lib/util/blocklet/process-manager.js +504 -0
  61. package/lib/util/blocklet/runtime-info.js +105 -0
  62. package/lib/util/blocklet/validation.js +418 -0
  63. package/lib/util/blocklet.js +98 -3066
  64. package/lib/util/wallet-app-notification.js +40 -0
  65. 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
+ };