@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.
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,467 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const merge = require('lodash/merge');
5
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:job');
6
+ const sleep = require('@abtnode/util/lib/sleep');
7
+ const { isInServerlessMode } = require('@abtnode/util/lib/serverless');
8
+ const { BACKUPS, MONITOR_RECORD_INTERVAL_SEC } = require('@abtnode/constant');
9
+ const { BlockletStatus, BLOCKLET_CONTROLLER_STATUS, SUSPENDED_REASON } = require('@blocklet/constant');
10
+
11
+ const formatContext = require('@abtnode/util/lib/format-context');
12
+ const states = require('../../../states');
13
+ const launcher = require('../../../util/launcher');
14
+ const checkDockerRunHistory = require('../../../util/docker/check-docker-run-history');
15
+ const { dockerExecChown } = require('../../../util/docker/docker-exec-chown');
16
+ const { SpacesBackup } = require('../../storage/backup/spaces');
17
+ const { DiskBackup } = require('../../storage/backup/disk');
18
+ const { DiskRestore } = require('../../storage/restore/disk');
19
+ const { getBackupJobId } = require('../../../util/spaces');
20
+ const { installApplicationFromBackup } = require('../helper/install-application-from-backup');
21
+ const { refresh: refreshAccessibleExternalNodeIp } = require('../../../util/get-accessible-external-node-ip');
22
+ const { isRuntimeMonitorDisabled } = require('../../../monitor/blocklet-runtime-monitor');
23
+
24
+ /**
25
+ * Backup blocklet to spaces
26
+ * @param {Object} manager - BlockletManager instance
27
+ * @param {Object} params
28
+ * @param {Object} context
29
+ * @returns {Promise<Object>}
30
+ */
31
+ async function _backupToSpaces(manager, { blocklet, context }) {
32
+ try {
33
+ const { did } = blocklet.meta;
34
+ const jobId = getBackupJobId(did);
35
+ const { job, willRunAt } = (await manager.backupQueue.get(jobId, { full: true })) ?? {};
36
+
37
+ // 任务正在运行或者将要在 1s 内运行,或者任务可能已过期,都是表示任务可用
38
+ const waitBackupDone = (job && willRunAt - Date.now() <= 1_000) || SpacesBackup.isRunning(did);
39
+
40
+ if (waitBackupDone) {
41
+ logger.warn(`This app(${did})'s manual or auto backup is already running, skip manual backup`, {
42
+ job,
43
+ willRunAt,
44
+ now: Date.now(),
45
+ isRunning: SpacesBackup.isRunning(did),
46
+ });
47
+ await manager.backupQueue.restoreCancelled(jobId);
48
+ } else {
49
+ await manager.backupQueue.delete(jobId);
50
+ manager.backupQueue.push(
51
+ {
52
+ entity: 'blocklet',
53
+ action: 'backupToSpaces',
54
+ did,
55
+ context,
56
+ backupState: {
57
+ strategy: BACKUPS.STRATEGY.MANUAL,
58
+ },
59
+ },
60
+ jobId
61
+ );
62
+ }
63
+
64
+ return blocklet;
65
+ } catch (error) {
66
+ logger.error(error);
67
+ throw error;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Backup blocklet to disk
73
+ * @param {Object} manager - BlockletManager instance
74
+ * @param {Object} params
75
+ * @returns {Promise<Object>}
76
+ */
77
+ async function _backupToDisk(manager, { blocklet }) {
78
+ const { appDid } = blocklet;
79
+ const nodeInfo = await states.node.read();
80
+ if (checkDockerRunHistory(nodeInfo)) {
81
+ await dockerExecChown({
82
+ name: `${appDid}-backup-to-disk`,
83
+ dirs: [path.join(process.env.ABT_NODE_DATA_DIR, 'data', appDid)],
84
+ });
85
+ }
86
+ const diskBackup = new DiskBackup({ appDid, event: manager });
87
+ await diskBackup.backup();
88
+
89
+ return blocklet;
90
+ }
91
+
92
+ /**
93
+ * Restore blocklet from spaces
94
+ * @param {Object} manager - BlockletManager instance
95
+ * @param {Object} input
96
+ * @param {Object} context
97
+ * @returns {Promise<void>}
98
+ */
99
+ async function _restoreFromSpaces(manager, input, context) {
100
+ if (input.delay) {
101
+ await sleep(input.delay);
102
+ }
103
+
104
+ const appPid = input.appDid;
105
+
106
+ manager.restoreQueue.push(
107
+ {
108
+ entity: 'blocklet',
109
+ action: 'restoreFromSpaces',
110
+ id: appPid,
111
+ input,
112
+ context,
113
+ },
114
+ appPid
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Restore blocklet from disk
120
+ * @param {Object} manager - BlockletManager instance
121
+ * @param {Object} input
122
+ * @param {Object} context
123
+ * @returns {Promise<void>}
124
+ */
125
+ async function _restoreFromDisk(manager, input, context) {
126
+ if (input.delay) {
127
+ await sleep(input.delay);
128
+ }
129
+
130
+ const diskRestore = new DiskRestore({ ...input, event: manager });
131
+ const params = await diskRestore.restore();
132
+
133
+ const removeRestoreDir = () => {
134
+ if (fs.existsSync(diskRestore.restoreDir)) {
135
+ fs.remove(diskRestore.restoreDir).catch((err) => {
136
+ logger.error('failed to remove restore dir', { error: err, dir: diskRestore.restoreDir });
137
+ });
138
+ }
139
+ };
140
+
141
+ try {
142
+ await installApplicationFromBackup({
143
+ url: `file://${diskRestore.restoreDir}`,
144
+ ...merge(...params),
145
+ manager,
146
+ states,
147
+ move: true,
148
+ sync: false, // use queue to download and install application
149
+ context,
150
+ });
151
+ removeRestoreDir();
152
+ } catch (error) {
153
+ removeRestoreDir();
154
+ throw error;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Check renewed blocklet
160
+ * @param {Object} manager - BlockletManager instance
161
+ * @returns {Promise<void>}
162
+ */
163
+ async function checkRenewedBlocklet(manager) {
164
+ try {
165
+ logger.info('start check renewed blocklet');
166
+
167
+ // 只检查因为过期而被 suspended 的 blocklet
168
+ const blockletExtras = await states.blockletExtras.find(
169
+ {
170
+ 'controller.status.value': BLOCKLET_CONTROLLER_STATUS.suspended,
171
+ 'controller.status.reason': SUSPENDED_REASON.expired,
172
+ },
173
+ { did: 1, meta: 1, controller: 1 }
174
+ );
175
+
176
+ for (const data of blockletExtras) {
177
+ const blocklet = await states.blocklet.getBlocklet(data.did);
178
+ if (!blocklet) {
179
+ logger.error('blocklet not found', { did: data.did });
180
+ // eslint-disable-next-line no-continue
181
+ continue;
182
+ }
183
+
184
+ const isExpired = await launcher.isBlockletExpired(data.did, data.controller);
185
+
186
+ if (isExpired === false) {
187
+ logger.info('blocklet is renewed', { did: data.did });
188
+ await states.blockletExtras.updateByDid(data.did, {
189
+ ...data.controller,
190
+ status: { value: BLOCKLET_CONTROLLER_STATUS.normal, reason: '' },
191
+ });
192
+
193
+ logger.info('start to start blocklet', { did: data.did });
194
+
195
+ if (![BlockletStatus.starting, BlockletStatus.running].includes(blocklet.status)) {
196
+ await manager.start({ did: data.did });
197
+ logger.info('start blocklet success', { did: data.did });
198
+ }
199
+ }
200
+ }
201
+ } catch (error) {
202
+ logger.error('start check renewed blocklet failed', { error });
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Clean expired blocklets that exceed redemption period
208
+ * @param {Object} manager - BlockletManager instance
209
+ * @returns {Promise<void>}
210
+ */
211
+ async function cleanExpiredBlocklets(manager) {
212
+ try {
213
+ logger.info('start checking exceeding the redemption period blocklet');
214
+ const blockletExtras = await states.blockletExtras.find(
215
+ {
216
+ 'controller.status.value': BLOCKLET_CONTROLLER_STATUS.suspended,
217
+ },
218
+ { did: 1, meta: 1, controller: 1 }
219
+ );
220
+
221
+ if (blockletExtras.length === 0) {
222
+ logger.info('no exceeding the redemption blocklet need to be cleaned');
223
+ return;
224
+ }
225
+
226
+ logger.info('exceeding the redemption blocklet count', { count: blockletExtras.length });
227
+
228
+ for (const data of blockletExtras) {
229
+ try {
230
+ const { did } = data;
231
+ const { launcherSession } = await launcher.getLauncherSession({
232
+ launcherUrl: data.controller.launcherUrl,
233
+ launcherSessionId: data.controller.launcherSessionId,
234
+ });
235
+
236
+ const isTerminated = launcher.isLaunchSessionTerminated(launcherSession);
237
+
238
+ if (!isTerminated) {
239
+ logger.info('skip cleaning the non-terminated blocklet', {
240
+ blockletDid: did,
241
+ controller: data.controller,
242
+ launcherSession,
243
+ });
244
+ continue;
245
+ }
246
+
247
+ if (!launcherSession.terminatedAt) {
248
+ logger.error('the blocklet launch session does not have terminatedAt, skip', {
249
+ blockletDid: did,
250
+ controller: data.controller,
251
+ launcherSession,
252
+ });
253
+ continue;
254
+ }
255
+
256
+ // 订阅终止后需要再保留一段时间数据
257
+ if (!launcher.isDataRetentionExceeded(launcherSession)) {
258
+ logger.info('skip cleaning the non-exceed redemption blocklet', {
259
+ blockletDid: did,
260
+ controller: data.controller,
261
+ launcherSession,
262
+ });
263
+ continue;
264
+ }
265
+
266
+ logger.info('the blocklet already exceed redemption and will be deleted', {
267
+ blockletDid: did,
268
+ nftId: data.controller.nftId,
269
+ });
270
+
271
+ await manager.delete({ did, keepData: false, keepConfigs: false, keepLogsDir: false });
272
+ logger.info('the exceed redemption blocklet already deleted', {
273
+ blockletDid: did,
274
+ nftId: data.controller.nftId,
275
+ });
276
+
277
+ // 删除 blocklet 后会 reload nginx, 所以这里每次删除一个
278
+ if (process.env.NODE_ENV !== 'test') {
279
+ await sleep(10 * 1000);
280
+ }
281
+ } catch (error) {
282
+ logger.error('delete exceed redemption blocklet failed', {
283
+ blockletDid: data.did,
284
+ nftId: data.controller?.nftId,
285
+ error,
286
+ });
287
+ }
288
+ }
289
+
290
+ logger.info('check exceeding the redemption period blocklet end');
291
+ } catch (error) {
292
+ logger.error('checking exceeding the redemption period blocklet failed', { error });
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Get cron jobs
298
+ * @param {Object} manager - BlockletManager instance
299
+ * @returns {Promise<Array>}
300
+ */
301
+ async function getCrons(manager) {
302
+ const info = await states.node.read();
303
+
304
+ const crons = [
305
+ {
306
+ name: 'sync-blocklet-list',
307
+ time: '*/60 * * * * *', // 60s
308
+ fn: manager.refreshListCache.bind(manager),
309
+ },
310
+ {
311
+ // 刷新 nodeIp 的定时任务
312
+ name: 'refresh-accessible-ip',
313
+ time: '0 */10 * * * *', // 10min
314
+ options: { runOnInit: true, runInService: true },
315
+ fn: async () => {
316
+ const nodeInfo = await states.node.read();
317
+ refreshAccessibleExternalNodeIp(nodeInfo);
318
+ },
319
+ },
320
+ {
321
+ name: 'update-blocklet-certificate',
322
+ time: '*/10 * * * *',
323
+ options: { runOnInit: false },
324
+ fn: () => {
325
+ const fn = manager.updateAllBlockletCertificate.bind(manager);
326
+ fn();
327
+ },
328
+ },
329
+ ];
330
+
331
+ if (!isRuntimeMonitorDisabled()) {
332
+ crons.push({
333
+ name: 'record-blocklet-runtime-history',
334
+ time: `*/${MONITOR_RECORD_INTERVAL_SEC} * * * * *`, // 10s
335
+ fn: () => manager.runtimeMonitor.monitAll(),
336
+ });
337
+ }
338
+
339
+ if (isInServerlessMode(info)) {
340
+ const serverlessJobs = [
341
+ {
342
+ name: 'check-renewed-blocklet',
343
+ time: '0 */10 * * * *', // 10min
344
+ options: { runOnInit: false },
345
+ fn: () => manager.checkRenewedBlocklet(),
346
+ },
347
+ {
348
+ name: 'stop-expired-external-blocklet',
349
+ time: '0 */10 * * * *', // 每10分钟
350
+ options: { runOnInit: false },
351
+ fn: () => manager.stopExpiredBlocklets(),
352
+ },
353
+ {
354
+ name: 'clean-expired-blocklet-data',
355
+ time: '0 10 * * * *', // 每小时
356
+ options: { runOnInit: false },
357
+ fn: () => manager.cleanExpiredBlocklets(),
358
+ },
359
+ {
360
+ name: 'send-serverless-heartbeat',
361
+ time: process.env.ABT_NODE_SERVERLESS_HEARTBEAT_INTERVAL || '*/5 * * * *', // default every 5 minutes
362
+ options: { runOnInit: true },
363
+ fn: () => launcher.sendServerlessHeartbeat(),
364
+ },
365
+ ];
366
+
367
+ logger.info('enable serverless jobs', serverlessJobs.map((x) => x.name).join(','));
368
+
369
+ crons.push(...serverlessJobs);
370
+ }
371
+
372
+ return crons;
373
+ }
374
+
375
+ /**
376
+ * Ensure jobs
377
+ * @param {Object} manager - BlockletManager instance
378
+ * @param {Object} params
379
+ * @returns {Promise<void>}
380
+ */
381
+ async function ensureJobs(manager, { queue, getJobId, find, entity, action, interval, restoreCancelled }) {
382
+ const blocklets = await states.blockletExtras.find(find);
383
+
384
+ const info = await states.node.read();
385
+
386
+ await Promise.all(
387
+ blocklets.map(async (x) => {
388
+ const { did } = x;
389
+ const jobId = getJobId(did);
390
+ const job = await queue.get(jobId);
391
+ if (restoreCancelled) {
392
+ queue.restoreCancelled(jobId);
393
+ }
394
+
395
+ if (job) {
396
+ return;
397
+ }
398
+
399
+ queue.push(
400
+ {
401
+ entity,
402
+ action,
403
+ did,
404
+ context: formatContext({
405
+ user: { did: info.did },
406
+ }),
407
+ },
408
+ jobId,
409
+ true,
410
+ interval
411
+ );
412
+ })
413
+ );
414
+ }
415
+
416
+ /**
417
+ * Handle job
418
+ * @param {Object} manager - BlockletManager instance
419
+ * @param {Object} job - Job data
420
+ * @returns {Promise<void>}
421
+ */
422
+ async function onJob(manager, job) {
423
+ if (job.entity === 'blocklet') {
424
+ if (job.action === 'download') {
425
+ await manager._downloadAndInstall(job);
426
+ }
427
+ if (job.action === 'restart') {
428
+ await manager._onRestart(job);
429
+ }
430
+
431
+ if (job.action === 'check_if_started') {
432
+ await manager._onCheckIfStarted(job);
433
+ }
434
+
435
+ if (job.action === 'backupToSpaces') {
436
+ await manager._onBackupToSpaces(job);
437
+ }
438
+
439
+ if (job.action === 'restoreFromSpaces') {
440
+ await manager._onRestoreFromSpaces(job);
441
+ }
442
+
443
+ if (job.action === 'autoCheckUpdate') {
444
+ await manager._onCheckForComponentUpdate(job);
445
+ }
446
+
447
+ if (job.action === 'reportComponentUsage') {
448
+ await manager._reportComponentUsage(job);
449
+ }
450
+
451
+ if (job.action === 'resend_notification') {
452
+ await manager._onResendNotification(job);
453
+ }
454
+ }
455
+ }
456
+
457
+ module.exports = {
458
+ _backupToSpaces,
459
+ _backupToDisk,
460
+ _restoreFromSpaces,
461
+ _restoreFromDisk,
462
+ checkRenewedBlocklet,
463
+ cleanExpiredBlocklets,
464
+ getCrons,
465
+ ensureJobs,
466
+ onJob,
467
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Lifecycle Manager - Orchestrates blocklet lifecycle operations
3
+ *
4
+ * This module re-exports functionality from specialized sub-modules:
5
+ * - start-manager.js: Start operations (start, _start, startRequiredComponents)
6
+ * - stop-restart-manager.js: Stop, restart, reload operations
7
+ * - delete-reset-manager.js: Delete and reset operations
8
+ */
9
+
10
+ const { startRequiredComponents, start, _start } = require('./start-manager');
11
+ const { stop, restart, reload, stopExpiredBlocklets } = require('./stop-restart-manager');
12
+ const { _safeRemoveDir, delete: deleteBlocklet, reset, deleteComponent } = require('./delete-reset-manager');
13
+
14
+ module.exports = {
15
+ startRequiredComponents,
16
+ start,
17
+ _start,
18
+ stop,
19
+ restart,
20
+ reload,
21
+ delete: deleteBlocklet,
22
+ _safeRemoveDir,
23
+ reset,
24
+ deleteComponent,
25
+ stopExpiredBlocklets,
26
+ };