@nocobase/server 2.0.0-beta.8 → 2.0.0

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.
Files changed (41) hide show
  1. package/lib/aes-encryptor.d.ts +1 -0
  2. package/lib/aes-encryptor.js +8 -5
  3. package/lib/ai/create-docs-index.d.ts +13 -0
  4. package/lib/ai/create-docs-index.js +892 -0
  5. package/lib/app-supervisor/app-options-factory.d.ts +80 -0
  6. package/lib/app-supervisor/app-options-factory.js +91 -0
  7. package/lib/app-supervisor/condition-registry.d.ts +18 -0
  8. package/lib/app-supervisor/condition-registry.js +60 -0
  9. package/lib/app-supervisor/db-creator.d.ts +16 -0
  10. package/lib/app-supervisor/db-creator.js +163 -0
  11. package/lib/app-supervisor/db-drivers.d.ts +11 -0
  12. package/lib/app-supervisor/db-drivers.js +52 -0
  13. package/lib/app-supervisor/index.d.ts +161 -0
  14. package/lib/app-supervisor/index.js +690 -0
  15. package/lib/app-supervisor/main-only-adapter.d.ts +37 -0
  16. package/lib/app-supervisor/main-only-adapter.js +156 -0
  17. package/lib/app-supervisor/types.d.ts +161 -0
  18. package/lib/app-supervisor/types.js +24 -0
  19. package/lib/application.d.ts +3 -1
  20. package/lib/application.js +10 -6
  21. package/lib/commands/ai.d.ts +11 -0
  22. package/lib/commands/ai.js +40 -0
  23. package/lib/commands/console.js +1 -1
  24. package/lib/commands/index.js +2 -0
  25. package/lib/commands/install.js +2 -0
  26. package/lib/commands/start.js +3 -0
  27. package/lib/commands/upgrade.js +2 -0
  28. package/lib/gateway/errors.js +1 -1
  29. package/lib/gateway/index.js +64 -15
  30. package/lib/gateway/ipc-socket-server.js +1 -1
  31. package/lib/gateway/ws-server.js +3 -2
  32. package/lib/helper.js +20 -3
  33. package/lib/plugin-manager/deps.js +1 -1
  34. package/lib/plugin-manager/plugin-manager.js +2 -0
  35. package/lib/plugin.d.ts +5 -0
  36. package/lib/plugin.js +25 -0
  37. package/lib/redis-connection-manager.d.ts +15 -5
  38. package/lib/redis-connection-manager.js +117 -24
  39. package/package.json +18 -17
  40. package/lib/app-supervisor.d.ts +0 -74
  41. package/lib/app-supervisor.js +0 -338
@@ -44,6 +44,7 @@ __export(gateway_exports, {
44
44
  module.exports = __toCommonJS(gateway_exports);
45
45
  var import_logger = require("@nocobase/logger");
46
46
  var import_utils = require("@nocobase/utils");
47
+ var import_utils2 = require("@nocobase/utils");
47
48
  var import_plugin_symlink = require("@nocobase/utils/plugin-symlink");
48
49
  var import_commander = require("commander");
49
50
  var import_compression = __toESM(require("compression"));
@@ -99,6 +100,9 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
99
100
  try {
100
101
  for (const app of apps) {
101
102
  try {
103
+ if (!app) {
104
+ continue;
105
+ }
102
106
  await app.destroy({ signal });
103
107
  } catch (error) {
104
108
  const logger = (app == null ? void 0 : app.log) ?? console;
@@ -226,7 +230,22 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
226
230
  res.end("ok");
227
231
  return;
228
232
  }
233
+ const supervisor = import_app_supervisor.AppSupervisor.getInstance();
234
+ let handleApp = "main";
235
+ try {
236
+ handleApp = await this.getRequestHandleAppName(req);
237
+ } catch (error) {
238
+ this.getLogger("main", res).error("Failed to get handle app name", { error });
239
+ this.responseErrorWithCode("APP_INITIALIZING", res, { appName: handleApp });
240
+ return;
241
+ }
229
242
  if (pathname.startsWith(APP_PUBLIC_PATH + "storage/uploads/")) {
243
+ if (handleApp !== "main") {
244
+ const isProxy = await supervisor.proxyWeb(handleApp, req, res);
245
+ if (isProxy) {
246
+ return;
247
+ }
248
+ }
230
249
  req.url = req.url.substring(APP_PUBLIC_PATH.length - 1);
231
250
  await compress(req, res);
232
251
  return (0, import_serve_handler.default)(req, res, {
@@ -235,6 +254,12 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
235
254
  });
236
255
  }
237
256
  if (pathname.startsWith(PLUGIN_STATICS_PATH) && !pathname.includes("/server/")) {
257
+ if (handleApp !== "main") {
258
+ const isProxy = await supervisor.proxyWeb(handleApp, req, res);
259
+ if (isProxy) {
260
+ return;
261
+ }
262
+ }
238
263
  await compress(req, res);
239
264
  const packageName = (0, import_plugin_manager.getPackageNameByExposeUrl)(pathname);
240
265
  const publicDir = (0, import_plugin_manager.getPackageDirByExposeUrl)(pathname);
@@ -250,6 +275,12 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
250
275
  });
251
276
  }
252
277
  if (!pathname.startsWith(import_node_process.default.env.API_BASE_PATH)) {
278
+ if (handleApp !== "main") {
279
+ const isProxy = await supervisor.proxyWeb(handleApp, req, res);
280
+ if (isProxy) {
281
+ return;
282
+ }
283
+ }
253
284
  req.url = req.url.substring(APP_PUBLIC_PATH.length - 1);
254
285
  await compress(req, res);
255
286
  return (0, import_serve_handler.default)(req, res, {
@@ -257,19 +288,17 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
257
288
  rewrites: [{ source: "/**", destination: "/index.html" }]
258
289
  });
259
290
  }
260
- let handleApp = "main";
261
- try {
262
- handleApp = await this.getRequestHandleAppName(req);
263
- } catch (error) {
264
- console.log(error);
265
- this.responseErrorWithCode("APP_INITIALIZING", res, { appName: handleApp });
266
- return;
291
+ if (handleApp !== "main") {
292
+ const isProxy = await supervisor.proxyWeb(handleApp, req, res);
293
+ if (isProxy) {
294
+ return;
295
+ }
267
296
  }
268
- const hasApp = import_app_supervisor.AppSupervisor.getInstance().hasApp(handleApp);
297
+ const hasApp = supervisor.hasApp(handleApp);
269
298
  if (!hasApp) {
270
- void import_app_supervisor.AppSupervisor.getInstance().bootStrapApp(handleApp);
299
+ void supervisor.bootstrapApp(handleApp);
271
300
  }
272
- let appStatus = import_app_supervisor.AppSupervisor.getInstance().getAppStatus(handleApp, "preparing");
301
+ let appStatus = await supervisor.getAppStatus(handleApp, "preparing");
273
302
  if (appStatus === "not_found") {
274
303
  this.responseErrorWithCode("APP_NOT_FOUND", res, { appName: handleApp });
275
304
  return;
@@ -283,11 +312,19 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
283
312
  return;
284
313
  }
285
314
  if (appStatus === "initialized") {
286
- const appInstance = await import_app_supervisor.AppSupervisor.getInstance().getApp(handleApp);
287
- appInstance.runCommand("start", "--quickstart");
288
- appStatus = import_app_supervisor.AppSupervisor.getInstance().getAppStatus(handleApp);
315
+ const appInstance = await supervisor.getApp(handleApp);
316
+ if (!appInstance) {
317
+ this.responseErrorWithCode("APP_NOT_FOUND", res, { appName: handleApp });
318
+ return;
319
+ }
320
+ supervisor.startApp(handleApp);
321
+ appStatus = await supervisor.getAppStatus(handleApp);
322
+ }
323
+ const app = await supervisor.getApp(handleApp);
324
+ if (!app) {
325
+ this.responseErrorWithCode("APP_NOT_FOUND", res, { appName: handleApp });
326
+ return;
289
327
  }
290
- const app = await import_app_supervisor.AppSupervisor.getInstance().getApp(handleApp);
291
328
  if (appStatus !== "running") {
292
329
  this.responseErrorWithCode(`${appStatus}`, res, { app, appName: handleApp });
293
330
  return;
@@ -298,7 +335,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
298
335
  return;
299
336
  }
300
337
  if (handleApp !== "main") {
301
- import_app_supervisor.AppSupervisor.getInstance().touchApp(handleApp);
338
+ await supervisor.setAppLastSeenAt(handleApp);
302
339
  }
303
340
  const ctx = { req, res, appName: handleApp };
304
341
  const fn = (0, import_koa_compose.default)([
@@ -366,6 +403,14 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
366
403
  }
367
404
  const mainApp = import_app_supervisor.AppSupervisor.getInstance().bootMainApp(options.mainAppOptions);
368
405
  mainApp.setMaxListeners(50);
406
+ mainApp.once("afterStart", () => {
407
+ (0, import_utils2.lockdownSes)({
408
+ consoleTaming: "unsafe",
409
+ errorTaming: "unsafe",
410
+ overrideTaming: "moderate",
411
+ stackFiltering: "verbose"
412
+ });
413
+ });
369
414
  let runArgs = [import_node_process.default.argv, { throwError: true, from: "node" }];
370
415
  if (!import_node_worker_threads.isMainThread) {
371
416
  runArgs = [import_node_worker_threads.workerData.argv, { throwError: true, from: "user" }];
@@ -433,6 +478,10 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
433
478
  });
434
479
  this.wsServer = new import_ws_server.WSServer();
435
480
  this.server.on("upgrade", async (request, socket, head) => {
481
+ const isProxy = await import_app_supervisor.AppSupervisor.getInstance().proxyWs(request, socket, head);
482
+ if (isProxy) {
483
+ return;
484
+ }
436
485
  const appInstance = await import_app_supervisor.AppSupervisor.getInstance().getApp("main");
437
486
  for (const handle of _Gateway.wsServers) {
438
487
  const result = await handle(request, socket, head, appInstance);
@@ -104,7 +104,7 @@ const _IPCSocketServer = class _IPCSocketServer {
104
104
  const max = 300;
105
105
  let count = 0;
106
106
  const timer = setInterval(async () => {
107
- status2 = import_app_supervisor.AppSupervisor.getInstance().getAppStatus("main");
107
+ status2 = await import_app_supervisor.AppSupervisor.getInstance().getAppStatus("main");
108
108
  if (status2 === "running") {
109
109
  clearInterval(timer);
110
110
  resolve(status2);
@@ -90,7 +90,7 @@ const _WSServer = class _WSServer extends import_events.default {
90
90
  }
91
91
  }
92
92
  client.tags.add(`app#${handleAppName}`);
93
- import_app_supervisor.AppSupervisor.getInstance().bootStrapApp(handleAppName);
93
+ import_app_supervisor.AppSupervisor.getInstance().bootstrapApp(handleAppName);
94
94
  });
95
95
  });
96
96
  import_app_supervisor.AppSupervisor.getInstance().on("appError", async ({ appName, error }) => {
@@ -112,6 +112,7 @@ const _WSServer = class _WSServer extends import_events.default {
112
112
  });
113
113
  const payload = getPayloadByErrorCode(status, {
114
114
  app,
115
+ appName,
115
116
  message,
116
117
  command
117
118
  });
@@ -248,7 +249,7 @@ const _WSServer = class _WSServer extends import_events.default {
248
249
  client.tags.add(`app#${handleAppName}`);
249
250
  const hasApp = import_app_supervisor.AppSupervisor.getInstance().hasApp(handleAppName);
250
251
  if (!hasApp) {
251
- import_app_supervisor.AppSupervisor.getInstance().bootStrapApp(handleAppName);
252
+ import_app_supervisor.AppSupervisor.getInstance().bootstrapApp(handleAppName);
252
253
  }
253
254
  }
254
255
  removeConnection(id) {
package/lib/helper.js CHANGED
@@ -76,6 +76,25 @@ function createResourcer(options) {
76
76
  return new import_resourcer.Resourcer({ ...options.resourcer });
77
77
  }
78
78
  __name(createResourcer, "createResourcer");
79
+ function resolveCorsOrigin(ctx) {
80
+ const origin = ctx.get("origin");
81
+ const disallowNoOrigin = process.env.CORS_DISALLOW_NO_ORIGIN === "true";
82
+ const whitelistString = process.env.CORS_ORIGIN_WHITELIST;
83
+ if (!origin && disallowNoOrigin) {
84
+ return false;
85
+ }
86
+ if (!whitelistString) {
87
+ return origin;
88
+ }
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;
96
+ }
97
+ __name(resolveCorsOrigin, "resolveCorsOrigin");
79
98
  function registerMiddlewares(app, options) {
80
99
  var _a;
81
100
  app.use(
@@ -90,9 +109,7 @@ function registerMiddlewares(app, options) {
90
109
  app.use(
91
110
  (0, import_cors.default)({
92
111
  exposeHeaders: ["content-disposition"],
93
- origin(ctx) {
94
- return ctx.get("origin");
95
- },
112
+ origin: resolveCorsOrigin,
96
113
  ...options.cors
97
114
  }),
98
115
  {
@@ -41,7 +41,7 @@ const deps = {
41
41
  umzug: "3.x",
42
42
  "async-mutex": "0.5.x",
43
43
  "@formulajs/formulajs": "4.x",
44
- mathjs: "10.x",
44
+ mathjs: "15.x",
45
45
  winston: "3.x",
46
46
  "winston-daily-rotate-file": "4.x",
47
47
  koa: "2.x",
@@ -429,6 +429,7 @@ const _PluginManager = class _PluginManager {
429
429
  await this.app.emitAsync("beforeLoadPlugin", plugin, options);
430
430
  this.app.logger.trace(`load plugin [${name}] `, { submodule: "plugin-manager", method: "load", name });
431
431
  await plugin.loadCollections();
432
+ await plugin.loadAI();
432
433
  await plugin.load();
433
434
  plugin.state.loaded = true;
434
435
  await this.app.emitAsync("afterLoadPlugin", plugin, options);
@@ -557,6 +558,7 @@ const _PluginManager = class _PluginManager {
557
558
  continue;
558
559
  }
559
560
  await plugin.loadCollections();
561
+ await plugin.loadAI();
560
562
  await plugin.load();
561
563
  }
562
564
  } catch (error) {
package/lib/plugin.d.ts CHANGED
@@ -45,6 +45,7 @@ export declare abstract class Plugin<O = any> implements PluginInterface {
45
45
  constructor(app: Application, options?: any);
46
46
  get log(): import("@nocobase/logger/lib/logger").Logger;
47
47
  get name(): string;
48
+ get ai(): import("@nocobase/ai").AIManager;
48
49
  get pm(): import("./plugin-manager").PluginManager;
49
50
  get db(): import("@nocobase/database").default;
50
51
  get enabled(): any;
@@ -88,6 +89,10 @@ export declare abstract class Plugin<O = any> implements PluginInterface {
88
89
  * @internal
89
90
  */
90
91
  loadCollections(): Promise<void>;
92
+ /**
93
+ * @internal
94
+ */
95
+ loadAI(): Promise<void>;
91
96
  /**
92
97
  * @deprecated
93
98
  */
package/lib/plugin.js CHANGED
@@ -46,6 +46,7 @@ var import_fs = __toESM(require("fs"));
46
46
  var import_path = require("path");
47
47
  var import_plugin_manager = require("./plugin-manager");
48
48
  var import_utils2 = require("./plugin-manager/utils");
49
+ var import_ai = require("@nocobase/ai");
49
50
  /* istanbul ignore file -- @preserve */
50
51
  const _Plugin = class _Plugin {
51
52
  options;
@@ -75,6 +76,9 @@ const _Plugin = class _Plugin {
75
76
  get name() {
76
77
  return this.options.name;
77
78
  }
79
+ get ai() {
80
+ return this.app.aiManager;
81
+ }
78
82
  get pm() {
79
83
  return this.app.pm;
80
84
  }
@@ -189,6 +193,27 @@ const _Plugin = class _Plugin {
189
193
  });
190
194
  }
191
195
  }
196
+ /**
197
+ * @internal
198
+ */
199
+ async loadAI() {
200
+ const pluginRoot = await this.getPluginBasePath();
201
+ if (!pluginRoot) {
202
+ return;
203
+ }
204
+ const basePath = (0, import_path.resolve)(pluginRoot, "ai");
205
+ if (!await (0, import_utils.fsExists)(basePath)) {
206
+ return;
207
+ }
208
+ const toolsLoader = new import_ai.ToolsLoader(this.ai, {
209
+ scan: {
210
+ basePath,
211
+ pattern: ["**/tools/**/*.ts", "**/tools/**/*.js", "!**/tools/**/*.d.ts", "**/tools/**/*/description.md"]
212
+ },
213
+ log: this.log
214
+ });
215
+ await toolsLoader.load();
216
+ }
192
217
  /**
193
218
  * @deprecated
194
219
  */
@@ -6,23 +6,33 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
- import Redis from 'ioredis';
10
9
  import { Logger } from '@nocobase/logger';
11
- export { Redis };
12
- export interface RedisConfig {
13
- connectionString: string;
10
+ import { createClient } from 'redis';
11
+ type RedisClientOptions = NonNullable<Parameters<typeof createClient>[0]>;
12
+ export type Redis = ReturnType<typeof createClient>;
13
+ export interface RedisConfig extends RedisClientOptions {
14
+ connectionString?: string;
14
15
  }
15
16
  export declare class RedisConnectionManager {
16
17
  private logger;
17
18
  private config;
18
19
  private connections;
20
+ private connectionConfigs;
21
+ private connecting;
19
22
  constructor(config: {
20
- redisConfig: RedisConfig;
23
+ redisConfig?: RedisConfig;
21
24
  logger: Logger;
22
25
  });
23
26
  private bindEvents;
27
+ private mergeConfig;
28
+ private hasConnectionOptions;
29
+ private buildClientOptions;
30
+ private ensureConnected;
24
31
  private getClient;
25
32
  getConnection(key?: string, config?: RedisConfig): Redis | null;
33
+ private waitUntilReady;
26
34
  getConnectionSync(key?: string, config?: RedisConfig): Promise<Redis>;
35
+ private closeConnection;
27
36
  close(): Promise<void>;
28
37
  }
38
+ export {};
@@ -37,76 +37,170 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
37
37
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
38
  var redis_connection_manager_exports = {};
39
39
  __export(redis_connection_manager_exports, {
40
- Redis: () => import_ioredis.default,
41
40
  RedisConnectionManager: () => RedisConnectionManager
42
41
  });
43
42
  module.exports = __toCommonJS(redis_connection_manager_exports);
44
- var import_ioredis = __toESM(require("ioredis"));
43
+ var import_merge = __toESM(require("lodash/merge"));
44
+ var import_redis = require("redis");
45
45
  const _RedisConnectionManager = class _RedisConnectionManager {
46
46
  logger;
47
47
  config;
48
48
  connections = /* @__PURE__ */ new Map();
49
+ connectionConfigs = /* @__PURE__ */ new Map();
50
+ connecting = /* @__PURE__ */ new WeakMap();
49
51
  constructor(config) {
50
- this.config = config.redisConfig;
52
+ this.config = config.redisConfig || {};
51
53
  this.logger = config.logger;
52
54
  }
53
55
  bindEvents(conn, key, config) {
54
56
  conn.on("connect", () => {
55
57
  this.logger.info(`Redis connected`, {
56
58
  method: "getConnection",
57
- key,
58
- config
59
+ key
60
+ });
61
+ });
62
+ conn.on("ready", () => {
63
+ this.logger.trace(`Redis ready`, {
64
+ method: "getConnection",
65
+ key
59
66
  });
60
67
  });
61
68
  conn.on("error", (err) => {
62
69
  this.logger.error(err.message, {
63
70
  err,
64
71
  method: "getConnection",
65
- key,
66
- config
72
+ key
67
73
  });
68
74
  });
69
- conn.on("close", () => {
75
+ conn.on("end", () => {
70
76
  this.logger.trace(`Redis closed`, {
71
77
  method: "getConnection",
78
+ key
79
+ });
80
+ });
81
+ }
82
+ mergeConfig(config) {
83
+ return (0, import_merge.default)({}, this.config || {}, config || {});
84
+ }
85
+ hasConnectionOptions(config) {
86
+ if (!config) {
87
+ return false;
88
+ }
89
+ return Boolean(config.connectionString || config.url || config.socket);
90
+ }
91
+ buildClientOptions(config) {
92
+ const { connectionString, ...rest } = config;
93
+ const options = { ...rest };
94
+ if (connectionString && !options.url) {
95
+ options.url = connectionString;
96
+ }
97
+ return options;
98
+ }
99
+ ensureConnected(conn, key, config) {
100
+ if (conn.isOpen || this.connecting.has(conn)) {
101
+ return;
102
+ }
103
+ const promise = conn.connect().catch((err) => {
104
+ this.logger.error(err.message, {
105
+ err,
106
+ method: "connect",
72
107
  key,
73
108
  config
74
109
  });
110
+ this.connections.delete(key);
111
+ this.connectionConfigs.delete(key);
112
+ throw err;
113
+ }).finally(() => {
114
+ this.connecting.delete(conn);
75
115
  });
116
+ this.connecting.set(conn, promise);
76
117
  }
77
118
  getClient(key = "default", config) {
78
119
  let conn = this.connections.get(key);
79
120
  if (conn) {
121
+ this.ensureConnected(conn, key, this.connectionConfigs.get(key));
80
122
  return conn;
81
123
  }
82
- const cfg = config || this.config;
83
- if (!cfg.connectionString) {
124
+ const cfg = this.mergeConfig(config);
125
+ if (!this.hasConnectionOptions(cfg)) {
84
126
  return null;
85
127
  }
86
- conn = new import_ioredis.default(cfg.connectionString);
128
+ conn = (0, import_redis.createClient)(this.buildClientOptions(cfg));
87
129
  this.connections.set(key, conn);
130
+ this.connectionConfigs.set(key, cfg);
88
131
  this.bindEvents(conn, key, cfg);
132
+ this.ensureConnected(conn, key, cfg);
89
133
  return conn;
90
134
  }
91
135
  getConnection(key = "default", config) {
92
136
  return this.getClient(key, config);
93
137
  }
94
- async getConnectionSync(key = "default", config) {
95
- return new Promise((resolve, reject) => {
96
- const conn = this.getClient(key, config);
97
- if (!conn) {
98
- return reject(new Error("Redis connect string is missing"));
138
+ async waitUntilReady(conn) {
139
+ if (conn.isReady) {
140
+ return conn;
141
+ }
142
+ const pendingConnect = this.connecting.get(conn);
143
+ if (pendingConnect) {
144
+ await pendingConnect;
145
+ if (conn.isReady) {
146
+ return conn;
99
147
  }
100
- conn.once("connect", () => resolve(conn));
101
- conn.once("error", reject);
148
+ } else if (!conn.isOpen) {
149
+ await conn.connect();
150
+ if (conn.isReady) {
151
+ return conn;
152
+ }
153
+ }
154
+ return new Promise((resolve, reject) => {
155
+ const cleanup = /* @__PURE__ */ __name(() => {
156
+ conn.off("ready", handleReady);
157
+ conn.off("error", handleError);
158
+ }, "cleanup");
159
+ const handleReady = /* @__PURE__ */ __name(() => {
160
+ cleanup();
161
+ resolve(conn);
162
+ }, "handleReady");
163
+ const handleError = /* @__PURE__ */ __name((err) => {
164
+ cleanup();
165
+ reject(err);
166
+ }, "handleError");
167
+ conn.once("ready", handleReady);
168
+ conn.once("error", handleError);
102
169
  });
103
170
  }
104
- async close() {
105
- for (const conn of this.connections.values()) {
106
- if (!(conn == null ? void 0 : conn.status) || conn.status === "close" || conn.status === "end") {
107
- continue;
108
- }
171
+ async getConnectionSync(key = "default", config) {
172
+ const conn = this.getClient(key, config);
173
+ if (!conn) {
174
+ throw new Error("Redis connection configuration is missing");
175
+ }
176
+ return this.waitUntilReady(conn);
177
+ }
178
+ async closeConnection(key, conn) {
179
+ if (!(conn == null ? void 0 : conn.isOpen)) {
180
+ return;
181
+ }
182
+ try {
109
183
  await conn.quit();
184
+ } catch (err) {
185
+ this.logger.warn(`Failed to quit redis connection`, {
186
+ err,
187
+ method: "closeConnection",
188
+ key
189
+ });
190
+ try {
191
+ conn.destroy();
192
+ } catch (disconnectErr) {
193
+ this.logger.warn(`Failed to disconnect redis connection`, {
194
+ err: disconnectErr,
195
+ method: "closeConnection",
196
+ key
197
+ });
198
+ }
199
+ }
200
+ }
201
+ async close() {
202
+ for (const [key, conn] of this.connections.entries()) {
203
+ await this.closeConnection(key, conn);
110
204
  }
111
205
  }
112
206
  };
@@ -114,6 +208,5 @@ __name(_RedisConnectionManager, "RedisConnectionManager");
114
208
  let RedisConnectionManager = _RedisConnectionManager;
115
209
  // Annotate the CommonJS export names for ESM import in node:
116
210
  0 && (module.exports = {
117
- Redis,
118
211
  RedisConnectionManager
119
212
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/server",
3
- "version": "2.0.0-beta.8",
3
+ "version": "2.0.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "license": "AGPL-3.0",
@@ -10,20 +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.0.0-beta.8",
14
- "@nocobase/actions": "2.0.0-beta.8",
15
- "@nocobase/auth": "2.0.0-beta.8",
16
- "@nocobase/cache": "2.0.0-beta.8",
17
- "@nocobase/data-source-manager": "2.0.0-beta.8",
18
- "@nocobase/database": "2.0.0-beta.8",
19
- "@nocobase/evaluators": "2.0.0-beta.8",
20
- "@nocobase/lock-manager": "2.0.0-beta.8",
21
- "@nocobase/logger": "2.0.0-beta.8",
22
- "@nocobase/resourcer": "2.0.0-beta.8",
23
- "@nocobase/sdk": "2.0.0-beta.8",
24
- "@nocobase/snowflake-id": "2.0.0-beta.8",
25
- "@nocobase/telemetry": "2.0.0-beta.8",
26
- "@nocobase/utils": "2.0.0-beta.8",
13
+ "@nocobase/acl": "2.0.0",
14
+ "@nocobase/actions": "2.0.0",
15
+ "@nocobase/ai": "2.0.0",
16
+ "@nocobase/auth": "2.0.0",
17
+ "@nocobase/cache": "2.0.0",
18
+ "@nocobase/data-source-manager": "2.0.0",
19
+ "@nocobase/database": "2.0.0",
20
+ "@nocobase/evaluators": "2.0.0",
21
+ "@nocobase/lock-manager": "2.0.0",
22
+ "@nocobase/logger": "2.0.0",
23
+ "@nocobase/resourcer": "2.0.0",
24
+ "@nocobase/sdk": "2.0.0",
25
+ "@nocobase/snowflake-id": "2.0.0",
26
+ "@nocobase/telemetry": "2.0.0",
27
+ "@nocobase/utils": "2.0.0",
27
28
  "@types/decompress": "4.2.7",
28
29
  "@types/ini": "^1.3.31",
29
30
  "@types/koa-send": "^4.1.3",
@@ -41,7 +42,6 @@
41
42
  "fs-extra": "^11.1.1",
42
43
  "i18next": "^22.4.9",
43
44
  "ini": "^4.1.1",
44
- "ioredis": "^5.7.0",
45
45
  "koa": "^2.15.4",
46
46
  "koa-bodyparser": "^4.3.0",
47
47
  "koa-send": "^5.0.1",
@@ -50,6 +50,7 @@
50
50
  "multer": "^1.4.5-lts.2",
51
51
  "nanoid": "^3.3.11",
52
52
  "p-queue": "^6.6.2",
53
+ "redis": "^5.10.0",
53
54
  "semver": "^7.7.1",
54
55
  "serve-handler": "^6.1.6",
55
56
  "ws": "^8.13.0",
@@ -60,5 +61,5 @@
60
61
  "@types/serve-handler": "^6.1.1",
61
62
  "@types/ws": "^8.5.5"
62
63
  },
63
- "gitHead": "6bd912b1028eb8bc09a823d35e4d37b3000861b1"
64
+ "gitHead": "3590c0087a56f0f285a5357f43a80bdc62b11bec"
64
65
  }