@nocobase/server 1.9.0-beta.5 → 1.9.0-beta.7

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.
@@ -279,6 +279,9 @@ const _Application = class _Application extends import_koa.default {
279
279
  if (!WORKER_MODE) {
280
280
  return true;
281
281
  }
282
+ if (WORKER_MODE === "-") {
283
+ return false;
284
+ }
282
285
  const topics = WORKER_MODE.trim().split(",");
283
286
  if (key) {
284
287
  if (WORKER_MODE === "*") {
@@ -252,6 +252,9 @@ const _AuditManager = class _AuditManager {
252
252
  async output(ctx, reqId, metadata) {
253
253
  var _a;
254
254
  try {
255
+ if (!ctx.action) {
256
+ return;
257
+ }
255
258
  const { resourceName, actionName } = ctx.action;
256
259
  const action = this.getAction(actionName, resourceName);
257
260
  if (!action) {
@@ -7,6 +7,7 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import Application from './application';
10
+ import { SystemLogger } from '@nocobase/logger';
10
11
  export declare const QUEUE_DEFAULT_INTERVAL = 250;
11
12
  export declare const QUEUE_DEFAULT_CONCURRENCY = 1;
12
13
  export declare const QUEUE_DEFAULT_ACK_TIMEOUT = 15000;
@@ -58,6 +59,7 @@ export declare class MemoryEventQueueAdapter implements IEventQueueAdapter {
58
59
  listen: (channel: string) => void;
59
60
  constructor(options: {
60
61
  appName: string;
62
+ logger: SystemLogger;
61
63
  });
62
64
  isConnected(): boolean;
63
65
  setConnected(connected: boolean): void;
@@ -77,9 +77,10 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
77
77
  if (!this.connected) {
78
78
  return;
79
79
  }
80
+ const { logger } = this.options;
80
81
  const event = this.events.get(channel);
81
82
  if (!event) {
82
- console.warn(`memory queue (${channel}) not found, skipping...`);
83
+ logger.warn(`memory queue (${channel}) not found, skipping...`);
83
84
  return;
84
85
  }
85
86
  if (!event.idle()) {
@@ -88,12 +89,9 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
88
89
  const reading = this.reading.get(channel) || [];
89
90
  const count = (event.concurrency || QUEUE_DEFAULT_CONCURRENCY) - reading.length;
90
91
  if (count <= 0) {
91
- console.debug(
92
- `memory queue (${channel}) is already reading as max concurrency (${reading.length}), waiting last reading to end...`
93
- );
94
92
  return;
95
93
  }
96
- console.debug(`reading more from queue (${channel}), count: ${count}`);
94
+ logger.debug(`reading more from queue (${channel}), count: ${count}`);
97
95
  this.read(channel, count).forEach((promise) => {
98
96
  reading.push(promise);
99
97
  promise.finally(() => {
@@ -114,20 +112,21 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
114
112
  async loadFromStorage() {
115
113
  let queues = {};
116
114
  let exists = false;
115
+ const { logger } = this.options;
117
116
  try {
118
117
  await import_promises.default.stat(this.storagePath);
119
118
  exists = true;
120
119
  } catch (ex) {
121
- console.info(`memory queue storage file not found, skip`);
120
+ logger.info(`memory queue storage file not found, skip`);
122
121
  }
123
122
  if (exists) {
124
123
  try {
125
124
  const queueJson = await import_promises.default.readFile(this.storagePath);
126
125
  queues = JSON.parse(queueJson.toString());
127
- console.debug("memory queue loaded from storage", queues);
126
+ logger.debug("memory queue loaded from storage", queues);
128
127
  await import_promises.default.unlink(this.storagePath);
129
128
  } catch (ex) {
130
- console.error("failed to load queue from storage", ex);
129
+ logger.error("failed to load queue from storage", ex);
131
130
  }
132
131
  }
133
132
  this.queues = new Map(Object.entries(queues));
@@ -139,12 +138,13 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
139
138
  }
140
139
  return acc;
141
140
  }, {});
141
+ const { logger } = this.options;
142
142
  if (Object.keys(queues).length) {
143
143
  await import_promises.default.mkdir(import_path.default.dirname(this.storagePath), { recursive: true });
144
144
  await import_promises.default.writeFile(this.storagePath, JSON.stringify(queues));
145
- console.debug("memory queue saved to storage", queues);
145
+ logger.debug("memory queue saved to storage", queues);
146
146
  } else {
147
- console.debug("memory queue empty, no need to save to storage");
147
+ logger.debug("memory queue empty, no need to save to storage");
148
148
  }
149
149
  }
150
150
  async connect() {
@@ -163,13 +163,14 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
163
163
  if (!this.connected) {
164
164
  return;
165
165
  }
166
+ const { logger } = this.options;
166
167
  this.connected = false;
167
168
  if (this.processing) {
168
- console.info("memory queue waiting for processing job...");
169
+ logger.info("memory queue waiting for processing job...");
169
170
  await this.processing;
170
- console.info("memory queue job cleaned");
171
+ logger.info("memory queue job cleaned");
171
172
  }
172
- console.log("memory queue gracefully shutting down...");
173
+ logger.info("memory queue gracefully shutting down...");
173
174
  await this.saveToStorage();
174
175
  }
175
176
  subscribe(channel, options) {
@@ -195,6 +196,7 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
195
196
  publish(channel, content, options = { timestamp: Date.now() }) {
196
197
  const event = this.events.get(channel);
197
198
  if (!event) {
199
+ console.debug(`memory queue (${channel}) not subscribed, skip`);
198
200
  return;
199
201
  }
200
202
  if (!this.queues.get(channel)) {
@@ -203,7 +205,8 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
203
205
  const queue = this.queues.get(channel);
204
206
  const message = { id: (0, import_crypto.randomUUID)(), content, options };
205
207
  queue.push(message);
206
- console.debug(`memory queue (${channel}) published message`, content);
208
+ const { logger } = this.options;
209
+ logger.debug(`memory queue (${channel}) published message`, content);
207
210
  setImmediate(() => {
208
211
  this.emitter.emit(channel, channel);
209
212
  });
@@ -227,8 +230,9 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
227
230
  if (!(queue == null ? void 0 : queue.length)) {
228
231
  return [];
229
232
  }
233
+ const { logger } = this.options;
230
234
  const messages = queue.slice(0, n);
231
- console.debug(`memory queue (${channel}) read ${messages.length} messages`, messages);
235
+ logger.debug(`memory queue (${channel}) read ${messages.length} messages`, messages);
232
236
  queue.splice(0, messages.length);
233
237
  const batch = messages.map(({ id, ...message }) => this.process(channel, { id, message }));
234
238
  return batch;
@@ -236,17 +240,18 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
236
240
  async process(channel, { id, message }) {
237
241
  const event = this.events.get(channel);
238
242
  const { content, options: { timeout = QUEUE_DEFAULT_ACK_TIMEOUT, maxRetries = 0, retried = 0 } = {} } = message;
239
- console.debug(`memory queue (${channel}) processing message (${id})...`, content);
243
+ const { logger } = this.options;
244
+ logger.debug(`memory queue (${channel}) processing message (${id})...`, content);
240
245
  return (async () => event.process(content, {
241
246
  id,
242
247
  retried,
243
248
  signal: AbortSignal.timeout(timeout)
244
249
  }))().then(() => {
245
- console.debug(`memory queue (${channel}) consumed message (${id})`);
250
+ logger.debug(`memory queue (${channel}) consumed message (${id})`);
246
251
  }).catch((ex) => {
247
252
  if (maxRetries > 0 && retried < maxRetries) {
248
253
  const currentRetry = retried + 1;
249
- console.warn(
254
+ logger.warn(
250
255
  `memory queue (${channel}) consum message (${id}) failed, retrying (${currentRetry} / ${maxRetries})...`,
251
256
  ex
252
257
  );
@@ -254,7 +259,7 @@ const _MemoryEventQueueAdapter = class _MemoryEventQueueAdapter {
254
259
  this.publish(channel, content, { timeout, maxRetries, retried: currentRetry, timestamp: Date.now() });
255
260
  }, 500);
256
261
  } else {
257
- console.error(ex);
262
+ logger.error(ex);
258
263
  }
259
264
  });
260
265
  }
@@ -265,14 +270,16 @@ const _EventQueue = class _EventQueue {
265
270
  constructor(app, options = {}) {
266
271
  this.app = app;
267
272
  this.options = options;
268
- this.setAdapter(new MemoryEventQueueAdapter({ appName: this.app.name }));
269
- app.on("afterStart", async () => {
270
- await this.connect();
271
- });
272
- app.on("beforeStop", async () => {
273
- app.logger.info("[queue] gracefully shutting down...");
274
- await this.close();
275
- });
273
+ if (app.serving()) {
274
+ this.setAdapter(new MemoryEventQueueAdapter({ appName: this.app.name, logger: this.app.logger }));
275
+ app.on("afterStart", async () => {
276
+ await this.connect();
277
+ });
278
+ app.on("beforeStop", async () => {
279
+ app.logger.info("[queue] gracefully shutting down...");
280
+ await this.close();
281
+ });
282
+ }
276
283
  }
277
284
  adapter;
278
285
  events = /* @__PURE__ */ new Map();
@@ -296,7 +303,12 @@ const _EventQueue = class _EventQueue {
296
303
  if (!this.adapter) {
297
304
  throw new Error("no adapter set, cannot connect");
298
305
  }
306
+ if (!this.app.serving()) {
307
+ this.app.logger.warn("app is not serving, will not connect to event queue");
308
+ return;
309
+ }
299
310
  await this.adapter.connect();
311
+ this.app.logger.debug(`connected to adapter, using memory? ${this.adapter instanceof MemoryEventQueueAdapter}`);
300
312
  for (const [channel, event] of this.events.entries()) {
301
313
  this.adapter.subscribe(this.getFullChannel(channel), event);
302
314
  }
@@ -337,7 +349,7 @@ const _EventQueue = class _EventQueue {
337
349
  throw new Error("event queue not connected, cannot publish");
338
350
  }
339
351
  const c = this.getFullChannel(channel);
340
- this.app.logger.debug("event queue publishing:", { channel: c, message });
352
+ this.app.logger.debug(`event queue publishing to channel(${c})`, { message });
341
353
  await this.adapter.publish(c, message, {
342
354
  timeout: QUEUE_DEFAULT_ACK_TIMEOUT,
343
355
  ...options,
@@ -172,6 +172,20 @@ const _WSServer = class _WSServer extends import_events.default {
172
172
  message
173
173
  );
174
174
  });
175
+ app.on("ws:sendToUser", ({ userId, message }) => {
176
+ this.sendToAppUser(app.name, userId, message);
177
+ app.logger.trace(`[broadcasting message] ws:sendToUser for user ${userId}`, { message });
178
+ app.pubSubManager.publish(
179
+ "ws:sendToUser",
180
+ {
181
+ userId,
182
+ message
183
+ },
184
+ {
185
+ skipSelf: true
186
+ }
187
+ );
188
+ });
175
189
  app.on("ws:sendToClient", ({ clientId, message }) => {
176
190
  this.sendToClient(clientId, message);
177
191
  });
@@ -184,6 +198,12 @@ const _WSServer = class _WSServer extends import_events.default {
184
198
  app.on("ws:authorized", ({ clientId, userId }) => {
185
199
  this.sendToClient(clientId, { type: "authorized" });
186
200
  });
201
+ app.on("afterLoad", () => {
202
+ app.pubSubManager.subscribe("ws:sendToUser", ({ userId, message }) => {
203
+ app.logger.debug(`[receive broadcasting message] ws:sendToUser for user ${userId}`, { message });
204
+ this.sendToAppUser(app.name, userId, message);
205
+ });
206
+ });
187
207
  }
188
208
  addNewConnection(ws, request) {
189
209
  const id = (0, import_nanoid.nanoid)();
@@ -11,11 +11,12 @@ import { HandlerManager } from './handler-manager';
11
11
  import { PubSubCallback, type IPubSubAdapter, type PubSubManagerOptions, type PubSubManagerPublishOptions, type PubSubManagerSubscribeOptions } from './types';
12
12
  export declare const createPubSubManager: (app: Application, options: PubSubManagerOptions) => PubSubManager;
13
13
  export declare class PubSubManager {
14
+ protected app: Application;
14
15
  protected options: PubSubManagerOptions;
15
16
  protected publisherId: string;
16
17
  protected adapter: IPubSubAdapter;
17
18
  protected handlerManager: HandlerManager;
18
- constructor(options?: PubSubManagerOptions);
19
+ constructor(app: Application, options?: PubSubManagerOptions);
19
20
  get channelPrefix(): string;
20
21
  setAdapter(adapter: IPubSubAdapter): void;
21
22
  isConnected(): Promise<boolean>;
@@ -23,5 +24,5 @@ export declare class PubSubManager {
23
24
  close(): Promise<any>;
24
25
  subscribe(channel: string, callback: PubSubCallback, options?: PubSubManagerSubscribeOptions): Promise<void>;
25
26
  unsubscribe(channel: string, callback: PubSubCallback): Promise<any>;
26
- publish(channel: string, message: any, options?: PubSubManagerPublishOptions): Promise<any>;
27
+ publish(channel: string, message: any, options?: PubSubManagerPublishOptions): Promise<void>;
27
28
  }
@@ -34,17 +34,20 @@ module.exports = __toCommonJS(pub_sub_manager_exports);
34
34
  var import_utils = require("@nocobase/utils");
35
35
  var import_handler_manager = require("./handler-manager");
36
36
  const createPubSubManager = /* @__PURE__ */ __name((app, options) => {
37
- const pubSubManager = new PubSubManager(options);
38
- app.on("afterStart", async () => {
39
- await pubSubManager.connect();
40
- });
41
- app.on("afterStop", async () => {
42
- await pubSubManager.close();
43
- });
37
+ const pubSubManager = new PubSubManager(app, options);
38
+ if (app.serving()) {
39
+ app.on("afterStart", async () => {
40
+ await pubSubManager.connect();
41
+ });
42
+ app.on("afterStop", async () => {
43
+ await pubSubManager.close();
44
+ });
45
+ }
44
46
  return pubSubManager;
45
47
  }, "createPubSubManager");
46
48
  const _PubSubManager = class _PubSubManager {
47
- constructor(options = {}) {
49
+ constructor(app, options = {}) {
50
+ this.app = app;
48
51
  this.options = options;
49
52
  this.publisherId = (0, import_utils.uid)();
50
53
  this.handlerManager = new import_handler_manager.HandlerManager(this.publisherId);
@@ -69,8 +72,13 @@ const _PubSubManager = class _PubSubManager {
69
72
  if (!this.adapter) {
70
73
  return;
71
74
  }
75
+ if (!this.app.serving()) {
76
+ this.app.logger.warn("app is not serving, will not connect to event queue");
77
+ return;
78
+ }
72
79
  await this.adapter.connect();
73
80
  await this.handlerManager.each(async (channel, headler) => {
81
+ this.app.logger.debug(`[PubSubManager] subscribe ${channel} added before connected`);
74
82
  await this.adapter.subscribe(`${this.channelPrefix}${channel}`, headler);
75
83
  });
76
84
  }
@@ -84,6 +92,7 @@ const _PubSubManager = class _PubSubManager {
84
92
  await this.unsubscribe(channel, callback);
85
93
  const handler = this.handlerManager.set(channel, callback, options);
86
94
  if (await this.isConnected()) {
95
+ this.app.logger.debug(`[PubSubManager] subscribe ${channel} added after connected`);
87
96
  await this.adapter.subscribe(`${this.channelPrefix}${channel}`, handler);
88
97
  }
89
98
  }
@@ -97,6 +106,9 @@ const _PubSubManager = class _PubSubManager {
97
106
  async publish(channel, message, options) {
98
107
  var _a;
99
108
  if (!((_a = this.adapter) == null ? void 0 : _a.isConnected())) {
109
+ this.app.logger.warn(
110
+ `[PubSubManager] adapter is not exist or not connected, cannot publish message to channel ${channel}`
111
+ );
100
112
  return;
101
113
  }
102
114
  const wrappedMessage = JSON.stringify({
@@ -104,7 +116,8 @@ const _PubSubManager = class _PubSubManager {
104
116
  ...options,
105
117
  message
106
118
  });
107
- return this.adapter.publish(`${this.channelPrefix}${channel}`, wrappedMessage);
119
+ await this.adapter.publish(`${this.channelPrefix}${channel}`, wrappedMessage);
120
+ this.app.logger.trace(`[PubSubManager] published message to channel ${channel}`);
108
121
  }
109
122
  };
110
123
  __name(_PubSubManager, "PubSubManager");
@@ -16,7 +16,7 @@ export declare class SyncMessageManager {
16
16
  protected pubSubManager: PubSubManager;
17
17
  constructor(app: Application, options?: any);
18
18
  get debounce(): any;
19
- publish(channel: string, message: any, options?: PubSubManagerPublishOptions & Transactionable): Promise<any>;
19
+ publish(channel: string, message: any, options?: PubSubManagerPublishOptions & Transactionable): Promise<unknown>;
20
20
  subscribe(channel: string, callback: PubSubCallback): Promise<void>;
21
21
  unsubscribe(channel: string, callback: PubSubCallback): Promise<any>;
22
22
  sync(): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/server",
3
- "version": "1.9.0-beta.5",
3
+ "version": "1.9.0-beta.7",
4
4
  "main": "lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "license": "AGPL-3.0",
@@ -10,19 +10,19 @@
10
10
  "@koa/cors": "^5.0.0",
11
11
  "@koa/multer": "^3.1.0",
12
12
  "@koa/router": "^13.1.0",
13
- "@nocobase/acl": "1.9.0-beta.5",
14
- "@nocobase/actions": "1.9.0-beta.5",
15
- "@nocobase/auth": "1.9.0-beta.5",
16
- "@nocobase/cache": "1.9.0-beta.5",
17
- "@nocobase/data-source-manager": "1.9.0-beta.5",
18
- "@nocobase/database": "1.9.0-beta.5",
19
- "@nocobase/evaluators": "1.9.0-beta.5",
20
- "@nocobase/lock-manager": "1.9.0-beta.5",
21
- "@nocobase/logger": "1.9.0-beta.5",
22
- "@nocobase/resourcer": "1.9.0-beta.5",
23
- "@nocobase/sdk": "1.9.0-beta.5",
24
- "@nocobase/telemetry": "1.9.0-beta.5",
25
- "@nocobase/utils": "1.9.0-beta.5",
13
+ "@nocobase/acl": "1.9.0-beta.7",
14
+ "@nocobase/actions": "1.9.0-beta.7",
15
+ "@nocobase/auth": "1.9.0-beta.7",
16
+ "@nocobase/cache": "1.9.0-beta.7",
17
+ "@nocobase/data-source-manager": "1.9.0-beta.7",
18
+ "@nocobase/database": "1.9.0-beta.7",
19
+ "@nocobase/evaluators": "1.9.0-beta.7",
20
+ "@nocobase/lock-manager": "1.9.0-beta.7",
21
+ "@nocobase/logger": "1.9.0-beta.7",
22
+ "@nocobase/resourcer": "1.9.0-beta.7",
23
+ "@nocobase/sdk": "1.9.0-beta.7",
24
+ "@nocobase/telemetry": "1.9.0-beta.7",
25
+ "@nocobase/utils": "1.9.0-beta.7",
26
26
  "@types/decompress": "4.2.7",
27
27
  "@types/ini": "^1.3.31",
28
28
  "@types/koa-send": "^4.1.3",
@@ -57,5 +57,5 @@
57
57
  "@types/serve-handler": "^6.1.1",
58
58
  "@types/ws": "^8.5.5"
59
59
  },
60
- "gitHead": "9a61c60dd3db5af64244e82447309b0cb17aabb6"
60
+ "gitHead": "4a5055c973b51611d5db1604aaaf6c1b73b4733c"
61
61
  }