Files
BYW-VoiceHub/server/utils/database-manager.ts
T
Henry_Du 4ce4649aaf
Build VoiceHub FPK / build-fpk (push) Failing after 56m5s
Build and Push Docker Image / build-and-push (push) Canceled after 1h9m35s
初次提交
2026-09-06 08:19:02 +08:00

201 lines
5.2 KiB
TypeScript

import { db, getConnectionStatus } from '~/drizzle/db'
import { sql } from 'drizzle-orm'
/**
* 数据库管理器
*/
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
}
}
}
// 导出单例实例
export const databaseManager = DatabaseManager.getInstance()