@nocobase/cli 2.1.0-beta.9 → 2.2.0-beta.1
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/assets/env-proxy/nginx/app.conf.tpl +23 -0
- package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
- package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
- package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
- package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
- package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
- package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
- package/assets/env-proxy/nginx/snippets/proxy-location.conf +17 -0
- package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
- package/assets/env-proxy/nginx/snippets/uploads-location.conf +21 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +145 -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/autostart/disable.js +55 -0
- package/dist/commands/app/autostart/enable.js +55 -0
- package/dist/commands/app/autostart/list.js +37 -0
- package/dist/commands/app/autostart/run.js +84 -0
- package/dist/commands/app/autostart/shared.js +49 -0
- package/dist/commands/app/destroy.js +234 -0
- package/dist/commands/app/down.js +71 -0
- package/dist/commands/app/logs.js +115 -0
- package/dist/commands/app/restart.js +229 -0
- package/dist/commands/app/shared.js +123 -0
- package/dist/commands/app/start.js +416 -0
- package/dist/commands/app/stop.js +183 -0
- package/dist/commands/app/upgrade.js +523 -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 +42 -0
- package/dist/commands/config/get.js +39 -0
- package/dist/commands/config/index.js +20 -0
- package/dist/commands/config/list.js +29 -0
- package/dist/commands/config/set.js +49 -0
- package/dist/commands/db/check.js +240 -0
- package/dist/commands/db/logs.js +85 -0
- package/dist/commands/db/ps.js +47 -0
- package/dist/commands/db/shared.js +96 -0
- package/dist/commands/db/start.js +86 -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 +406 -0
- package/dist/commands/env/auth.js +189 -0
- package/dist/commands/env/current.js +21 -0
- package/dist/commands/env/info.js +202 -0
- package/dist/commands/env/list.js +43 -0
- package/dist/commands/env/remove.js +174 -0
- package/dist/commands/env/shared.js +204 -0
- package/dist/commands/env/status.js +93 -0
- package/dist/commands/env/update.js +448 -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 +1390 -0
- package/dist/commands/install.js +2609 -0
- package/dist/commands/license/activate.js +179 -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 +382 -0
- package/dist/commands/license/plugins/sync.js +314 -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/import.js +108 -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/proxy/caddy/current.js +17 -0
- package/dist/commands/proxy/caddy/generate.js +69 -0
- package/dist/commands/proxy/caddy/index.js +28 -0
- package/dist/commands/proxy/caddy/info.js +31 -0
- package/dist/commands/proxy/caddy/reload.js +30 -0
- package/dist/commands/proxy/caddy/restart.js +28 -0
- package/dist/commands/proxy/caddy/start.js +30 -0
- package/dist/commands/proxy/caddy/status.js +19 -0
- package/dist/commands/proxy/caddy/stop.js +30 -0
- package/dist/commands/proxy/caddy/use.js +26 -0
- package/dist/commands/proxy/index.js +28 -0
- package/dist/commands/proxy/nginx/current.js +18 -0
- package/dist/commands/proxy/nginx/generate.js +68 -0
- package/dist/commands/proxy/nginx/index.js +28 -0
- package/dist/commands/proxy/nginx/info.js +34 -0
- package/dist/commands/proxy/nginx/reload.js +30 -0
- package/dist/commands/proxy/nginx/restart.js +28 -0
- package/dist/commands/proxy/nginx/start.js +30 -0
- package/dist/commands/proxy/nginx/status.js +19 -0
- package/dist/commands/proxy/nginx/stop.js +30 -0
- package/dist/commands/proxy/nginx/use.js +31 -0
- package/dist/commands/restart.js +12 -0
- package/dist/commands/revision/create.js +118 -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 +152 -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 +884 -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 +476 -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 +134 -0
- package/dist/help/runtime-help.js +23 -0
- package/dist/lib/api-client.js +335 -0
- package/dist/lib/api-command-compat.js +641 -0
- package/dist/lib/app-health.js +139 -0
- package/dist/lib/app-managed-resources.js +337 -0
- package/dist/lib/app-public-path.js +80 -0
- package/dist/lib/app-runtime.js +189 -0
- package/dist/lib/auth-store.js +528 -0
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +409 -0
- package/dist/lib/build-config.js +18 -0
- package/dist/lib/builtin-db.js +86 -0
- package/dist/lib/cli-config.js +569 -0
- package/dist/lib/cli-entry-error.js +52 -0
- package/dist/lib/cli-home.js +47 -0
- package/dist/lib/cli-locale.js +141 -0
- package/dist/lib/command-discovery.js +39 -0
- package/dist/lib/command-log.js +284 -0
- package/dist/lib/db-connection-check.js +219 -0
- package/dist/lib/docker-env-file.js +60 -0
- package/dist/lib/docker-image.js +37 -0
- package/dist/lib/docker-log-stream.js +45 -0
- package/dist/lib/env-auth.js +963 -0
- package/dist/lib/env-command-config.js +45 -0
- package/dist/lib/env-config.js +108 -0
- package/dist/lib/env-guard.js +61 -0
- package/dist/lib/env-paths.js +101 -0
- package/dist/lib/env-proxy.js +1325 -0
- package/dist/lib/generated-command.js +203 -0
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/inquirer-theme.js +17 -0
- package/dist/lib/inquirer.js +243 -0
- package/dist/lib/managed-env-file.js +101 -0
- package/dist/lib/managed-init-env.js +32 -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-import.js +279 -0
- package/dist/lib/plugin-storage.js +64 -0
- package/dist/lib/post-processors.js +23 -0
- package/dist/lib/prompt-catalog-core.js +186 -0
- package/dist/lib/prompt-catalog-terminal.js +374 -0
- package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
- package/dist/lib/prompt-validators.js +278 -0
- package/dist/lib/prompt-web-ui.js +2234 -0
- package/dist/lib/proxy-caddy.js +274 -0
- package/dist/lib/proxy-nginx.js +330 -0
- package/dist/lib/resource-command.js +357 -0
- package/dist/lib/resource-request.js +104 -0
- package/dist/lib/run-npm.js +429 -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 +438 -0
- package/dist/lib/source-publish.js +326 -0
- package/dist/lib/source-registry.js +188 -0
- package/dist/lib/startup-update.js +309 -0
- package/dist/lib/ui.js +159 -0
- package/dist/locale/en-US.json +526 -0
- package/dist/locale/zh-CN.json +526 -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 +128 -24
- package/scripts/build.mjs +34 -0
- package/scripts/clean.mjs +9 -0
- package/tsconfig.json +19 -0
- 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,70 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
export function toKebabCase(value) {
|
|
3
|
+
return value
|
|
4
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
5
|
+
.replace(/[^a-zA-Z0-9]+/g, '-')
|
|
6
|
+
.replace(/-+/g, '-')
|
|
7
|
+
.replace(/^-|-$/g, '')
|
|
8
|
+
.toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
export function splitPathAction(pathTemplate) {
|
|
11
|
+
const normalizedPath = pathTemplate.replace(/^\/+/, '');
|
|
12
|
+
const separatorIndex = normalizedPath.lastIndexOf(':');
|
|
13
|
+
if (separatorIndex === -1) {
|
|
14
|
+
return {
|
|
15
|
+
resourcePath: normalizedPath,
|
|
16
|
+
action: 'call',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
resourcePath: normalizedPath.slice(0, separatorIndex),
|
|
21
|
+
action: normalizedPath.slice(separatorIndex + 1),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function toLogicalResourceName(pathTemplate) {
|
|
25
|
+
const { resourcePath } = splitPathAction(pathTemplate);
|
|
26
|
+
return resourcePath
|
|
27
|
+
.split('/')
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.filter((segment) => !segment.startsWith('{'))
|
|
30
|
+
.map((segment) => toKebabCase(segment))
|
|
31
|
+
.join('.');
|
|
32
|
+
}
|
|
33
|
+
export function toLogicalActionName(pathTemplate) {
|
|
34
|
+
return toKebabCase(splitPathAction(pathTemplate).action);
|
|
35
|
+
}
|
|
36
|
+
export function toResourceSegments(pathTemplate, options) {
|
|
37
|
+
const { resourcePath, action } = splitPathAction(pathTemplate);
|
|
38
|
+
const pathSegments = resourcePath
|
|
39
|
+
.split('/')
|
|
40
|
+
.filter(Boolean)
|
|
41
|
+
.flatMap((segment) => {
|
|
42
|
+
if (!segment.startsWith('{')) {
|
|
43
|
+
return [toKebabCase(segment)];
|
|
44
|
+
}
|
|
45
|
+
if (!options?.includeParams) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
return [`by-${toKebabCase(segment.slice(1, -1))}`];
|
|
49
|
+
});
|
|
50
|
+
return [...pathSegments, toKebabCase(action)].filter(Boolean);
|
|
51
|
+
}
|
|
52
|
+
export function toCommandSegments(moduleName, pathTemplate, options) {
|
|
53
|
+
const resourceSegments = toResourceSegments(pathTemplate, options);
|
|
54
|
+
const segments = [options?.omitModule ? '' : toKebabCase(moduleName), ...resourceSegments].filter(Boolean);
|
|
55
|
+
return segments.length ? segments : [toKebabCase(moduleName), 'call'];
|
|
56
|
+
}
|
|
57
|
+
export function toClassName(segments) {
|
|
58
|
+
return segments
|
|
59
|
+
.map((segment) => segment.replace(/(^\w|-\w)/g, (token) => token.replace('-', '').toUpperCase()))
|
|
60
|
+
.join('');
|
|
61
|
+
}
|
|
62
|
+
export function toOutputFile(outputRoot, segments) {
|
|
63
|
+
const folder = path.join(outputRoot, ...segments.slice(0, -1));
|
|
64
|
+
const filePath = path.join(folder, `${segments.at(-1)}.ts`);
|
|
65
|
+
return filePath;
|
|
66
|
+
}
|
|
67
|
+
export function toImportPath(fromFile, targetFile) {
|
|
68
|
+
const relative = path.relative(path.dirname(fromFile), targetFile).replace(/\\/g, '/');
|
|
69
|
+
return relative.startsWith('.') ? relative : `./${relative}`;
|
|
70
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
export function pickKeys(object, keys) {
|
|
10
|
+
const picked = {};
|
|
11
|
+
for (const key of keys) {
|
|
12
|
+
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
|
13
|
+
const typedKey = key;
|
|
14
|
+
picked[typedKey] = object[typedKey];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return picked;
|
|
18
|
+
}
|
|
19
|
+
export function omitKeys(object, keys) {
|
|
20
|
+
const omittedKeys = new Set(keys);
|
|
21
|
+
const result = {};
|
|
22
|
+
for (const key of Object.keys(object)) {
|
|
23
|
+
if (!omittedKeys.has(key)) {
|
|
24
|
+
const typedKey = key;
|
|
25
|
+
result[typedKey] = object[typedKey];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
export function upperFirst(value) {
|
|
31
|
+
if (!value) {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
return value[0].toUpperCase() + value.slice(1);
|
|
35
|
+
}
|
|
36
|
+
export function deepEqual(left, right) {
|
|
37
|
+
if (Object.is(left, right)) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (left == null || right == null) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (typeof left !== typeof right) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (typeof left !== 'object') {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
50
|
+
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
54
|
+
if (!deepEqual(left[index], right[index])) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
const leftObject = left;
|
|
61
|
+
const rightObject = right;
|
|
62
|
+
const leftKeys = Object.keys(leftObject);
|
|
63
|
+
const rightKeys = Object.keys(rightObject);
|
|
64
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
for (const key of leftKeys) {
|
|
68
|
+
if (!Object.prototype.hasOwnProperty.call(rightObject, key)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (!deepEqual(leftObject[key], rightObject[key])) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
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 HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete'];
|
|
10
|
+
function resolveLocalRef(document, ref) {
|
|
11
|
+
if (!ref.startsWith('#/')) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return ref
|
|
15
|
+
.slice(2)
|
|
16
|
+
.split('/')
|
|
17
|
+
.reduce((current, segment) => current?.[segment], document);
|
|
18
|
+
}
|
|
19
|
+
function dereferenceNode(node, document, seen = new Set()) {
|
|
20
|
+
if (Array.isArray(node)) {
|
|
21
|
+
return node.map((item) => dereferenceNode(item, document, seen));
|
|
22
|
+
}
|
|
23
|
+
if (!node || typeof node !== 'object') {
|
|
24
|
+
return node;
|
|
25
|
+
}
|
|
26
|
+
const ref = node.$ref;
|
|
27
|
+
if (typeof ref === 'string') {
|
|
28
|
+
if (seen.has(ref)) {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
const resolved = resolveLocalRef(document, ref);
|
|
32
|
+
if (!resolved) {
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
return dereferenceNode(resolved, document, new Set([...seen, ref]));
|
|
36
|
+
}
|
|
37
|
+
return Object.fromEntries(Object.entries(node).map(([key, value]) => [key, dereferenceNode(value, document, seen)]));
|
|
38
|
+
}
|
|
39
|
+
export function collectOperations(document) {
|
|
40
|
+
const operations = [];
|
|
41
|
+
for (const [pathTemplate, pathItem] of Object.entries(document.paths ?? {})) {
|
|
42
|
+
for (const method of HTTP_METHODS) {
|
|
43
|
+
const operation = pathItem?.[method];
|
|
44
|
+
if (!operation || '$ref' in operation) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const parameters = [...(pathItem.parameters ?? []), ...(operation.parameters ?? [])]
|
|
48
|
+
.map((parameter) => dereferenceNode(parameter, document))
|
|
49
|
+
.filter((parameter) => Boolean(parameter && !('$ref' in parameter)));
|
|
50
|
+
operations.push({
|
|
51
|
+
method,
|
|
52
|
+
pathTemplate,
|
|
53
|
+
operation: {
|
|
54
|
+
...operation,
|
|
55
|
+
parameters,
|
|
56
|
+
requestBody: operation.requestBody ? dereferenceNode(operation.requestBody, document) : undefined,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return operations;
|
|
62
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
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 fs from 'node:fs';
|
|
10
|
+
import fsp from 'node:fs/promises';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { Readable } from 'node:stream';
|
|
14
|
+
import { pipeline } from 'node:stream/promises';
|
|
15
|
+
import { createGunzip } from 'node:zlib';
|
|
16
|
+
import * as tar from 'tar';
|
|
17
|
+
import { fetchWithPreservedAuthRedirect } from './http-request.js';
|
|
18
|
+
import { resolvePluginStoragePath } from './plugin-storage.js';
|
|
19
|
+
import { run } from './run-npm.js';
|
|
20
|
+
const NPM_PACK_TIMEOUT_MS = 30_000;
|
|
21
|
+
const NPM_AUTH_ERROR_PATTERNS = [
|
|
22
|
+
'e401',
|
|
23
|
+
'e403',
|
|
24
|
+
'unauthorized',
|
|
25
|
+
'authorization',
|
|
26
|
+
'forbidden',
|
|
27
|
+
'need auth',
|
|
28
|
+
'requires authentication',
|
|
29
|
+
'unable to authenticate',
|
|
30
|
+
'authentication token',
|
|
31
|
+
];
|
|
32
|
+
const NPM_NOT_FOUND_ERROR_PATTERNS = [
|
|
33
|
+
'e404',
|
|
34
|
+
'etarget',
|
|
35
|
+
'not found',
|
|
36
|
+
'not in this registry',
|
|
37
|
+
'no matching version found',
|
|
38
|
+
'no match found',
|
|
39
|
+
];
|
|
40
|
+
const NPM_NETWORK_ERROR_PATTERNS = [
|
|
41
|
+
'enotfound',
|
|
42
|
+
'eai_again',
|
|
43
|
+
'etimedout',
|
|
44
|
+
'esockettimedout',
|
|
45
|
+
'econnreset',
|
|
46
|
+
'econnrefused',
|
|
47
|
+
'enetunreach',
|
|
48
|
+
'ehostunreach',
|
|
49
|
+
'fetch failed',
|
|
50
|
+
'getaddrinfo',
|
|
51
|
+
'timed out',
|
|
52
|
+
];
|
|
53
|
+
function trimValue(value) {
|
|
54
|
+
const text = String(value ?? '').trim();
|
|
55
|
+
return text || undefined;
|
|
56
|
+
}
|
|
57
|
+
async function pathExists(target) {
|
|
58
|
+
try {
|
|
59
|
+
await fsp.access(target);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function isHttpArchiveSource(value) {
|
|
67
|
+
try {
|
|
68
|
+
const url = new URL(value);
|
|
69
|
+
return url.protocol === 'http:' || url.protocol === 'https:';
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function responseBodyToNodeReadable(body) {
|
|
76
|
+
return Readable.fromWeb(body);
|
|
77
|
+
}
|
|
78
|
+
function looksLikeLocalArchivePath(value) {
|
|
79
|
+
return (path.isAbsolute(value) ||
|
|
80
|
+
value.startsWith('./') ||
|
|
81
|
+
value.startsWith('../') ||
|
|
82
|
+
value.endsWith('.tgz') ||
|
|
83
|
+
value.endsWith('.tar.gz'));
|
|
84
|
+
}
|
|
85
|
+
function resolvePluginOutputDir(storagePluginsPath, packageName) {
|
|
86
|
+
const outputDir = path.resolve(storagePluginsPath, packageName);
|
|
87
|
+
const relative = path.relative(storagePluginsPath, outputDir);
|
|
88
|
+
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
89
|
+
throw new Error(`Imported archive package name "${packageName}" resolves outside plugin storage.`);
|
|
90
|
+
}
|
|
91
|
+
return outputDir;
|
|
92
|
+
}
|
|
93
|
+
function normalizeNpmRegistry(value) {
|
|
94
|
+
const text = trimValue(value);
|
|
95
|
+
return text ? text.replace(/\/+$/, '') : undefined;
|
|
96
|
+
}
|
|
97
|
+
function formatNpmPackFailureMessage(source, registry, details) {
|
|
98
|
+
const normalizedDetails = details.toLowerCase();
|
|
99
|
+
const lines = [
|
|
100
|
+
`Failed to import npm package "${source}" with \`npm pack\`.`,
|
|
101
|
+
details,
|
|
102
|
+
];
|
|
103
|
+
const loginCommand = registry ? `npm login --registry=${registry}` : 'npm login';
|
|
104
|
+
if (NPM_AUTH_ERROR_PATTERNS.some((pattern) => normalizedDetails.includes(pattern))) {
|
|
105
|
+
lines.push(`Hint: If this is a private registry, run \`${loginCommand}\` first and retry.`);
|
|
106
|
+
return lines.join('\n');
|
|
107
|
+
}
|
|
108
|
+
if (NPM_NOT_FOUND_ERROR_PATTERNS.some((pattern) => normalizedDetails.includes(pattern))) {
|
|
109
|
+
lines.push(registry
|
|
110
|
+
? `Hint: Check that "${source}" exists in ${registry} and that the package name or tag is correct.`
|
|
111
|
+
: `Hint: Check that "${source}" exists and that the package name or tag is correct.`);
|
|
112
|
+
return lines.join('\n');
|
|
113
|
+
}
|
|
114
|
+
if (NPM_NETWORK_ERROR_PATTERNS.some((pattern) => normalizedDetails.includes(pattern))) {
|
|
115
|
+
lines.push(registry
|
|
116
|
+
? `Hint: Check that the npm registry ${registry} is reachable from this machine.`
|
|
117
|
+
: 'Hint: Check that the npm registry is reachable from this machine.');
|
|
118
|
+
return lines.join('\n');
|
|
119
|
+
}
|
|
120
|
+
if (registry) {
|
|
121
|
+
lines.push(`Hint: If this is a private registry, make sure \`${loginCommand}\` has completed successfully.`);
|
|
122
|
+
}
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
async function resolvePackedPluginTarball(packRoot, sourceLabel) {
|
|
126
|
+
const entries = await fsp.readdir(packRoot, { withFileTypes: true });
|
|
127
|
+
const tarballs = entries
|
|
128
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.tgz'))
|
|
129
|
+
.map((entry) => path.join(packRoot, entry.name))
|
|
130
|
+
.sort();
|
|
131
|
+
if (tarballs.length === 1) {
|
|
132
|
+
return tarballs[0];
|
|
133
|
+
}
|
|
134
|
+
if (tarballs.length === 0) {
|
|
135
|
+
throw new Error(`npm pack did not produce a local tarball for ${sourceLabel}.`);
|
|
136
|
+
}
|
|
137
|
+
throw new Error(`npm pack produced multiple tarballs for ${sourceLabel}.`);
|
|
138
|
+
}
|
|
139
|
+
async function packNpmPluginSource(source, npmRegistry, runFn = run) {
|
|
140
|
+
const packRoot = await fsp.mkdtemp(path.join(os.tmpdir(), 'nocobase-cli-plugin-pack-'));
|
|
141
|
+
const args = ['pack', '--silent'];
|
|
142
|
+
const registry = normalizeNpmRegistry(npmRegistry);
|
|
143
|
+
if (registry) {
|
|
144
|
+
args.push(`--registry=${registry}`);
|
|
145
|
+
}
|
|
146
|
+
args.push(source);
|
|
147
|
+
let stdout = '';
|
|
148
|
+
let stderr = '';
|
|
149
|
+
try {
|
|
150
|
+
await runFn('npm', args, {
|
|
151
|
+
cwd: packRoot,
|
|
152
|
+
stdio: 'pipe',
|
|
153
|
+
errorName: 'npm pack',
|
|
154
|
+
timeoutMs: NPM_PACK_TIMEOUT_MS,
|
|
155
|
+
onStdout: (chunk) => {
|
|
156
|
+
stdout += chunk;
|
|
157
|
+
},
|
|
158
|
+
onStderr: (chunk) => {
|
|
159
|
+
stderr += chunk;
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
const tarballPath = await resolvePackedPluginTarball(packRoot, source);
|
|
163
|
+
return {
|
|
164
|
+
stream: fs.createReadStream(tarballPath),
|
|
165
|
+
source,
|
|
166
|
+
sourceType: 'npm',
|
|
167
|
+
cleanup: async () => {
|
|
168
|
+
await fsp.rm(packRoot, { recursive: true, force: true });
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
await fsp.rm(packRoot, { recursive: true, force: true });
|
|
174
|
+
const originalMessage = error instanceof Error ? error.message : String(error);
|
|
175
|
+
const details = trimValue(stderr) || trimValue(stdout) || trimValue(originalMessage) || 'npm pack failed.';
|
|
176
|
+
throw new Error(formatNpmPackFailureMessage(source, registry, details));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function openPluginSource(source, npmRegistry, runFn = run) {
|
|
180
|
+
if (isHttpArchiveSource(source)) {
|
|
181
|
+
const response = await fetchWithPreservedAuthRedirect(source);
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
const statusText = trimValue(response.statusText);
|
|
184
|
+
throw new Error(`Failed to download plugin archive from ${source}: ${response.status}${statusText ? ` ${statusText}` : ''}.`);
|
|
185
|
+
}
|
|
186
|
+
if (!response.body) {
|
|
187
|
+
throw new Error(`Downloaded plugin archive from ${source} does not include a response body.`);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
stream: responseBodyToNodeReadable(response.body),
|
|
191
|
+
source,
|
|
192
|
+
sourceType: 'url',
|
|
193
|
+
cleanup: async () => undefined,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const resolvedPath = path.resolve(process.cwd(), source);
|
|
197
|
+
if (await pathExists(resolvedPath)) {
|
|
198
|
+
return {
|
|
199
|
+
stream: fs.createReadStream(resolvedPath),
|
|
200
|
+
source: resolvedPath,
|
|
201
|
+
sourceType: 'file',
|
|
202
|
+
cleanup: async () => undefined,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (looksLikeLocalArchivePath(source)) {
|
|
206
|
+
throw new Error(`Plugin archive file does not exist: ${resolvedPath}`);
|
|
207
|
+
}
|
|
208
|
+
return await packNpmPluginSource(source, npmRegistry, runFn);
|
|
209
|
+
}
|
|
210
|
+
async function readPluginMetadata(extractRoot, sourceLabel) {
|
|
211
|
+
const packageJsonPath = path.join(extractRoot, 'package.json');
|
|
212
|
+
let content;
|
|
213
|
+
try {
|
|
214
|
+
content = await fsp.readFile(packageJsonPath, 'utf8');
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
throw new Error(`Imported archive from ${sourceLabel} does not contain a package.json at the package root.`);
|
|
218
|
+
}
|
|
219
|
+
let metadata;
|
|
220
|
+
try {
|
|
221
|
+
metadata = JSON.parse(content);
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
throw new Error(`Imported archive from ${sourceLabel} contains an invalid package.json.`);
|
|
225
|
+
}
|
|
226
|
+
const packageName = trimValue(metadata.name);
|
|
227
|
+
if (!packageName) {
|
|
228
|
+
throw new Error(`Imported archive from ${sourceLabel} is missing "package.json.name".`);
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
packageName,
|
|
232
|
+
packageVersion: trimValue(metadata.version),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
export async function importPluginSource(source, options = {}) {
|
|
236
|
+
const normalizedSource = trimValue(source);
|
|
237
|
+
if (!normalizedSource) {
|
|
238
|
+
throw new Error('Pass a plugin archive path, URL, or npm package spec.');
|
|
239
|
+
}
|
|
240
|
+
const storagePluginsPath = resolvePluginStoragePath(options.storagePath);
|
|
241
|
+
await fsp.mkdir(storagePluginsPath, { recursive: true });
|
|
242
|
+
const archive = await openPluginSource(normalizedSource, options.npmRegistry, options.runFn);
|
|
243
|
+
const stageDir = await fsp.mkdtemp(path.join(storagePluginsPath, '.nb-plugin-import-'));
|
|
244
|
+
let stageMoved = false;
|
|
245
|
+
try {
|
|
246
|
+
try {
|
|
247
|
+
await pipeline(archive.stream, createGunzip(), tar.extract({ cwd: stageDir, strip: 1 }));
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
251
|
+
throw new Error(`Failed to extract plugin archive from ${archive.source}: ${message}`);
|
|
252
|
+
}
|
|
253
|
+
const { packageName, packageVersion } = await readPluginMetadata(stageDir, archive.source);
|
|
254
|
+
const outputDir = resolvePluginOutputDir(storagePluginsPath, packageName);
|
|
255
|
+
const action = (await pathExists(outputDir)) ? 'updated' : 'installed';
|
|
256
|
+
await fsp.mkdir(path.dirname(outputDir), { recursive: true });
|
|
257
|
+
await fsp.rm(outputDir, { recursive: true, force: true });
|
|
258
|
+
await fsp.rename(stageDir, outputDir);
|
|
259
|
+
stageMoved = true;
|
|
260
|
+
return {
|
|
261
|
+
action,
|
|
262
|
+
packageName,
|
|
263
|
+
packageVersion,
|
|
264
|
+
outputDir,
|
|
265
|
+
source: archive.source,
|
|
266
|
+
sourceType: archive.sourceType,
|
|
267
|
+
storagePluginsPath,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
if (!stageMoved) {
|
|
272
|
+
await fsp.rm(stageDir, { recursive: true, force: true });
|
|
273
|
+
}
|
|
274
|
+
await archive.cleanup();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
export async function importPluginArchive(source, storagePath) {
|
|
278
|
+
return await importPluginSource(source, { storagePath });
|
|
279
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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 { access, lstat, readlink, rm } from 'node:fs/promises';
|
|
11
|
+
async function pathExists(target) {
|
|
12
|
+
try {
|
|
13
|
+
await access(target);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function resolvePluginStoragePath(storagePath) {
|
|
21
|
+
const root = String(storagePath ?? process.env.STORAGE_PATH ?? '').trim();
|
|
22
|
+
if (root) {
|
|
23
|
+
return path.join(path.isAbsolute(root) ? root : path.resolve(process.cwd(), root), 'plugins');
|
|
24
|
+
}
|
|
25
|
+
const configured = String(process.env.PLUGIN_STORAGE_PATH ?? '').trim();
|
|
26
|
+
if (configured) {
|
|
27
|
+
return path.isAbsolute(configured) ? configured : path.resolve(process.cwd(), configured);
|
|
28
|
+
}
|
|
29
|
+
return path.resolve(process.cwd(), 'storage', 'plugins');
|
|
30
|
+
}
|
|
31
|
+
export async function removeStoragePluginSymlink(pluginName, storagePath, nodeModulesPath = String(process.env.NODE_MODULES_PATH ?? '').trim()) {
|
|
32
|
+
if (!nodeModulesPath) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const storagePluginsPath = resolvePluginStoragePath(storagePath);
|
|
36
|
+
const targetPath = path.resolve(storagePluginsPath, pluginName);
|
|
37
|
+
const linkPath = path.resolve(nodeModulesPath, pluginName);
|
|
38
|
+
if (!(await pathExists(linkPath))) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
let statResult;
|
|
42
|
+
try {
|
|
43
|
+
statResult = await lstat(linkPath);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (!statResult.isSymbolicLink()) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
let resolvedLinkTarget = '';
|
|
52
|
+
try {
|
|
53
|
+
const linkTarget = await readlink(linkPath);
|
|
54
|
+
resolvedLinkTarget = path.resolve(path.dirname(linkPath), linkTarget);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (resolvedLinkTarget !== targetPath) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
await rm(linkPath, { recursive: true, force: true });
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function buildKey(resource, action) {
|
|
2
|
+
return `${resource}:${action}`;
|
|
3
|
+
}
|
|
4
|
+
class PostProcessorRegistry {
|
|
5
|
+
processors = new Map();
|
|
6
|
+
register(resource, action, processor) {
|
|
7
|
+
this.processors.set(buildKey(resource, action), processor);
|
|
8
|
+
}
|
|
9
|
+
resolve(resource, action) {
|
|
10
|
+
if (!resource || !action) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return this.processors.get(buildKey(resource, action));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export const postProcessorRegistry = new PostProcessorRegistry();
|
|
17
|
+
export async function applyPostProcessor(result, context) {
|
|
18
|
+
const processor = postProcessorRegistry.resolve(context.operation.logicalResourceName, context.operation.actionName);
|
|
19
|
+
if (!processor) {
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
return processor(result, context);
|
|
23
|
+
}
|