@nocobase/server 2.2.0-alpha.10 → 2.2.0-alpha.12

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.
@@ -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,12 @@ 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;
83
90
  private getPortalRootPublicPath;
84
91
  private getPortalAppPublicPath;
85
92
  private getPortalMatch;
@@ -92,6 +99,11 @@ export declare class Gateway extends EventEmitter {
92
99
  private getV2AssetPublicPath;
93
100
  private getV2IndexTemplate;
94
101
  private renderV2IndexHtml;
102
+ private getSettingsRuntimeConfig;
103
+ private getSettingsRuntimeConfigScript;
104
+ private getSettingsAssetPublicPath;
105
+ private getSettingsIndexTemplate;
106
+ private renderSettingsIndexHtml;
95
107
  requestHandler(req: IncomingMessage, res: ServerResponse): Promise<void>;
96
108
  getAppSelectorMiddlewares(): Toposort<AppSelectorMiddleware>;
97
109
  getRequestHandleAppName(req: IncomingMessage | IncomingRequest): Promise<string>;
@@ -114,6 +114,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
114
114
  host = "0.0.0.0";
115
115
  socketPath = getSocketPath();
116
116
  v2IndexTemplateCache = null;
117
+ settingsIndexTemplateCache = null;
117
118
  terminating = false;
118
119
  getOriginalRequestUrl(req) {
119
120
  return req.originalUrl || req.url;
@@ -156,6 +157,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
156
157
  }, "onTerminate");
157
158
  constructor() {
158
159
  super();
160
+ (0, import_utils3.normalizeModernClientPrefix)(import_node_process.default.env.APP_MODERN_CLIENT_PREFIX);
159
161
  this.reset();
160
162
  import_node_process.default.once("SIGTERM", this.onTerminate);
161
163
  import_node_process.default.once("SIGINT", this.onTerminate);
@@ -301,6 +303,88 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
301
303
  getAppPublicPath() {
302
304
  return (0, import_utils3.resolvePublicPath)(import_node_process.default.env.APP_PUBLIC_PATH || "/");
303
305
  }
306
+ getSettingsPublicPath() {
307
+ return (0, import_utils3.resolveSettingsPublicPath)(import_node_process.default.env.APP_PUBLIC_PATH || "/");
308
+ }
309
+ getPathWithinAppPublicPath(pathname) {
310
+ const appPublicPath = this.getAppPublicPath();
311
+ if (appPublicPath === "/") {
312
+ return pathname;
313
+ }
314
+ const appPublicPathWithoutSlash = appPublicPath.slice(0, -1);
315
+ if (pathname === appPublicPathWithoutSlash) {
316
+ return "/";
317
+ }
318
+ if (!pathname.startsWith(appPublicPath)) {
319
+ return null;
320
+ }
321
+ return pathname.slice(appPublicPath.length - 1);
322
+ }
323
+ isSettingsRequest(pathname) {
324
+ const appPath = this.getPathWithinAppPublicPath(pathname);
325
+ if (!appPath) {
326
+ return false;
327
+ }
328
+ return /^\/settings(?:\/|$)/.test(appPath);
329
+ }
330
+ isSettingsIndexRequest(pathname) {
331
+ if (!this.isSettingsRequest(pathname)) {
332
+ return false;
333
+ }
334
+ if (pathname.endsWith("/index.html")) {
335
+ return true;
336
+ }
337
+ return !(0, import_path.extname)(pathname);
338
+ }
339
+ isSettingsAssetsRequest(pathname) {
340
+ const appPath = this.getPathWithinAppPublicPath(pathname);
341
+ return appPath ? /^\/settings\/assets\//.test(appPath) : false;
342
+ }
343
+ resolveLegacyV2SettingsRedirect(pathname) {
344
+ const appPath = this.getPathWithinAppPublicPath(pathname);
345
+ if (!appPath) {
346
+ return null;
347
+ }
348
+ const modernPrefix = (0, import_utils3.normalizeModernClientPrefix)(import_node_process.default.env.APP_MODERN_CLIENT_PREFIX);
349
+ const escapedModernPrefix = modernPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
350
+ const match = appPath.match(new RegExp(`^/${escapedModernPrefix}((?:/(?:apps|_app)/[^/]+)?)(/admin(?:/.*)?)$`));
351
+ if (!match) {
352
+ return null;
353
+ }
354
+ const appScope = match[1] || "";
355
+ const legacyPath = match[2];
356
+ const mappings = [
357
+ {
358
+ from: "/admin/settings/mail/oauth2",
359
+ to: "/admin/settings/mail/oauth2"
360
+ },
361
+ {
362
+ from: "/admin/ai/knowledge-base/detail",
363
+ to: "/settings/ai/knowledge-base/detail"
364
+ },
365
+ {
366
+ from: "/admin/workflow/executions",
367
+ to: "/settings/workflow/executions"
368
+ },
369
+ {
370
+ from: "/admin/workflow/workflows",
371
+ to: "/settings/workflow/workflows"
372
+ },
373
+ {
374
+ from: "/admin/settings",
375
+ to: "/settings"
376
+ }
377
+ ];
378
+ for (const { from, to } of mappings) {
379
+ if (legacyPath !== from && !legacyPath.startsWith(`${from}/`)) {
380
+ continue;
381
+ }
382
+ const appPublicPath = this.getAppPublicPath().replace(/\/$/, "");
383
+ const targetPath = from === "/admin/settings/mail/oauth2" ? `${appScope}${to}` : appScope ? `/settings${appScope}${to.replace(/^\/settings(?=\/|$)/, "")}` : to;
384
+ return `${appPublicPath}${targetPath}${legacyPath.slice(from.length)}`;
385
+ }
386
+ return null;
387
+ }
304
388
  getPortalRootPublicPath() {
305
389
  return `${this.getAppPublicPath().replace(/\/$/, "")}/${import_utils3.PORTAL_CLIENT_PREFIX}/`;
306
390
  }
@@ -423,6 +507,57 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
423
507
  const html = (0, import_utils3.rewriteV2AssetPublicPath)(template, this.getV2AssetPublicPath());
424
508
  return (0, import_utils3.injectRuntimeScript)(html, this.getV2RuntimeConfigScript());
425
509
  }
510
+ getSettingsRuntimeConfig() {
511
+ return {
512
+ __nocobase_public_path__: this.getAppPublicPath(),
513
+ __nocobase_modern_client_prefix__: (0, import_utils3.normalizeModernClientPrefix)(import_node_process.default.env.APP_MODERN_CLIENT_PREFIX),
514
+ __webpack_public_path__: import_node_process.default.env.CDN_BASE_URL ? `${import_node_process.default.env.CDN_BASE_URL.replace(/\/+$/, "")}/` : "",
515
+ __nocobase_api_base_url__: import_node_process.default.env.API_BASE_URL || import_node_process.default.env.API_BASE_PATH,
516
+ __nocobase_api_client_storage_prefix__: import_node_process.default.env.API_CLIENT_STORAGE_PREFIX,
517
+ __nocobase_api_client_storage_type__: import_node_process.default.env.API_CLIENT_STORAGE_TYPE,
518
+ __nocobase_api_client_share_token__: import_node_process.default.env.API_CLIENT_SHARE_TOKEN === "true",
519
+ __nocobase_ws_url__: import_node_process.default.env.WEBSOCKET_URL || "",
520
+ __nocobase_ws_path__: import_node_process.default.env.WS_PATH,
521
+ __nocobase_app_dev__: import_node_process.default.env.NOCOBASE_APP_DEV === "true",
522
+ __esm_cdn_base_url__: import_node_process.default.env.ESM_CDN_BASE_URL || "https://esm.sh",
523
+ __esm_cdn_suffix__: import_node_process.default.env.ESM_CDN_SUFFIX || ""
524
+ };
525
+ }
526
+ getSettingsRuntimeConfigScript() {
527
+ const scriptContent = Object.entries(this.getSettingsRuntimeConfig()).map(([key, value]) => `window['${key}'] = ${JSON.stringify(value)};`).join("\n");
528
+ return `<script>${scriptContent}</script>`;
529
+ }
530
+ getSettingsAssetPublicPath() {
531
+ if (import_node_process.default.env.CDN_BASE_URL) {
532
+ return `${import_node_process.default.env.CDN_BASE_URL.replace(/\/+$/, "")}/${import_utils3.SETTINGS_CLIENT_DIST_DIR}/`;
533
+ }
534
+ return this.getSettingsPublicPath();
535
+ }
536
+ getSettingsIndexTemplate() {
537
+ const file = `${import_node_process.default.env.APP_PACKAGE_ROOT}/dist/client/${import_utils3.SETTINGS_CLIENT_DIST_DIR}/index.html`;
538
+ if (!import_fs.default.existsSync(file)) {
539
+ return null;
540
+ }
541
+ const stat = import_fs.default.statSync(file);
542
+ if (this.settingsIndexTemplateCache && this.settingsIndexTemplateCache.file === file && this.settingsIndexTemplateCache.mtimeMs === stat.mtimeMs) {
543
+ return this.settingsIndexTemplateCache.html;
544
+ }
545
+ const html = import_fs.default.readFileSync(file, "utf-8");
546
+ this.settingsIndexTemplateCache = {
547
+ file,
548
+ mtimeMs: stat.mtimeMs,
549
+ html
550
+ };
551
+ return html;
552
+ }
553
+ renderSettingsIndexHtml() {
554
+ const template = this.getSettingsIndexTemplate();
555
+ if (!template) {
556
+ return null;
557
+ }
558
+ const html = (0, import_utils3.rewriteSettingsAssetPublicPath)(template, this.getSettingsAssetPublicPath());
559
+ return (0, import_utils3.injectRuntimeScript)(html, this.getSettingsRuntimeConfigScript());
560
+ }
426
561
  async requestHandler(req, res) {
427
562
  const { pathname, search } = (0, import_url.parse)(req.url);
428
563
  const { PLUGIN_STATICS_PATH } = import_node_process.default.env;
@@ -438,6 +573,13 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
438
573
  res.end();
439
574
  return;
440
575
  }
576
+ const settingsRedirect = this.resolveLegacyV2SettingsRedirect(pathname);
577
+ if (settingsRedirect) {
578
+ res.statusCode = 302;
579
+ res.setHeader("Location", `${settingsRedirect}${search || ""}`);
580
+ res.end();
581
+ return;
582
+ }
441
583
  const supervisor = import_app_supervisor.AppSupervisor.getInstance();
442
584
  let handleApp = "main";
443
585
  try {
@@ -505,6 +647,31 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
505
647
  }
506
648
  const isFilesRequest = Boolean(getFileAccessRestPath(pathname, APP_PUBLIC_PATH));
507
649
  if (!pathname.startsWith(import_node_process.default.env.API_BASE_PATH) && !isFilesRequest) {
650
+ if (this.isSettingsRequest(pathname)) {
651
+ if (handleApp !== "main") {
652
+ const isProxy = await this.proxyRequestToSubApp(supervisor, handleApp, req, res);
653
+ if (isProxy) {
654
+ return;
655
+ }
656
+ }
657
+ if (this.isSettingsIndexRequest(pathname)) {
658
+ const settingsHtml = this.renderSettingsIndexHtml();
659
+ if (settingsHtml) {
660
+ res.setHeader("Cache-Control", "no-store");
661
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
662
+ res.end(settingsHtml);
663
+ return;
664
+ }
665
+ }
666
+ if (this.isSettingsAssetsRequest(pathname)) {
667
+ res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
668
+ }
669
+ req.url = req.url.substring(APP_PUBLIC_PATH.length - 1);
670
+ await compress(req, res);
671
+ return (0, import_serve_handler.default)(req, res, {
672
+ public: `${import_node_process.default.env.APP_PACKAGE_ROOT}/dist/client`
673
+ });
674
+ }
508
675
  const portalMatch = this.getPortalMatch(pathname);
509
676
  if (portalMatch) {
510
677
  if (handleApp !== "main" && handleApp !== portalMatch.appName) {
@@ -10,16 +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";
13
14
  export declare const PORTAL_CLIENT_PREFIX = "x";
14
15
  export declare const DEFAULT_PORTAL_APP_NAME = "main";
15
16
  export declare const DEFAULT_PORTAL_NAME = "admin";
16
17
  export declare function resolvePublicPath(appPublicPath?: string): string;
17
18
  export declare function normalizeModernClientPrefix(value?: string): string;
18
19
  export declare function resolveV2PublicPath(appPublicPath?: string): string;
20
+ export declare function resolveSettingsPublicPath(appPublicPath?: string): string;
19
21
  export declare function normalizePortalName(value?: string): string;
20
22
  export declare function normalizePortalAppName(value?: string): string;
21
23
  export declare function resolvePortalPublicPath(portalName: string, appPublicPath?: string): string;
22
24
  export declare function rewriteV2AssetPublicPath(html: string, assetPublicPath: string): string;
25
+ export declare function rewriteSettingsAssetPublicPath(html: string, assetPublicPath: string): string;
23
26
  export declare function injectRuntimeScript(html: string, runtimeScript: string): string;
24
27
  export declare function getHost(req: IncomingMessage | IncomingRequest): any;
25
28
  export declare function getHostname(req: IncomingMessage | IncomingRequest): any;
@@ -31,6 +31,7 @@ __export(utils_exports, {
31
31
  DEFAULT_PORTAL_NAME: () => DEFAULT_PORTAL_NAME,
32
32
  MODERN_CLIENT_DIST_DIR: () => MODERN_CLIENT_DIST_DIR,
33
33
  PORTAL_CLIENT_PREFIX: () => PORTAL_CLIENT_PREFIX,
34
+ SETTINGS_CLIENT_DIST_DIR: () => SETTINGS_CLIENT_DIST_DIR,
34
35
  getHost: () => getHost,
35
36
  getHostname: () => getHostname,
36
37
  injectRuntimeScript: () => injectRuntimeScript,
@@ -39,11 +40,14 @@ __export(utils_exports, {
39
40
  normalizePortalName: () => normalizePortalName,
40
41
  resolvePortalPublicPath: () => resolvePortalPublicPath,
41
42
  resolvePublicPath: () => resolvePublicPath,
43
+ resolveSettingsPublicPath: () => resolveSettingsPublicPath,
42
44
  resolveV2PublicPath: () => resolveV2PublicPath,
45
+ rewriteSettingsAssetPublicPath: () => rewriteSettingsAssetPublicPath,
43
46
  rewriteV2AssetPublicPath: () => rewriteV2AssetPublicPath
44
47
  });
45
48
  module.exports = __toCommonJS(utils_exports);
46
49
  const MODERN_CLIENT_DIST_DIR = "v";
50
+ const SETTINGS_CLIENT_DIST_DIR = "settings";
47
51
  const PORTAL_CLIENT_PREFIX = "x";
48
52
  const DEFAULT_PORTAL_APP_NAME = "main";
49
53
  const DEFAULT_PORTAL_NAME = "admin";
@@ -55,7 +59,11 @@ function resolvePublicPath(appPublicPath = "/") {
55
59
  __name(resolvePublicPath, "resolvePublicPath");
56
60
  function normalizeModernClientPrefix(value) {
57
61
  const segment = String(value || "").trim().replace(/^\/+|\/+$/g, "");
58
- return segment || MODERN_CLIENT_DIST_DIR;
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;
59
67
  }
60
68
  __name(normalizeModernClientPrefix, "normalizeModernClientPrefix");
61
69
  function resolveV2PublicPath(appPublicPath = "/") {
@@ -64,6 +72,11 @@ function resolveV2PublicPath(appPublicPath = "/") {
64
72
  return `${publicPath.replace(/\/$/, "")}/${prefix}/`;
65
73
  }
66
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");
67
80
  function normalizePortalName(value) {
68
81
  const segment = String(value || "").trim().replace(/^\/+|\/+$/g, "");
69
82
  return segment || DEFAULT_PORTAL_NAME;
@@ -93,6 +106,16 @@ function rewriteV2AssetPublicPath(html, assetPublicPath) {
93
106
  return html.replace(sentinelPattern, `$1${normalizedAssetPublicPath}`);
94
107
  }
95
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");
96
119
  function injectRuntimeScript(html, runtimeScript) {
97
120
  const browserCheckerScriptMatch = html.match(/<script\b[^>]*browser-checker\.js[^>]*><\/script>/i);
98
121
  if (browserCheckerScriptMatch == null ? void 0 : browserCheckerScriptMatch[0]) {
@@ -145,6 +168,7 @@ __name(getHostname, "getHostname");
145
168
  DEFAULT_PORTAL_NAME,
146
169
  MODERN_CLIENT_DIST_DIR,
147
170
  PORTAL_CLIENT_PREFIX,
171
+ SETTINGS_CLIENT_DIST_DIR,
148
172
  getHost,
149
173
  getHostname,
150
174
  injectRuntimeScript,
@@ -153,6 +177,8 @@ __name(getHostname, "getHostname");
153
177
  normalizePortalName,
154
178
  resolvePortalPublicPath,
155
179
  resolvePublicPath,
180
+ resolveSettingsPublicPath,
156
181
  resolveV2PublicPath,
182
+ rewriteSettingsAssetPublicPath,
157
183
  rewriteV2AssetPublicPath
158
184
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/server",
3
- "version": "2.2.0-alpha.10",
3
+ "version": "2.2.0-alpha.12",
4
4
  "main": "lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "license": "Apache-2.0",
@@ -10,21 +10,21 @@
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.2.0-alpha.10",
14
- "@nocobase/actions": "2.2.0-alpha.10",
15
- "@nocobase/ai": "2.2.0-alpha.10",
16
- "@nocobase/auth": "2.2.0-alpha.10",
17
- "@nocobase/cache": "2.2.0-alpha.10",
18
- "@nocobase/data-source-manager": "2.2.0-alpha.10",
19
- "@nocobase/database": "2.2.0-alpha.10",
20
- "@nocobase/evaluators": "2.2.0-alpha.10",
21
- "@nocobase/lock-manager": "2.2.0-alpha.10",
22
- "@nocobase/logger": "2.2.0-alpha.10",
23
- "@nocobase/resourcer": "2.2.0-alpha.10",
24
- "@nocobase/sdk": "2.2.0-alpha.10",
25
- "@nocobase/snowflake-id": "2.2.0-alpha.10",
26
- "@nocobase/telemetry": "2.2.0-alpha.10",
27
- "@nocobase/utils": "2.2.0-alpha.10",
13
+ "@nocobase/acl": "2.2.0-alpha.12",
14
+ "@nocobase/actions": "2.2.0-alpha.12",
15
+ "@nocobase/ai": "2.2.0-alpha.12",
16
+ "@nocobase/auth": "2.2.0-alpha.12",
17
+ "@nocobase/cache": "2.2.0-alpha.12",
18
+ "@nocobase/data-source-manager": "2.2.0-alpha.12",
19
+ "@nocobase/database": "2.2.0-alpha.12",
20
+ "@nocobase/evaluators": "2.2.0-alpha.12",
21
+ "@nocobase/lock-manager": "2.2.0-alpha.12",
22
+ "@nocobase/logger": "2.2.0-alpha.12",
23
+ "@nocobase/resourcer": "2.2.0-alpha.12",
24
+ "@nocobase/sdk": "2.2.0-alpha.12",
25
+ "@nocobase/snowflake-id": "2.2.0-alpha.12",
26
+ "@nocobase/telemetry": "2.2.0-alpha.12",
27
+ "@nocobase/utils": "2.2.0-alpha.12",
28
28
  "@types/decompress": "4.2.7",
29
29
  "@types/ini": "^1.3.31",
30
30
  "@types/koa-send": "^4.1.3",
@@ -61,5 +61,5 @@
61
61
  "@types/serve-handler": "^6.1.1",
62
62
  "@types/ws": "^8.5.5"
63
63
  },
64
- "gitHead": "b5a3f131bb0a4a2fbfd96852896bccadc933d834"
64
+ "gitHead": "96ae69754a6398de63037ead3a4aad5e5d0ab918"
65
65
  }