@nocobase/server 2.2.0-beta.9 → 2.3.0-alpha.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.
@@ -49,6 +49,7 @@ export declare class AppSupervisor extends EventEmitter implements AsyncEmitter
49
49
  private commandAdapterName;
50
50
  private appDbCreator;
51
51
  private appConditions;
52
+ private appManifests;
52
53
  private appSsoIssuer?;
53
54
  appOptionsFactory: AppOptionsFactory;
54
55
  private environmentHeartbeatInterval;
@@ -117,6 +118,8 @@ export declare class AppSupervisor extends EventEmitter implements AsyncEmitter
117
118
  removeAppManifest(appName: string, namespace: string): Promise<void>;
118
119
  getAppManifestItems<T = unknown>(appName: string, namespace: string): Promise<T[]>;
119
120
  getAppManifests<T = unknown>(namespace: string, appNames: string[]): Promise<Record<string, T[]>>;
121
+ private getAppManifestKey;
122
+ private getOrCreateAppManifest;
120
123
  registerAppCondition(name: string, condition: AppCondition): void;
121
124
  unregisterAppCondition(name: string): void;
122
125
  getAppCondition(name: string): AppCondition;
@@ -70,6 +70,7 @@ const _AppSupervisor = class _AppSupervisor extends import_events.EventEmitter {
70
70
  commandAdapterName;
71
71
  appDbCreator = new import_condition_registry.ConditionalRegistry();
72
72
  appConditions = /* @__PURE__ */ new Map();
73
+ appManifests = /* @__PURE__ */ new Map();
73
74
  appSsoIssuer;
74
75
  appOptionsFactory = import_app_options_factory.appOptionsFactory;
75
76
  environmentHeartbeatInterval = 2 * 60 * 1e3;
@@ -284,6 +285,7 @@ const _AppSupervisor = class _AppSupervisor extends import_events.EventEmitter {
284
285
  await this.processAdapter.removeAllApps();
285
286
  await ((_b = (_a = this.discoveryAdapter).dispose) == null ? void 0 : _b.call(_a));
286
287
  await ((_d = (_c = this.commandAdapter) == null ? void 0 : _c.dispose) == null ? void 0 : _d.call(_c));
288
+ this.appManifests.clear();
287
289
  if (this.environmentHeartbeatTimer) {
288
290
  this.environmentHeartbeatTimer = null;
289
291
  }
@@ -436,28 +438,31 @@ const _AppSupervisor = class _AppSupervisor extends import_events.EventEmitter {
436
438
  return this.discoveryAdapter.listAppModels();
437
439
  }
438
440
  async setAppManifestItem(appName, namespace, itemKey, item) {
439
- if (typeof this.discoveryAdapter.setAppManifestItem !== "function") {
440
- return;
441
+ if (typeof this.discoveryAdapter.setAppManifestItem === "function") {
442
+ return this.discoveryAdapter.setAppManifestItem(appName, namespace, itemKey, item);
441
443
  }
442
- return this.discoveryAdapter.setAppManifestItem(appName, namespace, itemKey, item);
444
+ const manifest = this.getOrCreateAppManifest(appName, namespace);
445
+ manifest.set(itemKey, item);
443
446
  }
444
447
  async removeAppManifestItem(appName, namespace, itemKey) {
445
- if (typeof this.discoveryAdapter.removeAppManifestItem !== "function") {
446
- return;
448
+ var _a;
449
+ if (typeof this.discoveryAdapter.removeAppManifestItem === "function") {
450
+ return this.discoveryAdapter.removeAppManifestItem(appName, namespace, itemKey);
447
451
  }
448
- return this.discoveryAdapter.removeAppManifestItem(appName, namespace, itemKey);
452
+ (_a = this.appManifests.get(this.getAppManifestKey(appName, namespace))) == null ? void 0 : _a.delete(itemKey);
449
453
  }
450
454
  async removeAppManifest(appName, namespace) {
451
- if (typeof this.discoveryAdapter.removeAppManifest !== "function") {
452
- return;
455
+ if (typeof this.discoveryAdapter.removeAppManifest === "function") {
456
+ return this.discoveryAdapter.removeAppManifest(appName, namespace);
453
457
  }
454
- return this.discoveryAdapter.removeAppManifest(appName, namespace);
458
+ this.appManifests.delete(this.getAppManifestKey(appName, namespace));
455
459
  }
456
460
  async getAppManifestItems(appName, namespace) {
457
- if (typeof this.discoveryAdapter.getAppManifestItems !== "function") {
458
- return [];
461
+ var _a;
462
+ if (typeof this.discoveryAdapter.getAppManifestItems === "function") {
463
+ return this.discoveryAdapter.getAppManifestItems(appName, namespace);
459
464
  }
460
- return this.discoveryAdapter.getAppManifestItems(appName, namespace);
465
+ return Array.from(((_a = this.appManifests.get(this.getAppManifestKey(appName, namespace))) == null ? void 0 : _a.values()) || []);
461
466
  }
462
467
  async getAppManifests(namespace, appNames) {
463
468
  if (typeof this.discoveryAdapter.getAppManifests === "function") {
@@ -474,6 +479,19 @@ const _AppSupervisor = class _AppSupervisor extends import_events.EventEmitter {
474
479
  );
475
480
  return result;
476
481
  }
482
+ getAppManifestKey(appName, namespace) {
483
+ return `${namespace}:${appName}`;
484
+ }
485
+ getOrCreateAppManifest(appName, namespace) {
486
+ const key = this.getAppManifestKey(appName, namespace);
487
+ const manifest = this.appManifests.get(key);
488
+ if (manifest) {
489
+ return manifest;
490
+ }
491
+ const nextManifest = /* @__PURE__ */ new Map();
492
+ this.appManifests.set(key, nextManifest);
493
+ return nextManifest;
494
+ }
477
495
  registerAppCondition(name, condition) {
478
496
  this.appConditions.set(name, condition);
479
497
  }
@@ -16,18 +16,12 @@ export declare class MainOnlyAdapter implements AppDiscoveryAdapter, AppProcessA
16
16
  apps: Record<string, Application>;
17
17
  status: AppStatus;
18
18
  appErrors: Record<string, Error>;
19
- private readonly appManifests;
20
19
  constructor(supervisor: AppSupervisor);
21
20
  getApp(appName: string, options?: GetAppOptions): Promise<Application<import("../application").DefaultState, import("../application").DefaultContext>>;
22
21
  bootstrapApp(appName: string): Promise<void>;
23
22
  addApp(app: Application): Application<import("../application").DefaultState, import("../application").DefaultContext>;
24
23
  getApps(): Application<import("../application").DefaultState, import("../application").DefaultContext>[];
25
24
  listAppModels(): Promise<any[]>;
26
- setAppManifestItem(appName: string, namespace: string, itemKey: string, item: unknown): Promise<void>;
27
- removeAppManifestItem(appName: string, namespace: string, itemKey: string): Promise<void>;
28
- removeAppManifest(appName: string, namespace: string): Promise<void>;
29
- getAppManifestItems<T = unknown>(appName: string, namespace: string): Promise<T[]>;
30
- getAppManifests<T = unknown>(namespace: string, appNames: string[]): Promise<Record<string, T[]>>;
31
25
  hasApp(appName: string): boolean;
32
26
  startApp(appName: string): Promise<void>;
33
27
  stopApp(appName: string): Promise<void>;
@@ -44,6 +38,4 @@ export declare class MainOnlyAdapter implements AppDiscoveryAdapter, AppProcessA
44
38
  clearAppError(appName: string): void;
45
39
  setAppLastSeenAt(): void;
46
40
  getAppLastSeenAt(appName: string): any;
47
- private getAppManifestKey;
48
- private getOrCreateAppManifest;
49
41
  }
@@ -39,7 +39,6 @@ const _MainOnlyAdapter = class _MainOnlyAdapter {
39
39
  apps = {};
40
40
  status;
41
41
  appErrors = {};
42
- appManifests = /* @__PURE__ */ new Map();
43
42
  async getApp(appName, options = {}) {
44
43
  if (appName !== "main") {
45
44
  this.supervisor.logger.warn(`only main app is supported`, { method: "getApp", appName });
@@ -82,31 +81,6 @@ const _MainOnlyAdapter = class _MainOnlyAdapter {
82
81
  async listAppModels() {
83
82
  return [];
84
83
  }
85
- async setAppManifestItem(appName, namespace, itemKey, item) {
86
- const manifest = this.getOrCreateAppManifest(appName, namespace);
87
- manifest.set(itemKey, item);
88
- }
89
- async removeAppManifestItem(appName, namespace, itemKey) {
90
- var _a;
91
- (_a = this.appManifests.get(this.getAppManifestKey(appName, namespace))) == null ? void 0 : _a.delete(itemKey);
92
- }
93
- async removeAppManifest(appName, namespace) {
94
- this.appManifests.delete(this.getAppManifestKey(appName, namespace));
95
- }
96
- async getAppManifestItems(appName, namespace) {
97
- var _a;
98
- return Array.from(((_a = this.appManifests.get(this.getAppManifestKey(appName, namespace))) == null ? void 0 : _a.values()) || []);
99
- }
100
- async getAppManifests(namespace, appNames) {
101
- const result = {};
102
- for (const appName of appNames) {
103
- const data = await this.getAppManifestItems(appName, namespace);
104
- if (data.length > 0) {
105
- result[appName] = data;
106
- }
107
- }
108
- return result;
109
- }
110
84
  hasApp(appName) {
111
85
  if (appName !== "main") {
112
86
  return false;
@@ -188,19 +162,6 @@ const _MainOnlyAdapter = class _MainOnlyAdapter {
188
162
  getAppLastSeenAt(appName) {
189
163
  return null;
190
164
  }
191
- getAppManifestKey(appName, namespace) {
192
- return `${namespace}:${appName}`;
193
- }
194
- getOrCreateAppManifest(appName, namespace) {
195
- const key = this.getAppManifestKey(appName, namespace);
196
- const manifest = this.appManifests.get(key);
197
- if (manifest) {
198
- return manifest;
199
- }
200
- const nextManifest = /* @__PURE__ */ new Map();
201
- this.appManifests.set(key, nextManifest);
202
- return nextManifest;
203
- }
204
165
  };
205
166
  __name(_MainOnlyAdapter, "MainOnlyAdapter");
206
167
  let MainOnlyAdapter = _MainOnlyAdapter;
@@ -944,6 +944,7 @@ const _Application = class _Application extends import_koa.default {
944
944
  }
945
945
  });
946
946
  this._dataSourceManager.use(this._authManager.middleware(), { tag: "auth", before: "default" });
947
+ this._dataSourceManager.use(import_auth.csrfMiddleware, { tag: "csrf", after: "auth", before: "default" });
947
948
  this._dataSourceManager.use(import_validate_filter_params.default, { tag: "validate-filter-params", before: ["auth"] });
948
949
  this._dataSourceManager.use(import_middlewares.parseVariables, {
949
950
  group: "parseVariables",
@@ -78,6 +78,17 @@ function normalizeBasePath(path = "") {
78
78
  return normalized || "/";
79
79
  }
80
80
  __name(normalizeBasePath, "normalizeBasePath");
81
+ function getFilesPathPrefixes(appPublicPath = "/") {
82
+ const normalizedPublicPath = normalizeBasePath(appPublicPath);
83
+ const canonicalPrefix = `${normalizedPublicPath === "/" ? "" : normalizedPublicPath}/files/`;
84
+ return canonicalPrefix === "/files/" ? ["/files/"] : [canonicalPrefix, "/files/"];
85
+ }
86
+ __name(getFilesPathPrefixes, "getFilesPathPrefixes");
87
+ function getFileAccessRestPath(pathname, appPublicPath = "/") {
88
+ const prefix = getFilesPathPrefixes(appPublicPath).find((prefix2) => pathname.startsWith(prefix2));
89
+ return prefix ? pathname.slice(prefix.length) : null;
90
+ }
91
+ __name(getFileAccessRestPath, "getFileAccessRestPath");
81
92
  function getSocketPath() {
82
93
  const socketPath = import_node_process.default.env.SOCKET_PATH;
83
94
  if (socketPath) {
@@ -202,6 +213,17 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
202
213
  req.url = rewrittenUrl;
203
214
  }
204
215
  }
216
+ const fileAccessRestPath = parsedUrl.pathname ? getFileAccessRestPath(parsedUrl.pathname, import_node_process.default.env.APP_PUBLIC_PATH || "/") : null;
217
+ if (fileAccessRestPath) {
218
+ const restPath = fileAccessRestPath;
219
+ const [pathAppName] = restPath.split("/");
220
+ if (pathAppName) {
221
+ try {
222
+ ctx.resolvedAppName = decodeURIComponent(pathAppName);
223
+ } catch (error) {
224
+ }
225
+ }
226
+ }
205
227
  await next();
206
228
  },
207
229
  {
@@ -337,7 +359,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
337
359
  return (0, import_utils3.injectRuntimeScript)(html, this.getV2RuntimeConfigScript());
338
360
  }
339
361
  async requestHandler(req, res) {
340
- const { pathname } = (0, import_url.parse)(req.url);
362
+ const { pathname, search } = (0, import_url.parse)(req.url);
341
363
  const { PLUGIN_STATICS_PATH } = import_node_process.default.env;
342
364
  const APP_PUBLIC_PATH = this.getAppPublicPath();
343
365
  if (pathname.endsWith("/__umi/api/bundle-status")) {
@@ -345,6 +367,12 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
345
367
  res.end("ok");
346
368
  return;
347
369
  }
370
+ if (APP_PUBLIC_PATH !== "/" && pathname.startsWith("/files/")) {
371
+ res.statusCode = 302;
372
+ res.setHeader("Location", `${APP_PUBLIC_PATH.replace(/\/$/, "")}${pathname}${search || ""}`);
373
+ res.end();
374
+ return;
375
+ }
348
376
  const supervisor = import_app_supervisor.AppSupervisor.getInstance();
349
377
  let handleApp = "main";
350
378
  try {
@@ -361,7 +389,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
361
389
  return;
362
390
  }
363
391
  }
364
- const headers = (0, import_static_file_security.getStorageUploadSecurityHeaders)(pathname);
392
+ const headers = (0, import_static_file_security.getStorageUploadSecurityHeaders)(`${pathname}${search || ""}`);
365
393
  for (const [key, value] of Object.entries(headers)) {
366
394
  res.setHeader(key, value);
367
395
  }
@@ -407,7 +435,8 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
407
435
  ]
408
436
  });
409
437
  }
410
- if (!pathname.startsWith(import_node_process.default.env.API_BASE_PATH)) {
438
+ const isFilesRequest = Boolean(getFileAccessRestPath(pathname, APP_PUBLIC_PATH));
439
+ if (!pathname.startsWith(import_node_process.default.env.API_BASE_PATH) && !isFilesRequest) {
411
440
  if (this.isV2Request(pathname)) {
412
441
  if (handleApp !== "main") {
413
442
  const isProxy = await this.proxyRequestToSubApp(supervisor, handleApp, req, res);
@@ -47,6 +47,15 @@ function stripQueryAndHash(pathname = "") {
47
47
  return pathname.split("?")[0].split("#")[0];
48
48
  }
49
49
  __name(stripQueryAndHash, "stripQueryAndHash");
50
+ function shouldDownload(pathname = "") {
51
+ var _a;
52
+ const query = (_a = pathname.split("?")[1]) == null ? void 0 : _a.split("#")[0];
53
+ if (!query) {
54
+ return false;
55
+ }
56
+ return new URLSearchParams(query).get("download") === "1";
57
+ }
58
+ __name(shouldDownload, "shouldDownload");
50
59
  function hasActiveContentExtension(pathname = "") {
51
60
  const ext = import_node_path.default.extname(stripQueryAndHash(pathname)).toLowerCase();
52
61
  return ACTIVE_CONTENT_EXTENSIONS.has(ext);
@@ -56,7 +65,7 @@ function getStorageUploadSecurityHeaders(pathname = "") {
56
65
  const headers = {
57
66
  "X-Content-Type-Options": "nosniff"
58
67
  };
59
- if (hasActiveContentExtension(pathname)) {
68
+ if (hasActiveContentExtension(pathname) || shouldDownload(pathname)) {
60
69
  headers["Content-Disposition"] = "attachment";
61
70
  }
62
71
  return headers;
package/lib/helper.js CHANGED
@@ -76,23 +76,28 @@ function createResourcer(options) {
76
76
  return new import_resourcer.Resourcer({ ...options.resourcer });
77
77
  }
78
78
  __name(createResourcer, "createResourcer");
79
+ function isWhitelistedCorsOrigin(ctx) {
80
+ const origin = ctx.get("origin");
81
+ const whitelist = (0, import_utils.getCorsWhitelist)();
82
+ if (!origin) {
83
+ return false;
84
+ }
85
+ if (!whitelist) {
86
+ return (0, import_utils.isTrustedOrigin)(ctx, origin);
87
+ }
88
+ return whitelist.has(origin);
89
+ }
90
+ __name(isWhitelistedCorsOrigin, "isWhitelistedCorsOrigin");
79
91
  function resolveCorsOrigin(ctx) {
80
92
  const origin = ctx.get("origin");
81
93
  const disallowNoOrigin = process.env.CORS_DISALLOW_NO_ORIGIN === "true";
82
- const whitelistString = process.env.CORS_ORIGIN_WHITELIST;
83
94
  if (!origin && disallowNoOrigin) {
84
95
  return false;
85
96
  }
86
- if (!whitelistString) {
97
+ if (isWhitelistedCorsOrigin(ctx)) {
87
98
  return origin;
88
99
  }
89
- const whitelist = new Set(
90
- whitelistString.split(",").map((item) => item.trim()).filter(Boolean)
91
- );
92
- if (whitelist.has(origin)) {
93
- return origin;
94
- }
95
- return false;
100
+ return (0, import_utils.getCorsWhitelist)() ? false : origin;
96
101
  }
97
102
  __name(resolveCorsOrigin, "resolveCorsOrigin");
98
103
  function registerMiddlewares(app, options) {
@@ -108,6 +113,7 @@ function registerMiddlewares(app, options) {
108
113
  app.use((0, import_logger.requestLogger)(app.name, app.requestLogger, (_a = options.logger) == null ? void 0 : _a.request), { tag: "logger" });
109
114
  app.use(
110
115
  (0, import_cors.default)({
116
+ credentials: isWhitelistedCorsOrigin,
111
117
  exposeHeaders: ["content-disposition"],
112
118
  origin: resolveCorsOrigin,
113
119
  ...options.cors
@@ -134,8 +140,18 @@ function registerMiddlewares(app, options) {
134
140
  }
135
141
  app.use(/* @__PURE__ */ __name(async function getBearerToken(ctx, next) {
136
142
  ctx.getBearerToken = () => {
137
- const token = ctx.get("Authorization").replace(/^Bearer\s+/gi, "");
138
- return token || ctx.query.token;
143
+ const authorization = ctx.get("Authorization");
144
+ if (authorization) {
145
+ ctx.state.pendingAuthTokenSource = "authorization";
146
+ return authorization.replace(/^Bearer\s+/gi, "");
147
+ }
148
+ if (ctx.query.token) {
149
+ ctx.state.pendingAuthTokenSource = "query";
150
+ return ctx.query.token;
151
+ }
152
+ const cookieToken = ctx.cookies.get((0, import_utils.getAuthCookieName)("authToken", app.name));
153
+ ctx.state.pendingAuthTokenSource = cookieToken ? "cookie" : void 0;
154
+ return cookieToken;
139
155
  };
140
156
  await next();
141
157
  }, "getBearerToken"));
@@ -62,6 +62,7 @@ async function trim(packageNames) {
62
62
  }
63
63
  __name(trim, "trim");
64
64
  const excludes = [
65
+ "external-db-data-source",
65
66
  "@nocobase/plugin-audit-logs",
66
67
  "@nocobase/plugin-backup-restore",
67
68
  "@nocobase/plugin-charts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/server",
3
- "version": "2.2.0-beta.9",
3
+ "version": "2.3.0-alpha.1",
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-beta.9",
14
- "@nocobase/actions": "2.2.0-beta.9",
15
- "@nocobase/ai": "2.2.0-beta.9",
16
- "@nocobase/auth": "2.2.0-beta.9",
17
- "@nocobase/cache": "2.2.0-beta.9",
18
- "@nocobase/data-source-manager": "2.2.0-beta.9",
19
- "@nocobase/database": "2.2.0-beta.9",
20
- "@nocobase/evaluators": "2.2.0-beta.9",
21
- "@nocobase/lock-manager": "2.2.0-beta.9",
22
- "@nocobase/logger": "2.2.0-beta.9",
23
- "@nocobase/resourcer": "2.2.0-beta.9",
24
- "@nocobase/sdk": "2.2.0-beta.9",
25
- "@nocobase/snowflake-id": "2.2.0-beta.9",
26
- "@nocobase/telemetry": "2.2.0-beta.9",
27
- "@nocobase/utils": "2.2.0-beta.9",
13
+ "@nocobase/acl": "2.3.0-alpha.1",
14
+ "@nocobase/actions": "2.3.0-alpha.1",
15
+ "@nocobase/ai": "2.3.0-alpha.1",
16
+ "@nocobase/auth": "2.3.0-alpha.1",
17
+ "@nocobase/cache": "2.3.0-alpha.1",
18
+ "@nocobase/data-source-manager": "2.3.0-alpha.1",
19
+ "@nocobase/database": "2.3.0-alpha.1",
20
+ "@nocobase/evaluators": "2.3.0-alpha.1",
21
+ "@nocobase/lock-manager": "2.3.0-alpha.1",
22
+ "@nocobase/logger": "2.3.0-alpha.1",
23
+ "@nocobase/resourcer": "2.3.0-alpha.1",
24
+ "@nocobase/sdk": "2.3.0-alpha.1",
25
+ "@nocobase/snowflake-id": "2.3.0-alpha.1",
26
+ "@nocobase/telemetry": "2.3.0-alpha.1",
27
+ "@nocobase/utils": "2.3.0-alpha.1",
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": "60e3d7abbaa0c7cead76f71a4f3d5eedb6b8acdb"
64
+ "gitHead": "2377df8ceb12549149017f7f14a61207bf6e49a2"
65
65
  }