@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,177 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const omit = require('lodash/omit');
|
|
4
|
+
|
|
5
|
+
const { forEachBlockletSync, getBlockletAppIdList } = require('@blocklet/meta/lib/util');
|
|
6
|
+
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
7
|
+
|
|
8
|
+
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
9
|
+
|
|
10
|
+
const logger = require('@abtnode/logger')('@abtnode/core:install-app-backup');
|
|
11
|
+
|
|
12
|
+
const { validateBlocklet, checkDuplicateAppSk, getAppDirs } = require('../../../util/blocklet');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* backup 目录结构
|
|
16
|
+
* /blocklets/<name1>version>
|
|
17
|
+
* /blocklets/<name2>version>
|
|
18
|
+
* /blocklets/<name3>version>
|
|
19
|
+
* /data
|
|
20
|
+
* /blocklet.json
|
|
21
|
+
* /blocklet_extras.json
|
|
22
|
+
* @see Blocklet 端到端备份方案的设计与实现(一期) https://team.arcblock.io/comment/discussions/e62084d5-fafa-489e-91d5-defcd6e93223
|
|
23
|
+
* @param {{ url: string, appSk: string, moveDir: boolean, manager: import('../disk'), states: import('../../../states/index'), context: Record<string, string> }}
|
|
24
|
+
* @return {Promise<any>}
|
|
25
|
+
*/
|
|
26
|
+
const installApplicationFromBackup = async ({ url, appSk, moveDir, context = {}, states, manager } = {}) => {
|
|
27
|
+
// TODO: support more url schema feature (http, did-spaces)
|
|
28
|
+
if (!url.startsWith('file://')) {
|
|
29
|
+
throw new Error('url must starts with file://');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const dir = url.replace('file://', '');
|
|
33
|
+
|
|
34
|
+
if (!dir || !fs.existsSync(dir)) {
|
|
35
|
+
throw new Error(`dir(${dir}) does not exist`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// parse data from source dir
|
|
39
|
+
const srcBundleDirs = await getAppDirs(path.join(dir, 'blocklets'));
|
|
40
|
+
|
|
41
|
+
const srcDataDir = path.join(dir, 'data');
|
|
42
|
+
|
|
43
|
+
/** @type {import('@abtnode/client').BlockletState} */
|
|
44
|
+
const state = omit(fs.readJSONSync(path.join(dir, 'blocklet.json')), [
|
|
45
|
+
'_id',
|
|
46
|
+
'createdAt',
|
|
47
|
+
'updatedAt',
|
|
48
|
+
'installedAt',
|
|
49
|
+
'startedAt',
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
if (!state.structVersion) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
'Only application of structVersion 2 can be restored on this server. Please migrate you application and re-backup your application on your previous server.'
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { meta } = state;
|
|
59
|
+
const { did, name: appName } = meta;
|
|
60
|
+
|
|
61
|
+
const extra = omit(fs.readJSONSync(path.join(dir, 'blocklet-extras.json')), ['_id', 'createdAt', 'updatedAt']);
|
|
62
|
+
|
|
63
|
+
if (state.meta.did !== extra.did) {
|
|
64
|
+
throw new Error(`did does not match in blocklet.json (${state.meta.did}) and blocklet_extra.json ${extra.did}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
forEachBlockletSync(state, (component) => {
|
|
68
|
+
delete component.status;
|
|
69
|
+
delete component.ports;
|
|
70
|
+
delete component.environments;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await validateBlocklet({ meta });
|
|
74
|
+
|
|
75
|
+
const existState = await states.blocklet.hasBlocklet(did);
|
|
76
|
+
|
|
77
|
+
if (existState) {
|
|
78
|
+
logger.error('blocklet is already exist', { did });
|
|
79
|
+
throw new Error(`blocklet is already exist. did: ${did}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (appSk) {
|
|
83
|
+
extra.configs = extra.configs || [];
|
|
84
|
+
const skConfig = extra.configs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
|
|
85
|
+
if (skConfig) {
|
|
86
|
+
skConfig.value = appSk;
|
|
87
|
+
skConfig.secure = true;
|
|
88
|
+
skConfig.shared = false;
|
|
89
|
+
} else {
|
|
90
|
+
extra.configs.push({
|
|
91
|
+
key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK,
|
|
92
|
+
value: appSk,
|
|
93
|
+
secure: true,
|
|
94
|
+
shared: false,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// validate appDid
|
|
100
|
+
const nodeInfo = await states.node.read();
|
|
101
|
+
const appIdList = getBlockletAppIdList(state);
|
|
102
|
+
const { wallet } = getBlockletInfo({ meta: state.meta, configs: extra.configs, environments: [] }, nodeInfo.sk);
|
|
103
|
+
if (appIdList.includes(wallet.address) === false) {
|
|
104
|
+
throw new Error('blocklet appDid is different from the previous one');
|
|
105
|
+
}
|
|
106
|
+
await checkDuplicateAppSk({ sk: wallet.secretKey, states });
|
|
107
|
+
|
|
108
|
+
states.blockletExtras.encryptSecurityData({ data: extra, rootDid: extra.did });
|
|
109
|
+
|
|
110
|
+
logger.info('installFromBackup', { srcBundleDirs, srcDataDir });
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
// copy extra
|
|
114
|
+
const existExtra = await states.blockletExtras.find({ did });
|
|
115
|
+
if (existExtra) {
|
|
116
|
+
// 如果数据存在, 当前视为脏数据, 直接删除
|
|
117
|
+
logger.error('old extra state exists and will be force removed', { existExtra });
|
|
118
|
+
await states.blockletExtras.remove({ did });
|
|
119
|
+
}
|
|
120
|
+
await states.blockletExtras.insert(extra);
|
|
121
|
+
logger.info('blocklet extra is copied successfully');
|
|
122
|
+
|
|
123
|
+
// add blocklet
|
|
124
|
+
await states.blocklet.addBlocklet(state);
|
|
125
|
+
logger.info('blocklet state is added successfully');
|
|
126
|
+
|
|
127
|
+
// copy bundle
|
|
128
|
+
// 假设相同名称的应用,肯定是同一个应用
|
|
129
|
+
// 假设版本号相同时, 应用不会变更
|
|
130
|
+
// FIXME: blocklet bundle name/did 不是唯一的. 修改 blocklet bundle name/did 生成方式 使 name/bundle 唯一
|
|
131
|
+
await Promise.all(
|
|
132
|
+
srcBundleDirs.map(async ({ key: bundleName, dir: srcDir }) => {
|
|
133
|
+
const installDir = path.join(manager.dataDirs.blocklets, bundleName);
|
|
134
|
+
if (fs.existsSync(installDir)) {
|
|
135
|
+
logger.info(`${bundleName} is already exist`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
140
|
+
if (moveDir) {
|
|
141
|
+
await fs.move(srcDir, installDir, { overwrite: true });
|
|
142
|
+
} else {
|
|
143
|
+
await fs.copy(srcDir, installDir);
|
|
144
|
+
}
|
|
145
|
+
logger.info(`bundle is ${moveDir ? 'moved' : 'copied'} successfully`, { installDir });
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
// 从 store 下载 blocklet
|
|
149
|
+
await manager.blockletDownloader.download(state, { skipCheckIntegrity: true });
|
|
150
|
+
|
|
151
|
+
const dataDir = path.join(manager.dataDirs.data, appName);
|
|
152
|
+
if (fs.existsSync(dataDir)) {
|
|
153
|
+
logger.error('old data exists and will be force removed', { dataDir });
|
|
154
|
+
await fs.remove(dataDir);
|
|
155
|
+
}
|
|
156
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
157
|
+
if (fs.existsSync(srcDataDir)) {
|
|
158
|
+
if (moveDir) {
|
|
159
|
+
await fs.move(srcDataDir, dataDir, { overwrite: true });
|
|
160
|
+
} else {
|
|
161
|
+
await fs.copy(srcDataDir, dataDir);
|
|
162
|
+
}
|
|
163
|
+
logger.info(`data is ${moveDir ? 'moved' : 'copied'} successfully`);
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
logger.error('installFromBackup failed', { did, error });
|
|
167
|
+
|
|
168
|
+
await manager._rollback('install', did);
|
|
169
|
+
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
logger.info('start install blocklet', { did });
|
|
174
|
+
return manager._installBlocklet({ did, context });
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
module.exports = { installApplicationFromBackup };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
|
|
4
|
+
const { toSvg: createDidLogo } =
|
|
5
|
+
process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
|
|
6
|
+
const { BlockletStatus, BLOCKLET_MODES, fromBlockletStatus, BlockletSource } = require('@blocklet/constant');
|
|
7
|
+
|
|
8
|
+
const logger = require('@abtnode/logger')('@abtnode/core:install-app-dev');
|
|
9
|
+
const { ensureMeta } = require('../../../util/blocklet');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param {{
|
|
14
|
+
* manager: import('../disk'),
|
|
15
|
+
* states: import('../../../states/index')
|
|
16
|
+
* }}
|
|
17
|
+
* @returns
|
|
18
|
+
*/
|
|
19
|
+
const installApplicationFromDev = async ({ folder, meta, states, manager } = {}) => {
|
|
20
|
+
const { did, version } = meta;
|
|
21
|
+
|
|
22
|
+
const exist = await states.blocklet.getBlocklet(did);
|
|
23
|
+
if (exist) {
|
|
24
|
+
if (exist.mode === BLOCKLET_MODES.PRODUCTION) {
|
|
25
|
+
throw new Error('The blocklet of production mode already exists, please remove it before developing');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const status = fromBlockletStatus(exist.status);
|
|
29
|
+
if (['starting', 'running'].includes(status)) {
|
|
30
|
+
throw new Error(`The blocklet is already on ${status}, please stop it before developing`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
logger.info('remove blocklet for dev', { did, version });
|
|
34
|
+
|
|
35
|
+
await manager.delete({ did, keepLogsDir: false });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// delete process
|
|
39
|
+
try {
|
|
40
|
+
await manager.deleteProcess({ did });
|
|
41
|
+
logger.info('delete blocklet precess for dev', { did, version });
|
|
42
|
+
} catch (err) {
|
|
43
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
44
|
+
logger.error('failed to delete blocklet process for dev', { error: err });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// create component
|
|
49
|
+
const component = {
|
|
50
|
+
meta: ensureMeta(meta),
|
|
51
|
+
mountPoint: '/',
|
|
52
|
+
source: BlockletSource.local,
|
|
53
|
+
deployedFrom: folder,
|
|
54
|
+
status: BlockletStatus.installed,
|
|
55
|
+
mode: BLOCKLET_MODES.DEVELOPMENT,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// create app
|
|
59
|
+
const added = await manager._addBlocklet({
|
|
60
|
+
component,
|
|
61
|
+
name: meta.name,
|
|
62
|
+
did: meta.did,
|
|
63
|
+
mode: BLOCKLET_MODES.DEVELOPMENT,
|
|
64
|
+
});
|
|
65
|
+
logger.info('add blocklet for dev', { did, version, meta });
|
|
66
|
+
|
|
67
|
+
await manager._downloadBlocklet(added);
|
|
68
|
+
|
|
69
|
+
// Add Config
|
|
70
|
+
await manager._setConfigsFromMeta(did);
|
|
71
|
+
|
|
72
|
+
// should ensure blocklet integrity
|
|
73
|
+
let blocklet = await manager.ensureBlocklet(did);
|
|
74
|
+
|
|
75
|
+
// pre install
|
|
76
|
+
await manager._runPreInstallHook(blocklet);
|
|
77
|
+
|
|
78
|
+
// Add environments
|
|
79
|
+
await manager._updateBlockletEnvironment(did);
|
|
80
|
+
blocklet = await manager.getBlocklet(did);
|
|
81
|
+
|
|
82
|
+
// post install
|
|
83
|
+
await manager._runPostInstallHook(blocklet);
|
|
84
|
+
|
|
85
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
|
|
86
|
+
|
|
87
|
+
blocklet = await manager.getBlocklet(did);
|
|
88
|
+
|
|
89
|
+
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
90
|
+
|
|
91
|
+
return blocklet;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
module.exports = { installApplicationFromDev };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
const joi = require('joi');
|
|
2
|
+
|
|
3
|
+
const { BLOCKLET_INSTALL_TYPE, NODE_MODES } = require('@abtnode/constant');
|
|
4
|
+
const { BlockletStatus, BlockletEvents, BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
5
|
+
|
|
6
|
+
const logger = require('@abtnode/logger')('@abtnode/core:install-app-general');
|
|
7
|
+
const getApplicationWallet = require('@blocklet/meta/lib/wallet');
|
|
8
|
+
|
|
9
|
+
const StoreUtil = require('../../../util/store');
|
|
10
|
+
const { getBlockletMetaFromUrl, ensureMeta, validateStore, validateInServerless } = require('../../../util/blocklet');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param {{
|
|
15
|
+
* manager: import('../disk'),
|
|
16
|
+
* states: import('../../../states/index')
|
|
17
|
+
* }} param0
|
|
18
|
+
* @returns
|
|
19
|
+
*/
|
|
20
|
+
const installApplicationFromGeneral = async ({
|
|
21
|
+
type,
|
|
22
|
+
appSk,
|
|
23
|
+
sync,
|
|
24
|
+
delay,
|
|
25
|
+
controller,
|
|
26
|
+
title,
|
|
27
|
+
description,
|
|
28
|
+
did: bundleDid,
|
|
29
|
+
storeUrl,
|
|
30
|
+
url: inputUrl,
|
|
31
|
+
context = {},
|
|
32
|
+
states,
|
|
33
|
+
manager,
|
|
34
|
+
} = {}) => {
|
|
35
|
+
const nodeInfo = await states.node.read();
|
|
36
|
+
|
|
37
|
+
let componentSourceUrl;
|
|
38
|
+
if (type === BLOCKLET_INSTALL_TYPE.STORE) {
|
|
39
|
+
if (!storeUrl) {
|
|
40
|
+
throw new Error('store url should not be empty');
|
|
41
|
+
}
|
|
42
|
+
componentSourceUrl = StoreUtil.getBlockletMetaUrl({ did: bundleDid, storeUrl });
|
|
43
|
+
await validateStore(nodeInfo, componentSourceUrl);
|
|
44
|
+
} else if (type === BLOCKLET_INSTALL_TYPE.URL) {
|
|
45
|
+
componentSourceUrl = inputUrl;
|
|
46
|
+
await validateStore(nodeInfo, componentSourceUrl);
|
|
47
|
+
} else if (type === BLOCKLET_INSTALL_TYPE.CREATE) {
|
|
48
|
+
await joi.string().label('title').max(20).required().validateAsync(title);
|
|
49
|
+
await joi.string().label('description').max(80).required().validateAsync(description);
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error(`Should not be here: unknown type ${type}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// check install count
|
|
55
|
+
if (controller?.nftId) {
|
|
56
|
+
// sometimes nedb will throw error if use states.blocklet.count({ ['controller.nftId']: controller.nftId })
|
|
57
|
+
const installedCount = await states.blockletExtras.count({ 'controller.nftId': controller.nftId });
|
|
58
|
+
|
|
59
|
+
if (installedCount >= (controller.appMaxCount || 1)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`You can only install ${controller.appMaxCount} blocklet with this credential: ${controller.nftId}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// app wallet
|
|
67
|
+
const wallet = getApplicationWallet(appSk);
|
|
68
|
+
const did = wallet.address;
|
|
69
|
+
const name = wallet.address;
|
|
70
|
+
|
|
71
|
+
// check exist
|
|
72
|
+
const exist = await states.blocklet.hasBlocklet(did);
|
|
73
|
+
if (exist) {
|
|
74
|
+
throw new Error(`blocklet ${did} already exists`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// remove dirty data
|
|
78
|
+
const oldExtraState = await states.blockletExtras.findOne({ did });
|
|
79
|
+
if (oldExtraState) {
|
|
80
|
+
logger.error('find dirty data in blocklet extra', { did });
|
|
81
|
+
await states.blockletExtras.remove({ did });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// create component
|
|
85
|
+
let component;
|
|
86
|
+
if (componentSourceUrl) {
|
|
87
|
+
const meta = await getBlockletMetaFromUrl(componentSourceUrl);
|
|
88
|
+
|
|
89
|
+
if (nodeInfo.mode === NODE_MODES.SERVERLESS) {
|
|
90
|
+
validateInServerless({ blockletMeta: meta });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
component = {
|
|
94
|
+
meta: ensureMeta(meta),
|
|
95
|
+
mountPoint: '/',
|
|
96
|
+
bundleSource: { url: componentSourceUrl },
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// create app
|
|
101
|
+
const blocklet = await manager._addBlocklet({ component, name, did, title, description });
|
|
102
|
+
logger.info('blocklet added to database', { did: blocklet.meta.did });
|
|
103
|
+
|
|
104
|
+
// create config
|
|
105
|
+
try {
|
|
106
|
+
await states.blockletExtras.addMeta({ did, meta: { did, name }, controller });
|
|
107
|
+
await manager._setConfigsFromMeta(did);
|
|
108
|
+
await states.blockletExtras.setConfigs(did, [
|
|
109
|
+
{
|
|
110
|
+
key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK,
|
|
111
|
+
value: appSk,
|
|
112
|
+
secure: true,
|
|
113
|
+
},
|
|
114
|
+
]);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
await manager._rollback('install', did, {});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!blocklet.children.length) {
|
|
120
|
+
return manager._installBlocklet({ did, context });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
|
|
125
|
+
manager.emit(BlockletEvents.added, blocklet1);
|
|
126
|
+
|
|
127
|
+
const action = 'install';
|
|
128
|
+
const downloadParams = {
|
|
129
|
+
blocklet: { ...blocklet1 },
|
|
130
|
+
context,
|
|
131
|
+
postAction: action,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// backup rollback data
|
|
135
|
+
await manager._rollbackCache.backup({ did, action });
|
|
136
|
+
|
|
137
|
+
if (sync) {
|
|
138
|
+
await manager._downloadAndInstall({ ...downloadParams, throwOnError: true });
|
|
139
|
+
return states.blocklet.getBlocklet(did);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
const ticket = manager.installQueue.push(
|
|
144
|
+
{
|
|
145
|
+
entity: 'blocklet',
|
|
146
|
+
action: 'download',
|
|
147
|
+
id: did,
|
|
148
|
+
...downloadParams,
|
|
149
|
+
},
|
|
150
|
+
did
|
|
151
|
+
);
|
|
152
|
+
ticket.on('failed', async (err) => {
|
|
153
|
+
const componentDid = component?.meta?.did;
|
|
154
|
+
logger.error('failed to install blocklet', { did, componentDid, error: err });
|
|
155
|
+
try {
|
|
156
|
+
await manager._rollback('install', did, {});
|
|
157
|
+
} catch (e) {
|
|
158
|
+
logger.error('failed to remove blocklet on install error', { did, componentDid, error: e });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
manager._createNotification(did, {
|
|
162
|
+
title: 'Blocklet Install Failed',
|
|
163
|
+
description: `Blocklet ${title || component?.meta?.title} install failed with error: ${
|
|
164
|
+
err.message || 'queue exception'
|
|
165
|
+
}`,
|
|
166
|
+
entityType: 'blocklet',
|
|
167
|
+
entityId: did,
|
|
168
|
+
severity: 'error',
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}, delay || 0);
|
|
172
|
+
|
|
173
|
+
return blocklet1;
|
|
174
|
+
} catch (err) {
|
|
175
|
+
const componentDid = component?.meta?.did;
|
|
176
|
+
logger.error('failed to install blocklet', { did, componentDid, error: err });
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
await manager._rollback('install', did, {});
|
|
180
|
+
} catch (e) {
|
|
181
|
+
logger.error('failed to remove blocklet on install error', { did, componentDid, error: e });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
module.exports = { installApplicationFromGeneral };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const logger = require('@abtnode/logger')('@abtnode/core:install-component-dev');
|
|
2
|
+
|
|
3
|
+
const { BlockletStatus, BlockletSource, BLOCKLET_MODES, fromBlockletStatus } = require('@blocklet/constant');
|
|
4
|
+
const {
|
|
5
|
+
parseComponents,
|
|
6
|
+
filterDuplicateComponents,
|
|
7
|
+
ensureMeta,
|
|
8
|
+
checkVersionCompatibility,
|
|
9
|
+
validateBlocklet,
|
|
10
|
+
} = require('../../../util/blocklet');
|
|
11
|
+
const { formatName } = require('../../../util/get-domain-for-blocklet');
|
|
12
|
+
|
|
13
|
+
const installComponentFromDev = async ({ folder, meta, rootDid, mountPoint, manager, states }) => {
|
|
14
|
+
const { did, version } = meta;
|
|
15
|
+
|
|
16
|
+
const existRoot = await states.blocklet.getBlocklet(rootDid);
|
|
17
|
+
if (!existRoot) {
|
|
18
|
+
throw new Error('Root blocklet does not exist');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const exist = existRoot.children.find((x) => x.meta.did === meta.did);
|
|
22
|
+
if (exist) {
|
|
23
|
+
if (exist.mode === BLOCKLET_MODES.PRODUCTION) {
|
|
24
|
+
throw new Error('The blocklet component of production mode already exists, please remove it before developing');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const status = fromBlockletStatus(exist.status);
|
|
28
|
+
if (['starting', 'running'].includes(status)) {
|
|
29
|
+
throw new Error(`The blocklet component is already on ${status}, please stop it before developing`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
logger.info('remove blocklet component for dev', { did, version });
|
|
33
|
+
|
|
34
|
+
await manager.deleteComponent({ did, rootDid });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let defaultPath = formatName(meta.name);
|
|
38
|
+
if (!existRoot.children.map((x) => x.mountPoint).includes('/')) {
|
|
39
|
+
defaultPath = '';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const component = {
|
|
43
|
+
meta: ensureMeta(meta),
|
|
44
|
+
mountPoint: mountPoint || `/${defaultPath}`,
|
|
45
|
+
source: BlockletSource.local,
|
|
46
|
+
deployedFrom: folder,
|
|
47
|
+
status: BlockletStatus.installed,
|
|
48
|
+
mode: BLOCKLET_MODES.DEVELOPMENT,
|
|
49
|
+
};
|
|
50
|
+
const { dynamicComponents } = await parseComponents(component);
|
|
51
|
+
dynamicComponents.unshift(component);
|
|
52
|
+
const children = filterDuplicateComponents(dynamicComponents, existRoot.children);
|
|
53
|
+
checkVersionCompatibility([...children, ...existRoot.children]);
|
|
54
|
+
await states.blocklet.addChildren(rootDid, children);
|
|
55
|
+
|
|
56
|
+
const newBlocklet = await states.blocklet.getBlocklet(rootDid);
|
|
57
|
+
await validateBlocklet(newBlocklet);
|
|
58
|
+
await manager._downloadBlocklet(newBlocklet);
|
|
59
|
+
await states.blocklet.setBlockletStatus(rootDid, BlockletStatus.stopped);
|
|
60
|
+
|
|
61
|
+
// Add Config
|
|
62
|
+
await manager._setConfigsFromMeta(rootDid);
|
|
63
|
+
|
|
64
|
+
// should ensure blocklet integrity
|
|
65
|
+
let blocklet = await manager.ensureBlocklet(rootDid);
|
|
66
|
+
|
|
67
|
+
// pre install
|
|
68
|
+
await manager._runPreInstallHook(blocklet);
|
|
69
|
+
|
|
70
|
+
// Add environments
|
|
71
|
+
await manager._updateBlockletEnvironment(rootDid);
|
|
72
|
+
blocklet = await manager.getBlocklet(rootDid);
|
|
73
|
+
|
|
74
|
+
// post install
|
|
75
|
+
await manager._runPostInstallHook(blocklet);
|
|
76
|
+
|
|
77
|
+
logger.info('add blocklet component for dev', { did, version, meta });
|
|
78
|
+
|
|
79
|
+
blocklet = await manager.getBlocklet(rootDid);
|
|
80
|
+
|
|
81
|
+
return blocklet;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
module.exports = { installComponentFromDev };
|