@nocobase/cli 2.0.22 → 2.1.0-alpha.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
@@ -12,6 +12,11 @@ log_format apm '"$time_local" client=$remote_addr '
12
12
  'upstream_connect_time=$upstream_connect_time '
13
13
  'upstream_header_time=$upstream_header_time';
14
14
 
15
+ map $http_x_forwarded_proto $upstream_x_forwarded_proto {
16
+ default $http_x_forwarded_proto;
17
+ "" $scheme;
18
+ }
19
+
15
20
  server {
16
21
  listen 80;
17
22
  server_name _;
@@ -50,6 +55,77 @@ server {
50
55
  }
51
56
  }
52
57
 
58
+ location = {{v2PublicPathNoTrailingSlash}} {
59
+ return 302 {{v2PublicPath}}$is_args$args;
60
+ }
61
+
62
+ location {{v2PublicPath}}assets/ {
63
+ alias {{cwd}}/node_modules/@nocobase/app/dist/client/v2/assets/;
64
+ expires 365d;
65
+ add_header Cache-Control "public";
66
+ access_log off;
67
+ autoindex off;
68
+ }
69
+
70
+ location {{v2PublicPath}} {
71
+ proxy_pass http://127.0.0.1:{{apiPort}};
72
+ proxy_http_version 1.1;
73
+ proxy_set_header Upgrade $http_upgrade;
74
+ proxy_set_header Connection 'upgrade';
75
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
76
+ proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto;
77
+ proxy_set_header Host $host;
78
+ proxy_set_header Referer $http_referer;
79
+ proxy_set_header User-Agent $http_user_agent;
80
+ add_header Cache-Control 'no-cache, no-store';
81
+ proxy_cache_bypass $http_upgrade;
82
+ proxy_connect_timeout 600;
83
+ proxy_send_timeout 600;
84
+ proxy_read_timeout 600;
85
+ send_timeout 600;
86
+ }
87
+
88
+ # RFC 8414 root-mounted discovery compatibility for path-based issuers/resources.
89
+ location ~ ^/\.well-known/oauth-authorization-server/(.+)$ {
90
+ rewrite ^/\.well-known/oauth-authorization-server/(.+)$ /$1/.well-known/oauth-authorization-server break;
91
+ proxy_pass http://127.0.0.1:{{apiPort}};
92
+ proxy_http_version 1.1;
93
+ proxy_set_header Upgrade $http_upgrade;
94
+ proxy_set_header Connection 'upgrade';
95
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
96
+ proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto;
97
+ proxy_set_header Host $host;
98
+ proxy_set_header Referer $http_referer;
99
+ proxy_set_header User-Agent $http_user_agent;
100
+ add_header Cache-Control 'no-cache, no-store';
101
+ proxy_cache_bypass $http_upgrade;
102
+ proxy_connect_timeout 600;
103
+ proxy_send_timeout 600;
104
+ proxy_read_timeout 600;
105
+ send_timeout 600;
106
+ }
107
+
108
+ location ~ ^/\.well-known/openid-configuration/(.+)$ {
109
+ rewrite ^/\.well-known/openid-configuration/(.+)$ /$1/.well-known/openid-configuration break;
110
+ proxy_pass http://127.0.0.1:{{apiPort}};
111
+ proxy_http_version 1.1;
112
+ proxy_set_header Upgrade $http_upgrade;
113
+ proxy_set_header Connection 'upgrade';
114
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
115
+ proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto;
116
+ proxy_set_header Host $host;
117
+ proxy_set_header Referer $http_referer;
118
+ proxy_set_header User-Agent $http_user_agent;
119
+ add_header Cache-Control 'no-cache, no-store';
120
+ proxy_cache_bypass $http_upgrade;
121
+ proxy_connect_timeout 600;
122
+ proxy_send_timeout 600;
123
+ proxy_read_timeout 600;
124
+ send_timeout 600;
125
+ }
126
+
127
+
128
+
53
129
  location {{publicPath}} {
54
130
  alias {{cwd}}/node_modules/@nocobase/app/dist/client/;
55
131
  try_files $uri $uri/ /index.html;
@@ -73,7 +149,7 @@ server {
73
149
  proxy_set_header Upgrade $http_upgrade;
74
150
  proxy_set_header Connection 'upgrade';
75
151
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
76
- proxy_set_header X-Forwarded-Proto $scheme;
152
+ proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto;
77
153
  proxy_set_header Host $host;
78
154
  proxy_set_header Referer $http_referer;
79
155
  proxy_set_header User-Agent $http_user_agent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "2.0.22",
3
+ "version": "2.1.0-alpha.10",
4
4
  "description": "",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./src/index.js",
@@ -8,7 +8,7 @@
8
8
  "nocobase": "./bin/index.js"
9
9
  },
10
10
  "dependencies": {
11
- "@nocobase/app": "2.0.22",
11
+ "@nocobase/app": "2.1.0-alpha.10",
12
12
  "@nocobase/license-kit": "^0.3.8",
13
13
  "@types/fs-extra": "^11.0.1",
14
14
  "@umijs/utils": "3.5.20",
@@ -27,12 +27,12 @@
27
27
  "tsx": "^4.19.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@nocobase/devtools": "2.0.22"
30
+ "@nocobase/devtools": "2.1.0-alpha.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": "7b703580c4bb60f1e5e44c8fd685141bb8b3a47c"
37
+ "gitHead": "ce790d46c0a5768ca9618c7d0d77ab8300de75c8"
38
38
  }
@@ -7,10 +7,34 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
+ const { existsSync } = require('fs');
10
11
  const { resolve } = require('path');
11
12
  const { Command } = require('commander');
12
13
  const { run, nodeCheck, isPackageValid, buildIndexHtml } = require('../util');
13
14
 
15
+ async function buildClientV2() {
16
+ const configPath = resolve(process.env.APP_PACKAGE_ROOT, 'client-v2/rsbuild.config.ts');
17
+ if (!existsSync(configPath)) {
18
+ console.log(`client-v2 config not found: ${configPath}`);
19
+ return;
20
+ }
21
+ await run('rsbuild', ['build', '--config', configPath], {
22
+ env: {
23
+ ...process.env,
24
+ APP_ENV: 'production',
25
+ NODE_ENV: 'production',
26
+ API_BASE_URL: process.env.API_BASE_URL || process.env.API_BASE_PATH,
27
+ API_CLIENT_STORAGE_PREFIX: process.env.API_CLIENT_STORAGE_PREFIX,
28
+ API_CLIENT_STORAGE_TYPE: process.env.API_CLIENT_STORAGE_TYPE,
29
+ API_CLIENT_SHARE_TOKEN: process.env.API_CLIENT_SHARE_TOKEN || 'false',
30
+ WEBSOCKET_URL: process.env.WEBSOCKET_URL || '',
31
+ WS_PATH: process.env.WS_PATH,
32
+ ESM_CDN_BASE_URL: process.env.ESM_CDN_BASE_URL || 'https://esm.sh',
33
+ ESM_CDN_SUFFIX: process.env.ESM_CDN_SUFFIX || '',
34
+ },
35
+ });
36
+ }
37
+
14
38
  /**
15
39
  *
16
40
  * @param {Command} cli
@@ -26,16 +50,23 @@ module.exports = (cli) => {
26
50
  .option('-w, --watch', 'watch compile the @nocobase/build package')
27
51
  .option('-s, --sourcemap', 'generate sourcemap')
28
52
  .option('--no-dts', 'not generate dts')
53
+ .option('--client-v2-only', 'build client-v2 shell only')
29
54
  .action(async (pkgs, options) => {
30
55
  nodeCheck();
56
+ process.env['VITE_CJS_IGNORE_WARNING'] = 'true';
57
+ process.env.APP_ENV = 'production';
58
+
59
+ if (options.clientV2Only) {
60
+ await buildClientV2();
61
+ return;
62
+ }
63
+
31
64
  if (options.compile || options.watch || isPackageValid('@nocobase/build/src/index.ts')) {
32
65
  await run('yarn', ['build', options.watch ? '--watch' : ''], {
33
66
  cwd: resolve(process.cwd(), 'packages/core/build'),
34
67
  });
35
68
  if (options.watch) return;
36
69
  }
37
- process.env['VITE_CJS_IGNORE_WARNING'] = 'true';
38
- process.env.APP_ENV = 'production';
39
70
 
40
71
  await run('nocobase-build', [
41
72
  ...pkgs,
@@ -45,5 +76,6 @@ module.exports = (cli) => {
45
76
  options.retry ? '--retry' : '',
46
77
  ]);
47
78
  buildIndexHtml(true);
79
+ await buildClientV2();
48
80
  });
49
81
  };
@@ -10,6 +10,7 @@
10
10
  const { resolve, posix } = require('path');
11
11
  const { Command } = require('commander');
12
12
  const { readFileSync, writeFileSync } = require('fs');
13
+ const { resolvePublicPath, resolveV2PublicPath } = require('../util');
13
14
 
14
15
  /**
15
16
  *
@@ -17,18 +18,33 @@ const { readFileSync, writeFileSync } = require('fs');
17
18
  */
18
19
  module.exports = (cli) => {
19
20
  cli.command('create-nginx-conf').action(async (name, options) => {
21
+ const rawAppPublicPath = process.env.APP_PUBLIC_PATH || '/';
22
+ const appPublicPath = resolvePublicPath(rawAppPublicPath);
23
+ const v2PublicPath = resolveV2PublicPath(rawAppPublicPath);
24
+ const appPublicPathWithoutTrailingSlash = appPublicPath.replace(/\/$/, '');
25
+ const v2PublicPathWithoutTrailingSlash = v2PublicPath.replace(/\/$/, '');
20
26
  const file = resolve(__dirname, '../../nocobase.conf.tpl');
21
27
  const data = readFileSync(file, 'utf-8');
22
28
  let otherLocation = '';
23
- if (process.env.APP_PUBLIC_PATH !== '/') {
24
- otherLocation = `location / {
29
+ if (appPublicPath !== '/') {
30
+ otherLocation = `location = /v2 {
31
+ return 302 ${v2PublicPath}$is_args$args;
32
+ }
33
+
34
+ location /v2/ {
35
+ return 302 ${appPublicPathWithoutTrailingSlash}$uri$is_args$args;
36
+ }
37
+
38
+ location / {
25
39
  alias ${posix.resolve(process.cwd())}/node_modules/@nocobase/app/dist/client/;
26
40
  try_files $uri $uri/ /index.html;
27
41
  }`;
28
42
  }
29
43
  const replaced = data
30
44
  .replace(/\{\{cwd\}\}/g, posix.resolve(process.cwd()))
31
- .replace(/\{\{publicPath\}\}/g, process.env.APP_PUBLIC_PATH)
45
+ .replace(/\{\{publicPath\}\}/g, appPublicPath)
46
+ .replace(/\{\{v2PublicPath\}\}/g, v2PublicPath)
47
+ .replace(/\{\{v2PublicPathNoTrailingSlash\}\}/g, v2PublicPathWithoutTrailingSlash)
32
48
  .replace(/\{\{apiPort\}\}/g, process.env.APP_PORT)
33
49
  .replace(/\{\{otherLocation\}\}/g, otherLocation);
34
50
  const targetFile = resolve(process.cwd(), 'storage', 'nocobase.conf');
@@ -42,74 +42,12 @@ module.exports = (cli) => {
42
42
  .option('-c, --client')
43
43
  .option('-s, --server')
44
44
  .option('--db-sync')
45
+ .option('--client-v2-only')
45
46
  .option('-i, --inspect [port]')
46
47
  .allowUnknownOption()
47
48
  .action(async (opts) => {
48
49
  checkDBDialect();
49
50
  await buildBundleStatusHtml();
50
- let subprocess;
51
- const runDevClient = () => {
52
- console.log('starting client', 1 * clientPort);
53
- subprocess = run('umi', ['dev'], {
54
- env: {
55
- ...process.env,
56
- stdio: 'inherit',
57
- shell: true,
58
- PORT: clientPort,
59
- APP_ROOT: `${APP_PACKAGE_ROOT}/client`,
60
- WEBSOCKET_URL:
61
- process.env.WEBSOCKET_URL ||
62
- (serverPort ? `ws://localhost:${serverPort}${process.env.WS_PATH}` : undefined),
63
- PROXY_TARGET_URL:
64
- process.env.PROXY_TARGET_URL || (serverPort ? `http://127.0.0.1:${serverPort}` : undefined),
65
- },
66
- });
67
- };
68
- const watcher = chokidar.watch('./storage/plugins/**/*', {
69
- cwd: process.cwd(),
70
- ignored: /(^|[\/\\])\../, // 忽略隐藏文件
71
- persistent: true,
72
- depth: 1, // 只监听第一层目录
73
- });
74
-
75
- await fs.promises.mkdir(path.dirname(process.env.WATCH_FILE), { recursive: true });
76
- let isReady = false;
77
-
78
- const restartClient = _.debounce(async () => {
79
- if (!isReady) return;
80
- generatePlugins();
81
- if (subprocess) {
82
- console.log('client restarting...');
83
- subprocess.cancel();
84
- let i = 0;
85
- while (true) {
86
- ++i;
87
- const result = await isPortReachable(clientPort);
88
- if (!result) {
89
- break;
90
- }
91
- await sleep(500);
92
- if (i > 10) {
93
- break;
94
- }
95
- }
96
- runDevClient();
97
- await fs.promises.writeFile(process.env.WATCH_FILE, `export const watchId = '${uid()}';`, 'utf-8');
98
- }
99
- }, 500);
100
-
101
- watcher
102
- .on('ready', () => {
103
- isReady = true;
104
- })
105
- .on('addDir', async (pathname) => {
106
- if (!isReady) return;
107
- restartClient();
108
- })
109
- .on('unlinkDir', async (pathname) => {
110
- if (!isReady) return;
111
- restartClient();
112
- });
113
51
 
114
52
  promptForTs();
115
53
  const { SERVER_TSCONFIG_PATH } = process.env;
@@ -127,30 +65,136 @@ module.exports = (cli) => {
127
65
  return;
128
66
  }
129
67
 
130
- const { port, client, server, inspect } = opts;
68
+ const { port, client, server, inspect, clientV2Only } = opts;
131
69
 
132
70
  if (port) {
133
71
  process.env.APP_PORT = opts.port;
134
72
  }
135
73
 
136
- const { APP_PORT } = process.env;
74
+ const APP_PORT = Number(process.env.APP_PORT);
137
75
 
138
76
  let clientPort = APP_PORT;
139
77
  let serverPort;
78
+ let clientV2Port = APP_PORT;
140
79
 
141
80
  nodeCheck();
142
-
143
81
  await postCheck(opts);
144
82
 
145
- if (server) {
83
+ const shouldRunClientV2 = clientV2Only || client || !server;
84
+ const shouldRunClient = !clientV2Only && (client || !server);
85
+ const shouldRunServer = !clientV2Only && (server || !client);
86
+
87
+ if (shouldRunServer && server) {
146
88
  serverPort = APP_PORT;
147
- } else if (!server && !client) {
89
+ } else if (shouldRunServer) {
148
90
  serverPort = await getPortPromise({
149
91
  port: 1 * clientPort + 1,
150
92
  });
151
93
  }
152
94
 
153
- if (server || !client) {
95
+ if (shouldRunClientV2 && !clientV2Only) {
96
+ clientV2Port = await getPortPromise({
97
+ port: 1 * clientPort + 2,
98
+ });
99
+ }
100
+
101
+ const runDevClientV2 = () => {
102
+ console.log('starting client-v2', 1 * clientV2Port);
103
+ run('rsbuild', ['dev', '--config', `${APP_PACKAGE_ROOT}/client-v2/rsbuild.config.ts`], {
104
+ env: {
105
+ ...process.env,
106
+ APP_V2_PORT: `${clientV2Port}`,
107
+ NODE_ENV: 'development',
108
+ RSPACK_HMR_CLIENT_PORT: `${clientV2Only ? clientV2Port : clientPort}`,
109
+ API_BASE_URL: process.env.API_BASE_URL || process.env.API_BASE_PATH,
110
+ API_CLIENT_STORAGE_PREFIX: process.env.API_CLIENT_STORAGE_PREFIX,
111
+ API_CLIENT_STORAGE_TYPE: process.env.API_CLIENT_STORAGE_TYPE,
112
+ API_CLIENT_SHARE_TOKEN: process.env.API_CLIENT_SHARE_TOKEN || 'false',
113
+ WEBSOCKET_URL:
114
+ process.env.WEBSOCKET_URL || (serverPort ? `ws://localhost:${serverPort}${process.env.WS_PATH}` : ''),
115
+ WS_PATH: process.env.WS_PATH,
116
+ ESM_CDN_BASE_URL: process.env.ESM_CDN_BASE_URL || 'https://esm.sh',
117
+ ESM_CDN_SUFFIX: process.env.ESM_CDN_SUFFIX || '',
118
+ PROXY_TARGET_URL:
119
+ process.env.PROXY_TARGET_URL || (serverPort ? `http://127.0.0.1:${serverPort}` : undefined),
120
+ },
121
+ });
122
+ };
123
+
124
+ if (clientV2Only) {
125
+ runDevClientV2();
126
+ return;
127
+ }
128
+
129
+ let subprocess;
130
+ const runDevClient = () => {
131
+ console.log('starting client', 1 * clientPort);
132
+ subprocess = run('umi', ['dev'], {
133
+ env: {
134
+ ...process.env,
135
+ stdio: 'inherit',
136
+ shell: true,
137
+ PORT: clientPort,
138
+ APP_ROOT: `${APP_PACKAGE_ROOT}/client`,
139
+ APP_V2_PORT: `${clientV2Port}`,
140
+ WEBSOCKET_URL:
141
+ process.env.WEBSOCKET_URL ||
142
+ (serverPort ? `ws://localhost:${serverPort}${process.env.WS_PATH}` : undefined),
143
+ PROXY_TARGET_URL:
144
+ process.env.PROXY_TARGET_URL || (serverPort ? `http://127.0.0.1:${serverPort}` : undefined),
145
+ },
146
+ });
147
+ };
148
+
149
+ if (shouldRunClient) {
150
+ const watcher = chokidar.watch('./storage/plugins/**/*', {
151
+ cwd: process.cwd(),
152
+ ignored: /(^|[\/\\])\../, // 忽略隐藏文件
153
+ persistent: true,
154
+ depth: 1, // 只监听第一层目录
155
+ });
156
+
157
+ await fs.promises.mkdir(path.dirname(process.env.WATCH_FILE), { recursive: true });
158
+ let isReady = false;
159
+
160
+ const restartClient = _.debounce(async () => {
161
+ if (!isReady) return;
162
+ generatePlugins();
163
+ if (subprocess) {
164
+ console.log('client restarting...');
165
+ subprocess.cancel();
166
+ let i = 0;
167
+ while (true) {
168
+ ++i;
169
+ const result = await isPortReachable(clientPort);
170
+ if (!result) {
171
+ break;
172
+ }
173
+ await sleep(500);
174
+ if (i > 10) {
175
+ break;
176
+ }
177
+ }
178
+ runDevClient();
179
+ await fs.promises.writeFile(process.env.WATCH_FILE, `export const watchId = '${uid()}';`, 'utf-8');
180
+ }
181
+ }, 500);
182
+
183
+ watcher
184
+ .on('ready', () => {
185
+ isReady = true;
186
+ })
187
+ .on('addDir', async () => {
188
+ if (!isReady) return;
189
+ restartClient();
190
+ })
191
+ .on('unlinkDir', async () => {
192
+ if (!isReady) return;
193
+ restartClient();
194
+ });
195
+ }
196
+
197
+ if (shouldRunServer) {
154
198
  console.log('starting server', serverPort);
155
199
 
156
200
  const filteredArgs = process.argv.filter(
@@ -193,8 +237,12 @@ module.exports = (cli) => {
193
237
  runDevServer();
194
238
  }
195
239
 
196
- if (client || !server) {
240
+ if (shouldRunClient) {
197
241
  runDevClient();
198
242
  }
243
+
244
+ if (shouldRunClientV2) {
245
+ runDevClientV2();
246
+ }
199
247
  });
200
248
  };
package/src/util.js CHANGED
@@ -282,6 +282,21 @@ function parseEnv(name) {
282
282
  }
283
283
  }
284
284
 
285
+ function resolvePublicPath(appPublicPath = '/') {
286
+ const normalized = String(appPublicPath || '/').trim() || '/';
287
+ const withLeadingSlash = normalized.startsWith('/') ? normalized : `/${normalized}`;
288
+ return withLeadingSlash.endsWith('/') ? withLeadingSlash : `${withLeadingSlash}/`;
289
+ }
290
+
291
+ exports.resolvePublicPath = resolvePublicPath;
292
+
293
+ function resolveV2PublicPath(appPublicPath = '/') {
294
+ const publicPath = resolvePublicPath(appPublicPath);
295
+ return `${publicPath.replace(/\/$/, '')}/v2/`;
296
+ }
297
+
298
+ exports.resolveV2PublicPath = resolveV2PublicPath;
299
+
285
300
  function buildIndexHtml(force = false) {
286
301
  const file = `${process.env.APP_PACKAGE_ROOT}/dist/client/index.html`;
287
302
  if (!fs.existsSync(file)) {