@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.
- package/lib/aes-encryptor.d.ts +1 -0
- package/lib/aes-encryptor.js +8 -5
- package/lib/ai/create-docs-index.d.ts +13 -0
- package/lib/ai/create-docs-index.js +892 -0
- package/lib/app-supervisor/app-options-factory.d.ts +80 -0
- package/lib/app-supervisor/app-options-factory.js +91 -0
- package/lib/app-supervisor/condition-registry.d.ts +18 -0
- package/lib/app-supervisor/condition-registry.js +60 -0
- package/lib/app-supervisor/db-creator.d.ts +16 -0
- package/lib/app-supervisor/db-creator.js +163 -0
- package/lib/app-supervisor/db-drivers.d.ts +11 -0
- package/lib/app-supervisor/db-drivers.js +52 -0
- package/lib/app-supervisor/index.d.ts +161 -0
- package/lib/app-supervisor/index.js +690 -0
- package/lib/app-supervisor/main-only-adapter.d.ts +37 -0
- package/lib/app-supervisor/main-only-adapter.js +156 -0
- package/lib/app-supervisor/types.d.ts +161 -0
- package/lib/app-supervisor/types.js +24 -0
- package/lib/application.d.ts +3 -1
- package/lib/application.js +10 -6
- package/lib/commands/ai.d.ts +11 -0
- package/lib/commands/ai.js +40 -0
- package/lib/commands/console.js +1 -1
- package/lib/commands/index.js +2 -0
- package/lib/commands/install.js +2 -0
- package/lib/commands/start.js +3 -0
- package/lib/commands/upgrade.js +2 -0
- package/lib/gateway/errors.js +1 -1
- package/lib/gateway/index.js +64 -15
- package/lib/gateway/ipc-socket-server.js +1 -1
- package/lib/gateway/ws-server.js +3 -2
- package/lib/helper.js +20 -3
- package/lib/plugin-manager/deps.js +1 -1
- package/lib/plugin-manager/plugin-manager.js +2 -0
- package/lib/plugin.d.ts +5 -0
- package/lib/plugin.js +25 -0
- package/lib/redis-connection-manager.d.ts +15 -5
- package/lib/redis-connection-manager.js +117 -24
- package/package.json +18 -17
- package/lib/app-supervisor.d.ts +0 -74
- package/lib/app-supervisor.js +0 -338
package/lib/gateway/index.js
CHANGED
|
@@ -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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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 =
|
|
297
|
+
const hasApp = supervisor.hasApp(handleApp);
|
|
269
298
|
if (!hasApp) {
|
|
270
|
-
void
|
|
299
|
+
void supervisor.bootstrapApp(handleApp);
|
|
271
300
|
}
|
|
272
|
-
let appStatus =
|
|
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
|
|
287
|
-
appInstance
|
|
288
|
-
|
|
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
|
-
|
|
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);
|
package/lib/gateway/ws-server.js
CHANGED
|
@@ -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().
|
|
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().
|
|
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
|
|
94
|
-
return ctx.get("origin");
|
|
95
|
-
},
|
|
112
|
+
origin: resolveCorsOrigin,
|
|
96
113
|
...options.cors
|
|
97
114
|
}),
|
|
98
115
|
{
|
|
@@ -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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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("
|
|
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 =
|
|
83
|
-
if (!cfg
|
|
124
|
+
const cfg = this.mergeConfig(config);
|
|
125
|
+
if (!this.hasConnectionOptions(cfg)) {
|
|
84
126
|
return null;
|
|
85
127
|
}
|
|
86
|
-
conn =
|
|
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
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
101
|
-
conn.
|
|
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
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
|
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
|
|
14
|
-
"@nocobase/actions": "2.0.0
|
|
15
|
-
"@nocobase/
|
|
16
|
-
"@nocobase/
|
|
17
|
-
"@nocobase/
|
|
18
|
-
"@nocobase/
|
|
19
|
-
"@nocobase/
|
|
20
|
-
"@nocobase/
|
|
21
|
-
"@nocobase/
|
|
22
|
-
"@nocobase/
|
|
23
|
-
"@nocobase/
|
|
24
|
-
"@nocobase/
|
|
25
|
-
"@nocobase/
|
|
26
|
-
"@nocobase/
|
|
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": "
|
|
64
|
+
"gitHead": "3590c0087a56f0f285a5357f43a80bdc62b11bec"
|
|
64
65
|
}
|