@abtnode/core 1.5.13 → 1.15.17

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.
@@ -26,7 +26,7 @@ const mergeConfigs = (oldConfigs, newConfigs = []) => {
26
26
  // newConfig 为用户传的,也可以是从环境变量中去读的
27
27
  const uniqConfigs = uniqBy(newConfigs, (x) => x.key || x.name);
28
28
 
29
- // `BLOCKLET_*` and `ABT_NODE_*` vars can only be set by ABT Node Daemon with only a few exceptions.
29
+ // `BLOCKLET_*` and `ABT_NODE_*` vars can only be set by Blocklet Server Daemon with only a few exceptions.
30
30
  const newConfig = uniqConfigs.filter((x) => {
31
31
  const key = x.key || x.name;
32
32
 
@@ -5,10 +5,13 @@ const camelCase = require('lodash/camelCase');
5
5
  // eslint-disable-next-line global-require
6
6
  const logger = require('@abtnode/logger')(`${require('../../package.json').name}:blocklet:hooks`);
7
7
 
8
+ const { getSafeEnv } = require('../util');
9
+
8
10
  const runAsync = ({ appDir, env, hook, progress = false }) => {
11
+ const safeEnv = getSafeEnv(env);
9
12
  const child = childProcess.exec(hook, {
10
13
  cwd: appDir,
11
- env: { ...process.env, ...env },
14
+ env: safeEnv,
12
15
  stdio: 'inherit',
13
16
  });
14
17
 
@@ -221,12 +221,6 @@ class BlockletManager extends BaseBlockletManager {
221
221
  async start({ did, checkHealthImmediately = false, throwOnError }, context) {
222
222
  logger.info('start blocklet', { did });
223
223
  const blocklet = await this.ensureBlocklet(did);
224
- try {
225
- await runMigrationScripts({ blocklet });
226
- } catch (error) {
227
- logger.error('Failed to migrate blocklet', { did, error });
228
- throw error;
229
- }
230
224
 
231
225
  try {
232
226
  // check required config
@@ -961,7 +955,7 @@ class BlockletManager extends BaseBlockletManager {
961
955
  return a.from.pathPrefix.length < b.from.pathPrefix ? 1 : -1;
962
956
  });
963
957
  const nodeIp = await getAccessibleExternalNodeIp(nodeInfo);
964
- return getBlockletInterfaces({ blocklet, context, nodeInfo, sites: routingRules, nodeIp });
958
+ return getBlockletInterfaces({ blocklet, context, nodeInfo, routingRules, nodeIp });
965
959
  }
966
960
 
967
961
  async attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context, cachedBlocklet }) {
@@ -1432,7 +1426,6 @@ class BlockletManager extends BaseBlockletManager {
1432
1426
 
1433
1427
  // download
1434
1428
  await this._downloadBlocklet(blocklet);
1435
-
1436
1429
  return this._installBlocklet({
1437
1430
  did: meta.did,
1438
1431
  context,
@@ -1644,6 +1637,7 @@ class BlockletManager extends BaseBlockletManager {
1644
1637
 
1645
1638
  const oldBlocklet = await this.state.getBlocklet(did);
1646
1639
 
1640
+ // NOTE: 目前的版本移除了降级通道,所以不需要考虑降级通道的情况
1647
1641
  const action = semver.gt(oldBlocklet.meta.version, version) ? 'downgrade' : 'upgrade';
1648
1642
  logger.info(`${action} blocklet`, { did, version });
1649
1643
 
@@ -1915,6 +1909,7 @@ class BlockletManager extends BaseBlockletManager {
1915
1909
  const { meta, source, deployedFrom, children } = newBlocklet;
1916
1910
  const { did, version, name } = meta;
1917
1911
 
1912
+ const oldVersion = oldBlocklet.meta.version;
1918
1913
  const action = semver.gt(oldBlocklet.meta.version, version) ? 'downgrade' : 'upgrade';
1919
1914
  try {
1920
1915
  // delete old process
@@ -1960,6 +1955,33 @@ class BlockletManager extends BaseBlockletManager {
1960
1955
  });
1961
1956
  await forEachBlocklet(blocklet, postInstall, { parallel: true });
1962
1957
 
1958
+ // run migrations
1959
+ const runMigration = (b) => {
1960
+ // BUG: 本身的定义是在执行 upgrade 操作时进行 migration,但是父 blocklet upgrade 时,子 blocklet 可能不需要 upgrade,就会导致子 blocklet 多走了一遍 migration 流程
1961
+ if (b.meta.did === did) {
1962
+ return runMigrationScripts({
1963
+ blocklet: b,
1964
+ oldVersion,
1965
+ newVersion: version,
1966
+ env: getRuntimeEnvironments(b, nodeEnvironments),
1967
+ appDir: b.env.appDir,
1968
+ did: b.meta.did,
1969
+ notification: this.notification,
1970
+ context,
1971
+ });
1972
+ }
1973
+ return Promise.resolve();
1974
+ };
1975
+ logger.info('start migration');
1976
+
1977
+ try {
1978
+ await forEachBlocklet(blocklet, runMigration, { parallel: true });
1979
+ } catch (error) {
1980
+ logger.error('Failed to migrate blocklet', { did, error });
1981
+ throw error;
1982
+ }
1983
+ logger.info('end migration');
1984
+
1963
1985
  logger.info('updated blocklet for upgrading', { did, version, source, name });
1964
1986
 
1965
1987
  // start new process
@@ -1,10 +1,68 @@
1
1
  /* eslint-disable no-await-in-loop */
2
+ const childProcess = require('child_process');
2
3
  const fs = require('fs-extra');
3
4
  const path = require('path');
4
5
  const semver = require('semver');
6
+
5
7
  const { getMigrationScripts: getScripts } = require('../migrations');
8
+ const { getSafeEnv } = require('../util');
9
+ const { name } = require('../../package.json');
10
+ const logger = require('@abtnode/logger')(`${name}:blocklet:migration`); // eslint-disable-line
11
+
12
+ const _runScript = ({ appDir, env, migrationScript, progress = false }) => {
13
+ const safeEnv = getSafeEnv(env);
14
+
15
+ const child = childProcess.exec(`node ${migrationScript}`, {
16
+ cwd: appDir,
17
+ env: safeEnv,
18
+ stdio: 'inherit',
19
+ });
20
+ let hasUnhandledRejection = false;
21
+
22
+ if (progress) {
23
+ child.stdout.pipe(process.stdout);
24
+ child.stderr.pipe(process.stderr);
25
+ }
26
+
27
+ return new Promise((resolve, reject) => {
28
+ const errorMessages = [];
29
+
30
+ child.stderr.on('data', (err) => {
31
+ // Check if has unhandledRejection in childProcess
32
+ // https://stackoverflow.com/questions/32784649/gracefully-handle-errors-in-child-processes-in-nodejs
33
+ if (err.includes('UnhandledPromiseRejectionWarning')) {
34
+ hasUnhandledRejection = true;
35
+ }
36
+ errorMessages.push(err);
37
+ });
38
+
39
+ child.on('exit', (code) => {
40
+ if (errorMessages.length > 0) {
41
+ if (code !== 0 || hasUnhandledRejection) {
42
+ return reject(new Error(errorMessages.join('\r\n')));
43
+ }
44
+
45
+ if (!progress) {
46
+ errorMessages.forEach((message) => process.stderr.write(message));
47
+ }
48
+ }
6
49
 
7
- async function runScripts({ CONFIG_FILE, DB_DIR, BAK_DIR, scriptsDir, printInfo, printSuccess, printError }) {
50
+ return resolve();
51
+ });
52
+ });
53
+ };
54
+
55
+ async function runScripts({
56
+ dbDir,
57
+ backupDir,
58
+ scriptsDir,
59
+ printInfo,
60
+ printSuccess,
61
+ printError,
62
+ appDir,
63
+ env,
64
+ oldVersion,
65
+ }) {
8
66
  let scripts = [];
9
67
  try {
10
68
  scripts = await getScripts(scriptsDir);
@@ -14,16 +72,7 @@ async function runScripts({ CONFIG_FILE, DB_DIR, BAK_DIR, scriptsDir, printInfo,
14
72
  throw err;
15
73
  }
16
74
 
17
- let config;
18
- try {
19
- config = fs.readJsonSync(CONFIG_FILE);
20
- } catch {
21
- config = {
22
- version: '0.0.0',
23
- };
24
- }
25
-
26
- const pendingScripts = scripts.filter((x) => semver.gt(x.version, config.version));
75
+ const pendingScripts = scripts.filter((x) => semver.gt(x.version, oldVersion));
27
76
 
28
77
  if (!pendingScripts.length) {
29
78
  return true;
@@ -31,25 +80,23 @@ async function runScripts({ CONFIG_FILE, DB_DIR, BAK_DIR, scriptsDir, printInfo,
31
80
 
32
81
  printInfo('pending scripts', pendingScripts);
33
82
  try {
34
- await doBackup({ DB_DIR, BAK_DIR, printInfo, printSuccess, printError });
83
+ await doBackup({ dbDir, backupDir, printInfo, printSuccess, printError });
35
84
  } catch (err) {
36
85
  printError(`Failed to backup state db due to ${err.message}, abort!`);
37
86
  throw err;
38
87
  }
39
88
 
40
89
  for (let i = 0; i < pendingScripts.length; i++) {
41
- const { path: scriptPath } = pendingScripts[i];
90
+ const { script: scriptPath } = pendingScripts[i];
42
91
  try {
43
- // eslint-disable-next-line
44
- const scriptFn = require(path.join(folder, scriptPath));
45
92
  printInfo(`Migration script started: ${scriptPath}`);
46
- await scriptFn({ printInfo, printSuccess, printError });
93
+ await _runScript({ appDir, env, migrationScript: path.join(scriptsDir, scriptPath) });
47
94
  printInfo(`Migration script executed: ${scriptPath}`);
48
95
  } catch (migrationErr) {
49
96
  printError(`Failed to execute migration script: ${scriptPath}, error: ${migrationErr.message}`);
50
97
 
51
98
  try {
52
- await doRestore({ DB_DIR, BAK_DIR, printInfo, printSuccess, printError });
99
+ await doRestore({ dbDir, backupDir, printInfo, printSuccess, printError });
53
100
  } catch (restoreErr) {
54
101
  printError(`Failed to restore state db due to: ${restoreErr.message}`);
55
102
  }
@@ -58,51 +105,52 @@ async function runScripts({ CONFIG_FILE, DB_DIR, BAK_DIR, scriptsDir, printInfo,
58
105
  }
59
106
  }
60
107
 
61
- fs.writeJsonSync(CONFIG_FILE, {
62
- version: pendingScripts[pendingScripts.length - 1].version,
63
- });
64
-
65
108
  return true;
66
109
  }
67
110
 
68
- async function doBackup({ DB_DIR, BAK_DIR, printInfo, printSuccess }) {
111
+ async function doBackup({ dbDir, backupDir, printInfo, printSuccess }) {
69
112
  printInfo('Backing up before migration...');
70
- fs.emptyDirSync(BAK_DIR);
71
- fs.copySync(DB_DIR, BAK_DIR);
113
+ fs.emptyDirSync(backupDir);
114
+ fs.copySync(dbDir, backupDir);
72
115
  printSuccess('Backup success');
73
116
  }
74
117
 
75
- async function doRestore({ DB_DIR, BAK_DIR, printInfo, printSuccess }) {
118
+ async function doRestore({ dbDir, backupDir, printInfo, printSuccess }) {
76
119
  printInfo('Restoring when migration failed...');
77
- fs.emptyDirSync(DB_DIR);
78
- fs.copySync(BAK_DIR, DB_DIR);
120
+ fs.emptyDirSync(dbDir);
121
+ fs.copySync(backupDir, dbDir);
79
122
  printSuccess('Restore succeed');
80
123
  }
81
124
 
82
125
  module.exports = async ({
83
- blocklet,
84
- printInfo = console.info, // eslint-disable-line no-console
85
- printSuccess = console.info, // eslint-disable-line no-console
86
- printError = console.error,
126
+ appDir,
127
+ env,
128
+ oldVersion,
129
+ newVersion,
130
+ printInfo = logger.info,
131
+ printSuccess = logger.info,
132
+ printError = logger.error,
87
133
  }) => {
88
- const { version } = blocklet.meta;
89
- const baseDir = blocklet.environmentObj.BLOCKLET_DATA_DIR;
90
- const appDir = blocklet.environmentObj.BLOCKLET_APP_DIR;
134
+ const baseDir = env.BLOCKLET_DATA_DIR;
91
135
 
92
136
  const scriptsDir = path.join(appDir, 'migration');
93
- const DB_DIR = path.join(baseDir, 'db');
94
- const BAK_DIR = path.join(baseDir, 'db-bak');
95
- const CONFIG_FILE = path.join(baseDir, 'config.json');
137
+ const dbDir = path.join(baseDir, 'db');
138
+ const backupDir = path.join(baseDir, 'db-bak');
96
139
 
97
140
  fs.ensureDirSync(scriptsDir);
98
- fs.ensureDirSync(DB_DIR);
99
- fs.ensureDirSync(BAK_DIR);
100
- fs.ensureFileSync(CONFIG_FILE);
101
-
102
- // 如果是首次安装,没有 CONFIG_FILE
103
- if (!fs.existsSync(CONFIG_FILE)) {
104
- fs.writeJsonSync(CONFIG_FILE, { version });
105
- return;
106
- }
107
- await runScripts({ DB_DIR, BAK_DIR, CONFIG_FILE, scriptsDir, printError, printInfo, printSuccess });
141
+ fs.ensureDirSync(dbDir);
142
+ fs.ensureDirSync(backupDir);
143
+
144
+ await runScripts({
145
+ dbDir,
146
+ backupDir,
147
+ scriptsDir,
148
+ printError,
149
+ printInfo,
150
+ printSuccess,
151
+ appDir,
152
+ env,
153
+ oldVersion,
154
+ newVersion,
155
+ });
108
156
  };
@@ -3,7 +3,7 @@ const { BlockletGroup } = require('@blocklet/meta/lib/constants');
3
3
  const joinURL = require('url-join');
4
4
  const get = require('lodash/get');
5
5
  const pick = require('lodash/pick');
6
- const { BLOCKLET_REGISTRY_API_PREFIX } = require('@abtnode/constant');
6
+ const { BLOCKLET_STORE_API_PREFIX } = require('@abtnode/constant');
7
7
 
8
8
  const { name } = require('../../package.json');
9
9
 
@@ -63,7 +63,7 @@ class BlockletRegistry {
63
63
 
64
64
  const url = joinURL(
65
65
  registryUrl || defaultRegistryUrl,
66
- BLOCKLET_REGISTRY_API_PREFIX,
66
+ BLOCKLET_STORE_API_PREFIX,
67
67
  `/blocklets/${did}/blocklet.json?__t__=${Date.now()}`
68
68
  );
69
69
 
@@ -109,7 +109,7 @@ class BlockletRegistry {
109
109
 
110
110
  async refreshBlocklets(context) {
111
111
  const registryUrl = await states.node.getBlockletRegistry();
112
- const url = joinURL(registryUrl, BLOCKLET_REGISTRY_API_PREFIX, '/blocklets.json');
112
+ const url = joinURL(registryUrl, BLOCKLET_STORE_API_PREFIX, '/blocklets.json');
113
113
  try {
114
114
  let res = await request.get(url, {
115
115
  validateStatus: (status) => (status >= 200 && status < 300) || status === 304,
@@ -153,7 +153,7 @@ class BlockletRegistry {
153
153
  }
154
154
 
155
155
  BlockletRegistry.validateRegistryURL = async (registry) => {
156
- const url = joinURL(registry, BLOCKLET_REGISTRY_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
156
+ const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
157
157
  try {
158
158
  const res = await request.get(url);
159
159
  if (Array.isArray(res.data)) {
@@ -170,7 +170,7 @@ BlockletRegistry.validateRegistryURL = async (registry) => {
170
170
 
171
171
  BlockletRegistry.getRegistryMeta = async (registry) => {
172
172
  try {
173
- const url = joinURL(registry, BLOCKLET_REGISTRY_API_PREFIX, `/registry.json?__t__=${Date.now()}`);
173
+ const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/registry.json?__t__=${Date.now()}`);
174
174
  const { data } = await request.get(url);
175
175
 
176
176
  if (!data) {
package/lib/event.js CHANGED
@@ -128,8 +128,8 @@ module.exports = ({
128
128
  const handleCLIEvent = (eventName) => {
129
129
  const [, status] = eventName.split('.');
130
130
  onEvent(eventName, {
131
- title: `ABT Node ${status}`,
132
- description: `ABT Node is ${status} successfully`,
131
+ title: `Blocklet Server ${status}`,
132
+ description: `Blocklet Server is ${status} successfully`,
133
133
  entityType: 'node',
134
134
  status: 'success',
135
135
  });
@@ -0,0 +1,184 @@
1
+ /* eslint-disable no-continue */
2
+ /* eslint-disable no-await-in-loop */
3
+ const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
4
+
5
+ const findBlocklet = (site, blocklets) => {
6
+ // prefix = /
7
+ const rootRule = site.rules.find((x) => x.from.pathPrefix === '/' && x.to.type === 'blocklet');
8
+ if (rootRule) {
9
+ const blocklet = blocklets.find((x) => x.meta.did === rootRule.to.did);
10
+ if (blocklet) {
11
+ return blocklet;
12
+ }
13
+ }
14
+
15
+ for (const rule of site.rules) {
16
+ if (rule.to.type === 'blocklet') {
17
+ const blocklet = blocklets.find((x) => x.meta.did === rule.to.did);
18
+ if (blocklet) {
19
+ return blocklet;
20
+ }
21
+ }
22
+ }
23
+
24
+ return null;
25
+ };
26
+
27
+ const mergeSite = (newSite, oldSite) => {
28
+ const { domain, domainAliases = [], isProtected, rules, corsAllowedOrigins } = oldSite;
29
+
30
+ // merge domain
31
+ const domains = [
32
+ {
33
+ value: domain,
34
+ isProtected,
35
+ },
36
+ ...(domainAliases || []).map((x) => (typeof x === 'string' ? { value: x, isProtected: false } : x)),
37
+ ];
38
+ domains.forEach((x) => {
39
+ if (!newSite.domainAliases.some((y) => y.value === x.value)) {
40
+ newSite.domainAliases.push(x);
41
+ }
42
+ });
43
+
44
+ // merge cors
45
+ (corsAllowedOrigins || []).forEach((x) => {
46
+ if (!newSite.corsAllowedOrigins.includes(x)) {
47
+ newSite.corsAllowedOrigins.push(x);
48
+ }
49
+ });
50
+
51
+ // merge rules
52
+ (rules || []).forEach((x) => {
53
+ if (!newSite.rules.some((y) => normalizePathPrefix(y.from.pathPrefix) === normalizePathPrefix(x.from.pathPrefix))) {
54
+ newSite.rules.push(x);
55
+ }
56
+ });
57
+ };
58
+
59
+ module.exports = async ({ states, node, printInfo }) => {
60
+ printInfo('Migrate site to 2.0 version for router...\n');
61
+
62
+ const sites = await states.site.getSites();
63
+ const blocklets = await states.blocklet.getBlocklets();
64
+ printInfo(`Find blocklets: ${blocklets.length}`);
65
+
66
+ // blocklet site which ends with 888-888-888-888.ip.abtnet.io
67
+ const blockletSystemSites = [];
68
+
69
+ // custom site added by user
70
+ const customSites = [];
71
+
72
+ sites.forEach((site) => {
73
+ const { domain } = site;
74
+
75
+ // filter 3 default basic site
76
+ if (domain === '' || domain === '127.0.0.1' || domain === '*') {
77
+ return;
78
+ }
79
+
80
+ const blocklet = findBlocklet(site, blocklets);
81
+
82
+ if (domain.endsWith('888-888-888-888.ip.abtnet.io')) {
83
+ blockletSystemSites.push({
84
+ site,
85
+ blocklet,
86
+ });
87
+ } else {
88
+ customSites.push({
89
+ site,
90
+ blocklet,
91
+ });
92
+ }
93
+ });
94
+
95
+ printInfo(
96
+ `Find sites: ${sites.length}; blockletSystemSites: ${blockletSystemSites.length}; customSites: ${customSites.length}\n`
97
+ );
98
+ printInfo(`blockletSystemSites: ${blockletSystemSites.map((x) => x.site.domain).join(', ')}\n`);
99
+ printInfo(`customSites: ${customSites.map((x) => x.site.domain).join(', ')}\n`);
100
+
101
+ // generate new blocklet site for every installed blocklet
102
+ const newBlockletSites = {}; // <blockletDid>: <site>
103
+ for (const blocklet of blocklets) {
104
+ const domain = `${blocklet.meta.did}.blocklet-domain-group`;
105
+ newBlockletSites[blocklet.meta.did] = {
106
+ domain,
107
+ domainAliases: [],
108
+ isProtected: true,
109
+ rules: [],
110
+ corsAllowedOrigins: [],
111
+ };
112
+ }
113
+ printInfo(
114
+ `newBlockletSites: ${Object.values(newBlockletSites)
115
+ .map((x) => x.domain)
116
+ .join(', ')}\n`
117
+ );
118
+
119
+ printInfo('\n');
120
+ printInfo('Start merge blockletSystemSites to new sites');
121
+ for (const { site: oldSite, blocklet } of blockletSystemSites) {
122
+ // merge blockletSystemSite to new blocklet site
123
+ if (blocklet) {
124
+ const newSite = newBlockletSites[blocklet.meta.did];
125
+ mergeSite(newSite, oldSite);
126
+ printInfo(`Merge site from ${oldSite.domain} to ${newSite.domain}`);
127
+ }
128
+ }
129
+
130
+ printInfo('\n');
131
+ printInfo('Start merge customSites to new sites');
132
+ for (const { site: oldSite, blocklet } of customSites) {
133
+ // reserve custom site which not belong to any blocklet
134
+ if (!blocklet) {
135
+ printInfo(`Skip merge custom site: ${oldSite.domain}`);
136
+ oldSite.skip = true;
137
+ continue;
138
+ }
139
+ // merge custom site to new blocklet site
140
+ const newSite = newBlockletSites[blocklet.meta.did];
141
+ mergeSite(newSite, oldSite);
142
+ printInfo(`Merge site from ${oldSite.domain} to ${newSite.domain}`);
143
+ }
144
+
145
+ printInfo('\n');
146
+ printInfo('Start delete blockletSystemSites from db');
147
+ for (const { site: oldSite } of blockletSystemSites) {
148
+ if (oldSite.skip) {
149
+ printInfo(`Skip delete site from db: ${oldSite.domain}`);
150
+ continue;
151
+ }
152
+ // delete each blockletSystemSite
153
+ await states.site.remove({ _id: oldSite.id });
154
+ printInfo(`Delete site from db: ${oldSite.domain}`);
155
+ }
156
+
157
+ printInfo('\n');
158
+ printInfo('Start delete customSites from db');
159
+ for (const { site: oldSite } of customSites) {
160
+ if (oldSite.skip) {
161
+ printInfo(`Skip delete site from db: ${oldSite.domain}`);
162
+ continue;
163
+ }
164
+ // delete custom site which bind to a blocklet
165
+ await states.site.remove({ _id: oldSite.id });
166
+ printInfo(`Delete site from db: ${oldSite.domain}`);
167
+ }
168
+
169
+ // add new blocklet site to db
170
+ printInfo('\n');
171
+ printInfo('Start add new sites to db');
172
+ for (const site of Object.values(newBlockletSites)) {
173
+ await states.site.add(site);
174
+ printInfo(`Add site to db: ${site.domain}`);
175
+ }
176
+
177
+ await node.takeRoutingSnapshot({
178
+ message: 'Migrate site to 2.0 version for router',
179
+ dryRun: false,
180
+ handleRouting: false,
181
+ });
182
+ printInfo('\n');
183
+ printInfo('Take routing snapshot');
184
+ };