@lark-apaas/nestjs-datapaas 1.0.18 → 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 CHANGED
@@ -683,7 +683,20 @@ DataPaasDatabaseService = _ts_decorate2([
683
683
 
684
684
  // src/database/sql-exection-context.middleware.ts
685
685
  var import_common4 = require("@nestjs/common");
686
- var session = __toESM(require("drizzle-orm/postgres-js"), 1);
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
687
700
  function _ts_decorate3(decorators, target, key, desc) {
688
701
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
689
702
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -726,7 +739,7 @@ var SqlExecutionContextMiddleware = class {
726
739
  // 用户类型
727
740
  userType ? `SET LOCAL app.user_type = '${userType}'` : `SET LOCAL app.user_type = ''`
728
741
  ].filter(Boolean).join(";");
729
- session.runWithAuthContext({
742
+ runWithAuthContext({
730
743
  preSql: sqls
731
744
  }, () => {
732
745
  next();
@@ -745,6 +758,49 @@ SqlExecutionContextMiddleware = _ts_decorate3([
745
758
  // src/database/database.module.ts
746
759
  var import_nestjs_common3 = require("@lark-apaas/nestjs-common");
747
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
748
804
  function _ts_decorate4(decorators, target, key, desc) {
749
805
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
750
806
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -752,20 +808,7 @@ function _ts_decorate4(decorators, target, key, desc) {
752
808
  return c > 3 && r && Object.defineProperty(target, key, r), r;
753
809
  }
754
810
  __name(_ts_decorate4, "_ts_decorate");
755
- function verifyDrizzlePatched() {
756
- try {
757
- const postgres2 = require("drizzle-orm/postgres-js");
758
- const ok = postgres2 && typeof postgres2.getAuthContextStore === "function" && typeof postgres2.runWithAuthContext === "function";
759
- if (!ok) {
760
- throw new Error("drizzle-orm \u672A\u68C0\u6D4B\u5230\u8865\u4E01\u5BFC\u51FA\uFF1AgetAuthContextStore/runWithAuthContext");
761
- }
762
- } catch (err) {
763
- const reason = err instanceof Error ? err.message : String(err);
764
- throw new Error(`drizzle-orm \u8865\u4E01\u672A\u751F\u6548\u6216\u7248\u672C\u4E0D\u5339\u914D\uFF1A${reason}`);
765
- }
766
- }
767
- __name(verifyDrizzlePatched, "verifyDrizzlePatched");
768
- verifyDrizzlePatched();
811
+ applyDrizzleMonkeyPatch();
769
812
  var DataPaasModule = class _DataPaasModule {
770
813
  static {
771
814
  __name(this, "DataPaasModule");
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/database/drizzle-manager.ts","../src/database/const.ts","../src/utils.ts","../src/database/postgres-proxy.ts","../src/database/database.module.ts","../src/database/database.service.ts","../src/database/sql-exection-context.middleware.ts","../src/database/database-utils.ts"],"sourcesContent":["// 导出所有公共API\n\nexport * from './database';\nexport * from './types';\n","import { Injectable, OnModuleInit, OnModuleDestroy, Inject } from '@nestjs/common';\nimport { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { sql } from 'drizzle-orm';\nimport postgres from 'postgres';\nimport { readFile } from 'node:fs/promises';\nimport { watchFile, unwatchFile } from 'node:fs';\nimport { DATAPAAS_CONFIG, DATAPAAS_LOGGER_SERVICE } from './const';\nimport { RequestContextService, ObservableService } from '@lark-apaas/nestjs-common';\n\n\nimport { type DataPaasConfig, type DatabaseConnectionInfo } from '../types';\nimport { parseConnectionInfo } from '../utils';\nimport { genPostgresProxy } from './postgres-proxy';\n\n@Injectable()\nexport class DrizzleDatabaseManager implements OnModuleInit, OnModuleDestroy {\n private db: PostgresJsDatabase | null = null;\n private sqlClient: ReturnType<typeof postgres> | null = null;\n private connectionOpPromise: Promise<PostgresJsDatabase> | null = null;\n private currentConnectionInfo: DatabaseConnectionInfo | null = null;\n private isConnected: boolean = false;\n private skipInitDbConnection: boolean = false;\n private currentToken: string | null = null;\n private currentResolvedConnectionString: string | null = null;\n private tokenWatchFilePath: string | null = null;\n private tokenWatchDebounceTimer: NodeJS.Timeout | null = null;\n private refreshingByTokenChange = false;\n\n private readonly sqlClientShutdownTimeoutSeconds = 5;\n\n /**\n * 构造函数:注入配置、日志能力与请求上下文。\n */\n constructor(\n @Inject(DATAPAAS_CONFIG)\n private readonly config: DataPaasConfig,\n @Inject(DATAPAAS_LOGGER_SERVICE)\n private readonly observable: ObservableService,\n private readonly requestContext: RequestContextService,\n ) {\n this.skipInitDbConnection = !!process.env.DEPRECATED_SKIP_INIT_DB_CONNECTION;\n }\n\n /**\n * 模块初始化:建立首个数据库连接并启动 token 监听。\n */\n async onModuleInit() {\n await this.initialize();\n this.startTokenWatcher();\n }\n\n /**\n * 模块销毁:先停止 token 监听,再断开数据库连接。\n */\n async onModuleDestroy() {\n this.stopTokenWatcher();\n await this.disconnect();\n }\n\n /**\n * 初始化数据库连接\n * @returns Drizzle 数据库实例\n */\n async initialize(): Promise<PostgresJsDatabase> {\n if (this.skipInitDbConnection) {\n this.isConnected = true;\n this.db = { name: 'skipInitDbConnection' } as unknown as PostgresJsDatabase;\n return this.db;\n }\n\n if (this.db && this.isConnected) {\n return this.db;\n }\n\n if (this.connectionOpPromise) {\n return this.connectionOpPromise;\n }\n\n return this.runWithConnectionLock(async () => {\n if (this.db && this.isConnected) {\n return this.db;\n }\n\n try {\n const token = await this.readConnectionTokenFromFile();\n const resolvedConnectionString = this.appendTokenToConnectionString(this.config.connectionString, token);\n const nextState = await this.createConnectedState(resolvedConnectionString, token);\n\n this.applyConnectedState(nextState);\n return nextState.db;\n } catch (error) {\n // 避免并发 initialize 时后失败覆盖先成功状态。\n if (!this.db || !this.isConnected) {\n this.isConnected = false;\n this.db = null;\n }\n throw new Error(`Failed to initialize database connection: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n });\n }\n\n /**\n * 获取数据库实例\n * @returns Drizzle 数据库实例\n */\n getDatabase(): PostgresJsDatabase {\n if (!this.db || !this.isConnected) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n return this.db;\n }\n\n /**\n * 测试数据库连接\n * @returns 连接是否正常\n */\n async testConnection(): Promise<boolean> {\n if (!this.db) {\n return false;\n }\n\n try {\n await this.db.execute(sql`SELECT 1`);\n return true;\n } catch (error) {\n console.error('Database connection test failed:', error);\n this.isConnected = false;\n return false;\n }\n }\n\n /**\n * 重新连接数据库。\n * 注意:重连时不关闭 token watcher,避免重连后丢失后续文件变更监听。\n * 同时通过 promise 锁避免并发重连导致状态抖动。\n * @returns 新的数据库实例\n */\n async reconnect(): Promise<PostgresJsDatabase> {\n return this.reconnectInternal();\n }\n\n /**\n * 断开数据库连接\n */\n async disconnect(shouldStopWatcher: boolean = true): Promise<void> {\n if (shouldStopWatcher) {\n this.stopTokenWatcher();\n }\n\n const pendingOp = this.connectionOpPromise;\n if (pendingOp) {\n await Promise.allSettled([pendingOp]);\n }\n\n await this.shutdownSqlClientSafely(this.sqlClient, 'Error disconnecting from database:');\n this.sqlClient = null;\n this.db = null;\n this.isConnected = false;\n this.currentConnectionInfo = null;\n this.currentResolvedConnectionString = null;\n this.currentToken = null;\n }\n\n /**\n * 获取当前连接信息\n * @returns 当前连接信息\n */\n getConnectionInfo(): DatabaseConnectionInfo | null {\n return this.currentConnectionInfo;\n }\n\n /**\n * 检查连接状态\n * @returns 是否已连接\n */\n isDatabaseConnected(): boolean {\n return this.isConnected;\n }\n\n /**\n * 开始事务\n * @param callback 事务回调函数\n * @returns 事务结果\n */\n async transaction<T>(callback: (tx: PostgresJsDatabase) => Promise<T>): Promise<T> {\n const db = this.getDatabase();\n return db.transaction(callback);\n }\n\n /**\n * 获取连接池状态\n * @returns 连接池状态信息\n */\n getConnectionPoolStatus(): { isConnected: boolean; connectionInfo: DatabaseConnectionInfo | null } {\n return {\n isConnected: this.isConnected,\n connectionInfo: this.currentConnectionInfo,\n };\n }\n\n /**\n * 读取 token 文件,空文件等价于无 token\n */\n private async readConnectionTokenFromFile(): Promise<string | null> {\n const tokenFilePath = this.config.connectionTokenFilePath;\n if (!tokenFilePath) {\n return null;\n }\n try {\n const raw = await readFile(tokenFilePath, 'utf-8');\n const trimmed = raw.trim();\n return trimmed.length > 0 ? trimmed : null;\n } catch {\n return null;\n }\n }\n\n /**\n * 基于基础 connectionString 和 token 生成最终连接串\n * 只修改 options 参数值,在末尾追加 -c miaoda.zti_token=<token>\n */\n private appendTokenToConnectionString(connectionString: string, token: string | null): string {\n if (!token) {\n return connectionString;\n }\n\n const url = new URL(connectionString);\n const currentOptions = url.searchParams.get('options') || '';\n const optionsWithoutToken = currentOptions\n .replace(/(?:^|\\s)-c\\s+miaoda.zti_token=[^\\s]*/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n const tokenOption = `-c miaoda.zti_token=${token}`;\n const nextOptions = [optionsWithoutToken, tokenOption].filter(Boolean).join(' ').trim();\n\n url.searchParams.set('options', nextOptions);\n return url.toString();\n }\n\n private async createConnectedState(resolvedConnectionString: string, token: string | null): Promise<{\n db: PostgresJsDatabase;\n sqlClient: ReturnType<typeof postgres>;\n connectionInfo: DatabaseConnectionInfo;\n resolvedConnectionString: string;\n token: string | null;\n }> {\n const { connectionString, connectionConfig } = parseConnectionInfo(resolvedConnectionString, this.config);\n const baseSql = postgres(connectionString, connectionConfig);\n try {\n const sqlProxy = genPostgresProxy(baseSql, this.observable, this.requestContext);\n const db = drizzle(sqlProxy, { schema: this.config.schema, logger: this.config.logger });\n\n return {\n db,\n sqlClient: baseSql,\n resolvedConnectionString: connectionString,\n token,\n connectionInfo: {\n connectionString,\n connectionConfig: {\n ssl: connectionConfig.ssl,\n connectionTimeout: connectionConfig.connect_timeout,\n idleTimeout: connectionConfig.idle_timeout,\n maxConnections: connectionConfig.max,\n },\n },\n };\n } catch (error) {\n await this.shutdownSqlClientSafely(baseSql, 'Error closing failed new database client:');\n throw error;\n }\n }\n\n private applyConnectedState(nextState: {\n db: PostgresJsDatabase;\n sqlClient: ReturnType<typeof postgres>;\n connectionInfo: DatabaseConnectionInfo;\n resolvedConnectionString: string;\n token: string | null;\n }): void {\n this.db = nextState.db;\n this.sqlClient = nextState.sqlClient;\n this.currentConnectionInfo = nextState.connectionInfo;\n this.currentResolvedConnectionString = nextState.resolvedConnectionString;\n this.currentToken = nextState.token;\n this.isConnected = true;\n }\n\n private reconnectInternal(\n nextResolvedConnectionString?: string,\n nextToken?: string | null,\n ): Promise<PostgresJsDatabase> {\n return this.runWithConnectionLock(() => this.reconnectWithResolvedConnectionString(\n nextResolvedConnectionString,\n nextToken,\n ));\n }\n\n private runWithConnectionLock(operation: () => Promise<PostgresJsDatabase>): Promise<PostgresJsDatabase> {\n if (this.connectionOpPromise) {\n return this.connectionOpPromise;\n }\n\n this.connectionOpPromise = (async () => {\n try {\n return await operation();\n } finally {\n this.connectionOpPromise = null;\n }\n })();\n\n return this.connectionOpPromise;\n }\n\n private async reconnectWithResolvedConnectionString(\n nextResolvedConnectionString?: string,\n nextToken?: string | null,\n ): Promise<PostgresJsDatabase> {\n const resolvedToken = nextToken ?? await this.readConnectionTokenFromFile();\n const resolvedConnectionString =\n nextResolvedConnectionString ??\n this.appendTokenToConnectionString(this.config.connectionString, resolvedToken);\n\n const nextState = await this.createConnectedState(resolvedConnectionString, resolvedToken);\n const previousSqlClient = this.sqlClient;\n\n this.applyConnectedState(nextState);\n if (previousSqlClient && previousSqlClient !== nextState.sqlClient) {\n await this.shutdownSqlClientSafely(previousSqlClient, 'Error shutting down previous database client after reconnect:');\n }\n\n return nextState.db;\n }\n\n private async shutdownSqlClientSafely(\n client: ReturnType<typeof postgres> | null,\n errorPrefix: string,\n ): Promise<void> {\n try {\n if (client && typeof client.end === 'function') {\n await client.end({ timeout: this.sqlClientShutdownTimeoutSeconds });\n }\n } catch (error) {\n console.error(errorPrefix, error);\n }\n }\n\n /**\n * 启动 token 文件监听;若已存在旧监听则先清理后重建。\n * 使用 watchFile 轮询,规避虚拟化/网络文件系统下 fs.watch 不可靠的问题。\n */\n private startTokenWatcher(): void {\n const tokenFilePath = this.config.connectionTokenFilePath;\n if (!tokenFilePath || this.skipInitDbConnection) {\n return;\n }\n this.stopTokenWatcher();\n this.startTokenWatchFileFallback(tokenFilePath);\n }\n\n /**\n * watchFile 兜底监听:即使 fs.watch 丢事件,也能通过轮询感知 token 文件变化。\n */\n private startTokenWatchFileFallback(tokenFilePath: string): void {\n if (this.tokenWatchFilePath === tokenFilePath) {\n return;\n }\n\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n\n watchFile(tokenFilePath, { interval: 600_000 }, () => {\n this.scheduleTokenRefresh();\n });\n this.tokenWatchFilePath = tokenFilePath;\n }\n\n /**\n * 停止 watcher 与防抖定时器,释放监听资源。\n */\n private stopTokenWatcher(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n this.tokenWatchDebounceTimer = null;\n }\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n }\n\n /**\n * 对频繁文件事件做防抖,避免短时间重复重连。\n */\n private scheduleTokenRefresh(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n }\n this.tokenWatchDebounceTimer = setTimeout(() => {\n void this.refreshConnectionIfTokenChanged();\n }, 300);\n }\n\n /**\n * token 变化时刷新连接:仅在 token/连接串发生变化,或当前连接不可用时触发重连。\n */\n private async refreshConnectionIfTokenChanged(): Promise<void> {\n if (this.refreshingByTokenChange) {\n return;\n }\n this.refreshingByTokenChange = true;\n\n try {\n const nextToken = await this.readConnectionTokenFromFile();\n\n const nextResolvedConnectionString = this.appendTokenToConnectionString(\n this.config.connectionString,\n nextToken,\n );\n const sameToken = nextToken === this.currentToken;\n const sameConnectionString = nextResolvedConnectionString === this.currentResolvedConnectionString;\n\n if (sameToken && sameConnectionString && this.isConnected) {\n return;\n }\n\n await this.reconnectInternal(nextResolvedConnectionString, nextToken);\n } catch (error) {\n console.error('Failed to refresh database connection from token file:', error);\n } finally {\n this.refreshingByTokenChange = false;\n }\n }\n}\n","export const DRIZZLE_DATABASE = 'DRIZZLE_DATABASE';\nexport const DATAPAAS_CONFIG = 'DATAPAAS_CONFIG';\nexport const DATAPAAS_LOGGER_SERVICE = Symbol('DATAPAAS_LOGGER_SERVICE');","import { type Logger } from 'drizzle-orm';\nimport { PostgresOptions } from './types';\ninterface ConnectionConfig {\n maxConnections?: number;\n idleTimeout?: number;\n connectionTimeout?: number;\n ssl?: PostgresOptions['ssl'];\n logger?: boolean | Logger;\n}\nexport function parseConnectionInfo(connectionString: string, config?: ConnectionConfig) {\n const { maxConnections, idleTimeout, connectionTimeout, ssl, logger } = config || {};\n // 创建 postgres 连接\n let parsedConfigFromUrl = null;\n try {\n parsedConfigFromUrl = new URL(connectionString);\n const temp = new URL(connectionString);\n temp.searchParams.delete('schema'); // 移除schema参数,避免 postgres 解析错误\n connectionString = temp.toString();\n } catch (error) {\n throw new Error('Failed to parse connection info');\n }\n const { username, password, hostname, port, pathname } = parsedConfigFromUrl;\n const connectionConfig = {\n username: decodeURIComponent(username),\n password: decodeURIComponent(password),\n host: [hostname] as unknown as string,\n port: [parseInt(port) || 5432] as unknown as number,\n pathname: decodeURIComponent(pathname),\n max: maxConnections || 1,\n idle_timeout: idleTimeout || 20,\n connect_timeout: connectionTimeout || 10,\n ssl,\n onnotice: logger ? console.log : undefined,\n }\n return { connectionString, connectionConfig };\n}","import type postgres from 'postgres';\nimport { Logger } from '@nestjs/common';\nimport { Span } from '@lark-apaas/nestjs-observable';\nimport { ObservableService, RequestContextService, createTimer } from '@lark-apaas/nestjs-common';\n\nconst nestLogger = new Logger('Database');\n\n/**\n * 包装 postgres 实例以记录 SQL 和结果\n */\nexport function genPostgresProxy(sql: postgres.Sql, observable: ObservableService, requestContext: RequestContextService): postgres.Sql {\n const wrapQuery = <T extends readonly (postgres.Row | undefined)[]>(query: postgres.PendingQuery<T>): postgres.PendingQuery<T> => {\n if (query && typeof query.then === 'function') {\n return new Proxy(query, {\n get(target, prop, receiver) {\n if (prop === 'then') {\n return async (onfulfilled?: (value: postgres.RowList<T>) => unknown, onrejected?: (reason: unknown) => unknown) => {\n const sqlStr = (query as unknown as Record<string, unknown>).strings instanceof Array ? (query as unknown as { strings: string[] }).strings[0] : '';\n const span = observable.startTrace(sqlStr, requestContext?.getContext()?.requestRootSpan as Span);\n const spanContext = {\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId\n }\n // referer_path 和 api 由 logger 自动从 RequestContext 读取\n const logContext: Record<string, unknown> = {\n source_type: 'platform',\n paas_attributes_module: 'data',\n paas_parent_span_context: spanContext,\n };\n span.setAttribute(\"module\", \"data\");\n span.setAttribute(\"source_type\", \"platform\");\n\n const elapsed = createTimer();\n try {\n const result = await target;\n const isSetRoleSql = result?.command === 'SET';\n\n // rls SET ROLE 不上报日志\n if (!isSetRoleSql) {\n const duration = elapsed();\n\n const parsedParams = JSON.stringify({\n operation: result?.command,\n sql: result?.statement?.string,\n duration_ms: duration,\n result_count: result.length ?? 0,\n error_message: ''\n });\n\n nestLogger.log(parsedParams, { ...logContext, duration_ms: duration });\n span.setAttribute('duration_ms', duration);\n } else {\n observable.dropTrace(span);\n }\n\n span.end();\n return onfulfilled ? onfulfilled(result) : result;\n } catch (error) {\n const duration = elapsed();\n const sqlStr = (query as unknown as Record<string, unknown>).strings instanceof Array ? (query as unknown as { strings: string[] }).strings[0] : '';\n const parsedParams = JSON.stringify({\n operation: '',\n sql: sqlStr,\n duration_ms: duration,\n result_count: 0,\n error_message: error instanceof Error ? error.message : String(error)\n });\n nestLogger.log(parsedParams, { ...logContext, duration_ms: duration });\n span.setAttribute('duration_ms', duration);\n\n span.end();\n return onrejected ? onrejected(error) : Promise.reject(error);\n }\n };\n }\n const value = Reflect.get(target, prop, receiver);\n return typeof value === 'function' ? value.bind(receiver) : value;\n },\n }) as postgres.PendingQuery<T>;\n }\n return query;\n };\n\n return new Proxy(sql, {\n apply(target, thisArg, argArray) {\n const query = Reflect.apply(target, thisArg, argArray);\n return wrapQuery(query);\n },\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (prop === 'begin' && typeof value === 'function') {\n return <T>(arg: string | ((tx: postgres.TransactionSql) => T | Promise<T>), cb?: (tx: postgres.TransactionSql) => T | Promise<T>) => {\n if (typeof arg === 'function') {\n return value.call(target, (tx: postgres.TransactionSql) => arg(genPostgresProxy(tx as unknown as postgres.Sql, observable, requestContext) as unknown as postgres.TransactionSql));\n }\n if (typeof cb === 'function') {\n return value.call(target, arg, (tx: postgres.TransactionSql) => cb(genPostgresProxy(tx as unknown as postgres.Sql, observable, requestContext) as unknown as postgres.TransactionSql));\n }\n return value.call(target, arg);\n };\n }\n if (prop === 'unsafe' && typeof value === 'function') {\n return <T extends readonly (postgres.Row | undefined)[] = postgres.Row[]>(queryStr: string, args?: unknown[], options?: postgres.UnsafeQueryOptions): postgres.PendingQuery<T> => {\n const query = value.call(target, queryStr, args, options) as postgres.PendingQuery<T>;\n return wrapQuery(query);\n };\n }\n return typeof value === 'function' ? value.bind(receiver) : value;\n },\n }) as postgres.Sql;\n}","import {\n Module,\n DynamicModule,\n Provider,\n MiddlewareConsumer,\n InjectionToken,\n ModuleMetadata,\n} from '@nestjs/common';\nimport { DrizzleDatabaseManager } from './drizzle-manager';\nimport { DataPaasDatabaseService } from './database.service';\nimport { SqlExecutionContextMiddleware } from './sql-exection-context.middleware';\nimport { type DataPaasConfig } from '@/types';\nimport { DATAPAAS_CONFIG, DRIZZLE_DATABASE, DATAPAAS_LOGGER_SERVICE } from './const';\nexport { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { RequestContextService, OBSERVABLE_SERVICE, ObservableService } from '@lark-apaas/nestjs-common';\nimport { Observable } from '@lark-apaas/nestjs-observable';\n\nexport interface DataPaasModuleOptions extends DataPaasConfig {}\n\n/**\n * 异步配置选项\n */\nexport interface DataPaasModuleAsyncOptions {\n /**\n * 需要导入的模块(例如 ConfigModule)\n */\n imports?: ModuleMetadata['imports'];\n /**\n * 使用工厂函数创建配置\n */\n useFactory?: (...args: unknown[]) => Promise<DataPaasConfig> | DataPaasConfig;\n /**\n * 工厂函数需要注入的依赖\n */\n inject?: InjectionToken[];\n}\n\nfunction verifyDrizzlePatched(): void {\n try {\n // 直接加载被 patch 的 CJS 模块并检查导出是否存在\n // 注意:此路径与补丁文件一致:drizzle-orm/postgres-js/session.cjs\n // 若未来包结构变化,请同步更新补丁与此校验\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const postgres = require('drizzle-orm/postgres-js');\n const ok =\n postgres &&\n typeof postgres.getAuthContextStore === 'function' &&\n typeof postgres.runWithAuthContext === 'function';\n if (!ok) {\n throw new Error(\n 'drizzle-orm 未检测到补丁导出:getAuthContextStore/runWithAuthContext',\n );\n }\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`drizzle-orm 补丁未生效或版本不匹配:${reason}`);\n }\n}\n\n// 在模块初始化阶段进行一次 drizzle-orm 补丁自检\nverifyDrizzlePatched();\n\n@Module({\n providers: [{\n provide: DATAPAAS_LOGGER_SERVICE,\n useFactory: (observable: ObservableService | null, rtx: RequestContextService) => {\n return observable ?? new Observable(rtx);\n },\n inject: [{\n token: OBSERVABLE_SERVICE,\n optional: true,\n }, RequestContextService],\n }],\n})\nexport class DataPaasModule {\n configure(_consumer: MiddlewareConsumer) {}\n /**\n * 注册数据平台模块(同步方式)\n * @param options 模块配置选项\n * @returns 动态模块\n */\n static forRoot(options: DataPaasConfig): DynamicModule {\n let roleSchema = '';\n let sslModeRequired = false;\n if (options.connectionString) {\n try {\n const url = new URL(options.connectionString);\n\n roleSchema = url.searchParams.get(\n 'schema'\n ) as string;\n sslModeRequired = url.searchParams.get('sslmode') === \"require\";\n } catch (err) {\n roleSchema = '';\n }\n }\n const config: DataPaasConfig = {\n connectionString: options.connectionString,\n schema: options.schema,\n logger: options.logger || false,\n timeout: options.timeout || 10000,\n maxConnections: options.maxConnections || 1,\n idleTimeout: options.idleTimeout || 20,\n connectionTimeout: options.connectionTimeout || 10,\n ssl: options.ssl || (sslModeRequired ? 'require' : false),\n autoContext: options.autoContext == null ? true : !!options.autoContext,\n roleSchema,\n connectionTokenFilePath: options.connectionTokenFilePath,\n };\n\n const providers: Provider[] = [\n // 配置提供者 - 使用 useValue\n {\n provide: DATAPAAS_CONFIG,\n useValue: config,\n },\n // 数据库管理器 - 使用 useClass,依赖注入连接服务和配置服务\n {\n provide: DrizzleDatabaseManager,\n useClass: DrizzleDatabaseManager,\n },\n // 数据库服务 - 使用 useClass,依赖注入管理器\n {\n provide: DataPaasDatabaseService,\n useClass: DataPaasDatabaseService,\n },\n {\n provide: SqlExecutionContextMiddleware,\n useClass: SqlExecutionContextMiddleware,\n },\n // Drizzle 数据库实例 - 使用 useFactory 获取管理器中的数据库实例\n {\n provide: DRIZZLE_DATABASE,\n useFactory: async (manager: DrizzleDatabaseManager) => {\n await manager.initialize();\n return new Proxy({} as Record<string, unknown>, {\n get: (_target, prop, receiver) => {\n const db = manager.getDatabase() as unknown as Record<string, unknown>;\n const value = Reflect.get(db, prop, receiver);\n if (typeof value === 'function') {\n return value.bind(db);\n }\n return value;\n },\n });\n },\n inject: [DrizzleDatabaseManager],\n },\n ];\n\n return {\n module: DataPaasModule,\n providers,\n exports: [\n DrizzleDatabaseManager,\n DataPaasDatabaseService,\n SqlExecutionContextMiddleware,\n DRIZZLE_DATABASE,\n DATAPAAS_CONFIG,\n ],\n global: true,\n };\n }\n\n /**\n * 注册数据平台模块(异步方式)\n * 支持从 ConfigModule 等异步源获取配置\n * @param options 异步配置选项\n * @returns 动态模块\n * @example\n * ```typescript\n * DataPaasModule.forRootAsync({\n * imports: [ConfigModule],\n * useFactory: async (configService: ConfigService) => ({\n * connectionString: configService.get('DATABASE_URL'),\n * }),\n * inject: [ConfigService],\n * })\n * ```\n */\n static forRootAsync(options: DataPaasModuleAsyncOptions): DynamicModule {\n const asyncProviders = this.createAsyncProviders(options);\n\n const providers: Provider[] = [\n ...asyncProviders,\n // 数据库管理器 - 使用 useClass,依赖注入连接服务和配置服务\n {\n provide: DrizzleDatabaseManager,\n useClass: DrizzleDatabaseManager,\n },\n // 数据库服务 - 使用 useClass,依赖注入管理器\n {\n provide: DataPaasDatabaseService,\n useClass: DataPaasDatabaseService,\n },\n {\n provide: SqlExecutionContextMiddleware,\n useClass: SqlExecutionContextMiddleware,\n },\n // Drizzle 数据库实例 - 使用 useFactory 获取管理器中的数据库实例\n {\n provide: DRIZZLE_DATABASE,\n useFactory: async (manager: DrizzleDatabaseManager) => {\n await manager.initialize();\n return new Proxy({} as Record<string, unknown>, {\n get: (_target, prop, receiver) => {\n const db = manager.getDatabase() as unknown as Record<string, unknown>;\n const value = Reflect.get(db, prop, receiver);\n if (typeof value === 'function') {\n return value.bind(db);\n }\n return value;\n },\n });\n },\n inject: [DrizzleDatabaseManager],\n },\n ];\n\n return {\n module: DataPaasModule,\n imports: options.imports || [],\n providers,\n exports: [\n DrizzleDatabaseManager,\n DataPaasDatabaseService,\n SqlExecutionContextMiddleware,\n DRIZZLE_DATABASE,\n DATAPAAS_CONFIG,\n ],\n global: true,\n };\n }\n\n /**\n * 创建异步配置 providers\n */\n private static createAsyncProviders(\n options: DataPaasModuleAsyncOptions\n ): Provider[] {\n if (options.useFactory) {\n return [this.createAsyncConfigProvider(options)];\n }\n\n throw new Error('DataPaasModule.forRootAsync 需要指定 useFactory');\n }\n\n /**\n * 创建异步配置 provider\n */\n private static createAsyncConfigProvider(\n options: DataPaasModuleAsyncOptions\n ): Provider {\n if (options.useFactory) {\n return {\n provide: DATAPAAS_CONFIG,\n useFactory: async (...args: unknown[]) => {\n const config = await options.useFactory!(...args);\n return this.normalizeConfig(config);\n },\n inject: options.inject || [],\n };\n }\n\n throw new Error('Invalid async options');\n }\n\n /**\n * 标准化配置,设置默认值\n */\n private static normalizeConfig(config: DataPaasConfig): DataPaasConfig {\n let roleSchema = '';\n let sslModeRequired = false;\n if (config.connectionString) {\n try {\n const url = new URL(config.connectionString);\n roleSchema = url.searchParams.get(\n 'schema'\n ) as string;\n sslModeRequired = url.searchParams.get('sslmode') === \"require\";\n } catch (err) {\n roleSchema = '';\n }\n }\n return {\n connectionString: config.connectionString,\n schema: config.schema,\n logger: config.logger || false,\n timeout: config.timeout || 10000,\n maxConnections: config.maxConnections || 1,\n idleTimeout: config.idleTimeout || 20,\n connectionTimeout: config.connectionTimeout || 10,\n ssl: config.ssl || (sslModeRequired ? 'require' : false),\n autoContext: config.autoContext == null ? true : !!config.autoContext,\n roleSchema,\n connectionTokenFilePath: config.connectionTokenFilePath,\n };\n }\n}\n","import { Injectable, OnModuleDestroy } from '@nestjs/common';\nimport { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { DrizzleDatabaseManager } from './drizzle-manager';\nimport { DatabaseConnectionInfo } from '../types';\n\n@Injectable()\nexport class DataPaasDatabaseService implements OnModuleDestroy {\n constructor(\n private readonly databaseManager: DrizzleDatabaseManager,\n ) {}\n\n /**\n * 获取 Drizzle 数据库实例\n * @returns Drizzle 数据库实例\n */\n getDatabase(): PostgresJsDatabase {\n return this.databaseManager.getDatabase();\n }\n\n /**\n * 获取数据库管理器\n * @returns 数据库管理器实例\n */\n getDatabaseManager(): DrizzleDatabaseManager {\n return this.databaseManager;\n }\n\n\n /**\n * 测试数据库连接\n * @returns 连接是否正常\n */\n async testConnection(): Promise<boolean> {\n return this.databaseManager.testConnection();\n }\n\n /**\n * 重新连接数据库\n * @returns 新的数据库实例\n */\n async reconnect(): Promise<PostgresJsDatabase> {\n return this.databaseManager.reconnect();\n }\n\n /**\n * 获取当前连接信息\n * @returns 当前连接信息\n */\n getConnectionInfo(): DatabaseConnectionInfo | null {\n return this.databaseManager.getConnectionInfo();\n }\n\n /**\n * 检查连接状态\n * @returns 是否已连接\n */\n isConnected(): boolean {\n return this.databaseManager.isDatabaseConnected();\n }\n\n /**\n * 开始事务\n * @param callback 事务回调函数\n * @returns 事务结果\n */\n async transaction<T>(callback: (tx: PostgresJsDatabase) => Promise<T>): Promise<T> {\n return this.databaseManager.transaction(callback);\n }\n\n /**\n * 获取连接池状态\n * @returns 连接池状态信息\n */\n getConnectionPoolStatus(): { isConnected: boolean; connectionInfo: DatabaseConnectionInfo | null } {\n return this.databaseManager.getConnectionPoolStatus();\n }\n\n\n /**\n * 模块销毁时断开数据库连接\n */\n async onModuleDestroy(): Promise<void> {\n await this.databaseManager.disconnect();\n }\n}\n","import { Injectable, NestMiddleware, Inject } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\nimport * as session from 'drizzle-orm/postgres-js';\nimport { DATAPAAS_CONFIG } from './const';\nimport { type DataPaasConfig } from '../types';\n\ninterface UserContext {\n userId?: string;\n tenantId?: number;\n appId?: string;\n // 用户角色 id 列表\n roles?: string[];\n // 是否为系统账号\n isSystemAccount: boolean;\n // 用户类型(如果有)\n userType?: string;\n}\n@Injectable()\nexport class SqlExecutionContextMiddleware implements NestMiddleware {\n constructor(\n @Inject(DATAPAAS_CONFIG)\n private readonly config: DataPaasConfig\n ) {}\n use(req: Request, _res: Response, next: NextFunction) {\n const roleSchema = this.config.roleSchema ?? '';\n const userContext =\n (req as unknown as { userContext: UserContext }).userContext || {};\n const roles = (userContext.roles || []).join(',');\n const userId = userContext.userId;\n const isSystemAccount = userContext.isSystemAccount;\n const userType = userContext.userType;\n const sqls = [\n // 设置用户 ID, 匿名用户时设置为空字符串\n userId\n ? `SET LOCAL app.user_id = '${userId}'`\n : `SET LOCAL app.user_id = ''`,\n // 系统用户设置为service_role, 登录用户设置为 authenticated_xxx 角色,匿名用户设置为 anon_xxx 角色\n isSystemAccount\n ? `SET LOCAL ROLE 'service_role_${roleSchema}'`\n : userId\n ? `SET LOCAL ROLE 'authenticated_${roleSchema}'`\n : `SET LOCAL ROLE 'anon_${roleSchema}'`,\n // 自定义角色\n roles\n ? `SET LOCAL app.role_ids = '${roles}'`\n : `SET LOCAL app.role_ids = ''`,\n // 用户类型\n userType\n ? `SET LOCAL app.user_type = '${userType}'`\n : `SET LOCAL app.user_type = ''`,\n ]\n .filter(Boolean)\n .join(';');\n // 需要确认角色设置是否对后续 SQL 执行有影响,预期是只影响当前会话\n session.runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n }\n}\n","import { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { sql } from 'drizzle-orm';\n\nimport { DataPaasDatabaseService } from './database.service';\nimport { type DatabaseConnectionInfo } from '../types';\n\n/**\n * 数据库工具函数集合\n */\nexport class DatabaseUtils {\n /**\n * 安全执行数据库操作\n * @param databaseService 数据库服务实例\n * @param operation 数据库操作函数\n * @param fallback 失败时的回退值\n * @returns 操作结果或回退值\n */\n static async safeExecute<T>(\n databaseService: DataPaasDatabaseService,\n operation: (db: PostgresJsDatabase) => Promise<T>,\n fallback?: T\n ): Promise<T | undefined> {\n try {\n if (!databaseService.isConnected()) {\n throw new Error('Database not connected');\n }\n \n const db = databaseService.getDatabase();\n return await operation(db);\n } catch (error) {\n console.error('Database operation failed:', error);\n return fallback;\n }\n }\n\n /**\n * 批量执行数据库操作\n * @param databaseService 数据库服务实例\n * @param operations 操作数组\n * @param batchSize 批处理大小\n * @returns 所有操作的结果\n */\n static async batchExecute<T>(\n databaseService: DataPaasDatabaseService,\n operations: Array<(db: PostgresJsDatabase) => Promise<T>>,\n batchSize: number = 10\n ): Promise<T[]> {\n const results: T[] = [];\n const db = databaseService.getDatabase();\n\n for (let i = 0; i < operations.length; i += batchSize) {\n const batch = operations.slice(i, i + batchSize);\n const batchResults = await Promise.all(\n batch.map(operation => operation(db))\n );\n results.push(...batchResults);\n }\n\n return results;\n }\n\n /**\n * 执行带重试的数据库操作\n * @param databaseService 数据库服务实例\n * @param operation 数据库操作函数\n * @param maxRetries 最大重试次数\n * @param retryDelay 重试延迟(毫秒)\n * @returns 操作结果\n */\n static async executeWithRetry<T>(\n databaseService: DataPaasDatabaseService,\n operation: (db: PostgresJsDatabase) => Promise<T>,\n maxRetries: number = 3,\n retryDelay: number = 1000\n ): Promise<T> {\n let lastError: Error;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const db = databaseService.getDatabase();\n return await operation(db);\n } catch (error) {\n lastError = error as Error;\n \n if (attempt === maxRetries) {\n break;\n }\n\n console.warn(`Database operation failed (attempt ${attempt + 1}/${maxRetries + 1}):`, error);\n \n // 等待后重试\n await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1)));\n }\n }\n\n throw lastError!;\n }\n\n /**\n * 检查表是否存在\n * @param databaseService 数据库服务实例\n * @param tableName 表名\n * @returns 表是否存在\n */\n static async tableExists(\n db: PostgresJsDatabase,\n tableName: string\n ): Promise<boolean> {\n try {\n const result = await db.execute(\n sql`SELECT EXISTS (\n SELECT FROM information_schema.tables \n WHERE table_schema = 'public' \n AND table_name = ${tableName}\n )`\n );\n \n return result[0]?.exists as boolean || false;\n } catch (error) {\n console.error('Failed to check table existence:', error);\n return false;\n }\n }\n\n /**\n * 获取表结构信息\n * @param databaseService 数据库服务实例\n * @param tableName 表名\n * @returns 表结构信息\n */\n static async getTableStructure(\n db: PostgresJsDatabase,\n tableName: string\n ): Promise<Record<string, unknown>[]> {\n try {\n const result = await db.execute(\n sql`SELECT \n column_name,\n data_type,\n is_nullable,\n column_default,\n character_maximum_length\n FROM information_schema.columns \n WHERE table_schema = 'public' \n AND table_name = ${tableName}\n ORDER BY ordinal_position`,\n );\n \n return result;\n } catch (error) {\n console.error('Failed to get table structure:', error);\n return [];\n }\n }\n\n /**\n * 获取数据库版本信息\n * @param databaseService 数据库服务实例\n * @returns 数据库版本\n */\n static async getDatabaseVersion(\n db: PostgresJsDatabase\n ): Promise<string> {\n try {\n const result = await db.execute(sql`SELECT version()`);\n return result[0]?.version as string || 'Unknown';\n } catch (error) {\n console.error('Failed to get database version:', error);\n return 'Unknown';\n }\n }\n\n /**\n * 获取连接统计信息\n * @param databaseService 数据库服务实例\n * @returns 连接统计信息\n */\n static async getConnectionStats(\n db: PostgresJsDatabase\n ): Promise<Record<string, unknown>> {\n try {\n const result = await db.execute(sql`\n SELECT \n count(*) as total_connections,\n count(*) FILTER (WHERE state = 'active') as active_connections,\n count(*) FILTER (WHERE state = 'idle') as idle_connections\n FROM pg_stat_activity \n WHERE datname = current_database()\n `);\n \n return result[0] || {};\n } catch (error) {\n console.error('Failed to get connection stats:', error);\n return {};\n }\n }\n\n /**\n * 健康检查\n * @param databaseService 数据库服务实例\n * @returns 健康状态\n */\n static async healthCheck(\n databaseService: DataPaasDatabaseService\n ): Promise<{\n isHealthy: boolean;\n isConnected: boolean;\n version?: string;\n connectionInfo?: DatabaseConnectionInfo | null;\n error?: string;\n }> {\n try {\n const isConnected = databaseService.isConnected();\n const connectionInfo = databaseService.getConnectionInfo();\n\n const db = databaseService.getDatabase();\n if (!isConnected) {\n return {\n isHealthy: false,\n isConnected: false,\n error: 'Database not connected'\n };\n }\n\n const version = await this.getDatabaseVersion(db);\n \n return {\n isHealthy: true,\n isConnected: true,\n version,\n connectionInfo\n };\n } catch (error) {\n return {\n isHealthy: false,\n isConnected: false,\n error: error instanceof Error ? error.message : 'Unknown error'\n };\n }\n }\n}\n"],"mappings":"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","names":["import_common","DRIZZLE_DATABASE","DATAPAAS_CONFIG","DATAPAAS_LOGGER_SERVICE","Symbol","import_nestjs_common","parseConnectionInfo","connectionString","config","maxConnections","idleTimeout","connectionTimeout","ssl","logger","parsedConfigFromUrl","URL","temp","searchParams","delete","toString","error","Error","username","password","hostname","port","pathname","connectionConfig","decodeURIComponent","host","parseInt","max","idle_timeout","connect_timeout","onnotice","console","log","undefined","nestLogger","Logger","genPostgresProxy","sql","observable","requestContext","wrapQuery","query","then","Proxy","get","target","prop","receiver","onfulfilled","onrejected","sqlStr","strings","Array","span","startTrace","getContext","requestRootSpan","spanContext","traceId","spanId","logContext","source_type","paas_attributes_module","paas_parent_span_context","setAttribute","elapsed","createTimer","result","isSetRoleSql","command","duration","parsedParams","JSON","stringify","operation","statement","string","duration_ms","result_count","length","error_message","log","dropTrace","end","error","Error","message","String","Promise","reject","value","Reflect","bind","apply","thisArg","argArray","arg","cb","call","tx","queryStr","args","options","DrizzleDatabaseManager","db","sqlClient","connectionOpPromise","currentConnectionInfo","isConnected","skipInitDbConnection","currentToken","currentResolvedConnectionString","tokenWatchFilePath","tokenWatchDebounceTimer","refreshingByTokenChange","sqlClientShutdownTimeoutSeconds","config","observable","requestContext","process","env","DEPRECATED_SKIP_INIT_DB_CONNECTION","onModuleInit","initialize","startTokenWatcher","onModuleDestroy","stopTokenWatcher","disconnect","name","runWithConnectionLock","token","readConnectionTokenFromFile","resolvedConnectionString","appendTokenToConnectionString","connectionString","nextState","createConnectedState","applyConnectedState","error","Error","message","getDatabase","testConnection","execute","sql","console","reconnect","reconnectInternal","shouldStopWatcher","pendingOp","Promise","allSettled","shutdownSqlClientSafely","getConnectionInfo","isDatabaseConnected","transaction","callback","getConnectionPoolStatus","connectionInfo","tokenFilePath","connectionTokenFilePath","raw","readFile","trimmed","trim","length","url","URL","currentOptions","searchParams","get","optionsWithoutToken","replace","tokenOption","nextOptions","filter","Boolean","join","set","toString","connectionConfig","parseConnectionInfo","baseSql","postgres","sqlProxy","genPostgresProxy","drizzle","schema","logger","ssl","connectionTimeout","connect_timeout","idleTimeout","idle_timeout","maxConnections","max","nextResolvedConnectionString","nextToken","reconnectWithResolvedConnectionString","operation","resolvedToken","previousSqlClient","client","errorPrefix","end","timeout","startTokenWatchFileFallback","unwatchFile","watchFile","interval","scheduleTokenRefresh","clearTimeout","setTimeout","refreshConnectionIfTokenChanged","sameToken","sameConnectionString","import_common","import_common","DataPaasDatabaseService","databaseManager","getDatabase","getDatabaseManager","testConnection","reconnect","getConnectionInfo","isConnected","isDatabaseConnected","transaction","callback","getConnectionPoolStatus","onModuleDestroy","disconnect","import_common","SqlExecutionContextMiddleware","config","use","req","_res","next","roleSchema","userContext","roles","join","userId","isSystemAccount","userType","sqls","filter","Boolean","session","runWithAuthContext","preSql","import_nestjs_common","verifyDrizzlePatched","postgres","require","ok","getAuthContextStore","runWithAuthContext","Error","err","reason","message","String","DataPaasModule","configure","_consumer","forRoot","options","roleSchema","sslModeRequired","connectionString","url","URL","searchParams","get","config","schema","logger","timeout","maxConnections","idleTimeout","connectionTimeout","ssl","autoContext","connectionTokenFilePath","providers","provide","DATAPAAS_CONFIG","useValue","DrizzleDatabaseManager","useClass","DataPaasDatabaseService","SqlExecutionContextMiddleware","DRIZZLE_DATABASE","useFactory","manager","initialize","Proxy","_target","prop","receiver","db","getDatabase","value","Reflect","bind","inject","module","exports","global","forRootAsync","asyncProviders","createAsyncProviders","imports","createAsyncConfigProvider","args","normalizeConfig","DATAPAAS_LOGGER_SERVICE","observable","rtx","Observable","token","OBSERVABLE_SERVICE","optional","RequestContextService","import_drizzle_orm","DatabaseUtils","safeExecute","databaseService","operation","fallback","isConnected","Error","db","getDatabase","error","console","batchExecute","operations","batchSize","results","i","length","batch","slice","batchResults","Promise","all","map","push","executeWithRetry","maxRetries","retryDelay","lastError","attempt","warn","resolve","setTimeout","tableExists","tableName","result","execute","sql","exists","getTableStructure","getDatabaseVersion","version","getConnectionStats","healthCheck","connectionInfo","getConnectionInfo","isHealthy","message"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/database/drizzle-manager.ts","../src/database/const.ts","../src/utils.ts","../src/database/postgres-proxy.ts","../src/database/database.module.ts","../src/database/database.service.ts","../src/database/sql-exection-context.middleware.ts","../src/database/drizzle-auth-context.ts","../src/database/drizzle-monkey-patch.ts","../src/database/database-utils.ts"],"sourcesContent":["// 导出所有公共API\n\nexport * from './database';\nexport * from './types';\n","import { Injectable, OnModuleInit, OnModuleDestroy, Inject } from '@nestjs/common';\nimport { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { sql } from 'drizzle-orm';\nimport postgres from 'postgres';\nimport { readFile } from 'node:fs/promises';\nimport { watchFile, unwatchFile } from 'node:fs';\nimport { DATAPAAS_CONFIG, DATAPAAS_LOGGER_SERVICE } from './const';\nimport { RequestContextService, ObservableService } from '@lark-apaas/nestjs-common';\n\n\nimport { type DataPaasConfig, type DatabaseConnectionInfo } from '../types';\nimport { parseConnectionInfo } from '../utils';\nimport { genPostgresProxy } from './postgres-proxy';\n\n@Injectable()\nexport class DrizzleDatabaseManager implements OnModuleInit, OnModuleDestroy {\n private db: PostgresJsDatabase | null = null;\n private sqlClient: ReturnType<typeof postgres> | null = null;\n private connectionOpPromise: Promise<PostgresJsDatabase> | null = null;\n private currentConnectionInfo: DatabaseConnectionInfo | null = null;\n private isConnected: boolean = false;\n private skipInitDbConnection: boolean = false;\n private currentToken: string | null = null;\n private currentResolvedConnectionString: string | null = null;\n private tokenWatchFilePath: string | null = null;\n private tokenWatchDebounceTimer: NodeJS.Timeout | null = null;\n private refreshingByTokenChange = false;\n\n private readonly sqlClientShutdownTimeoutSeconds = 5;\n\n /**\n * 构造函数:注入配置、日志能力与请求上下文。\n */\n constructor(\n @Inject(DATAPAAS_CONFIG)\n private readonly config: DataPaasConfig,\n @Inject(DATAPAAS_LOGGER_SERVICE)\n private readonly observable: ObservableService,\n private readonly requestContext: RequestContextService,\n ) {\n this.skipInitDbConnection = !!process.env.DEPRECATED_SKIP_INIT_DB_CONNECTION;\n }\n\n /**\n * 模块初始化:建立首个数据库连接并启动 token 监听。\n */\n async onModuleInit() {\n await this.initialize();\n this.startTokenWatcher();\n }\n\n /**\n * 模块销毁:先停止 token 监听,再断开数据库连接。\n */\n async onModuleDestroy() {\n this.stopTokenWatcher();\n await this.disconnect();\n }\n\n /**\n * 初始化数据库连接\n * @returns Drizzle 数据库实例\n */\n async initialize(): Promise<PostgresJsDatabase> {\n if (this.skipInitDbConnection) {\n this.isConnected = true;\n this.db = { name: 'skipInitDbConnection' } as unknown as PostgresJsDatabase;\n return this.db;\n }\n\n if (this.db && this.isConnected) {\n return this.db;\n }\n\n if (this.connectionOpPromise) {\n return this.connectionOpPromise;\n }\n\n return this.runWithConnectionLock(async () => {\n if (this.db && this.isConnected) {\n return this.db;\n }\n\n try {\n const token = await this.readConnectionTokenFromFile();\n const resolvedConnectionString = this.appendTokenToConnectionString(this.config.connectionString, token);\n const nextState = await this.createConnectedState(resolvedConnectionString, token);\n\n this.applyConnectedState(nextState);\n return nextState.db;\n } catch (error) {\n // 避免并发 initialize 时后失败覆盖先成功状态。\n if (!this.db || !this.isConnected) {\n this.isConnected = false;\n this.db = null;\n }\n throw new Error(`Failed to initialize database connection: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n });\n }\n\n /**\n * 获取数据库实例\n * @returns Drizzle 数据库实例\n */\n getDatabase(): PostgresJsDatabase {\n if (!this.db || !this.isConnected) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n return this.db;\n }\n\n /**\n * 测试数据库连接\n * @returns 连接是否正常\n */\n async testConnection(): Promise<boolean> {\n if (!this.db) {\n return false;\n }\n\n try {\n await this.db.execute(sql`SELECT 1`);\n return true;\n } catch (error) {\n console.error('Database connection test failed:', error);\n this.isConnected = false;\n return false;\n }\n }\n\n /**\n * 重新连接数据库。\n * 注意:重连时不关闭 token watcher,避免重连后丢失后续文件变更监听。\n * 同时通过 promise 锁避免并发重连导致状态抖动。\n * @returns 新的数据库实例\n */\n async reconnect(): Promise<PostgresJsDatabase> {\n return this.reconnectInternal();\n }\n\n /**\n * 断开数据库连接\n */\n async disconnect(shouldStopWatcher: boolean = true): Promise<void> {\n if (shouldStopWatcher) {\n this.stopTokenWatcher();\n }\n\n const pendingOp = this.connectionOpPromise;\n if (pendingOp) {\n await Promise.allSettled([pendingOp]);\n }\n\n await this.shutdownSqlClientSafely(this.sqlClient, 'Error disconnecting from database:');\n this.sqlClient = null;\n this.db = null;\n this.isConnected = false;\n this.currentConnectionInfo = null;\n this.currentResolvedConnectionString = null;\n this.currentToken = null;\n }\n\n /**\n * 获取当前连接信息\n * @returns 当前连接信息\n */\n getConnectionInfo(): DatabaseConnectionInfo | null {\n return this.currentConnectionInfo;\n }\n\n /**\n * 检查连接状态\n * @returns 是否已连接\n */\n isDatabaseConnected(): boolean {\n return this.isConnected;\n }\n\n /**\n * 开始事务\n * @param callback 事务回调函数\n * @returns 事务结果\n */\n async transaction<T>(callback: (tx: PostgresJsDatabase) => Promise<T>): Promise<T> {\n const db = this.getDatabase();\n return db.transaction(callback);\n }\n\n /**\n * 获取连接池状态\n * @returns 连接池状态信息\n */\n getConnectionPoolStatus(): { isConnected: boolean; connectionInfo: DatabaseConnectionInfo | null } {\n return {\n isConnected: this.isConnected,\n connectionInfo: this.currentConnectionInfo,\n };\n }\n\n /**\n * 读取 token 文件,空文件等价于无 token\n */\n private async readConnectionTokenFromFile(): Promise<string | null> {\n const tokenFilePath = this.config.connectionTokenFilePath;\n if (!tokenFilePath) {\n return null;\n }\n try {\n const raw = await readFile(tokenFilePath, 'utf-8');\n const trimmed = raw.trim();\n return trimmed.length > 0 ? trimmed : null;\n } catch {\n return null;\n }\n }\n\n /**\n * 基于基础 connectionString 和 token 生成最终连接串\n * 只修改 options 参数值,在末尾追加 -c miaoda.zti_token=<token>\n */\n private appendTokenToConnectionString(connectionString: string, token: string | null): string {\n if (!token) {\n return connectionString;\n }\n\n const url = new URL(connectionString);\n const currentOptions = url.searchParams.get('options') || '';\n const optionsWithoutToken = currentOptions\n .replace(/(?:^|\\s)-c\\s+miaoda.zti_token=[^\\s]*/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n const tokenOption = `-c miaoda.zti_token=${token}`;\n const nextOptions = [optionsWithoutToken, tokenOption].filter(Boolean).join(' ').trim();\n\n url.searchParams.set('options', nextOptions);\n return url.toString();\n }\n\n private async createConnectedState(resolvedConnectionString: string, token: string | null): Promise<{\n db: PostgresJsDatabase;\n sqlClient: ReturnType<typeof postgres>;\n connectionInfo: DatabaseConnectionInfo;\n resolvedConnectionString: string;\n token: string | null;\n }> {\n const { connectionString, connectionConfig } = parseConnectionInfo(resolvedConnectionString, this.config);\n const baseSql = postgres(connectionString, connectionConfig);\n try {\n const sqlProxy = genPostgresProxy(baseSql, this.observable, this.requestContext);\n const db = drizzle(sqlProxy, { schema: this.config.schema, logger: this.config.logger });\n\n return {\n db,\n sqlClient: baseSql,\n resolvedConnectionString: connectionString,\n token,\n connectionInfo: {\n connectionString,\n connectionConfig: {\n ssl: connectionConfig.ssl,\n connectionTimeout: connectionConfig.connect_timeout,\n idleTimeout: connectionConfig.idle_timeout,\n maxConnections: connectionConfig.max,\n },\n },\n };\n } catch (error) {\n await this.shutdownSqlClientSafely(baseSql, 'Error closing failed new database client:');\n throw error;\n }\n }\n\n private applyConnectedState(nextState: {\n db: PostgresJsDatabase;\n sqlClient: ReturnType<typeof postgres>;\n connectionInfo: DatabaseConnectionInfo;\n resolvedConnectionString: string;\n token: string | null;\n }): void {\n this.db = nextState.db;\n this.sqlClient = nextState.sqlClient;\n this.currentConnectionInfo = nextState.connectionInfo;\n this.currentResolvedConnectionString = nextState.resolvedConnectionString;\n this.currentToken = nextState.token;\n this.isConnected = true;\n }\n\n private reconnectInternal(\n nextResolvedConnectionString?: string,\n nextToken?: string | null,\n ): Promise<PostgresJsDatabase> {\n return this.runWithConnectionLock(() => this.reconnectWithResolvedConnectionString(\n nextResolvedConnectionString,\n nextToken,\n ));\n }\n\n private runWithConnectionLock(operation: () => Promise<PostgresJsDatabase>): Promise<PostgresJsDatabase> {\n if (this.connectionOpPromise) {\n return this.connectionOpPromise;\n }\n\n this.connectionOpPromise = (async () => {\n try {\n return await operation();\n } finally {\n this.connectionOpPromise = null;\n }\n })();\n\n return this.connectionOpPromise;\n }\n\n private async reconnectWithResolvedConnectionString(\n nextResolvedConnectionString?: string,\n nextToken?: string | null,\n ): Promise<PostgresJsDatabase> {\n const resolvedToken = nextToken ?? await this.readConnectionTokenFromFile();\n const resolvedConnectionString =\n nextResolvedConnectionString ??\n this.appendTokenToConnectionString(this.config.connectionString, resolvedToken);\n\n const nextState = await this.createConnectedState(resolvedConnectionString, resolvedToken);\n const previousSqlClient = this.sqlClient;\n\n this.applyConnectedState(nextState);\n if (previousSqlClient && previousSqlClient !== nextState.sqlClient) {\n await this.shutdownSqlClientSafely(previousSqlClient, 'Error shutting down previous database client after reconnect:');\n }\n\n return nextState.db;\n }\n\n private async shutdownSqlClientSafely(\n client: ReturnType<typeof postgres> | null,\n errorPrefix: string,\n ): Promise<void> {\n try {\n if (client && typeof client.end === 'function') {\n await client.end({ timeout: this.sqlClientShutdownTimeoutSeconds });\n }\n } catch (error) {\n console.error(errorPrefix, error);\n }\n }\n\n /**\n * 启动 token 文件监听;若已存在旧监听则先清理后重建。\n * 使用 watchFile 轮询,规避虚拟化/网络文件系统下 fs.watch 不可靠的问题。\n */\n private startTokenWatcher(): void {\n const tokenFilePath = this.config.connectionTokenFilePath;\n if (!tokenFilePath || this.skipInitDbConnection) {\n return;\n }\n this.stopTokenWatcher();\n this.startTokenWatchFileFallback(tokenFilePath);\n }\n\n /**\n * watchFile 兜底监听:即使 fs.watch 丢事件,也能通过轮询感知 token 文件变化。\n */\n private startTokenWatchFileFallback(tokenFilePath: string): void {\n if (this.tokenWatchFilePath === tokenFilePath) {\n return;\n }\n\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n\n watchFile(tokenFilePath, { interval: 600_000 }, () => {\n this.scheduleTokenRefresh();\n });\n this.tokenWatchFilePath = tokenFilePath;\n }\n\n /**\n * 停止 watcher 与防抖定时器,释放监听资源。\n */\n private stopTokenWatcher(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n this.tokenWatchDebounceTimer = null;\n }\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n }\n\n /**\n * 对频繁文件事件做防抖,避免短时间重复重连。\n */\n private scheduleTokenRefresh(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n }\n this.tokenWatchDebounceTimer = setTimeout(() => {\n void this.refreshConnectionIfTokenChanged();\n }, 300);\n }\n\n /**\n * token 变化时刷新连接:仅在 token/连接串发生变化,或当前连接不可用时触发重连。\n */\n private async refreshConnectionIfTokenChanged(): Promise<void> {\n if (this.refreshingByTokenChange) {\n return;\n }\n this.refreshingByTokenChange = true;\n\n try {\n const nextToken = await this.readConnectionTokenFromFile();\n\n const nextResolvedConnectionString = this.appendTokenToConnectionString(\n this.config.connectionString,\n nextToken,\n );\n const sameToken = nextToken === this.currentToken;\n const sameConnectionString = nextResolvedConnectionString === this.currentResolvedConnectionString;\n\n if (sameToken && sameConnectionString && this.isConnected) {\n return;\n }\n\n await this.reconnectInternal(nextResolvedConnectionString, nextToken);\n } catch (error) {\n console.error('Failed to refresh database connection from token file:', error);\n } finally {\n this.refreshingByTokenChange = false;\n }\n }\n}\n","export const DRIZZLE_DATABASE = 'DRIZZLE_DATABASE';\nexport const DATAPAAS_CONFIG = 'DATAPAAS_CONFIG';\nexport const DATAPAAS_LOGGER_SERVICE = Symbol('DATAPAAS_LOGGER_SERVICE');","import { type Logger } from 'drizzle-orm';\nimport { PostgresOptions } from './types';\ninterface ConnectionConfig {\n maxConnections?: number;\n idleTimeout?: number;\n connectionTimeout?: number;\n ssl?: PostgresOptions['ssl'];\n logger?: boolean | Logger;\n}\nexport function parseConnectionInfo(connectionString: string, config?: ConnectionConfig) {\n const { maxConnections, idleTimeout, connectionTimeout, ssl, logger } = config || {};\n // 创建 postgres 连接\n let parsedConfigFromUrl = null;\n try {\n parsedConfigFromUrl = new URL(connectionString);\n const temp = new URL(connectionString);\n temp.searchParams.delete('schema'); // 移除schema参数,避免 postgres 解析错误\n connectionString = temp.toString();\n } catch (error) {\n throw new Error('Failed to parse connection info');\n }\n const { username, password, hostname, port, pathname } = parsedConfigFromUrl;\n const connectionConfig = {\n username: decodeURIComponent(username),\n password: decodeURIComponent(password),\n host: [hostname] as unknown as string,\n port: [parseInt(port) || 5432] as unknown as number,\n pathname: decodeURIComponent(pathname),\n max: maxConnections || 1,\n idle_timeout: idleTimeout || 20,\n connect_timeout: connectionTimeout || 10,\n ssl,\n onnotice: logger ? console.log : undefined,\n }\n return { connectionString, connectionConfig };\n}","import type postgres from 'postgres';\nimport { Logger } from '@nestjs/common';\nimport { Span } from '@lark-apaas/nestjs-observable';\nimport { ObservableService, RequestContextService, createTimer } from '@lark-apaas/nestjs-common';\n\nconst nestLogger = new Logger('Database');\n\n/**\n * 包装 postgres 实例以记录 SQL 和结果\n */\nexport function genPostgresProxy(sql: postgres.Sql, observable: ObservableService, requestContext: RequestContextService): postgres.Sql {\n const wrapQuery = <T extends readonly (postgres.Row | undefined)[]>(query: postgres.PendingQuery<T>): postgres.PendingQuery<T> => {\n if (query && typeof query.then === 'function') {\n return new Proxy(query, {\n get(target, prop, receiver) {\n if (prop === 'then') {\n return async (onfulfilled?: (value: postgres.RowList<T>) => unknown, onrejected?: (reason: unknown) => unknown) => {\n const sqlStr = (query as unknown as Record<string, unknown>).strings instanceof Array ? (query as unknown as { strings: string[] }).strings[0] : '';\n const span = observable.startTrace(sqlStr, requestContext?.getContext()?.requestRootSpan as Span);\n const spanContext = {\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId\n }\n // referer_path 和 api 由 logger 自动从 RequestContext 读取\n const logContext: Record<string, unknown> = {\n source_type: 'platform',\n paas_attributes_module: 'data',\n paas_parent_span_context: spanContext,\n };\n span.setAttribute(\"module\", \"data\");\n span.setAttribute(\"source_type\", \"platform\");\n\n const elapsed = createTimer();\n try {\n const result = await target;\n const isSetRoleSql = result?.command === 'SET';\n\n // rls SET ROLE 不上报日志\n if (!isSetRoleSql) {\n const duration = elapsed();\n\n const parsedParams = JSON.stringify({\n operation: result?.command,\n sql: result?.statement?.string,\n duration_ms: duration,\n result_count: result.length ?? 0,\n error_message: ''\n });\n\n nestLogger.log(parsedParams, { ...logContext, duration_ms: duration });\n span.setAttribute('duration_ms', duration);\n } else {\n observable.dropTrace(span);\n }\n\n span.end();\n return onfulfilled ? onfulfilled(result) : result;\n } catch (error) {\n const duration = elapsed();\n const sqlStr = (query as unknown as Record<string, unknown>).strings instanceof Array ? (query as unknown as { strings: string[] }).strings[0] : '';\n const parsedParams = JSON.stringify({\n operation: '',\n sql: sqlStr,\n duration_ms: duration,\n result_count: 0,\n error_message: error instanceof Error ? error.message : String(error)\n });\n nestLogger.log(parsedParams, { ...logContext, duration_ms: duration });\n span.setAttribute('duration_ms', duration);\n\n span.end();\n return onrejected ? onrejected(error) : Promise.reject(error);\n }\n };\n }\n const value = Reflect.get(target, prop, receiver);\n return typeof value === 'function' ? value.bind(receiver) : value;\n },\n }) as postgres.PendingQuery<T>;\n }\n return query;\n };\n\n return new Proxy(sql, {\n apply(target, thisArg, argArray) {\n const query = Reflect.apply(target, thisArg, argArray);\n return wrapQuery(query);\n },\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (prop === 'begin' && typeof value === 'function') {\n return <T>(arg: string | ((tx: postgres.TransactionSql) => T | Promise<T>), cb?: (tx: postgres.TransactionSql) => T | Promise<T>) => {\n if (typeof arg === 'function') {\n return value.call(target, (tx: postgres.TransactionSql) => arg(genPostgresProxy(tx as unknown as postgres.Sql, observable, requestContext) as unknown as postgres.TransactionSql));\n }\n if (typeof cb === 'function') {\n return value.call(target, arg, (tx: postgres.TransactionSql) => cb(genPostgresProxy(tx as unknown as postgres.Sql, observable, requestContext) as unknown as postgres.TransactionSql));\n }\n return value.call(target, arg);\n };\n }\n if (prop === 'unsafe' && typeof value === 'function') {\n return <T extends readonly (postgres.Row | undefined)[] = postgres.Row[]>(queryStr: string, args?: unknown[], options?: postgres.UnsafeQueryOptions): postgres.PendingQuery<T> => {\n const query = value.call(target, queryStr, args, options) as postgres.PendingQuery<T>;\n return wrapQuery(query);\n };\n }\n return typeof value === 'function' ? value.bind(receiver) : value;\n },\n }) as postgres.Sql;\n}","import {\n Module,\n DynamicModule,\n Provider,\n MiddlewareConsumer,\n InjectionToken,\n ModuleMetadata,\n} from '@nestjs/common';\nimport { DrizzleDatabaseManager } from './drizzle-manager';\nimport { DataPaasDatabaseService } from './database.service';\nimport { SqlExecutionContextMiddleware } from './sql-exection-context.middleware';\nimport { type DataPaasConfig } from '@/types';\nimport { DATAPAAS_CONFIG, DRIZZLE_DATABASE, DATAPAAS_LOGGER_SERVICE } from './const';\nexport { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { RequestContextService, OBSERVABLE_SERVICE, ObservableService } from '@lark-apaas/nestjs-common';\nimport { Observable } from '@lark-apaas/nestjs-observable';\nimport { applyDrizzleMonkeyPatch } from './drizzle-monkey-patch';\n\nexport interface DataPaasModuleOptions extends DataPaasConfig {}\n\n/**\n * 异步配置选项\n */\nexport interface DataPaasModuleAsyncOptions {\n /**\n * 需要导入的模块(例如 ConfigModule)\n */\n imports?: ModuleMetadata['imports'];\n /**\n * 使用工厂函数创建配置\n */\n useFactory?: (...args: unknown[]) => Promise<DataPaasConfig> | DataPaasConfig;\n /**\n * 工厂函数需要注入的依赖\n */\n inject?: InjectionToken[];\n}\n\n// 在模块加载阶段应用运行时 monkey-patch,替代 postinstall + patch-package\napplyDrizzleMonkeyPatch();\n\n@Module({\n providers: [{\n provide: DATAPAAS_LOGGER_SERVICE,\n useFactory: (observable: ObservableService | null, rtx: RequestContextService) => {\n return observable ?? new Observable(rtx);\n },\n inject: [{\n token: OBSERVABLE_SERVICE,\n optional: true,\n }, RequestContextService],\n }],\n})\nexport class DataPaasModule {\n configure(_consumer: MiddlewareConsumer) {}\n /**\n * 注册数据平台模块(同步方式)\n * @param options 模块配置选项\n * @returns 动态模块\n */\n static forRoot(options: DataPaasConfig): DynamicModule {\n let roleSchema = '';\n let sslModeRequired = false;\n if (options.connectionString) {\n try {\n const url = new URL(options.connectionString);\n\n roleSchema = url.searchParams.get(\n 'schema'\n ) as string;\n sslModeRequired = url.searchParams.get('sslmode') === \"require\";\n } catch (err) {\n roleSchema = '';\n }\n }\n const config: DataPaasConfig = {\n connectionString: options.connectionString,\n schema: options.schema,\n logger: options.logger || false,\n timeout: options.timeout || 10000,\n maxConnections: options.maxConnections || 1,\n idleTimeout: options.idleTimeout || 20,\n connectionTimeout: options.connectionTimeout || 10,\n ssl: options.ssl || (sslModeRequired ? 'require' : false),\n autoContext: options.autoContext == null ? true : !!options.autoContext,\n roleSchema,\n connectionTokenFilePath: options.connectionTokenFilePath,\n };\n\n const providers: Provider[] = [\n // 配置提供者 - 使用 useValue\n {\n provide: DATAPAAS_CONFIG,\n useValue: config,\n },\n // 数据库管理器 - 使用 useClass,依赖注入连接服务和配置服务\n {\n provide: DrizzleDatabaseManager,\n useClass: DrizzleDatabaseManager,\n },\n // 数据库服务 - 使用 useClass,依赖注入管理器\n {\n provide: DataPaasDatabaseService,\n useClass: DataPaasDatabaseService,\n },\n {\n provide: SqlExecutionContextMiddleware,\n useClass: SqlExecutionContextMiddleware,\n },\n // Drizzle 数据库实例 - 使用 useFactory 获取管理器中的数据库实例\n {\n provide: DRIZZLE_DATABASE,\n useFactory: async (manager: DrizzleDatabaseManager) => {\n await manager.initialize();\n return new Proxy({} as Record<string, unknown>, {\n get: (_target, prop, receiver) => {\n const db = manager.getDatabase() as unknown as Record<string, unknown>;\n const value = Reflect.get(db, prop, receiver);\n if (typeof value === 'function') {\n return value.bind(db);\n }\n return value;\n },\n });\n },\n inject: [DrizzleDatabaseManager],\n },\n ];\n\n return {\n module: DataPaasModule,\n providers,\n exports: [\n DrizzleDatabaseManager,\n DataPaasDatabaseService,\n SqlExecutionContextMiddleware,\n DRIZZLE_DATABASE,\n DATAPAAS_CONFIG,\n ],\n global: true,\n };\n }\n\n /**\n * 注册数据平台模块(异步方式)\n * 支持从 ConfigModule 等异步源获取配置\n * @param options 异步配置选项\n * @returns 动态模块\n * @example\n * ```typescript\n * DataPaasModule.forRootAsync({\n * imports: [ConfigModule],\n * useFactory: async (configService: ConfigService) => ({\n * connectionString: configService.get('DATABASE_URL'),\n * }),\n * inject: [ConfigService],\n * })\n * ```\n */\n static forRootAsync(options: DataPaasModuleAsyncOptions): DynamicModule {\n const asyncProviders = this.createAsyncProviders(options);\n\n const providers: Provider[] = [\n ...asyncProviders,\n // 数据库管理器 - 使用 useClass,依赖注入连接服务和配置服务\n {\n provide: DrizzleDatabaseManager,\n useClass: DrizzleDatabaseManager,\n },\n // 数据库服务 - 使用 useClass,依赖注入管理器\n {\n provide: DataPaasDatabaseService,\n useClass: DataPaasDatabaseService,\n },\n {\n provide: SqlExecutionContextMiddleware,\n useClass: SqlExecutionContextMiddleware,\n },\n // Drizzle 数据库实例 - 使用 useFactory 获取管理器中的数据库实例\n {\n provide: DRIZZLE_DATABASE,\n useFactory: async (manager: DrizzleDatabaseManager) => {\n await manager.initialize();\n return new Proxy({} as Record<string, unknown>, {\n get: (_target, prop, receiver) => {\n const db = manager.getDatabase() as unknown as Record<string, unknown>;\n const value = Reflect.get(db, prop, receiver);\n if (typeof value === 'function') {\n return value.bind(db);\n }\n return value;\n },\n });\n },\n inject: [DrizzleDatabaseManager],\n },\n ];\n\n return {\n module: DataPaasModule,\n imports: options.imports || [],\n providers,\n exports: [\n DrizzleDatabaseManager,\n DataPaasDatabaseService,\n SqlExecutionContextMiddleware,\n DRIZZLE_DATABASE,\n DATAPAAS_CONFIG,\n ],\n global: true,\n };\n }\n\n /**\n * 创建异步配置 providers\n */\n private static createAsyncProviders(\n options: DataPaasModuleAsyncOptions\n ): Provider[] {\n if (options.useFactory) {\n return [this.createAsyncConfigProvider(options)];\n }\n\n throw new Error('DataPaasModule.forRootAsync 需要指定 useFactory');\n }\n\n /**\n * 创建异步配置 provider\n */\n private static createAsyncConfigProvider(\n options: DataPaasModuleAsyncOptions\n ): Provider {\n if (options.useFactory) {\n return {\n provide: DATAPAAS_CONFIG,\n useFactory: async (...args: unknown[]) => {\n const config = await options.useFactory!(...args);\n return this.normalizeConfig(config);\n },\n inject: options.inject || [],\n };\n }\n\n throw new Error('Invalid async options');\n }\n\n /**\n * 标准化配置,设置默认值\n */\n private static normalizeConfig(config: DataPaasConfig): DataPaasConfig {\n let roleSchema = '';\n let sslModeRequired = false;\n if (config.connectionString) {\n try {\n const url = new URL(config.connectionString);\n roleSchema = url.searchParams.get(\n 'schema'\n ) as string;\n sslModeRequired = url.searchParams.get('sslmode') === \"require\";\n } catch (err) {\n roleSchema = '';\n }\n }\n return {\n connectionString: config.connectionString,\n schema: config.schema,\n logger: config.logger || false,\n timeout: config.timeout || 10000,\n maxConnections: config.maxConnections || 1,\n idleTimeout: config.idleTimeout || 20,\n connectionTimeout: config.connectionTimeout || 10,\n ssl: config.ssl || (sslModeRequired ? 'require' : false),\n autoContext: config.autoContext == null ? true : !!config.autoContext,\n roleSchema,\n connectionTokenFilePath: config.connectionTokenFilePath,\n };\n }\n}\n","import { Injectable, OnModuleDestroy } from '@nestjs/common';\nimport { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { DrizzleDatabaseManager } from './drizzle-manager';\nimport { DatabaseConnectionInfo } from '../types';\n\n@Injectable()\nexport class DataPaasDatabaseService implements OnModuleDestroy {\n constructor(\n private readonly databaseManager: DrizzleDatabaseManager,\n ) {}\n\n /**\n * 获取 Drizzle 数据库实例\n * @returns Drizzle 数据库实例\n */\n getDatabase(): PostgresJsDatabase {\n return this.databaseManager.getDatabase();\n }\n\n /**\n * 获取数据库管理器\n * @returns 数据库管理器实例\n */\n getDatabaseManager(): DrizzleDatabaseManager {\n return this.databaseManager;\n }\n\n\n /**\n * 测试数据库连接\n * @returns 连接是否正常\n */\n async testConnection(): Promise<boolean> {\n return this.databaseManager.testConnection();\n }\n\n /**\n * 重新连接数据库\n * @returns 新的数据库实例\n */\n async reconnect(): Promise<PostgresJsDatabase> {\n return this.databaseManager.reconnect();\n }\n\n /**\n * 获取当前连接信息\n * @returns 当前连接信息\n */\n getConnectionInfo(): DatabaseConnectionInfo | null {\n return this.databaseManager.getConnectionInfo();\n }\n\n /**\n * 检查连接状态\n * @returns 是否已连接\n */\n isConnected(): boolean {\n return this.databaseManager.isDatabaseConnected();\n }\n\n /**\n * 开始事务\n * @param callback 事务回调函数\n * @returns 事务结果\n */\n async transaction<T>(callback: (tx: PostgresJsDatabase) => Promise<T>): Promise<T> {\n return this.databaseManager.transaction(callback);\n }\n\n /**\n * 获取连接池状态\n * @returns 连接池状态信息\n */\n getConnectionPoolStatus(): { isConnected: boolean; connectionInfo: DatabaseConnectionInfo | null } {\n return this.databaseManager.getConnectionPoolStatus();\n }\n\n\n /**\n * 模块销毁时断开数据库连接\n */\n async onModuleDestroy(): Promise<void> {\n await this.databaseManager.disconnect();\n }\n}\n","import { Injectable, NestMiddleware, Inject } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\nimport { runWithAuthContext } from './drizzle-auth-context';\nimport { DATAPAAS_CONFIG } from './const';\nimport { type DataPaasConfig } from '../types';\n\ninterface UserContext {\n userId?: string;\n tenantId?: number;\n appId?: string;\n // 用户角色 id 列表\n roles?: string[];\n // 是否为系统账号\n isSystemAccount: boolean;\n // 用户类型(如果有)\n userType?: string;\n}\n@Injectable()\nexport class SqlExecutionContextMiddleware implements NestMiddleware {\n constructor(\n @Inject(DATAPAAS_CONFIG)\n private readonly config: DataPaasConfig\n ) {}\n use(req: Request, _res: Response, next: NextFunction) {\n const roleSchema = this.config.roleSchema ?? '';\n const userContext =\n (req as unknown as { userContext: UserContext }).userContext || {};\n const roles = (userContext.roles || []).join(',');\n const userId = userContext.userId;\n const isSystemAccount = userContext.isSystemAccount;\n const userType = userContext.userType;\n const sqls = [\n // 设置用户 ID, 匿名用户时设置为空字符串\n userId\n ? `SET LOCAL app.user_id = '${userId}'`\n : `SET LOCAL app.user_id = ''`,\n // 系统用户设置为service_role, 登录用户设置为 authenticated_xxx 角色,匿名用户设置为 anon_xxx 角色\n isSystemAccount\n ? `SET LOCAL ROLE 'service_role_${roleSchema}'`\n : userId\n ? `SET LOCAL ROLE 'authenticated_${roleSchema}'`\n : `SET LOCAL ROLE 'anon_${roleSchema}'`,\n // 自定义角色\n roles\n ? `SET LOCAL app.role_ids = '${roles}'`\n : `SET LOCAL app.role_ids = ''`,\n // 用户类型\n userType\n ? `SET LOCAL app.user_type = '${userType}'`\n : `SET LOCAL app.user_type = ''`,\n ]\n .filter(Boolean)\n .join(';');\n // 需要确认角色设置是否对后续 SQL 执行有影响,预期是只影响当前会话\n runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n }\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\nexport interface AuthContext {\n preSql?: string;\n postSql?: string;\n}\n\nconst asyncLocalStorage = new AsyncLocalStorage<AuthContext>();\n\n/**\n * 获取当前请求的 RLS 上下文(preSql/postSql)\n */\nexport function getAuthContextStore(): AuthContext | undefined {\n return asyncLocalStorage.getStore();\n}\n\n/**\n * 在 RLS 上下文中执行回调,使得回调内的所有 drizzle 查询自动包裹 preSql/postSql\n */\nexport function runWithAuthContext<T>(store: AuthContext, fn: () => T): T {\n return asyncLocalStorage.run(store, fn);\n}\n","/**\n * 运行时 monkey-patch:拦截 drizzle-orm PostgresJsPreparedQuery.prototype.execute,\n * 在 AsyncLocalStorage 携带 preSql/postSql 时自动包裹事务。\n *\n * 替代 postinstall + patch-package 方案,兼容 pnpm content-addressable store。\n * 模式参考 Datadog/OpenTelemetry 对数据库驱动的运行时拦截。\n */\n\nimport { getAuthContextStore } from './drizzle-auth-context';\n\nlet patched = false;\n\nexport function applyDrizzleMonkeyPatch(): void {\n if (patched) return;\n\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const pgModule = require('drizzle-orm/postgres-js');\n const PreparedQuery = pgModule.PostgresJsPreparedQuery;\n\n if (!PreparedQuery?.prototype?.execute) {\n throw new Error(\n 'drizzle-orm PostgresJsPreparedQuery.prototype.execute 不存在,版本可能不兼容',\n );\n }\n\n const originalExecute = PreparedQuery.prototype.execute;\n\n // 注意:旧 patch 在 constructor 时从 AsyncLocalStorage 捕获 preSql/postSql,\n // 新方案在 execute 时读取。对于 RLS 场景这更正确——应使用实际执行查询\n // 的请求上下文,而非 PreparedQuery 创建时的上下文。\n // drizzle 每次查询创建新的 PreparedQuery 实例,不存在跨请求复用的情况。\n // 以下包裹逻辑在 test/drizzle-monkey-patch.spec.ts 中有对应副本用于单测,\n // 修改此处逻辑时需同步更新测试中的 applyPatch 函数。\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n PreparedQuery.prototype.execute = async function (this: any, ...args: any[]) {\n const authContext = getAuthContextStore();\n const preSql = authContext?.preSql;\n const postSql = authContext?.postSql;\n\n // 无 RLS 上下文时走原始路径,零开销\n if (!preSql && !postSql) {\n return originalExecute.apply(this, args);\n }\n\n const client = this.client;\n\n // client.begin 存在 → 当前不在事务中,需要开启事务\n if (typeof client.begin === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return client.begin(async (tx: any) => {\n // 临时将 this.client 替换为事务客户端,\n // 原始 execute 内部通过 `const { client } = this` 解构获取,\n // 所以替换后原始逻辑会自动使用事务客户端执行主查询\n const savedClient = this.client;\n this.client = tx;\n try {\n if (preSql) await tx.unsafe(preSql);\n const result = await originalExecute.apply(this, args);\n if (postSql) await tx.unsafe(postSql);\n return result;\n } finally {\n this.client = savedClient;\n }\n });\n }\n\n // 已在事务中(client 没有 begin),直接顺序执行\n if (preSql) await client.unsafe(preSql);\n const result = await originalExecute.apply(this, args);\n if (postSql) await client.unsafe(postSql);\n return result;\n };\n\n patched = true;\n}\n\nexport function isDrizzlePatched(): boolean {\n return patched;\n}\n","import { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { sql } from 'drizzle-orm';\n\nimport { DataPaasDatabaseService } from './database.service';\nimport { type DatabaseConnectionInfo } from '../types';\n\n/**\n * 数据库工具函数集合\n */\nexport class DatabaseUtils {\n /**\n * 安全执行数据库操作\n * @param databaseService 数据库服务实例\n * @param operation 数据库操作函数\n * @param fallback 失败时的回退值\n * @returns 操作结果或回退值\n */\n static async safeExecute<T>(\n databaseService: DataPaasDatabaseService,\n operation: (db: PostgresJsDatabase) => Promise<T>,\n fallback?: T\n ): Promise<T | undefined> {\n try {\n if (!databaseService.isConnected()) {\n throw new Error('Database not connected');\n }\n \n const db = databaseService.getDatabase();\n return await operation(db);\n } catch (error) {\n console.error('Database operation failed:', error);\n return fallback;\n }\n }\n\n /**\n * 批量执行数据库操作\n * @param databaseService 数据库服务实例\n * @param operations 操作数组\n * @param batchSize 批处理大小\n * @returns 所有操作的结果\n */\n static async batchExecute<T>(\n databaseService: DataPaasDatabaseService,\n operations: Array<(db: PostgresJsDatabase) => Promise<T>>,\n batchSize: number = 10\n ): Promise<T[]> {\n const results: T[] = [];\n const db = databaseService.getDatabase();\n\n for (let i = 0; i < operations.length; i += batchSize) {\n const batch = operations.slice(i, i + batchSize);\n const batchResults = await Promise.all(\n batch.map(operation => operation(db))\n );\n results.push(...batchResults);\n }\n\n return results;\n }\n\n /**\n * 执行带重试的数据库操作\n * @param databaseService 数据库服务实例\n * @param operation 数据库操作函数\n * @param maxRetries 最大重试次数\n * @param retryDelay 重试延迟(毫秒)\n * @returns 操作结果\n */\n static async executeWithRetry<T>(\n databaseService: DataPaasDatabaseService,\n operation: (db: PostgresJsDatabase) => Promise<T>,\n maxRetries: number = 3,\n retryDelay: number = 1000\n ): Promise<T> {\n let lastError: Error;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const db = databaseService.getDatabase();\n return await operation(db);\n } catch (error) {\n lastError = error as Error;\n \n if (attempt === maxRetries) {\n break;\n }\n\n console.warn(`Database operation failed (attempt ${attempt + 1}/${maxRetries + 1}):`, error);\n \n // 等待后重试\n await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1)));\n }\n }\n\n throw lastError!;\n }\n\n /**\n * 检查表是否存在\n * @param databaseService 数据库服务实例\n * @param tableName 表名\n * @returns 表是否存在\n */\n static async tableExists(\n db: PostgresJsDatabase,\n tableName: string\n ): Promise<boolean> {\n try {\n const result = await db.execute(\n sql`SELECT EXISTS (\n SELECT FROM information_schema.tables \n WHERE table_schema = 'public' \n AND table_name = ${tableName}\n )`\n );\n \n return result[0]?.exists as boolean || false;\n } catch (error) {\n console.error('Failed to check table existence:', error);\n return false;\n }\n }\n\n /**\n * 获取表结构信息\n * @param databaseService 数据库服务实例\n * @param tableName 表名\n * @returns 表结构信息\n */\n static async getTableStructure(\n db: PostgresJsDatabase,\n tableName: string\n ): Promise<Record<string, unknown>[]> {\n try {\n const result = await db.execute(\n sql`SELECT \n column_name,\n data_type,\n is_nullable,\n column_default,\n character_maximum_length\n FROM information_schema.columns \n WHERE table_schema = 'public' \n AND table_name = ${tableName}\n ORDER BY ordinal_position`,\n );\n \n return result;\n } catch (error) {\n console.error('Failed to get table structure:', error);\n return [];\n }\n }\n\n /**\n * 获取数据库版本信息\n * @param databaseService 数据库服务实例\n * @returns 数据库版本\n */\n static async getDatabaseVersion(\n db: PostgresJsDatabase\n ): Promise<string> {\n try {\n const result = await db.execute(sql`SELECT version()`);\n return result[0]?.version as string || 'Unknown';\n } catch (error) {\n console.error('Failed to get database version:', error);\n return 'Unknown';\n }\n }\n\n /**\n * 获取连接统计信息\n * @param databaseService 数据库服务实例\n * @returns 连接统计信息\n */\n static async getConnectionStats(\n db: PostgresJsDatabase\n ): Promise<Record<string, unknown>> {\n try {\n const result = await db.execute(sql`\n SELECT \n count(*) as total_connections,\n count(*) FILTER (WHERE state = 'active') as active_connections,\n count(*) FILTER (WHERE state = 'idle') as idle_connections\n FROM pg_stat_activity \n WHERE datname = current_database()\n `);\n \n return result[0] || {};\n } catch (error) {\n console.error('Failed to get connection stats:', error);\n return {};\n }\n }\n\n /**\n * 健康检查\n * @param databaseService 数据库服务实例\n * @returns 健康状态\n */\n static async healthCheck(\n databaseService: DataPaasDatabaseService\n ): Promise<{\n isHealthy: boolean;\n isConnected: boolean;\n version?: string;\n connectionInfo?: DatabaseConnectionInfo | null;\n error?: string;\n }> {\n try {\n const isConnected = databaseService.isConnected();\n const connectionInfo = databaseService.getConnectionInfo();\n\n const db = databaseService.getDatabase();\n if (!isConnected) {\n return {\n isHealthy: false,\n isConnected: false,\n error: 'Database not connected'\n };\n }\n\n const version = await this.getDatabaseVersion(db);\n \n return {\n isHealthy: true,\n isConnected: true,\n version,\n connectionInfo\n };\n } catch (error) {\n return {\n isHealthy: false,\n isConnected: false,\n error: error instanceof Error ? error.message : 'Unknown error'\n };\n }\n }\n}\n"],"mappings":"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","names":["import_common","DRIZZLE_DATABASE","DATAPAAS_CONFIG","DATAPAAS_LOGGER_SERVICE","Symbol","import_nestjs_common","parseConnectionInfo","connectionString","config","maxConnections","idleTimeout","connectionTimeout","ssl","logger","parsedConfigFromUrl","URL","temp","searchParams","delete","toString","error","Error","username","password","hostname","port","pathname","connectionConfig","decodeURIComponent","host","parseInt","max","idle_timeout","connect_timeout","onnotice","console","log","undefined","nestLogger","Logger","genPostgresProxy","sql","observable","requestContext","wrapQuery","query","then","Proxy","get","target","prop","receiver","onfulfilled","onrejected","sqlStr","strings","Array","span","startTrace","getContext","requestRootSpan","spanContext","traceId","spanId","logContext","source_type","paas_attributes_module","paas_parent_span_context","setAttribute","elapsed","createTimer","result","isSetRoleSql","command","duration","parsedParams","JSON","stringify","operation","statement","string","duration_ms","result_count","length","error_message","log","dropTrace","end","error","Error","message","String","Promise","reject","value","Reflect","bind","apply","thisArg","argArray","arg","cb","call","tx","queryStr","args","options","DrizzleDatabaseManager","db","sqlClient","connectionOpPromise","currentConnectionInfo","isConnected","skipInitDbConnection","currentToken","currentResolvedConnectionString","tokenWatchFilePath","tokenWatchDebounceTimer","refreshingByTokenChange","sqlClientShutdownTimeoutSeconds","config","observable","requestContext","process","env","DEPRECATED_SKIP_INIT_DB_CONNECTION","onModuleInit","initialize","startTokenWatcher","onModuleDestroy","stopTokenWatcher","disconnect","name","runWithConnectionLock","token","readConnectionTokenFromFile","resolvedConnectionString","appendTokenToConnectionString","connectionString","nextState","createConnectedState","applyConnectedState","error","Error","message","getDatabase","testConnection","execute","sql","console","reconnect","reconnectInternal","shouldStopWatcher","pendingOp","Promise","allSettled","shutdownSqlClientSafely","getConnectionInfo","isDatabaseConnected","transaction","callback","getConnectionPoolStatus","connectionInfo","tokenFilePath","connectionTokenFilePath","raw","readFile","trimmed","trim","length","url","URL","currentOptions","searchParams","get","optionsWithoutToken","replace","tokenOption","nextOptions","filter","Boolean","join","set","toString","connectionConfig","parseConnectionInfo","baseSql","postgres","sqlProxy","genPostgresProxy","drizzle","schema","logger","ssl","connectionTimeout","connect_timeout","idleTimeout","idle_timeout","maxConnections","max","nextResolvedConnectionString","nextToken","reconnectWithResolvedConnectionString","operation","resolvedToken","previousSqlClient","client","errorPrefix","end","timeout","startTokenWatchFileFallback","unwatchFile","watchFile","interval","scheduleTokenRefresh","clearTimeout","setTimeout","refreshConnectionIfTokenChanged","sameToken","sameConnectionString","import_common","import_common","DataPaasDatabaseService","databaseManager","getDatabase","getDatabaseManager","testConnection","reconnect","getConnectionInfo","isConnected","isDatabaseConnected","transaction","callback","getConnectionPoolStatus","onModuleDestroy","disconnect","import_common","asyncLocalStorage","AsyncLocalStorage","getAuthContextStore","getStore","runWithAuthContext","store","fn","run","SqlExecutionContextMiddleware","config","use","req","_res","next","roleSchema","userContext","roles","join","userId","isSystemAccount","userType","sqls","filter","Boolean","runWithAuthContext","preSql","import_nestjs_common","patched","applyDrizzleMonkeyPatch","pgModule","require","PreparedQuery","PostgresJsPreparedQuery","prototype","execute","Error","originalExecute","args","authContext","getAuthContextStore","preSql","postSql","apply","client","begin","tx","savedClient","unsafe","result","applyDrizzleMonkeyPatch","DataPaasModule","configure","_consumer","forRoot","options","roleSchema","sslModeRequired","connectionString","url","URL","searchParams","get","err","config","schema","logger","timeout","maxConnections","idleTimeout","connectionTimeout","ssl","autoContext","connectionTokenFilePath","providers","provide","DATAPAAS_CONFIG","useValue","DrizzleDatabaseManager","useClass","DataPaasDatabaseService","SqlExecutionContextMiddleware","DRIZZLE_DATABASE","useFactory","manager","initialize","Proxy","_target","prop","receiver","db","getDatabase","value","Reflect","bind","inject","module","exports","global","forRootAsync","asyncProviders","createAsyncProviders","imports","createAsyncConfigProvider","Error","args","normalizeConfig","DATAPAAS_LOGGER_SERVICE","observable","rtx","Observable","token","OBSERVABLE_SERVICE","optional","RequestContextService","import_drizzle_orm","DatabaseUtils","safeExecute","databaseService","operation","fallback","isConnected","Error","db","getDatabase","error","console","batchExecute","operations","batchSize","results","i","length","batch","slice","batchResults","Promise","all","map","push","executeWithRetry","maxRetries","retryDelay","lastError","attempt","warn","resolve","setTimeout","tableExists","tableName","result","execute","sql","exists","getTableStructure","getDatabaseVersion","version","getConnectionStats","healthCheck","connectionInfo","getConnectionInfo","isHealthy","message"]}
package/dist/index.js CHANGED
@@ -648,7 +648,20 @@ DataPaasDatabaseService = _ts_decorate2([
648
648
 
649
649
  // src/database/sql-exection-context.middleware.ts
650
650
  import { Injectable as Injectable3, Inject as Inject2 } from "@nestjs/common";
651
- import * as session from "drizzle-orm/postgres-js";
651
+
652
+ // src/database/drizzle-auth-context.ts
653
+ import { AsyncLocalStorage } from "async_hooks";
654
+ var asyncLocalStorage = new AsyncLocalStorage();
655
+ function getAuthContextStore() {
656
+ return asyncLocalStorage.getStore();
657
+ }
658
+ __name(getAuthContextStore, "getAuthContextStore");
659
+ function runWithAuthContext(store, fn) {
660
+ return asyncLocalStorage.run(store, fn);
661
+ }
662
+ __name(runWithAuthContext, "runWithAuthContext");
663
+
664
+ // src/database/sql-exection-context.middleware.ts
652
665
  function _ts_decorate3(decorators, target, key, desc) {
653
666
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
654
667
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -691,7 +704,7 @@ var SqlExecutionContextMiddleware = class {
691
704
  // 用户类型
692
705
  userType ? `SET LOCAL app.user_type = '${userType}'` : `SET LOCAL app.user_type = ''`
693
706
  ].filter(Boolean).join(";");
694
- session.runWithAuthContext({
707
+ runWithAuthContext({
695
708
  preSql: sqls
696
709
  }, () => {
697
710
  next();
@@ -710,6 +723,49 @@ SqlExecutionContextMiddleware = _ts_decorate3([
710
723
  // src/database/database.module.ts
711
724
  import { RequestContextService as RequestContextService2, OBSERVABLE_SERVICE } from "@lark-apaas/nestjs-common";
712
725
  import { Observable } from "@lark-apaas/nestjs-observable";
726
+
727
+ // src/database/drizzle-monkey-patch.ts
728
+ var patched = false;
729
+ function applyDrizzleMonkeyPatch() {
730
+ if (patched) return;
731
+ const pgModule = __require("drizzle-orm/postgres-js");
732
+ const PreparedQuery = pgModule.PostgresJsPreparedQuery;
733
+ if (!PreparedQuery?.prototype?.execute) {
734
+ throw new Error("drizzle-orm PostgresJsPreparedQuery.prototype.execute \u4E0D\u5B58\u5728\uFF0C\u7248\u672C\u53EF\u80FD\u4E0D\u517C\u5BB9");
735
+ }
736
+ const originalExecute = PreparedQuery.prototype.execute;
737
+ PreparedQuery.prototype.execute = async function(...args) {
738
+ const authContext = getAuthContextStore();
739
+ const preSql = authContext?.preSql;
740
+ const postSql = authContext?.postSql;
741
+ if (!preSql && !postSql) {
742
+ return originalExecute.apply(this, args);
743
+ }
744
+ const client = this.client;
745
+ if (typeof client.begin === "function") {
746
+ return client.begin(async (tx) => {
747
+ const savedClient = this.client;
748
+ this.client = tx;
749
+ try {
750
+ if (preSql) await tx.unsafe(preSql);
751
+ const result2 = await originalExecute.apply(this, args);
752
+ if (postSql) await tx.unsafe(postSql);
753
+ return result2;
754
+ } finally {
755
+ this.client = savedClient;
756
+ }
757
+ });
758
+ }
759
+ if (preSql) await client.unsafe(preSql);
760
+ const result = await originalExecute.apply(this, args);
761
+ if (postSql) await client.unsafe(postSql);
762
+ return result;
763
+ };
764
+ patched = true;
765
+ }
766
+ __name(applyDrizzleMonkeyPatch, "applyDrizzleMonkeyPatch");
767
+
768
+ // src/database/database.module.ts
713
769
  function _ts_decorate4(decorators, target, key, desc) {
714
770
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
715
771
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -717,20 +773,7 @@ function _ts_decorate4(decorators, target, key, desc) {
717
773
  return c > 3 && r && Object.defineProperty(target, key, r), r;
718
774
  }
719
775
  __name(_ts_decorate4, "_ts_decorate");
720
- function verifyDrizzlePatched() {
721
- try {
722
- const postgres2 = __require("drizzle-orm/postgres-js");
723
- const ok = postgres2 && typeof postgres2.getAuthContextStore === "function" && typeof postgres2.runWithAuthContext === "function";
724
- if (!ok) {
725
- throw new Error("drizzle-orm \u672A\u68C0\u6D4B\u5230\u8865\u4E01\u5BFC\u51FA\uFF1AgetAuthContextStore/runWithAuthContext");
726
- }
727
- } catch (err) {
728
- const reason = err instanceof Error ? err.message : String(err);
729
- throw new Error(`drizzle-orm \u8865\u4E01\u672A\u751F\u6548\u6216\u7248\u672C\u4E0D\u5339\u914D\uFF1A${reason}`);
730
- }
731
- }
732
- __name(verifyDrizzlePatched, "verifyDrizzlePatched");
733
- verifyDrizzlePatched();
776
+ applyDrizzleMonkeyPatch();
734
777
  var DataPaasModule = class _DataPaasModule {
735
778
  static {
736
779
  __name(this, "DataPaasModule");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/database/drizzle-manager.ts","../src/database/const.ts","../src/utils.ts","../src/database/postgres-proxy.ts","../src/database/database.module.ts","../src/database/database.service.ts","../src/database/sql-exection-context.middleware.ts","../src/database/database-utils.ts"],"sourcesContent":["import { Injectable, OnModuleInit, OnModuleDestroy, Inject } from '@nestjs/common';\nimport { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { sql } from 'drizzle-orm';\nimport postgres from 'postgres';\nimport { readFile } from 'node:fs/promises';\nimport { watchFile, unwatchFile } from 'node:fs';\nimport { DATAPAAS_CONFIG, DATAPAAS_LOGGER_SERVICE } from './const';\nimport { RequestContextService, ObservableService } from '@lark-apaas/nestjs-common';\n\n\nimport { type DataPaasConfig, type DatabaseConnectionInfo } from '../types';\nimport { parseConnectionInfo } from '../utils';\nimport { genPostgresProxy } from './postgres-proxy';\n\n@Injectable()\nexport class DrizzleDatabaseManager implements OnModuleInit, OnModuleDestroy {\n private db: PostgresJsDatabase | null = null;\n private sqlClient: ReturnType<typeof postgres> | null = null;\n private connectionOpPromise: Promise<PostgresJsDatabase> | null = null;\n private currentConnectionInfo: DatabaseConnectionInfo | null = null;\n private isConnected: boolean = false;\n private skipInitDbConnection: boolean = false;\n private currentToken: string | null = null;\n private currentResolvedConnectionString: string | null = null;\n private tokenWatchFilePath: string | null = null;\n private tokenWatchDebounceTimer: NodeJS.Timeout | null = null;\n private refreshingByTokenChange = false;\n\n private readonly sqlClientShutdownTimeoutSeconds = 5;\n\n /**\n * 构造函数:注入配置、日志能力与请求上下文。\n */\n constructor(\n @Inject(DATAPAAS_CONFIG)\n private readonly config: DataPaasConfig,\n @Inject(DATAPAAS_LOGGER_SERVICE)\n private readonly observable: ObservableService,\n private readonly requestContext: RequestContextService,\n ) {\n this.skipInitDbConnection = !!process.env.DEPRECATED_SKIP_INIT_DB_CONNECTION;\n }\n\n /**\n * 模块初始化:建立首个数据库连接并启动 token 监听。\n */\n async onModuleInit() {\n await this.initialize();\n this.startTokenWatcher();\n }\n\n /**\n * 模块销毁:先停止 token 监听,再断开数据库连接。\n */\n async onModuleDestroy() {\n this.stopTokenWatcher();\n await this.disconnect();\n }\n\n /**\n * 初始化数据库连接\n * @returns Drizzle 数据库实例\n */\n async initialize(): Promise<PostgresJsDatabase> {\n if (this.skipInitDbConnection) {\n this.isConnected = true;\n this.db = { name: 'skipInitDbConnection' } as unknown as PostgresJsDatabase;\n return this.db;\n }\n\n if (this.db && this.isConnected) {\n return this.db;\n }\n\n if (this.connectionOpPromise) {\n return this.connectionOpPromise;\n }\n\n return this.runWithConnectionLock(async () => {\n if (this.db && this.isConnected) {\n return this.db;\n }\n\n try {\n const token = await this.readConnectionTokenFromFile();\n const resolvedConnectionString = this.appendTokenToConnectionString(this.config.connectionString, token);\n const nextState = await this.createConnectedState(resolvedConnectionString, token);\n\n this.applyConnectedState(nextState);\n return nextState.db;\n } catch (error) {\n // 避免并发 initialize 时后失败覆盖先成功状态。\n if (!this.db || !this.isConnected) {\n this.isConnected = false;\n this.db = null;\n }\n throw new Error(`Failed to initialize database connection: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n });\n }\n\n /**\n * 获取数据库实例\n * @returns Drizzle 数据库实例\n */\n getDatabase(): PostgresJsDatabase {\n if (!this.db || !this.isConnected) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n return this.db;\n }\n\n /**\n * 测试数据库连接\n * @returns 连接是否正常\n */\n async testConnection(): Promise<boolean> {\n if (!this.db) {\n return false;\n }\n\n try {\n await this.db.execute(sql`SELECT 1`);\n return true;\n } catch (error) {\n console.error('Database connection test failed:', error);\n this.isConnected = false;\n return false;\n }\n }\n\n /**\n * 重新连接数据库。\n * 注意:重连时不关闭 token watcher,避免重连后丢失后续文件变更监听。\n * 同时通过 promise 锁避免并发重连导致状态抖动。\n * @returns 新的数据库实例\n */\n async reconnect(): Promise<PostgresJsDatabase> {\n return this.reconnectInternal();\n }\n\n /**\n * 断开数据库连接\n */\n async disconnect(shouldStopWatcher: boolean = true): Promise<void> {\n if (shouldStopWatcher) {\n this.stopTokenWatcher();\n }\n\n const pendingOp = this.connectionOpPromise;\n if (pendingOp) {\n await Promise.allSettled([pendingOp]);\n }\n\n await this.shutdownSqlClientSafely(this.sqlClient, 'Error disconnecting from database:');\n this.sqlClient = null;\n this.db = null;\n this.isConnected = false;\n this.currentConnectionInfo = null;\n this.currentResolvedConnectionString = null;\n this.currentToken = null;\n }\n\n /**\n * 获取当前连接信息\n * @returns 当前连接信息\n */\n getConnectionInfo(): DatabaseConnectionInfo | null {\n return this.currentConnectionInfo;\n }\n\n /**\n * 检查连接状态\n * @returns 是否已连接\n */\n isDatabaseConnected(): boolean {\n return this.isConnected;\n }\n\n /**\n * 开始事务\n * @param callback 事务回调函数\n * @returns 事务结果\n */\n async transaction<T>(callback: (tx: PostgresJsDatabase) => Promise<T>): Promise<T> {\n const db = this.getDatabase();\n return db.transaction(callback);\n }\n\n /**\n * 获取连接池状态\n * @returns 连接池状态信息\n */\n getConnectionPoolStatus(): { isConnected: boolean; connectionInfo: DatabaseConnectionInfo | null } {\n return {\n isConnected: this.isConnected,\n connectionInfo: this.currentConnectionInfo,\n };\n }\n\n /**\n * 读取 token 文件,空文件等价于无 token\n */\n private async readConnectionTokenFromFile(): Promise<string | null> {\n const tokenFilePath = this.config.connectionTokenFilePath;\n if (!tokenFilePath) {\n return null;\n }\n try {\n const raw = await readFile(tokenFilePath, 'utf-8');\n const trimmed = raw.trim();\n return trimmed.length > 0 ? trimmed : null;\n } catch {\n return null;\n }\n }\n\n /**\n * 基于基础 connectionString 和 token 生成最终连接串\n * 只修改 options 参数值,在末尾追加 -c miaoda.zti_token=<token>\n */\n private appendTokenToConnectionString(connectionString: string, token: string | null): string {\n if (!token) {\n return connectionString;\n }\n\n const url = new URL(connectionString);\n const currentOptions = url.searchParams.get('options') || '';\n const optionsWithoutToken = currentOptions\n .replace(/(?:^|\\s)-c\\s+miaoda.zti_token=[^\\s]*/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n const tokenOption = `-c miaoda.zti_token=${token}`;\n const nextOptions = [optionsWithoutToken, tokenOption].filter(Boolean).join(' ').trim();\n\n url.searchParams.set('options', nextOptions);\n return url.toString();\n }\n\n private async createConnectedState(resolvedConnectionString: string, token: string | null): Promise<{\n db: PostgresJsDatabase;\n sqlClient: ReturnType<typeof postgres>;\n connectionInfo: DatabaseConnectionInfo;\n resolvedConnectionString: string;\n token: string | null;\n }> {\n const { connectionString, connectionConfig } = parseConnectionInfo(resolvedConnectionString, this.config);\n const baseSql = postgres(connectionString, connectionConfig);\n try {\n const sqlProxy = genPostgresProxy(baseSql, this.observable, this.requestContext);\n const db = drizzle(sqlProxy, { schema: this.config.schema, logger: this.config.logger });\n\n return {\n db,\n sqlClient: baseSql,\n resolvedConnectionString: connectionString,\n token,\n connectionInfo: {\n connectionString,\n connectionConfig: {\n ssl: connectionConfig.ssl,\n connectionTimeout: connectionConfig.connect_timeout,\n idleTimeout: connectionConfig.idle_timeout,\n maxConnections: connectionConfig.max,\n },\n },\n };\n } catch (error) {\n await this.shutdownSqlClientSafely(baseSql, 'Error closing failed new database client:');\n throw error;\n }\n }\n\n private applyConnectedState(nextState: {\n db: PostgresJsDatabase;\n sqlClient: ReturnType<typeof postgres>;\n connectionInfo: DatabaseConnectionInfo;\n resolvedConnectionString: string;\n token: string | null;\n }): void {\n this.db = nextState.db;\n this.sqlClient = nextState.sqlClient;\n this.currentConnectionInfo = nextState.connectionInfo;\n this.currentResolvedConnectionString = nextState.resolvedConnectionString;\n this.currentToken = nextState.token;\n this.isConnected = true;\n }\n\n private reconnectInternal(\n nextResolvedConnectionString?: string,\n nextToken?: string | null,\n ): Promise<PostgresJsDatabase> {\n return this.runWithConnectionLock(() => this.reconnectWithResolvedConnectionString(\n nextResolvedConnectionString,\n nextToken,\n ));\n }\n\n private runWithConnectionLock(operation: () => Promise<PostgresJsDatabase>): Promise<PostgresJsDatabase> {\n if (this.connectionOpPromise) {\n return this.connectionOpPromise;\n }\n\n this.connectionOpPromise = (async () => {\n try {\n return await operation();\n } finally {\n this.connectionOpPromise = null;\n }\n })();\n\n return this.connectionOpPromise;\n }\n\n private async reconnectWithResolvedConnectionString(\n nextResolvedConnectionString?: string,\n nextToken?: string | null,\n ): Promise<PostgresJsDatabase> {\n const resolvedToken = nextToken ?? await this.readConnectionTokenFromFile();\n const resolvedConnectionString =\n nextResolvedConnectionString ??\n this.appendTokenToConnectionString(this.config.connectionString, resolvedToken);\n\n const nextState = await this.createConnectedState(resolvedConnectionString, resolvedToken);\n const previousSqlClient = this.sqlClient;\n\n this.applyConnectedState(nextState);\n if (previousSqlClient && previousSqlClient !== nextState.sqlClient) {\n await this.shutdownSqlClientSafely(previousSqlClient, 'Error shutting down previous database client after reconnect:');\n }\n\n return nextState.db;\n }\n\n private async shutdownSqlClientSafely(\n client: ReturnType<typeof postgres> | null,\n errorPrefix: string,\n ): Promise<void> {\n try {\n if (client && typeof client.end === 'function') {\n await client.end({ timeout: this.sqlClientShutdownTimeoutSeconds });\n }\n } catch (error) {\n console.error(errorPrefix, error);\n }\n }\n\n /**\n * 启动 token 文件监听;若已存在旧监听则先清理后重建。\n * 使用 watchFile 轮询,规避虚拟化/网络文件系统下 fs.watch 不可靠的问题。\n */\n private startTokenWatcher(): void {\n const tokenFilePath = this.config.connectionTokenFilePath;\n if (!tokenFilePath || this.skipInitDbConnection) {\n return;\n }\n this.stopTokenWatcher();\n this.startTokenWatchFileFallback(tokenFilePath);\n }\n\n /**\n * watchFile 兜底监听:即使 fs.watch 丢事件,也能通过轮询感知 token 文件变化。\n */\n private startTokenWatchFileFallback(tokenFilePath: string): void {\n if (this.tokenWatchFilePath === tokenFilePath) {\n return;\n }\n\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n\n watchFile(tokenFilePath, { interval: 600_000 }, () => {\n this.scheduleTokenRefresh();\n });\n this.tokenWatchFilePath = tokenFilePath;\n }\n\n /**\n * 停止 watcher 与防抖定时器,释放监听资源。\n */\n private stopTokenWatcher(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n this.tokenWatchDebounceTimer = null;\n }\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n }\n\n /**\n * 对频繁文件事件做防抖,避免短时间重复重连。\n */\n private scheduleTokenRefresh(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n }\n this.tokenWatchDebounceTimer = setTimeout(() => {\n void this.refreshConnectionIfTokenChanged();\n }, 300);\n }\n\n /**\n * token 变化时刷新连接:仅在 token/连接串发生变化,或当前连接不可用时触发重连。\n */\n private async refreshConnectionIfTokenChanged(): Promise<void> {\n if (this.refreshingByTokenChange) {\n return;\n }\n this.refreshingByTokenChange = true;\n\n try {\n const nextToken = await this.readConnectionTokenFromFile();\n\n const nextResolvedConnectionString = this.appendTokenToConnectionString(\n this.config.connectionString,\n nextToken,\n );\n const sameToken = nextToken === this.currentToken;\n const sameConnectionString = nextResolvedConnectionString === this.currentResolvedConnectionString;\n\n if (sameToken && sameConnectionString && this.isConnected) {\n return;\n }\n\n await this.reconnectInternal(nextResolvedConnectionString, nextToken);\n } catch (error) {\n console.error('Failed to refresh database connection from token file:', error);\n } finally {\n this.refreshingByTokenChange = false;\n }\n }\n}\n","export const DRIZZLE_DATABASE = 'DRIZZLE_DATABASE';\nexport const DATAPAAS_CONFIG = 'DATAPAAS_CONFIG';\nexport const DATAPAAS_LOGGER_SERVICE = Symbol('DATAPAAS_LOGGER_SERVICE');","import { type Logger } from 'drizzle-orm';\nimport { PostgresOptions } from './types';\ninterface ConnectionConfig {\n maxConnections?: number;\n idleTimeout?: number;\n connectionTimeout?: number;\n ssl?: PostgresOptions['ssl'];\n logger?: boolean | Logger;\n}\nexport function parseConnectionInfo(connectionString: string, config?: ConnectionConfig) {\n const { maxConnections, idleTimeout, connectionTimeout, ssl, logger } = config || {};\n // 创建 postgres 连接\n let parsedConfigFromUrl = null;\n try {\n parsedConfigFromUrl = new URL(connectionString);\n const temp = new URL(connectionString);\n temp.searchParams.delete('schema'); // 移除schema参数,避免 postgres 解析错误\n connectionString = temp.toString();\n } catch (error) {\n throw new Error('Failed to parse connection info');\n }\n const { username, password, hostname, port, pathname } = parsedConfigFromUrl;\n const connectionConfig = {\n username: decodeURIComponent(username),\n password: decodeURIComponent(password),\n host: [hostname] as unknown as string,\n port: [parseInt(port) || 5432] as unknown as number,\n pathname: decodeURIComponent(pathname),\n max: maxConnections || 1,\n idle_timeout: idleTimeout || 20,\n connect_timeout: connectionTimeout || 10,\n ssl,\n onnotice: logger ? console.log : undefined,\n }\n return { connectionString, connectionConfig };\n}","import type postgres from 'postgres';\nimport { Logger } from '@nestjs/common';\nimport { Span } from '@lark-apaas/nestjs-observable';\nimport { ObservableService, RequestContextService, createTimer } from '@lark-apaas/nestjs-common';\n\nconst nestLogger = new Logger('Database');\n\n/**\n * 包装 postgres 实例以记录 SQL 和结果\n */\nexport function genPostgresProxy(sql: postgres.Sql, observable: ObservableService, requestContext: RequestContextService): postgres.Sql {\n const wrapQuery = <T extends readonly (postgres.Row | undefined)[]>(query: postgres.PendingQuery<T>): postgres.PendingQuery<T> => {\n if (query && typeof query.then === 'function') {\n return new Proxy(query, {\n get(target, prop, receiver) {\n if (prop === 'then') {\n return async (onfulfilled?: (value: postgres.RowList<T>) => unknown, onrejected?: (reason: unknown) => unknown) => {\n const sqlStr = (query as unknown as Record<string, unknown>).strings instanceof Array ? (query as unknown as { strings: string[] }).strings[0] : '';\n const span = observable.startTrace(sqlStr, requestContext?.getContext()?.requestRootSpan as Span);\n const spanContext = {\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId\n }\n // referer_path 和 api 由 logger 自动从 RequestContext 读取\n const logContext: Record<string, unknown> = {\n source_type: 'platform',\n paas_attributes_module: 'data',\n paas_parent_span_context: spanContext,\n };\n span.setAttribute(\"module\", \"data\");\n span.setAttribute(\"source_type\", \"platform\");\n\n const elapsed = createTimer();\n try {\n const result = await target;\n const isSetRoleSql = result?.command === 'SET';\n\n // rls SET ROLE 不上报日志\n if (!isSetRoleSql) {\n const duration = elapsed();\n\n const parsedParams = JSON.stringify({\n operation: result?.command,\n sql: result?.statement?.string,\n duration_ms: duration,\n result_count: result.length ?? 0,\n error_message: ''\n });\n\n nestLogger.log(parsedParams, { ...logContext, duration_ms: duration });\n span.setAttribute('duration_ms', duration);\n } else {\n observable.dropTrace(span);\n }\n\n span.end();\n return onfulfilled ? onfulfilled(result) : result;\n } catch (error) {\n const duration = elapsed();\n const sqlStr = (query as unknown as Record<string, unknown>).strings instanceof Array ? (query as unknown as { strings: string[] }).strings[0] : '';\n const parsedParams = JSON.stringify({\n operation: '',\n sql: sqlStr,\n duration_ms: duration,\n result_count: 0,\n error_message: error instanceof Error ? error.message : String(error)\n });\n nestLogger.log(parsedParams, { ...logContext, duration_ms: duration });\n span.setAttribute('duration_ms', duration);\n\n span.end();\n return onrejected ? onrejected(error) : Promise.reject(error);\n }\n };\n }\n const value = Reflect.get(target, prop, receiver);\n return typeof value === 'function' ? value.bind(receiver) : value;\n },\n }) as postgres.PendingQuery<T>;\n }\n return query;\n };\n\n return new Proxy(sql, {\n apply(target, thisArg, argArray) {\n const query = Reflect.apply(target, thisArg, argArray);\n return wrapQuery(query);\n },\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (prop === 'begin' && typeof value === 'function') {\n return <T>(arg: string | ((tx: postgres.TransactionSql) => T | Promise<T>), cb?: (tx: postgres.TransactionSql) => T | Promise<T>) => {\n if (typeof arg === 'function') {\n return value.call(target, (tx: postgres.TransactionSql) => arg(genPostgresProxy(tx as unknown as postgres.Sql, observable, requestContext) as unknown as postgres.TransactionSql));\n }\n if (typeof cb === 'function') {\n return value.call(target, arg, (tx: postgres.TransactionSql) => cb(genPostgresProxy(tx as unknown as postgres.Sql, observable, requestContext) as unknown as postgres.TransactionSql));\n }\n return value.call(target, arg);\n };\n }\n if (prop === 'unsafe' && typeof value === 'function') {\n return <T extends readonly (postgres.Row | undefined)[] = postgres.Row[]>(queryStr: string, args?: unknown[], options?: postgres.UnsafeQueryOptions): postgres.PendingQuery<T> => {\n const query = value.call(target, queryStr, args, options) as postgres.PendingQuery<T>;\n return wrapQuery(query);\n };\n }\n return typeof value === 'function' ? value.bind(receiver) : value;\n },\n }) as postgres.Sql;\n}","import {\n Module,\n DynamicModule,\n Provider,\n MiddlewareConsumer,\n InjectionToken,\n ModuleMetadata,\n} from '@nestjs/common';\nimport { DrizzleDatabaseManager } from './drizzle-manager';\nimport { DataPaasDatabaseService } from './database.service';\nimport { SqlExecutionContextMiddleware } from './sql-exection-context.middleware';\nimport { type DataPaasConfig } from '@/types';\nimport { DATAPAAS_CONFIG, DRIZZLE_DATABASE, DATAPAAS_LOGGER_SERVICE } from './const';\nexport { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { RequestContextService, OBSERVABLE_SERVICE, ObservableService } from '@lark-apaas/nestjs-common';\nimport { Observable } from '@lark-apaas/nestjs-observable';\n\nexport interface DataPaasModuleOptions extends DataPaasConfig {}\n\n/**\n * 异步配置选项\n */\nexport interface DataPaasModuleAsyncOptions {\n /**\n * 需要导入的模块(例如 ConfigModule)\n */\n imports?: ModuleMetadata['imports'];\n /**\n * 使用工厂函数创建配置\n */\n useFactory?: (...args: unknown[]) => Promise<DataPaasConfig> | DataPaasConfig;\n /**\n * 工厂函数需要注入的依赖\n */\n inject?: InjectionToken[];\n}\n\nfunction verifyDrizzlePatched(): void {\n try {\n // 直接加载被 patch 的 CJS 模块并检查导出是否存在\n // 注意:此路径与补丁文件一致:drizzle-orm/postgres-js/session.cjs\n // 若未来包结构变化,请同步更新补丁与此校验\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const postgres = require('drizzle-orm/postgres-js');\n const ok =\n postgres &&\n typeof postgres.getAuthContextStore === 'function' &&\n typeof postgres.runWithAuthContext === 'function';\n if (!ok) {\n throw new Error(\n 'drizzle-orm 未检测到补丁导出:getAuthContextStore/runWithAuthContext',\n );\n }\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`drizzle-orm 补丁未生效或版本不匹配:${reason}`);\n }\n}\n\n// 在模块初始化阶段进行一次 drizzle-orm 补丁自检\nverifyDrizzlePatched();\n\n@Module({\n providers: [{\n provide: DATAPAAS_LOGGER_SERVICE,\n useFactory: (observable: ObservableService | null, rtx: RequestContextService) => {\n return observable ?? new Observable(rtx);\n },\n inject: [{\n token: OBSERVABLE_SERVICE,\n optional: true,\n }, RequestContextService],\n }],\n})\nexport class DataPaasModule {\n configure(_consumer: MiddlewareConsumer) {}\n /**\n * 注册数据平台模块(同步方式)\n * @param options 模块配置选项\n * @returns 动态模块\n */\n static forRoot(options: DataPaasConfig): DynamicModule {\n let roleSchema = '';\n let sslModeRequired = false;\n if (options.connectionString) {\n try {\n const url = new URL(options.connectionString);\n\n roleSchema = url.searchParams.get(\n 'schema'\n ) as string;\n sslModeRequired = url.searchParams.get('sslmode') === \"require\";\n } catch (err) {\n roleSchema = '';\n }\n }\n const config: DataPaasConfig = {\n connectionString: options.connectionString,\n schema: options.schema,\n logger: options.logger || false,\n timeout: options.timeout || 10000,\n maxConnections: options.maxConnections || 1,\n idleTimeout: options.idleTimeout || 20,\n connectionTimeout: options.connectionTimeout || 10,\n ssl: options.ssl || (sslModeRequired ? 'require' : false),\n autoContext: options.autoContext == null ? true : !!options.autoContext,\n roleSchema,\n connectionTokenFilePath: options.connectionTokenFilePath,\n };\n\n const providers: Provider[] = [\n // 配置提供者 - 使用 useValue\n {\n provide: DATAPAAS_CONFIG,\n useValue: config,\n },\n // 数据库管理器 - 使用 useClass,依赖注入连接服务和配置服务\n {\n provide: DrizzleDatabaseManager,\n useClass: DrizzleDatabaseManager,\n },\n // 数据库服务 - 使用 useClass,依赖注入管理器\n {\n provide: DataPaasDatabaseService,\n useClass: DataPaasDatabaseService,\n },\n {\n provide: SqlExecutionContextMiddleware,\n useClass: SqlExecutionContextMiddleware,\n },\n // Drizzle 数据库实例 - 使用 useFactory 获取管理器中的数据库实例\n {\n provide: DRIZZLE_DATABASE,\n useFactory: async (manager: DrizzleDatabaseManager) => {\n await manager.initialize();\n return new Proxy({} as Record<string, unknown>, {\n get: (_target, prop, receiver) => {\n const db = manager.getDatabase() as unknown as Record<string, unknown>;\n const value = Reflect.get(db, prop, receiver);\n if (typeof value === 'function') {\n return value.bind(db);\n }\n return value;\n },\n });\n },\n inject: [DrizzleDatabaseManager],\n },\n ];\n\n return {\n module: DataPaasModule,\n providers,\n exports: [\n DrizzleDatabaseManager,\n DataPaasDatabaseService,\n SqlExecutionContextMiddleware,\n DRIZZLE_DATABASE,\n DATAPAAS_CONFIG,\n ],\n global: true,\n };\n }\n\n /**\n * 注册数据平台模块(异步方式)\n * 支持从 ConfigModule 等异步源获取配置\n * @param options 异步配置选项\n * @returns 动态模块\n * @example\n * ```typescript\n * DataPaasModule.forRootAsync({\n * imports: [ConfigModule],\n * useFactory: async (configService: ConfigService) => ({\n * connectionString: configService.get('DATABASE_URL'),\n * }),\n * inject: [ConfigService],\n * })\n * ```\n */\n static forRootAsync(options: DataPaasModuleAsyncOptions): DynamicModule {\n const asyncProviders = this.createAsyncProviders(options);\n\n const providers: Provider[] = [\n ...asyncProviders,\n // 数据库管理器 - 使用 useClass,依赖注入连接服务和配置服务\n {\n provide: DrizzleDatabaseManager,\n useClass: DrizzleDatabaseManager,\n },\n // 数据库服务 - 使用 useClass,依赖注入管理器\n {\n provide: DataPaasDatabaseService,\n useClass: DataPaasDatabaseService,\n },\n {\n provide: SqlExecutionContextMiddleware,\n useClass: SqlExecutionContextMiddleware,\n },\n // Drizzle 数据库实例 - 使用 useFactory 获取管理器中的数据库实例\n {\n provide: DRIZZLE_DATABASE,\n useFactory: async (manager: DrizzleDatabaseManager) => {\n await manager.initialize();\n return new Proxy({} as Record<string, unknown>, {\n get: (_target, prop, receiver) => {\n const db = manager.getDatabase() as unknown as Record<string, unknown>;\n const value = Reflect.get(db, prop, receiver);\n if (typeof value === 'function') {\n return value.bind(db);\n }\n return value;\n },\n });\n },\n inject: [DrizzleDatabaseManager],\n },\n ];\n\n return {\n module: DataPaasModule,\n imports: options.imports || [],\n providers,\n exports: [\n DrizzleDatabaseManager,\n DataPaasDatabaseService,\n SqlExecutionContextMiddleware,\n DRIZZLE_DATABASE,\n DATAPAAS_CONFIG,\n ],\n global: true,\n };\n }\n\n /**\n * 创建异步配置 providers\n */\n private static createAsyncProviders(\n options: DataPaasModuleAsyncOptions\n ): Provider[] {\n if (options.useFactory) {\n return [this.createAsyncConfigProvider(options)];\n }\n\n throw new Error('DataPaasModule.forRootAsync 需要指定 useFactory');\n }\n\n /**\n * 创建异步配置 provider\n */\n private static createAsyncConfigProvider(\n options: DataPaasModuleAsyncOptions\n ): Provider {\n if (options.useFactory) {\n return {\n provide: DATAPAAS_CONFIG,\n useFactory: async (...args: unknown[]) => {\n const config = await options.useFactory!(...args);\n return this.normalizeConfig(config);\n },\n inject: options.inject || [],\n };\n }\n\n throw new Error('Invalid async options');\n }\n\n /**\n * 标准化配置,设置默认值\n */\n private static normalizeConfig(config: DataPaasConfig): DataPaasConfig {\n let roleSchema = '';\n let sslModeRequired = false;\n if (config.connectionString) {\n try {\n const url = new URL(config.connectionString);\n roleSchema = url.searchParams.get(\n 'schema'\n ) as string;\n sslModeRequired = url.searchParams.get('sslmode') === \"require\";\n } catch (err) {\n roleSchema = '';\n }\n }\n return {\n connectionString: config.connectionString,\n schema: config.schema,\n logger: config.logger || false,\n timeout: config.timeout || 10000,\n maxConnections: config.maxConnections || 1,\n idleTimeout: config.idleTimeout || 20,\n connectionTimeout: config.connectionTimeout || 10,\n ssl: config.ssl || (sslModeRequired ? 'require' : false),\n autoContext: config.autoContext == null ? true : !!config.autoContext,\n roleSchema,\n connectionTokenFilePath: config.connectionTokenFilePath,\n };\n }\n}\n","import { Injectable, OnModuleDestroy } from '@nestjs/common';\nimport { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { DrizzleDatabaseManager } from './drizzle-manager';\nimport { DatabaseConnectionInfo } from '../types';\n\n@Injectable()\nexport class DataPaasDatabaseService implements OnModuleDestroy {\n constructor(\n private readonly databaseManager: DrizzleDatabaseManager,\n ) {}\n\n /**\n * 获取 Drizzle 数据库实例\n * @returns Drizzle 数据库实例\n */\n getDatabase(): PostgresJsDatabase {\n return this.databaseManager.getDatabase();\n }\n\n /**\n * 获取数据库管理器\n * @returns 数据库管理器实例\n */\n getDatabaseManager(): DrizzleDatabaseManager {\n return this.databaseManager;\n }\n\n\n /**\n * 测试数据库连接\n * @returns 连接是否正常\n */\n async testConnection(): Promise<boolean> {\n return this.databaseManager.testConnection();\n }\n\n /**\n * 重新连接数据库\n * @returns 新的数据库实例\n */\n async reconnect(): Promise<PostgresJsDatabase> {\n return this.databaseManager.reconnect();\n }\n\n /**\n * 获取当前连接信息\n * @returns 当前连接信息\n */\n getConnectionInfo(): DatabaseConnectionInfo | null {\n return this.databaseManager.getConnectionInfo();\n }\n\n /**\n * 检查连接状态\n * @returns 是否已连接\n */\n isConnected(): boolean {\n return this.databaseManager.isDatabaseConnected();\n }\n\n /**\n * 开始事务\n * @param callback 事务回调函数\n * @returns 事务结果\n */\n async transaction<T>(callback: (tx: PostgresJsDatabase) => Promise<T>): Promise<T> {\n return this.databaseManager.transaction(callback);\n }\n\n /**\n * 获取连接池状态\n * @returns 连接池状态信息\n */\n getConnectionPoolStatus(): { isConnected: boolean; connectionInfo: DatabaseConnectionInfo | null } {\n return this.databaseManager.getConnectionPoolStatus();\n }\n\n\n /**\n * 模块销毁时断开数据库连接\n */\n async onModuleDestroy(): Promise<void> {\n await this.databaseManager.disconnect();\n }\n}\n","import { Injectable, NestMiddleware, Inject } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\nimport * as session from 'drizzle-orm/postgres-js';\nimport { DATAPAAS_CONFIG } from './const';\nimport { type DataPaasConfig } from '../types';\n\ninterface UserContext {\n userId?: string;\n tenantId?: number;\n appId?: string;\n // 用户角色 id 列表\n roles?: string[];\n // 是否为系统账号\n isSystemAccount: boolean;\n // 用户类型(如果有)\n userType?: string;\n}\n@Injectable()\nexport class SqlExecutionContextMiddleware implements NestMiddleware {\n constructor(\n @Inject(DATAPAAS_CONFIG)\n private readonly config: DataPaasConfig\n ) {}\n use(req: Request, _res: Response, next: NextFunction) {\n const roleSchema = this.config.roleSchema ?? '';\n const userContext =\n (req as unknown as { userContext: UserContext }).userContext || {};\n const roles = (userContext.roles || []).join(',');\n const userId = userContext.userId;\n const isSystemAccount = userContext.isSystemAccount;\n const userType = userContext.userType;\n const sqls = [\n // 设置用户 ID, 匿名用户时设置为空字符串\n userId\n ? `SET LOCAL app.user_id = '${userId}'`\n : `SET LOCAL app.user_id = ''`,\n // 系统用户设置为service_role, 登录用户设置为 authenticated_xxx 角色,匿名用户设置为 anon_xxx 角色\n isSystemAccount\n ? `SET LOCAL ROLE 'service_role_${roleSchema}'`\n : userId\n ? `SET LOCAL ROLE 'authenticated_${roleSchema}'`\n : `SET LOCAL ROLE 'anon_${roleSchema}'`,\n // 自定义角色\n roles\n ? `SET LOCAL app.role_ids = '${roles}'`\n : `SET LOCAL app.role_ids = ''`,\n // 用户类型\n userType\n ? `SET LOCAL app.user_type = '${userType}'`\n : `SET LOCAL app.user_type = ''`,\n ]\n .filter(Boolean)\n .join(';');\n // 需要确认角色设置是否对后续 SQL 执行有影响,预期是只影响当前会话\n session.runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n }\n}\n","import { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { sql } from 'drizzle-orm';\n\nimport { DataPaasDatabaseService } from './database.service';\nimport { type DatabaseConnectionInfo } from '../types';\n\n/**\n * 数据库工具函数集合\n */\nexport class DatabaseUtils {\n /**\n * 安全执行数据库操作\n * @param databaseService 数据库服务实例\n * @param operation 数据库操作函数\n * @param fallback 失败时的回退值\n * @returns 操作结果或回退值\n */\n static async safeExecute<T>(\n databaseService: DataPaasDatabaseService,\n operation: (db: PostgresJsDatabase) => Promise<T>,\n fallback?: T\n ): Promise<T | undefined> {\n try {\n if (!databaseService.isConnected()) {\n throw new Error('Database not connected');\n }\n \n const db = databaseService.getDatabase();\n return await operation(db);\n } catch (error) {\n console.error('Database operation failed:', error);\n return fallback;\n }\n }\n\n /**\n * 批量执行数据库操作\n * @param databaseService 数据库服务实例\n * @param operations 操作数组\n * @param batchSize 批处理大小\n * @returns 所有操作的结果\n */\n static async batchExecute<T>(\n databaseService: DataPaasDatabaseService,\n operations: Array<(db: PostgresJsDatabase) => Promise<T>>,\n batchSize: number = 10\n ): Promise<T[]> {\n const results: T[] = [];\n const db = databaseService.getDatabase();\n\n for (let i = 0; i < operations.length; i += batchSize) {\n const batch = operations.slice(i, i + batchSize);\n const batchResults = await Promise.all(\n batch.map(operation => operation(db))\n );\n results.push(...batchResults);\n }\n\n return results;\n }\n\n /**\n * 执行带重试的数据库操作\n * @param databaseService 数据库服务实例\n * @param operation 数据库操作函数\n * @param maxRetries 最大重试次数\n * @param retryDelay 重试延迟(毫秒)\n * @returns 操作结果\n */\n static async executeWithRetry<T>(\n databaseService: DataPaasDatabaseService,\n operation: (db: PostgresJsDatabase) => Promise<T>,\n maxRetries: number = 3,\n retryDelay: number = 1000\n ): Promise<T> {\n let lastError: Error;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const db = databaseService.getDatabase();\n return await operation(db);\n } catch (error) {\n lastError = error as Error;\n \n if (attempt === maxRetries) {\n break;\n }\n\n console.warn(`Database operation failed (attempt ${attempt + 1}/${maxRetries + 1}):`, error);\n \n // 等待后重试\n await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1)));\n }\n }\n\n throw lastError!;\n }\n\n /**\n * 检查表是否存在\n * @param databaseService 数据库服务实例\n * @param tableName 表名\n * @returns 表是否存在\n */\n static async tableExists(\n db: PostgresJsDatabase,\n tableName: string\n ): Promise<boolean> {\n try {\n const result = await db.execute(\n sql`SELECT EXISTS (\n SELECT FROM information_schema.tables \n WHERE table_schema = 'public' \n AND table_name = ${tableName}\n )`\n );\n \n return result[0]?.exists as boolean || false;\n } catch (error) {\n console.error('Failed to check table existence:', error);\n return false;\n }\n }\n\n /**\n * 获取表结构信息\n * @param databaseService 数据库服务实例\n * @param tableName 表名\n * @returns 表结构信息\n */\n static async getTableStructure(\n db: PostgresJsDatabase,\n tableName: string\n ): Promise<Record<string, unknown>[]> {\n try {\n const result = await db.execute(\n sql`SELECT \n column_name,\n data_type,\n is_nullable,\n column_default,\n character_maximum_length\n FROM information_schema.columns \n WHERE table_schema = 'public' \n AND table_name = ${tableName}\n ORDER BY ordinal_position`,\n );\n \n return result;\n } catch (error) {\n console.error('Failed to get table structure:', error);\n return [];\n }\n }\n\n /**\n * 获取数据库版本信息\n * @param databaseService 数据库服务实例\n * @returns 数据库版本\n */\n static async getDatabaseVersion(\n db: PostgresJsDatabase\n ): Promise<string> {\n try {\n const result = await db.execute(sql`SELECT version()`);\n return result[0]?.version as string || 'Unknown';\n } catch (error) {\n console.error('Failed to get database version:', error);\n return 'Unknown';\n }\n }\n\n /**\n * 获取连接统计信息\n * @param databaseService 数据库服务实例\n * @returns 连接统计信息\n */\n static async getConnectionStats(\n db: PostgresJsDatabase\n ): Promise<Record<string, unknown>> {\n try {\n const result = await db.execute(sql`\n SELECT \n count(*) as total_connections,\n count(*) FILTER (WHERE state = 'active') as active_connections,\n count(*) FILTER (WHERE state = 'idle') as idle_connections\n FROM pg_stat_activity \n WHERE datname = current_database()\n `);\n \n return result[0] || {};\n } catch (error) {\n console.error('Failed to get connection stats:', error);\n return {};\n }\n }\n\n /**\n * 健康检查\n * @param databaseService 数据库服务实例\n * @returns 健康状态\n */\n static async healthCheck(\n databaseService: DataPaasDatabaseService\n ): Promise<{\n isHealthy: boolean;\n isConnected: boolean;\n version?: string;\n connectionInfo?: DatabaseConnectionInfo | null;\n error?: string;\n }> {\n try {\n const isConnected = databaseService.isConnected();\n const connectionInfo = databaseService.getConnectionInfo();\n\n const db = databaseService.getDatabase();\n if (!isConnected) {\n return {\n isHealthy: false,\n isConnected: false,\n error: 'Database not connected'\n };\n }\n\n const version = await this.getDatabaseVersion(db);\n \n return {\n isHealthy: true,\n isConnected: true,\n version,\n connectionInfo\n };\n } catch (error) {\n return {\n isHealthy: false,\n isConnected: false,\n error: error instanceof Error ? error.message : 'Unknown error'\n };\n }\n }\n}\n"],"mappings":"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","names":["Injectable","Inject","drizzle","sql","postgres","readFile","watchFile","unwatchFile","DRIZZLE_DATABASE","DATAPAAS_CONFIG","DATAPAAS_LOGGER_SERVICE","Symbol","RequestContextService","ObservableService","parseConnectionInfo","connectionString","config","maxConnections","idleTimeout","connectionTimeout","ssl","logger","parsedConfigFromUrl","URL","temp","searchParams","delete","toString","error","Error","username","password","hostname","port","pathname","connectionConfig","decodeURIComponent","host","parseInt","max","idle_timeout","connect_timeout","onnotice","console","log","undefined","Logger","createTimer","nestLogger","Logger","genPostgresProxy","sql","observable","requestContext","wrapQuery","query","then","Proxy","get","target","prop","receiver","onfulfilled","onrejected","sqlStr","strings","Array","span","startTrace","getContext","requestRootSpan","spanContext","traceId","spanId","logContext","source_type","paas_attributes_module","paas_parent_span_context","setAttribute","elapsed","createTimer","result","isSetRoleSql","command","duration","parsedParams","JSON","stringify","operation","statement","string","duration_ms","result_count","length","error_message","log","dropTrace","end","error","Error","message","String","Promise","reject","value","Reflect","bind","apply","thisArg","argArray","arg","cb","call","tx","queryStr","args","options","DrizzleDatabaseManager","db","sqlClient","connectionOpPromise","currentConnectionInfo","isConnected","skipInitDbConnection","currentToken","currentResolvedConnectionString","tokenWatchFilePath","tokenWatchDebounceTimer","refreshingByTokenChange","sqlClientShutdownTimeoutSeconds","config","observable","requestContext","process","env","DEPRECATED_SKIP_INIT_DB_CONNECTION","onModuleInit","initialize","startTokenWatcher","onModuleDestroy","stopTokenWatcher","disconnect","name","runWithConnectionLock","token","readConnectionTokenFromFile","resolvedConnectionString","appendTokenToConnectionString","connectionString","nextState","createConnectedState","applyConnectedState","error","Error","message","getDatabase","testConnection","execute","sql","console","reconnect","reconnectInternal","shouldStopWatcher","pendingOp","Promise","allSettled","shutdownSqlClientSafely","getConnectionInfo","isDatabaseConnected","transaction","callback","getConnectionPoolStatus","connectionInfo","tokenFilePath","connectionTokenFilePath","raw","readFile","trimmed","trim","length","url","URL","currentOptions","searchParams","get","optionsWithoutToken","replace","tokenOption","nextOptions","filter","Boolean","join","set","toString","connectionConfig","parseConnectionInfo","baseSql","postgres","sqlProxy","genPostgresProxy","drizzle","schema","logger","ssl","connectionTimeout","connect_timeout","idleTimeout","idle_timeout","maxConnections","max","nextResolvedConnectionString","nextToken","reconnectWithResolvedConnectionString","operation","resolvedToken","previousSqlClient","client","errorPrefix","end","timeout","startTokenWatchFileFallback","unwatchFile","watchFile","interval","scheduleTokenRefresh","clearTimeout","setTimeout","refreshConnectionIfTokenChanged","sameToken","sameConnectionString","Module","Injectable","DataPaasDatabaseService","databaseManager","getDatabase","getDatabaseManager","testConnection","reconnect","getConnectionInfo","isConnected","isDatabaseConnected","transaction","callback","getConnectionPoolStatus","onModuleDestroy","disconnect","Injectable","Inject","session","SqlExecutionContextMiddleware","config","use","req","_res","next","roleSchema","userContext","roles","join","userId","isSystemAccount","userType","sqls","filter","Boolean","session","runWithAuthContext","preSql","RequestContextService","OBSERVABLE_SERVICE","Observable","verifyDrizzlePatched","postgres","require","ok","getAuthContextStore","runWithAuthContext","Error","err","reason","message","String","DataPaasModule","configure","_consumer","forRoot","options","roleSchema","sslModeRequired","connectionString","url","URL","searchParams","get","config","schema","logger","timeout","maxConnections","idleTimeout","connectionTimeout","ssl","autoContext","connectionTokenFilePath","providers","provide","DATAPAAS_CONFIG","useValue","DrizzleDatabaseManager","useClass","DataPaasDatabaseService","SqlExecutionContextMiddleware","DRIZZLE_DATABASE","useFactory","manager","initialize","Proxy","_target","prop","receiver","db","getDatabase","value","Reflect","bind","inject","module","exports","global","forRootAsync","asyncProviders","createAsyncProviders","imports","createAsyncConfigProvider","args","normalizeConfig","DATAPAAS_LOGGER_SERVICE","observable","rtx","Observable","token","OBSERVABLE_SERVICE","optional","RequestContextService","sql","DatabaseUtils","safeExecute","databaseService","operation","fallback","isConnected","Error","db","getDatabase","error","console","batchExecute","operations","batchSize","results","i","length","batch","slice","batchResults","Promise","all","map","push","executeWithRetry","maxRetries","retryDelay","lastError","attempt","warn","resolve","setTimeout","tableExists","tableName","result","execute","sql","exists","getTableStructure","getDatabaseVersion","version","getConnectionStats","healthCheck","connectionInfo","getConnectionInfo","isHealthy","message"]}
1
+ {"version":3,"sources":["../src/database/drizzle-manager.ts","../src/database/const.ts","../src/utils.ts","../src/database/postgres-proxy.ts","../src/database/database.module.ts","../src/database/database.service.ts","../src/database/sql-exection-context.middleware.ts","../src/database/drizzle-auth-context.ts","../src/database/drizzle-monkey-patch.ts","../src/database/database-utils.ts"],"sourcesContent":["import { Injectable, OnModuleInit, OnModuleDestroy, Inject } from '@nestjs/common';\nimport { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { sql } from 'drizzle-orm';\nimport postgres from 'postgres';\nimport { readFile } from 'node:fs/promises';\nimport { watchFile, unwatchFile } from 'node:fs';\nimport { DATAPAAS_CONFIG, DATAPAAS_LOGGER_SERVICE } from './const';\nimport { RequestContextService, ObservableService } from '@lark-apaas/nestjs-common';\n\n\nimport { type DataPaasConfig, type DatabaseConnectionInfo } from '../types';\nimport { parseConnectionInfo } from '../utils';\nimport { genPostgresProxy } from './postgres-proxy';\n\n@Injectable()\nexport class DrizzleDatabaseManager implements OnModuleInit, OnModuleDestroy {\n private db: PostgresJsDatabase | null = null;\n private sqlClient: ReturnType<typeof postgres> | null = null;\n private connectionOpPromise: Promise<PostgresJsDatabase> | null = null;\n private currentConnectionInfo: DatabaseConnectionInfo | null = null;\n private isConnected: boolean = false;\n private skipInitDbConnection: boolean = false;\n private currentToken: string | null = null;\n private currentResolvedConnectionString: string | null = null;\n private tokenWatchFilePath: string | null = null;\n private tokenWatchDebounceTimer: NodeJS.Timeout | null = null;\n private refreshingByTokenChange = false;\n\n private readonly sqlClientShutdownTimeoutSeconds = 5;\n\n /**\n * 构造函数:注入配置、日志能力与请求上下文。\n */\n constructor(\n @Inject(DATAPAAS_CONFIG)\n private readonly config: DataPaasConfig,\n @Inject(DATAPAAS_LOGGER_SERVICE)\n private readonly observable: ObservableService,\n private readonly requestContext: RequestContextService,\n ) {\n this.skipInitDbConnection = !!process.env.DEPRECATED_SKIP_INIT_DB_CONNECTION;\n }\n\n /**\n * 模块初始化:建立首个数据库连接并启动 token 监听。\n */\n async onModuleInit() {\n await this.initialize();\n this.startTokenWatcher();\n }\n\n /**\n * 模块销毁:先停止 token 监听,再断开数据库连接。\n */\n async onModuleDestroy() {\n this.stopTokenWatcher();\n await this.disconnect();\n }\n\n /**\n * 初始化数据库连接\n * @returns Drizzle 数据库实例\n */\n async initialize(): Promise<PostgresJsDatabase> {\n if (this.skipInitDbConnection) {\n this.isConnected = true;\n this.db = { name: 'skipInitDbConnection' } as unknown as PostgresJsDatabase;\n return this.db;\n }\n\n if (this.db && this.isConnected) {\n return this.db;\n }\n\n if (this.connectionOpPromise) {\n return this.connectionOpPromise;\n }\n\n return this.runWithConnectionLock(async () => {\n if (this.db && this.isConnected) {\n return this.db;\n }\n\n try {\n const token = await this.readConnectionTokenFromFile();\n const resolvedConnectionString = this.appendTokenToConnectionString(this.config.connectionString, token);\n const nextState = await this.createConnectedState(resolvedConnectionString, token);\n\n this.applyConnectedState(nextState);\n return nextState.db;\n } catch (error) {\n // 避免并发 initialize 时后失败覆盖先成功状态。\n if (!this.db || !this.isConnected) {\n this.isConnected = false;\n this.db = null;\n }\n throw new Error(`Failed to initialize database connection: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n });\n }\n\n /**\n * 获取数据库实例\n * @returns Drizzle 数据库实例\n */\n getDatabase(): PostgresJsDatabase {\n if (!this.db || !this.isConnected) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n return this.db;\n }\n\n /**\n * 测试数据库连接\n * @returns 连接是否正常\n */\n async testConnection(): Promise<boolean> {\n if (!this.db) {\n return false;\n }\n\n try {\n await this.db.execute(sql`SELECT 1`);\n return true;\n } catch (error) {\n console.error('Database connection test failed:', error);\n this.isConnected = false;\n return false;\n }\n }\n\n /**\n * 重新连接数据库。\n * 注意:重连时不关闭 token watcher,避免重连后丢失后续文件变更监听。\n * 同时通过 promise 锁避免并发重连导致状态抖动。\n * @returns 新的数据库实例\n */\n async reconnect(): Promise<PostgresJsDatabase> {\n return this.reconnectInternal();\n }\n\n /**\n * 断开数据库连接\n */\n async disconnect(shouldStopWatcher: boolean = true): Promise<void> {\n if (shouldStopWatcher) {\n this.stopTokenWatcher();\n }\n\n const pendingOp = this.connectionOpPromise;\n if (pendingOp) {\n await Promise.allSettled([pendingOp]);\n }\n\n await this.shutdownSqlClientSafely(this.sqlClient, 'Error disconnecting from database:');\n this.sqlClient = null;\n this.db = null;\n this.isConnected = false;\n this.currentConnectionInfo = null;\n this.currentResolvedConnectionString = null;\n this.currentToken = null;\n }\n\n /**\n * 获取当前连接信息\n * @returns 当前连接信息\n */\n getConnectionInfo(): DatabaseConnectionInfo | null {\n return this.currentConnectionInfo;\n }\n\n /**\n * 检查连接状态\n * @returns 是否已连接\n */\n isDatabaseConnected(): boolean {\n return this.isConnected;\n }\n\n /**\n * 开始事务\n * @param callback 事务回调函数\n * @returns 事务结果\n */\n async transaction<T>(callback: (tx: PostgresJsDatabase) => Promise<T>): Promise<T> {\n const db = this.getDatabase();\n return db.transaction(callback);\n }\n\n /**\n * 获取连接池状态\n * @returns 连接池状态信息\n */\n getConnectionPoolStatus(): { isConnected: boolean; connectionInfo: DatabaseConnectionInfo | null } {\n return {\n isConnected: this.isConnected,\n connectionInfo: this.currentConnectionInfo,\n };\n }\n\n /**\n * 读取 token 文件,空文件等价于无 token\n */\n private async readConnectionTokenFromFile(): Promise<string | null> {\n const tokenFilePath = this.config.connectionTokenFilePath;\n if (!tokenFilePath) {\n return null;\n }\n try {\n const raw = await readFile(tokenFilePath, 'utf-8');\n const trimmed = raw.trim();\n return trimmed.length > 0 ? trimmed : null;\n } catch {\n return null;\n }\n }\n\n /**\n * 基于基础 connectionString 和 token 生成最终连接串\n * 只修改 options 参数值,在末尾追加 -c miaoda.zti_token=<token>\n */\n private appendTokenToConnectionString(connectionString: string, token: string | null): string {\n if (!token) {\n return connectionString;\n }\n\n const url = new URL(connectionString);\n const currentOptions = url.searchParams.get('options') || '';\n const optionsWithoutToken = currentOptions\n .replace(/(?:^|\\s)-c\\s+miaoda.zti_token=[^\\s]*/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n const tokenOption = `-c miaoda.zti_token=${token}`;\n const nextOptions = [optionsWithoutToken, tokenOption].filter(Boolean).join(' ').trim();\n\n url.searchParams.set('options', nextOptions);\n return url.toString();\n }\n\n private async createConnectedState(resolvedConnectionString: string, token: string | null): Promise<{\n db: PostgresJsDatabase;\n sqlClient: ReturnType<typeof postgres>;\n connectionInfo: DatabaseConnectionInfo;\n resolvedConnectionString: string;\n token: string | null;\n }> {\n const { connectionString, connectionConfig } = parseConnectionInfo(resolvedConnectionString, this.config);\n const baseSql = postgres(connectionString, connectionConfig);\n try {\n const sqlProxy = genPostgresProxy(baseSql, this.observable, this.requestContext);\n const db = drizzle(sqlProxy, { schema: this.config.schema, logger: this.config.logger });\n\n return {\n db,\n sqlClient: baseSql,\n resolvedConnectionString: connectionString,\n token,\n connectionInfo: {\n connectionString,\n connectionConfig: {\n ssl: connectionConfig.ssl,\n connectionTimeout: connectionConfig.connect_timeout,\n idleTimeout: connectionConfig.idle_timeout,\n maxConnections: connectionConfig.max,\n },\n },\n };\n } catch (error) {\n await this.shutdownSqlClientSafely(baseSql, 'Error closing failed new database client:');\n throw error;\n }\n }\n\n private applyConnectedState(nextState: {\n db: PostgresJsDatabase;\n sqlClient: ReturnType<typeof postgres>;\n connectionInfo: DatabaseConnectionInfo;\n resolvedConnectionString: string;\n token: string | null;\n }): void {\n this.db = nextState.db;\n this.sqlClient = nextState.sqlClient;\n this.currentConnectionInfo = nextState.connectionInfo;\n this.currentResolvedConnectionString = nextState.resolvedConnectionString;\n this.currentToken = nextState.token;\n this.isConnected = true;\n }\n\n private reconnectInternal(\n nextResolvedConnectionString?: string,\n nextToken?: string | null,\n ): Promise<PostgresJsDatabase> {\n return this.runWithConnectionLock(() => this.reconnectWithResolvedConnectionString(\n nextResolvedConnectionString,\n nextToken,\n ));\n }\n\n private runWithConnectionLock(operation: () => Promise<PostgresJsDatabase>): Promise<PostgresJsDatabase> {\n if (this.connectionOpPromise) {\n return this.connectionOpPromise;\n }\n\n this.connectionOpPromise = (async () => {\n try {\n return await operation();\n } finally {\n this.connectionOpPromise = null;\n }\n })();\n\n return this.connectionOpPromise;\n }\n\n private async reconnectWithResolvedConnectionString(\n nextResolvedConnectionString?: string,\n nextToken?: string | null,\n ): Promise<PostgresJsDatabase> {\n const resolvedToken = nextToken ?? await this.readConnectionTokenFromFile();\n const resolvedConnectionString =\n nextResolvedConnectionString ??\n this.appendTokenToConnectionString(this.config.connectionString, resolvedToken);\n\n const nextState = await this.createConnectedState(resolvedConnectionString, resolvedToken);\n const previousSqlClient = this.sqlClient;\n\n this.applyConnectedState(nextState);\n if (previousSqlClient && previousSqlClient !== nextState.sqlClient) {\n await this.shutdownSqlClientSafely(previousSqlClient, 'Error shutting down previous database client after reconnect:');\n }\n\n return nextState.db;\n }\n\n private async shutdownSqlClientSafely(\n client: ReturnType<typeof postgres> | null,\n errorPrefix: string,\n ): Promise<void> {\n try {\n if (client && typeof client.end === 'function') {\n await client.end({ timeout: this.sqlClientShutdownTimeoutSeconds });\n }\n } catch (error) {\n console.error(errorPrefix, error);\n }\n }\n\n /**\n * 启动 token 文件监听;若已存在旧监听则先清理后重建。\n * 使用 watchFile 轮询,规避虚拟化/网络文件系统下 fs.watch 不可靠的问题。\n */\n private startTokenWatcher(): void {\n const tokenFilePath = this.config.connectionTokenFilePath;\n if (!tokenFilePath || this.skipInitDbConnection) {\n return;\n }\n this.stopTokenWatcher();\n this.startTokenWatchFileFallback(tokenFilePath);\n }\n\n /**\n * watchFile 兜底监听:即使 fs.watch 丢事件,也能通过轮询感知 token 文件变化。\n */\n private startTokenWatchFileFallback(tokenFilePath: string): void {\n if (this.tokenWatchFilePath === tokenFilePath) {\n return;\n }\n\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n\n watchFile(tokenFilePath, { interval: 600_000 }, () => {\n this.scheduleTokenRefresh();\n });\n this.tokenWatchFilePath = tokenFilePath;\n }\n\n /**\n * 停止 watcher 与防抖定时器,释放监听资源。\n */\n private stopTokenWatcher(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n this.tokenWatchDebounceTimer = null;\n }\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n }\n\n /**\n * 对频繁文件事件做防抖,避免短时间重复重连。\n */\n private scheduleTokenRefresh(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n }\n this.tokenWatchDebounceTimer = setTimeout(() => {\n void this.refreshConnectionIfTokenChanged();\n }, 300);\n }\n\n /**\n * token 变化时刷新连接:仅在 token/连接串发生变化,或当前连接不可用时触发重连。\n */\n private async refreshConnectionIfTokenChanged(): Promise<void> {\n if (this.refreshingByTokenChange) {\n return;\n }\n this.refreshingByTokenChange = true;\n\n try {\n const nextToken = await this.readConnectionTokenFromFile();\n\n const nextResolvedConnectionString = this.appendTokenToConnectionString(\n this.config.connectionString,\n nextToken,\n );\n const sameToken = nextToken === this.currentToken;\n const sameConnectionString = nextResolvedConnectionString === this.currentResolvedConnectionString;\n\n if (sameToken && sameConnectionString && this.isConnected) {\n return;\n }\n\n await this.reconnectInternal(nextResolvedConnectionString, nextToken);\n } catch (error) {\n console.error('Failed to refresh database connection from token file:', error);\n } finally {\n this.refreshingByTokenChange = false;\n }\n }\n}\n","export const DRIZZLE_DATABASE = 'DRIZZLE_DATABASE';\nexport const DATAPAAS_CONFIG = 'DATAPAAS_CONFIG';\nexport const DATAPAAS_LOGGER_SERVICE = Symbol('DATAPAAS_LOGGER_SERVICE');","import { type Logger } from 'drizzle-orm';\nimport { PostgresOptions } from './types';\ninterface ConnectionConfig {\n maxConnections?: number;\n idleTimeout?: number;\n connectionTimeout?: number;\n ssl?: PostgresOptions['ssl'];\n logger?: boolean | Logger;\n}\nexport function parseConnectionInfo(connectionString: string, config?: ConnectionConfig) {\n const { maxConnections, idleTimeout, connectionTimeout, ssl, logger } = config || {};\n // 创建 postgres 连接\n let parsedConfigFromUrl = null;\n try {\n parsedConfigFromUrl = new URL(connectionString);\n const temp = new URL(connectionString);\n temp.searchParams.delete('schema'); // 移除schema参数,避免 postgres 解析错误\n connectionString = temp.toString();\n } catch (error) {\n throw new Error('Failed to parse connection info');\n }\n const { username, password, hostname, port, pathname } = parsedConfigFromUrl;\n const connectionConfig = {\n username: decodeURIComponent(username),\n password: decodeURIComponent(password),\n host: [hostname] as unknown as string,\n port: [parseInt(port) || 5432] as unknown as number,\n pathname: decodeURIComponent(pathname),\n max: maxConnections || 1,\n idle_timeout: idleTimeout || 20,\n connect_timeout: connectionTimeout || 10,\n ssl,\n onnotice: logger ? console.log : undefined,\n }\n return { connectionString, connectionConfig };\n}","import type postgres from 'postgres';\nimport { Logger } from '@nestjs/common';\nimport { Span } from '@lark-apaas/nestjs-observable';\nimport { ObservableService, RequestContextService, createTimer } from '@lark-apaas/nestjs-common';\n\nconst nestLogger = new Logger('Database');\n\n/**\n * 包装 postgres 实例以记录 SQL 和结果\n */\nexport function genPostgresProxy(sql: postgres.Sql, observable: ObservableService, requestContext: RequestContextService): postgres.Sql {\n const wrapQuery = <T extends readonly (postgres.Row | undefined)[]>(query: postgres.PendingQuery<T>): postgres.PendingQuery<T> => {\n if (query && typeof query.then === 'function') {\n return new Proxy(query, {\n get(target, prop, receiver) {\n if (prop === 'then') {\n return async (onfulfilled?: (value: postgres.RowList<T>) => unknown, onrejected?: (reason: unknown) => unknown) => {\n const sqlStr = (query as unknown as Record<string, unknown>).strings instanceof Array ? (query as unknown as { strings: string[] }).strings[0] : '';\n const span = observable.startTrace(sqlStr, requestContext?.getContext()?.requestRootSpan as Span);\n const spanContext = {\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId\n }\n // referer_path 和 api 由 logger 自动从 RequestContext 读取\n const logContext: Record<string, unknown> = {\n source_type: 'platform',\n paas_attributes_module: 'data',\n paas_parent_span_context: spanContext,\n };\n span.setAttribute(\"module\", \"data\");\n span.setAttribute(\"source_type\", \"platform\");\n\n const elapsed = createTimer();\n try {\n const result = await target;\n const isSetRoleSql = result?.command === 'SET';\n\n // rls SET ROLE 不上报日志\n if (!isSetRoleSql) {\n const duration = elapsed();\n\n const parsedParams = JSON.stringify({\n operation: result?.command,\n sql: result?.statement?.string,\n duration_ms: duration,\n result_count: result.length ?? 0,\n error_message: ''\n });\n\n nestLogger.log(parsedParams, { ...logContext, duration_ms: duration });\n span.setAttribute('duration_ms', duration);\n } else {\n observable.dropTrace(span);\n }\n\n span.end();\n return onfulfilled ? onfulfilled(result) : result;\n } catch (error) {\n const duration = elapsed();\n const sqlStr = (query as unknown as Record<string, unknown>).strings instanceof Array ? (query as unknown as { strings: string[] }).strings[0] : '';\n const parsedParams = JSON.stringify({\n operation: '',\n sql: sqlStr,\n duration_ms: duration,\n result_count: 0,\n error_message: error instanceof Error ? error.message : String(error)\n });\n nestLogger.log(parsedParams, { ...logContext, duration_ms: duration });\n span.setAttribute('duration_ms', duration);\n\n span.end();\n return onrejected ? onrejected(error) : Promise.reject(error);\n }\n };\n }\n const value = Reflect.get(target, prop, receiver);\n return typeof value === 'function' ? value.bind(receiver) : value;\n },\n }) as postgres.PendingQuery<T>;\n }\n return query;\n };\n\n return new Proxy(sql, {\n apply(target, thisArg, argArray) {\n const query = Reflect.apply(target, thisArg, argArray);\n return wrapQuery(query);\n },\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (prop === 'begin' && typeof value === 'function') {\n return <T>(arg: string | ((tx: postgres.TransactionSql) => T | Promise<T>), cb?: (tx: postgres.TransactionSql) => T | Promise<T>) => {\n if (typeof arg === 'function') {\n return value.call(target, (tx: postgres.TransactionSql) => arg(genPostgresProxy(tx as unknown as postgres.Sql, observable, requestContext) as unknown as postgres.TransactionSql));\n }\n if (typeof cb === 'function') {\n return value.call(target, arg, (tx: postgres.TransactionSql) => cb(genPostgresProxy(tx as unknown as postgres.Sql, observable, requestContext) as unknown as postgres.TransactionSql));\n }\n return value.call(target, arg);\n };\n }\n if (prop === 'unsafe' && typeof value === 'function') {\n return <T extends readonly (postgres.Row | undefined)[] = postgres.Row[]>(queryStr: string, args?: unknown[], options?: postgres.UnsafeQueryOptions): postgres.PendingQuery<T> => {\n const query = value.call(target, queryStr, args, options) as postgres.PendingQuery<T>;\n return wrapQuery(query);\n };\n }\n return typeof value === 'function' ? value.bind(receiver) : value;\n },\n }) as postgres.Sql;\n}","import {\n Module,\n DynamicModule,\n Provider,\n MiddlewareConsumer,\n InjectionToken,\n ModuleMetadata,\n} from '@nestjs/common';\nimport { DrizzleDatabaseManager } from './drizzle-manager';\nimport { DataPaasDatabaseService } from './database.service';\nimport { SqlExecutionContextMiddleware } from './sql-exection-context.middleware';\nimport { type DataPaasConfig } from '@/types';\nimport { DATAPAAS_CONFIG, DRIZZLE_DATABASE, DATAPAAS_LOGGER_SERVICE } from './const';\nexport { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { RequestContextService, OBSERVABLE_SERVICE, ObservableService } from '@lark-apaas/nestjs-common';\nimport { Observable } from '@lark-apaas/nestjs-observable';\nimport { applyDrizzleMonkeyPatch } from './drizzle-monkey-patch';\n\nexport interface DataPaasModuleOptions extends DataPaasConfig {}\n\n/**\n * 异步配置选项\n */\nexport interface DataPaasModuleAsyncOptions {\n /**\n * 需要导入的模块(例如 ConfigModule)\n */\n imports?: ModuleMetadata['imports'];\n /**\n * 使用工厂函数创建配置\n */\n useFactory?: (...args: unknown[]) => Promise<DataPaasConfig> | DataPaasConfig;\n /**\n * 工厂函数需要注入的依赖\n */\n inject?: InjectionToken[];\n}\n\n// 在模块加载阶段应用运行时 monkey-patch,替代 postinstall + patch-package\napplyDrizzleMonkeyPatch();\n\n@Module({\n providers: [{\n provide: DATAPAAS_LOGGER_SERVICE,\n useFactory: (observable: ObservableService | null, rtx: RequestContextService) => {\n return observable ?? new Observable(rtx);\n },\n inject: [{\n token: OBSERVABLE_SERVICE,\n optional: true,\n }, RequestContextService],\n }],\n})\nexport class DataPaasModule {\n configure(_consumer: MiddlewareConsumer) {}\n /**\n * 注册数据平台模块(同步方式)\n * @param options 模块配置选项\n * @returns 动态模块\n */\n static forRoot(options: DataPaasConfig): DynamicModule {\n let roleSchema = '';\n let sslModeRequired = false;\n if (options.connectionString) {\n try {\n const url = new URL(options.connectionString);\n\n roleSchema = url.searchParams.get(\n 'schema'\n ) as string;\n sslModeRequired = url.searchParams.get('sslmode') === \"require\";\n } catch (err) {\n roleSchema = '';\n }\n }\n const config: DataPaasConfig = {\n connectionString: options.connectionString,\n schema: options.schema,\n logger: options.logger || false,\n timeout: options.timeout || 10000,\n maxConnections: options.maxConnections || 1,\n idleTimeout: options.idleTimeout || 20,\n connectionTimeout: options.connectionTimeout || 10,\n ssl: options.ssl || (sslModeRequired ? 'require' : false),\n autoContext: options.autoContext == null ? true : !!options.autoContext,\n roleSchema,\n connectionTokenFilePath: options.connectionTokenFilePath,\n };\n\n const providers: Provider[] = [\n // 配置提供者 - 使用 useValue\n {\n provide: DATAPAAS_CONFIG,\n useValue: config,\n },\n // 数据库管理器 - 使用 useClass,依赖注入连接服务和配置服务\n {\n provide: DrizzleDatabaseManager,\n useClass: DrizzleDatabaseManager,\n },\n // 数据库服务 - 使用 useClass,依赖注入管理器\n {\n provide: DataPaasDatabaseService,\n useClass: DataPaasDatabaseService,\n },\n {\n provide: SqlExecutionContextMiddleware,\n useClass: SqlExecutionContextMiddleware,\n },\n // Drizzle 数据库实例 - 使用 useFactory 获取管理器中的数据库实例\n {\n provide: DRIZZLE_DATABASE,\n useFactory: async (manager: DrizzleDatabaseManager) => {\n await manager.initialize();\n return new Proxy({} as Record<string, unknown>, {\n get: (_target, prop, receiver) => {\n const db = manager.getDatabase() as unknown as Record<string, unknown>;\n const value = Reflect.get(db, prop, receiver);\n if (typeof value === 'function') {\n return value.bind(db);\n }\n return value;\n },\n });\n },\n inject: [DrizzleDatabaseManager],\n },\n ];\n\n return {\n module: DataPaasModule,\n providers,\n exports: [\n DrizzleDatabaseManager,\n DataPaasDatabaseService,\n SqlExecutionContextMiddleware,\n DRIZZLE_DATABASE,\n DATAPAAS_CONFIG,\n ],\n global: true,\n };\n }\n\n /**\n * 注册数据平台模块(异步方式)\n * 支持从 ConfigModule 等异步源获取配置\n * @param options 异步配置选项\n * @returns 动态模块\n * @example\n * ```typescript\n * DataPaasModule.forRootAsync({\n * imports: [ConfigModule],\n * useFactory: async (configService: ConfigService) => ({\n * connectionString: configService.get('DATABASE_URL'),\n * }),\n * inject: [ConfigService],\n * })\n * ```\n */\n static forRootAsync(options: DataPaasModuleAsyncOptions): DynamicModule {\n const asyncProviders = this.createAsyncProviders(options);\n\n const providers: Provider[] = [\n ...asyncProviders,\n // 数据库管理器 - 使用 useClass,依赖注入连接服务和配置服务\n {\n provide: DrizzleDatabaseManager,\n useClass: DrizzleDatabaseManager,\n },\n // 数据库服务 - 使用 useClass,依赖注入管理器\n {\n provide: DataPaasDatabaseService,\n useClass: DataPaasDatabaseService,\n },\n {\n provide: SqlExecutionContextMiddleware,\n useClass: SqlExecutionContextMiddleware,\n },\n // Drizzle 数据库实例 - 使用 useFactory 获取管理器中的数据库实例\n {\n provide: DRIZZLE_DATABASE,\n useFactory: async (manager: DrizzleDatabaseManager) => {\n await manager.initialize();\n return new Proxy({} as Record<string, unknown>, {\n get: (_target, prop, receiver) => {\n const db = manager.getDatabase() as unknown as Record<string, unknown>;\n const value = Reflect.get(db, prop, receiver);\n if (typeof value === 'function') {\n return value.bind(db);\n }\n return value;\n },\n });\n },\n inject: [DrizzleDatabaseManager],\n },\n ];\n\n return {\n module: DataPaasModule,\n imports: options.imports || [],\n providers,\n exports: [\n DrizzleDatabaseManager,\n DataPaasDatabaseService,\n SqlExecutionContextMiddleware,\n DRIZZLE_DATABASE,\n DATAPAAS_CONFIG,\n ],\n global: true,\n };\n }\n\n /**\n * 创建异步配置 providers\n */\n private static createAsyncProviders(\n options: DataPaasModuleAsyncOptions\n ): Provider[] {\n if (options.useFactory) {\n return [this.createAsyncConfigProvider(options)];\n }\n\n throw new Error('DataPaasModule.forRootAsync 需要指定 useFactory');\n }\n\n /**\n * 创建异步配置 provider\n */\n private static createAsyncConfigProvider(\n options: DataPaasModuleAsyncOptions\n ): Provider {\n if (options.useFactory) {\n return {\n provide: DATAPAAS_CONFIG,\n useFactory: async (...args: unknown[]) => {\n const config = await options.useFactory!(...args);\n return this.normalizeConfig(config);\n },\n inject: options.inject || [],\n };\n }\n\n throw new Error('Invalid async options');\n }\n\n /**\n * 标准化配置,设置默认值\n */\n private static normalizeConfig(config: DataPaasConfig): DataPaasConfig {\n let roleSchema = '';\n let sslModeRequired = false;\n if (config.connectionString) {\n try {\n const url = new URL(config.connectionString);\n roleSchema = url.searchParams.get(\n 'schema'\n ) as string;\n sslModeRequired = url.searchParams.get('sslmode') === \"require\";\n } catch (err) {\n roleSchema = '';\n }\n }\n return {\n connectionString: config.connectionString,\n schema: config.schema,\n logger: config.logger || false,\n timeout: config.timeout || 10000,\n maxConnections: config.maxConnections || 1,\n idleTimeout: config.idleTimeout || 20,\n connectionTimeout: config.connectionTimeout || 10,\n ssl: config.ssl || (sslModeRequired ? 'require' : false),\n autoContext: config.autoContext == null ? true : !!config.autoContext,\n roleSchema,\n connectionTokenFilePath: config.connectionTokenFilePath,\n };\n }\n}\n","import { Injectable, OnModuleDestroy } from '@nestjs/common';\nimport { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { DrizzleDatabaseManager } from './drizzle-manager';\nimport { DatabaseConnectionInfo } from '../types';\n\n@Injectable()\nexport class DataPaasDatabaseService implements OnModuleDestroy {\n constructor(\n private readonly databaseManager: DrizzleDatabaseManager,\n ) {}\n\n /**\n * 获取 Drizzle 数据库实例\n * @returns Drizzle 数据库实例\n */\n getDatabase(): PostgresJsDatabase {\n return this.databaseManager.getDatabase();\n }\n\n /**\n * 获取数据库管理器\n * @returns 数据库管理器实例\n */\n getDatabaseManager(): DrizzleDatabaseManager {\n return this.databaseManager;\n }\n\n\n /**\n * 测试数据库连接\n * @returns 连接是否正常\n */\n async testConnection(): Promise<boolean> {\n return this.databaseManager.testConnection();\n }\n\n /**\n * 重新连接数据库\n * @returns 新的数据库实例\n */\n async reconnect(): Promise<PostgresJsDatabase> {\n return this.databaseManager.reconnect();\n }\n\n /**\n * 获取当前连接信息\n * @returns 当前连接信息\n */\n getConnectionInfo(): DatabaseConnectionInfo | null {\n return this.databaseManager.getConnectionInfo();\n }\n\n /**\n * 检查连接状态\n * @returns 是否已连接\n */\n isConnected(): boolean {\n return this.databaseManager.isDatabaseConnected();\n }\n\n /**\n * 开始事务\n * @param callback 事务回调函数\n * @returns 事务结果\n */\n async transaction<T>(callback: (tx: PostgresJsDatabase) => Promise<T>): Promise<T> {\n return this.databaseManager.transaction(callback);\n }\n\n /**\n * 获取连接池状态\n * @returns 连接池状态信息\n */\n getConnectionPoolStatus(): { isConnected: boolean; connectionInfo: DatabaseConnectionInfo | null } {\n return this.databaseManager.getConnectionPoolStatus();\n }\n\n\n /**\n * 模块销毁时断开数据库连接\n */\n async onModuleDestroy(): Promise<void> {\n await this.databaseManager.disconnect();\n }\n}\n","import { Injectable, NestMiddleware, Inject } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\nimport { runWithAuthContext } from './drizzle-auth-context';\nimport { DATAPAAS_CONFIG } from './const';\nimport { type DataPaasConfig } from '../types';\n\ninterface UserContext {\n userId?: string;\n tenantId?: number;\n appId?: string;\n // 用户角色 id 列表\n roles?: string[];\n // 是否为系统账号\n isSystemAccount: boolean;\n // 用户类型(如果有)\n userType?: string;\n}\n@Injectable()\nexport class SqlExecutionContextMiddleware implements NestMiddleware {\n constructor(\n @Inject(DATAPAAS_CONFIG)\n private readonly config: DataPaasConfig\n ) {}\n use(req: Request, _res: Response, next: NextFunction) {\n const roleSchema = this.config.roleSchema ?? '';\n const userContext =\n (req as unknown as { userContext: UserContext }).userContext || {};\n const roles = (userContext.roles || []).join(',');\n const userId = userContext.userId;\n const isSystemAccount = userContext.isSystemAccount;\n const userType = userContext.userType;\n const sqls = [\n // 设置用户 ID, 匿名用户时设置为空字符串\n userId\n ? `SET LOCAL app.user_id = '${userId}'`\n : `SET LOCAL app.user_id = ''`,\n // 系统用户设置为service_role, 登录用户设置为 authenticated_xxx 角色,匿名用户设置为 anon_xxx 角色\n isSystemAccount\n ? `SET LOCAL ROLE 'service_role_${roleSchema}'`\n : userId\n ? `SET LOCAL ROLE 'authenticated_${roleSchema}'`\n : `SET LOCAL ROLE 'anon_${roleSchema}'`,\n // 自定义角色\n roles\n ? `SET LOCAL app.role_ids = '${roles}'`\n : `SET LOCAL app.role_ids = ''`,\n // 用户类型\n userType\n ? `SET LOCAL app.user_type = '${userType}'`\n : `SET LOCAL app.user_type = ''`,\n ]\n .filter(Boolean)\n .join(';');\n // 需要确认角色设置是否对后续 SQL 执行有影响,预期是只影响当前会话\n runWithAuthContext({ preSql: sqls }, () => {\n next();\n });\n }\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\nexport interface AuthContext {\n preSql?: string;\n postSql?: string;\n}\n\nconst asyncLocalStorage = new AsyncLocalStorage<AuthContext>();\n\n/**\n * 获取当前请求的 RLS 上下文(preSql/postSql)\n */\nexport function getAuthContextStore(): AuthContext | undefined {\n return asyncLocalStorage.getStore();\n}\n\n/**\n * 在 RLS 上下文中执行回调,使得回调内的所有 drizzle 查询自动包裹 preSql/postSql\n */\nexport function runWithAuthContext<T>(store: AuthContext, fn: () => T): T {\n return asyncLocalStorage.run(store, fn);\n}\n","/**\n * 运行时 monkey-patch:拦截 drizzle-orm PostgresJsPreparedQuery.prototype.execute,\n * 在 AsyncLocalStorage 携带 preSql/postSql 时自动包裹事务。\n *\n * 替代 postinstall + patch-package 方案,兼容 pnpm content-addressable store。\n * 模式参考 Datadog/OpenTelemetry 对数据库驱动的运行时拦截。\n */\n\nimport { getAuthContextStore } from './drizzle-auth-context';\n\nlet patched = false;\n\nexport function applyDrizzleMonkeyPatch(): void {\n if (patched) return;\n\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const pgModule = require('drizzle-orm/postgres-js');\n const PreparedQuery = pgModule.PostgresJsPreparedQuery;\n\n if (!PreparedQuery?.prototype?.execute) {\n throw new Error(\n 'drizzle-orm PostgresJsPreparedQuery.prototype.execute 不存在,版本可能不兼容',\n );\n }\n\n const originalExecute = PreparedQuery.prototype.execute;\n\n // 注意:旧 patch 在 constructor 时从 AsyncLocalStorage 捕获 preSql/postSql,\n // 新方案在 execute 时读取。对于 RLS 场景这更正确——应使用实际执行查询\n // 的请求上下文,而非 PreparedQuery 创建时的上下文。\n // drizzle 每次查询创建新的 PreparedQuery 实例,不存在跨请求复用的情况。\n // 以下包裹逻辑在 test/drizzle-monkey-patch.spec.ts 中有对应副本用于单测,\n // 修改此处逻辑时需同步更新测试中的 applyPatch 函数。\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n PreparedQuery.prototype.execute = async function (this: any, ...args: any[]) {\n const authContext = getAuthContextStore();\n const preSql = authContext?.preSql;\n const postSql = authContext?.postSql;\n\n // 无 RLS 上下文时走原始路径,零开销\n if (!preSql && !postSql) {\n return originalExecute.apply(this, args);\n }\n\n const client = this.client;\n\n // client.begin 存在 → 当前不在事务中,需要开启事务\n if (typeof client.begin === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return client.begin(async (tx: any) => {\n // 临时将 this.client 替换为事务客户端,\n // 原始 execute 内部通过 `const { client } = this` 解构获取,\n // 所以替换后原始逻辑会自动使用事务客户端执行主查询\n const savedClient = this.client;\n this.client = tx;\n try {\n if (preSql) await tx.unsafe(preSql);\n const result = await originalExecute.apply(this, args);\n if (postSql) await tx.unsafe(postSql);\n return result;\n } finally {\n this.client = savedClient;\n }\n });\n }\n\n // 已在事务中(client 没有 begin),直接顺序执行\n if (preSql) await client.unsafe(preSql);\n const result = await originalExecute.apply(this, args);\n if (postSql) await client.unsafe(postSql);\n return result;\n };\n\n patched = true;\n}\n\nexport function isDrizzlePatched(): boolean {\n return patched;\n}\n","import { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { sql } from 'drizzle-orm';\n\nimport { DataPaasDatabaseService } from './database.service';\nimport { type DatabaseConnectionInfo } from '../types';\n\n/**\n * 数据库工具函数集合\n */\nexport class DatabaseUtils {\n /**\n * 安全执行数据库操作\n * @param databaseService 数据库服务实例\n * @param operation 数据库操作函数\n * @param fallback 失败时的回退值\n * @returns 操作结果或回退值\n */\n static async safeExecute<T>(\n databaseService: DataPaasDatabaseService,\n operation: (db: PostgresJsDatabase) => Promise<T>,\n fallback?: T\n ): Promise<T | undefined> {\n try {\n if (!databaseService.isConnected()) {\n throw new Error('Database not connected');\n }\n \n const db = databaseService.getDatabase();\n return await operation(db);\n } catch (error) {\n console.error('Database operation failed:', error);\n return fallback;\n }\n }\n\n /**\n * 批量执行数据库操作\n * @param databaseService 数据库服务实例\n * @param operations 操作数组\n * @param batchSize 批处理大小\n * @returns 所有操作的结果\n */\n static async batchExecute<T>(\n databaseService: DataPaasDatabaseService,\n operations: Array<(db: PostgresJsDatabase) => Promise<T>>,\n batchSize: number = 10\n ): Promise<T[]> {\n const results: T[] = [];\n const db = databaseService.getDatabase();\n\n for (let i = 0; i < operations.length; i += batchSize) {\n const batch = operations.slice(i, i + batchSize);\n const batchResults = await Promise.all(\n batch.map(operation => operation(db))\n );\n results.push(...batchResults);\n }\n\n return results;\n }\n\n /**\n * 执行带重试的数据库操作\n * @param databaseService 数据库服务实例\n * @param operation 数据库操作函数\n * @param maxRetries 最大重试次数\n * @param retryDelay 重试延迟(毫秒)\n * @returns 操作结果\n */\n static async executeWithRetry<T>(\n databaseService: DataPaasDatabaseService,\n operation: (db: PostgresJsDatabase) => Promise<T>,\n maxRetries: number = 3,\n retryDelay: number = 1000\n ): Promise<T> {\n let lastError: Error;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const db = databaseService.getDatabase();\n return await operation(db);\n } catch (error) {\n lastError = error as Error;\n \n if (attempt === maxRetries) {\n break;\n }\n\n console.warn(`Database operation failed (attempt ${attempt + 1}/${maxRetries + 1}):`, error);\n \n // 等待后重试\n await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1)));\n }\n }\n\n throw lastError!;\n }\n\n /**\n * 检查表是否存在\n * @param databaseService 数据库服务实例\n * @param tableName 表名\n * @returns 表是否存在\n */\n static async tableExists(\n db: PostgresJsDatabase,\n tableName: string\n ): Promise<boolean> {\n try {\n const result = await db.execute(\n sql`SELECT EXISTS (\n SELECT FROM information_schema.tables \n WHERE table_schema = 'public' \n AND table_name = ${tableName}\n )`\n );\n \n return result[0]?.exists as boolean || false;\n } catch (error) {\n console.error('Failed to check table existence:', error);\n return false;\n }\n }\n\n /**\n * 获取表结构信息\n * @param databaseService 数据库服务实例\n * @param tableName 表名\n * @returns 表结构信息\n */\n static async getTableStructure(\n db: PostgresJsDatabase,\n tableName: string\n ): Promise<Record<string, unknown>[]> {\n try {\n const result = await db.execute(\n sql`SELECT \n column_name,\n data_type,\n is_nullable,\n column_default,\n character_maximum_length\n FROM information_schema.columns \n WHERE table_schema = 'public' \n AND table_name = ${tableName}\n ORDER BY ordinal_position`,\n );\n \n return result;\n } catch (error) {\n console.error('Failed to get table structure:', error);\n return [];\n }\n }\n\n /**\n * 获取数据库版本信息\n * @param databaseService 数据库服务实例\n * @returns 数据库版本\n */\n static async getDatabaseVersion(\n db: PostgresJsDatabase\n ): Promise<string> {\n try {\n const result = await db.execute(sql`SELECT version()`);\n return result[0]?.version as string || 'Unknown';\n } catch (error) {\n console.error('Failed to get database version:', error);\n return 'Unknown';\n }\n }\n\n /**\n * 获取连接统计信息\n * @param databaseService 数据库服务实例\n * @returns 连接统计信息\n */\n static async getConnectionStats(\n db: PostgresJsDatabase\n ): Promise<Record<string, unknown>> {\n try {\n const result = await db.execute(sql`\n SELECT \n count(*) as total_connections,\n count(*) FILTER (WHERE state = 'active') as active_connections,\n count(*) FILTER (WHERE state = 'idle') as idle_connections\n FROM pg_stat_activity \n WHERE datname = current_database()\n `);\n \n return result[0] || {};\n } catch (error) {\n console.error('Failed to get connection stats:', error);\n return {};\n }\n }\n\n /**\n * 健康检查\n * @param databaseService 数据库服务实例\n * @returns 健康状态\n */\n static async healthCheck(\n databaseService: DataPaasDatabaseService\n ): Promise<{\n isHealthy: boolean;\n isConnected: boolean;\n version?: string;\n connectionInfo?: DatabaseConnectionInfo | null;\n error?: string;\n }> {\n try {\n const isConnected = databaseService.isConnected();\n const connectionInfo = databaseService.getConnectionInfo();\n\n const db = databaseService.getDatabase();\n if (!isConnected) {\n return {\n isHealthy: false,\n isConnected: false,\n error: 'Database not connected'\n };\n }\n\n const version = await this.getDatabaseVersion(db);\n \n return {\n isHealthy: true,\n isConnected: true,\n version,\n connectionInfo\n };\n } catch (error) {\n return {\n isHealthy: false,\n isConnected: false,\n error: error instanceof Error ? error.message : 'Unknown error'\n };\n }\n }\n}\n"],"mappings":"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","names":["Injectable","Inject","drizzle","sql","postgres","readFile","watchFile","unwatchFile","DRIZZLE_DATABASE","DATAPAAS_CONFIG","DATAPAAS_LOGGER_SERVICE","Symbol","RequestContextService","ObservableService","parseConnectionInfo","connectionString","config","maxConnections","idleTimeout","connectionTimeout","ssl","logger","parsedConfigFromUrl","URL","temp","searchParams","delete","toString","error","Error","username","password","hostname","port","pathname","connectionConfig","decodeURIComponent","host","parseInt","max","idle_timeout","connect_timeout","onnotice","console","log","undefined","Logger","createTimer","nestLogger","Logger","genPostgresProxy","sql","observable","requestContext","wrapQuery","query","then","Proxy","get","target","prop","receiver","onfulfilled","onrejected","sqlStr","strings","Array","span","startTrace","getContext","requestRootSpan","spanContext","traceId","spanId","logContext","source_type","paas_attributes_module","paas_parent_span_context","setAttribute","elapsed","createTimer","result","isSetRoleSql","command","duration","parsedParams","JSON","stringify","operation","statement","string","duration_ms","result_count","length","error_message","log","dropTrace","end","error","Error","message","String","Promise","reject","value","Reflect","bind","apply","thisArg","argArray","arg","cb","call","tx","queryStr","args","options","DrizzleDatabaseManager","db","sqlClient","connectionOpPromise","currentConnectionInfo","isConnected","skipInitDbConnection","currentToken","currentResolvedConnectionString","tokenWatchFilePath","tokenWatchDebounceTimer","refreshingByTokenChange","sqlClientShutdownTimeoutSeconds","config","observable","requestContext","process","env","DEPRECATED_SKIP_INIT_DB_CONNECTION","onModuleInit","initialize","startTokenWatcher","onModuleDestroy","stopTokenWatcher","disconnect","name","runWithConnectionLock","token","readConnectionTokenFromFile","resolvedConnectionString","appendTokenToConnectionString","connectionString","nextState","createConnectedState","applyConnectedState","error","Error","message","getDatabase","testConnection","execute","sql","console","reconnect","reconnectInternal","shouldStopWatcher","pendingOp","Promise","allSettled","shutdownSqlClientSafely","getConnectionInfo","isDatabaseConnected","transaction","callback","getConnectionPoolStatus","connectionInfo","tokenFilePath","connectionTokenFilePath","raw","readFile","trimmed","trim","length","url","URL","currentOptions","searchParams","get","optionsWithoutToken","replace","tokenOption","nextOptions","filter","Boolean","join","set","toString","connectionConfig","parseConnectionInfo","baseSql","postgres","sqlProxy","genPostgresProxy","drizzle","schema","logger","ssl","connectionTimeout","connect_timeout","idleTimeout","idle_timeout","maxConnections","max","nextResolvedConnectionString","nextToken","reconnectWithResolvedConnectionString","operation","resolvedToken","previousSqlClient","client","errorPrefix","end","timeout","startTokenWatchFileFallback","unwatchFile","watchFile","interval","scheduleTokenRefresh","clearTimeout","setTimeout","refreshConnectionIfTokenChanged","sameToken","sameConnectionString","Module","Injectable","DataPaasDatabaseService","databaseManager","getDatabase","getDatabaseManager","testConnection","reconnect","getConnectionInfo","isConnected","isDatabaseConnected","transaction","callback","getConnectionPoolStatus","onModuleDestroy","disconnect","Injectable","Inject","AsyncLocalStorage","asyncLocalStorage","AsyncLocalStorage","getAuthContextStore","getStore","runWithAuthContext","store","fn","run","SqlExecutionContextMiddleware","config","use","req","_res","next","roleSchema","userContext","roles","join","userId","isSystemAccount","userType","sqls","filter","Boolean","runWithAuthContext","preSql","RequestContextService","OBSERVABLE_SERVICE","Observable","patched","applyDrizzleMonkeyPatch","pgModule","require","PreparedQuery","PostgresJsPreparedQuery","prototype","execute","Error","originalExecute","args","authContext","getAuthContextStore","preSql","postSql","apply","client","begin","tx","savedClient","unsafe","result","applyDrizzleMonkeyPatch","DataPaasModule","configure","_consumer","forRoot","options","roleSchema","sslModeRequired","connectionString","url","URL","searchParams","get","err","config","schema","logger","timeout","maxConnections","idleTimeout","connectionTimeout","ssl","autoContext","connectionTokenFilePath","providers","provide","DATAPAAS_CONFIG","useValue","DrizzleDatabaseManager","useClass","DataPaasDatabaseService","SqlExecutionContextMiddleware","DRIZZLE_DATABASE","useFactory","manager","initialize","Proxy","_target","prop","receiver","db","getDatabase","value","Reflect","bind","inject","module","exports","global","forRootAsync","asyncProviders","createAsyncProviders","imports","createAsyncConfigProvider","Error","args","normalizeConfig","DATAPAAS_LOGGER_SERVICE","observable","rtx","Observable","token","OBSERVABLE_SERVICE","optional","RequestContextService","sql","DatabaseUtils","safeExecute","databaseService","operation","fallback","isConnected","Error","db","getDatabase","error","console","batchExecute","operations","batchSize","results","i","length","batch","slice","batchResults","Promise","all","map","push","executeWithRetry","maxRetries","retryDelay","lastError","attempt","warn","resolve","setTimeout","tableExists","tableName","result","execute","sql","exists","getTableStructure","getDatabaseVersion","version","getConnectionStats","healthCheck","connectionInfo","getConnectionInfo","isHealthy","message"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/nestjs-datapaas",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "FullStack Nestjs DataPaas",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,9 +19,7 @@
19
19
  "node": ">=18.0.0"
20
20
  },
21
21
  "files": [
22
- "dist",
23
- "patches",
24
- "scripts"
22
+ "dist"
25
23
  ],
26
24
  "exports": {
27
25
  ".": {
@@ -32,8 +30,6 @@
32
30
  },
33
31
  "scripts": {
34
32
  "build": "tsup",
35
- "postinstall": "npm run apply-patch",
36
- "apply-patch": "node scripts/apply-patch.js",
37
33
  "dev": "tsup --watch",
38
34
  "clean": "rm -rf dist",
39
35
  "lint": "eslint src/**/*.ts",
@@ -44,7 +40,6 @@
44
40
  },
45
41
  "dependencies": {
46
42
  "@types/pg": "^8.10.9",
47
- "patch-package": "^8.0.0",
48
43
  "postgres": "^3.4.3",
49
44
  "reflect-metadata": "^0.1.13"
50
45
  },
@@ -1,178 +0,0 @@
1
- diff --git a/node_modules/drizzle-orm/postgres-js/session.cjs b/node_modules/drizzle-orm/postgres-js/session.cjs
2
- index dac222a..7708081 100644
3
- --- a/node_modules/drizzle-orm/postgres-js/session.cjs
4
- +++ b/node_modules/drizzle-orm/postgres-js/session.cjs
5
- @@ -20,9 +20,12 @@ var session_exports = {};
6
- __export(session_exports, {
7
- PostgresJsPreparedQuery: () => PostgresJsPreparedQuery,
8
- PostgresJsSession: () => PostgresJsSession,
9
- - PostgresJsTransaction: () => PostgresJsTransaction
10
- + PostgresJsTransaction: () => PostgresJsTransaction,
11
- + getAuthContextStore: () => getAuthContextStore,
12
- + runWithAuthContext: () => runWithAuthContext,
13
- });
14
- module.exports = __toCommonJS(session_exports);
15
- +var { AsyncLocalStorage } = require('node:async_hooks');
16
- var import_core = require("../cache/core/index.cjs");
17
- var import_entity = require("../entity.cjs");
18
- var import_logger = require("../logger.cjs");
19
- @@ -31,10 +34,24 @@ var import_session = require("../pg-core/session.cjs");
20
- var import_sql = require("../sql/sql.cjs");
21
- var import_tracing = require("../tracing.cjs");
22
- var import_utils = require("../utils.cjs");
23
- +
24
- +const asyncLocalStorage = new AsyncLocalStorage();
25
- +
26
- +function getAuthContextStore() {
27
- + return asyncLocalStorage.getStore();
28
- +}
29
- +
30
- +function runWithAuthContext(store, fn) {
31
- + return asyncLocalStorage.run(store, fn);
32
- +}
33
- +
34
- class PostgresJsPreparedQuery extends import_session.PgPreparedQuery {
35
- constructor(client, queryString, params, logger, cache, queryMetadata, cacheConfig, fields, _isResponseInArrayMode, customResultMapper) {
36
- super({ sql: queryString, params }, cache, queryMetadata, cacheConfig);
37
- this.client = client;
38
- + const authContextStore = getAuthContextStore();
39
- + this.preSql = authContextStore?.preSql;
40
- + this.postSql = authContextStore?.postSql;
41
- this.queryString = queryString;
42
- this.params = params;
43
- this.logger = logger;
44
- @@ -51,21 +68,103 @@ class PostgresJsPreparedQuery extends import_session.PgPreparedQuery {
45
- "drizzle.query.params": JSON.stringify(params)
46
- });
47
- this.logger.logQuery(this.queryString, params);
48
- - const { fields, queryString: query, client, joinsNotNullableMap, customResultMapper } = this;
49
- + const { fields, client, joinsNotNullableMap, customResultMapper, preSql, postSql } = this;
50
- +
51
- + // 在同一事务中依次执行:preSql(可选)→ 主查询 → postSql(可选)
52
- + if (preSql || postSql) {
53
- + return import_tracing.tracer.startActiveSpan("drizzle.driver.execute.transactional", async () => {
54
- + // 若当前不在事务中(client 有 begin 方法),则开启事务;否则复用当前事务
55
- + if (typeof client.begin === 'function') {
56
- + return client.begin(async (tx) => {
57
- + // 若存在 preSql,则执行
58
- + if (preSql) {
59
- + await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.pre", () => tx.unsafe(preSql));
60
- + }
61
- +
62
- + // 执行主查询
63
- + let result;
64
- + if (!fields && !customResultMapper) {
65
- + result = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.main", () => {
66
- + return this.queryWithCache(this.queryString, params, async () => {
67
- + return await tx.unsafe(this.queryString, params);
68
- + });
69
- + });
70
- + } else {
71
- + const rows = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.main", () => {
72
- + span?.setAttributes({
73
- + "drizzle.query.text": this.queryString,
74
- + "drizzle.query.params": JSON.stringify(params)
75
- + });
76
- + return this.queryWithCache(this.queryString, params, async () => {
77
- + return await tx.unsafe(this.queryString, params).values();
78
- + });;
79
- + });
80
- + result = import_tracing.tracer.startActiveSpan("drizzle.mapResponse", () => {
81
- + return customResultMapper ? customResultMapper(rows) : rows.map((row) => (0, import_utils.mapResultRow)(fields, row, joinsNotNullableMap));
82
- + });
83
- + }
84
- +
85
- + // 若存在 postSql,则执行
86
- + if (postSql) {
87
- + await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.post", () => tx.unsafe(postSql));
88
- + }
89
- +
90
- + return result;
91
- + });
92
- + } else {
93
- + // 已在事务中,直接执行
94
- + // 若存在 preSql,则执行
95
- + if (preSql) {
96
- + await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.pre", () => client.unsafe(preSql));
97
- + }
98
- +
99
- + // 执行主查询
100
- + let result;
101
- + if (!fields && !customResultMapper) {
102
- + result = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.main", () => {
103
- + return this.queryWithCache(this.queryString, params, async () => {
104
- + return await client.unsafe(this.queryString, params);
105
- + });
106
- + });
107
- + } else {
108
- + const rows = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.main", () => {
109
- + span?.setAttributes({
110
- + "drizzle.query.text": this.queryString,
111
- + "drizzle.query.params": JSON.stringify(params)
112
- + });
113
- + return this.queryWithCache(this.queryString, params, async () => {
114
- + return await client.unsafe(this.queryString, params).values();
115
- + });
116
- + });
117
- + result = import_tracing.tracer.startActiveSpan("drizzle.mapResponse", () => {
118
- + return customResultMapper ? customResultMapper(rows) : rows.map((row) => (0, import_utils.mapResultRow)(fields, row, joinsNotNullableMap));
119
- + });
120
- + }
121
- +
122
- + // 若存在 postSql,则执行
123
- + if (postSql) {
124
- + await import_tracing.tracer.startActiveSpan("drizzle.driver.execute.post", () => client.unsafe(postSql));
125
- + }
126
- +
127
- + return result;
128
- + }
129
- + });
130
- + }
131
- + // 单语句执行(原始逻辑)
132
- if (!fields && !customResultMapper) {
133
- return import_tracing.tracer.startActiveSpan("drizzle.driver.execute", () => {
134
- - return this.queryWithCache(query, params, async () => {
135
- - return await client.unsafe(query, params);
136
- + return this.queryWithCache(this.queryString, params, async () => {
137
- + return await client.unsafe(this.queryString, params);
138
- });
139
- });
140
- }
141
- const rows = await import_tracing.tracer.startActiveSpan("drizzle.driver.execute", () => {
142
- span?.setAttributes({
143
- - "drizzle.query.text": query,
144
- + "drizzle.query.text": this.queryString,
145
- "drizzle.query.params": JSON.stringify(params)
146
- });
147
- - return this.queryWithCache(query, params, async () => {
148
- - return await client.unsafe(query, params).values();
149
- + return this.queryWithCache(this.queryString, params, async () => {
150
- + return await client.unsafe(this.queryString, params).values();
151
- });
152
- });
153
- return import_tracing.tracer.startActiveSpan("drizzle.mapResponse", () => {
154
- @@ -169,6 +268,8 @@ class PostgresJsTransaction extends import_pg_core.PgTransaction {
155
- 0 && (module.exports = {
156
- PostgresJsPreparedQuery,
157
- PostgresJsSession,
158
- - PostgresJsTransaction
159
- + PostgresJsTransaction,
160
- + getAuthContextStore,
161
- + runWithAuthContext,
162
- });
163
- //# sourceMappingURL=session.cjs.map
164
-
165
- diff --git a/node_modules/drizzle-orm/postgres-js/session.d.ts b/node_modules/drizzle-orm/postgres-js/session.d.ts
166
- index bccaa53..b07425b 100644
167
- --- a/node_modules/drizzle-orm/postgres-js/session.d.ts
168
- +++ b/node_modules/drizzle-orm/postgres-js/session.d.ts
169
- @@ -58,3 +58,8 @@ export declare class PostgresJsTransaction<TFullSchema extends Record<string, un
170
- export interface PostgresJsQueryResultHKT extends PgQueryResultHKT {
171
- type: RowList<Assume<this['row'], Row>[]>;
172
- }
173
- +export interface RequestSessionContext {
174
- + preSql?: string;
175
- +}
176
- +export declare function getAuthContextStore(): RequestSessionContext | undefined;
177
- +export declare function runWithAuthContext(store: RequestSessionContext, fn: () => void): void;
178
-
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env node
2
- import { execSync } from "child_process";
3
- import path from "path";
4
- import { fileURLToPath } from "url";
5
-
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- const sdkRoot = path.resolve(__dirname, "..");
8
- const hostRoot = path.resolve(sdkRoot, "../../.."); // npm、yarn 场景下,安装在 node_modules/@lark-apaas/nestjs-datapaas
9
-
10
- console.log(`[patch] Applying drizzle-orm patch at host: ${hostRoot}`);
11
-
12
- try {
13
- execSync(
14
- "npx patch-package --patch-dir node_modules/@lark-apaas/nestjs-datapaas/patches",
15
- { stdio: "inherit", cwd: hostRoot }
16
- );
17
- console.log("[patch] drizzle-orm patched ✅");
18
- } catch (err) {
19
- console.error("[patch] Failed to apply drizzle-orm patch ❌", err);
20
- process.exit(1);
21
- }