@abtnode/core 1.15.17 → 1.16.0-beta-b16cb035

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 (119) hide show
  1. package/lib/api/node.js +67 -69
  2. package/lib/api/team.js +386 -55
  3. package/lib/blocklet/downloader/blocklet-downloader.js +226 -0
  4. package/lib/blocklet/downloader/bundle-downloader.js +272 -0
  5. package/lib/blocklet/downloader/constants.js +3 -0
  6. package/lib/blocklet/downloader/resolve-download.js +199 -0
  7. package/lib/blocklet/extras.js +83 -26
  8. package/lib/blocklet/hooks.js +18 -65
  9. package/lib/blocklet/manager/base.js +10 -16
  10. package/lib/blocklet/manager/disk.js +1679 -1566
  11. package/lib/blocklet/manager/helper/install-application-from-backup.js +177 -0
  12. package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
  13. package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
  14. package/lib/blocklet/manager/helper/install-component-from-dev.js +84 -0
  15. package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
  16. package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
  17. package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +450 -0
  18. package/lib/blocklet/manager/helper/rollback-cache.js +41 -0
  19. package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
  20. package/lib/blocklet/migration.js +30 -52
  21. package/lib/blocklet/storage/backup/audit-log.js +27 -0
  22. package/lib/blocklet/storage/backup/base.js +62 -0
  23. package/lib/blocklet/storage/backup/blocklet-extras.js +92 -0
  24. package/lib/blocklet/storage/backup/blocklet.js +70 -0
  25. package/lib/blocklet/storage/backup/blocklets.js +74 -0
  26. package/lib/blocklet/storage/backup/data.js +19 -0
  27. package/lib/blocklet/storage/backup/logs.js +24 -0
  28. package/lib/blocklet/storage/backup/routing-rule.js +19 -0
  29. package/lib/blocklet/storage/backup/spaces.js +240 -0
  30. package/lib/blocklet/storage/restore/base.js +67 -0
  31. package/lib/blocklet/storage/restore/blocklet-extras.js +86 -0
  32. package/lib/blocklet/storage/restore/blocklet.js +56 -0
  33. package/lib/blocklet/storage/restore/blocklets.js +43 -0
  34. package/lib/blocklet/storage/restore/logs.js +21 -0
  35. package/lib/blocklet/storage/restore/spaces.js +156 -0
  36. package/lib/blocklet/storage/utils/hash.js +51 -0
  37. package/lib/blocklet/storage/utils/zip.js +43 -0
  38. package/lib/cert.js +206 -0
  39. package/lib/event.js +237 -64
  40. package/lib/index.js +191 -83
  41. package/lib/migrations/1.0.21-update-config.js +1 -1
  42. package/lib/migrations/1.0.22-max-memory.js +1 -1
  43. package/lib/migrations/1.0.25.js +1 -1
  44. package/lib/migrations/1.0.32-update-config.js +1 -1
  45. package/lib/migrations/1.0.33-blocklets.js +1 -1
  46. package/lib/migrations/1.5.20-registry.js +15 -0
  47. package/lib/migrations/1.6.17-blocklet-children.js +48 -0
  48. package/lib/migrations/1.6.21-rename-ip-echo-domain.js +35 -0
  49. package/lib/migrations/1.6.4-security.js +59 -0
  50. package/lib/migrations/1.6.5-security.js +60 -0
  51. package/lib/migrations/1.6.9-update-node-info-and-certificate.js +38 -0
  52. package/lib/migrations/1.7.1-blocklet-setup.js +18 -0
  53. package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
  54. package/lib/migrations/1.7.15-blocklet-bundle-source.js +42 -0
  55. package/lib/migrations/1.7.20-blocklet-component.js +41 -0
  56. package/lib/migrations/1.8.33-blocklet-mem-limit.js +20 -0
  57. package/lib/migrations/README.md +1 -1
  58. package/lib/migrations/index.js +6 -2
  59. package/lib/monitor/blocklet-runtime-monitor.js +200 -0
  60. package/lib/monitor/get-history-list.js +37 -0
  61. package/lib/monitor/node-runtime-monitor.js +228 -0
  62. package/lib/router/helper.js +572 -497
  63. package/lib/router/index.js +85 -21
  64. package/lib/router/manager.js +146 -187
  65. package/lib/states/README.md +36 -1
  66. package/lib/states/access-key.js +39 -17
  67. package/lib/states/audit-log.js +462 -0
  68. package/lib/states/base.js +4 -213
  69. package/lib/states/blocklet-extras.js +194 -138
  70. package/lib/states/blocklet.js +361 -104
  71. package/lib/states/cache.js +8 -6
  72. package/lib/states/challenge.js +5 -5
  73. package/lib/states/index.js +19 -36
  74. package/lib/states/migration.js +4 -4
  75. package/lib/states/node.js +135 -46
  76. package/lib/states/notification.js +22 -35
  77. package/lib/states/session.js +17 -9
  78. package/lib/states/site.js +50 -25
  79. package/lib/states/user.js +74 -20
  80. package/lib/states/webhook.js +10 -6
  81. package/lib/team/manager.js +124 -7
  82. package/lib/util/blocklet.js +1223 -246
  83. package/lib/util/chain.js +1 -1
  84. package/lib/util/default-node-config.js +5 -23
  85. package/lib/util/disk-monitor.js +13 -10
  86. package/lib/util/domain-status.js +84 -15
  87. package/lib/util/get-accessible-external-node-ip.js +2 -2
  88. package/lib/util/get-domain-for-blocklet.js +13 -0
  89. package/lib/util/get-meta-from-url.js +33 -0
  90. package/lib/util/index.js +207 -272
  91. package/lib/util/ip.js +6 -0
  92. package/lib/util/maintain.js +233 -0
  93. package/lib/util/public-to-store.js +85 -0
  94. package/lib/util/ready.js +1 -1
  95. package/lib/util/requirement.js +28 -9
  96. package/lib/util/reset-node.js +22 -7
  97. package/lib/util/router.js +13 -0
  98. package/lib/util/rpc.js +16 -0
  99. package/lib/util/store.js +179 -0
  100. package/lib/util/sysinfo.js +44 -0
  101. package/lib/util/ua.js +54 -0
  102. package/lib/validators/blocklet-extra.js +24 -0
  103. package/lib/validators/node.js +25 -12
  104. package/lib/validators/permission.js +16 -1
  105. package/lib/validators/role.js +17 -3
  106. package/lib/validators/router.js +40 -20
  107. package/lib/validators/trusted-passport.js +1 -0
  108. package/lib/validators/util.js +22 -5
  109. package/lib/webhook/index.js +45 -35
  110. package/lib/webhook/sender/index.js +5 -0
  111. package/lib/webhook/sender/slack/index.js +1 -1
  112. package/lib/webhook/sender/wallet/index.js +48 -0
  113. package/package.json +54 -36
  114. package/lib/blocklet/registry.js +0 -205
  115. package/lib/states/https-cert.js +0 -67
  116. package/lib/util/get-ip-dns-domain-for-blocklet.js +0 -19
  117. package/lib/util/service.js +0 -66
  118. package/lib/util/upgrade.js +0 -178
  119. /package/lib/{queue.js → util/queue.js} +0 -0
@@ -0,0 +1,233 @@
1
+ /* eslint-disable no-param-reassign */
2
+ const semver = require('semver');
3
+ const sleep = require('@abtnode/util/lib/sleep');
4
+ const SysInfo = require('systeminformation');
5
+ const { NODE_MODES, NODE_MAINTAIN_PROGRESS, EVENTS } = require('@abtnode/constant');
6
+ const listNpmPackageVersion = require('@abtnode/util/lib/list-npm-package-version');
7
+ const Lock = require('@abtnode/util/lib/lock');
8
+ const logger = require('@abtnode/logger')('@abtnode/core:maintain');
9
+
10
+ const states = require('../states');
11
+ const doRpc = require('./rpc');
12
+
13
+ const lock = new Lock('node-upgrade-lock');
14
+
15
+ // eslint-disable-next-line no-unused-vars
16
+ const checkNewVersion = async (params, context) => {
17
+ try {
18
+ const info = await states.node.read();
19
+
20
+ if (!process.env.ABT_NODE_PACKAGE_NAME) {
21
+ logger.error('ABT_NODE_PACKAGE_NAME name was not found in environment');
22
+ return '';
23
+ }
24
+
25
+ const versions = await listNpmPackageVersion(process.env.ABT_NODE_PACKAGE_NAME);
26
+ if (Array.isArray(versions) && versions.length) {
27
+ const latestVersion = versions[0].version;
28
+ if (semver.gt(latestVersion, info.version)) {
29
+ // if (semver.gte(latestVersion, info.version)) {
30
+ logger.info('New version found for Blocklet Server', {
31
+ latestVersion,
32
+ currentVersion: info.version,
33
+ nextVersion: info.nextVersion,
34
+ });
35
+ await states.node.updateNodeInfo({ nextVersion: latestVersion });
36
+ await states.notification.create({
37
+ title: 'Blocklet Server upgrade available',
38
+ description: 'A new and improved version of blocklet server is now available',
39
+ entityType: 'node',
40
+ severity: 'info',
41
+ sticky: true,
42
+ action: '/settings/about',
43
+ });
44
+
45
+ return latestVersion;
46
+ }
47
+ }
48
+
49
+ return '';
50
+ } catch (err) {
51
+ logger.error('Failed to check new version for Blocklet Server', { error: err });
52
+ }
53
+
54
+ return '';
55
+ };
56
+
57
+ const triggerMaintain = async ({ action, next }) => {
58
+ if (['upgrade', 'restart'].includes(action) === false) {
59
+ throw new Error(`Unrecognized server maintain action: ${action}`);
60
+ }
61
+
62
+ const info = await states.node.read();
63
+
64
+ if (action === 'upgrade') {
65
+ if (!info.nextVersion) {
66
+ throw new Error('Do nothing since there is no next version to upgrade');
67
+ }
68
+
69
+ // ensure we have enough memory for upgrading since the upgrading will consume a lot of memory
70
+ const { mem } = await SysInfo.get({ mem: '*' });
71
+ const freeMemory = mem.free / 1024 / 1024;
72
+ const availableMemory = mem.available / 1024 / 1024;
73
+ if (freeMemory < 200 && availableMemory < 300) {
74
+ throw new Error('Upgrade aborted because free memory not enough, please stop some blocklets and try again');
75
+ }
76
+ }
77
+
78
+ const from = info.version;
79
+ const to = action === 'upgrade' ? info.nextVersion : info.version;
80
+
81
+ // ensure we have an maintain session, we need a session to recover from crash
82
+ let sessionId = info.upgradeSessionId;
83
+ if (!sessionId) {
84
+ const result = await states.session.start({
85
+ action,
86
+ from,
87
+ to,
88
+ stage: NODE_MAINTAIN_PROGRESS.SETUP,
89
+ history: [],
90
+ startedAt: Date.now(),
91
+ });
92
+ sessionId = result.id;
93
+ logger.info(`generate new session for ${action}`, { from, to, sessionId });
94
+ await states.node.updateNodeInfo({ upgradeSessionId: sessionId });
95
+ }
96
+ const session = await states.session.read(sessionId);
97
+ if (!session) {
98
+ throw new Error(`${action} aborted due to invalid session: ${sessionId}`);
99
+ }
100
+
101
+ process.nextTick(async () => {
102
+ await next(session);
103
+ });
104
+
105
+ return sessionId;
106
+ };
107
+
108
+ const resumeMaintain = async (session) => {
109
+ await lock.acquire();
110
+
111
+ const sessionId = session.id;
112
+ const { action, from, to, history = [] } = session;
113
+
114
+ const goNextState = async (stage, previousSucceed) => {
115
+ session = await states.session.update(sessionId, {
116
+ stage,
117
+ history: [...history, { stage: session.stage, succeed: previousSucceed }],
118
+ });
119
+ // Emit events so client will keep up
120
+ states.node.emit(EVENTS.NODE_MAINTAIN_PROGRESS, session);
121
+ };
122
+
123
+ // 1. enter maintenance mode
124
+ if (session.stage === NODE_MAINTAIN_PROGRESS.SETUP) {
125
+ logger.info('enter maintenance mode', { from, to, sessionId });
126
+ await states.node.enterMode(NODE_MODES.MAINTENANCE);
127
+ if (action === 'upgrade') {
128
+ await goNextState(NODE_MAINTAIN_PROGRESS.INSTALLING, true);
129
+ }
130
+ if (action === 'restart') {
131
+ await goNextState(NODE_MAINTAIN_PROGRESS.VERIFYING, true);
132
+ }
133
+ }
134
+
135
+ // 2. install new version
136
+ if (session.stage === NODE_MAINTAIN_PROGRESS.INSTALLING) {
137
+ const result = await doRpc({ command: 'install', version: to });
138
+ logger.info('new version installed', { from, to, sessionId });
139
+ if (result.code !== 0) {
140
+ await sleep(3000);
141
+ }
142
+ await goNextState(NODE_MAINTAIN_PROGRESS.VERIFYING, result.code === 0);
143
+ }
144
+
145
+ // 2. verify new version
146
+ if (session.stage === NODE_MAINTAIN_PROGRESS.VERIFYING) {
147
+ const result = await doRpc({ command: 'verify', version: to });
148
+ await sleep(3000);
149
+ if (result.code === 0) {
150
+ logger.info('new version verified', { from, to, sessionId });
151
+ await goNextState(NODE_MAINTAIN_PROGRESS.RESTARTING, true);
152
+ } else {
153
+ logger.info('new version verify failed', { from, to, sessionId });
154
+ await goNextState(NODE_MAINTAIN_PROGRESS.ROLLBACK, false);
155
+ }
156
+ }
157
+
158
+ // 4. reset node.nextVersion/upgradeSessionId and exit maintenance mode
159
+ if (session.stage === NODE_MAINTAIN_PROGRESS.ROLLBACK) {
160
+ logger.info('rollback', { from, to, sessionId });
161
+ await sleep(3000);
162
+ await goNextState(NODE_MAINTAIN_PROGRESS.COMPLETE, true);
163
+ await sleep(5000);
164
+ await states.node.updateNodeInfo({ nextVersion: '', version: from, upgradeSessionId: '' });
165
+ try {
166
+ await states.node.exitMode(NODE_MODES.MAINTENANCE);
167
+ } catch (error) {
168
+ logger.error('Failed to rollback', { error });
169
+ }
170
+ await lock.release();
171
+ return;
172
+ }
173
+
174
+ // 3. restarting update version in node config and state
175
+ if (session.stage === NODE_MAINTAIN_PROGRESS.RESTARTING) {
176
+ logger.info('restart server', { from, to, sessionId });
177
+ await sleep(3000);
178
+ await goNextState(NODE_MAINTAIN_PROGRESS.CLEANUP, true);
179
+ await sleep(3000);
180
+ await doRpc({ command: 'restart', dataDir: process.env.ABT_NODE_DATA_DIR });
181
+ await lock.release();
182
+ return; // we should abort here and resume after restart
183
+ }
184
+
185
+ // 4. reset node.nextVersion/upgradeSessionId and exit maintenance mode
186
+ if (session.stage === NODE_MAINTAIN_PROGRESS.CLEANUP) {
187
+ logger.info('cleanup', { from, to, sessionId });
188
+ await goNextState(NODE_MAINTAIN_PROGRESS.COMPLETE, true);
189
+ await sleep(8000);
190
+ await states.node.updateNodeInfo({ nextVersion: '', version: to, upgradeSessionId: '' });
191
+ try {
192
+ await states.node.exitMode(NODE_MODES.MAINTENANCE);
193
+ } catch (error) {
194
+ logger.error('Failed to exit maintenance mode', { error });
195
+ }
196
+ }
197
+ await lock.release();
198
+ };
199
+
200
+ const isBeingMaintained = async () => {
201
+ const info = await states.node.read();
202
+ if (!info.upgradeSessionId) {
203
+ return false;
204
+ }
205
+
206
+ const session = await states.session.read(info.upgradeSessionId);
207
+ if (!session) {
208
+ return false;
209
+ }
210
+
211
+ if (session.action === 'upgrade' && !info.nextVersion) {
212
+ return false;
213
+ }
214
+
215
+ return session;
216
+ };
217
+
218
+ const getCron = () => ({
219
+ name: 'check-update',
220
+ time: '0 0 8 * * *', // check every day
221
+ // time: '0 */5 * * * *', // check every 5 minutes
222
+ fn: async () => {
223
+ const info = await states.node.read();
224
+ if (!info.autoUpgrade) {
225
+ return;
226
+ }
227
+
228
+ checkNewVersion();
229
+ },
230
+ options: { runOnInit: false },
231
+ });
232
+
233
+ module.exports = { getCron, checkNewVersion, resumeMaintain, triggerMaintain, isBeingMaintained };
@@ -0,0 +1,85 @@
1
+ const { sign } = require('@arcblock/jwt');
2
+ const { BlockletSource } = require('@blocklet/constant');
3
+ const { WHO_CAN_ACCESS } = require('@abtnode/constant');
4
+ const logger = require('@abtnode/logger')('@abtnode/util:public-to-store');
5
+
6
+ const getWallet = require('@abtnode/util/lib/get-app-wallet');
7
+ const axios = require('@abtnode/util/lib/axios');
8
+
9
+ const getAppToken = (blocklet) =>
10
+ sign(blocklet.environmentObj?.BLOCKLET_APP_ID, blocklet.environmentObj?.BLOCKLET_APP_SK);
11
+
12
+ const getAppId = (blocklet) => blocklet.environmentObj?.BLOCKLET_APP_ID;
13
+
14
+ const getAppSK = (blocklet) => blocklet.environmentObj?.BLOCKLET_APP_SK;
15
+
16
+ const getBlockletDid = (blocklet) => blocklet.meta?.bundleDid;
17
+
18
+ const getAppUrl = (blocklet) => blocklet.environmentObj?.BLOCKLET_APP_URL;
19
+
20
+ /**
21
+ * verify manages the permissions of blocklet public instance
22
+ * @param {*} blocklet
23
+ * @returns bool
24
+ */
25
+ const verifyPublicInstance = (blocklet) => {
26
+ const { whoCanAccess = WHO_CAN_ACCESS.ALL } = blocklet.settings;
27
+ return blocklet.source === BlockletSource.registry && whoCanAccess === WHO_CAN_ACCESS.ALL;
28
+ };
29
+
30
+ /**
31
+ *
32
+ * @param {*} blocklet
33
+ * @param {*} {userDid publicToStore}
34
+ * @returns bool
35
+ */
36
+ async function handleInstanceInStore(blocklet, { userDid = null, publicToStore = false } = {}) {
37
+ if (!blocklet) {
38
+ logger.error('blocklet argument is required');
39
+ throw new Error('blocklet argument is required');
40
+ }
41
+
42
+ const ownerDid = userDid || blocklet.settings?.owner?.did;
43
+ if (!ownerDid) {
44
+ return false;
45
+ }
46
+
47
+ if (!verifyPublicInstance(blocklet)) {
48
+ logger.error('no permission to set publicInstance');
49
+ throw new Error('no permission to set publicInstance');
50
+ }
51
+
52
+ const appToken = getAppToken(blocklet);
53
+ const blockletDid = getBlockletDid(blocklet);
54
+ const appId = getAppId(blocklet);
55
+ const appSK = getAppSK(blocklet);
56
+ const wallet = getWallet(appSK);
57
+ const body = {
58
+ appToken,
59
+ appId,
60
+ appPK: wallet.publicKey,
61
+ ownerDid,
62
+ appUrl: getAppUrl(blocklet),
63
+ blockletDid,
64
+ };
65
+
66
+ const api = axios.create({ baseURL: blocklet.deployedFrom, timeout: 1000 * 10 });
67
+ if (!publicToStore) {
68
+ try {
69
+ await api.delete(`/api/blocklet-instances/${appId}`, { data: body });
70
+ } catch (error) {
71
+ logger.error('failed to delete blocklet instance', { error });
72
+ throw new Error('failed to delete blocklet instance');
73
+ }
74
+ } else {
75
+ try {
76
+ await api.put(`/api/blocklet-instances/${appId}`, body);
77
+ } catch (error) {
78
+ logger.error('failed to update blocklet instance', { error });
79
+ throw new Error('failed to delete blocklet instance');
80
+ }
81
+ }
82
+ return true;
83
+ }
84
+
85
+ module.exports = handleInstanceInStore;
package/lib/util/ready.js CHANGED
@@ -33,7 +33,7 @@ const createStateReadyHandler =
33
33
  console.error('Sharing data dir between Blocklet Server instances may break things!');
34
34
  console.error('======================================================\x1b[0m');
35
35
  console.log('\nIf you intend to use this dir:');
36
- console.log(` 1. Stop Blocklet Server by ${chalk.cyan('abtnode stop --force')}`);
36
+ console.log(` 1. Stop Blocklet Server by ${chalk.cyan('blocklet server stop --force')}`);
37
37
  console.log(` 2. Clear data dir by ${chalk.cyan(`rm -r ${options.dataDir}`)}`);
38
38
  console.log(' 3. Reinitialize and start Blocklet Server');
39
39
  process.exit(1);
@@ -1,8 +1,10 @@
1
1
  const semver = require('semver');
2
2
 
3
- let { version: actualVersion } = require('../../package.json');
3
+ let { version: actualServerVersion } = require('../../package.json');
4
4
 
5
- const defaults = { abtnode: '>= 1.1.0', os: '*', cpu: '*' };
5
+ const { version: actualNodejsVersion } = process;
6
+
7
+ const defaults = { abtnode: '>= 1.1.0', os: '*', cpu: '*', nodejs: '*' };
6
8
 
7
9
  const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
8
10
  if (!requirements || typeof requirements !== 'object') {
@@ -10,14 +12,23 @@ const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
10
12
  }
11
13
 
12
14
  const { platform: actualOs, arch: actualCpu } = process;
13
- const { os: expectedOs, cpu: expectedCpu, abtnode: expectedVersion } = Object.assign(defaults, requirements);
15
+ const {
16
+ os: expectedOs,
17
+ cpu: expectedCpu,
18
+ abtnode,
19
+ server,
20
+ nodejs: expectedNodejsVersion,
21
+ } = Object.assign(defaults, requirements);
22
+ const expectedServerVersion = server || abtnode;
14
23
 
15
24
  const isOsSatisfied =
16
25
  expectedOs === '*' || expectedOs === actualOs || (Array.isArray(expectedOs) && expectedOs.includes(actualOs));
17
26
  const isCpuSatisfied =
18
27
  expectedCpu === '*' || expectedCpu === actualCpu || (Array.isArray(expectedCpu) && expectedCpu.includes(actualCpu));
19
28
 
20
- const isVersionSatisfied = semver.satisfies(semver.coerce(actualVersion), expectedVersion);
29
+ const isServerVersionSatisfied = semver.satisfies(semver.coerce(actualServerVersion), expectedServerVersion);
30
+
31
+ const isNodejsVersionSatisfied = semver.satisfies(semver.coerce(actualNodejsVersion), expectedNodejsVersion);
21
32
 
22
33
  if (throwOnUnsatisfied) {
23
34
  if (isOsSatisfied === false) {
@@ -26,17 +37,25 @@ const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
26
37
  if (isCpuSatisfied === false) {
27
38
  throw new Error(`Your blocklet is not supported on architecture ${actualCpu}`);
28
39
  }
29
- if (isVersionSatisfied === false) {
30
- throw new Error(`Your blocklet is not supported on abtnode version v${actualVersion}`);
40
+ if (isServerVersionSatisfied === false) {
41
+ throw new Error(
42
+ `Expected server version for the blocklet is ${expectedServerVersion}, but your server version is ${actualServerVersion}, please upgrade or downgrade your server to continue.`
43
+ );
44
+ }
45
+ if (isNodejsVersionSatisfied === false) {
46
+ throw new Error(
47
+ `Expected Nodejs version for the blocklet is ${expectedNodejsVersion}, but your Nodejs version is ${actualNodejsVersion}, please upgrade or downgrade Nodejs and restart your server.`
48
+ );
31
49
  }
32
50
  }
33
51
 
34
- return isOsSatisfied && isCpuSatisfied && isVersionSatisfied;
52
+ return isOsSatisfied && isCpuSatisfied && isServerVersionSatisfied;
35
53
  };
36
54
 
37
55
  // Just for test
38
- isSatisfied._setActualVersion = (d) => {
39
- actualVersion = d;
56
+ isSatisfied._setActualServerVersion = (d) => {
57
+ actualServerVersion = d;
40
58
  };
59
+ isSatisfied._actualNodejsVersion = actualNodejsVersion;
41
60
 
42
61
  module.exports = isSatisfied;
@@ -52,14 +52,14 @@ const resetSites = async ({ context, routerManager, takeRoutingSnapshot }) => {
52
52
  };
53
53
 
54
54
  /* istanbul ignore next */
55
- const resetCertificates = async ({ context, routerManager }) => {
56
- const certs = await states.certificate.find();
55
+ const resetCertificates = async ({ certManager }) => {
56
+ const certs = await certManager.getAll();
57
57
  for (let i = 0; i < certs.length; i++) {
58
58
  const cert = certs[i];
59
- if (!cert.sans.includes('*.ip.abtnet.io')) {
59
+ if (!cert.domain === '*.ip.abtnet.io') {
60
60
  try {
61
61
  // eslint-disable-next-line no-await-in-loop
62
- await routerManager.deleteCertificate({ id: cert.id }, context);
62
+ await certManager.remove(cert.id);
63
63
  } catch (err) {
64
64
  // Do nothing
65
65
  }
@@ -77,7 +77,7 @@ const resetUsers = async ({ teamManager }) => {
77
77
 
78
78
  let count = 0;
79
79
  for (const user of users) {
80
- if (nodeInfo.nodeOwner.did !== user.find) {
80
+ if (nodeInfo.nodeOwner.did !== user.did) {
81
81
  // eslint-disable-next-line no-await-in-loop
82
82
  await userState.remove({ did: user.did });
83
83
  count++;
@@ -109,7 +109,15 @@ const resetFns = {
109
109
  invitations: resetInvitations,
110
110
  };
111
111
 
112
- module.exports = async ({ params, context, blockletManager, routerManager, takeRoutingSnapshot, teamManager }) => {
112
+ module.exports = async ({
113
+ params,
114
+ context,
115
+ blockletManager,
116
+ routerManager,
117
+ takeRoutingSnapshot,
118
+ teamManager,
119
+ certManager,
120
+ }) => {
113
121
  if (process.env.NODE_ENV !== 'e2e') {
114
122
  throw new Error('Reset node only exists for test purpose');
115
123
  }
@@ -140,7 +148,14 @@ module.exports = async ({ params, context, blockletManager, routerManager, takeR
140
148
  }
141
149
 
142
150
  // eslint-disable-next-line no-await-in-loop
143
- results[key] = await resetFns[key]({ context, blockletManager, routerManager, takeRoutingSnapshot, teamManager });
151
+ results[key] = await resetFns[key]({
152
+ context,
153
+ blockletManager,
154
+ routerManager,
155
+ takeRoutingSnapshot,
156
+ teamManager,
157
+ certManager,
158
+ });
144
159
  }
145
160
  }
146
161
 
@@ -0,0 +1,13 @@
1
+ const { BLOCKLET_SITE_GROUP_SUFFIX } = require('@abtnode/constant');
2
+
3
+ const getBlockletDomainGroupName = (did) => `${did}${BLOCKLET_SITE_GROUP_SUFFIX}`;
4
+
5
+ const getDidFromDomainGroupName = (name) => {
6
+ const did = name.replace(BLOCKLET_SITE_GROUP_SUFFIX, '');
7
+ return did;
8
+ };
9
+
10
+ module.exports = {
11
+ getBlockletDomainGroupName,
12
+ getDidFromDomainGroupName,
13
+ };
@@ -0,0 +1,16 @@
1
+ const axon = require('axon');
2
+ const logger = require('@abtnode/logger')('@abtnode/core:rpc');
3
+
4
+ const sock = axon.socket('req');
5
+ sock.connect(Number(process.env.ABT_NODE_UPDATER_PORT || 40405), '127.0.0.1');
6
+
7
+ const doRpc = async (message) =>
8
+ new Promise((resolve) => {
9
+ logger.info('send rpc request', message);
10
+ sock.send(JSON.stringify(message), (result) => {
11
+ logger.info('receive rpc response', result);
12
+ resolve(result);
13
+ });
14
+ });
15
+
16
+ module.exports = doRpc;
@@ -0,0 +1,179 @@
1
+ const joinURL = require('url-join');
2
+ const pick = require('lodash/pick');
3
+ const isBase64 = require('is-base64');
4
+
5
+ const { BLOCKLET_STORE_API_PREFIX, BLOCKLET_STORE_META_PATH } = require('@abtnode/constant');
6
+ const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
7
+ const verifyMultiSig = require('@blocklet/meta/lib/verify-multi-sig');
8
+
9
+ const { name } = require('../../package.json');
10
+ const logger = require('@abtnode/logger')(`${name}:util:registry`); // eslint-disable-line
11
+
12
+ const request = require('./request');
13
+
14
+ const fixAndVerifyMetaFromStore = (meta) => {
15
+ const sanitized = validateMeta(meta, { ensureDist: false, schemaOptions: { noDefaults: true, stripUnknown: true } });
16
+
17
+ if (meta.children) {
18
+ sanitized.children = meta.children;
19
+ delete sanitized.components;
20
+ }
21
+
22
+ if (meta.navigation) {
23
+ meta.navigation.forEach((nav, index) => {
24
+ if (nav.child) {
25
+ sanitized.navigation[index].child = nav.child;
26
+ delete sanitized.navigation[index].component;
27
+ }
28
+ });
29
+ }
30
+
31
+ // verify
32
+
33
+ try {
34
+ const result = verifyMultiSig(sanitized);
35
+ if (!result) {
36
+ throw new Error('invalid signature from developer or store');
37
+ }
38
+ } catch (err) {
39
+ throw new Error(`Invalid blocklet meta: ${err.message}`);
40
+ }
41
+
42
+ // this step comes last because it has side effects: the meta is changed
43
+ return fixAndValidateService(meta);
44
+ };
45
+
46
+ const validateStoreUrl = async (registry) => {
47
+ const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
48
+ try {
49
+ const res = await request.get(url);
50
+ if (Array.isArray(res.data)) {
51
+ return res.data;
52
+ }
53
+
54
+ logger.error('Blocklet list fetch failed ', { url, data: res.data });
55
+ throw new Error('blocklet list fetch failed');
56
+ } catch (error) {
57
+ logger.error('Blocklet registry refresh failed', { url, error });
58
+ throw new Error(`Invalid Blocklet Registry URL ${registry}: ${error.message}`);
59
+ }
60
+ };
61
+
62
+ const getStoreMeta = async (registry) => {
63
+ try {
64
+ const url = joinURL(registry, BLOCKLET_STORE_META_PATH, `?__t__=${Date.now()}`);
65
+ const { data } = await request.get(url);
66
+
67
+ if (!data) {
68
+ return {};
69
+ }
70
+
71
+ const requiredFields = ['name', 'description', 'maintainer'];
72
+
73
+ const missingFields = requiredFields.filter((x) => !data[x]);
74
+ if (missingFields.length > 0) {
75
+ throw new Error(`the registry missing required information: ${missingFields.join(', ')}`);
76
+ }
77
+
78
+ const result = pick(data, ['id', 'name', 'description', 'maintainer', 'cdnUrl', 'chainHost']);
79
+ const { logoUrl } = data;
80
+ if (logoUrl) {
81
+ if (logoUrl.startsWith('http') === true) {
82
+ result.logoUrl = logoUrl;
83
+ } else if (isBase64(logoUrl, { allowMime: true })) {
84
+ result.logoUrl = logoUrl;
85
+ } else {
86
+ result.logoUrl = joinURL(registry, logoUrl);
87
+ }
88
+ }
89
+
90
+ return result;
91
+ } catch (err) {
92
+ throw new Error(`Can not get meta info for registry [${registry}]: ${err.message}`);
93
+ }
94
+ };
95
+
96
+ const parseSourceUrl = async (url) => {
97
+ const { origin, pathname } = new URL(url);
98
+
99
+ const match = pathname.match(/^\/api\/blocklets\/(\w*)\/blocklet\.json/);
100
+ if (match) {
101
+ try {
102
+ const m = await getStoreMeta(origin);
103
+ if (m && m.id) {
104
+ return {
105
+ inStore: true,
106
+ registryUrl: origin,
107
+ blockletDid: match[1],
108
+ };
109
+ }
110
+ } catch {
111
+ // meat is not in store, do nothing
112
+ }
113
+ }
114
+
115
+ return {
116
+ inStore: false,
117
+ };
118
+ };
119
+
120
+ const resolveTarballURL = async ({ did, tarball = '', storeUrl = '' }) => {
121
+ if (!tarball) {
122
+ return '';
123
+ }
124
+
125
+ if (tarball.startsWith('file://')) {
126
+ return decodeURIComponent(tarball);
127
+ }
128
+
129
+ if (tarball.startsWith('http://') || tarball.startsWith('https://')) {
130
+ return tarball;
131
+ }
132
+
133
+ if (!storeUrl) {
134
+ return '';
135
+ }
136
+
137
+ if (!did) {
138
+ return '';
139
+ }
140
+
141
+ return joinURL(storeUrl, 'api', 'blocklets', did, tarball);
142
+ };
143
+
144
+ const getBlockletMetaUrl = ({ did, storeUrl }) =>
145
+ joinURL(storeUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json`);
146
+
147
+ const getBlockletMeta = async ({ did, storeUrl }) => {
148
+ const url = joinURL(storeUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json?__t__=${Date.now()}`);
149
+
150
+ const { data } = await request.get(url);
151
+ try {
152
+ if (data.did !== did) {
153
+ throw new Error('Invalid blocklet meta: did does not match');
154
+ }
155
+
156
+ const meta = fixAndVerifyMetaFromStore(data);
157
+
158
+ meta.dist.tarball = await resolveTarballURL({
159
+ did,
160
+ tarball: meta.dist.tarball,
161
+ storeUrl,
162
+ });
163
+
164
+ return meta;
165
+ } catch (err) {
166
+ logger.error('failed to get blocklet meta', { did, data, error: err });
167
+ throw err;
168
+ }
169
+ };
170
+
171
+ module.exports = {
172
+ validateStoreUrl,
173
+ getStoreMeta,
174
+ parseSourceUrl,
175
+ getBlockletMeta,
176
+ resolveTarballURL,
177
+ getBlockletMetaUrl,
178
+ fixAndVerifyMetaFromStore,
179
+ };