@nocobase/cli 2.0.0-alpha.9 → 2.0.0-beta.10

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/nocobase.conf.tpl CHANGED
@@ -73,7 +73,10 @@ server {
73
73
  proxy_set_header Upgrade $http_upgrade;
74
74
  proxy_set_header Connection 'upgrade';
75
75
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
76
+ proxy_set_header X-Forwarded-Proto $scheme;
76
77
  proxy_set_header Host $host;
78
+ proxy_set_header Referer $http_referer;
79
+ proxy_set_header User-Agent $http_user_agent;
77
80
  add_header Cache-Control 'no-cache, no-store';
78
81
  proxy_cache_bypass $http_upgrade;
79
82
  proxy_connect_timeout 600;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "2.0.0-alpha.9",
3
+ "version": "2.0.0-beta.10",
4
4
  "description": "",
5
5
  "license": "AGPL-3.0",
6
6
  "main": "./src/index.js",
@@ -8,8 +8,8 @@
8
8
  "nocobase": "./bin/index.js"
9
9
  },
10
10
  "dependencies": {
11
- "@nocobase/app": "2.0.0-alpha.9",
12
- "@nocobase/license-kit": "^0.2.17",
11
+ "@nocobase/app": "2.0.0-beta.10",
12
+ "@nocobase/license-kit": "^0.3.5",
13
13
  "@types/fs-extra": "^11.0.1",
14
14
  "@umijs/utils": "3.5.20",
15
15
  "chalk": "^4.1.1",
@@ -27,12 +27,12 @@
27
27
  "tsx": "^4.19.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@nocobase/devtools": "2.0.0-alpha.9"
30
+ "@nocobase/devtools": "2.0.0-beta.10"
31
31
  },
32
32
  "repository": {
33
33
  "type": "git",
34
34
  "url": "git+https://github.com/nocobase/nocobase.git",
35
35
  "directory": "packages/core/cli"
36
36
  },
37
- "gitHead": "4a9acf96f21a3aa35bccbd188b942595b09da0a9"
37
+ "gitHead": "9943dc4b0fdedcac3f304714b58635ae441e2560"
38
38
  }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ const chalk = require('chalk');
11
+ const { Command } = require('commander');
12
+ const fs = require('fs-extra');
13
+ const { resolve } = require('path');
14
+
15
+ /**
16
+ * 复制主应用客户端文件
17
+ * @param {string} source - 源目录路径
18
+ * @param {string} target - 目标目录路径
19
+ */
20
+ async function copyMainClient(source, target) {
21
+ if (!(await fs.exists(source))) {
22
+ console.log(chalk.yellow(`Source directory does not exist: ${source}`));
23
+ return;
24
+ }
25
+ // 确保目标目录存在且为空
26
+ await fs.ensureDir(target);
27
+ await fs.emptyDir(target);
28
+ await fs.copy(source, target, { recursive: true });
29
+ console.log(chalk.green(`Copied main client files from ${source} to ${target}`));
30
+ }
31
+
32
+ /**
33
+ * 复制插件客户端文件
34
+ * @param {string} pluginsBaseDir - 插件基础目录路径
35
+ * @param {string} namespace - 命名空间(如 '@nocobase' 或 '@nocobase-example')
36
+ * @param {string} target - 目标目录
37
+ */
38
+ async function copyPluginClients(pluginsBaseDir, namespace, target) {
39
+ const pluginsDir = resolve(process.cwd(), pluginsBaseDir, namespace);
40
+ if (await fs.exists(pluginsDir)) {
41
+ const pluginNames = await fs.readdir(pluginsDir);
42
+ for (const pluginName of pluginNames) {
43
+ const pluginPath = resolve(pluginsDir, pluginName);
44
+ const pluginDistClient = resolve(pluginPath, 'dist/client');
45
+ if (await fs.exists(pluginDistClient)) {
46
+ const pluginTarget = resolve(target, 'static/plugins', namespace, pluginName, 'dist/client');
47
+ await fs.mkdir(resolve(pluginTarget, '..'), { recursive: true });
48
+ await fs.copy(pluginDistClient, pluginTarget, { recursive: true });
49
+ console.log(chalk.green(`Copied ${namespace}/${pluginName} client files`));
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 递归上传目录到 OSS
57
+ * @param {Client} client - OSS 客户端实例
58
+ * @param {string} localDir - 本地目录路径
59
+ * @param {string} ossPrefix - OSS 对象前缀(目录路径)
60
+ */
61
+ async function uploadDirectoryToOSS(client, localDir, ossPrefix = '') {
62
+ if (!(await fs.exists(localDir))) {
63
+ console.log(chalk.yellow(`Directory does not exist: ${localDir}`));
64
+ return;
65
+ }
66
+
67
+ const stats = await fs.stat(localDir);
68
+ if (!stats.isDirectory()) {
69
+ throw new Error(`${localDir} is not a directory`);
70
+ }
71
+
72
+ const files = await fs.readdir(localDir);
73
+ let uploadedCount = 0;
74
+
75
+ for (const file of files) {
76
+ const filePath = resolve(localDir, file);
77
+ const fileStats = await fs.stat(filePath);
78
+
79
+ if (fileStats.isDirectory()) {
80
+ // 递归处理子目录
81
+ const subOssPrefix = ossPrefix ? `${ossPrefix}/${file}` : file;
82
+ const subCount = await uploadDirectoryToOSS(client, filePath, subOssPrefix);
83
+ uploadedCount += subCount;
84
+ } else {
85
+ // 上传文件
86
+ const ossKey = ossPrefix ? `${ossPrefix}/${file}` : file;
87
+ try {
88
+ await client.put(ossKey, filePath);
89
+ // console.log(chalk.green(`Uploaded: ${ossKey}`));
90
+ uploadedCount++;
91
+ } catch (error) {
92
+ console.error(chalk.red(`Failed to upload ${ossKey}:`), error.message);
93
+ throw error;
94
+ }
95
+ }
96
+ }
97
+
98
+ return uploadedCount;
99
+ }
100
+
101
+ /**
102
+ *
103
+ * @param {Command} cli
104
+ */
105
+ module.exports = (cli) => {
106
+ cli
107
+ .command('client:extract')
108
+ .allowUnknownOption()
109
+ .action(async () => {
110
+ const version = require('../../package.json').version;
111
+ const target = resolve(process.cwd(), 'storage/dist-client', version);
112
+ const mainClientSource = resolve(process.cwd(), 'node_modules/@nocobase/app/dist/client');
113
+ await copyMainClient(mainClientSource, target);
114
+ await copyPluginClients('packages/plugins', '@nocobase', target);
115
+ await copyPluginClients('packages/plugins', '@nocobase-example', target);
116
+ await copyPluginClients('packages/pro-plugins', '@nocobase', target);
117
+ });
118
+
119
+ cli
120
+ .command('client:upload')
121
+ .allowUnknownOption()
122
+ .action(async () => {
123
+ const version = require('../../package.json').version;
124
+ const target = resolve(process.cwd(), 'storage/dist-client', version);
125
+
126
+ // 检查必要的环境变量
127
+ if (
128
+ !process.env.CDN_ALI_OSS_ACCESS_KEY_ID ||
129
+ !process.env.CDN_ALI_OSS_ACCESS_KEY_SECRET ||
130
+ !process.env.CDN_ALI_OSS_BUCKET ||
131
+ !process.env.CDN_ALI_OSS_REGION
132
+ ) {
133
+ console.error(
134
+ chalk.red(
135
+ 'Missing required environment variables: CDN_ALI_OSS_ACCESS_KEY_ID, CDN_ALI_OSS_ACCESS_KEY_SECRET, CDN_ALI_OSS_BUCKET, CDN_ALI_OSS_REGION',
136
+ ),
137
+ );
138
+ process.exit(1);
139
+ }
140
+
141
+ const Client = require('ali-oss');
142
+
143
+ const client = new Client({
144
+ accessKeyId: process.env.CDN_ALI_OSS_ACCESS_KEY_ID,
145
+ accessKeySecret: process.env.CDN_ALI_OSS_ACCESS_KEY_SECRET,
146
+ bucket: process.env.CDN_ALI_OSS_BUCKET,
147
+ region: process.env.CDN_ALI_OSS_REGION,
148
+ });
149
+
150
+ if (!(await fs.exists(target))) {
151
+ console.error(chalk.red(`Target directory does not exist: ${target}`));
152
+ console.log(chalk.yellow('Please run "client:extract" first to generate the client files.'));
153
+ process.exit(1);
154
+ }
155
+
156
+ console.log(chalk.blue(`Uploading directory ${target} to OSS...`));
157
+ const ossPrefix = `${version}`;
158
+ try {
159
+ const uploadedCount = await uploadDirectoryToOSS(client, target, ossPrefix);
160
+ console.log(chalk.green(`Successfully uploaded ${uploadedCount} files to OSS`));
161
+ } catch (error) {
162
+ console.error(chalk.red('Upload failed:'), error);
163
+ process.exit(1);
164
+ }
165
+ });
166
+ };
@@ -38,6 +38,7 @@ module.exports = (cli) => {
38
38
  require('./pkg')(cli);
39
39
  require('./instance-id')(cli);
40
40
  require('./view-license-key')(cli);
41
+ require('./client')(cli);
41
42
  if (isPackageValid('@umijs/utils')) {
42
43
  require('./create-plugin')(cli);
43
44
  }
@@ -13,6 +13,7 @@ const { run, isDev } = require('../util');
13
13
  const { getInstanceIdAsync } = require('@nocobase/license-kit');
14
14
  const path = require('path');
15
15
  const fs = require('fs');
16
+ const { logger } = require('../logger');
16
17
 
17
18
  /**
18
19
  *
@@ -24,11 +25,11 @@ module.exports = (cli) => {
24
25
  .description('Generate InstanceID')
25
26
  .option('--force', 'Force generate InstanceID')
26
27
  .action(async (options) => {
27
- console.log('Generating InstanceID...');
28
+ logger.info('Generating InstanceID...');
28
29
  const dir = path.resolve(process.cwd(), 'storage/.license');
29
30
  const filePath = path.resolve(dir, 'instance-id');
30
31
  if (fs.existsSync(filePath) && !options.force) {
31
- console.log('InstanceID already exists at ' + filePath);
32
+ logger.info('InstanceID already exists at ' + filePath);
32
33
  return;
33
34
  } else {
34
35
  if (!fs.existsSync(dir)) {
@@ -37,9 +38,9 @@ module.exports = (cli) => {
37
38
  try {
38
39
  const instanceId = await getInstanceIdAsync();
39
40
  fs.writeFileSync(filePath, instanceId + '\n');
40
- console.log(chalk.greenBright(`InstanceID saved to ${filePath}`));
41
+ logger.info(`InstanceID saved to ${filePath}`);
41
42
  } catch (e) {
42
- console.log(e);
43
+ logger.error('Failed to generate InstanceID', e.message || e);
43
44
  }
44
45
  }
45
46
  });
@@ -53,8 +53,8 @@ module.exports = (cli) => {
53
53
  dotenv.config({ path: envFilePath, override: true });
54
54
  }
55
55
  }
56
- if (!process.env.API_BASE_URL) {
57
- throw new Error('Please set API_BASE_URL in environment variables or in .env.perf file');
56
+ if (!process.env.TARGET_ORIGIN) {
57
+ throw new Error('Please set TARGET_ORIGIN in environment variables or in .env.perf file');
58
58
  }
59
59
  const args = command.args.filter((arg) => arg !== file);
60
60
  await run(`k6`, ['run', f, ...(args.length ? ['--', ...args] : [])]);
@@ -14,8 +14,8 @@ const zlib = require('zlib');
14
14
  const tar = require('tar');
15
15
  const path = require('path');
16
16
  const { createStoragePluginsSymlink } = require('@nocobase/utils/plugin-symlink');
17
- const chalk = require('chalk');
18
- const { getAccessKeyPair, showLicenseInfo, LicenseKeyError } = require('../util');
17
+ const { getAccessKeyPair, showLicenseInfo, LicenseKeyError } = require('../license');
18
+ const { logger } = require('../logger');
19
19
 
20
20
  class Package {
21
21
  data;
@@ -78,7 +78,7 @@ class Package {
78
78
  }
79
79
 
80
80
  if (!this.data.versions[version]) {
81
- console.log(chalk.redBright(`Download failed: ${this.packageName}@${version} package does not exist`));
81
+ logger.error(`Download failed: ${this.packageName}@${version} package does not exist`);
82
82
  }
83
83
 
84
84
  return [version, this.data.versions[version].dist.tarball];
@@ -99,7 +99,7 @@ class Package {
99
99
  async isDepPackage() {
100
100
  const pkg1 = path.resolve(process.cwd(), 'node_modules', this.packageName, 'package.json');
101
101
  const pkg2 = path.resolve(process.cwd(), process.env.PLUGIN_STORAGE_PATH, this.packageName, 'package.json');
102
- if (await fs.exists(pkg1) && await fs.exists(pkg2)) {
102
+ if ((await fs.exists(pkg1)) && (await fs.exists(pkg2))) {
103
103
  const readPath1 = fs.realpathSync(pkg1);
104
104
  const readPath2 = fs.realpathSync(pkg2);
105
105
  if (readPath1 !== readPath2) {
@@ -122,11 +122,11 @@ class Package {
122
122
 
123
123
  async download(options = {}) {
124
124
  if (await this.isDevPackage()) {
125
- console.log(chalk.yellowBright(`Skipped: ${this.packageName} is dev package`));
125
+ logger.info(`Skipped: ${this.packageName} is dev package`);
126
126
  return;
127
127
  }
128
128
  if (await this.isDepPackage()) {
129
- console.log(chalk.yellowBright(`Skipped: ${this.packageName} is dependency package`));
129
+ logger.info(`Skipped: ${this.packageName} is dependency package`);
130
130
  return;
131
131
  }
132
132
  if (await this.isDownloaded(options.version)) {
@@ -134,7 +134,7 @@ class Package {
134
134
  }
135
135
  await this.getInfo();
136
136
  if (!this.data) {
137
- console.log(chalk.redBright(`Download failed: ${this.packageName} package does not exist`));
137
+ logger.error(`Download failed: ${this.packageName} package does not exist`);
138
138
  return;
139
139
  }
140
140
  try {
@@ -158,7 +158,7 @@ class Package {
158
158
  .on('finish', resolve)
159
159
  .on('error', reject);
160
160
  });
161
- console.log(chalk.greenBright(`Downloaded: ${this.packageName}@${version}`));
161
+ logger.info(`Downloaded: ${this.packageName}@${version}`);
162
162
  } catch (error) {
163
163
  if (error?.response?.data && typeof error?.response?.data?.pipe === 'function') {
164
164
  let errorMessageBuffer = '';
@@ -167,11 +167,11 @@ class Package {
167
167
  });
168
168
  error.response.data.on?.('end', () => {
169
169
  if (error.response.status === 403) {
170
- console.error(chalk.redBright('You do not have permission to download this package version.'));
170
+ logger.error('You do not have permission to download this package version.');
171
171
  }
172
172
  });
173
173
  }
174
- console.log(chalk.redBright(`Download failed: ${this.packageName}`));
174
+ logger.error(`Download failed: ${this.packageName}`);
175
175
  }
176
176
  }
177
177
  }
@@ -202,12 +202,13 @@ class PackageManager {
202
202
  responseType: 'json',
203
203
  });
204
204
  this.token = res1.data.token;
205
+ logger.info('Login success');
205
206
  } catch (error) {
206
207
  if (error?.response?.data?.error === 'license not valid') {
207
208
  showLicenseInfo(LicenseKeyError.notValid);
208
209
  }
209
- console.error(chalk.redBright(`Login failed: ${this.baseURL}`));
210
- console.error(error);
210
+ logger.error(`Login failed: ${this.baseURL}`);
211
+ logger.error(error?.message || error);
211
212
  }
212
213
  }
213
214
 
@@ -244,7 +245,7 @@ class PackageManager {
244
245
  const dir = path.resolve(process.env.PLUGIN_STORAGE_PATH, packageName);
245
246
  const r = await fs.exists(dir);
246
247
  if (r) {
247
- console.log(chalk.yellowBright(`Removed: ${packageName}`));
248
+ logger.info(`Removed: ${packageName}`);
248
249
  await fs.rm(dir, { force: true, recursive: true });
249
250
  }
250
251
  }
@@ -260,9 +261,11 @@ class PackageManager {
260
261
  await this.removePackage(pkg);
261
262
  }
262
263
  }
264
+ logger.info(`Download plugins...`);
263
265
  for (const pkg of licensed_plugins) {
264
266
  await this.getPackage(pkg).download({ version });
265
267
  }
268
+ logger.info('Download plugins done');
266
269
  }
267
270
  }
268
271
 
@@ -280,14 +283,25 @@ module.exports = (cli) => {
280
283
  NOCOBASE_PKG_URL = 'https://pkg.nocobase.com/',
281
284
  NOCOBASE_PKG_USERNAME,
282
285
  NOCOBASE_PKG_PASSWORD,
286
+ DISABLE_PKG_DOWNLOAD,
283
287
  } = process.env;
288
+ if (DISABLE_PKG_DOWNLOAD === 'true') {
289
+ logger.info('Package download is disabled.');
290
+ return;
291
+ }
284
292
  let accessKeyId;
285
293
  let accessKeySecret;
286
294
  try {
287
295
  ({ accessKeyId, accessKeySecret } = await getAccessKeyPair());
288
296
  } catch (e) {
297
+ // logger.error('Get AccessKey Pair error', e);
289
298
  return;
290
299
  }
300
+ if (NOCOBASE_PKG_USERNAME && NOCOBASE_PKG_PASSWORD && !accessKeyId && !accessKeySecret) {
301
+ logger.warn(
302
+ 'NOCOBASE_PKG_USERNAME and NOCOBASE_PKG_PASSWORD will be deprecated in future versions. Please log in to NocoBase Service and refer to the documentation to learn how to install and upgrade commercial plugins.\n',
303
+ );
304
+ }
291
305
  if (!(NOCOBASE_PKG_USERNAME && NOCOBASE_PKG_PASSWORD) && !(accessKeyId && accessKeySecret)) {
292
306
  return;
293
307
  }
@@ -302,6 +316,6 @@ module.exports = (cli) => {
302
316
  await createStoragePluginsSymlink();
303
317
  });
304
318
  pkg.command('export-all').action(async () => {
305
- console.log('Todo...');
319
+ logger.info('Todo...');
306
320
  });
307
321
  };
@@ -56,32 +56,34 @@ module.exports = (cli) => {
56
56
  await downloadPro();
57
57
  }
58
58
 
59
- const watcher = chokidar.watch('./storage/plugins/**/*', {
60
- cwd: process.cwd(),
61
- ignoreInitial: true,
62
- ignored: /(^|[\/\\])\../, // 忽略隐藏文件
63
- persistent: true,
64
- depth: 1, // 只监听第一层目录
65
- });
59
+ if (process.env.NO_WATCH_PLUGINS === true || process.env.NO_WATCH_PLUGINS === 'true') {
60
+ const restart = _.debounce(async () => {
61
+ console.log('restarting...');
62
+ await run('yarn', ['nocobase', 'pm2-restart']);
63
+ }, 500);
66
64
 
67
- const restart = _.debounce(async () => {
68
- console.log('restarting...');
69
- await run('yarn', ['nocobase', 'pm2-restart']);
70
- }, 500);
71
-
72
- watcher
73
- .on('ready', () => {
74
- isReady = true;
75
- })
76
- .on('addDir', async (pathname) => {
77
- if (!isReady) return;
78
- restart();
79
- })
80
- .on('unlinkDir', async (pathname) => {
81
- if (!isReady) return;
82
- restart();
65
+ const watcher = chokidar.watch('./storage/plugins/**/*', {
66
+ cwd: process.cwd(),
67
+ ignoreInitial: true,
68
+ ignored: /(^|[\/\\])\../, // 忽略隐藏文件
69
+ persistent: true,
70
+ depth: 1, // 只监听第一层目录
83
71
  });
84
72
 
73
+ watcher
74
+ .on('ready', () => {
75
+ isReady = true;
76
+ })
77
+ .on('addDir', async (pathname) => {
78
+ if (!isReady) return;
79
+ restart();
80
+ })
81
+ .on('unlinkDir', async (pathname) => {
82
+ if (!isReady) return;
83
+ restart();
84
+ });
85
+ }
86
+
85
87
  if (opts.port) {
86
88
  process.env.APP_PORT = opts.port;
87
89
  }
@@ -54,7 +54,7 @@ function addTestCommand(name, cli) {
54
54
  const first = paths?.[0];
55
55
  if (!process.env.TEST_ENV && first) {
56
56
  const key = first.split(path.sep).join('/');
57
- if (key.includes('/client/') || key.includes('/flow-engine/')) {
57
+ if (key.includes('/client/') || key.includes('/client-v2/') || key.includes('/flow-engine/')) {
58
58
  process.env.TEST_ENV = 'client-side';
59
59
  } else {
60
60
  process.env.TEST_ENV = 'server-side';
@@ -62,6 +62,7 @@ module.exports = (cli) => {
62
62
  if (descJson['devDependencies']?.['@nocobase/devtools']) {
63
63
  descJson['devDependencies']['@nocobase/devtools'] = stdout;
64
64
  }
65
+ descJson['resolutions'] = sourceJson['resolutions'];
65
66
  const json = deepmerge(descJson, sourceJson);
66
67
  await writeJSON(descPath, json, { spaces: 2, encoding: 'utf8' });
67
68
  await run('yarn', ['install']);
package/src/license.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ const chalk = require('chalk');
11
+ const { resolve } = require('path');
12
+ const fs = require('fs-extra');
13
+ const { keyDecrypt, getEnvAsync } = require('@nocobase/license-kit');
14
+ const { isEnvMatch } = require('@nocobase/plugin-license/utils/env');
15
+ const { logger } = require('./logger');
16
+ const { pick } = require('lodash');
17
+
18
+ exports.getAccessKeyPair = async function () {
19
+ const keyFile = resolve(process.cwd(), 'storage/.license/license-key');
20
+ if (!fs.existsSync(keyFile)) {
21
+ logger.error('License key not found');
22
+ return {};
23
+ }
24
+ logger.info('License key found');
25
+ let keyData = {};
26
+ try {
27
+ const str = fs.readFileSync(keyFile, 'utf-8');
28
+ const keyDataStr = keyDecrypt(str);
29
+ keyData = JSON.parse(keyDataStr);
30
+ } catch (error) {
31
+ showLicenseInfo(LicenseKeyError.parseFailed);
32
+ throw new Error(LicenseKeyError.parseFailed.title);
33
+ }
34
+ const env = await getEnvAsync();
35
+ const isEnvMatched = await isEnvMatch(env, keyData);
36
+ if (!isEnvMatched) {
37
+ showLicenseInfo({
38
+ ...LicenseKeyError.notMatch,
39
+ env,
40
+ });
41
+ throw new Error(LicenseKeyError.notMatch.title);
42
+ }
43
+ const { accessKeyId, accessKeySecret } = keyData;
44
+ return { accessKeyId, accessKeySecret };
45
+ };
46
+
47
+ const LicenseKeyError = {
48
+ notExist: {
49
+ title: 'License key not found',
50
+ content:
51
+ 'Please go to the license settings page to obtain the Instance ID for the current environment, and then generate the license key on the service platform.',
52
+ },
53
+ parseFailed: {
54
+ title: 'Invalid license key format',
55
+ content: 'Please check your license key, or regenerate the license key on the service platform.',
56
+ },
57
+ notMatch: {
58
+ title: 'License key not match current environment',
59
+ content:
60
+ 'Please go to the license settings page to obtain the Instance ID for the current environment, and then regenerate the license key on the service platform.',
61
+ },
62
+ notValid: {
63
+ title: 'Invalid license key',
64
+ content:
65
+ 'Please go to the license settings page to obtain the Instance ID for the current environment, and then regenerate the license key on the service platform.',
66
+ },
67
+ };
68
+
69
+ exports.LicenseKeyError = LicenseKeyError;
70
+
71
+ function showLicenseInfo({ title, content, env }) {
72
+ logger.error(title + '. ' + content);
73
+ logger.error('Current environment', pick(env, ['sys', 'osVer', 'db']));
74
+ }
75
+
76
+ exports.showLicenseInfo = showLicenseInfo;
package/src/logger.js ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const winston = require('winston');
13
+ require('winston-daily-rotate-file');
14
+
15
+ function ensureDir(dir) {
16
+ if (!fs.existsSync(dir)) {
17
+ fs.mkdirSync(dir, { recursive: true });
18
+ }
19
+ }
20
+
21
+ function createSystemLogger({ dirname, filename, defaultMeta = {} }) {
22
+ ensureDir(dirname);
23
+
24
+ const commonFormat = winston.format.combine(
25
+ winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
26
+ winston.format.printf((info) => {
27
+ const meta = info.meta ? JSON.stringify(info.meta) : '';
28
+ return `${info.timestamp} [${info.level}] ${info.message} ${meta}`;
29
+ }),
30
+ );
31
+
32
+ const consoleFormat = winston.format.combine(winston.format.colorize(), commonFormat);
33
+
34
+ const fileTransport = new winston.transports.DailyRotateFile({
35
+ dirname,
36
+ filename: `${filename}_%DATE%.log`,
37
+ datePattern: 'YYYY-MM-DD',
38
+ zippedArchive: false,
39
+ format: commonFormat,
40
+ });
41
+
42
+ const consoleTransport = new winston.transports.Console({
43
+ format: consoleFormat,
44
+ });
45
+
46
+ const logger = winston.createLogger({
47
+ level: 'info',
48
+ transports: [consoleTransport, fileTransport],
49
+ defaultMeta,
50
+ });
51
+
52
+ const wrap = (level) => (message, meta) => {
53
+ logger.log({ level, message, meta });
54
+ return logger;
55
+ };
56
+
57
+ return {
58
+ info: wrap('info'),
59
+ warn: wrap('warn'),
60
+ error: wrap('error'),
61
+ };
62
+ }
63
+
64
+ const getLoggerFilePath = (...paths) => {
65
+ return path.resolve(process.env.LOGGER_BASE_PATH || path.resolve(process.cwd(), 'storage', 'logs'), ...paths);
66
+ };
67
+
68
+ const logger = createSystemLogger({
69
+ dirname: getLoggerFilePath('main'),
70
+ filename: 'system',
71
+ });
72
+
73
+ module.exports = {
74
+ logger,
75
+ };
package/src/util.js CHANGED
@@ -18,8 +18,6 @@ const dotenv = require('dotenv');
18
18
  const fs = require('fs-extra');
19
19
  const os = require('os');
20
20
  const moment = require('moment-timezone');
21
- const { keyDecrypt, getEnvAsync } = require('@nocobase/license-kit');
22
- const omit = require('lodash/omit');
23
21
 
24
22
  exports.isPackageValid = (pkg) => {
25
23
  try {
@@ -297,14 +295,24 @@ function buildIndexHtml(force = false) {
297
295
  fs.copyFileSync(file, tpl);
298
296
  }
299
297
  const data = fs.readFileSync(tpl, 'utf-8');
300
- const replacedData = data
298
+ let replacedData = data
299
+ .replace(/\{\{env.CDN_BASE_URL\}\}/g, process.env.CDN_BASE_URL)
301
300
  .replace(/\{\{env.APP_PUBLIC_PATH\}\}/g, process.env.APP_PUBLIC_PATH)
301
+ .replace(/\{\{env.API_CLIENT_SHARE_TOKEN\}\}/g, process.env.API_CLIENT_SHARE_TOKEN || 'false')
302
302
  .replace(/\{\{env.API_CLIENT_STORAGE_TYPE\}\}/g, process.env.API_CLIENT_STORAGE_TYPE)
303
303
  .replace(/\{\{env.API_CLIENT_STORAGE_PREFIX\}\}/g, process.env.API_CLIENT_STORAGE_PREFIX)
304
304
  .replace(/\{\{env.API_BASE_URL\}\}/g, process.env.API_BASE_URL || process.env.API_BASE_PATH)
305
305
  .replace(/\{\{env.WS_URL\}\}/g, process.env.WEBSOCKET_URL || '')
306
306
  .replace(/\{\{env.WS_PATH\}\}/g, process.env.WS_PATH)
307
307
  .replace('src="/umi.', `src="${process.env.APP_PUBLIC_PATH}umi.`);
308
+
309
+ if (process.env.CDN_BASE_URL) {
310
+ const appBaseUrl = process.env.CDN_BASE_URL.replace(/\/+$/, '');
311
+ const appPublicPath = process.env.APP_PUBLIC_PATH.replace(/\/+$/, '');
312
+ const re1 = new RegExp(`src="${appPublicPath}/`, 'g');
313
+ const re2 = new RegExp(`href="${appPublicPath}/`, 'g');
314
+ replacedData = replacedData.replace(re1, `src="${appBaseUrl}/`).replace(re2, `href="${appBaseUrl}/`);
315
+ }
308
316
  fs.writeFileSync(file, replacedData, 'utf-8');
309
317
  }
310
318
 
@@ -362,6 +370,7 @@ exports.initEnv = function initEnv() {
362
370
  APP_PORT: 13000,
363
371
  API_BASE_PATH: '/api/',
364
372
  API_CLIENT_STORAGE_PREFIX: 'NOCOBASE_',
373
+ API_CLIENT_SHARE_TOKEN: 'false',
365
374
  API_CLIENT_STORAGE_TYPE: 'localStorage',
366
375
  // DB_DIALECT: 'sqlite',
367
376
  DB_STORAGE: 'storage/db/nocobase.sqlite',
@@ -385,6 +394,8 @@ exports.initEnv = function initEnv() {
385
394
  PLUGIN_STATICS_PATH: '/static/plugins/',
386
395
  LOGGER_BASE_PATH: 'storage/logs',
387
396
  APP_SERVER_BASE_URL: '',
397
+ APP_BASE_URL: '',
398
+ CDN_BASE_URL: '',
388
399
  APP_PUBLIC_PATH: '/',
389
400
  WATCH_FILE: resolve(process.cwd(), 'storage/app.watch.ts'),
390
401
  };
@@ -440,6 +451,16 @@ exports.initEnv = function initEnv() {
440
451
  process.env.__env_modified__ = true;
441
452
  }
442
453
 
454
+ if (!process.env.CDN_BASE_URL && process.env.APP_PUBLIC_PATH !== '/') {
455
+ process.env.CDN_BASE_URL = process.env.APP_PUBLIC_PATH;
456
+ }
457
+
458
+ if (process.env.CDN_BASE_URL.includes('http') && process.env.CDN_VERSION === 'auto') {
459
+ const version = require('../package.json').version;
460
+ process.env.CDN_BASE_URL = process.env.CDN_BASE_URL.replace(/\/+$/, '') + '/' + version + '/';
461
+ process.env.CDN_VERSION = '';
462
+ }
463
+
443
464
  if (!process.env.TZ) {
444
465
  const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
445
466
  process.env.TZ = getTimezonesByOffset(process.env.DB_TIMEZONE || timeZone);
@@ -490,89 +511,3 @@ exports.generatePlugins = function () {
490
511
  return;
491
512
  }
492
513
  };
493
-
494
- async function isEnvMatch(keyData) {
495
- const env = await getEnvAsync();
496
- if (env?.container?.id && keyData?.instanceData?.container?.id) {
497
- return (
498
- JSON.stringify(omit(env, ['timestamp', 'container', 'hostname', 'mac'])) ===
499
- JSON.stringify(omit(keyData?.instanceData, ['timestamp', 'container', 'hostname', 'mac']))
500
- );
501
- }
502
- return (
503
- JSON.stringify(omit(env, ['timestamp', 'mac'])) ===
504
- JSON.stringify(omit(keyData?.instanceData, ['timestamp', 'mac']))
505
- );
506
- }
507
-
508
- exports.getAccessKeyPair = async function () {
509
- const keyFile = resolve(process.cwd(), 'storage/.license/license-key');
510
- if (!fs.existsSync(keyFile)) {
511
- return {};
512
- }
513
-
514
- let keyData = {};
515
- try {
516
- const str = fs.readFileSync(keyFile, 'utf-8');
517
- const keyDataStr = keyDecrypt(str);
518
- keyData = JSON.parse(keyDataStr);
519
- } catch (error) {
520
- showLicenseInfo(LicenseKeyError.parseFailed);
521
- throw new Error(LicenseKeyError.parseFailed.title);
522
- }
523
-
524
- const isEnvMatched = await isEnvMatch(keyData);
525
- if (!isEnvMatched) {
526
- showLicenseInfo(LicenseKeyError.notMatch);
527
- throw new Error(LicenseKeyError.notMatch.title);
528
- }
529
-
530
- const { accessKeyId, accessKeySecret } = keyData;
531
- return { accessKeyId, accessKeySecret };
532
- };
533
-
534
- const LicenseKeyError = {
535
- notExist: {
536
- title: 'License key not found',
537
- content:
538
- 'Please go to the license settings page to obtain the Instance ID for the current environment, and then generate the license key on the service platform.',
539
- },
540
- parseFailed: {
541
- title: 'Invalid license key format',
542
- content: 'Please check your license key, or regenerate the license key on the service platform.',
543
- },
544
- notMatch: {
545
- title: 'License key mismatch',
546
- content:
547
- 'Please go to the license settings page to obtain the Instance ID for the current environment, and then regenerate the license key on the service platform.',
548
- },
549
- notValid: {
550
- title: 'Invalid license key',
551
- content:
552
- 'Please go to the license settings page to obtain the Instance ID for the current environment, and then regenerate the license key on the service platform.',
553
- },
554
- };
555
-
556
- exports.LicenseKeyError = LicenseKeyError;
557
-
558
- function showLicenseInfo({ title, content }) {
559
- const rows = [];
560
- const length = 80;
561
- let row = '';
562
- content.split(' ').forEach((word) => {
563
- if (row.length + word.length > length) {
564
- rows.push(row);
565
- row = '';
566
- }
567
- row += word + ' ';
568
- });
569
- if (row) {
570
- rows.push(row);
571
- }
572
- console.log(Array(length).fill('-').join(''));
573
- console.log(chalk.yellow(title));
574
- console.log(chalk.yellow(rows.join('\n')));
575
- console.log(Array(length).fill('-').join(''));
576
- }
577
-
578
- exports.showLicenseInfo = showLicenseInfo;
@@ -24,8 +24,8 @@
24
24
  "@types/react-dom": "^18.0.0",
25
25
  "react-router-dom": "^6.30.1",
26
26
  "react-router": "^6.30.1",
27
+ "antd": "5.24.2",
27
28
  "async": "^3.2.6",
28
- "antd": "5.12.8",
29
29
  "rollup": "4.24.0",
30
30
  "semver": "^7.7.1"
31
31
  },
@@ -4,8 +4,8 @@
4
4
  "main": "dist/server/index.js",
5
5
  "dependencies": {},
6
6
  "peerDependencies": {
7
- "@nocobase/client": "1.x",
8
- "@nocobase/server": "1.x",
9
- "@nocobase/test": "1.x"
7
+ "@nocobase/client": "2.x",
8
+ "@nocobase/server": "2.x",
9
+ "@nocobase/test": "2.x"
10
10
  }
11
11
  }