@nocobase/cli 2.3.0-beta.3 → 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.
Files changed (56) hide show
  1. package/dist/commands/api/swagger/get.js +96 -0
  2. package/dist/commands/api/swagger/index.js +20 -0
  3. package/dist/commands/api/swagger/list.js +92 -0
  4. package/dist/commands/config/set.js +1 -0
  5. package/dist/commands/env/add.js +6 -0
  6. package/dist/commands/env/update.js +13 -0
  7. package/dist/commands/init.js +51 -5
  8. package/dist/commands/install.js +60 -3
  9. package/dist/commands/portal/config.js +99 -0
  10. package/dist/commands/portal/create.js +96 -0
  11. package/dist/commands/portal/deploy.js +81 -0
  12. package/dist/commands/portal/destroy.js +126 -0
  13. package/dist/commands/portal/dev.js +73 -0
  14. package/dist/commands/portal/index.js +20 -0
  15. package/dist/commands/portal/info.js +82 -0
  16. package/dist/commands/portal/list.js +98 -0
  17. package/dist/commands/portal/pull.js +113 -0
  18. package/dist/commands/portal/push.js +79 -0
  19. package/dist/commands/source/dev.js +1 -1
  20. package/dist/lib/api-client.js +35 -9
  21. package/dist/lib/app-client-entry-mode.js +28 -0
  22. package/dist/lib/app-managed-resources.js +2 -0
  23. package/dist/lib/auth-store.js +55 -2
  24. package/dist/lib/bootstrap.js +3 -1
  25. package/dist/lib/cli-config.js +20 -1
  26. package/dist/lib/env-auth.js +2 -36
  27. package/dist/lib/env-command-config.js +1 -0
  28. package/dist/lib/env-config.js +11 -0
  29. package/dist/lib/env-portal-config.js +30 -0
  30. package/dist/lib/env-proxy.js +154 -7
  31. package/dist/lib/managed-env-file.js +119 -9
  32. package/dist/lib/managed-init-env.js +4 -1
  33. package/dist/lib/portal-build-html.js +27 -0
  34. package/dist/lib/portal-command-env.js +31 -0
  35. package/dist/lib/portal-config.js +119 -0
  36. package/dist/lib/portal-configure.js +110 -0
  37. package/dist/lib/portal-create.js +515 -0
  38. package/dist/lib/portal-deploy.js +266 -0
  39. package/dist/lib/portal-destroy.js +114 -0
  40. package/dist/lib/portal-dev.js +78 -0
  41. package/dist/lib/portal-env-files.js +54 -0
  42. package/dist/lib/portal-info.js +28 -0
  43. package/dist/lib/portal-list.js +205 -0
  44. package/dist/lib/portal-path-safety.js +76 -0
  45. package/dist/lib/portal-source.js +692 -0
  46. package/dist/lib/prompt-catalog-core.js +2 -2
  47. package/dist/lib/prompt-catalog-terminal.js +4 -5
  48. package/dist/lib/prompt-web-ui.js +12 -6
  49. package/dist/lib/proxy-caddy.js +2 -0
  50. package/dist/lib/proxy-nginx.js +1 -0
  51. package/dist/lib/run-npm.js +85 -20
  52. package/dist/lib/swagger-command.js +52 -0
  53. package/dist/lib/ui.js +28 -1
  54. package/dist/locale/en-US.json +245 -1
  55. package/dist/locale/zh-CN.json +245 -1
  56. package/package.json +5 -2
@@ -0,0 +1,27 @@
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 { readFile, writeFile } from 'node:fs/promises';
10
+ import path from 'node:path';
11
+ const BUILD_HTML_SCRIPT_PATH = path.join('scripts', 'build-html.mjs');
12
+ const BUILD_HTML_ENV_FILES_PATTERN = /return\s+\[\s*["']\.env["']\s*,\s*["']\.env\.local["']\s*,\s*`\.env\.\$\{mode\}`\s*,\s*`\.env\.\$\{mode\}\.local`\s*\]\.map\(\s*\(?file\)?\s*=>\s*path\.join\(rootDir,\s*file\)\s*\);/m;
13
+ const BUILD_HTML_ENV_ONLY_REPLACEMENT = 'return [".env"].map((file) => path.join(rootDir, file));';
14
+ export async function ensurePortalBuildHtmlReadsEnvOnly(portalDir) {
15
+ const scriptPath = path.join(portalDir, BUILD_HTML_SCRIPT_PATH);
16
+ let content;
17
+ try {
18
+ content = await readFile(scriptPath, 'utf-8');
19
+ }
20
+ catch {
21
+ return;
22
+ }
23
+ const nextContent = content.replace(BUILD_HTML_ENV_FILES_PATTERN, BUILD_HTML_ENV_ONLY_REPLACEMENT);
24
+ if (nextContent !== content) {
25
+ await writeFile(scriptPath, nextContent, 'utf-8');
26
+ }
27
+ }
@@ -0,0 +1,31 @@
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
+ const PORTAL_COMMAND_BASE_ENV_KEYS = [
10
+ 'PATH',
11
+ 'Path',
12
+ 'PATHEXT',
13
+ 'SystemRoot',
14
+ 'WINDIR',
15
+ 'ComSpec',
16
+ 'HOME',
17
+ 'USERPROFILE',
18
+ 'TMPDIR',
19
+ 'TEMP',
20
+ 'TMP',
21
+ ];
22
+ export function buildPortalCommandEnv(env = {}) {
23
+ const out = {};
24
+ for (const key of PORTAL_COMMAND_BASE_ENV_KEYS) {
25
+ const value = process.env[key];
26
+ if (value) {
27
+ out[key] = value;
28
+ }
29
+ }
30
+ return { ...out, ...env };
31
+ }
@@ -0,0 +1,119 @@
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 path from 'node:path';
10
+ import { executeApiRequest } from './api-client.js';
11
+ import { translateCli } from './cli-locale.js';
12
+ export const DEFAULT_PORTAL_GIT_PATH = '.';
13
+ const portalConfigText = (key, values, fallback) => translateCli(`commands.portalConfig.${key}`, values, { fallback });
14
+ const UPDATE_PORTAL_OPERATION = {
15
+ method: 'POST',
16
+ pathTemplate: '/multiPortals:update',
17
+ hasBody: true,
18
+ bodyRequired: true,
19
+ parameters: [
20
+ {
21
+ name: 'filter',
22
+ flagName: 'filter',
23
+ in: 'query',
24
+ type: 'object',
25
+ required: true,
26
+ },
27
+ ],
28
+ };
29
+ function trimValue(value) {
30
+ return String(value ?? '').trim();
31
+ }
32
+ function readObject(value) {
33
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
34
+ }
35
+ function validatePortalSourceStorage(value) {
36
+ const sourceStorage = trimValue(value) || 'nocobase';
37
+ if (sourceStorage === 'nocobase' || sourceStorage === 'git') {
38
+ return sourceStorage;
39
+ }
40
+ throw new Error(portalConfigText('errors.invalidSourceStorage', { value: sourceStorage }, `Invalid source storage "${sourceStorage}". Use "nocobase" or "git".`));
41
+ }
42
+ function isFullGitRemoteUrl(value) {
43
+ return /^(?:https?:\/\/|ssh:\/\/|file:\/\/|git@[^:]+:).+/.test(value);
44
+ }
45
+ function validateGitPath(value) {
46
+ const gitPath = trimValue(value);
47
+ if (!gitPath || path.isAbsolute(gitPath) || gitPath.split(/[\\/]+/).includes('..')) {
48
+ throw new Error(portalConfigText('errors.invalidGitPath', { value }, '--git-path must be a relative path inside the Git repository.'));
49
+ }
50
+ return gitPath.replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
51
+ }
52
+ export function buildPortalConfig(options) {
53
+ const sourceStorage = validatePortalSourceStorage(options.sourceStorage ?? options.existingConfig?.sourceStorage);
54
+ const hasGitOption = Boolean(trimValue(options.gitRepo) || trimValue(options.gitBranch) || trimValue(options.gitPath));
55
+ if (sourceStorage === 'nocobase') {
56
+ if (hasGitOption) {
57
+ throw new Error(portalConfigText('errors.gitOptionsForNocobaseStorage', undefined, '--git-repo, --git-branch, and --git-path can only be used with --source-storage git.'));
58
+ }
59
+ return { sourceStorage };
60
+ }
61
+ const repo = trimValue(options.gitRepo) || options.existingConfig?.git?.repo || '';
62
+ if (!repo) {
63
+ throw new Error(portalConfigText('errors.gitRepoRequired', undefined, '--git-repo is required when --source-storage is git.'));
64
+ }
65
+ if (!isFullGitRemoteUrl(repo)) {
66
+ throw new Error(portalConfigText('errors.gitRepoInvalid', undefined, '--git-repo must be a full Git remote URL.'));
67
+ }
68
+ return {
69
+ sourceStorage,
70
+ git: {
71
+ repo,
72
+ branch: trimValue(options.gitBranch) || options.existingConfig?.git?.branch || 'main',
73
+ path: validateGitPath(trimValue(options.gitPath) || options.existingConfig?.git?.path || DEFAULT_PORTAL_GIT_PATH),
74
+ },
75
+ };
76
+ }
77
+ export function buildPortalConfigFromOptions(options, portal) {
78
+ const sourceOptions = readObject(options);
79
+ const git = readObject(sourceOptions.git);
80
+ return buildPortalConfig({
81
+ portal,
82
+ sourceStorage: trimValue(sourceOptions.sourceStorage) || 'nocobase',
83
+ gitRepo: trimValue(git.repo),
84
+ gitBranch: trimValue(git.branch),
85
+ gitPath: trimValue(git.path),
86
+ });
87
+ }
88
+ export function mergePortalConfigIntoOptions(config, currentOptions) {
89
+ const nextOptions = {
90
+ ...(currentOptions ?? {}),
91
+ sourceStorage: config.sourceStorage,
92
+ };
93
+ if (config.sourceStorage === 'git') {
94
+ nextOptions.git = config.git;
95
+ }
96
+ else {
97
+ delete nextOptions.git;
98
+ }
99
+ return nextOptions;
100
+ }
101
+ export async function syncPortalConfigToRemote(options) {
102
+ const apiRequest = options.apiRequest ?? executeApiRequest;
103
+ const response = await apiRequest({
104
+ cliVersion: options.cliVersion ?? '',
105
+ envName: options.envName,
106
+ flags: {
107
+ filter: {
108
+ portalName: options.portal,
109
+ },
110
+ body: JSON.stringify({
111
+ options: mergePortalConfigIntoOptions(options.config, options.currentOptions),
112
+ }),
113
+ },
114
+ operation: UPDATE_PORTAL_OPERATION,
115
+ });
116
+ if (!response.ok) {
117
+ throw new Error(portalConfigText('errors.updateFailed', { status: response.status, details: JSON.stringify(response.data, null, 2) }, `Portal config update failed with status ${response.status}\n${JSON.stringify(response.data, null, 2)}`));
118
+ }
119
+ }
@@ -0,0 +1,110 @@
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 { translateCli } from './cli-locale.js';
10
+ import { resolvePortalAppContext, resolvePortalSourcePath, resolveSavedPortalSourcePath, validatePortalSlug, } from './portal-create.js';
11
+ import { buildPortalConfig, buildPortalConfigFromOptions, syncPortalConfigToRemote, } from './portal-config.js';
12
+ import { findPortalListItem } from './portal-info.js';
13
+ import { listPortalWorkspaces } from './portal-list.js';
14
+ const portalConfigureText = (key, values, fallback) => translateCli(`commands.portalConfigure.${key}`, values, { fallback });
15
+ function trimValue(value) {
16
+ return String(value ?? '').trim();
17
+ }
18
+ function hasSourceConfigurationChange(options) {
19
+ return Boolean(options.sourceStorage !== undefined ||
20
+ trimValue(options.gitRepo) ||
21
+ trimValue(options.gitBranch) ||
22
+ trimValue(options.gitPath));
23
+ }
24
+ function hasPathConfigurationChange(options) {
25
+ return Boolean(trimValue(options.sourcePath));
26
+ }
27
+ function buildConfigFromRemoteOptions(params) {
28
+ if (params.options && Object.keys(params.options).length > 0) {
29
+ return buildPortalConfigFromOptions(params.options, params.portal);
30
+ }
31
+ if (!params.sourceStorage && !params.gitRepo && !params.gitBranch && !params.gitPath) {
32
+ return undefined;
33
+ }
34
+ return buildPortalConfig({
35
+ portal: params.portal,
36
+ sourceStorage: params.sourceStorage,
37
+ gitRepo: params.gitRepo,
38
+ gitBranch: params.gitBranch,
39
+ gitPath: params.gitPath,
40
+ });
41
+ }
42
+ export async function configurePortalWorkspace(options) {
43
+ const hasSourceChange = hasSourceConfigurationChange(options);
44
+ const hasPathChange = hasPathConfigurationChange(options);
45
+ if (!hasSourceChange && !hasPathChange) {
46
+ throw new Error(portalConfigureText('errors.noChanges', undefined, 'No portal configuration changes were provided. Pass --path, --source-storage, or a --git-* flag.'));
47
+ }
48
+ const portal = validatePortalSlug(options.portal);
49
+ const portalDir = hasPathChange
50
+ ? resolvePortalSourcePath(portal, options.sourcePath)
51
+ : resolveSavedPortalSourcePath(options.env, portal) ?? '';
52
+ const appContext = await resolvePortalAppContext(options);
53
+ const { app } = appContext;
54
+ let config;
55
+ let remoteSynced = false;
56
+ const list = await listPortalWorkspaces({
57
+ env: options.env,
58
+ envName: options.envName,
59
+ cliVersion: options.cliVersion,
60
+ apiRequest: options.apiRequest,
61
+ appContext,
62
+ });
63
+ const remoteItem = findPortalListItem(list.items, portal);
64
+ if (!remoteItem) {
65
+ throw new Error(portalConfigureText('errors.notFound', { portal }, `Portal "${portal}" was not found. Run \`nb portal list\` to see available portals.`));
66
+ }
67
+ if (!hasSourceChange) {
68
+ return {
69
+ app,
70
+ portal,
71
+ portalDir,
72
+ config: undefined,
73
+ remoteSynced: false,
74
+ pathUpdated: hasPathChange,
75
+ };
76
+ }
77
+ const existingConfig = buildConfigFromRemoteOptions({
78
+ portal,
79
+ options: remoteItem.options,
80
+ sourceStorage: remoteItem.sourceStorage,
81
+ gitRepo: remoteItem.gitRepo,
82
+ gitBranch: remoteItem.gitBranch,
83
+ gitPath: remoteItem.gitPath,
84
+ });
85
+ config = buildPortalConfig({
86
+ portal,
87
+ sourceStorage: options.sourceStorage,
88
+ gitRepo: options.gitRepo,
89
+ gitBranch: options.gitBranch,
90
+ gitPath: options.gitPath,
91
+ existingConfig,
92
+ });
93
+ await syncPortalConfigToRemote({
94
+ portal,
95
+ config,
96
+ currentOptions: remoteItem.options,
97
+ envName: options.envName,
98
+ cliVersion: options.cliVersion,
99
+ apiRequest: options.apiRequest,
100
+ });
101
+ remoteSynced = true;
102
+ return {
103
+ app,
104
+ portal,
105
+ portalDir,
106
+ config,
107
+ remoteSynced,
108
+ pathUpdated: hasPathChange,
109
+ };
110
+ }