@nocobase/cli 3.0.0-alpha.1 → 3.0.0-alpha.11
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/dist/commands/api/resource/create.js +11 -2
- package/dist/commands/api/swagger/get.js +96 -0
- package/dist/commands/api/swagger/index.js +20 -0
- package/dist/commands/api/swagger/list.js +92 -0
- package/dist/commands/env/add.js +6 -0
- package/dist/commands/env/update.js +13 -0
- package/dist/commands/init.js +38 -0
- package/dist/commands/install.js +18 -62
- package/dist/commands/portal/config.js +16 -5
- package/dist/commands/portal/create.js +17 -26
- package/dist/commands/portal/destroy.js +28 -6
- package/dist/commands/portal/dev.js +2 -0
- package/dist/commands/portal/list.js +5 -5
- package/dist/commands/portal/pull.js +32 -3
- package/dist/lib/api-client.js +35 -9
- package/dist/lib/app-client-entry-mode.js +28 -0
- package/dist/lib/app-managed-resources.js +2 -0
- package/dist/lib/auth-store.js +52 -1
- package/dist/lib/bootstrap.js +3 -1
- package/dist/lib/browser.js +29 -0
- package/dist/lib/env-auth.js +2 -36
- package/dist/lib/env-command-config.js +1 -0
- package/dist/lib/env-config.js +10 -2
- package/dist/lib/env-portal-config.js +30 -0
- package/dist/lib/env-proxy.js +25 -4
- package/dist/lib/generated-command.js +81 -0
- package/dist/lib/managed-env-file.js +67 -13
- package/dist/lib/managed-init-env.js +0 -2
- package/dist/lib/plugin-import.js +30 -8
- package/dist/lib/portal-build-html.js +27 -0
- package/dist/lib/portal-config.js +6 -20
- package/dist/lib/portal-configure.js +49 -56
- package/dist/lib/portal-create.js +96 -14
- package/dist/lib/portal-deploy.js +30 -47
- package/dist/lib/portal-destroy.js +34 -20
- package/dist/lib/portal-dev.js +6 -7
- package/dist/lib/portal-env-files.js +2 -1
- package/dist/lib/portal-info.js +2 -5
- package/dist/lib/portal-list.js +20 -26
- package/dist/lib/portal-path-safety.js +76 -0
- package/dist/lib/portal-source.js +227 -58
- package/dist/lib/prompt-catalog-core.js +2 -2
- package/dist/lib/prompt-catalog-terminal.js +4 -5
- package/dist/lib/prompt-web-ui.js +12 -6
- package/dist/lib/resource-command.js +18 -2
- package/dist/lib/resource-request.js +8 -0
- package/dist/lib/run-npm.js +68 -4
- package/dist/lib/runtime-generator.js +28 -1
- package/dist/lib/swagger-command.js +52 -0
- package/dist/locale/en-US.json +81 -15
- package/dist/locale/zh-CN.json +81 -15
- package/package.json +2 -2
|
@@ -1,10 +1,19 @@
|
|
|
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
|
+
*/
|
|
1
9
|
import { Command } from '@oclif/core';
|
|
2
10
|
import { buildCreateArgs, createFlags, runResourceCommand } from '../../../lib/resource-command.js';
|
|
3
11
|
export default class ResourceCreate extends Command {
|
|
4
|
-
static summary = 'Create
|
|
5
|
-
static description = 'Create
|
|
12
|
+
static summary = 'Create one or more records in a resource';
|
|
13
|
+
static description = 'Create records in a generic resource. Pass record content through --values as a JSON object, or as a JSON array of objects to create multiple records in a single request.';
|
|
6
14
|
static examples = [
|
|
7
15
|
`<%= config.bin %> <%= command.id %> --resource users --values '{"nickname":"Ada"}'`,
|
|
16
|
+
`<%= config.bin %> <%= command.id %> --resource users --values '[{"nickname":"Ada"},{"nickname":"Grace"}]'`,
|
|
8
17
|
`<%= config.bin %> <%= command.id %> --resource posts.comments --source-id 1 --values '{"content":"Hello"}'`,
|
|
9
18
|
];
|
|
10
19
|
static flags = createFlags;
|
|
@@ -0,0 +1,96 @@
|
|
|
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
|
+
import { promises as fs } from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { Command, Flags } from '@oclif/core';
|
|
12
|
+
import { translateCli } from '../../../lib/cli-locale.js';
|
|
13
|
+
import { executeSwaggerRequest, swaggerRequestFlags } from '../../../lib/swagger-command.js';
|
|
14
|
+
import { renderTable } from '../../../lib/ui.js';
|
|
15
|
+
const swaggerText = (key, values, fallback) => translateCli(`commands.swagger.${key}`, values, { fallback });
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
18
|
+
}
|
|
19
|
+
function unwrapData(value) {
|
|
20
|
+
if (!isRecord(value) || typeof value.openapi === 'string') {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
return Object.prototype.hasOwnProperty.call(value, 'data') ? value.data : value;
|
|
24
|
+
}
|
|
25
|
+
function normalizeDocument(value) {
|
|
26
|
+
const document = unwrapData(value);
|
|
27
|
+
if (!isRecord(document) ||
|
|
28
|
+
typeof document.openapi !== 'string' ||
|
|
29
|
+
!isRecord(document.info) ||
|
|
30
|
+
!isRecord(document.paths)) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return document;
|
|
34
|
+
}
|
|
35
|
+
export default class SwaggerGet extends Command {
|
|
36
|
+
static summary = 'Get a NocoBase OpenAPI document';
|
|
37
|
+
static examples = [
|
|
38
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
39
|
+
'<%= config.bin %> <%= command.id %> --namespace collections/orders --json',
|
|
40
|
+
'<%= config.bin %> <%= command.id %> --namespace plugins/ai --output ./openapi/ai.json',
|
|
41
|
+
];
|
|
42
|
+
static flags = {
|
|
43
|
+
...swaggerRequestFlags,
|
|
44
|
+
namespace: Flags.string({
|
|
45
|
+
aliases: ['ns'],
|
|
46
|
+
description: 'Document namespace, such as core, plugins/ai, or collections/orders',
|
|
47
|
+
}),
|
|
48
|
+
output: Flags.string({
|
|
49
|
+
char: 'o',
|
|
50
|
+
description: 'Write the OpenAPI document to a file',
|
|
51
|
+
}),
|
|
52
|
+
'json-output': Flags.boolean({
|
|
53
|
+
char: 'j',
|
|
54
|
+
aliases: ['json'],
|
|
55
|
+
description: 'Print the complete OpenAPI document as JSON',
|
|
56
|
+
default: false,
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
async run() {
|
|
60
|
+
const { flags } = await this.parse(SwaggerGet);
|
|
61
|
+
const namespace = flags.namespace?.trim() || undefined;
|
|
62
|
+
const response = await executeSwaggerRequest(this, flags, '/swagger:get', { ns: namespace });
|
|
63
|
+
if (!response) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const details = JSON.stringify(response.data, null, 2);
|
|
68
|
+
this.error(response.status === 404
|
|
69
|
+
? swaggerText('errors.pluginDisabled', undefined, 'The API documentation plugin is not enabled. Enable it before requesting Swagger documents.')
|
|
70
|
+
: swaggerText('errors.requestFailed', { status: response.status, details }, `Swagger request failed with status ${response.status}\n${details}`));
|
|
71
|
+
}
|
|
72
|
+
const document = normalizeDocument(response.data);
|
|
73
|
+
if (!document) {
|
|
74
|
+
this.error(swaggerText('errors.invalidDocument', undefined, 'swagger:get returned an invalid OpenAPI document.'));
|
|
75
|
+
}
|
|
76
|
+
const json = `${JSON.stringify(document, null, 2)}\n`;
|
|
77
|
+
if (flags.output) {
|
|
78
|
+
const outputPath = path.resolve(flags.output);
|
|
79
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
80
|
+
await fs.writeFile(outputPath, json);
|
|
81
|
+
this.log(swaggerText('messages.saved', { output: outputPath }, `Saved Swagger document to ${outputPath}.`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (flags['json-output']) {
|
|
85
|
+
this.log(json.trimEnd());
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.log(renderTable([swaggerText('table.field', undefined, 'Field'), swaggerText('table.value', undefined, 'Value')], [
|
|
89
|
+
[swaggerText('fields.namespace', undefined, 'Namespace'), namespace ?? 'all'],
|
|
90
|
+
[swaggerText('fields.title', undefined, 'Title'), document.info.title ?? ''],
|
|
91
|
+
[swaggerText('fields.version', undefined, 'Version'), document.info.version ?? ''],
|
|
92
|
+
[swaggerText('fields.openapi', undefined, 'OpenAPI'), document.openapi],
|
|
93
|
+
[swaggerText('fields.paths', undefined, 'Paths'), String(Object.keys(document.paths).length)],
|
|
94
|
+
]));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
import { Command, loadHelpClass } from '@oclif/core';
|
|
10
|
+
export default class Swagger extends Command {
|
|
11
|
+
static summary = 'Inspect the current NocoBase OpenAPI documentation';
|
|
12
|
+
async run() {
|
|
13
|
+
await this.parse(Swagger);
|
|
14
|
+
const Help = await loadHelpClass(this.config);
|
|
15
|
+
await new Help(this.config, this.config.pjson.oclif.helpOptions ?? this.config.pjson.helpOptions).showHelp([
|
|
16
|
+
this.id ?? 'api:swagger',
|
|
17
|
+
...this.argv,
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { translateCli } from '../../../lib/cli-locale.js';
|
|
11
|
+
import { executeSwaggerRequest, swaggerRequestFlags } from '../../../lib/swagger-command.js';
|
|
12
|
+
import { renderTable } from '../../../lib/ui.js';
|
|
13
|
+
const swaggerText = (key, values, fallback) => translateCli(`commands.swagger.${key}`, values, { fallback });
|
|
14
|
+
function isRecord(value) {
|
|
15
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
16
|
+
}
|
|
17
|
+
function unwrapData(value) {
|
|
18
|
+
return isRecord(value) && Object.prototype.hasOwnProperty.call(value, 'data') ? value.data : value;
|
|
19
|
+
}
|
|
20
|
+
function readNamespace(url) {
|
|
21
|
+
try {
|
|
22
|
+
return new URL(url, 'http://localhost').searchParams.get('ns') || 'all';
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return 'all';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function normalizeDestinations(value) {
|
|
29
|
+
const data = unwrapData(value);
|
|
30
|
+
if (!Array.isArray(data)) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const destinations = [];
|
|
34
|
+
for (const item of data) {
|
|
35
|
+
if (!isRecord(item) || typeof item.name !== 'string' || typeof item.url !== 'string') {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
destinations.push({
|
|
39
|
+
name: item.name,
|
|
40
|
+
namespace: readNamespace(item.url),
|
|
41
|
+
url: item.url,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return destinations;
|
|
45
|
+
}
|
|
46
|
+
export default class SwaggerList extends Command {
|
|
47
|
+
static summary = 'List available NocoBase OpenAPI document namespaces';
|
|
48
|
+
static examples = [
|
|
49
|
+
'<%= config.bin %> <%= command.id %>',
|
|
50
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
51
|
+
'<%= config.bin %> <%= command.id %> --env dev --yes --json',
|
|
52
|
+
];
|
|
53
|
+
static flags = {
|
|
54
|
+
...swaggerRequestFlags,
|
|
55
|
+
'json-output': Flags.boolean({
|
|
56
|
+
char: 'j',
|
|
57
|
+
aliases: ['json'],
|
|
58
|
+
description: 'Print namespaces as JSON',
|
|
59
|
+
default: false,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
async run() {
|
|
63
|
+
const { flags } = await this.parse(SwaggerList);
|
|
64
|
+
const response = await executeSwaggerRequest(this, flags, '/swagger:getUrls');
|
|
65
|
+
if (!response) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const details = JSON.stringify(response.data, null, 2);
|
|
70
|
+
this.error(response.status === 404
|
|
71
|
+
? swaggerText('errors.pluginDisabled', undefined, 'The API documentation plugin is not enabled. Enable it before requesting Swagger documents.')
|
|
72
|
+
: swaggerText('errors.requestFailed', { status: response.status, details }, `Swagger request failed with status ${response.status}\n${details}`));
|
|
73
|
+
}
|
|
74
|
+
const destinations = normalizeDestinations(response.data);
|
|
75
|
+
if (!destinations) {
|
|
76
|
+
this.error(swaggerText('errors.invalidDestinations', undefined, 'swagger:getUrls returned an invalid destination list.'));
|
|
77
|
+
}
|
|
78
|
+
if (flags['json-output']) {
|
|
79
|
+
this.log(JSON.stringify(destinations, null, 2));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!destinations.length) {
|
|
83
|
+
this.log(swaggerText('messages.empty', undefined, 'No Swagger document namespaces are available.'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.log(renderTable([
|
|
87
|
+
swaggerText('table.name', undefined, 'Name'),
|
|
88
|
+
swaggerText('table.namespace', undefined, 'Namespace'),
|
|
89
|
+
swaggerText('table.url', undefined, 'URL'),
|
|
90
|
+
], destinations.map((item) => [item.name, item.namespace, item.url])));
|
|
91
|
+
}
|
|
92
|
+
}
|
package/dist/commands/env/add.js
CHANGED
|
@@ -11,6 +11,7 @@ import { setCurrentEnv, upsertEnv } from '../../lib/auth-store.js';
|
|
|
11
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
12
|
import { ENV_BOOLEAN_CONFIG_FLAG_MAP, ENV_STRING_CONFIG_FLAG_MAP } from '../../lib/env-command-config.js';
|
|
13
13
|
import { buildStoredEnvConfig } from '../../lib/env-config.js';
|
|
14
|
+
import { PUBLIC_APP_CLIENT_ENTRY_MODES } from '../../lib/app-client-entry-mode.js';
|
|
14
15
|
import { runPromptCatalog, } from '../../lib/prompt-catalog.js';
|
|
15
16
|
import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, localeText, } from '../../lib/cli-locale.js';
|
|
16
17
|
import { validateApiBaseUrl } from '../../lib/prompt-validators.js';
|
|
@@ -172,6 +173,11 @@ export default class EnvAdd extends Command {
|
|
|
172
173
|
hidden: true,
|
|
173
174
|
description: 'Docker env file saved with this env',
|
|
174
175
|
}),
|
|
176
|
+
'app-client-entry-mode': Flags.string({
|
|
177
|
+
hidden: true,
|
|
178
|
+
description: 'UI entry mode saved with this env',
|
|
179
|
+
options: [...PUBLIC_APP_CLIENT_ENTRY_MODES],
|
|
180
|
+
}),
|
|
175
181
|
'app-port': Flags.string({
|
|
176
182
|
hidden: true,
|
|
177
183
|
description: 'Application HTTP port saved with this env',
|
|
@@ -16,6 +16,8 @@ import { appendDiagnosticLogPath } from '../../lib/cli-entry-error.js';
|
|
|
16
16
|
import { getActiveCommandLogFile } from '../../lib/command-log.js';
|
|
17
17
|
import { ENV_BOOLEAN_CONFIG_FLAG_MAP, ENV_STRING_CONFIG_FLAG_MAP } from '../../lib/env-command-config.js';
|
|
18
18
|
import { buildStoredEnvConfig } from '../../lib/env-config.js';
|
|
19
|
+
import { PUBLIC_APP_CLIENT_ENTRY_MODES } from '../../lib/app-client-entry-mode.js';
|
|
20
|
+
import { upsertManagedEnvFileValues } from '../../lib/managed-env-file.js';
|
|
19
21
|
import { validateApiBaseUrl } from '../../lib/prompt-validators.js';
|
|
20
22
|
import { failTask, printInfo, printVerbose, printWarningBlock, setVerboseMode, startTask, stopTask, succeedTask, } from '../../lib/ui.js';
|
|
21
23
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -35,6 +37,7 @@ const UPDATE_STRING_FLAGS = [
|
|
|
35
37
|
'app-public-path',
|
|
36
38
|
'cdn-base-url',
|
|
37
39
|
'env-file',
|
|
40
|
+
'app-client-entry-mode',
|
|
38
41
|
'app-port',
|
|
39
42
|
'app-key',
|
|
40
43
|
'timezone',
|
|
@@ -68,6 +71,7 @@ const APP_RESTART_FIELDS = new Set([
|
|
|
68
71
|
'storage-path',
|
|
69
72
|
'app-public-path',
|
|
70
73
|
'env-file',
|
|
74
|
+
'app-client-entry-mode',
|
|
71
75
|
'app-port',
|
|
72
76
|
'app-key',
|
|
73
77
|
'timezone',
|
|
@@ -255,6 +259,10 @@ export default class EnvUpdate extends Command {
|
|
|
255
259
|
hidden: true,
|
|
256
260
|
description: 'Saved Docker --env-file path for this env',
|
|
257
261
|
}),
|
|
262
|
+
'app-client-entry-mode': Flags.string({
|
|
263
|
+
description: 'Saved UI entry mode for this env',
|
|
264
|
+
options: [...PUBLIC_APP_CLIENT_ENTRY_MODES],
|
|
265
|
+
}),
|
|
258
266
|
'app-port': Flags.string({
|
|
259
267
|
description: 'Saved application HTTP port for this env',
|
|
260
268
|
}),
|
|
@@ -423,6 +431,11 @@ export default class EnvUpdate extends Command {
|
|
|
423
431
|
startTask(`Saving env config: ${envName}`);
|
|
424
432
|
try {
|
|
425
433
|
await replaceEnvConfig(envName, nextConfig, { scope: resolveDefaultConfigScope() });
|
|
434
|
+
if (providedFields.has('app-client-entry-mode') && nextConfig.appClientEntryMode) {
|
|
435
|
+
await upsertManagedEnvFileValues(envName, nextConfig, {
|
|
436
|
+
APP_CLIENT_ENTRY_MODE: nextConfig.appClientEntryMode,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
426
439
|
succeedTask(`Saved env config for "${envName}".`);
|
|
427
440
|
}
|
|
428
441
|
catch (error) {
|
package/dist/commands/init.js
CHANGED
|
@@ -13,6 +13,7 @@ import { existsSync } from 'node:fs';
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { stdin as stdinStream, stdout as stdoutStream } from 'node:process';
|
|
15
15
|
import { getEnv, upsertEnv } from "../lib/auth-store.js";
|
|
16
|
+
import { defaultAppClientEntryModeForDownloadVersion, normalizePublicAppClientEntryMode, PUBLIC_APP_CLIENT_ENTRY_MODES, } from '../lib/app-client-entry-mode.js';
|
|
16
17
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
17
18
|
import { applyCliLocale, localeText, translateCli } from "../lib/cli-locale.js";
|
|
18
19
|
import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRelativePath } from '../lib/cli-home.js';
|
|
@@ -106,9 +107,23 @@ function resolveInitDownloadVersion(results) {
|
|
|
106
107
|
}
|
|
107
108
|
return preset;
|
|
108
109
|
}
|
|
110
|
+
function resolveInitAppClientEntryMode(results, explicitValue) {
|
|
111
|
+
return (normalizePublicAppClientEntryMode(explicitValue) ??
|
|
112
|
+
normalizePublicAppClientEntryMode(results.appClientEntryMode) ??
|
|
113
|
+
defaultAppClientEntryModeForDownloadVersion(resolveInitDownloadVersion(results)));
|
|
114
|
+
}
|
|
109
115
|
function initVersionPromptValue(version) {
|
|
110
116
|
return version === 'latest' || version === 'beta' || version === 'alpha' ? version : 'other';
|
|
111
117
|
}
|
|
118
|
+
export function defaultInitDownloadVersionForCliVersion(cliVersion) {
|
|
119
|
+
if (/-alpha(?:[.-]|$)/i.test(cliVersion)) {
|
|
120
|
+
return 'alpha';
|
|
121
|
+
}
|
|
122
|
+
if (/-beta(?:[.-]|$)/i.test(cliVersion)) {
|
|
123
|
+
return 'beta';
|
|
124
|
+
}
|
|
125
|
+
return 'latest';
|
|
126
|
+
}
|
|
112
127
|
function yesInitialValue(def, fallback) {
|
|
113
128
|
if ('yesInitialValue' in def && def.yesInitialValue !== undefined) {
|
|
114
129
|
return String(def.yesInitialValue);
|
|
@@ -409,8 +424,15 @@ Prompt modes:
|
|
|
409
424
|
installAccessToken: installConnectionAccessTokenPrompt,
|
|
410
425
|
};
|
|
411
426
|
buildPromptCatalog(flags, options) {
|
|
427
|
+
const downloadVersion = defaultInitDownloadVersionForCliVersion(String(this.config.pjson?.version ?? '').trim());
|
|
428
|
+
const versionPrompt = Init.prompts.version;
|
|
412
429
|
const prompts = {
|
|
413
430
|
...Init.prompts,
|
|
431
|
+
version: {
|
|
432
|
+
...versionPrompt,
|
|
433
|
+
initialValue: downloadVersion,
|
|
434
|
+
yesInitialValue: downloadVersion,
|
|
435
|
+
},
|
|
414
436
|
installApiBaseUrl: createInstallConnectionApiBaseUrlPrompt(options.defaultApiHost),
|
|
415
437
|
};
|
|
416
438
|
if (flags['skip-auth']) {
|
|
@@ -474,6 +496,10 @@ Prompt modes:
|
|
|
474
496
|
description: 'Skip installing NocoBase AI coding skills during init',
|
|
475
497
|
default: false,
|
|
476
498
|
}),
|
|
499
|
+
'app-client-entry-mode': Flags.string({
|
|
500
|
+
description: 'UI entry mode for this app env: modern-only, modern-default, or legacy-default',
|
|
501
|
+
options: [...PUBLIC_APP_CLIENT_ENTRY_MODES],
|
|
502
|
+
}),
|
|
477
503
|
'ui-host': Flags.string({
|
|
478
504
|
description: 'Browser-accessible host for the --ui setup page URL (default: 127.0.0.1)',
|
|
479
505
|
}),
|
|
@@ -874,6 +900,9 @@ Prompt modes:
|
|
|
874
900
|
if (flags['app-public-path'] !== undefined && String(flags['app-public-path']).trim() !== '') {
|
|
875
901
|
preset.appPublicPath = String(flags['app-public-path']).trim();
|
|
876
902
|
}
|
|
903
|
+
if (flags['app-client-entry-mode'] !== undefined && String(flags['app-client-entry-mode']).trim() !== '') {
|
|
904
|
+
preset.appClientEntryMode = String(flags['app-client-entry-mode']).trim();
|
|
905
|
+
}
|
|
877
906
|
if (flags['root-username'] !== undefined) {
|
|
878
907
|
preset.rootUsername = String(flags['root-username'] ?? '').trim();
|
|
879
908
|
}
|
|
@@ -1011,6 +1040,7 @@ Prompt modes:
|
|
|
1011
1040
|
const existingEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
|
|
1012
1041
|
const appPort = String(results.appPort ?? '').trim();
|
|
1013
1042
|
const appPublicPath = String(results.appPublicPath ?? '').trim();
|
|
1043
|
+
const appClientEntryMode = resolveInitAppClientEntryMode(results, flags['app-client-entry-mode']);
|
|
1014
1044
|
const source = String(results.source ?? '').trim();
|
|
1015
1045
|
const version = resolveInitDownloadVersion(results);
|
|
1016
1046
|
const dockerRegistry = String(results.dockerRegistry ?? '').trim();
|
|
@@ -1084,6 +1114,7 @@ Prompt modes:
|
|
|
1084
1114
|
...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
|
|
1085
1115
|
...(appPort ? { appPort } : {}),
|
|
1086
1116
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
1117
|
+
...(appClientEntryMode ? { appClientEntryMode } : {}),
|
|
1087
1118
|
...(appKey ? { appKey } : {}),
|
|
1088
1119
|
...(timeZone ? { timezone: timeZone } : {}),
|
|
1089
1120
|
...(!skipDownload && results.devDependencies !== undefined
|
|
@@ -1212,6 +1243,13 @@ Prompt modes:
|
|
|
1212
1243
|
if (appPublicPath) {
|
|
1213
1244
|
argv.push('--app-public-path', appPublicPath);
|
|
1214
1245
|
}
|
|
1246
|
+
const appClientEntryMode = normalizePublicAppClientEntryMode(flags['app-client-entry-mode']) ||
|
|
1247
|
+
(results.setupMode === 'install-new' && results.hasNocobase === undefined
|
|
1248
|
+
? resolveInitAppClientEntryMode(results)
|
|
1249
|
+
: undefined);
|
|
1250
|
+
if (appClientEntryMode) {
|
|
1251
|
+
argv.push('--app-client-entry-mode', appClientEntryMode);
|
|
1252
|
+
}
|
|
1215
1253
|
if (flags.force) {
|
|
1216
1254
|
argv.push('--force');
|
|
1217
1255
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -25,6 +25,7 @@ import { commandOutput, commandSucceeds, ensureDockerDaemonRunning, run, runNoco
|
|
|
25
25
|
import { printInfo, printStage, printVerbose, printWarning, setVerboseMode } from '../lib/ui.js';
|
|
26
26
|
import { omitKeys, upperFirst } from "../lib/object-utils.js";
|
|
27
27
|
import { clearEnvRootSetup, getEnv, setCurrentEnv, upsertEnv } from '../lib/auth-store.js';
|
|
28
|
+
import { defaultAppClientEntryModeForDownloadVersion, normalizePublicAppClientEntryMode, PUBLIC_APP_CLIENT_ENTRY_MODES, } from '../lib/app-client-entry-mode.js';
|
|
28
29
|
import { buildStoredEnvConfig } from '../lib/env-config.js';
|
|
29
30
|
import { resolveDockerEnvFileArg } from "../lib/docker-env-file.js";
|
|
30
31
|
import { startDockerLogFollower } from '../lib/docker-log-stream.js';
|
|
@@ -54,10 +55,7 @@ const DEFAULT_INSTALL_ROOT_EMAIL = 'admin@nocobase.com';
|
|
|
54
55
|
const DEFAULT_INSTALL_ROOT_PASSWORD = 'admin123';
|
|
55
56
|
const DEFAULT_INSTALL_ROOT_NICKNAME = 'Super Admin';
|
|
56
57
|
const DEFAULT_INSTALL_API_HOST = '127.0.0.1';
|
|
57
|
-
const DEFAULT_INSTALL_PORTAL_TYPE = 'ai';
|
|
58
|
-
const DEFAULT_INSTALL_PORTAL_NAME = 'main';
|
|
59
58
|
const DEFAULT_INSTALL_PORTAL_TEMPLATE = '@nocobase/portal-template-default';
|
|
60
|
-
const INSTALL_PORTAL_TYPES = ['no-code', 'ai'];
|
|
61
59
|
function toOptionalPromptString(value) {
|
|
62
60
|
const text = String(value ?? '').trim();
|
|
63
61
|
return text || undefined;
|
|
@@ -185,9 +183,6 @@ function defaultBuiltinDbImageForDialect(value, options) {
|
|
|
185
183
|
function defaultDbDatabaseForDialect(value) {
|
|
186
184
|
return String(value ?? '').trim() === 'kingbase' ? 'kingbase' : DEFAULT_INSTALL_DB_DATABASE;
|
|
187
185
|
}
|
|
188
|
-
function isAiMode(values) {
|
|
189
|
-
return String(values.portalType ?? DEFAULT_INSTALL_PORTAL_TYPE).trim() === 'ai';
|
|
190
|
-
}
|
|
191
186
|
function supportsDbSchemaPrompt(value) {
|
|
192
187
|
const dialect = String(value ?? '').trim();
|
|
193
188
|
return dialect === 'postgres' || dialect === 'kingbase';
|
|
@@ -412,15 +407,12 @@ export default class Install extends Command {
|
|
|
412
407
|
'app-public-path': Flags.string({
|
|
413
408
|
description: 'Public path for the local app, for example / or /console/',
|
|
414
409
|
}),
|
|
415
|
-
'
|
|
416
|
-
description: '
|
|
417
|
-
options: [...
|
|
418
|
-
}),
|
|
419
|
-
'portal-name': Flags.string({
|
|
420
|
-
description: 'Initial portal name',
|
|
410
|
+
'app-client-entry-mode': Flags.string({
|
|
411
|
+
description: 'UI entry mode for this app env: modern-only, modern-default, or legacy-default',
|
|
412
|
+
options: [...PUBLIC_APP_CLIENT_ENTRY_MODES],
|
|
421
413
|
}),
|
|
422
414
|
'portal-template': Flags.string({
|
|
423
|
-
description: '
|
|
415
|
+
description: 'Template npm package or local path for the default AI Portal "main"',
|
|
424
416
|
}),
|
|
425
417
|
'root-username': Flags.string({
|
|
426
418
|
description: 'Initial admin username for the installed app',
|
|
@@ -531,39 +523,12 @@ export default class Install extends Command {
|
|
|
531
523
|
yesInitialValue: '/',
|
|
532
524
|
validate: validateAppPublicPath,
|
|
533
525
|
},
|
|
534
|
-
portalType: {
|
|
535
|
-
type: 'select',
|
|
536
|
-
message: installText('prompts.portalType.message'),
|
|
537
|
-
options: [
|
|
538
|
-
{
|
|
539
|
-
value: 'no-code',
|
|
540
|
-
label: installText('prompts.portalType.noCodeLabel'),
|
|
541
|
-
hint: installText('prompts.portalType.noCodeHint'),
|
|
542
|
-
},
|
|
543
|
-
{
|
|
544
|
-
value: 'ai',
|
|
545
|
-
label: installText('prompts.portalType.aiLabel'),
|
|
546
|
-
hint: installText('prompts.portalType.aiHint'),
|
|
547
|
-
},
|
|
548
|
-
],
|
|
549
|
-
initialValue: DEFAULT_INSTALL_PORTAL_TYPE,
|
|
550
|
-
yesInitialValue: DEFAULT_INSTALL_PORTAL_TYPE,
|
|
551
|
-
required: true,
|
|
552
|
-
},
|
|
553
|
-
portalName: {
|
|
554
|
-
type: 'text',
|
|
555
|
-
message: installText('prompts.portalName.message'),
|
|
556
|
-
placeholder: DEFAULT_INSTALL_PORTAL_NAME,
|
|
557
|
-
initialValue: DEFAULT_INSTALL_PORTAL_NAME,
|
|
558
|
-
yesInitialValue: DEFAULT_INSTALL_PORTAL_NAME,
|
|
559
|
-
required: true,
|
|
560
|
-
},
|
|
561
526
|
portalTemplate: {
|
|
562
527
|
type: 'text',
|
|
563
528
|
message: installText('prompts.portalTemplate.message'),
|
|
564
529
|
placeholder: DEFAULT_INSTALL_PORTAL_TEMPLATE,
|
|
530
|
+
initialValue: DEFAULT_INSTALL_PORTAL_TEMPLATE,
|
|
565
531
|
yesInitialValue: DEFAULT_INSTALL_PORTAL_TEMPLATE,
|
|
566
|
-
hidden: (values) => !isAiMode(values),
|
|
567
532
|
required: true,
|
|
568
533
|
},
|
|
569
534
|
};
|
|
@@ -825,16 +790,10 @@ export default class Install extends Command {
|
|
|
825
790
|
preset.appPublicPath = v;
|
|
826
791
|
}
|
|
827
792
|
}
|
|
828
|
-
if (flags['
|
|
829
|
-
const v =
|
|
830
|
-
if (v) {
|
|
831
|
-
preset.portalType = v;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
if (flags['portal-name'] !== undefined) {
|
|
835
|
-
const v = String(flags['portal-name'] ?? '').trim();
|
|
793
|
+
if (flags['app-client-entry-mode'] !== undefined) {
|
|
794
|
+
const v = normalizePublicAppClientEntryMode(flags['app-client-entry-mode']);
|
|
836
795
|
if (v) {
|
|
837
|
-
preset.
|
|
796
|
+
preset.appClientEntryMode = v;
|
|
838
797
|
}
|
|
839
798
|
}
|
|
840
799
|
if (flags['portal-template'] !== undefined) {
|
|
@@ -934,8 +893,7 @@ export default class Install extends Command {
|
|
|
934
893
|
'appPort',
|
|
935
894
|
'storagePath',
|
|
936
895
|
'appPublicPath',
|
|
937
|
-
'
|
|
938
|
-
'portalName',
|
|
896
|
+
'appClientEntryMode',
|
|
939
897
|
'portalTemplate',
|
|
940
898
|
]);
|
|
941
899
|
}
|
|
@@ -1186,8 +1144,6 @@ export default class Install extends Command {
|
|
|
1186
1144
|
const rootPassword = Install.toOptionalPromptString(config.rootPassword);
|
|
1187
1145
|
const rootNickname = Install.toOptionalPromptString(config.rootNickname);
|
|
1188
1146
|
const lang = Install.toOptionalPromptString(config.lang);
|
|
1189
|
-
const portalType = Install.toOptionalPromptString(config.portalType);
|
|
1190
|
-
const portalName = Install.toOptionalPromptString(config.portalName);
|
|
1191
1147
|
const portalTemplate = Install.toOptionalPromptString(config.portalTemplate);
|
|
1192
1148
|
const auth = config.auth;
|
|
1193
1149
|
const savedAuthType = Install.toOptionalPromptString(config.authType) ?? Install.toOptionalPromptString(auth?.type);
|
|
@@ -1198,8 +1154,6 @@ export default class Install extends Command {
|
|
|
1198
1154
|
...(appPort ? { appPort } : {}),
|
|
1199
1155
|
...(storagePath ? { storagePath } : {}),
|
|
1200
1156
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
1201
|
-
...(portalType ? { portalType } : {}),
|
|
1202
|
-
...(portalName ? { portalName } : {}),
|
|
1203
1157
|
...(portalTemplate ? { portalTemplate } : {}),
|
|
1204
1158
|
...(hookScript ? { hookScript } : {}),
|
|
1205
1159
|
};
|
|
@@ -1523,8 +1477,6 @@ export default class Install extends Command {
|
|
|
1523
1477
|
rootEmail: String(params.rootResults.rootEmail ?? ''),
|
|
1524
1478
|
rootPassword: String(params.rootResults.rootPassword ?? ''),
|
|
1525
1479
|
rootNickname: String(params.rootResults.rootNickname ?? ''),
|
|
1526
|
-
portalType: String(params.appResults.portalType ?? ''),
|
|
1527
|
-
portalName: String(params.appResults.portalName ?? ''),
|
|
1528
1480
|
portalTemplate: String(params.appResults.portalTemplate ?? ''),
|
|
1529
1481
|
}, options);
|
|
1530
1482
|
}
|
|
@@ -1874,6 +1826,7 @@ export default class Install extends Command {
|
|
|
1874
1826
|
const extractClientAssets = resolveExtractClientAssetsDefaultEnabled(process.env.NOCOBASE_EXTRACT_CLIENT_ASSETS);
|
|
1875
1827
|
const appKey = Install.resolveManagedAppKey(params.appResults.appKey);
|
|
1876
1828
|
const appPublicPath = Install.toOptionalPromptString(params.appResults.appPublicPath);
|
|
1829
|
+
const appClientEntryMode = Install.toOptionalPromptString(params.appResults.appClientEntryMode);
|
|
1877
1830
|
const timeZone = Install.resolveManagedTimeZone(params.appResults.timeZone);
|
|
1878
1831
|
const containerName = Install.buildDockerAppContainerName(params.envName, params.dockerContainerPrefix ?? params.workspaceName);
|
|
1879
1832
|
const configuredEnvFile = String(params.appResults.envFile ?? '').trim();
|
|
@@ -1901,6 +1854,7 @@ export default class Install extends Command {
|
|
|
1901
1854
|
}
|
|
1902
1855
|
args.push('-e', `APP_KEY=${appKey}`, '-e', `DB_DIALECT=${dbDialect}`, '-e', `DB_HOST=${dbHost}`, '-e', `DB_PORT=${dbPort}`, '-e', `DB_DATABASE=${dbDatabase}`, '-e', `DB_USER=${dbUser}`, '-e', `DB_PASSWORD=${dbPassword}`, '-e', `TZ=${timeZone}`, '-v', `${storagePath}:/app/nocobase/storage`);
|
|
1903
1856
|
pushOptionalEnvArg(args, 'APP_PUBLIC_PATH', appPublicPath);
|
|
1857
|
+
pushOptionalEnvArg(args, 'APP_CLIENT_ENTRY_MODE', appClientEntryMode);
|
|
1904
1858
|
pushOptionalEnvArg(args, 'DB_SCHEMA', dbSchema);
|
|
1905
1859
|
pushOptionalEnvArg(args, 'DB_TABLE_PREFIX', dbTablePrefix);
|
|
1906
1860
|
pushOptionalEnvArg(args, 'DB_UNDERSCORED', dbUnderscored);
|
|
@@ -2161,6 +2115,7 @@ export default class Install extends Command {
|
|
|
2161
2115
|
}),
|
|
2162
2116
|
};
|
|
2163
2117
|
setOptionalEnvVar(env, 'APP_PUBLIC_PATH', Install.toOptionalPromptString(params.appResults.appPublicPath));
|
|
2118
|
+
setOptionalEnvVar(env, 'APP_CLIENT_ENTRY_MODE', Install.toOptionalPromptString(params.appResults.appClientEntryMode));
|
|
2164
2119
|
setOptionalEnvVar(env, 'DB_SCHEMA', optionalEnvString(params.dbResults.dbSchema));
|
|
2165
2120
|
setOptionalEnvVar(env, 'DB_TABLE_PREFIX', optionalEnvString(params.dbResults.dbTablePrefix));
|
|
2166
2121
|
setOptionalEnvVar(env, 'DB_UNDERSCORED', optionalEnvBoolean(params.dbResults.dbUnderscored));
|
|
@@ -2402,8 +2357,7 @@ export default class Install extends Command {
|
|
|
2402
2357
|
const appRootPath = Install.toOptionalPromptString(params.appResults.appRootPath);
|
|
2403
2358
|
const storagePath = Install.toOptionalPromptString(params.appResults.storagePath);
|
|
2404
2359
|
const appPublicPath = Install.toOptionalPromptString(params.appResults.appPublicPath);
|
|
2405
|
-
const
|
|
2406
|
-
const portalName = Install.toOptionalPromptString(params.appResults.portalName);
|
|
2360
|
+
const appClientEntryMode = Install.toOptionalPromptString(params.appResults.appClientEntryMode);
|
|
2407
2361
|
const portalTemplate = Install.toOptionalPromptString(params.appResults.portalTemplate);
|
|
2408
2362
|
const derivedAppRootPath = appPath ? deriveConfiguredSourcePath(appPath) : undefined;
|
|
2409
2363
|
const derivedStoragePath = appPath ? deriveConfiguredStoragePath(appPath) : undefined;
|
|
@@ -2437,10 +2391,9 @@ export default class Install extends Command {
|
|
|
2437
2391
|
appPort,
|
|
2438
2392
|
...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
|
|
2439
2393
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
2394
|
+
...(appClientEntryMode ? { appClientEntryMode } : {}),
|
|
2440
2395
|
...(envFile ? { envFile } : {}),
|
|
2441
2396
|
lang: params.appResults.lang,
|
|
2442
|
-
portalType,
|
|
2443
|
-
portalName,
|
|
2444
2397
|
portalTemplate,
|
|
2445
2398
|
appKey: params.appResults.appKey,
|
|
2446
2399
|
timezone: params.appResults.timeZone,
|
|
@@ -2512,6 +2465,9 @@ export default class Install extends Command {
|
|
|
2512
2465
|
};
|
|
2513
2466
|
downloadOpts.yes = yes;
|
|
2514
2467
|
const downloadResults = await runPromptCatalog(Download.prompts, downloadOpts);
|
|
2468
|
+
appResults.appClientEntryMode =
|
|
2469
|
+
normalizePublicAppClientEntryMode(appResults.appClientEntryMode) ??
|
|
2470
|
+
defaultAppClientEntryModeForDownloadVersion(downloadResultsValue(downloadResults, 'version'));
|
|
2515
2471
|
if (parsed['skip-download']) {
|
|
2516
2472
|
delete downloadResults.outputDir;
|
|
2517
2473
|
delete downloadResults.replace;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Args, Command, Flags } from '@oclif/core';
|
|
10
|
-
import { getCurrentEnvName, getEnv } from '../../lib/auth-store.js';
|
|
10
|
+
import { getCurrentEnvName, getEnv, setEnvPortalPath } from '../../lib/auth-store.js';
|
|
11
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
12
|
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
13
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
@@ -17,6 +17,7 @@ const portalConfigureText = (key, values, fallback) => translateCli(`commands.po
|
|
|
17
17
|
export default class PortalConfig extends Command {
|
|
18
18
|
static summary = 'Update portal source configuration';
|
|
19
19
|
static examples = [
|
|
20
|
+
'<%= config.bin %> <%= command.id %> customer --path ./portals/customer',
|
|
20
21
|
'<%= config.bin %> <%= command.id %> customer --source-storage nocobase',
|
|
21
22
|
'<%= config.bin %> <%= command.id %> customer --source-storage git --git-repo git@github.com:nocobase/customer-portal.git',
|
|
22
23
|
'<%= config.bin %> <%= command.id %> customer --git-branch main --git-path portals/customer',
|
|
@@ -41,6 +42,9 @@ export default class PortalConfig extends Command {
|
|
|
41
42
|
description: 'Where portal source code is managed',
|
|
42
43
|
options: ['nocobase', 'git'],
|
|
43
44
|
}),
|
|
45
|
+
path: Flags.string({
|
|
46
|
+
description: 'Portal development workspace directory',
|
|
47
|
+
}),
|
|
44
48
|
'git-repo': Flags.string({
|
|
45
49
|
description: 'Git repository URL used when --source-storage=git',
|
|
46
50
|
}),
|
|
@@ -79,10 +83,17 @@ export default class PortalConfig extends Command {
|
|
|
79
83
|
gitRepo: flags['git-repo'],
|
|
80
84
|
gitBranch: flags['git-branch'],
|
|
81
85
|
gitPath: flags['git-path'],
|
|
86
|
+
sourcePath: flags.path,
|
|
82
87
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
if (flags.path) {
|
|
89
|
+
await setEnvPortalPath(envName, result.portal, result.portalDir, { scope });
|
|
90
|
+
}
|
|
91
|
+
printSuccess(portalConfigureText('messages.updated', { portal: result.portal }, `Portal "${result.portal}" configuration updated.`));
|
|
92
|
+
if (result.pathUpdated) {
|
|
93
|
+
printInfo(portalConfigureText('messages.pathUpdated', { portalDir: result.portalDir }, `Development path: ${result.portalDir}`));
|
|
94
|
+
}
|
|
95
|
+
if (result.config) {
|
|
96
|
+
printInfo(portalConfigureText('messages.remoteSynced', undefined, 'Remote portal record: synced'));
|
|
97
|
+
}
|
|
87
98
|
}
|
|
88
99
|
}
|