@nocobase/cli 2.1.0-alpha.4 → 2.1.0-alpha.40
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/LICENSE.txt +107 -0
- package/README.md +393 -19
- package/README.zh-CN.md +343 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +135 -0
- package/bin/session-env.js +39 -0
- package/dist/commands/api/resource/create.js +15 -0
- package/dist/commands/api/resource/destroy.js +15 -0
- package/dist/commands/api/resource/get.js +15 -0
- package/dist/commands/api/resource/index.js +20 -0
- package/dist/commands/api/resource/list.js +16 -0
- package/dist/commands/api/resource/query.js +15 -0
- package/dist/commands/api/resource/update.js +15 -0
- package/dist/commands/app/down.js +301 -0
- package/dist/commands/app/logs.js +114 -0
- package/dist/commands/app/restart.js +158 -0
- package/dist/commands/app/start.js +305 -0
- package/dist/commands/app/stop.js +115 -0
- package/dist/commands/app/upgrade.js +636 -0
- package/dist/commands/backup/create.js +147 -0
- package/dist/commands/backup/index.js +20 -0
- package/dist/commands/backup/restore.js +105 -0
- package/{src/cli.js → dist/commands/build.js} +4 -11
- package/dist/commands/config/delete.js +30 -0
- package/dist/commands/config/get.js +29 -0
- package/dist/commands/config/index.js +20 -0
- package/dist/commands/config/list.js +29 -0
- package/dist/commands/config/set.js +35 -0
- package/dist/commands/db/check.js +240 -0
- package/dist/commands/db/logs.js +85 -0
- package/dist/commands/db/ps.js +60 -0
- package/dist/commands/db/shared.js +96 -0
- package/dist/commands/db/start.js +71 -0
- package/dist/commands/db/stop.js +71 -0
- package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
- package/{src/commands/locale/react-js-cron/index.js → dist/commands/down.js} +3 -8
- package/dist/commands/download.js +13 -0
- package/dist/commands/env/add.js +366 -0
- package/dist/commands/env/auth.js +130 -0
- package/dist/commands/env/current.js +21 -0
- package/dist/commands/env/info.js +157 -0
- package/dist/commands/env/list.js +44 -0
- package/dist/commands/env/remove.js +84 -0
- package/dist/commands/env/shared.js +158 -0
- package/dist/commands/env/status.js +90 -0
- package/dist/commands/env/update.js +74 -0
- package/dist/commands/env/use.js +38 -0
- package/dist/commands/examples/prompts-stages.js +150 -0
- package/dist/commands/examples/prompts-test.js +181 -0
- package/dist/commands/init.js +1092 -0
- package/dist/commands/install.js +2378 -0
- package/dist/commands/license/activate.js +360 -0
- package/dist/commands/license/env.js +94 -0
- package/dist/commands/license/generate-id.js +108 -0
- package/dist/commands/license/id.js +70 -0
- package/dist/commands/license/index.js +20 -0
- package/dist/commands/license/plugins/clean.js +115 -0
- package/dist/commands/license/plugins/index.js +20 -0
- package/dist/commands/license/plugins/list.js +64 -0
- package/dist/commands/license/plugins/shared.js +325 -0
- package/dist/commands/license/plugins/sync.js +285 -0
- package/dist/commands/license/shared.js +423 -0
- package/dist/commands/license/status.js +64 -0
- package/dist/commands/logs.js +12 -0
- package/dist/commands/plugin/disable.js +86 -0
- package/dist/commands/plugin/enable.js +86 -0
- package/dist/commands/plugin/list.js +82 -0
- package/dist/commands/pm/disable.js +12 -0
- package/dist/commands/pm/enable.js +12 -0
- package/dist/commands/pm/list.js +12 -0
- package/dist/commands/restart.js +12 -0
- package/dist/commands/scaffold/migration.js +38 -0
- package/dist/commands/scaffold/plugin.js +37 -0
- package/dist/commands/self/check.js +71 -0
- package/dist/commands/self/index.js +20 -0
- package/dist/commands/self/update.js +95 -0
- package/dist/commands/session/id.js +24 -0
- package/dist/commands/session/remove.js +57 -0
- package/dist/commands/session/setup.js +62 -0
- package/dist/commands/skills/check.js +69 -0
- package/dist/commands/skills/index.js +20 -0
- package/dist/commands/skills/install.js +80 -0
- package/dist/commands/skills/remove.js +80 -0
- package/dist/commands/skills/update.js +87 -0
- package/dist/commands/source/build.js +58 -0
- package/dist/commands/source/dev.js +182 -0
- package/dist/commands/source/download.js +880 -0
- package/dist/commands/source/publish.js +109 -0
- package/dist/commands/source/registry/logs.js +70 -0
- package/dist/commands/source/registry/start.js +57 -0
- package/dist/commands/source/registry/status.js +33 -0
- package/dist/commands/source/registry/stop.js +48 -0
- package/dist/commands/source/test.js +477 -0
- package/dist/commands/start.js +12 -0
- package/dist/commands/stop.js +12 -0
- package/dist/commands/test.js +12 -0
- package/dist/commands/upgrade.js +12 -0
- package/dist/commands/v1.js +210 -0
- package/dist/generated/command-registry.js +133 -0
- package/dist/help/runtime-help.js +23 -0
- package/dist/lib/api-client.js +329 -0
- package/dist/lib/app-health.js +126 -0
- package/dist/lib/app-managed-resources.js +316 -0
- package/dist/lib/app-runtime.js +180 -0
- package/dist/lib/auth-store.js +368 -0
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +403 -0
- package/dist/lib/build-config.js +18 -0
- package/dist/lib/builtin-db.js +86 -0
- package/dist/lib/cli-config.js +176 -0
- package/dist/lib/cli-home.js +47 -0
- package/dist/lib/cli-locale.js +129 -0
- package/dist/lib/command-discovery.js +39 -0
- package/dist/lib/db-connection-check.js +158 -0
- package/dist/lib/docker-env-file.js +52 -0
- package/dist/lib/docker-image.js +37 -0
- package/dist/lib/env-auth.js +873 -0
- package/dist/lib/env-config.js +94 -0
- package/dist/lib/env-guard.js +62 -0
- package/dist/lib/generated-command.js +186 -0
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/inquirer-theme.js +17 -0
- package/dist/lib/inquirer.js +244 -0
- package/dist/lib/naming.js +70 -0
- package/dist/lib/object-utils.js +76 -0
- package/dist/lib/openapi.js +62 -0
- package/dist/lib/plugin-storage.js +64 -0
- package/dist/lib/post-processors.js +23 -0
- package/dist/lib/prompt-catalog-core.js +185 -0
- package/dist/lib/prompt-catalog-terminal.js +375 -0
- package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
- package/dist/lib/prompt-validators.js +240 -0
- package/dist/lib/prompt-web-ui.js +2103 -0
- package/dist/lib/resource-command.js +357 -0
- package/dist/lib/resource-request.js +104 -0
- package/dist/lib/run-npm.js +275 -0
- package/dist/lib/runtime-env-vars.js +32 -0
- package/dist/lib/runtime-generator.js +498 -0
- package/dist/lib/runtime-store.js +56 -0
- package/dist/lib/self-manager.js +301 -0
- package/dist/lib/session-id.js +17 -0
- package/dist/lib/session-integration.js +703 -0
- package/dist/lib/session-store.js +118 -0
- package/dist/lib/skills-manager.js +360 -0
- package/dist/lib/source-publish.js +306 -0
- package/dist/lib/source-registry.js +188 -0
- package/dist/lib/startup-update.js +285 -0
- package/dist/lib/ui.js +155 -0
- package/dist/locale/en-US.json +344 -0
- package/dist/locale/zh-CN.json +344 -0
- package/dist/post-processors/data-modeling.js +84 -0
- package/dist/post-processors/data-source-manager.js +138 -0
- package/dist/post-processors/index.js +19 -0
- package/nocobase-ctl.config.json +388 -0
- package/package.json +100 -26
- package/LICENSE +0 -661
- package/bin/index.js +0 -39
- package/nocobase.conf.tpl +0 -95
- package/src/commands/benchmark.js +0 -73
- package/src/commands/build.js +0 -49
- package/src/commands/clean.js +0 -30
- package/src/commands/client.js +0 -166
- package/src/commands/create-nginx-conf.js +0 -37
- package/src/commands/create-plugin.js +0 -33
- package/src/commands/dev.js +0 -200
- package/src/commands/doc.js +0 -76
- package/src/commands/e2e.js +0 -265
- package/src/commands/global.js +0 -43
- package/src/commands/index.js +0 -45
- package/src/commands/instance-id.js +0 -47
- package/src/commands/locale/cronstrue.js +0 -122
- package/src/commands/locale/react-js-cron/en-US.json +0 -75
- package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
- package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
- package/src/commands/locale.js +0 -81
- package/src/commands/p-test.js +0 -88
- package/src/commands/perf.js +0 -63
- package/src/commands/pkg.js +0 -321
- package/src/commands/pm2.js +0 -37
- package/src/commands/postinstall.js +0 -88
- package/src/commands/start.js +0 -148
- package/src/commands/tar.js +0 -36
- package/src/commands/test-coverage.js +0 -55
- package/src/commands/test.js +0 -107
- package/src/commands/umi.js +0 -33
- package/src/commands/update-deps.js +0 -72
- package/src/commands/upgrade.js +0 -47
- package/src/commands/view-license-key.js +0 -44
- package/src/license.js +0 -76
- package/src/logger.js +0 -75
- package/src/plugin-generator.js +0 -80
- package/src/util.js +0 -517
- package/templates/bundle-status.html +0 -338
- package/templates/create-app-package.json +0 -39
- package/templates/plugin/.npmignore.tpl +0 -2
- package/templates/plugin/README.md.tpl +0 -1
- package/templates/plugin/client.d.ts +0 -2
- package/templates/plugin/client.js +0 -1
- package/templates/plugin/package.json.tpl +0 -11
- package/templates/plugin/server.d.ts +0 -2
- package/templates/plugin/server.js +0 -1
- package/templates/plugin/src/client/client.d.ts +0 -249
- package/templates/plugin/src/client/index.tsx.tpl +0 -1
- package/templates/plugin/src/client/locale.ts +0 -21
- package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
- package/templates/plugin/src/index.ts +0 -2
- package/templates/plugin/src/locale/en-US.json +0 -1
- package/templates/plugin/src/locale/zh-CN.json +0 -1
- package/templates/plugin/src/server/collections/.gitkeep +0 -0
- package/templates/plugin/src/server/index.ts.tpl +0 -1
- package/templates/plugin/src/server/plugin.ts.tpl +0 -19
|
@@ -0,0 +1,360 @@
|
|
|
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 { readFile } from 'node:fs/promises';
|
|
11
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
12
|
+
import { input, password as promptPassword, select } from "../../lib/inquirer.js";
|
|
13
|
+
import { createLicenseEnvFlag, ensureInstanceId, licenseJsonFlag, licensePkgUrlFlag, licenseYesFlag, redactLicenseKey, requireLicenseRuntime, resolveLicenseKeyFile, resolveLicenseServiceUrl, saveLicenseKey, sanitizeLicenseOutput, validateLicenseKey, } from './shared.js';
|
|
14
|
+
import { announceTargetEnv, isInteractiveTerminal } from '../../lib/ui.js';
|
|
15
|
+
import { appUrl } from '../env/shared.js';
|
|
16
|
+
function resolveOnlineInputValue(value) {
|
|
17
|
+
return String(value ?? '').trim();
|
|
18
|
+
}
|
|
19
|
+
async function promptActivationMode() {
|
|
20
|
+
try {
|
|
21
|
+
return await select({
|
|
22
|
+
message: 'How do you want to activate the license?',
|
|
23
|
+
choices: [
|
|
24
|
+
{ value: 'online', name: 'Request and activate a license online' },
|
|
25
|
+
{ value: 'key', name: 'Use an existing license key' },
|
|
26
|
+
{ value: 'cancel', name: 'Cancel' },
|
|
27
|
+
],
|
|
28
|
+
default: 'online',
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return 'cancel';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function promptLicenseKeyInput() {
|
|
36
|
+
let answer;
|
|
37
|
+
try {
|
|
38
|
+
answer = await select({
|
|
39
|
+
message: 'How do you want to provide the license key?',
|
|
40
|
+
choices: [
|
|
41
|
+
{ value: 'key', name: 'Paste the license key' },
|
|
42
|
+
{ value: 'file', name: 'Read the key from a file' },
|
|
43
|
+
],
|
|
44
|
+
default: 'key',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
if (answer === 'key') {
|
|
51
|
+
try {
|
|
52
|
+
const key = await input({
|
|
53
|
+
message: 'License key',
|
|
54
|
+
validate: (value) => String(value ?? '').trim() ? true : 'License key is required.',
|
|
55
|
+
});
|
|
56
|
+
return { key: String(key ?? '').trim() || undefined };
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const keyFile = await input({
|
|
64
|
+
message: 'Path to the license key file',
|
|
65
|
+
validate: (value) => String(value ?? '').trim() ? true : 'License key file path is required.',
|
|
66
|
+
});
|
|
67
|
+
return { keyFile: String(keyFile ?? '').trim() || undefined };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function promptOnlineActivationInput(initial, defaultAppName) {
|
|
74
|
+
let account = String(initial.account ?? '').trim();
|
|
75
|
+
if (!account) {
|
|
76
|
+
try {
|
|
77
|
+
const answer = await input({
|
|
78
|
+
message: 'Service account',
|
|
79
|
+
validate: (value) => String(value ?? '').trim() ? true : 'Service account is required.',
|
|
80
|
+
});
|
|
81
|
+
account = String(answer ?? '').trim();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!account) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
let password = String(initial.password ?? '').trim();
|
|
91
|
+
if (!password) {
|
|
92
|
+
try {
|
|
93
|
+
const answer = await promptPassword({
|
|
94
|
+
message: 'Service password',
|
|
95
|
+
mask: '•',
|
|
96
|
+
validate: (value) => String(value ?? '').trim() ? true : 'Service password is required.',
|
|
97
|
+
});
|
|
98
|
+
password = String(answer ?? '').trim();
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!password) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
let appName = String(initial.appName ?? '').trim();
|
|
108
|
+
if (!appName) {
|
|
109
|
+
try {
|
|
110
|
+
const resolvedDefaultAppName = String(defaultAppName ?? '').trim();
|
|
111
|
+
const answer = await input({
|
|
112
|
+
message: 'Application name',
|
|
113
|
+
default: resolvedDefaultAppName || undefined,
|
|
114
|
+
validate: (value) => String(value ?? '').trim() ? true : 'Application name is required.',
|
|
115
|
+
});
|
|
116
|
+
appName = String(answer ?? '').trim();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!appName) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
account,
|
|
127
|
+
password,
|
|
128
|
+
appName,
|
|
129
|
+
serviceUrl: await resolveLicenseServiceUrl(initial.serviceUrl),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function resolveAppUrlOrThrow(runtime) {
|
|
133
|
+
const currentAppUrl = appUrl(runtime);
|
|
134
|
+
if (!currentAppUrl) {
|
|
135
|
+
throw new Error(`Env "${runtime.envName}" does not have an app URL or app port configured.`);
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
return new URL(currentAppUrl).toString();
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
throw new Error(`Env "${runtime.envName}" has an invalid app URL: ${currentAppUrl}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function requestOnlineLicenseKey(serviceUrl, account, password, payload) {
|
|
145
|
+
const response = await fetch(`${serviceUrl}/license-key`, {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers: {
|
|
148
|
+
'content-type': 'application/json',
|
|
149
|
+
},
|
|
150
|
+
body: JSON.stringify({
|
|
151
|
+
account,
|
|
152
|
+
password,
|
|
153
|
+
appUrl: payload.appUrl,
|
|
154
|
+
appName: payload.appName,
|
|
155
|
+
instanceId: payload.instanceId,
|
|
156
|
+
type: payload.type,
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
if (!response.ok) {
|
|
160
|
+
throw new Error(`License service request failed with status ${response.status}.`);
|
|
161
|
+
}
|
|
162
|
+
const data = await response.json();
|
|
163
|
+
const key = String(data?.data?.key ?? '').trim();
|
|
164
|
+
if (!key) {
|
|
165
|
+
throw new Error('License service did not return a license key.');
|
|
166
|
+
}
|
|
167
|
+
return key;
|
|
168
|
+
}
|
|
169
|
+
export default class LicenseActivate extends Command {
|
|
170
|
+
static summary = 'Activate commercial licensing for the selected env';
|
|
171
|
+
static description = 'Activate a commercial license for the selected env. Provide an existing license key directly, or use `--online` to request and activate one from the online license service.';
|
|
172
|
+
static examples = [
|
|
173
|
+
'<%= config.bin %> <%= command.id %> --env app1 --key <licenseKey>',
|
|
174
|
+
'<%= config.bin %> <%= command.id %> --env app1 --key-file ./license.txt',
|
|
175
|
+
'<%= config.bin %> <%= command.id %> --env app1 --online',
|
|
176
|
+
'<%= config.bin %> <%= command.id %> --env app1 --online --account aa --password bb --desc test24',
|
|
177
|
+
'<%= config.bin %> <%= command.id %> --env app1 --online --account aa --password bb --desc test24 --yes',
|
|
178
|
+
'<%= config.bin %> <%= command.id %> --env app1 --json --key-file ./license.txt',
|
|
179
|
+
];
|
|
180
|
+
static flags = {
|
|
181
|
+
env: createLicenseEnvFlag('CLI env name to activate a license for. Defaults to the current env when omitted'),
|
|
182
|
+
json: licenseJsonFlag,
|
|
183
|
+
key: Flags.string({
|
|
184
|
+
description: 'Existing license key to activate',
|
|
185
|
+
}),
|
|
186
|
+
'key-file': Flags.string({
|
|
187
|
+
description: 'Path to a file containing the license key to activate',
|
|
188
|
+
}),
|
|
189
|
+
online: Flags.boolean({
|
|
190
|
+
description: 'Request a license online and activate it',
|
|
191
|
+
default: false,
|
|
192
|
+
}),
|
|
193
|
+
account: Flags.string({
|
|
194
|
+
description: 'License service account for online activation',
|
|
195
|
+
}),
|
|
196
|
+
password: Flags.string({
|
|
197
|
+
description: 'License service password for online activation',
|
|
198
|
+
}),
|
|
199
|
+
desc: Flags.string({
|
|
200
|
+
description: 'Application name for online activation',
|
|
201
|
+
}),
|
|
202
|
+
'pkg-url': licensePkgUrlFlag,
|
|
203
|
+
yes: licenseYesFlag,
|
|
204
|
+
};
|
|
205
|
+
async run() {
|
|
206
|
+
const { flags } = await this.parse(LicenseActivate);
|
|
207
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
208
|
+
const explicitEnvSelection = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv ?? []));
|
|
209
|
+
if (explicitEnvSelection) {
|
|
210
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
211
|
+
command: this,
|
|
212
|
+
requestedEnv,
|
|
213
|
+
yes: flags.yes,
|
|
214
|
+
});
|
|
215
|
+
if (!confirmed) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const runtime = await requireLicenseRuntime(flags.env);
|
|
220
|
+
if (!flags.json) {
|
|
221
|
+
announceTargetEnv(runtime.envName);
|
|
222
|
+
}
|
|
223
|
+
let key = String(flags.key ?? '').trim();
|
|
224
|
+
let keyFile = String(flags['key-file'] ?? '').trim();
|
|
225
|
+
let online = Boolean(flags.online);
|
|
226
|
+
if (!key && !keyFile && !online) {
|
|
227
|
+
if (!isInteractiveTerminal()) {
|
|
228
|
+
this.error('Provide --key, --key-file, or --online to continue.');
|
|
229
|
+
}
|
|
230
|
+
const mode = await promptActivationMode();
|
|
231
|
+
if (mode === 'cancel') {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (mode === 'online') {
|
|
235
|
+
online = true;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const prompted = await promptLicenseKeyInput();
|
|
239
|
+
key = String(prompted.key ?? '').trim();
|
|
240
|
+
keyFile = String(prompted.keyFile ?? '').trim();
|
|
241
|
+
if (!key && !keyFile) {
|
|
242
|
+
this.error('License key input was empty.');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if ((key || keyFile) && online) {
|
|
247
|
+
this.error('Use either an existing key (--key / --key-file) or --online, not both.');
|
|
248
|
+
}
|
|
249
|
+
if (online) {
|
|
250
|
+
const resolvedServiceUrl = await resolveLicenseServiceUrl(flags['pkg-url']);
|
|
251
|
+
const initialOnline = {
|
|
252
|
+
account: resolveOnlineInputValue(flags.account),
|
|
253
|
+
password: resolveOnlineInputValue(flags.password),
|
|
254
|
+
appName: resolveOnlineInputValue(flags.desc),
|
|
255
|
+
serviceUrl: resolvedServiceUrl,
|
|
256
|
+
};
|
|
257
|
+
let onlineInput = initialOnline;
|
|
258
|
+
if (!onlineInput.account
|
|
259
|
+
|| !onlineInput.password
|
|
260
|
+
|| !onlineInput.appName) {
|
|
261
|
+
if (!isInteractiveTerminal()) {
|
|
262
|
+
this.error('Online activation requires --account, --password, and --desc when not using a TTY.');
|
|
263
|
+
}
|
|
264
|
+
const prompted = await promptOnlineActivationInput(initialOnline, runtime.envName);
|
|
265
|
+
if (!prompted) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
onlineInput = prompted;
|
|
269
|
+
}
|
|
270
|
+
const instanceId = await ensureInstanceId(runtime);
|
|
271
|
+
const resolvedAppUrl = resolveAppUrlOrThrow(runtime);
|
|
272
|
+
const resolvedKey = await requestOnlineLicenseKey(onlineInput.serviceUrl, onlineInput.account, onlineInput.password, {
|
|
273
|
+
appUrl: resolvedAppUrl,
|
|
274
|
+
appName: onlineInput.appName,
|
|
275
|
+
instanceId,
|
|
276
|
+
type: 'internal',
|
|
277
|
+
});
|
|
278
|
+
const validation = await validateLicenseKey(runtime, resolvedKey);
|
|
279
|
+
const ok = !validation.keyStatus
|
|
280
|
+
&& validation.envMatch
|
|
281
|
+
&& validation.domainMatch
|
|
282
|
+
&& validation.licenseStatus === 'active';
|
|
283
|
+
const licenseKeyPath = ok ? await saveLicenseKey(runtime, resolvedKey) : resolveLicenseKeyFile(runtime);
|
|
284
|
+
const payload = {
|
|
285
|
+
ok,
|
|
286
|
+
env: runtime.envName,
|
|
287
|
+
kind: runtime.kind,
|
|
288
|
+
instanceId,
|
|
289
|
+
mode: 'online',
|
|
290
|
+
serviceUrl: onlineInput.serviceUrl,
|
|
291
|
+
appUrl: resolvedAppUrl,
|
|
292
|
+
appName: onlineInput.appName,
|
|
293
|
+
key: redactLicenseKey(resolvedKey),
|
|
294
|
+
licenseKeyPath,
|
|
295
|
+
validation: sanitizeLicenseOutput(validation),
|
|
296
|
+
};
|
|
297
|
+
if (flags.json) {
|
|
298
|
+
this.log(JSON.stringify(payload, null, 2));
|
|
299
|
+
if (!ok) {
|
|
300
|
+
this.exit(1);
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (!ok) {
|
|
305
|
+
const reason = validation.keyStatus
|
|
306
|
+
? `license key is ${validation.keyStatus}`
|
|
307
|
+
: !validation.envMatch
|
|
308
|
+
? 'license key does not match the current instance environment'
|
|
309
|
+
: !validation.domainMatch
|
|
310
|
+
? 'license key does not match the current app domain'
|
|
311
|
+
: validation.licenseStatus !== 'active'
|
|
312
|
+
? `license status is ${validation.licenseStatus}`
|
|
313
|
+
: 'license validation failed';
|
|
314
|
+
this.error(`Failed to activate the online license for env "${runtime.envName}": ${reason}.`);
|
|
315
|
+
}
|
|
316
|
+
this.log(`Activated the online license for env "${runtime.envName}".`);
|
|
317
|
+
this.log(`Saved license key at ${licenseKeyPath}`);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const resolvedKey = key || String(await readFile(keyFile, 'utf8')).trim();
|
|
321
|
+
const validation = await validateLicenseKey(runtime, resolvedKey);
|
|
322
|
+
const ok = !validation.keyStatus
|
|
323
|
+
&& validation.envMatch
|
|
324
|
+
&& validation.domainMatch
|
|
325
|
+
&& validation.licenseStatus === 'active';
|
|
326
|
+
const licenseKeyPath = ok ? await saveLicenseKey(runtime, resolvedKey) : resolveLicenseKeyFile(runtime);
|
|
327
|
+
const payload = {
|
|
328
|
+
ok,
|
|
329
|
+
env: runtime.envName,
|
|
330
|
+
kind: runtime.kind,
|
|
331
|
+
instanceId: await ensureInstanceId(runtime),
|
|
332
|
+
mode: 'key',
|
|
333
|
+
key: redactLicenseKey(resolvedKey),
|
|
334
|
+
keyFile: keyFile || undefined,
|
|
335
|
+
licenseKeyPath,
|
|
336
|
+
validation: sanitizeLicenseOutput(validation),
|
|
337
|
+
};
|
|
338
|
+
if (flags.json) {
|
|
339
|
+
this.log(JSON.stringify(payload, null, 2));
|
|
340
|
+
if (!ok) {
|
|
341
|
+
this.exit(1);
|
|
342
|
+
}
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (!ok) {
|
|
346
|
+
const reason = validation.keyStatus
|
|
347
|
+
? `license key is ${validation.keyStatus}`
|
|
348
|
+
: !validation.envMatch
|
|
349
|
+
? 'license key does not match the current instance environment'
|
|
350
|
+
: !validation.domainMatch
|
|
351
|
+
? 'license key does not match the current app domain'
|
|
352
|
+
: validation.licenseStatus !== 'active'
|
|
353
|
+
? `license status is ${validation.licenseStatus}`
|
|
354
|
+
: 'license validation failed';
|
|
355
|
+
this.error(`Failed to activate the license for env "${runtime.envName}": ${reason}.`);
|
|
356
|
+
}
|
|
357
|
+
this.log(`Activated the license for env "${runtime.envName}".`);
|
|
358
|
+
this.log(`Saved license key at ${licenseKeyPath}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
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 { getEnvAsync } from '@nocobase/license-kit';
|
|
11
|
+
import { validateTcpPort } from "../../lib/prompt-validators.js";
|
|
12
|
+
import { licenseJsonFlag, withLicenseEnvVars } from './shared.js';
|
|
13
|
+
function trimValue(value) {
|
|
14
|
+
const text = String(value ?? '').trim();
|
|
15
|
+
return text || undefined;
|
|
16
|
+
}
|
|
17
|
+
function formatMissingFieldsMessage(missing) {
|
|
18
|
+
return [
|
|
19
|
+
'Missing database settings for license environment inspection.',
|
|
20
|
+
`Required: ${missing.join(', ')}.`,
|
|
21
|
+
'Pass all required `--db-*` flags explicitly.',
|
|
22
|
+
].join('\n');
|
|
23
|
+
}
|
|
24
|
+
export default class LicenseEnv extends Command {
|
|
25
|
+
static hidden = true;
|
|
26
|
+
static flags = {
|
|
27
|
+
'db-dialect': Flags.string({
|
|
28
|
+
description: 'Database dialect: postgres, kingbase, mysql, or mariadb.',
|
|
29
|
+
options: ['postgres', 'kingbase', 'mysql', 'mariadb'],
|
|
30
|
+
}),
|
|
31
|
+
'db-host': Flags.string({
|
|
32
|
+
description: 'Database host name or IP address.',
|
|
33
|
+
}),
|
|
34
|
+
'db-port': Flags.string({
|
|
35
|
+
description: 'Database TCP port.',
|
|
36
|
+
}),
|
|
37
|
+
'db-database': Flags.string({
|
|
38
|
+
description: 'Database name.',
|
|
39
|
+
}),
|
|
40
|
+
'db-user': Flags.string({
|
|
41
|
+
description: 'Database username.',
|
|
42
|
+
}),
|
|
43
|
+
'db-password': Flags.string({
|
|
44
|
+
description: 'Database password.',
|
|
45
|
+
}),
|
|
46
|
+
json: licenseJsonFlag,
|
|
47
|
+
};
|
|
48
|
+
async run() {
|
|
49
|
+
const { flags } = await this.parse(LicenseEnv);
|
|
50
|
+
const envVars = {
|
|
51
|
+
DB_DIALECT: trimValue(flags['db-dialect']),
|
|
52
|
+
DB_HOST: trimValue(flags['db-host']),
|
|
53
|
+
DB_PORT: trimValue(flags['db-port']),
|
|
54
|
+
DB_DATABASE: trimValue(flags['db-database']),
|
|
55
|
+
DB_USER: trimValue(flags['db-user']),
|
|
56
|
+
DB_PASSWORD: flags['db-password'] !== undefined ? String(flags['db-password']) : undefined,
|
|
57
|
+
};
|
|
58
|
+
const missing = [];
|
|
59
|
+
if (!envVars.DB_DIALECT) {
|
|
60
|
+
missing.push('--db-dialect');
|
|
61
|
+
}
|
|
62
|
+
if (!envVars.DB_HOST) {
|
|
63
|
+
missing.push('--db-host');
|
|
64
|
+
}
|
|
65
|
+
if (!envVars.DB_PORT) {
|
|
66
|
+
missing.push('--db-port');
|
|
67
|
+
}
|
|
68
|
+
if (!envVars.DB_DATABASE) {
|
|
69
|
+
missing.push('--db-database');
|
|
70
|
+
}
|
|
71
|
+
if (!envVars.DB_USER) {
|
|
72
|
+
missing.push('--db-user');
|
|
73
|
+
}
|
|
74
|
+
if (!envVars.DB_PASSWORD) {
|
|
75
|
+
missing.push('--db-password');
|
|
76
|
+
}
|
|
77
|
+
if (missing.length > 0) {
|
|
78
|
+
this.error(formatMissingFieldsMessage(missing));
|
|
79
|
+
}
|
|
80
|
+
const portError = validateTcpPort(envVars.DB_PORT);
|
|
81
|
+
if (portError) {
|
|
82
|
+
this.error(portError);
|
|
83
|
+
}
|
|
84
|
+
const env = await withLicenseEnvVars(envVars, async () => await getEnvAsync());
|
|
85
|
+
if (flags.json) {
|
|
86
|
+
this.log(JSON.stringify({
|
|
87
|
+
ok: true,
|
|
88
|
+
env,
|
|
89
|
+
}, null, 2));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.log(JSON.stringify(env, null, 2));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
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 { validateTcpPort } from "../../lib/prompt-validators.js";
|
|
11
|
+
import { generateValidatedInstanceIdFromEnvVars, licenseJsonFlag } from './shared.js';
|
|
12
|
+
function trimValue(value) {
|
|
13
|
+
const text = String(value ?? '').trim();
|
|
14
|
+
return text || undefined;
|
|
15
|
+
}
|
|
16
|
+
function formatMissingFieldsMessage(missing) {
|
|
17
|
+
return [
|
|
18
|
+
'Missing database settings for instance ID generation.',
|
|
19
|
+
`Required: ${missing.join(', ')}.`,
|
|
20
|
+
'Pass all required `--db-*` flags explicitly.',
|
|
21
|
+
].join('\n');
|
|
22
|
+
}
|
|
23
|
+
export default class LicenseGenerateId extends Command {
|
|
24
|
+
static hidden = true;
|
|
25
|
+
static summary = 'Generate a commercial license instance ID from explicit database settings';
|
|
26
|
+
static description = 'Generate the commercial licensing instance ID from explicit `--db-*` flags. This command only prints the generated ID and does not save it.';
|
|
27
|
+
static examples = [
|
|
28
|
+
'<%= config.bin %> <%= command.id %> --db-dialect postgres --db-host 127.0.0.1 --db-port 5432 --db-database nocobase --db-user nocobase --db-password secret',
|
|
29
|
+
'<%= config.bin %> <%= command.id %> --db-dialect postgres --db-host 127.0.0.1 --db-port 5432 --db-database nocobase --db-user nocobase --db-password secret --json',
|
|
30
|
+
];
|
|
31
|
+
static flags = {
|
|
32
|
+
'db-dialect': Flags.string({
|
|
33
|
+
description: 'Database dialect: postgres, kingbase, mysql, or mariadb.',
|
|
34
|
+
options: ['postgres', 'kingbase', 'mysql', 'mariadb'],
|
|
35
|
+
required: false,
|
|
36
|
+
}),
|
|
37
|
+
'db-host': Flags.string({
|
|
38
|
+
description: 'Database host name or IP address.',
|
|
39
|
+
}),
|
|
40
|
+
'db-port': Flags.string({
|
|
41
|
+
description: 'Database TCP port.',
|
|
42
|
+
}),
|
|
43
|
+
'db-database': Flags.string({
|
|
44
|
+
description: 'Database name.',
|
|
45
|
+
}),
|
|
46
|
+
'db-user': Flags.string({
|
|
47
|
+
description: 'Database username.',
|
|
48
|
+
}),
|
|
49
|
+
'db-password': Flags.string({
|
|
50
|
+
description: 'Database password.',
|
|
51
|
+
}),
|
|
52
|
+
json: licenseJsonFlag,
|
|
53
|
+
};
|
|
54
|
+
async run() {
|
|
55
|
+
const { flags } = await this.parse(LicenseGenerateId);
|
|
56
|
+
const dbConfig = {
|
|
57
|
+
dbDialect: trimValue(flags['db-dialect']),
|
|
58
|
+
dbHost: trimValue(flags['db-host']),
|
|
59
|
+
dbPort: trimValue(flags['db-port']),
|
|
60
|
+
dbDatabase: trimValue(flags['db-database']),
|
|
61
|
+
dbUser: trimValue(flags['db-user']),
|
|
62
|
+
dbPassword: flags['db-password'] !== undefined ? String(flags['db-password']) : undefined,
|
|
63
|
+
};
|
|
64
|
+
const missing = [];
|
|
65
|
+
if (!dbConfig.dbDialect) {
|
|
66
|
+
missing.push('--db-dialect');
|
|
67
|
+
}
|
|
68
|
+
if (!dbConfig.dbHost) {
|
|
69
|
+
missing.push('--db-host');
|
|
70
|
+
}
|
|
71
|
+
if (!dbConfig.dbPort) {
|
|
72
|
+
missing.push('--db-port');
|
|
73
|
+
}
|
|
74
|
+
if (!dbConfig.dbDatabase) {
|
|
75
|
+
missing.push('--db-database');
|
|
76
|
+
}
|
|
77
|
+
if (!dbConfig.dbUser) {
|
|
78
|
+
missing.push('--db-user');
|
|
79
|
+
}
|
|
80
|
+
if (!dbConfig.dbPassword) {
|
|
81
|
+
missing.push('--db-password');
|
|
82
|
+
}
|
|
83
|
+
if (missing.length > 0) {
|
|
84
|
+
this.error(formatMissingFieldsMessage(missing));
|
|
85
|
+
}
|
|
86
|
+
const portError = validateTcpPort(dbConfig.dbPort);
|
|
87
|
+
if (portError) {
|
|
88
|
+
this.error(portError);
|
|
89
|
+
}
|
|
90
|
+
const envVars = {
|
|
91
|
+
DB_DIALECT: dbConfig.dbDialect,
|
|
92
|
+
DB_HOST: dbConfig.dbHost,
|
|
93
|
+
DB_PORT: dbConfig.dbPort,
|
|
94
|
+
DB_DATABASE: dbConfig.dbDatabase,
|
|
95
|
+
DB_USER: dbConfig.dbUser,
|
|
96
|
+
DB_PASSWORD: dbConfig.dbPassword,
|
|
97
|
+
};
|
|
98
|
+
const instanceId = await generateValidatedInstanceIdFromEnvVars(envVars);
|
|
99
|
+
if (flags.json) {
|
|
100
|
+
this.log(JSON.stringify({
|
|
101
|
+
ok: true,
|
|
102
|
+
instanceId,
|
|
103
|
+
}, null, 2));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.log(instanceId);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
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 { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
11
|
+
import { createLicenseEnvFlag, generateAndSaveInstanceId, licenseJsonFlag, licenseYesFlag, readSavedInstanceId, requireLicenseRuntime, resolveInstanceIdFile, } from './shared.js';
|
|
12
|
+
import { announceTargetEnv } from '../../lib/ui.js';
|
|
13
|
+
export default class LicenseId extends Command {
|
|
14
|
+
static summary = 'Show the instance ID for the selected env';
|
|
15
|
+
static description = 'Show the commercial licensing instance ID for the selected env, generating and saving it if needed.';
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> <%= command.id %>',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --env app1',
|
|
19
|
+
'<%= config.bin %> <%= command.id %> --env app1 --force',
|
|
20
|
+
'<%= config.bin %> <%= command.id %> --env app1 --json',
|
|
21
|
+
];
|
|
22
|
+
static flags = {
|
|
23
|
+
env: createLicenseEnvFlag('CLI env name to inspect. Defaults to the current env when omitted'),
|
|
24
|
+
json: licenseJsonFlag,
|
|
25
|
+
yes: licenseYesFlag,
|
|
26
|
+
force: Flags.boolean({
|
|
27
|
+
description: 'Force regenerate the instance ID even if one is already saved',
|
|
28
|
+
default: false,
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
async run() {
|
|
32
|
+
const { flags } = await this.parse(LicenseId);
|
|
33
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
34
|
+
const explicitEnvSelection = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv ?? []));
|
|
35
|
+
if (explicitEnvSelection) {
|
|
36
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
37
|
+
command: this,
|
|
38
|
+
requestedEnv,
|
|
39
|
+
yes: flags.yes,
|
|
40
|
+
});
|
|
41
|
+
if (!confirmed) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const runtime = await requireLicenseRuntime(flags.env);
|
|
46
|
+
if (!flags.json) {
|
|
47
|
+
announceTargetEnv(runtime.envName);
|
|
48
|
+
}
|
|
49
|
+
const savedBefore = await readSavedInstanceId(runtime);
|
|
50
|
+
const shouldGenerate = Boolean(flags.force) || !savedBefore;
|
|
51
|
+
const instanceId = shouldGenerate
|
|
52
|
+
? await generateAndSaveInstanceId(runtime)
|
|
53
|
+
: savedBefore;
|
|
54
|
+
const filePath = resolveInstanceIdFile(runtime);
|
|
55
|
+
const generated = shouldGenerate;
|
|
56
|
+
if (flags.json) {
|
|
57
|
+
this.log(JSON.stringify({
|
|
58
|
+
ok: true,
|
|
59
|
+
env: runtime.envName,
|
|
60
|
+
kind: runtime.kind,
|
|
61
|
+
instanceId,
|
|
62
|
+
filePath,
|
|
63
|
+
generated,
|
|
64
|
+
}, null, 2));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
this.log(`Instance ID for env "${runtime.envName}": ${instanceId}`);
|
|
68
|
+
this.log(`${generated ? 'Saved' : 'Loaded'} instance ID at ${filePath}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -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 License extends Command {
|
|
11
|
+
static summary = 'Manage NocoBase commercial licensing';
|
|
12
|
+
async run() {
|
|
13
|
+
await this.parse(License);
|
|
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 ?? 'license',
|
|
17
|
+
...this.argv,
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
}
|