DatabaseManager 类从未定义 getConnectionPoolStatus / getPerformanceMetrics, 但有 4 个接口直接调用它们,导致运行时 TypeError 并返回 500: GET /api/admin/database/pool-status GET /api/admin/database/performance GET /api/system/status POST /api/system/reconnect 两个函数本就实现在 utils/database-health.ts(getConnectionPoolStatus / getDatabaseMetrics),这里补上委托方法,调用点无需改动。 验证:用项目的 .nuxt/tsconfig.server.json 做 A/B 类型检查, 改动前有 4 处 'Property does not exist on type DatabaseManager', 改动后全部消失(总报错数 682 -> 678)。
226 lines
6.1 KiB
TypeScript
226 lines
6.1 KiB
TypeScript
import { db, getConnectionStatus } from '~/drizzle/db'
|
||
import { sql } from 'drizzle-orm'
|
||
import { getConnectionPoolStatus, getDatabaseMetrics } from './database-health'
|
||
|
||
/**
|
||
* 数据库管理器
|
||
*/
|
||
export class DatabaseManager {
|
||
private static instance: DatabaseManager
|
||
private healthCheckCache: { status: boolean; timestamp: number; latency: number } | null = null
|
||
private readonly CACHE_TTL = 30000 // 30秒缓存
|
||
|
||
private constructor() {}
|
||
|
||
static getInstance(): DatabaseManager {
|
||
if (!DatabaseManager.instance) {
|
||
DatabaseManager.instance = new DatabaseManager()
|
||
}
|
||
return DatabaseManager.instance
|
||
}
|
||
|
||
/**
|
||
* 数据库健康检查(带缓存)
|
||
*/
|
||
async healthCheck(): Promise<{
|
||
status: boolean
|
||
latency: number
|
||
timestamp: Date
|
||
connectionStatus: string
|
||
error?: string
|
||
}> {
|
||
const now = Date.now()
|
||
|
||
// 检查缓存
|
||
if (this.healthCheckCache && now - this.healthCheckCache.timestamp < this.CACHE_TTL) {
|
||
return {
|
||
status: this.healthCheckCache.status,
|
||
latency: this.healthCheckCache.latency,
|
||
timestamp: new Date(this.healthCheckCache.timestamp),
|
||
connectionStatus: 'cached'
|
||
}
|
||
}
|
||
|
||
const startTime = Date.now()
|
||
try {
|
||
// 获取连接状态
|
||
const connectionStatus = await getConnectionStatus()
|
||
|
||
// 执行简单查询测试连接
|
||
await db.execute(sql`SELECT 1 as health_check`)
|
||
|
||
const latency = Date.now() - startTime
|
||
const result = {
|
||
status: true,
|
||
latency,
|
||
timestamp: new Date(),
|
||
connectionStatus: connectionStatus.status
|
||
}
|
||
|
||
// 更新缓存
|
||
this.healthCheckCache = {
|
||
status: true,
|
||
timestamp: now,
|
||
latency
|
||
}
|
||
|
||
return result
|
||
} catch (error) {
|
||
const latency = Date.now() - startTime
|
||
const result = {
|
||
status: false,
|
||
latency,
|
||
timestamp: new Date(),
|
||
connectionStatus: 'error',
|
||
error: error instanceof Error ? error.message : 'Unknown error'
|
||
}
|
||
|
||
// 更新缓存
|
||
this.healthCheckCache = {
|
||
status: false,
|
||
timestamp: now,
|
||
latency
|
||
}
|
||
|
||
return result
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取基础数据库信息 - 简化版本适配 Neon Database
|
||
*/
|
||
async getBasicMetrics(): Promise<{
|
||
databaseSize: string
|
||
activeConnections: number
|
||
serverless: boolean
|
||
}> {
|
||
try {
|
||
// 获取数据库大小
|
||
const sizeResult = await db.execute(sql`
|
||
SELECT pg_size_pretty(pg_database_size(current_database())) as database_size
|
||
`)
|
||
|
||
// 获取当前连接数(简化版)
|
||
const connectionStats = await db.execute(sql`
|
||
SELECT count(*) as active_connections
|
||
FROM pg_stat_activity
|
||
WHERE datname = current_database() AND state = 'active'
|
||
`)
|
||
|
||
const sizeRow = sizeResult[0] as any
|
||
const connectionRow = connectionStats[0] as any
|
||
|
||
return {
|
||
databaseSize: sizeRow?.database_size || 'Unknown',
|
||
activeConnections: parseInt(connectionRow?.active_connections) || 0,
|
||
serverless: true // Neon Database 是无服务器架构
|
||
}
|
||
} catch (error) {
|
||
console.error('[DatabaseManager] Failed to get basic metrics:', error)
|
||
throw new Error(
|
||
`Failed to retrieve basic metrics: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||
)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取连接状态 - 适配 Neon Database 无服务器架构
|
||
*/
|
||
async getConnectionStatus(): Promise<{
|
||
status: string
|
||
activeConnections: number
|
||
serverlessMode: boolean
|
||
autoSuspend: boolean
|
||
}> {
|
||
try {
|
||
const connectionStatus = await getConnectionStatus()
|
||
|
||
// 获取当前活跃连接数
|
||
const connectionStats = await db.execute(sql`
|
||
SELECT count(*) as active_connections
|
||
FROM pg_stat_activity
|
||
WHERE datname = current_database() AND state = 'active'
|
||
`)
|
||
|
||
const connRow = connectionStats[0] as any
|
||
|
||
return {
|
||
status: connectionStatus.status,
|
||
activeConnections: parseInt(connRow?.active_connections) || 0,
|
||
serverlessMode: true, // Neon Database 是无服务器架构
|
||
autoSuspend: true // 支持自动暂停
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to get connection status:', error)
|
||
throw new Error('Failed to retrieve connection status')
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量清理过期会话
|
||
*/
|
||
async cleanupExpiredSessions(): Promise<number> {
|
||
try {
|
||
const result = await db.execute(sql`
|
||
DELETE FROM session
|
||
WHERE expires_at < NOW()
|
||
`)
|
||
|
||
// postgres-js returns count in the result array object properties
|
||
return (result as any).count || 0
|
||
} catch (error) {
|
||
console.error('Failed to cleanup expired sessions:', error)
|
||
throw new Error('Failed to cleanup expired sessions')
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清除健康检查缓存
|
||
*/
|
||
clearHealthCheckCache(): void {
|
||
this.healthCheckCache = null
|
||
}
|
||
|
||
/**
|
||
* 获取数据库管理器状态
|
||
*/
|
||
getManagerStatus(): {
|
||
cacheEnabled: boolean
|
||
cacheTTL: number
|
||
lastHealthCheck: Date | null
|
||
} {
|
||
return {
|
||
cacheEnabled: true,
|
||
cacheTTL: this.CACHE_TTL,
|
||
lastHealthCheck: this.healthCheckCache ? new Date(this.healthCheckCache.timestamp) : null
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 连接池状态(最大/活跃/总连接数、使用率)。
|
||
*
|
||
* 实现位于 utils/database-health.ts。此前有 3 个接口直接调用
|
||
* databaseManager.getConnectionPoolStatus(),但本类从未定义该方法,
|
||
* 导致以下接口全部 500(TypeError: not a function):
|
||
* GET /api/admin/database/pool-status
|
||
* POST /api/system/reconnect
|
||
* GET /api/system/status
|
||
*/
|
||
async getConnectionPoolStatus() {
|
||
return getConnectionPoolStatus()
|
||
}
|
||
|
||
/**
|
||
* 数据库性能指标。
|
||
*
|
||
* 同上:GET /api/admin/database/performance 调用 databaseManager.getPerformanceMetrics(),
|
||
* 而本类没有该方法,接口一直 500。实现位于 utils/database-health.ts 的 getDatabaseMetrics。
|
||
*/
|
||
async getPerformanceMetrics() {
|
||
return getDatabaseMetrics()
|
||
}
|
||
}
|
||
|
||
// 导出单例实例
|
||
export const databaseManager = DatabaseManager.getInstance()
|