@nocobase/cli-v1 2.3.0-beta.2 → 2.3.0-beta.21
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
|
@@ -22,6 +22,11 @@ map $http_host $final_host {
|
|
|
22
22
|
"" $host;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
map $request_uri $legacy_file_app {
|
|
26
|
+
default "";
|
|
27
|
+
~[?&]__appName=(?<legacy_file_app_name>[A-Za-z0-9_-]+)(?:&|$) $legacy_file_app_name;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
server {
|
|
26
31
|
listen 80;
|
|
27
32
|
server_name _;
|
|
@@ -36,9 +41,25 @@ server {
|
|
|
36
41
|
gzip on;
|
|
37
42
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
|
38
43
|
|
|
44
|
+
location = /_nocobase_legacy_file_auth {
|
|
45
|
+
internal;
|
|
46
|
+
proxy_pass http://127.0.0.1:{{apiPort}}{{publicPath}}api/auth:checkLegacyFileAccess;
|
|
47
|
+
proxy_pass_request_body off;
|
|
48
|
+
proxy_set_header Content-Length "";
|
|
49
|
+
proxy_set_header Cookie $http_cookie;
|
|
50
|
+
proxy_set_header Authorization $http_authorization;
|
|
51
|
+
proxy_set_header X-App $legacy_file_app;
|
|
52
|
+
proxy_set_header X-Original-URI $request_uri;
|
|
53
|
+
proxy_set_header Host $final_host;
|
|
54
|
+
proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto;
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
location ~* ^{{publicPath}}storage/uploads/(.*\.(?:htm|html|pdf|svg|svgz|xht|xhtml|xml|xsl|xslt))$ {
|
|
40
58
|
alias {{cwd}}/storage/uploads/$1;
|
|
41
|
-
|
|
59
|
+
auth_request /_nocobase_legacy_file_auth;
|
|
60
|
+
auth_request_set $legacy_auth_set_cookie $upstream_http_set_cookie;
|
|
61
|
+
add_header Cache-Control "private, no-store" always;
|
|
62
|
+
add_header Set-Cookie $legacy_auth_set_cookie always;
|
|
42
63
|
add_header Content-Disposition "attachment" always;
|
|
43
64
|
add_header Content-Security-Policy "sandbox" always;
|
|
44
65
|
add_header X-Content-Type-Options "nosniff" always;
|
|
@@ -48,7 +69,10 @@ server {
|
|
|
48
69
|
|
|
49
70
|
location {{publicPath}}storage/uploads/ {
|
|
50
71
|
alias {{cwd}}/storage/uploads/;
|
|
51
|
-
|
|
72
|
+
auth_request /_nocobase_legacy_file_auth;
|
|
73
|
+
auth_request_set $legacy_auth_set_cookie $upstream_http_set_cookie;
|
|
74
|
+
add_header Cache-Control "private, no-store" always;
|
|
75
|
+
add_header Set-Cookie $legacy_auth_set_cookie always;
|
|
52
76
|
add_header Content-Security-Policy "sandbox" always;
|
|
53
77
|
add_header X-Content-Type-Options "nosniff" always;
|
|
54
78
|
access_log off;
|
|
@@ -57,6 +81,8 @@ server {
|
|
|
57
81
|
location ~* \.md$ {
|
|
58
82
|
default_type text/markdown;
|
|
59
83
|
add_header Content-Disposition "inline";
|
|
84
|
+
add_header Cache-Control "private, no-store" always;
|
|
85
|
+
add_header Set-Cookie $legacy_auth_set_cookie always;
|
|
60
86
|
add_header Content-Security-Policy "sandbox" always;
|
|
61
87
|
add_header X-Content-Type-Options "nosniff" always;
|
|
62
88
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/cli-v1",
|
|
3
|
-
"version": "2.3.0-beta.
|
|
3
|
+
"version": "2.3.0-beta.21",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
"nocobase-v1": "./bin/index.js"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@nocobase/cli": "2.3.0-beta.
|
|
12
|
+
"@nocobase/cli": "2.3.0-beta.21",
|
|
13
13
|
"@nocobase/license-kit": "^0.3.8",
|
|
14
|
-
"@nocobase/utils": "2.3.0-beta.
|
|
14
|
+
"@nocobase/utils": "2.3.0-beta.21",
|
|
15
15
|
"chalk": "^4.1.1",
|
|
16
|
+
"chokidar": "3.6.0",
|
|
16
17
|
"commander": "^9.2.0",
|
|
17
18
|
"deepmerge": "^4.3.1",
|
|
18
19
|
"dotenv": "^16.0.0",
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
"fast-glob": "^3.3.1",
|
|
21
22
|
"fs-extra": "^11.1.1",
|
|
22
23
|
"p-all": "3.0.0",
|
|
23
|
-
"pm2": "^
|
|
24
|
+
"pm2": "^7.0.0",
|
|
24
25
|
"portfinder": "^1.0.28",
|
|
25
26
|
"tar": "^7.4.3",
|
|
26
27
|
"tree-kill": "^1.2.2"
|
|
@@ -33,5 +34,5 @@
|
|
|
33
34
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
34
35
|
"directory": "packages/core/cli"
|
|
35
36
|
},
|
|
36
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "a2bdd27cc7bc8ced9ad395b3a7468fc1850eef4a"
|
|
37
38
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
/* eslint-env jest */
|
|
11
|
+
/* global vi */
|
|
12
|
+
|
|
13
|
+
const { putWithRetry, OSS_TIMEOUT, OSS_MAX_ATTEMPTS } = require('../commands/client')._test;
|
|
14
|
+
|
|
15
|
+
describe('cli-v1 client:upload', () => {
|
|
16
|
+
let warn;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
warn.mockRestore();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should raise the ali-oss response timeout above its 60s default', () => {
|
|
27
|
+
expect(OSS_TIMEOUT).toBeGreaterThan(60000);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return the result without retrying when the upload succeeds', async () => {
|
|
31
|
+
const put = vi.fn().mockResolvedValue({ name: 'a.js' });
|
|
32
|
+
|
|
33
|
+
await expect(putWithRetry({ put }, 'a.js', '/tmp/a.js')).resolves.toEqual({ name: 'a.js' });
|
|
34
|
+
|
|
35
|
+
expect(put).toHaveBeenCalledTimes(1);
|
|
36
|
+
expect(put).toHaveBeenCalledWith('a.js', '/tmp/a.js');
|
|
37
|
+
expect(warn).not.toHaveBeenCalled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should retry a transient timeout and resolve', async () => {
|
|
41
|
+
const put = vi
|
|
42
|
+
.fn()
|
|
43
|
+
.mockRejectedValueOnce(new Error('Response timeout for 60000ms'))
|
|
44
|
+
.mockResolvedValue({ name: 'a.js' });
|
|
45
|
+
|
|
46
|
+
await expect(putWithRetry({ put }, 'a.js', '/tmp/a.js')).resolves.toEqual({ name: 'a.js' });
|
|
47
|
+
|
|
48
|
+
expect(put).toHaveBeenCalledTimes(2);
|
|
49
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should give up and rethrow after the attempt limit', async () => {
|
|
53
|
+
const error = new Error('Response timeout for 60000ms');
|
|
54
|
+
const put = vi.fn().mockRejectedValue(error);
|
|
55
|
+
|
|
56
|
+
await expect(putWithRetry({ put }, 'a.js', '/tmp/a.js')).rejects.toThrow(error);
|
|
57
|
+
|
|
58
|
+
expect(put).toHaveBeenCalledTimes(OSS_MAX_ATTEMPTS);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
import { mkdtempSync, mkdirSync, rmSync } from 'node:fs';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import chokidar from 'chokidar';
|
|
14
|
+
import { describe, expect, test } from 'vitest';
|
|
15
|
+
import packageJson from '../../package.json';
|
|
16
|
+
|
|
17
|
+
describe('cli-v1 runtime dependencies', () => {
|
|
18
|
+
test('declares chokidar directly for production command initialization', () => {
|
|
19
|
+
expect(packageJson.dependencies.chokidar).toBe('3.6.0');
|
|
20
|
+
expect(packageJson.devDependencies).not.toHaveProperty('chokidar');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('keeps glob-based plugin directory watching compatible', async () => {
|
|
24
|
+
const directory = mkdtempSync(path.join(os.tmpdir(), 'nocobase-cli-watch-'));
|
|
25
|
+
const pluginDirectory = path.join(directory, 'plugins');
|
|
26
|
+
mkdirSync(pluginDirectory);
|
|
27
|
+
const watcher = chokidar.watch(`${pluginDirectory}/**/*`, { ignoreInitial: true });
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await new Promise<void>((resolve, reject) => {
|
|
31
|
+
watcher.once('ready', resolve);
|
|
32
|
+
watcher.once('error', reject);
|
|
33
|
+
});
|
|
34
|
+
const added = new Promise<string>((resolve, reject) => {
|
|
35
|
+
watcher.once('addDir', resolve);
|
|
36
|
+
watcher.once('error', reject);
|
|
37
|
+
});
|
|
38
|
+
const addedDirectory = path.join(pluginDirectory, 'example-plugin');
|
|
39
|
+
mkdirSync(addedDirectory);
|
|
40
|
+
|
|
41
|
+
expect(path.resolve(await added)).toBe(addedDirectory);
|
|
42
|
+
} finally {
|
|
43
|
+
await watcher.close();
|
|
44
|
+
rmSync(directory, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
}, 10000);
|
|
47
|
+
});
|
package/src/commands/client.js
CHANGED
|
@@ -63,6 +63,31 @@ async function writeActiveVersion(version) {
|
|
|
63
63
|
return activeVersionFile;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// 客户端资源里有体积较大的 bundle,ali-oss 默认的 60s 响应超时在 CI 上不够用。
|
|
67
|
+
const OSS_TIMEOUT = 300000;
|
|
68
|
+
// 单个文件偶发超时不应该让整次上传前功尽弃,做有限重试。
|
|
69
|
+
const OSS_MAX_ATTEMPTS = 3;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 上传单个文件,对偶发失败重试
|
|
73
|
+
* @param {Client} client - OSS 客户端实例
|
|
74
|
+
* @param {string} ossKey - OSS 对象名
|
|
75
|
+
* @param {string} filePath - 本地文件路径
|
|
76
|
+
*/
|
|
77
|
+
async function putWithRetry(client, ossKey, filePath) {
|
|
78
|
+
for (let attempt = 1; ; attempt++) {
|
|
79
|
+
try {
|
|
80
|
+
return await client.put(ossKey, filePath);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (attempt >= OSS_MAX_ATTEMPTS) {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
console.warn(chalk.yellow(`Retrying ${ossKey} (${attempt}/${OSS_MAX_ATTEMPTS - 1}): ${error.message}`));
|
|
86
|
+
await new Promise((_resolve) => setTimeout(_resolve, attempt * 2000));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
66
91
|
/**
|
|
67
92
|
* 递归上传目录到 OSS
|
|
68
93
|
* @param {Client} client - OSS 客户端实例
|
|
@@ -96,7 +121,7 @@ async function uploadDirectoryToOSS(client, localDir, ossPrefix = '') {
|
|
|
96
121
|
// 上传文件
|
|
97
122
|
const ossKey = ossPrefix ? `${ossPrefix}/${file}` : file;
|
|
98
123
|
try {
|
|
99
|
-
await client
|
|
124
|
+
await putWithRetry(client, ossKey, filePath);
|
|
100
125
|
// console.log(chalk.green(`Uploaded: ${ossKey}`));
|
|
101
126
|
uploadedCount++;
|
|
102
127
|
} catch (error) {
|
|
@@ -179,6 +204,7 @@ module.exports = (cli) => {
|
|
|
179
204
|
accessKeySecret: process.env.CDN_ALI_OSS_ACCESS_KEY_SECRET,
|
|
180
205
|
bucket: process.env.CDN_ALI_OSS_BUCKET,
|
|
181
206
|
region: process.env.CDN_ALI_OSS_REGION,
|
|
207
|
+
timeout: OSS_TIMEOUT,
|
|
182
208
|
});
|
|
183
209
|
|
|
184
210
|
if (!(await fs.exists(target))) {
|
|
@@ -198,3 +224,9 @@ module.exports = (cli) => {
|
|
|
198
224
|
}
|
|
199
225
|
});
|
|
200
226
|
};
|
|
227
|
+
|
|
228
|
+
module.exports._test = {
|
|
229
|
+
putWithRetry,
|
|
230
|
+
OSS_TIMEOUT,
|
|
231
|
+
OSS_MAX_ATTEMPTS,
|
|
232
|
+
};
|