@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.
- package/lib/api/node.js +67 -69
- package/lib/api/team.js +386 -55
- package/lib/blocklet/downloader/blocklet-downloader.js +226 -0
- package/lib/blocklet/downloader/bundle-downloader.js +272 -0
- package/lib/blocklet/downloader/constants.js +3 -0
- package/lib/blocklet/downloader/resolve-download.js +199 -0
- package/lib/blocklet/extras.js +83 -26
- package/lib/blocklet/hooks.js +18 -65
- package/lib/blocklet/manager/base.js +10 -16
- package/lib/blocklet/manager/disk.js +1679 -1566
- package/lib/blocklet/manager/helper/install-application-from-backup.js +177 -0
- package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
- package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
- package/lib/blocklet/manager/helper/install-component-from-dev.js +84 -0
- package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
- package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
- package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +450 -0
- package/lib/blocklet/manager/helper/rollback-cache.js +41 -0
- package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
- package/lib/blocklet/migration.js +30 -52
- package/lib/blocklet/storage/backup/audit-log.js +27 -0
- package/lib/blocklet/storage/backup/base.js +62 -0
- package/lib/blocklet/storage/backup/blocklet-extras.js +92 -0
- package/lib/blocklet/storage/backup/blocklet.js +70 -0
- package/lib/blocklet/storage/backup/blocklets.js +74 -0
- package/lib/blocklet/storage/backup/data.js +19 -0
- package/lib/blocklet/storage/backup/logs.js +24 -0
- package/lib/blocklet/storage/backup/routing-rule.js +19 -0
- package/lib/blocklet/storage/backup/spaces.js +240 -0
- package/lib/blocklet/storage/restore/base.js +67 -0
- package/lib/blocklet/storage/restore/blocklet-extras.js +86 -0
- package/lib/blocklet/storage/restore/blocklet.js +56 -0
- package/lib/blocklet/storage/restore/blocklets.js +43 -0
- package/lib/blocklet/storage/restore/logs.js +21 -0
- package/lib/blocklet/storage/restore/spaces.js +156 -0
- package/lib/blocklet/storage/utils/hash.js +51 -0
- package/lib/blocklet/storage/utils/zip.js +43 -0
- package/lib/cert.js +206 -0
- package/lib/event.js +237 -64
- package/lib/index.js +191 -83
- package/lib/migrations/1.0.21-update-config.js +1 -1
- package/lib/migrations/1.0.22-max-memory.js +1 -1
- package/lib/migrations/1.0.25.js +1 -1
- package/lib/migrations/1.0.32-update-config.js +1 -1
- package/lib/migrations/1.0.33-blocklets.js +1 -1
- package/lib/migrations/1.5.20-registry.js +15 -0
- package/lib/migrations/1.6.17-blocklet-children.js +48 -0
- package/lib/migrations/1.6.21-rename-ip-echo-domain.js +35 -0
- package/lib/migrations/1.6.4-security.js +59 -0
- package/lib/migrations/1.6.5-security.js +60 -0
- package/lib/migrations/1.6.9-update-node-info-and-certificate.js +38 -0
- package/lib/migrations/1.7.1-blocklet-setup.js +18 -0
- package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
- package/lib/migrations/1.7.15-blocklet-bundle-source.js +42 -0
- package/lib/migrations/1.7.20-blocklet-component.js +41 -0
- package/lib/migrations/1.8.33-blocklet-mem-limit.js +20 -0
- package/lib/migrations/README.md +1 -1
- package/lib/migrations/index.js +6 -2
- package/lib/monitor/blocklet-runtime-monitor.js +200 -0
- package/lib/monitor/get-history-list.js +37 -0
- package/lib/monitor/node-runtime-monitor.js +228 -0
- package/lib/router/helper.js +572 -497
- package/lib/router/index.js +85 -21
- package/lib/router/manager.js +146 -187
- package/lib/states/README.md +36 -1
- package/lib/states/access-key.js +39 -17
- package/lib/states/audit-log.js +462 -0
- package/lib/states/base.js +4 -213
- package/lib/states/blocklet-extras.js +194 -138
- package/lib/states/blocklet.js +361 -104
- package/lib/states/cache.js +8 -6
- package/lib/states/challenge.js +5 -5
- package/lib/states/index.js +19 -36
- package/lib/states/migration.js +4 -4
- package/lib/states/node.js +135 -46
- package/lib/states/notification.js +22 -35
- package/lib/states/session.js +17 -9
- package/lib/states/site.js +50 -25
- package/lib/states/user.js +74 -20
- package/lib/states/webhook.js +10 -6
- package/lib/team/manager.js +124 -7
- package/lib/util/blocklet.js +1223 -246
- package/lib/util/chain.js +1 -1
- package/lib/util/default-node-config.js +5 -23
- package/lib/util/disk-monitor.js +13 -10
- package/lib/util/domain-status.js +84 -15
- package/lib/util/get-accessible-external-node-ip.js +2 -2
- package/lib/util/get-domain-for-blocklet.js +13 -0
- package/lib/util/get-meta-from-url.js +33 -0
- package/lib/util/index.js +207 -272
- package/lib/util/ip.js +6 -0
- package/lib/util/maintain.js +233 -0
- package/lib/util/public-to-store.js +85 -0
- package/lib/util/ready.js +1 -1
- package/lib/util/requirement.js +28 -9
- package/lib/util/reset-node.js +22 -7
- package/lib/util/router.js +13 -0
- package/lib/util/rpc.js +16 -0
- package/lib/util/store.js +179 -0
- package/lib/util/sysinfo.js +44 -0
- package/lib/util/ua.js +54 -0
- package/lib/validators/blocklet-extra.js +24 -0
- package/lib/validators/node.js +25 -12
- package/lib/validators/permission.js +16 -1
- package/lib/validators/role.js +17 -3
- package/lib/validators/router.js +40 -20
- package/lib/validators/trusted-passport.js +1 -0
- package/lib/validators/util.js +22 -5
- package/lib/webhook/index.js +45 -35
- package/lib/webhook/sender/index.js +5 -0
- package/lib/webhook/sender/slack/index.js +1 -1
- package/lib/webhook/sender/wallet/index.js +48 -0
- package/package.json +54 -36
- package/lib/blocklet/registry.js +0 -205
- package/lib/states/https-cert.js +0 -67
- package/lib/util/get-ip-dns-domain-for-blocklet.js +0 -19
- package/lib/util/service.js +0 -66
- package/lib/util/upgrade.js +0 -178
- /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('
|
|
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);
|
package/lib/util/requirement.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
const semver = require('semver');
|
|
2
2
|
|
|
3
|
-
let { version:
|
|
3
|
+
let { version: actualServerVersion } = require('../../package.json');
|
|
4
4
|
|
|
5
|
-
const
|
|
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 {
|
|
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
|
|
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 (
|
|
30
|
-
throw new Error(
|
|
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 &&
|
|
52
|
+
return isOsSatisfied && isCpuSatisfied && isServerVersionSatisfied;
|
|
35
53
|
};
|
|
36
54
|
|
|
37
55
|
// Just for test
|
|
38
|
-
isSatisfied.
|
|
39
|
-
|
|
56
|
+
isSatisfied._setActualServerVersion = (d) => {
|
|
57
|
+
actualServerVersion = d;
|
|
40
58
|
};
|
|
59
|
+
isSatisfied._actualNodejsVersion = actualNodejsVersion;
|
|
41
60
|
|
|
42
61
|
module.exports = isSatisfied;
|
package/lib/util/reset-node.js
CHANGED
|
@@ -52,14 +52,14 @@ const resetSites = async ({ context, routerManager, takeRoutingSnapshot }) => {
|
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
/* istanbul ignore next */
|
|
55
|
-
const resetCertificates = async ({
|
|
56
|
-
const certs = await
|
|
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.
|
|
59
|
+
if (!cert.domain === '*.ip.abtnet.io') {
|
|
60
60
|
try {
|
|
61
61
|
// eslint-disable-next-line no-await-in-loop
|
|
62
|
-
await
|
|
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.
|
|
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 ({
|
|
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]({
|
|
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
|
+
};
|
package/lib/util/rpc.js
ADDED
|
@@ -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
|
+
};
|