@lark-apaas/nestjs-datapaas 1.0.17 → 1.0.19
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/dist/index.cjs +174 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -9
- package/dist/index.d.ts +10 -9
- package/dist/index.js +174 -80
- package/dist/index.js.map +1 -1
- package/package.json +4 -9
- package/patches/drizzle-orm+0.44.6.patch +0 -178
- package/scripts/apply-patch.js +0 -21
package/dist/index.cjs
CHANGED
|
@@ -225,6 +225,7 @@ var DrizzleDatabaseManager = class {
|
|
|
225
225
|
requestContext;
|
|
226
226
|
db = null;
|
|
227
227
|
sqlClient = null;
|
|
228
|
+
connectionOpPromise = null;
|
|
228
229
|
currentConnectionInfo = null;
|
|
229
230
|
isConnected = false;
|
|
230
231
|
skipInitDbConnection = false;
|
|
@@ -233,7 +234,7 @@ var DrizzleDatabaseManager = class {
|
|
|
233
234
|
tokenWatchFilePath = null;
|
|
234
235
|
tokenWatchDebounceTimer = null;
|
|
235
236
|
refreshingByTokenChange = false;
|
|
236
|
-
|
|
237
|
+
sqlClientShutdownTimeoutSeconds = 5;
|
|
237
238
|
/**
|
|
238
239
|
* 构造函数:注入配置、日志能力与请求上下文。
|
|
239
240
|
*/
|
|
@@ -269,32 +270,30 @@ var DrizzleDatabaseManager = class {
|
|
|
269
270
|
};
|
|
270
271
|
return this.db;
|
|
271
272
|
}
|
|
272
|
-
|
|
273
|
-
const resolvedConnectionString = await this.buildConnectionStringWithToken();
|
|
274
|
-
const { connectionString, connectionConfig } = parseConnectionInfo(resolvedConnectionString, this.config);
|
|
275
|
-
const baseSql = (0, import_postgres.default)(connectionString, connectionConfig);
|
|
276
|
-
const sqlProxy = genPostgresProxy(baseSql, this.observable, this.requestContext);
|
|
277
|
-
this.db = (0, import_postgres_js.drizzle)(sqlProxy, {
|
|
278
|
-
schema: this.config.schema,
|
|
279
|
-
logger: this.config.logger
|
|
280
|
-
});
|
|
281
|
-
this.sqlClient = baseSql;
|
|
282
|
-
this.currentResolvedConnectionString = connectionString;
|
|
283
|
-
this.currentConnectionInfo = {
|
|
284
|
-
connectionString,
|
|
285
|
-
connectionConfig: {
|
|
286
|
-
ssl: connectionConfig.ssl,
|
|
287
|
-
connectionTimeout: connectionConfig.connect_timeout,
|
|
288
|
-
idleTimeout: connectionConfig.idle_timeout,
|
|
289
|
-
maxConnections: connectionConfig.max
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
this.isConnected = true;
|
|
273
|
+
if (this.db && this.isConnected) {
|
|
293
274
|
return this.db;
|
|
294
|
-
} catch (error) {
|
|
295
|
-
this.isConnected = false;
|
|
296
|
-
throw new Error(`Failed to initialize database connection: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
297
275
|
}
|
|
276
|
+
if (this.connectionOpPromise) {
|
|
277
|
+
return this.connectionOpPromise;
|
|
278
|
+
}
|
|
279
|
+
return this.runWithConnectionLock(async () => {
|
|
280
|
+
if (this.db && this.isConnected) {
|
|
281
|
+
return this.db;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
const token = await this.readConnectionTokenFromFile();
|
|
285
|
+
const resolvedConnectionString = this.appendTokenToConnectionString(this.config.connectionString, token);
|
|
286
|
+
const nextState = await this.createConnectedState(resolvedConnectionString, token);
|
|
287
|
+
this.applyConnectedState(nextState);
|
|
288
|
+
return nextState.db;
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (!this.db || !this.isConnected) {
|
|
291
|
+
this.isConnected = false;
|
|
292
|
+
this.db = null;
|
|
293
|
+
}
|
|
294
|
+
throw new Error(`Failed to initialize database connection: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
298
297
|
}
|
|
299
298
|
/**
|
|
300
299
|
* 获取数据库实例
|
|
@@ -327,22 +326,10 @@ var DrizzleDatabaseManager = class {
|
|
|
327
326
|
* 重新连接数据库。
|
|
328
327
|
* 注意:重连时不关闭 token watcher,避免重连后丢失后续文件变更监听。
|
|
329
328
|
* 同时通过 promise 锁避免并发重连导致状态抖动。
|
|
330
|
-
* @param forceRefresh 是否强制刷新连接信息
|
|
331
329
|
* @returns 新的数据库实例
|
|
332
330
|
*/
|
|
333
|
-
async reconnect(
|
|
334
|
-
|
|
335
|
-
return this.reconnectingPromise;
|
|
336
|
-
}
|
|
337
|
-
this.reconnectingPromise = (async () => {
|
|
338
|
-
await this.disconnect(false);
|
|
339
|
-
return this.initialize();
|
|
340
|
-
})();
|
|
341
|
-
try {
|
|
342
|
-
return await this.reconnectingPromise;
|
|
343
|
-
} finally {
|
|
344
|
-
this.reconnectingPromise = null;
|
|
345
|
-
}
|
|
331
|
+
async reconnect() {
|
|
332
|
+
return this.reconnectInternal();
|
|
346
333
|
}
|
|
347
334
|
/**
|
|
348
335
|
* 断开数据库连接
|
|
@@ -351,21 +338,19 @@ var DrizzleDatabaseManager = class {
|
|
|
351
338
|
if (shouldStopWatcher) {
|
|
352
339
|
this.stopTokenWatcher();
|
|
353
340
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
} catch (error) {
|
|
361
|
-
console.error("Error disconnecting from database:", error);
|
|
362
|
-
} finally {
|
|
363
|
-
this.sqlClient = null;
|
|
364
|
-
this.db = null;
|
|
365
|
-
this.isConnected = false;
|
|
366
|
-
this.currentConnectionInfo = null;
|
|
367
|
-
this.currentResolvedConnectionString = null;
|
|
341
|
+
const pendingOp = this.connectionOpPromise;
|
|
342
|
+
if (pendingOp) {
|
|
343
|
+
await Promise.allSettled([
|
|
344
|
+
pendingOp
|
|
345
|
+
]);
|
|
368
346
|
}
|
|
347
|
+
await this.shutdownSqlClientSafely(this.sqlClient, "Error disconnecting from database:");
|
|
348
|
+
this.sqlClient = null;
|
|
349
|
+
this.db = null;
|
|
350
|
+
this.isConnected = false;
|
|
351
|
+
this.currentConnectionInfo = null;
|
|
352
|
+
this.currentResolvedConnectionString = null;
|
|
353
|
+
this.currentToken = null;
|
|
369
354
|
}
|
|
370
355
|
/**
|
|
371
356
|
* 获取当前连接信息
|
|
@@ -435,13 +420,80 @@ var DrizzleDatabaseManager = class {
|
|
|
435
420
|
url.searchParams.set("options", nextOptions);
|
|
436
421
|
return url.toString();
|
|
437
422
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
423
|
+
async createConnectedState(resolvedConnectionString, token) {
|
|
424
|
+
const { connectionString, connectionConfig } = parseConnectionInfo(resolvedConnectionString, this.config);
|
|
425
|
+
const baseSql = (0, import_postgres.default)(connectionString, connectionConfig);
|
|
426
|
+
try {
|
|
427
|
+
const sqlProxy = genPostgresProxy(baseSql, this.observable, this.requestContext);
|
|
428
|
+
const db = (0, import_postgres_js.drizzle)(sqlProxy, {
|
|
429
|
+
schema: this.config.schema,
|
|
430
|
+
logger: this.config.logger
|
|
431
|
+
});
|
|
432
|
+
return {
|
|
433
|
+
db,
|
|
434
|
+
sqlClient: baseSql,
|
|
435
|
+
resolvedConnectionString: connectionString,
|
|
436
|
+
token,
|
|
437
|
+
connectionInfo: {
|
|
438
|
+
connectionString,
|
|
439
|
+
connectionConfig: {
|
|
440
|
+
ssl: connectionConfig.ssl,
|
|
441
|
+
connectionTimeout: connectionConfig.connect_timeout,
|
|
442
|
+
idleTimeout: connectionConfig.idle_timeout,
|
|
443
|
+
maxConnections: connectionConfig.max
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
} catch (error) {
|
|
448
|
+
await this.shutdownSqlClientSafely(baseSql, "Error closing failed new database client:");
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
applyConnectedState(nextState) {
|
|
453
|
+
this.db = nextState.db;
|
|
454
|
+
this.sqlClient = nextState.sqlClient;
|
|
455
|
+
this.currentConnectionInfo = nextState.connectionInfo;
|
|
456
|
+
this.currentResolvedConnectionString = nextState.resolvedConnectionString;
|
|
457
|
+
this.currentToken = nextState.token;
|
|
458
|
+
this.isConnected = true;
|
|
459
|
+
}
|
|
460
|
+
reconnectInternal(nextResolvedConnectionString, nextToken) {
|
|
461
|
+
return this.runWithConnectionLock(() => this.reconnectWithResolvedConnectionString(nextResolvedConnectionString, nextToken));
|
|
462
|
+
}
|
|
463
|
+
runWithConnectionLock(operation) {
|
|
464
|
+
if (this.connectionOpPromise) {
|
|
465
|
+
return this.connectionOpPromise;
|
|
466
|
+
}
|
|
467
|
+
this.connectionOpPromise = (async () => {
|
|
468
|
+
try {
|
|
469
|
+
return await operation();
|
|
470
|
+
} finally {
|
|
471
|
+
this.connectionOpPromise = null;
|
|
472
|
+
}
|
|
473
|
+
})();
|
|
474
|
+
return this.connectionOpPromise;
|
|
475
|
+
}
|
|
476
|
+
async reconnectWithResolvedConnectionString(nextResolvedConnectionString, nextToken) {
|
|
477
|
+
const resolvedToken = nextToken ?? await this.readConnectionTokenFromFile();
|
|
478
|
+
const resolvedConnectionString = nextResolvedConnectionString ?? this.appendTokenToConnectionString(this.config.connectionString, resolvedToken);
|
|
479
|
+
const nextState = await this.createConnectedState(resolvedConnectionString, resolvedToken);
|
|
480
|
+
const previousSqlClient = this.sqlClient;
|
|
481
|
+
this.applyConnectedState(nextState);
|
|
482
|
+
if (previousSqlClient && previousSqlClient !== nextState.sqlClient) {
|
|
483
|
+
await this.shutdownSqlClientSafely(previousSqlClient, "Error shutting down previous database client after reconnect:");
|
|
484
|
+
}
|
|
485
|
+
return nextState.db;
|
|
486
|
+
}
|
|
487
|
+
async shutdownSqlClientSafely(client, errorPrefix) {
|
|
488
|
+
try {
|
|
489
|
+
if (client && typeof client.end === "function") {
|
|
490
|
+
await client.end({
|
|
491
|
+
timeout: this.sqlClientShutdownTimeoutSeconds
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
} catch (error) {
|
|
495
|
+
console.error(errorPrefix, error);
|
|
496
|
+
}
|
|
445
497
|
}
|
|
446
498
|
/**
|
|
447
499
|
* 启动 token 文件监听;若已存在旧监听则先清理后重建。
|
|
@@ -513,7 +565,7 @@ var DrizzleDatabaseManager = class {
|
|
|
513
565
|
if (sameToken && sameConnectionString && this.isConnected) {
|
|
514
566
|
return;
|
|
515
567
|
}
|
|
516
|
-
await this.
|
|
568
|
+
await this.reconnectInternal(nextResolvedConnectionString, nextToken);
|
|
517
569
|
} catch (error) {
|
|
518
570
|
console.error("Failed to refresh database connection from token file:", error);
|
|
519
571
|
} finally {
|
|
@@ -580,11 +632,10 @@ var DataPaasDatabaseService = class {
|
|
|
580
632
|
}
|
|
581
633
|
/**
|
|
582
634
|
* 重新连接数据库
|
|
583
|
-
* @param forceRefresh 是否强制刷新连接信息
|
|
584
635
|
* @returns 新的数据库实例
|
|
585
636
|
*/
|
|
586
|
-
async reconnect(
|
|
587
|
-
return this.databaseManager.reconnect(
|
|
637
|
+
async reconnect() {
|
|
638
|
+
return this.databaseManager.reconnect();
|
|
588
639
|
}
|
|
589
640
|
/**
|
|
590
641
|
* 获取当前连接信息
|
|
@@ -632,7 +683,20 @@ DataPaasDatabaseService = _ts_decorate2([
|
|
|
632
683
|
|
|
633
684
|
// src/database/sql-exection-context.middleware.ts
|
|
634
685
|
var import_common4 = require("@nestjs/common");
|
|
635
|
-
|
|
686
|
+
|
|
687
|
+
// src/database/drizzle-auth-context.ts
|
|
688
|
+
var import_node_async_hooks = require("async_hooks");
|
|
689
|
+
var asyncLocalStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
690
|
+
function getAuthContextStore() {
|
|
691
|
+
return asyncLocalStorage.getStore();
|
|
692
|
+
}
|
|
693
|
+
__name(getAuthContextStore, "getAuthContextStore");
|
|
694
|
+
function runWithAuthContext(store, fn) {
|
|
695
|
+
return asyncLocalStorage.run(store, fn);
|
|
696
|
+
}
|
|
697
|
+
__name(runWithAuthContext, "runWithAuthContext");
|
|
698
|
+
|
|
699
|
+
// src/database/sql-exection-context.middleware.ts
|
|
636
700
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
637
701
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
638
702
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -675,7 +739,7 @@ var SqlExecutionContextMiddleware = class {
|
|
|
675
739
|
// 用户类型
|
|
676
740
|
userType ? `SET LOCAL app.user_type = '${userType}'` : `SET LOCAL app.user_type = ''`
|
|
677
741
|
].filter(Boolean).join(";");
|
|
678
|
-
|
|
742
|
+
runWithAuthContext({
|
|
679
743
|
preSql: sqls
|
|
680
744
|
}, () => {
|
|
681
745
|
next();
|
|
@@ -694,6 +758,49 @@ SqlExecutionContextMiddleware = _ts_decorate3([
|
|
|
694
758
|
// src/database/database.module.ts
|
|
695
759
|
var import_nestjs_common3 = require("@lark-apaas/nestjs-common");
|
|
696
760
|
var import_nestjs_observable = require("@lark-apaas/nestjs-observable");
|
|
761
|
+
|
|
762
|
+
// src/database/drizzle-monkey-patch.ts
|
|
763
|
+
var patched = false;
|
|
764
|
+
function applyDrizzleMonkeyPatch() {
|
|
765
|
+
if (patched) return;
|
|
766
|
+
const pgModule = require("drizzle-orm/postgres-js");
|
|
767
|
+
const PreparedQuery = pgModule.PostgresJsPreparedQuery;
|
|
768
|
+
if (!PreparedQuery?.prototype?.execute) {
|
|
769
|
+
throw new Error("drizzle-orm PostgresJsPreparedQuery.prototype.execute \u4E0D\u5B58\u5728\uFF0C\u7248\u672C\u53EF\u80FD\u4E0D\u517C\u5BB9");
|
|
770
|
+
}
|
|
771
|
+
const originalExecute = PreparedQuery.prototype.execute;
|
|
772
|
+
PreparedQuery.prototype.execute = async function(...args) {
|
|
773
|
+
const authContext = getAuthContextStore();
|
|
774
|
+
const preSql = authContext?.preSql;
|
|
775
|
+
const postSql = authContext?.postSql;
|
|
776
|
+
if (!preSql && !postSql) {
|
|
777
|
+
return originalExecute.apply(this, args);
|
|
778
|
+
}
|
|
779
|
+
const client = this.client;
|
|
780
|
+
if (typeof client.begin === "function") {
|
|
781
|
+
return client.begin(async (tx) => {
|
|
782
|
+
const savedClient = this.client;
|
|
783
|
+
this.client = tx;
|
|
784
|
+
try {
|
|
785
|
+
if (preSql) await tx.unsafe(preSql);
|
|
786
|
+
const result2 = await originalExecute.apply(this, args);
|
|
787
|
+
if (postSql) await tx.unsafe(postSql);
|
|
788
|
+
return result2;
|
|
789
|
+
} finally {
|
|
790
|
+
this.client = savedClient;
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
if (preSql) await client.unsafe(preSql);
|
|
795
|
+
const result = await originalExecute.apply(this, args);
|
|
796
|
+
if (postSql) await client.unsafe(postSql);
|
|
797
|
+
return result;
|
|
798
|
+
};
|
|
799
|
+
patched = true;
|
|
800
|
+
}
|
|
801
|
+
__name(applyDrizzleMonkeyPatch, "applyDrizzleMonkeyPatch");
|
|
802
|
+
|
|
803
|
+
// src/database/database.module.ts
|
|
697
804
|
function _ts_decorate4(decorators, target, key, desc) {
|
|
698
805
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
699
806
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -701,20 +808,7 @@ function _ts_decorate4(decorators, target, key, desc) {
|
|
|
701
808
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
702
809
|
}
|
|
703
810
|
__name(_ts_decorate4, "_ts_decorate");
|
|
704
|
-
|
|
705
|
-
try {
|
|
706
|
-
const postgres2 = require("drizzle-orm/postgres-js");
|
|
707
|
-
const ok = postgres2 && typeof postgres2.getAuthContextStore === "function" && typeof postgres2.runWithAuthContext === "function";
|
|
708
|
-
if (!ok) {
|
|
709
|
-
throw new Error("drizzle-orm \u672A\u68C0\u6D4B\u5230\u8865\u4E01\u5BFC\u51FA\uFF1AgetAuthContextStore/runWithAuthContext");
|
|
710
|
-
}
|
|
711
|
-
} catch (err) {
|
|
712
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
713
|
-
throw new Error(`drizzle-orm \u8865\u4E01\u672A\u751F\u6548\u6216\u7248\u672C\u4E0D\u5339\u914D\uFF1A${reason}`);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
__name(verifyDrizzlePatched, "verifyDrizzlePatched");
|
|
717
|
-
verifyDrizzlePatched();
|
|
811
|
+
applyDrizzleMonkeyPatch();
|
|
718
812
|
var DataPaasModule = class _DataPaasModule {
|
|
719
813
|
static {
|
|
720
814
|
__name(this, "DataPaasModule");
|