@nocobase/server 2.3.0-beta.9 → 2.4.0-alpha.10
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/lib/environment.d.ts +5 -1
- package/lib/environment.js +17 -1
- package/lib/gateway/index.d.ts +18 -0
- package/lib/gateway/index.js +292 -2
- package/lib/gateway/utils.d.ts +9 -0
- package/lib/gateway/utils.js +57 -1
- package/lib/plugin-manager/utils.js +1 -1
- package/lib/types/vendor.d.js +9 -0
- package/package.json +18 -19
package/lib/environment.d.ts
CHANGED
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export declare class Environment {
|
|
10
10
|
private vars;
|
|
11
|
-
|
|
11
|
+
private secretKeys;
|
|
12
|
+
setVariable(key: string, value: string, options?: {
|
|
13
|
+
isSecret?: boolean;
|
|
14
|
+
}): void;
|
|
12
15
|
removeVariable(key: string): void;
|
|
13
16
|
getVariablesAndSecrets(): {};
|
|
14
17
|
getVariables(): {};
|
|
18
|
+
getNonSecretVariables(): {};
|
|
15
19
|
renderJsonTemplate(template: any, options?: {
|
|
16
20
|
omit?: string[];
|
|
17
21
|
}): any;
|
package/lib/environment.js
CHANGED
|
@@ -44,11 +44,18 @@ var import_utils = require("@nocobase/utils");
|
|
|
44
44
|
var import_lodash = __toESM(require("lodash"));
|
|
45
45
|
const _Environment = class _Environment {
|
|
46
46
|
vars = {};
|
|
47
|
-
|
|
47
|
+
secretKeys = /* @__PURE__ */ new Set();
|
|
48
|
+
setVariable(key, value, options) {
|
|
48
49
|
this.vars[key] = value;
|
|
50
|
+
if ((options == null ? void 0 : options.isSecret) === true) {
|
|
51
|
+
this.secretKeys.add(key);
|
|
52
|
+
} else if ((options == null ? void 0 : options.isSecret) === false) {
|
|
53
|
+
this.secretKeys.delete(key);
|
|
54
|
+
}
|
|
49
55
|
}
|
|
50
56
|
removeVariable(key) {
|
|
51
57
|
delete this.vars[key];
|
|
58
|
+
this.secretKeys.delete(key);
|
|
52
59
|
}
|
|
53
60
|
getVariablesAndSecrets() {
|
|
54
61
|
return this.vars;
|
|
@@ -56,6 +63,15 @@ const _Environment = class _Environment {
|
|
|
56
63
|
getVariables() {
|
|
57
64
|
return this.vars;
|
|
58
65
|
}
|
|
66
|
+
getNonSecretVariables() {
|
|
67
|
+
const result = {};
|
|
68
|
+
for (const key of Object.keys(this.vars)) {
|
|
69
|
+
if (!this.secretKeys.has(key)) {
|
|
70
|
+
result[key] = this.vars[key];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
59
75
|
renderJsonTemplate(template, options) {
|
|
60
76
|
if (options == null ? void 0 : options.omit) {
|
|
61
77
|
const omitTemplate = import_lodash.default.omit(template, options.omit);
|
package/lib/gateway/index.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ export declare class Gateway extends EventEmitter {
|
|
|
59
59
|
private host;
|
|
60
60
|
private socketPath;
|
|
61
61
|
private v2IndexTemplateCache;
|
|
62
|
+
private settingsIndexTemplateCache;
|
|
62
63
|
private terminating;
|
|
63
64
|
private getOriginalRequestUrl;
|
|
64
65
|
private proxyRequestToSubApp;
|
|
@@ -80,6 +81,18 @@ export declare class Gateway extends EventEmitter {
|
|
|
80
81
|
responseErrorWithCode(code: any, res: any, options: any): void;
|
|
81
82
|
private getV2PublicPath;
|
|
82
83
|
private getAppPublicPath;
|
|
84
|
+
private getSettingsPublicPath;
|
|
85
|
+
private getPathWithinAppPublicPath;
|
|
86
|
+
private isSettingsRequest;
|
|
87
|
+
private isSettingsIndexRequest;
|
|
88
|
+
private isSettingsAssetsRequest;
|
|
89
|
+
private resolveLegacyV2SettingsRedirect;
|
|
90
|
+
private getPortalRootPublicPath;
|
|
91
|
+
private getPortalAppPublicPath;
|
|
92
|
+
private resolvePortalRootEntryRedirect;
|
|
93
|
+
private getPortalMatch;
|
|
94
|
+
private isPortalIndexRequest;
|
|
95
|
+
private getPortalDistRoot;
|
|
83
96
|
private isV2Request;
|
|
84
97
|
private isV2IndexRequest;
|
|
85
98
|
private getV2RuntimeConfig;
|
|
@@ -87,6 +100,11 @@ export declare class Gateway extends EventEmitter {
|
|
|
87
100
|
private getV2AssetPublicPath;
|
|
88
101
|
private getV2IndexTemplate;
|
|
89
102
|
private renderV2IndexHtml;
|
|
103
|
+
private getSettingsRuntimeConfig;
|
|
104
|
+
private getSettingsRuntimeConfigScript;
|
|
105
|
+
private getSettingsAssetPublicPath;
|
|
106
|
+
private getSettingsIndexTemplate;
|
|
107
|
+
private renderSettingsIndexHtml;
|
|
90
108
|
requestHandler(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
91
109
|
getAppSelectorMiddlewares(): Toposort<AppSelectorMiddleware>;
|
|
92
110
|
getRequestHandleAppName(req: IncomingMessage | IncomingRequest): Promise<string>;
|
package/lib/gateway/index.js
CHANGED
|
@@ -113,6 +113,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
113
113
|
host = "0.0.0.0";
|
|
114
114
|
socketPath = getSocketPath();
|
|
115
115
|
v2IndexTemplateCache = null;
|
|
116
|
+
settingsIndexTemplateCache = null;
|
|
116
117
|
terminating = false;
|
|
117
118
|
getOriginalRequestUrl(req) {
|
|
118
119
|
return req.originalUrl || req.url;
|
|
@@ -165,6 +166,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
165
166
|
}, "onTerminate");
|
|
166
167
|
constructor() {
|
|
167
168
|
super();
|
|
169
|
+
(0, import_utils3.normalizeModernClientPrefix)(import_node_process.default.env.APP_MODERN_CLIENT_PREFIX);
|
|
168
170
|
this.reset();
|
|
169
171
|
import_node_process.default.once("SIGTERM", this.onTerminate);
|
|
170
172
|
import_node_process.default.once("SIGINT", this.onTerminate);
|
|
@@ -197,19 +199,28 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
197
199
|
this.selectorMiddlewares = new import_utils.Toposort();
|
|
198
200
|
this.addAppSelectorMiddleware(
|
|
199
201
|
async (ctx, next) => {
|
|
200
|
-
var _a, _b;
|
|
202
|
+
var _a, _b, _c;
|
|
201
203
|
const { req } = ctx;
|
|
202
204
|
const parsedUrl = (0, import_url.parse)(req.url);
|
|
203
205
|
const appName = (_a = import_qs.default.parse(parsedUrl.query)) == null ? void 0 : _a.__appName;
|
|
204
206
|
const apiBasePath = normalizeBasePath(import_node_process.default.env.API_BASE_PATH || "/api");
|
|
205
207
|
const appPathPrefix = `${apiBasePath}/__app/`;
|
|
208
|
+
const appPublicPath = (0, import_utils3.resolvePublicPath)(import_node_process.default.env.APP_PUBLIC_PATH || "/");
|
|
209
|
+
const portalAppsPathPrefix = `${appPublicPath.replace(/\/$/, "")}/${import_utils3.PORTAL_CLIENT_PREFIX}/apps/`;
|
|
206
210
|
if (req.headers["x-app"]) {
|
|
207
211
|
ctx.resolvedAppName = req.headers["x-app"];
|
|
208
212
|
}
|
|
209
213
|
if (appName) {
|
|
210
214
|
ctx.resolvedAppName = appName;
|
|
211
215
|
}
|
|
212
|
-
if ((_b = parsedUrl.pathname) == null ? void 0 : _b.startsWith(
|
|
216
|
+
if ((_b = parsedUrl.pathname) == null ? void 0 : _b.startsWith(portalAppsPathPrefix)) {
|
|
217
|
+
const restPath = parsedUrl.pathname.slice(portalAppsPathPrefix.length);
|
|
218
|
+
const [pathAppName] = restPath.split("/");
|
|
219
|
+
if (pathAppName) {
|
|
220
|
+
ctx.resolvedAppName = (0, import_utils3.normalizePortalAppName)(pathAppName);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if ((_c = parsedUrl.pathname) == null ? void 0 : _c.startsWith(appPathPrefix)) {
|
|
213
224
|
const restPath = parsedUrl.pathname.slice(appPathPrefix.length);
|
|
214
225
|
const [pathAppName, ...segments] = restPath.split("/");
|
|
215
226
|
if (pathAppName) {
|
|
@@ -301,6 +312,156 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
301
312
|
getAppPublicPath() {
|
|
302
313
|
return (0, import_utils3.resolvePublicPath)(import_node_process.default.env.APP_PUBLIC_PATH || "/");
|
|
303
314
|
}
|
|
315
|
+
getSettingsPublicPath() {
|
|
316
|
+
return (0, import_utils3.resolveSettingsPublicPath)(import_node_process.default.env.APP_PUBLIC_PATH || "/");
|
|
317
|
+
}
|
|
318
|
+
getPathWithinAppPublicPath(pathname) {
|
|
319
|
+
const appPublicPath = this.getAppPublicPath();
|
|
320
|
+
if (appPublicPath === "/") {
|
|
321
|
+
return pathname;
|
|
322
|
+
}
|
|
323
|
+
const appPublicPathWithoutSlash = appPublicPath.slice(0, -1);
|
|
324
|
+
if (pathname === appPublicPathWithoutSlash) {
|
|
325
|
+
return "/";
|
|
326
|
+
}
|
|
327
|
+
if (!pathname.startsWith(appPublicPath)) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
return pathname.slice(appPublicPath.length - 1);
|
|
331
|
+
}
|
|
332
|
+
isSettingsRequest(pathname) {
|
|
333
|
+
const appPath = this.getPathWithinAppPublicPath(pathname);
|
|
334
|
+
if (!appPath) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
return /^\/settings(?:\/|$)/.test(appPath);
|
|
338
|
+
}
|
|
339
|
+
isSettingsIndexRequest(pathname) {
|
|
340
|
+
if (!this.isSettingsRequest(pathname)) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
if (pathname.endsWith("/index.html")) {
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
return !(0, import_path.extname)(pathname);
|
|
347
|
+
}
|
|
348
|
+
isSettingsAssetsRequest(pathname) {
|
|
349
|
+
const appPath = this.getPathWithinAppPublicPath(pathname);
|
|
350
|
+
return appPath ? /^\/settings\/assets\//.test(appPath) : false;
|
|
351
|
+
}
|
|
352
|
+
resolveLegacyV2SettingsRedirect(pathname) {
|
|
353
|
+
const appPath = this.getPathWithinAppPublicPath(pathname);
|
|
354
|
+
if (!appPath) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
const modernPrefix = (0, import_utils3.normalizeModernClientPrefix)(import_node_process.default.env.APP_MODERN_CLIENT_PREFIX);
|
|
358
|
+
const escapedModernPrefix = modernPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
359
|
+
const match = appPath.match(new RegExp(`^/${escapedModernPrefix}((?:/(?:apps|_app)/[^/]+)?)(/admin(?:/.*)?)$`));
|
|
360
|
+
if (!match) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
const appScope = match[1] || "";
|
|
364
|
+
const legacyPath = match[2];
|
|
365
|
+
const mappings = [
|
|
366
|
+
{
|
|
367
|
+
from: "/admin/settings/mail/oauth2",
|
|
368
|
+
to: "/admin/settings/mail/oauth2"
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
from: "/admin/workflow/executions",
|
|
372
|
+
to: "/settings/workflow/executions"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
from: "/admin/workflow/workflows",
|
|
376
|
+
to: "/settings/workflow/workflows"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
from: "/admin/settings",
|
|
380
|
+
to: "/settings"
|
|
381
|
+
}
|
|
382
|
+
];
|
|
383
|
+
for (const { from, to } of mappings) {
|
|
384
|
+
if (legacyPath !== from && !legacyPath.startsWith(`${from}/`)) {
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
const appPublicPath = this.getAppPublicPath().replace(/\/$/, "");
|
|
388
|
+
const targetPath = from === "/admin/settings/mail/oauth2" ? `${appScope}${to}` : appScope ? `/settings${appScope}${to.replace(/^\/settings(?=\/|$)/, "")}` : to;
|
|
389
|
+
return `${appPublicPath}${targetPath}${legacyPath.slice(from.length)}`;
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
getPortalRootPublicPath() {
|
|
394
|
+
return `${this.getAppPublicPath().replace(/\/$/, "")}/${import_utils3.PORTAL_CLIENT_PREFIX}/`;
|
|
395
|
+
}
|
|
396
|
+
getPortalAppPublicPath(appName) {
|
|
397
|
+
if (appName === import_utils3.DEFAULT_PORTAL_APP_NAME) {
|
|
398
|
+
return this.getPortalRootPublicPath();
|
|
399
|
+
}
|
|
400
|
+
return `${this.getPortalRootPublicPath()}apps/${(0, import_utils3.normalizePortalAppName)(appName)}/`;
|
|
401
|
+
}
|
|
402
|
+
resolvePortalRootEntryRedirect(pathname) {
|
|
403
|
+
const portalRootPublicPath = this.getPortalRootPublicPath();
|
|
404
|
+
const v2PublicPath = this.getV2PublicPath();
|
|
405
|
+
if (v2PublicPath === portalRootPublicPath) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
if (pathname === portalRootPublicPath || pathname === portalRootPublicPath.slice(0, -1)) {
|
|
409
|
+
return v2PublicPath;
|
|
410
|
+
}
|
|
411
|
+
const portalAppsPublicPath = `${portalRootPublicPath}apps/`;
|
|
412
|
+
if (!pathname.startsWith(portalAppsPublicPath)) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
const match = /^([A-Za-z0-9_-]+)\/?$/.exec(pathname.slice(portalAppsPublicPath.length));
|
|
416
|
+
return match ? `${v2PublicPath}apps/${match[1]}/` : null;
|
|
417
|
+
}
|
|
418
|
+
getPortalMatch(pathname) {
|
|
419
|
+
const portalRootPublicPath = this.getPortalRootPublicPath();
|
|
420
|
+
if (!pathname.startsWith(portalRootPublicPath)) {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
let appName = import_utils3.DEFAULT_PORTAL_APP_NAME;
|
|
424
|
+
let publicRoot = portalRootPublicPath;
|
|
425
|
+
let restPath = pathname.slice(portalRootPublicPath.length).replace(/^\/+/, "");
|
|
426
|
+
const [firstSegment, secondSegment, ...remainingSegments] = restPath.split("/");
|
|
427
|
+
if (firstSegment === "apps") {
|
|
428
|
+
if (!secondSegment || !/^[A-Za-z0-9_-]+$/.test(secondSegment)) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
appName = (0, import_utils3.normalizePortalAppName)(secondSegment);
|
|
432
|
+
publicRoot = this.getPortalAppPublicPath(appName);
|
|
433
|
+
if (!pathname.startsWith(publicRoot)) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
restPath = remainingSegments.join("/").replace(/^\/+/, "");
|
|
437
|
+
}
|
|
438
|
+
const [portalName] = restPath.split("/");
|
|
439
|
+
if (!portalName || !/^[A-Za-z0-9_-]+$/.test(portalName)) {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
appName,
|
|
444
|
+
portalName,
|
|
445
|
+
publicPath: `${publicRoot}${portalName}/`
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
isPortalIndexRequest(pathname, portalPublicPath) {
|
|
449
|
+
if (pathname === portalPublicPath || pathname === portalPublicPath.slice(0, -1) || pathname === `${portalPublicPath}index.html`) {
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
return !(0, import_path.extname)(pathname);
|
|
453
|
+
}
|
|
454
|
+
getPortalDistRoot(portalMatch) {
|
|
455
|
+
const scopedRoot = (0, import_utils.storagePathJoin)("portals", portalMatch.appName, portalMatch.portalName, "dist");
|
|
456
|
+
if (portalMatch.appName !== import_utils3.DEFAULT_PORTAL_APP_NAME) {
|
|
457
|
+
return scopedRoot;
|
|
458
|
+
}
|
|
459
|
+
const legacyRoot = (0, import_utils.storagePathJoin)("portals", portalMatch.portalName, "dist");
|
|
460
|
+
if (!import_fs.default.existsSync((0, import_path.resolve)(scopedRoot, "index.html")) && import_fs.default.existsSync((0, import_path.resolve)(legacyRoot, "index.html"))) {
|
|
461
|
+
return legacyRoot;
|
|
462
|
+
}
|
|
463
|
+
return scopedRoot;
|
|
464
|
+
}
|
|
304
465
|
isV2Request(pathname) {
|
|
305
466
|
const v2PublicPath = this.getV2PublicPath();
|
|
306
467
|
return pathname === v2PublicPath.slice(0, -1) || pathname.startsWith(v2PublicPath);
|
|
@@ -367,6 +528,57 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
367
528
|
const html = (0, import_utils3.rewriteV2AssetPublicPath)(template, this.getV2AssetPublicPath());
|
|
368
529
|
return (0, import_utils3.injectRuntimeScript)(html, this.getV2RuntimeConfigScript());
|
|
369
530
|
}
|
|
531
|
+
getSettingsRuntimeConfig() {
|
|
532
|
+
return {
|
|
533
|
+
__nocobase_public_path__: this.getAppPublicPath(),
|
|
534
|
+
__nocobase_modern_client_prefix__: (0, import_utils3.normalizeModernClientPrefix)(import_node_process.default.env.APP_MODERN_CLIENT_PREFIX),
|
|
535
|
+
__webpack_public_path__: import_node_process.default.env.CDN_BASE_URL ? `${import_node_process.default.env.CDN_BASE_URL.replace(/\/+$/, "")}/` : "",
|
|
536
|
+
__nocobase_api_base_url__: import_node_process.default.env.API_BASE_URL || import_node_process.default.env.API_BASE_PATH,
|
|
537
|
+
__nocobase_api_client_storage_prefix__: import_node_process.default.env.API_CLIENT_STORAGE_PREFIX,
|
|
538
|
+
__nocobase_api_client_storage_type__: import_node_process.default.env.API_CLIENT_STORAGE_TYPE,
|
|
539
|
+
__nocobase_api_client_share_token__: import_node_process.default.env.API_CLIENT_SHARE_TOKEN === "true",
|
|
540
|
+
__nocobase_ws_url__: import_node_process.default.env.WEBSOCKET_URL || "",
|
|
541
|
+
__nocobase_ws_path__: import_node_process.default.env.WS_PATH,
|
|
542
|
+
__nocobase_app_dev__: import_node_process.default.env.NOCOBASE_APP_DEV === "true",
|
|
543
|
+
__esm_cdn_base_url__: import_node_process.default.env.ESM_CDN_BASE_URL || "https://esm.sh",
|
|
544
|
+
__esm_cdn_suffix__: import_node_process.default.env.ESM_CDN_SUFFIX || ""
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
getSettingsRuntimeConfigScript() {
|
|
548
|
+
const scriptContent = Object.entries(this.getSettingsRuntimeConfig()).map(([key, value]) => `window['${key}'] = ${JSON.stringify(value)};`).join("\n");
|
|
549
|
+
return `<script>${scriptContent}</script>`;
|
|
550
|
+
}
|
|
551
|
+
getSettingsAssetPublicPath() {
|
|
552
|
+
if (import_node_process.default.env.CDN_BASE_URL) {
|
|
553
|
+
return `${import_node_process.default.env.CDN_BASE_URL.replace(/\/+$/, "")}/${import_utils3.SETTINGS_CLIENT_DIST_DIR}/`;
|
|
554
|
+
}
|
|
555
|
+
return this.getSettingsPublicPath();
|
|
556
|
+
}
|
|
557
|
+
getSettingsIndexTemplate() {
|
|
558
|
+
const file = `${import_node_process.default.env.APP_PACKAGE_ROOT}/dist/client/${import_utils3.SETTINGS_CLIENT_DIST_DIR}/index.html`;
|
|
559
|
+
if (!import_fs.default.existsSync(file)) {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
const stat = import_fs.default.statSync(file);
|
|
563
|
+
if (this.settingsIndexTemplateCache && this.settingsIndexTemplateCache.file === file && this.settingsIndexTemplateCache.mtimeMs === stat.mtimeMs) {
|
|
564
|
+
return this.settingsIndexTemplateCache.html;
|
|
565
|
+
}
|
|
566
|
+
const html = import_fs.default.readFileSync(file, "utf-8");
|
|
567
|
+
this.settingsIndexTemplateCache = {
|
|
568
|
+
file,
|
|
569
|
+
mtimeMs: stat.mtimeMs,
|
|
570
|
+
html
|
|
571
|
+
};
|
|
572
|
+
return html;
|
|
573
|
+
}
|
|
574
|
+
renderSettingsIndexHtml() {
|
|
575
|
+
const template = this.getSettingsIndexTemplate();
|
|
576
|
+
if (!template) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
const html = (0, import_utils3.rewriteSettingsAssetPublicPath)(template, this.getSettingsAssetPublicPath());
|
|
580
|
+
return (0, import_utils3.injectRuntimeScript)(html, this.getSettingsRuntimeConfigScript());
|
|
581
|
+
}
|
|
370
582
|
async requestHandler(req, res) {
|
|
371
583
|
const { pathname, search } = (0, import_url.parse)(req.url);
|
|
372
584
|
const { PLUGIN_STATICS_PATH } = import_node_process.default.env;
|
|
@@ -382,6 +594,20 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
382
594
|
res.end();
|
|
383
595
|
return;
|
|
384
596
|
}
|
|
597
|
+
const settingsRedirect = this.resolveLegacyV2SettingsRedirect(pathname);
|
|
598
|
+
if (settingsRedirect) {
|
|
599
|
+
res.statusCode = 302;
|
|
600
|
+
res.setHeader("Location", `${settingsRedirect}${search || ""}`);
|
|
601
|
+
res.end();
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
const portalRootEntryRedirect = this.resolvePortalRootEntryRedirect(pathname);
|
|
605
|
+
if (portalRootEntryRedirect) {
|
|
606
|
+
res.statusCode = 302;
|
|
607
|
+
res.setHeader("Location", `${portalRootEntryRedirect}${search || ""}`);
|
|
608
|
+
res.end();
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
385
611
|
const supervisor = import_app_supervisor.AppSupervisor.getInstance();
|
|
386
612
|
let handleApp = "main";
|
|
387
613
|
try {
|
|
@@ -429,6 +655,70 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
429
655
|
const isFilesRequest = Boolean(getFileAccessRestPath(pathname, APP_PUBLIC_PATH));
|
|
430
656
|
const isLegacyUploadRequest = pathname.startsWith(APP_PUBLIC_PATH + "storage/uploads/");
|
|
431
657
|
if (!pathname.startsWith(import_node_process.default.env.API_BASE_PATH) && !isFilesRequest && !isLegacyUploadRequest) {
|
|
658
|
+
if (this.isSettingsRequest(pathname)) {
|
|
659
|
+
if (handleApp !== "main") {
|
|
660
|
+
const isProxy = await this.proxyRequestToSubApp(supervisor, handleApp, req, res);
|
|
661
|
+
if (isProxy) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (this.isSettingsIndexRequest(pathname)) {
|
|
666
|
+
const settingsHtml = this.renderSettingsIndexHtml();
|
|
667
|
+
if (settingsHtml) {
|
|
668
|
+
res.setHeader("Cache-Control", "no-store");
|
|
669
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
670
|
+
res.end(settingsHtml);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
if (this.isSettingsAssetsRequest(pathname)) {
|
|
675
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
676
|
+
}
|
|
677
|
+
req.url = req.url.substring(APP_PUBLIC_PATH.length - 1);
|
|
678
|
+
await compress(req, res);
|
|
679
|
+
return (0, import_serve_handler.default)(req, res, {
|
|
680
|
+
public: `${import_node_process.default.env.APP_PACKAGE_ROOT}/dist/client`
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
const portalMatch = this.getPortalMatch(pathname);
|
|
684
|
+
if (portalMatch) {
|
|
685
|
+
if (handleApp !== "main" && handleApp !== portalMatch.appName) {
|
|
686
|
+
const isProxy = await this.proxyRequestToSubApp(supervisor, handleApp, req, res);
|
|
687
|
+
if (isProxy) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
const portalDistRoot = this.getPortalDistRoot(portalMatch);
|
|
692
|
+
const portalIndex = (0, import_path.resolve)(portalDistRoot, "index.html");
|
|
693
|
+
if (!import_fs.default.existsSync(portalIndex)) {
|
|
694
|
+
res.statusCode = 404;
|
|
695
|
+
res.end();
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
if (!pathname.startsWith(portalMatch.publicPath)) {
|
|
699
|
+
res.statusCode = 302;
|
|
700
|
+
res.setHeader("Location", `${portalMatch.publicPath}${search || ""}`);
|
|
701
|
+
res.end();
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if (this.isPortalIndexRequest(pathname, portalMatch.publicPath)) {
|
|
705
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
706
|
+
res.end(import_fs.default.readFileSync(portalIndex, "utf-8"));
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
req.url = req.url.substring(portalMatch.publicPath.length - 1);
|
|
710
|
+
await compress(req, res);
|
|
711
|
+
return (0, import_serve_handler.default)(req, res, {
|
|
712
|
+
public: portalDistRoot,
|
|
713
|
+
directoryListing: false
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
const portalRootPublicPath = this.getPortalRootPublicPath();
|
|
717
|
+
if (pathname === portalRootPublicPath.slice(0, -1) || pathname.startsWith(portalRootPublicPath)) {
|
|
718
|
+
res.statusCode = 404;
|
|
719
|
+
res.end();
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
432
722
|
if (this.isV2Request(pathname)) {
|
|
433
723
|
if (handleApp !== "main") {
|
|
434
724
|
const isProxy = await this.proxyRequestToSubApp(supervisor, handleApp, req, res);
|
package/lib/gateway/utils.d.ts
CHANGED
|
@@ -10,10 +10,19 @@
|
|
|
10
10
|
import { IncomingMessage } from 'http';
|
|
11
11
|
import { IncomingRequest } from '.';
|
|
12
12
|
export declare const MODERN_CLIENT_DIST_DIR = "v";
|
|
13
|
+
export declare const SETTINGS_CLIENT_DIST_DIR = "settings";
|
|
14
|
+
export declare const PORTAL_CLIENT_PREFIX = "x";
|
|
15
|
+
export declare const DEFAULT_PORTAL_APP_NAME = "main";
|
|
16
|
+
export declare const DEFAULT_PORTAL_NAME = "admin";
|
|
13
17
|
export declare function resolvePublicPath(appPublicPath?: string): string;
|
|
14
18
|
export declare function normalizeModernClientPrefix(value?: string): string;
|
|
15
19
|
export declare function resolveV2PublicPath(appPublicPath?: string): string;
|
|
20
|
+
export declare function resolveSettingsPublicPath(appPublicPath?: string): string;
|
|
21
|
+
export declare function normalizePortalName(value?: string): string;
|
|
22
|
+
export declare function normalizePortalAppName(value?: string): string;
|
|
23
|
+
export declare function resolvePortalPublicPath(portalName: string, appPublicPath?: string): string;
|
|
16
24
|
export declare function rewriteV2AssetPublicPath(html: string, assetPublicPath: string): string;
|
|
25
|
+
export declare function rewriteSettingsAssetPublicPath(html: string, assetPublicPath: string): string;
|
|
17
26
|
export declare function injectRuntimeScript(html: string, runtimeScript: string): string;
|
|
18
27
|
export declare function getHost(req: IncomingMessage | IncomingRequest): any;
|
|
19
28
|
export declare function getHostname(req: IncomingMessage | IncomingRequest): any;
|
package/lib/gateway/utils.js
CHANGED
|
@@ -27,17 +27,30 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
27
27
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
28
|
var utils_exports = {};
|
|
29
29
|
__export(utils_exports, {
|
|
30
|
+
DEFAULT_PORTAL_APP_NAME: () => DEFAULT_PORTAL_APP_NAME,
|
|
31
|
+
DEFAULT_PORTAL_NAME: () => DEFAULT_PORTAL_NAME,
|
|
30
32
|
MODERN_CLIENT_DIST_DIR: () => MODERN_CLIENT_DIST_DIR,
|
|
33
|
+
PORTAL_CLIENT_PREFIX: () => PORTAL_CLIENT_PREFIX,
|
|
34
|
+
SETTINGS_CLIENT_DIST_DIR: () => SETTINGS_CLIENT_DIST_DIR,
|
|
31
35
|
getHost: () => getHost,
|
|
32
36
|
getHostname: () => getHostname,
|
|
33
37
|
injectRuntimeScript: () => injectRuntimeScript,
|
|
34
38
|
normalizeModernClientPrefix: () => normalizeModernClientPrefix,
|
|
39
|
+
normalizePortalAppName: () => normalizePortalAppName,
|
|
40
|
+
normalizePortalName: () => normalizePortalName,
|
|
41
|
+
resolvePortalPublicPath: () => resolvePortalPublicPath,
|
|
35
42
|
resolvePublicPath: () => resolvePublicPath,
|
|
43
|
+
resolveSettingsPublicPath: () => resolveSettingsPublicPath,
|
|
36
44
|
resolveV2PublicPath: () => resolveV2PublicPath,
|
|
45
|
+
rewriteSettingsAssetPublicPath: () => rewriteSettingsAssetPublicPath,
|
|
37
46
|
rewriteV2AssetPublicPath: () => rewriteV2AssetPublicPath
|
|
38
47
|
});
|
|
39
48
|
module.exports = __toCommonJS(utils_exports);
|
|
40
49
|
const MODERN_CLIENT_DIST_DIR = "v";
|
|
50
|
+
const SETTINGS_CLIENT_DIST_DIR = "settings";
|
|
51
|
+
const PORTAL_CLIENT_PREFIX = "x";
|
|
52
|
+
const DEFAULT_PORTAL_APP_NAME = "main";
|
|
53
|
+
const DEFAULT_PORTAL_NAME = "admin";
|
|
41
54
|
function resolvePublicPath(appPublicPath = "/") {
|
|
42
55
|
const normalized = String(appPublicPath || "/").trim() || "/";
|
|
43
56
|
const withLeadingSlash = normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
@@ -46,7 +59,11 @@ function resolvePublicPath(appPublicPath = "/") {
|
|
|
46
59
|
__name(resolvePublicPath, "resolvePublicPath");
|
|
47
60
|
function normalizeModernClientPrefix(value) {
|
|
48
61
|
const segment = String(value || "").trim().replace(/^\/+|\/+$/g, "");
|
|
49
|
-
|
|
62
|
+
const normalized = segment || MODERN_CLIENT_DIST_DIR;
|
|
63
|
+
if (normalized === SETTINGS_CLIENT_DIST_DIR) {
|
|
64
|
+
throw new Error('APP_MODERN_CLIENT_PREFIX "settings" is reserved for the standalone Settings application.');
|
|
65
|
+
}
|
|
66
|
+
return normalized;
|
|
50
67
|
}
|
|
51
68
|
__name(normalizeModernClientPrefix, "normalizeModernClientPrefix");
|
|
52
69
|
function resolveV2PublicPath(appPublicPath = "/") {
|
|
@@ -55,6 +72,26 @@ function resolveV2PublicPath(appPublicPath = "/") {
|
|
|
55
72
|
return `${publicPath.replace(/\/$/, "")}/${prefix}/`;
|
|
56
73
|
}
|
|
57
74
|
__name(resolveV2PublicPath, "resolveV2PublicPath");
|
|
75
|
+
function resolveSettingsPublicPath(appPublicPath = "/") {
|
|
76
|
+
const publicPath = resolvePublicPath(appPublicPath);
|
|
77
|
+
return `${publicPath.replace(/\/$/, "")}/${SETTINGS_CLIENT_DIST_DIR}/`;
|
|
78
|
+
}
|
|
79
|
+
__name(resolveSettingsPublicPath, "resolveSettingsPublicPath");
|
|
80
|
+
function normalizePortalName(value) {
|
|
81
|
+
const segment = String(value || "").trim().replace(/^\/+|\/+$/g, "");
|
|
82
|
+
return segment || DEFAULT_PORTAL_NAME;
|
|
83
|
+
}
|
|
84
|
+
__name(normalizePortalName, "normalizePortalName");
|
|
85
|
+
function normalizePortalAppName(value) {
|
|
86
|
+
const segment = String(value || "").trim().replace(/^\/+|\/+$/g, "");
|
|
87
|
+
return segment || DEFAULT_PORTAL_APP_NAME;
|
|
88
|
+
}
|
|
89
|
+
__name(normalizePortalAppName, "normalizePortalAppName");
|
|
90
|
+
function resolvePortalPublicPath(portalName, appPublicPath = "/") {
|
|
91
|
+
const publicPath = resolvePublicPath(appPublicPath);
|
|
92
|
+
return `${publicPath.replace(/\/$/, "")}/${PORTAL_CLIENT_PREFIX}/${normalizePortalName(portalName)}/`;
|
|
93
|
+
}
|
|
94
|
+
__name(resolvePortalPublicPath, "resolvePortalPublicPath");
|
|
58
95
|
function ensureTrailingSlash(value) {
|
|
59
96
|
return value.endsWith("/") ? value : `${value}/`;
|
|
60
97
|
}
|
|
@@ -69,6 +106,16 @@ function rewriteV2AssetPublicPath(html, assetPublicPath) {
|
|
|
69
106
|
return html.replace(sentinelPattern, `$1${normalizedAssetPublicPath}`);
|
|
70
107
|
}
|
|
71
108
|
__name(rewriteV2AssetPublicPath, "rewriteV2AssetPublicPath");
|
|
109
|
+
function rewriteSettingsAssetPublicPath(html, assetPublicPath) {
|
|
110
|
+
const normalizedAssetPublicPath = ensureTrailingSlash(assetPublicPath);
|
|
111
|
+
const sentinel = `/${SETTINGS_CLIENT_DIST_DIR}/`;
|
|
112
|
+
if (normalizedAssetPublicPath === sentinel) {
|
|
113
|
+
return html;
|
|
114
|
+
}
|
|
115
|
+
const sentinelPattern = new RegExp(`((?:src|href)=["'])${sentinel.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "g");
|
|
116
|
+
return html.replace(sentinelPattern, `$1${normalizedAssetPublicPath}`);
|
|
117
|
+
}
|
|
118
|
+
__name(rewriteSettingsAssetPublicPath, "rewriteSettingsAssetPublicPath");
|
|
72
119
|
function injectRuntimeScript(html, runtimeScript) {
|
|
73
120
|
const browserCheckerScriptMatch = html.match(/<script\b[^>]*browser-checker\.js[^>]*><\/script>/i);
|
|
74
121
|
if (browserCheckerScriptMatch == null ? void 0 : browserCheckerScriptMatch[0]) {
|
|
@@ -117,12 +164,21 @@ function getHostname(req) {
|
|
|
117
164
|
__name(getHostname, "getHostname");
|
|
118
165
|
// Annotate the CommonJS export names for ESM import in node:
|
|
119
166
|
0 && (module.exports = {
|
|
167
|
+
DEFAULT_PORTAL_APP_NAME,
|
|
168
|
+
DEFAULT_PORTAL_NAME,
|
|
120
169
|
MODERN_CLIENT_DIST_DIR,
|
|
170
|
+
PORTAL_CLIENT_PREFIX,
|
|
171
|
+
SETTINGS_CLIENT_DIST_DIR,
|
|
121
172
|
getHost,
|
|
122
173
|
getHostname,
|
|
123
174
|
injectRuntimeScript,
|
|
124
175
|
normalizeModernClientPrefix,
|
|
176
|
+
normalizePortalAppName,
|
|
177
|
+
normalizePortalName,
|
|
178
|
+
resolvePortalPublicPath,
|
|
125
179
|
resolvePublicPath,
|
|
180
|
+
resolveSettingsPublicPath,
|
|
126
181
|
resolveV2PublicPath,
|
|
182
|
+
rewriteSettingsAssetPublicPath,
|
|
127
183
|
rewriteV2AssetPublicPath
|
|
128
184
|
});
|
|
@@ -79,7 +79,7 @@ module.exports = __toCommonJS(utils_exports);
|
|
|
79
79
|
var import_utils = require("@nocobase/utils");
|
|
80
80
|
var import_plugin_symlink = require("@nocobase/utils/plugin-symlink");
|
|
81
81
|
var import_axios = __toESM(require("axios"));
|
|
82
|
-
var import_decompress = __toESM(require("decompress"));
|
|
82
|
+
var import_decompress = __toESM(require("@xhmikosr/decompress"));
|
|
83
83
|
var import_fast_glob = __toESM(require("fast-glob"));
|
|
84
84
|
var import_fs_extra = __toESM(require("fs-extra"));
|
|
85
85
|
var import_ini = __toESM(require("ini"));
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0-alpha.10",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "./lib/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,25 +10,25 @@
|
|
|
10
10
|
"@koa/cors": "^5.0.0",
|
|
11
11
|
"@koa/multer": "^3.1.0",
|
|
12
12
|
"@koa/router": "^13.1.0",
|
|
13
|
-
"@nocobase/acl": "2.
|
|
14
|
-
"@nocobase/actions": "2.
|
|
15
|
-
"@nocobase/ai": "2.
|
|
16
|
-
"@nocobase/auth": "2.
|
|
17
|
-
"@nocobase/cache": "2.
|
|
18
|
-
"@nocobase/data-source-manager": "2.
|
|
19
|
-
"@nocobase/database": "2.
|
|
20
|
-
"@nocobase/evaluators": "2.
|
|
21
|
-
"@nocobase/lock-manager": "2.
|
|
22
|
-
"@nocobase/logger": "2.
|
|
23
|
-
"@nocobase/resourcer": "2.
|
|
24
|
-
"@nocobase/sdk": "2.
|
|
25
|
-
"@nocobase/snowflake-id": "2.
|
|
26
|
-
"@nocobase/telemetry": "2.
|
|
27
|
-
"@nocobase/utils": "2.
|
|
28
|
-
"@types/decompress": "4.2.7",
|
|
13
|
+
"@nocobase/acl": "2.4.0-alpha.10",
|
|
14
|
+
"@nocobase/actions": "2.4.0-alpha.10",
|
|
15
|
+
"@nocobase/ai": "2.4.0-alpha.10",
|
|
16
|
+
"@nocobase/auth": "2.4.0-alpha.10",
|
|
17
|
+
"@nocobase/cache": "2.4.0-alpha.10",
|
|
18
|
+
"@nocobase/data-source-manager": "2.4.0-alpha.10",
|
|
19
|
+
"@nocobase/database": "2.4.0-alpha.10",
|
|
20
|
+
"@nocobase/evaluators": "2.4.0-alpha.10",
|
|
21
|
+
"@nocobase/lock-manager": "2.4.0-alpha.10",
|
|
22
|
+
"@nocobase/logger": "2.4.0-alpha.10",
|
|
23
|
+
"@nocobase/resourcer": "2.4.0-alpha.10",
|
|
24
|
+
"@nocobase/sdk": "2.4.0-alpha.10",
|
|
25
|
+
"@nocobase/snowflake-id": "2.4.0-alpha.10",
|
|
26
|
+
"@nocobase/telemetry": "2.4.0-alpha.10",
|
|
27
|
+
"@nocobase/utils": "2.4.0-alpha.10",
|
|
29
28
|
"@types/ini": "^1.3.31",
|
|
30
29
|
"@types/koa-send": "^4.1.3",
|
|
31
30
|
"@types/multer": "^1.4.12",
|
|
31
|
+
"@xhmikosr/decompress": "^11.1.4",
|
|
32
32
|
"async-mutex": "^0.5.0",
|
|
33
33
|
"axios": "^1.7.0",
|
|
34
34
|
"chalk": "^4.1.1",
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
"cron": "^2.4.4",
|
|
38
38
|
"cronstrue": "^2.11.0",
|
|
39
39
|
"dayjs": "^1.11.8",
|
|
40
|
-
"decompress": "4.2.1",
|
|
41
40
|
"find-package-json": "^1.2.0",
|
|
42
41
|
"fs-extra": "^11.1.1",
|
|
43
42
|
"i18next": "^22.4.9",
|
|
@@ -61,5 +60,5 @@
|
|
|
61
60
|
"@types/serve-handler": "^6.1.1",
|
|
62
61
|
"@types/ws": "^8.5.5"
|
|
63
62
|
},
|
|
64
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "c869865c0413fbe4ef85b26be1f2b8d7078c38df"
|
|
65
64
|
}
|