@abtnode/core 1.16.37 → 1.16.38-beta-20250108-235929-17742885

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.
@@ -25,6 +25,8 @@ const { joinURL } = require('ufo');
25
25
  const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
26
26
  const { getDidDomainForBlocklet } = require('@abtnode/util/lib/get-domain-for-blocklet');
27
27
  const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
28
+ const promiseSpawn = require('@abtnode/util/lib/promise-spawn');
29
+
28
30
  const {
29
31
  BLOCKLET_INSTALL_TYPE,
30
32
  APP_STRUCT_VERSION,
@@ -2646,6 +2648,17 @@ class DiskBlockletManager extends BaseBlockletManager {
2646
2648
  const { meta } = blocklet;
2647
2649
  const { name, did, version } = meta;
2648
2650
 
2651
+ const nodeInfo = await states.node.read();
2652
+ if (checkDockerRunHistory(nodeInfo)) {
2653
+ const newBlocklet = await this.getBlocklet(did);
2654
+ // pull docker image
2655
+ await forEachComponentV2(newBlocklet, async (component) => {
2656
+ if (component?.meta?.docker?.image) {
2657
+ await promiseSpawn(`docker pull ${component?.meta?.docker?.image}`, {}, { timeout: 120 * 1000, retry: 0 });
2658
+ }
2659
+ });
2660
+ }
2661
+
2649
2662
  // check status
2650
2663
  if (!skipCheckStatusBeforeDownload) {
2651
2664
  try {
@@ -2707,7 +2720,6 @@ class DiskBlockletManager extends BaseBlockletManager {
2707
2720
  }
2708
2721
  } catch (err) {
2709
2722
  if (err.message.includes('EACCES:')) {
2710
- const nodeInfo = await states.node.read();
2711
2723
  if (checkDockerRunHistory(nodeInfo)) {
2712
2724
  try {
2713
2725
  await dockerExecChown(`${did}-${blocklet.meta.name}-download`, [
@@ -4374,6 +4386,15 @@ class DiskBlockletManager extends BaseBlockletManager {
4374
4386
  if (!isExternal) {
4375
4387
  // 将 notification 信息保存在 server 中
4376
4388
  const serverReceivers = await this.getReceiverDids(this.teamManager.nodeDid);
4389
+ // 需要处理 action, staging 环境中 path 要带有 admin/
4390
+ const { action } = notification;
4391
+ let actionPath = action;
4392
+ if (actionPath) {
4393
+ const nodeInfo = await states.node.read();
4394
+ actionPath =
4395
+ process.env.NODE_ENV === 'production' ? joinURL(nodeInfo.routing.adminPath, actionPath) : actionPath;
4396
+ }
4397
+
4377
4398
  await states.notification.create({
4378
4399
  ...notification,
4379
4400
  blockletUrl,
@@ -7964,6 +7964,7 @@ function useColors() {
7964
7964
 
7965
7965
  // Is webkit? http://stackoverflow.com/a/16459606/376773
7966
7966
  // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
7967
+ // eslint-disable-next-line no-return-assign
7967
7968
  return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
7968
7969
  // Is firebug? http://stackoverflow.com/a/398120/376773
7969
7970
  (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
@@ -8279,24 +8280,62 @@ function setup(env) {
8279
8280
  createDebug.names = [];
8280
8281
  createDebug.skips = [];
8281
8282
 
8282
- let i;
8283
- const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
8284
- const len = split.length;
8283
+ const split = (typeof namespaces === 'string' ? namespaces : '')
8284
+ .trim()
8285
+ .replace(' ', ',')
8286
+ .split(',')
8287
+ .filter(Boolean);
8285
8288
 
8286
- for (i = 0; i < len; i++) {
8287
- if (!split[i]) {
8288
- // ignore empty strings
8289
- continue;
8289
+ for (const ns of split) {
8290
+ if (ns[0] === '-') {
8291
+ createDebug.skips.push(ns.slice(1));
8292
+ } else {
8293
+ createDebug.names.push(ns);
8290
8294
  }
8295
+ }
8296
+ }
8291
8297
 
8292
- namespaces = split[i].replace(/\*/g, '.*?');
8293
-
8294
- if (namespaces[0] === '-') {
8295
- createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));
8298
+ /**
8299
+ * Checks if the given string matches a namespace template, honoring
8300
+ * asterisks as wildcards.
8301
+ *
8302
+ * @param {String} search
8303
+ * @param {String} template
8304
+ * @return {Boolean}
8305
+ */
8306
+ function matchesTemplate(search, template) {
8307
+ let searchIndex = 0;
8308
+ let templateIndex = 0;
8309
+ let starIndex = -1;
8310
+ let matchIndex = 0;
8311
+
8312
+ while (searchIndex < search.length) {
8313
+ if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) {
8314
+ // Match character or proceed with wildcard
8315
+ if (template[templateIndex] === '*') {
8316
+ starIndex = templateIndex;
8317
+ matchIndex = searchIndex;
8318
+ templateIndex++; // Skip the '*'
8319
+ } else {
8320
+ searchIndex++;
8321
+ templateIndex++;
8322
+ }
8323
+ } else if (starIndex !== -1) { // eslint-disable-line no-negated-condition
8324
+ // Backtrack to the last '*' and try to match more characters
8325
+ templateIndex = starIndex + 1;
8326
+ matchIndex++;
8327
+ searchIndex = matchIndex;
8296
8328
  } else {
8297
- createDebug.names.push(new RegExp('^' + namespaces + '$'));
8329
+ return false; // No match
8298
8330
  }
8299
8331
  }
8332
+
8333
+ // Handle trailing '*' in template
8334
+ while (templateIndex < template.length && template[templateIndex] === '*') {
8335
+ templateIndex++;
8336
+ }
8337
+
8338
+ return templateIndex === template.length;
8300
8339
  }
8301
8340
 
8302
8341
  /**
@@ -8307,8 +8346,8 @@ function setup(env) {
8307
8346
  */
8308
8347
  function disable() {
8309
8348
  const namespaces = [
8310
- ...createDebug.names.map(toNamespace),
8311
- ...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)
8349
+ ...createDebug.names,
8350
+ ...createDebug.skips.map(namespace => '-' + namespace)
8312
8351
  ].join(',');
8313
8352
  createDebug.enable('');
8314
8353
  return namespaces;
@@ -8322,21 +8361,14 @@ function setup(env) {
8322
8361
  * @api public
8323
8362
  */
8324
8363
  function enabled(name) {
8325
- if (name[name.length - 1] === '*') {
8326
- return true;
8327
- }
8328
-
8329
- let i;
8330
- let len;
8331
-
8332
- for (i = 0, len = createDebug.skips.length; i < len; i++) {
8333
- if (createDebug.skips[i].test(name)) {
8364
+ for (const skip of createDebug.skips) {
8365
+ if (matchesTemplate(name, skip)) {
8334
8366
  return false;
8335
8367
  }
8336
8368
  }
8337
8369
 
8338
- for (i = 0, len = createDebug.names.length; i < len; i++) {
8339
- if (createDebug.names[i].test(name)) {
8370
+ for (const ns of createDebug.names) {
8371
+ if (matchesTemplate(name, ns)) {
8340
8372
  return true;
8341
8373
  }
8342
8374
  }
@@ -8344,19 +8376,6 @@ function setup(env) {
8344
8376
  return false;
8345
8377
  }
8346
8378
 
8347
- /**
8348
- * Convert regexp to namespace
8349
- *
8350
- * @param {RegExp} regxep
8351
- * @return {String} namespace
8352
- * @api private
8353
- */
8354
- function toNamespace(regexp) {
8355
- return regexp.toString()
8356
- .substring(2, regexp.toString().length - 2)
8357
- .replace(/\.\*\?$/, '*');
8358
- }
8359
-
8360
8379
  /**
8361
8380
  * Coerce `val`.
8362
8381
  *
@@ -38553,7 +38572,7 @@ module.exports = require("zlib");
38553
38572
  /***/ ((module) => {
38554
38573
 
38555
38574
  "use strict";
38556
- module.exports = JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.36","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib","test":"node tools/jest.js","coverage":"npm run test -- --coverage"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.16.36","@abtnode/auth":"1.16.36","@abtnode/certificate-manager":"1.16.36","@abtnode/constant":"1.16.36","@abtnode/cron":"1.16.36","@abtnode/logger":"1.16.36","@abtnode/models":"1.16.36","@abtnode/queue":"1.16.36","@abtnode/rbac":"1.16.36","@abtnode/router-provider":"1.16.36","@abtnode/static-server":"1.16.36","@abtnode/timemachine":"1.16.36","@abtnode/util":"1.16.36","@arcblock/did":"1.19.2","@arcblock/did-auth":"1.19.2","@arcblock/did-ext":"^1.19.2","@arcblock/did-motif":"^1.1.13","@arcblock/did-util":"1.19.2","@arcblock/event-hub":"1.19.2","@arcblock/jwt":"^1.19.2","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"^1.19.2","@arcblock/vc":"1.19.2","@blocklet/constant":"1.16.36","@blocklet/did-space-js":"^1.0.1","@blocklet/env":"1.16.36","@blocklet/meta":"1.16.36","@blocklet/resolver":"1.16.36","@blocklet/sdk":"1.16.36","@blocklet/store":"1.16.36","@fidm/x509":"^1.2.1","@ocap/mcrypto":"1.19.2","@ocap/util":"1.19.2","@ocap/wallet":"1.19.2","@slack/webhook":"^5.0.4","archiver":"^7.0.1","axios":"^1.7.9","axon":"^2.0.3","chalk":"^4.1.2","cross-spawn":"^7.0.3","deep-diff":"^1.0.2","detect-port":"^1.5.1","envfile":"^7.1.0","escape-string-regexp":"^4.0.0","fast-glob":"^3.3.2","filesize":"^10.1.1","flat":"^5.0.2","fs-extra":"^11.2.0","get-port":"^5.1.1","hasha":"^5.2.2","is-base64":"^1.1.0","is-cidr":"4","is-ip":"3","is-url":"^1.2.4","joi":"17.12.2","joi-extension-semver":"^5.0.0","js-yaml":"^4.1.0","kill-port":"^2.0.1","lodash":"^4.17.21","lru-cache":"^11.0.2","node-stream-zip":"^1.15.0","p-all":"^3.0.0","p-limit":"^3.1.0","p-map":"^4.0.0","p-retry":"^4.6.2","read-last-lines":"^1.8.0","semver":"^7.6.3","sequelize":"^6.35.0","shelljs":"^0.8.5","ssri":"^8.0.1","stream-throttle":"^0.1.3","stream-to-promise":"^3.0.0","systeminformation":"^5.23.3","tail":"^2.2.4","tar":"^6.1.11","transliteration":"^2.3.5","ua-parser-js":"^1.0.2","ufo":"^1.5.3","uuid":"^9.0.1","valid-url":"^1.0.9","which":"^2.0.2","xbytes":"^1.8.0"},"devDependencies":{"expand-tilde":"^2.0.2","express":"^4.18.2","jest":"^29.7.0","unzipper":"^0.10.11"},"gitHead":"e5764f753181ed6a7c615cd4fc6682aacf0cb7cd"}');
38575
+ module.exports = JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.37","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib","test":"node tools/jest.js","coverage":"npm run test -- --coverage"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.16.37","@abtnode/auth":"1.16.37","@abtnode/certificate-manager":"1.16.37","@abtnode/constant":"1.16.37","@abtnode/cron":"1.16.37","@abtnode/logger":"1.16.37","@abtnode/models":"1.16.37","@abtnode/queue":"1.16.37","@abtnode/rbac":"1.16.37","@abtnode/router-provider":"1.16.37","@abtnode/static-server":"1.16.37","@abtnode/timemachine":"1.16.37","@abtnode/util":"1.16.37","@abtnode/docker-utils":"1.16.37","@arcblock/did":"1.19.2","@arcblock/did-auth":"1.19.2","@arcblock/did-ext":"^1.19.2","@arcblock/did-motif":"^1.1.13","@arcblock/did-util":"1.19.2","@arcblock/event-hub":"1.19.2","@arcblock/jwt":"^1.19.2","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"^1.19.2","@arcblock/vc":"1.19.2","@blocklet/constant":"1.16.37","@blocklet/did-space-js":"^1.0.1","@blocklet/env":"1.16.37","@blocklet/meta":"1.16.37","@blocklet/resolver":"1.16.37","@blocklet/sdk":"1.16.37","@blocklet/store":"1.16.37","@fidm/x509":"^1.2.1","@ocap/mcrypto":"1.19.2","@ocap/util":"1.19.2","@ocap/wallet":"1.19.2","@slack/webhook":"^5.0.4","archiver":"^7.0.1","axios":"^1.7.9","axon":"^2.0.3","chalk":"^4.1.2","cross-spawn":"^7.0.3","deep-diff":"^1.0.2","detect-port":"^1.5.1","envfile":"^7.1.0","escape-string-regexp":"^4.0.0","fast-glob":"^3.3.2","filesize":"^10.1.1","flat":"^5.0.2","fs-extra":"^11.2.0","get-port":"^5.1.1","hasha":"^5.2.2","is-base64":"^1.1.0","is-cidr":"4","is-ip":"3","is-url":"^1.2.4","joi":"17.12.2","joi-extension-semver":"^5.0.0","js-yaml":"^4.1.0","kill-port":"^2.0.1","lodash":"^4.17.21","lru-cache":"^11.0.2","node-stream-zip":"^1.15.0","p-all":"^3.0.0","p-limit":"^3.1.0","p-map":"^4.0.0","p-retry":"^4.6.2","read-last-lines":"^1.8.0","semver":"^7.6.3","sequelize":"^6.35.0","shelljs":"^0.8.5","ssri":"^8.0.1","stream-throttle":"^0.1.3","stream-to-promise":"^3.0.0","systeminformation":"^5.23.3","tail":"^2.2.4","tar":"^6.1.11","transliteration":"^2.3.5","ua-parser-js":"^1.0.2","ufo":"^1.5.3","uuid":"^9.0.1","valid-url":"^1.0.9","which":"^2.0.2","xbytes":"^1.8.0"},"devDependencies":{"expand-tilde":"^2.0.2","express":"^4.18.2","jest":"^29.7.0","unzipper":"^0.10.11"},"gitHead":"e5764f753181ed6a7c615cd4fc6682aacf0cb7cd"}');
38557
38576
 
38558
38577
  /***/ }),
38559
38578
 
@@ -11,6 +11,7 @@ const { update: updateMetaFile, read: readMetaFile } = require('@blocklet/meta/l
11
11
  const { createRelease: createBlockletRelease } = require('@abtnode/util/lib/create-blocklet-release');
12
12
  const { hasStartEngine } = require('@blocklet/meta/lib/util');
13
13
  const { convertToMoniker } = require('@abtnode/util/lib/transfer-to-moniker');
14
+ const { parseDockerArgsToSchema, dockerParseEnvironments, dockerParsePublishPorts } = require('@abtnode/docker-utils');
14
15
 
15
16
  const logger = require('@abtnode/logger')('blocklet-studio-pack');
16
17
 
@@ -42,6 +43,8 @@ const createPackRelease = async ({
42
43
  blockletCommunity,
43
44
  blockletRepository,
44
45
  blockletVideos,
46
+ blockletDocker,
47
+ contentType,
45
48
  manager,
46
49
  status,
47
50
  }) => {
@@ -67,6 +70,8 @@ const createPackRelease = async ({
67
70
  blockletCommunity,
68
71
  blockletRepository,
69
72
  blockletVideos,
73
+ blockletDocker,
74
+ contentType,
70
75
  };
71
76
 
72
77
  const releaseSchema = createReleaseSchema(status);
@@ -103,7 +108,7 @@ const createPackRelease = async ({
103
108
 
104
109
  const projectDir = path.join(blocklet.env.dataDir, PROJECT.DIR, `${projectId}`);
105
110
 
106
- if (status !== PROJECT.RELEASE_STATUS.draft) {
111
+ if (status !== PROJECT.RELEASE_STATUS.draft && !blockletDocker?.dockerImage) {
107
112
  await checkResourceExists(projectDir, action, releaseId);
108
113
  }
109
114
 
@@ -217,6 +222,15 @@ const createPackRelease = async ({
217
222
  return res;
218
223
  });
219
224
 
225
+ let dockerYml;
226
+ if (blockletDocker?.dockerImage) {
227
+ const { error, value } = parseDockerArgsToSchema(blockletDocker.dockerImage, blockletDocker.dockerArgs);
228
+ if (error) {
229
+ throw new Error(`Docker args is invalid: ${error.message}`);
230
+ }
231
+ dockerYml = value;
232
+ }
233
+
220
234
  // create blocklet.yml
221
235
  const meta = {
222
236
  did: blockletDid,
@@ -239,6 +253,21 @@ const createPackRelease = async ({
239
253
  videos: blockletVideos || [],
240
254
  };
241
255
 
256
+ if (blockletDocker?.dockerImage && dockerYml) {
257
+ meta.group = 'dapp';
258
+ meta.docker = dockerYml;
259
+ meta.environments = dockerParseEnvironments(blockletDocker.dockerEnvs, true);
260
+ if (!meta.interfaces) {
261
+ meta.interfaces = [];
262
+ }
263
+ meta.timeout = {
264
+ start: 120,
265
+ script: 120,
266
+ };
267
+
268
+ meta.interfaces.push(...dockerParsePublishPorts(blockletDocker.dockerImage, blockletDocker.dockerArgs));
269
+ }
270
+
242
271
  // merge extended blocklet.yml
243
272
  const extendedMetaFile = getExtendedMetaFile({ app: blocklet, projectId, releaseId });
244
273
  if (fs.existsSync(extendedMetaFile)) {
@@ -8,7 +8,6 @@ const { createRelease: createBlockletRelease } = require('@abtnode/util/lib/crea
8
8
  const { titleSchema } = require('@blocklet/meta/lib/schema');
9
9
  const { validateNewDid } = require('@blocklet/meta/lib/name');
10
10
  const { convertToMoniker } = require('@abtnode/util/lib/transfer-to-moniker');
11
-
12
11
  const logger = require('@abtnode/logger')('blocklet-studio');
13
12
 
14
13
  const { createReleaseSchema } = require('../../validators/project');
@@ -184,6 +183,8 @@ const createRelease = async ({
184
183
  uploadedResource,
185
184
  blockletComponents,
186
185
  blockletResourceType,
186
+ blockletDocker,
187
+ contentType,
187
188
  note,
188
189
  manager,
189
190
  status,
@@ -214,6 +215,8 @@ const createRelease = async ({
214
215
  blockletVideos,
215
216
  uploadedResource,
216
217
  blockletResourceType,
218
+ blockletDocker,
219
+ contentType,
217
220
  note,
218
221
  };
219
222
 
@@ -228,7 +231,7 @@ const createRelease = async ({
228
231
  throw new Error('project not found');
229
232
  }
230
233
 
231
- const isPack = blockletComponents?.length > 0;
234
+ const isPack = blockletComponents?.length > 0 || !!blockletDocker?.dockerImage;
232
235
 
233
236
  if (isPack) {
234
237
  return createPackRelease({
@@ -248,6 +251,8 @@ const createRelease = async ({
248
251
  blockletScreenshots,
249
252
  blockletComponents,
250
253
  blockletResourceType,
254
+ blockletDocker,
255
+ contentType,
251
256
  note,
252
257
  manager,
253
258
  status,
@@ -35,6 +35,8 @@ class Release extends BaseState {
35
35
  'blockletSupport',
36
36
  'blockletCommunity',
37
37
  'blockletRepository',
38
+ 'blockletDocker',
39
+ 'contentType',
38
40
  'publishedStoreIds',
39
41
  'blockletComponents',
40
42
  'uploadedResource',
@@ -714,6 +714,7 @@ const startBlockletProcess = async (
714
714
  ports: b.ports,
715
715
  onlyStart,
716
716
  dockerNetworkName,
717
+ rootBlocklet: blocklet,
717
718
  });
718
719
 
719
720
  await pm2.startAsync(nextOptions);
@@ -1,9 +1,10 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs');
3
3
  const md5 = require('@abtnode/util/lib/md5');
4
+ const { dockerArgsToCamelCase } = require('@abtnode/docker-utils');
4
5
  const debianDockerfile = require('./debian-dockerfile');
5
6
 
6
- function getBlockletCustomDockerfile({ appDir, dataDir, meta, ports }) {
7
+ function getBlockletCustomDockerfile({ appDir, dataDir, meta, ports, env }) {
7
8
  const dockerMeta = meta.docker || {};
8
9
  if (!dockerMeta.image && !dockerMeta.dockerfile) {
9
10
  return debianDockerfile;
@@ -46,7 +47,7 @@ function getBlockletCustomDockerfile({ appDir, dataDir, meta, ports }) {
46
47
  }
47
48
  }
48
49
 
49
- const volumes = (dockerMeta.volumes || [])
50
+ const volumes = (dockerMeta.volume || [])
50
51
  .map((volume) => {
51
52
  const [a, b] = volume.split(':');
52
53
  const hostDir = a.replace('$BLOCKLET_DATA_DIR', dataDir).replace('$BLOCKLET_APP_DIR', appDir);
@@ -75,6 +76,31 @@ function getBlockletCustomDockerfile({ appDir, dataDir, meta, ports }) {
75
76
  throw new Error('Detected forbidden volume mount (-v) in docker args');
76
77
  }
77
78
 
79
+ const otherRunParams = {
80
+ '--attach': dockerMeta.attach || '',
81
+ '--quiet': dockerMeta.quiet || '',
82
+ '--user': dockerMeta.user || '',
83
+ '--workdir': dockerMeta.workdir || '',
84
+ '--cidfile': dockerMeta.cidfile || '',
85
+ '--detach-keys': dockerMeta.detachKeys || '',
86
+ '--disable-content-trust': dockerMeta.disableContentTrust || '',
87
+ '--domainname': dockerMeta.domainname || '',
88
+ '--expose': dockerMeta.expose || '',
89
+ '--ip': dockerMeta.ip || '',
90
+ '--link-local-ip': dockerMeta.linkLocalIp || '',
91
+ '--platform': dockerMeta.platform || '',
92
+ };
93
+ Object.entries(otherRunParams).forEach(([key, value]) => {
94
+ if (value === `$${dockerArgsToCamelCase(key)}`) {
95
+ otherRunParams[key] = env[dockerArgsToCamelCase(key)] || '';
96
+ }
97
+ });
98
+
99
+ const runParamString = Object.entries(otherRunParams)
100
+ .filter(([, value]) => value !== '')
101
+ .map(([key, value]) => `${key} ${value}`)
102
+ .join(' ');
103
+
78
104
  return {
79
105
  dockerfile: file,
80
106
  image: dockerMeta.image || `blocklet_custom_${hash.slice(0, 16)}`,
@@ -86,6 +112,7 @@ function getBlockletCustomDockerfile({ appDir, dataDir, meta, ports }) {
86
112
  args: dockerMeta.command || '',
87
113
  script: dockerMeta.script || dockerMeta.installNodeModules ? '' : 'tail -f /dev/null',
88
114
  installNodeModules: !!dockerMeta.installNodeModules,
115
+ runParamString,
89
116
  };
90
117
  }
91
118
 
@@ -11,16 +11,31 @@ const lockFile = new LockFile(
11
11
  1000 * 60 * 2
12
12
  );
13
13
 
14
- async function dockerExecChown(name, targetDirs) {
15
- const uid = process.getuid();
16
- const gid = process.getgid();
14
+ let safedDataAndBlocklets = false;
15
+
16
+ async function dockerExecChown(name, targetDirs, code = 777) {
17
17
  const { image, baseDir } = debianDockerfile;
18
+
19
+ // 将 data、blocklets、logs 目录设置为 750 权限,防止因为修改子目录权限,其他组可以访问这三个目录的子目录
20
+ if (!safedDataAndBlocklets && process.env.ABT_NODE_DATA_DIR && code !== 750) {
21
+ safedDataAndBlocklets = true;
22
+ await dockerExecChown(
23
+ 'safe-data-blocklets-logs',
24
+ [
25
+ path.join(process.env.ABT_NODE_DATA_DIR, 'blocklets'),
26
+ path.join(process.env.ABT_NODE_DATA_DIR, 'data'),
27
+ path.join(process.env.ABT_NODE_DATA_DIR, 'logs'),
28
+ ],
29
+ 750
30
+ );
31
+ }
32
+
18
33
  const dirs = targetDirs.filter((dir) => fs.existsSync(dir));
19
34
  const volumes = dirs
20
35
  .map((dir) => `-v ${dir}:${path.join(baseDir, dir.replace(process.env.ABT_NODE_DATA_DIR, ''))}:rw`)
21
36
  .join(' ');
22
37
  const command = dirs
23
- .map((dir) => `chown -R ${uid}:${gid} ${path.join(baseDir, dir.replace(process.env.ABT_NODE_DATA_DIR, ''))}`)
38
+ .map((dir) => `chmod -R ${code} ${path.join(baseDir, dir.replace(process.env.ABT_NODE_DATA_DIR, ''))}`)
24
39
  .join(' && ');
25
40
  const realName = parseDockerName(name, 'docker-exec-chown');
26
41
  const startTime = Date.now();
@@ -8,7 +8,11 @@ const os = require('os');
8
8
  const logger = require('@abtnode/logger')('@abtnode/docker');
9
9
  const { existsSync, writeFileSync } = require('fs-extra');
10
10
  const { NODE_MODES } = require('@abtnode/constant');
11
+ const promiseSpawn = require('@abtnode/util/lib/promise-spawn');
11
12
  const getLocalIPAddress = require('@abtnode/util/lib/get-local-ip-address');
13
+ const { getObjByPath } = require('@abtnode/docker-utils');
14
+ const { isValid: isDid } = require('@arcblock/did');
15
+
12
16
  const parseDockerName = require('./parse-docker-name');
13
17
  const { createDockerImage } = require('./create-docker-image');
14
18
  const checkNeedRunDocker = require('./check-need-run-docker');
@@ -36,6 +40,7 @@ async function parseDockerOptionsFromPm2({
36
40
  nodeInfo,
37
41
  meta,
38
42
  ports,
43
+ rootBlocklet,
39
44
  overrideScript,
40
45
  dockerNamePrefix = 'blocklet',
41
46
  isDockerRm = false,
@@ -54,6 +59,7 @@ async function parseDockerOptionsFromPm2({
54
59
  dataDir: options.env.BLOCKLET_DATA_DIR,
55
60
  meta,
56
61
  ports,
62
+ env: options.env,
57
63
  })),
58
64
  };
59
65
 
@@ -136,7 +142,8 @@ async function parseDockerOptionsFromPm2({
136
142
  dockerInfo.network = `--network ${network}`;
137
143
  Object.keys(ports).forEach((key) => {
138
144
  const port = ports[key];
139
- dockerInfo.network += ` -p 127.0.0.1:${port}:${port}`;
145
+ const inter = meta.interfaces?.find((x) => x.port === key);
146
+ dockerInfo.network += ` -p ${inter?.hostIP || '127.0.0.1'}:${port}:${inter?.containerPort || port}`;
140
147
  });
141
148
  if (nextOptions.env.ALLOW_DOCKER_HOST_ACCESS) {
142
149
  dockerInfo.network += ' --add-host=host.docker.internal:host-gateway';
@@ -144,6 +151,50 @@ async function parseDockerOptionsFromPm2({
144
151
  dockerEnv.BLOCKLET_DOCKER_NETWORK = network;
145
152
  }
146
153
 
154
+ const needGetVars = Object.keys(dockerEnv).filter((key) => {
155
+ const value = dockerEnv[key];
156
+ if (
157
+ typeof value === 'string' &&
158
+ value.startsWith('z') &&
159
+ !key.startsWith('BLOCKLET_') &&
160
+ !key.startsWith('ABT_NODE_')
161
+ ) {
162
+ const [prefix, rest] = value.split('-');
163
+ return isDid(prefix) && rest;
164
+ }
165
+ return false;
166
+ });
167
+
168
+ // 读取 env 和 ports 的值
169
+ // zxxxxxxxx-host 读取 host
170
+ // zxxxxxxxx-env.aaa 读取 env
171
+ // zxxxxxxxx-port 读取 port
172
+ // zxxxxxxxx-ports.xxx 读取其他 port
173
+ for (const envKey of needGetVars) {
174
+ const [did, ...rest] = dockerEnv[envKey].split('-');
175
+ const matchingBlocklet = rootBlocklet.children.find((blocklet) => blocklet.meta && blocklet.meta.did === did);
176
+ if (!matchingBlocklet) {
177
+ continue;
178
+ }
179
+ const envMap = {};
180
+ matchingBlocklet.configs?.forEach((config) => {
181
+ envMap[config.key] = config.value || config.default;
182
+ });
183
+ const value = getObjByPath(
184
+ {
185
+ env: envMap,
186
+ ports: matchingBlocklet.ports,
187
+ host: parseDockerName(`${rootBlocklet.meta.did}-${matchingBlocklet.meta.name}`, dockerNamePrefix),
188
+ port: matchingBlocklet.ports?.BLOCKLET_PORT,
189
+ },
190
+ rest.join('-')
191
+ );
192
+
193
+ if (value) {
194
+ dockerEnv[envKey] = value;
195
+ }
196
+ }
197
+
147
198
  let nodeModulesName = 'node_modules';
148
199
  let installScript = '';
149
200
  const appDir = nextOptions.env.BLOCKLET_APP_DIR;
@@ -178,7 +229,10 @@ async function parseDockerOptionsFromPm2({
178
229
 
179
230
  let runScript = dockerInfo.script || '';
180
231
  if (!runScript && dockerInfo.installNodeModules) {
181
- runScript = /(npm|yarn|pnpm|bun)/g.test(nextOptions.script) ? nextOptions.script : `node ${nextOptions.script}`;
232
+ const maxOldSpaceSize = Math.floor(Number(dockerEnv.BLOCKLET_DOCKER_MEMORY.replace('g', '')) * 0.8 * 1024);
233
+ runScript = /(npm|yarn|pnpm|bun)/g.test(nextOptions.script)
234
+ ? nextOptions.script
235
+ : `node --max-old-space-size=${maxOldSpaceSize} ${nextOptions.script}`;
182
236
  }
183
237
 
184
238
  const runScripts = ['preInstall', '--installScript--', 'postInstall', 'preFlight', 'preStart', '--runScript--']
@@ -306,25 +360,32 @@ async function parseDockerOptionsFromPm2({
306
360
  `mkdir -p ${dockerEnv.BLOCKLET_CACHE_DIR} || :`,
307
361
  'mkdir -p /.local || :',
308
362
  `mkdir -p ${path.join(baseDir, '.npm')} || :`,
309
- `chown ${uid}:${gid} ${baseDir} || :`,
310
- `chown -R ${uid}:${gid} /.local || :`,
311
- `chown -R ${uid}:${gid} ${path.join(baseDir, 'dockerTemp')} || :`,
312
- `chown -R ${uid}:${gid} ${dockerEnv.BLOCKLET_APP_DIR} || :`,
313
- `chown -R ${uid}:${gid} ${dockerEnv.BLOCKLET_DATA_DIR} || :`,
314
- `chown -R ${uid}:${gid} ${dockerEnv.BLOCKLET_LOG_DIR} || :`,
315
- `chown -R ${uid}:${gid} ${dockerEnv.BLOCKLET_CACHE_DIR} || :`,
316
- `chown -R ${uid}:${gid} ${dockerEnv.BLOCKLET_SHARE_DIR} || :`,
317
- `chown -R ${uid}:${gid} ${path.join(dockerEnv.BLOCKLET_APP_DATA_DIR, '.projects')} || :`,
363
+ `chmod 777 ${baseDir} || :`,
364
+ 'chmod 777 /.local || :',
365
+ `chmod 777 ${path.join(baseDir, 'dockerTemp')} || :`,
366
+ `chmod 777 ${dockerEnv.BLOCKLET_APP_DIR} || :`,
367
+ `chmod 777 ${dockerEnv.BLOCKLET_DATA_DIR} || :`,
368
+ `chmod 777 ${dockerEnv.BLOCKLET_LOG_DIR} || :`,
369
+ `chmod 777 ${dockerEnv.BLOCKLET_CACHE_DIR} || :`,
370
+ `chmod 777 ${dockerEnv.BLOCKLET_SHARE_DIR} || :`,
371
+ `chmod 777 ${path.join(dockerEnv.BLOCKLET_APP_DATA_DIR, '.projects')} || :`,
318
372
  ],
319
373
  name,
320
374
  userCommands: [`cd ${dockerEnv.BLOCKLET_APP_DIR}`, ...runScripts],
321
375
  baseDir,
322
376
  scriptDir: dockerTempDir,
323
377
  });
378
+
379
+ let user = '';
380
+ if (meta.docker?.image && !!meta.docker?.useUidGid) {
381
+ user = `-u ${uid}:${gid}`;
382
+ }
383
+
324
384
  nextOptions.script = `
325
385
  docker rm -fv ${name} > /dev/null 2>&1 || true && \
326
386
  ${awaitRestart} \
327
387
  docker run ${isDockerRm ? '--rm' : ''} --name ${name} \
388
+ ${user} \
328
389
  ${volumes} \
329
390
  ${dockerInfo.volumes || ''} \
330
391
  -v ${dockerTempDir}:${path.join(baseDir, 'dockerTemp')}:rw \
@@ -333,6 +394,7 @@ async function parseDockerOptionsFromPm2({
333
394
  --memory="${dockerEnv.BLOCKLET_DOCKER_MEMORY}" \
334
395
  --env-file ${dockerEnvFile} \
335
396
  ${dockerInfo.network} \
397
+ ${dockerInfo.runParamString || ''} \
336
398
  ${dockerInfo.image} ${dockerInfo.command || ''} \
337
399
  ${dockerInfo.runBaseScript ? `sh ${path.join(baseDir, 'dockerTemp', `root-script-${name}.sh`)}` : ''}
338
400
  `;
@@ -372,6 +434,10 @@ async function parseDockerOptionsFromPm2({
372
434
  }
373
435
  }
374
436
 
437
+ if (meta.docker?.image) {
438
+ await promiseSpawn(`docker pull ${meta.docker.image}`, {}, { timeout: 120 * 1000, retry: 0 });
439
+ }
440
+
375
441
  return nextOptions;
376
442
  }
377
443
 
@@ -63,6 +63,39 @@ const createReleaseSchema = (status) =>
63
63
  required: Joi.boolean().allow(null),
64
64
  })
65
65
  ),
66
+ contentType: Joi.string().valid('blocklet', 'upload', 'docker'),
67
+ blockletDocker: Joi.object({
68
+ dockerImage: Joi.string().allow('').default(''),
69
+ dockerArgs: Joi.array()
70
+ .items(
71
+ Joi.object({
72
+ key: Joi.string().required(),
73
+ value: Joi.alternatives()
74
+ .try(Joi.string().required(), Joi.array().items(Joi.string().required()))
75
+ .required(),
76
+ type: Joi.string().valid('docker', 'web').default('docker'),
77
+ port: Joi.string().optional().allow('').default(''),
78
+ prefix: Joi.string().optional().allow('').default(''),
79
+ path: Joi.string().optional().allow('').default('/'),
80
+ name: Joi.string().optional().allow('').default(''),
81
+ protocol: Joi.string().optional().allow('').default(''),
82
+ })
83
+ )
84
+ .default([]),
85
+ dockerEnvs: Joi.array()
86
+ .items(
87
+ Joi.object({
88
+ key: Joi.string().required(),
89
+ value: Joi.string().optional().allow('').default(''),
90
+ description: Joi.string().optional().allow('').default(''),
91
+ secure: Joi.boolean().optional().default(false),
92
+ shared: Joi.boolean().optional().default(false),
93
+ required: Joi.boolean().optional().default(false),
94
+ custom: Joi.string().optional().allow('').default(''),
95
+ })
96
+ )
97
+ .default([]),
98
+ }).optional(),
66
99
  uploadedResource,
67
100
  });
68
101
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.37",
6
+ "version": "1.16.38-beta-20250108-235929-17742885",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,19 +19,20 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "Apache-2.0",
21
21
  "dependencies": {
22
- "@abtnode/analytics": "1.16.37",
23
- "@abtnode/auth": "1.16.37",
24
- "@abtnode/certificate-manager": "1.16.37",
25
- "@abtnode/constant": "1.16.37",
26
- "@abtnode/cron": "1.16.37",
27
- "@abtnode/logger": "1.16.37",
28
- "@abtnode/models": "1.16.37",
29
- "@abtnode/queue": "1.16.37",
30
- "@abtnode/rbac": "1.16.37",
31
- "@abtnode/router-provider": "1.16.37",
32
- "@abtnode/static-server": "1.16.37",
33
- "@abtnode/timemachine": "1.16.37",
34
- "@abtnode/util": "1.16.37",
22
+ "@abtnode/analytics": "1.16.38-beta-20250108-235929-17742885",
23
+ "@abtnode/auth": "1.16.38-beta-20250108-235929-17742885",
24
+ "@abtnode/certificate-manager": "1.16.38-beta-20250108-235929-17742885",
25
+ "@abtnode/constant": "1.16.38-beta-20250108-235929-17742885",
26
+ "@abtnode/cron": "1.16.38-beta-20250108-235929-17742885",
27
+ "@abtnode/docker-utils": "1.16.38-beta-20250108-235929-17742885",
28
+ "@abtnode/logger": "1.16.38-beta-20250108-235929-17742885",
29
+ "@abtnode/models": "1.16.38-beta-20250108-235929-17742885",
30
+ "@abtnode/queue": "1.16.38-beta-20250108-235929-17742885",
31
+ "@abtnode/rbac": "1.16.38-beta-20250108-235929-17742885",
32
+ "@abtnode/router-provider": "1.16.38-beta-20250108-235929-17742885",
33
+ "@abtnode/static-server": "1.16.38-beta-20250108-235929-17742885",
34
+ "@abtnode/timemachine": "1.16.38-beta-20250108-235929-17742885",
35
+ "@abtnode/util": "1.16.38-beta-20250108-235929-17742885",
35
36
  "@arcblock/did": "1.19.2",
36
37
  "@arcblock/did-auth": "1.19.2",
37
38
  "@arcblock/did-ext": "^1.19.2",
@@ -42,13 +43,13 @@
42
43
  "@arcblock/pm2-events": "^0.0.5",
43
44
  "@arcblock/validator": "^1.19.2",
44
45
  "@arcblock/vc": "1.19.2",
45
- "@blocklet/constant": "1.16.37",
46
+ "@blocklet/constant": "1.16.38-beta-20250108-235929-17742885",
46
47
  "@blocklet/did-space-js": "^1.0.1",
47
- "@blocklet/env": "1.16.37",
48
- "@blocklet/meta": "1.16.37",
49
- "@blocklet/resolver": "1.16.37",
50
- "@blocklet/sdk": "1.16.37",
51
- "@blocklet/store": "1.16.37",
48
+ "@blocklet/env": "1.16.38-beta-20250108-235929-17742885",
49
+ "@blocklet/meta": "1.16.38-beta-20250108-235929-17742885",
50
+ "@blocklet/resolver": "1.16.38-beta-20250108-235929-17742885",
51
+ "@blocklet/sdk": "1.16.38-beta-20250108-235929-17742885",
52
+ "@blocklet/store": "1.16.38-beta-20250108-235929-17742885",
52
53
  "@fidm/x509": "^1.2.1",
53
54
  "@ocap/mcrypto": "1.19.2",
54
55
  "@ocap/util": "1.19.2",
@@ -108,5 +109,5 @@
108
109
  "jest": "^29.7.0",
109
110
  "unzipper": "^0.10.11"
110
111
  },
111
- "gitHead": "3e06fd31ca113b7555c3eec22c9087e256b92f44"
112
+ "gitHead": "09d67442bff28543c35824a41c23bea3b75a2a23"
112
113
  }