241 lines
6.5 KiB
TypeScript
241 lines
6.5 KiB
TypeScript
/**
|
||
* 文件: src/utils/ConnectDB.ts
|
||
* 作者: 阿瑞
|
||
* 功能: MongoDB数据库连接管理工具 - 高阶函数装饰器
|
||
* 版本: v2.0.0
|
||
* @description 为Next.js API路由提供数据库连接管理,支持连接复用、错误重试、连接超时等功能
|
||
*/
|
||
|
||
import mongoose from 'mongoose';
|
||
import { NextApiRequest, NextApiResponse } from 'next';
|
||
|
||
// ===========================================
|
||
// 类型定义区域
|
||
// ===========================================
|
||
|
||
/**
|
||
* 数据库连接配置接口
|
||
*/
|
||
interface DatabaseConfig {
|
||
maxRetries?: number; // 最大重试次数
|
||
retryDelay?: number; // 重试延迟(毫秒)
|
||
connectTimeout?: number; // 连接超时(毫秒)
|
||
enableLogging?: boolean; // 是否启用日志
|
||
}
|
||
|
||
/**
|
||
* API处理函数类型定义
|
||
*/
|
||
type ApiHandler = (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
|
||
|
||
// ===========================================
|
||
// 配置常量区域
|
||
// ===========================================
|
||
|
||
/**
|
||
* 默认数据库连接配置
|
||
*/
|
||
const DEFAULT_CONFIG: Required<DatabaseConfig> = {
|
||
maxRetries: 3, // 默认重试3次
|
||
retryDelay: 1000, // 默认延迟1秒
|
||
connectTimeout: 10000, // 默认10秒超时
|
||
enableLogging: process.env.NODE_ENV === 'development', // 开发环境启用日志
|
||
};
|
||
|
||
/**
|
||
* Mongoose连接状态枚举
|
||
* 0 = 断开连接, 1 = 已连接, 2 = 正在连接, 3 = 正在断开连接
|
||
*/
|
||
const CONNECTION_STATES = {
|
||
DISCONNECTED: 0,
|
||
CONNECTED: 1,
|
||
CONNECTING: 2,
|
||
DISCONNECTING: 3,
|
||
} as const;
|
||
|
||
// ===========================================
|
||
// 工具函数区域
|
||
// ===========================================
|
||
|
||
/**
|
||
* 安全的日志记录函数
|
||
* @param message 日志消息
|
||
* @param data 可选的数据对象
|
||
* @param isError 是否为错误日志
|
||
*/
|
||
const safeLog = (message: string, data?: any, isError = false): void => {
|
||
if (DEFAULT_CONFIG.enableLogging) {
|
||
if (isError) {
|
||
console.error(`[DB Error] ${message}`, data);
|
||
} else {
|
||
console.log(`[DB Info] ${message}`, data);
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 延迟函数
|
||
* @param ms 延迟毫秒数
|
||
*/
|
||
const delay = (ms: number): Promise<void> =>
|
||
new Promise(resolve => setTimeout(resolve, ms));
|
||
|
||
/**
|
||
* 检查数据库连接状态
|
||
* @returns 是否已连接
|
||
*/
|
||
const isDbConnected = (): boolean => {
|
||
const connection = mongoose.connections[0];
|
||
return connection && connection.readyState === CONNECTION_STATES.CONNECTED;
|
||
};
|
||
|
||
/**
|
||
* 获取数据库连接URI
|
||
* @returns 数据库URI或null
|
||
*/
|
||
const getDatabaseUri = (): string | null => {
|
||
const dbUri = process.env.MONGODB_URI;
|
||
if (!dbUri) {
|
||
safeLog('数据库URI未在环境变量中配置', null, true);
|
||
return null;
|
||
}
|
||
return dbUri;
|
||
};
|
||
|
||
// ===========================================
|
||
// 核心连接功能区域
|
||
// ===========================================
|
||
|
||
/**
|
||
* 执行数据库连接(带重试机制)
|
||
* @param dbUri 数据库连接URI
|
||
* @param config 连接配置
|
||
* @returns Promise<boolean> 连接是否成功
|
||
*/
|
||
const connectWithRetry = async (
|
||
dbUri: string,
|
||
config: Required<DatabaseConfig>
|
||
): Promise<boolean> => {
|
||
for (let attempt = 1; attempt <= config.maxRetries; attempt++) {
|
||
try {
|
||
safeLog(`尝试连接数据库 (第${attempt}/${config.maxRetries}次)`);
|
||
|
||
// 设置mongoose连接选项
|
||
const connectOptions = {
|
||
serverSelectionTimeoutMS: config.connectTimeout,
|
||
socketTimeoutMS: config.connectTimeout,
|
||
family: 4, // 使用IPv4
|
||
maxPoolSize: 10, // 连接池大小
|
||
retryWrites: true,
|
||
w: 'majority' as const, // 写关注级别 - 修复类型错误
|
||
};
|
||
|
||
await mongoose.connect(dbUri, connectOptions);
|
||
safeLog('数据库连接成功');
|
||
return true;
|
||
|
||
} catch (error) {
|
||
const isLastAttempt = attempt === config.maxRetries;
|
||
safeLog(
|
||
`数据库连接失败 (第${attempt}/${config.maxRetries}次)`,
|
||
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
||
true
|
||
);
|
||
|
||
if (!isLastAttempt) {
|
||
safeLog(`等待${config.retryDelay}ms后重试...`);
|
||
await delay(config.retryDelay);
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
// ===========================================
|
||
// 主要导出功能区域
|
||
// ===========================================
|
||
|
||
/**
|
||
* 数据库连接高阶函数装饰器
|
||
* @description 为API路由提供数据库连接管理,包含连接复用、错误重试、超时处理等功能
|
||
* @param handler API处理函数
|
||
* @param config 可选的数据库连接配置
|
||
* @returns 包装后的API处理函数
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* export default connectDB(async (req, res) => {
|
||
* // 你的API逻辑
|
||
* });
|
||
* ```
|
||
*/
|
||
const connectDB = (
|
||
handler: ApiHandler,
|
||
config: DatabaseConfig = {}
|
||
) => async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
|
||
|
||
// 合并配置
|
||
const finalConfig: Required<DatabaseConfig> = { ...DEFAULT_CONFIG, ...config };
|
||
|
||
try {
|
||
// 检查现有连接状态
|
||
if (isDbConnected()) {
|
||
safeLog('使用现有数据库连接');
|
||
return await handler(req, res);
|
||
}
|
||
|
||
// 获取数据库URI
|
||
const dbUri = getDatabaseUri();
|
||
if (!dbUri) {
|
||
return res.status(500).json({
|
||
error: 'Database configuration error',
|
||
message: process.env.NODE_ENV === 'development'
|
||
? 'MONGODB_URI not found in environment variables'
|
||
: 'Database configuration is missing'
|
||
});
|
||
}
|
||
|
||
// 尝试连接数据库
|
||
const connected = await connectWithRetry(dbUri, finalConfig);
|
||
|
||
if (!connected) {
|
||
safeLog('所有数据库连接尝试均失败', null, true);
|
||
return res.status(500).json({
|
||
error: 'Database connection failed',
|
||
message: 'Unable to establish database connection after multiple attempts'
|
||
});
|
||
}
|
||
|
||
// 成功连接后执行API处理函数
|
||
return await handler(req, res);
|
||
|
||
} catch (error) {
|
||
// 全局错误捕获
|
||
safeLog('数据库连接装饰器发生未预期错误', error, true);
|
||
|
||
return res.status(500).json({
|
||
error: 'Internal server error',
|
||
message: process.env.NODE_ENV === 'development'
|
||
? (error instanceof Error ? error.message : 'Unknown error occurred')
|
||
: 'An unexpected error occurred'
|
||
});
|
||
}
|
||
};
|
||
|
||
// ===========================================
|
||
// 导出区域
|
||
// ===========================================
|
||
|
||
export default connectDB;
|
||
|
||
/**
|
||
* 导出配置常量供外部使用
|
||
*/
|
||
export { DEFAULT_CONFIG, CONNECTION_STATES };
|
||
|
||
/**
|
||
* 导出类型定义供外部使用
|
||
*/
|
||
export type { DatabaseConfig, ApiHandler };
|