@nocobase/server 2.2.0-beta.15 → 2.2.0-beta.16

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.
@@ -53,17 +53,18 @@ export interface EventQueueOptions {
53
53
  export declare class MemoryEventQueueAdapter implements IEventQueueAdapter {
54
54
  private options;
55
55
  private connected;
56
- private emitter;
57
56
  private reading;
57
+ private scheduledChannels;
58
58
  protected events: Map<string, QueueEventOptions>;
59
59
  protected queues: Map<string, {
60
60
  id: string;
61
61
  content: any;
62
62
  options?: QueueMessageOptions;
63
63
  }[]>;
64
- get processing(): Promise<Promise<void>[][]>;
64
+ get processing(): Promise<void[]>;
65
65
  private get storagePath();
66
66
  listen: (channel: string) => void;
67
+ private scheduleListen;
67
68
  constructor(options: {
68
69
  appName: string;
69
70
  logger: SystemLogger;
@@ -46,7 +46,6 @@ __export(event_queue_exports, {
46
46
  });
47
47
  module.exports = __toCommonJS(event_queue_exports);
48
48
  var import_crypto = require("crypto");
49
- var import_events = require("events");
50
49
  var import_path = __toESM(require("path"));
51
50
  var import_promises = __toESM(require("fs/promises"));
52
51
  var import_utils = require("@nocobase/utils");
@@ -56,15 +55,14 @@ const QUEUE_DEFAULT_ACK_TIMEOUT = 15e3;
56
55
  const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
57
56
  constructor(options) {
58
57
  this.options = options;
59
- this.emitter.setMaxListeners(0);
60
58
  }
61
59
  connected = false;
62
- emitter = new import_events.EventEmitter();
63
60
  reading = /* @__PURE__ */ new Map();
61
+ scheduledChannels = /* @__PURE__ */ new Set();
64
62
  events = /* @__PURE__ */ new Map();
65
63
  queues = /* @__PURE__ */ new Map();
66
64
  get processing() {
67
- const processing = Array.from(this.reading.values());
65
+ const processing = Array.from(this.reading.values()).flat();
68
66
  if (processing.length > 0) {
69
67
  return Promise.all(processing);
70
68
  }
@@ -80,7 +78,6 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
80
78
  const { logger } = this.options;
81
79
  const event = this.events.get(channel);
82
80
  if (!event) {
83
- logger.warn(`memory queue (${channel}) not found, skipping...`);
84
81
  return;
85
82
  }
86
83
  if (!event.idle()) {
@@ -99,10 +96,24 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
99
96
  if (index > -1) {
100
97
  reading.splice(index, 1);
101
98
  }
99
+ this.scheduleListen(channel);
102
100
  });
103
101
  });
104
102
  this.reading.set(channel, reading);
105
103
  }, "listen");
104
+ scheduleListen(channel) {
105
+ if (this.scheduledChannels.has(channel)) {
106
+ return;
107
+ }
108
+ this.scheduledChannels.add(channel);
109
+ setImmediate(() => {
110
+ this.scheduledChannels.delete(channel);
111
+ if (!this.events.has(channel)) {
112
+ return;
113
+ }
114
+ this.listen(channel);
115
+ });
116
+ }
106
117
  isConnected() {
107
118
  return this.connected;
108
119
  }
@@ -181,7 +192,6 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
181
192
  if (!this.queues.has(channel)) {
182
193
  this.queues.set(channel, []);
183
194
  }
184
- this.emitter.on(channel, this.listen);
185
195
  if (this.connected) {
186
196
  this.consume(channel);
187
197
  }
@@ -191,12 +201,12 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
191
201
  return;
192
202
  }
193
203
  this.events.delete(channel);
194
- this.emitter.off(channel, this.listen);
195
204
  }
196
205
  publish(channel, content, options = { timestamp: Date.now() }) {
206
+ const { logger } = this.options;
197
207
  const event = this.events.get(channel);
198
208
  if (!event) {
199
- console.debug(`memory queue (${channel}) not subscribed, skip`);
209
+ logger.debug(`memory queue (${channel}) not subscribed, skip`);
200
210
  return;
201
211
  }
202
212
  if (!this.queues.get(channel)) {
@@ -205,11 +215,8 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
205
215
  const queue = this.queues.get(channel);
206
216
  const message = { id: (0, import_crypto.randomUUID)(), content, options };
207
217
  queue.push(message);
208
- const { logger } = this.options;
209
218
  logger.debug(`memory queue (${channel}) published message`, content);
210
- setImmediate(() => {
211
- this.emitter.emit(channel, channel);
212
- });
219
+ this.scheduleListen(channel);
213
220
  }
214
221
  async consume(channel, once = false) {
215
222
  while (this.connected && this.events.get(channel)) {
@@ -390,14 +390,17 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
390
390
  }
391
391
  }
392
392
  const headers = (0, import_static_file_security.getStorageUploadSecurityHeaders)(`${pathname}${search || ""}`);
393
- for (const [key, value] of Object.entries(headers)) {
394
- res.setHeader(key, value);
395
- }
396
393
  req.url = req.url.substring(APP_PUBLIC_PATH.length + "storage".length);
397
394
  await compress(req, res);
398
395
  return (0, import_serve_handler.default)(req, res, {
399
396
  public: (0, import_utils.resolveStorageRoot)(),
400
- directoryListing: false
397
+ directoryListing: false,
398
+ headers: [
399
+ {
400
+ source: "**/*",
401
+ headers: Object.entries(headers).map(([key, value]) => ({ key, value }))
402
+ }
403
+ ]
401
404
  });
402
405
  }
403
406
  if (pathname.startsWith(APP_PUBLIC_PATH + "dist/")) {
@@ -42,7 +42,18 @@ __export(static_file_security_exports, {
42
42
  });
43
43
  module.exports = __toCommonJS(static_file_security_exports);
44
44
  var import_node_path = __toESM(require("node:path"));
45
- const ACTIVE_CONTENT_EXTENSIONS = /* @__PURE__ */ new Set([".htm", ".html", ".pdf", ".svg", ".svgz", ".xhtml"]);
45
+ const ACTIVE_CONTENT_EXTENSIONS = /* @__PURE__ */ new Set([
46
+ ".htm",
47
+ ".html",
48
+ ".pdf",
49
+ ".svg",
50
+ ".svgz",
51
+ ".xht",
52
+ ".xhtml",
53
+ ".xml",
54
+ ".xsl",
55
+ ".xslt"
56
+ ]);
46
57
  function stripQueryAndHash(pathname = "") {
47
58
  return pathname.split("?")[0].split("#")[0];
48
59
  }
@@ -63,6 +74,7 @@ function hasActiveContentExtension(pathname = "") {
63
74
  __name(hasActiveContentExtension, "hasActiveContentExtension");
64
75
  function getStorageUploadSecurityHeaders(pathname = "") {
65
76
  const headers = {
77
+ "Content-Security-Policy": "sandbox",
66
78
  "X-Content-Type-Options": "nosniff"
67
79
  };
68
80
  if (hasActiveContentExtension(pathname) || shouldDownload(pathname)) {
package/lib/helper.d.ts CHANGED
@@ -11,6 +11,7 @@ import { Command } from 'commander';
11
11
  import Application, { ApplicationOptions } from './application';
12
12
  export declare function createI18n(options: ApplicationOptions): import("i18next").i18n;
13
13
  export declare function createResourcer(options: ApplicationOptions): Resourcer;
14
+ export declare function resolveCorsOrigin(ctx: any): any;
14
15
  export declare function registerMiddlewares(app: Application, options: ApplicationOptions): void;
15
16
  export declare const createAppProxy: (app: Application) => Application<import("./application").DefaultState, import("./application").DefaultContext>;
16
17
  export declare const getCommandFullName: (command: Command) => string;
package/lib/helper.js CHANGED
@@ -45,6 +45,7 @@ __export(helper_exports, {
45
45
  getBodyLimit: () => getBodyLimit,
46
46
  getCommandFullName: () => getCommandFullName,
47
47
  registerMiddlewares: () => registerMiddlewares,
48
+ resolveCorsOrigin: () => resolveCorsOrigin,
48
49
  tsxRerunning: () => tsxRerunning
49
50
  });
50
51
  module.exports = __toCommonJS(helper_exports);
@@ -78,26 +79,23 @@ function createResourcer(options) {
78
79
  __name(createResourcer, "createResourcer");
79
80
  function isWhitelistedCorsOrigin(ctx) {
80
81
  const origin = ctx.get("origin");
81
- const whitelist = (0, import_utils.getCorsWhitelist)();
82
82
  if (!origin) {
83
83
  return false;
84
84
  }
85
- if (!whitelist) {
86
- return (0, import_utils.isTrustedOrigin)(ctx, origin);
87
- }
88
- return whitelist.has(origin);
85
+ return (0, import_utils.isTrustedOrigin)(ctx, origin);
89
86
  }
90
87
  __name(isWhitelistedCorsOrigin, "isWhitelistedCorsOrigin");
91
88
  function resolveCorsOrigin(ctx) {
92
89
  const origin = ctx.get("origin");
93
90
  const disallowNoOrigin = process.env.CORS_DISALLOW_NO_ORIGIN === "true";
91
+ const whitelist = (0, import_utils.getCorsWhitelist)();
94
92
  if (!origin && disallowNoOrigin) {
95
93
  return false;
96
94
  }
97
95
  if (isWhitelistedCorsOrigin(ctx)) {
98
96
  return origin;
99
97
  }
100
- return (0, import_utils.getCorsWhitelist)() ? false : origin;
98
+ return whitelist ? false : origin;
101
99
  }
102
100
  __name(resolveCorsOrigin, "resolveCorsOrigin");
103
101
  function registerMiddlewares(app, options) {
@@ -325,5 +323,6 @@ __name(createContextVariablesScope, "createContextVariablesScope");
325
323
  getBodyLimit,
326
324
  getCommandFullName,
327
325
  registerMiddlewares,
326
+ resolveCorsOrigin,
328
327
  tsxRerunning
329
328
  });
package/lib/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export * from './gateway/ws-server';
13
13
  export { Application as default } from './application';
14
14
  export * from './audit-manager';
15
15
  export * from './gateway';
16
+ export * from './gateway/static-file-security';
16
17
  export * as middlewares from './middlewares';
17
18
  export * from './migration';
18
19
  export * from './plugin';
package/lib/index.js CHANGED
@@ -55,6 +55,7 @@ __reExport(src_exports, require("./gateway/ws-server"), module.exports);
55
55
  var import_application = require("./application");
56
56
  __reExport(src_exports, require("./audit-manager"), module.exports);
57
57
  __reExport(src_exports, require("./gateway"), module.exports);
58
+ __reExport(src_exports, require("./gateway/static-file-security"), module.exports);
58
59
  var middlewares = __toESM(require("./middlewares"));
59
60
  __reExport(src_exports, require("./migration"), module.exports);
60
61
  __reExport(src_exports, require("./plugin"), module.exports);
@@ -85,6 +86,7 @@ var import_helper = require("./helper");
85
86
  ...require("./gateway/ws-server"),
86
87
  ...require("./audit-manager"),
87
88
  ...require("./gateway"),
89
+ ...require("./gateway/static-file-security"),
88
90
  ...require("./migration"),
89
91
  ...require("./plugin"),
90
92
  ...require("./plugin-manager"),
@@ -12,7 +12,23 @@ declare const _default: {
12
12
  readonly tags: readonly ["app"];
13
13
  readonly summary: "Get the current application language";
14
14
  readonly description: "Return the current locale used by the server.";
15
- readonly parameters: readonly [];
15
+ readonly parameters: readonly [{
16
+ readonly name: "locale";
17
+ readonly in: "query";
18
+ readonly required: false;
19
+ readonly schema: {
20
+ readonly type: "string";
21
+ };
22
+ readonly description: "Requested application locale. The server validates it against enabled languages.";
23
+ }, {
24
+ readonly name: "ns";
25
+ readonly in: "query";
26
+ readonly required: false;
27
+ readonly schema: {
28
+ readonly type: "string";
29
+ };
30
+ readonly description: "Comma-separated resource namespaces to return. Omit it to preserve the full legacy payload.";
31
+ }];
16
32
  readonly responses: {
17
33
  readonly 200: {
18
34
  readonly description: "OK";
@@ -35,7 +35,22 @@ var app_default = {
35
35
  tags: ["app"],
36
36
  summary: "Get the current application language",
37
37
  description: "Return the current locale used by the server.",
38
- parameters: [],
38
+ parameters: [
39
+ {
40
+ name: "locale",
41
+ in: "query",
42
+ required: false,
43
+ schema: { type: "string" },
44
+ description: "Requested application locale. The server validates it against enabled languages."
45
+ },
46
+ {
47
+ name: "ns",
48
+ in: "query",
49
+ required: false,
50
+ schema: { type: "string" },
51
+ description: "Comma-separated resource namespaces to return. Omit it to preserve the full legacy payload."
52
+ }
53
+ ],
39
54
  responses: {
40
55
  200: {
41
56
  description: "OK",
@@ -694,7 +694,23 @@ declare const _default: {
694
694
  readonly tags: readonly ["app"];
695
695
  readonly summary: "Get the current application language";
696
696
  readonly description: "Return the current locale used by the server.";
697
- readonly parameters: readonly [];
697
+ readonly parameters: readonly [{
698
+ readonly name: "locale";
699
+ readonly in: "query";
700
+ readonly required: false;
701
+ readonly schema: {
702
+ readonly type: "string";
703
+ };
704
+ readonly description: "Requested application locale. The server validates it against enabled languages.";
705
+ }, {
706
+ readonly name: "ns";
707
+ readonly in: "query";
708
+ readonly required: false;
709
+ readonly schema: {
710
+ readonly type: "string";
711
+ };
712
+ readonly description: "Comma-separated resource namespaces to return. Omit it to preserve the full legacy payload.";
713
+ }];
698
714
  readonly responses: {
699
715
  readonly 200: {
700
716
  readonly description: "OK";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/server",
3
- "version": "2.2.0-beta.15",
3
+ "version": "2.2.0-beta.16",
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.15",
14
- "@nocobase/actions": "2.2.0-beta.15",
15
- "@nocobase/ai": "2.2.0-beta.15",
16
- "@nocobase/auth": "2.2.0-beta.15",
17
- "@nocobase/cache": "2.2.0-beta.15",
18
- "@nocobase/data-source-manager": "2.2.0-beta.15",
19
- "@nocobase/database": "2.2.0-beta.15",
20
- "@nocobase/evaluators": "2.2.0-beta.15",
21
- "@nocobase/lock-manager": "2.2.0-beta.15",
22
- "@nocobase/logger": "2.2.0-beta.15",
23
- "@nocobase/resourcer": "2.2.0-beta.15",
24
- "@nocobase/sdk": "2.2.0-beta.15",
25
- "@nocobase/snowflake-id": "2.2.0-beta.15",
26
- "@nocobase/telemetry": "2.2.0-beta.15",
27
- "@nocobase/utils": "2.2.0-beta.15",
13
+ "@nocobase/acl": "2.2.0-beta.16",
14
+ "@nocobase/actions": "2.2.0-beta.16",
15
+ "@nocobase/ai": "2.2.0-beta.16",
16
+ "@nocobase/auth": "2.2.0-beta.16",
17
+ "@nocobase/cache": "2.2.0-beta.16",
18
+ "@nocobase/data-source-manager": "2.2.0-beta.16",
19
+ "@nocobase/database": "2.2.0-beta.16",
20
+ "@nocobase/evaluators": "2.2.0-beta.16",
21
+ "@nocobase/lock-manager": "2.2.0-beta.16",
22
+ "@nocobase/logger": "2.2.0-beta.16",
23
+ "@nocobase/resourcer": "2.2.0-beta.16",
24
+ "@nocobase/sdk": "2.2.0-beta.16",
25
+ "@nocobase/snowflake-id": "2.2.0-beta.16",
26
+ "@nocobase/telemetry": "2.2.0-beta.16",
27
+ "@nocobase/utils": "2.2.0-beta.16",
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": "4c471f6b7d26adc412b2cc1d91264572296ca873"
64
+ "gitHead": "49d9bd2e21122eab9435f128375828b8c5bb4c07"
65
65
  }