修復 too many connection 問題

This commit is contained in:
2025-09-21 02:46:16 +08:00
parent a36ab3c98d
commit 808d5bb52c
36 changed files with 5582 additions and 249 deletions

39
lib/app-initializer.ts Normal file
View File

@@ -0,0 +1,39 @@
// =====================================================
// 應用程式初始化器
// =====================================================
import { dbShutdownManager } from './database-shutdown-manager';
import { startConnectionMonitoring } from './database-middleware';
import { smartPool } from './smart-connection-pool';
// 應用程式初始化
export function initializeApp() {
console.log('🚀 正在初始化應用程式...');
try {
// 初始化資料庫關閉管理器
console.log('📋 初始化資料庫關閉管理器...');
const shutdownStatus = dbShutdownManager.getShutdownStatus();
console.log('✅ 資料庫關閉管理器已初始化:', shutdownStatus);
// 啟動資料庫連線監控
console.log('📊 啟動資料庫連線監控...');
startConnectionMonitoring();
// 初始化智能連線池
console.log('🧠 初始化智能連線池...');
const poolStats = smartPool.getConnectionStats();
console.log('✅ 智能連線池已初始化:', poolStats);
console.log('✅ 應用程式初始化完成');
} catch (error) {
console.error('❌ 應用程式初始化失敗:', error);
}
}
// 在模組載入時自動初始化
if (typeof window === 'undefined') {
// 只在伺服器端執行
initializeApp();
}

View File

@@ -0,0 +1,231 @@
// =====================================================
// 客戶端連線清理腳本
// =====================================================
export class ClientConnectionCleanup {
private static instance: ClientConnectionCleanup;
private isCleaning = false;
private clientId: string;
private constructor() {
this.clientId = this.generateClientId();
this.setupEventListeners();
}
public static getInstance(): ClientConnectionCleanup {
if (!ClientConnectionCleanup.instance) {
ClientConnectionCleanup.instance = new ClientConnectionCleanup();
}
return ClientConnectionCleanup.instance;
}
// 生成客戶端唯一標識符
private generateClientId(): string {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2);
return `client_${timestamp}_${random}`;
}
// 設置事件監聽器
private setupEventListeners() {
// 確保在瀏覽器環境中執行
if (typeof window === 'undefined') {
console.log('🖥️ 客戶端連線清理跳過(服務端環境)');
return;
}
// 頁面關閉前清理
window.addEventListener('beforeunload', () => {
this.cleanupOnUnload();
});
// 頁面隱藏時清理
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
this.cleanupOnHidden();
}
});
// 頁面卸載時清理
window.addEventListener('unload', () => {
this.cleanupOnUnload();
});
// 定期清理每5分鐘
setInterval(() => {
this.periodicCleanup();
}, 5 * 60 * 1000);
console.log('🖥️ 客戶端連線清理已啟動');
}
// 頁面關閉前清理
private async cleanupOnUnload() {
if (this.isCleaning) return;
// 確保在瀏覽器環境中執行
if (typeof window === 'undefined') return;
this.isCleaning = true;
console.log('🔄 頁面關閉前清理連線...');
try {
// 使用 sendBeacon 確保請求能夠發送
const data = JSON.stringify({
action: 'cleanup-current',
clientId: this.clientId,
timestamp: Date.now()
});
// 嘗試使用 sendBeacon
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/ip-cleanup', data);
console.log('✅ 使用 sendBeacon 發送清理請求');
} else {
// 備用方案:使用 fetch
await fetch('/api/ip-cleanup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: data,
keepalive: true
});
console.log('✅ 使用 fetch 發送清理請求');
}
} catch (error) {
console.error('❌ 清理請求發送失敗:', error);
}
}
// 頁面隱藏時清理
private async cleanupOnHidden() {
// 確保在瀏覽器環境中執行
if (typeof window === 'undefined') return;
console.log('🔄 頁面隱藏時清理連線...');
try {
await fetch('/api/ip-cleanup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'cleanup-current',
clientId: this.clientId,
reason: 'page-hidden'
})
});
console.log('✅ 頁面隱藏清理完成');
} catch (error) {
console.error('❌ 頁面隱藏清理失敗:', error);
}
}
// 定期清理
private async periodicCleanup() {
// 確保在瀏覽器環境中執行
if (typeof window === 'undefined') return;
console.log('🔄 定期清理連線...');
try {
const response = await fetch('/api/ip-cleanup?action=local-stats');
const data = await response.json();
if (data.success && data.data.trackedConnections > 10) {
// 如果追蹤的連線數超過10個執行清理
await fetch('/api/ip-cleanup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'cleanup-local',
clientId: this.clientId,
reason: 'periodic-cleanup'
})
});
console.log('✅ 定期清理完成');
}
} catch (error) {
console.error('❌ 定期清理失敗:', error);
}
}
// 手動清理
public async manualCleanup(): Promise<boolean> {
// 確保在瀏覽器環境中執行
if (typeof window === 'undefined') return false;
try {
const response = await fetch('/api/ip-cleanup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'cleanup-current',
clientId: this.clientId,
reason: 'manual-cleanup'
})
});
const data = await response.json();
if (data.success) {
console.log('✅ 手動清理完成:', data.message);
return true;
} else {
console.error('❌ 手動清理失敗:', data.error);
return false;
}
} catch (error) {
console.error('❌ 手動清理錯誤:', error);
return false;
}
}
// 獲取連線狀態
public async getConnectionStatus() {
// 確保在瀏覽器環境中執行
if (typeof window === 'undefined') return null;
try {
const response = await fetch('/api/ip-cleanup?action=status');
const data = await response.json();
if (data.success) {
console.log('📊 連線狀態:', data.data);
return data.data;
} else {
console.error('❌ 獲取連線狀態失敗:', data.error);
return null;
}
} catch (error) {
console.error('❌ 獲取連線狀態錯誤:', error);
return null;
}
}
// 獲取客戶端 ID
public getClientId(): string {
return this.clientId;
}
}
// 導出單例實例
export const clientCleanup = ClientConnectionCleanup.getInstance();
// 在模組載入時自動初始化(只在瀏覽器環境中)
if (typeof window !== 'undefined') {
// 延遲初始化,確保 DOM 已載入
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
clientCleanup;
});
} else {
clientCleanup;
}
}

View File

@@ -0,0 +1,178 @@
// =====================================================
// 連線生命週期管理器
// =====================================================
import { db } from './database';
import { dbFailover } from './database-failover';
export class ConnectionLifecycleManager {
private static instance: ConnectionLifecycleManager;
private activeConnections = new Map<string, {
connection: any;
createdAt: number;
lastUsed: number;
userId?: string;
sessionId?: string;
}>();
private cleanupInterval: NodeJS.Timeout | null = null;
private maxIdleTime = 5 * 60 * 1000; // 5分鐘空閒超時
private maxConnectionAge = 30 * 60 * 1000; // 30分鐘最大連線時間
private constructor() {
this.startCleanupProcess();
}
public static getInstance(): ConnectionLifecycleManager {
if (!ConnectionLifecycleManager.instance) {
ConnectionLifecycleManager.instance = new ConnectionLifecycleManager();
}
return ConnectionLifecycleManager.instance;
}
// 註冊連線
public registerConnection(connectionId: string, connection: any, metadata?: {
userId?: string;
sessionId?: string;
}) {
this.activeConnections.set(connectionId, {
connection,
createdAt: Date.now(),
lastUsed: Date.now(),
userId: metadata?.userId,
sessionId: metadata?.sessionId
});
console.log(`📝 註冊連線: ${connectionId} (總數: ${this.activeConnections.size})`);
}
// 更新連線使用時間
public updateConnectionUsage(connectionId: string) {
const conn = this.activeConnections.get(connectionId);
if (conn) {
conn.lastUsed = Date.now();
}
}
// 釋放連線
public releaseConnection(connectionId: string) {
const conn = this.activeConnections.get(connectionId);
if (conn) {
try {
if (conn.connection && typeof conn.connection.release === 'function') {
conn.connection.release();
}
this.activeConnections.delete(connectionId);
console.log(`🗑️ 釋放連線: ${connectionId} (剩餘: ${this.activeConnections.size})`);
} catch (error) {
console.error(`❌ 釋放連線失敗: ${connectionId}`, error);
}
}
}
// 開始清理程序
private startCleanupProcess() {
// 每30秒檢查一次空閒連線
this.cleanupInterval = setInterval(() => {
this.cleanupIdleConnections();
}, 30 * 1000);
console.log('🧹 連線生命週期管理器已啟動');
}
// 清理空閒連線
private cleanupIdleConnections() {
const now = Date.now();
const toRemove: string[] = [];
for (const [connectionId, conn] of this.activeConnections) {
const idleTime = now - conn.lastUsed;
const connectionAge = now - conn.createdAt;
// 檢查空閒時間
if (idleTime > this.maxIdleTime) {
console.log(`⏰ 連線 ${connectionId} 空閒時間過長 (${Math.round(idleTime / 1000)}s),準備釋放`);
toRemove.push(connectionId);
}
// 檢查連線年齡
else if (connectionAge > this.maxConnectionAge) {
console.log(`⏰ 連線 ${connectionId} 存在時間過長 (${Math.round(connectionAge / 1000)}s),準備釋放`);
toRemove.push(connectionId);
}
}
// 釋放需要清理的連線
toRemove.forEach(connectionId => {
this.releaseConnection(connectionId);
});
if (toRemove.length > 0) {
console.log(`🧹 清理了 ${toRemove.length} 個空閒連線`);
}
}
// 強制清理所有連線
public forceCleanupAll() {
console.log('🚨 強制清理所有連線...');
const connectionIds = Array.from(this.activeConnections.keys());
connectionIds.forEach(connectionId => {
this.releaseConnection(connectionId);
});
console.log(`✅ 已清理 ${connectionIds.length} 個連線`);
}
// 獲取連線統計
public getConnectionStats() {
const now = Date.now();
const connections = Array.from(this.activeConnections.values());
const idleConnections = connections.filter(conn =>
now - conn.lastUsed > this.maxIdleTime
).length;
const oldConnections = connections.filter(conn =>
now - conn.createdAt > this.maxConnectionAge
).length;
return {
totalConnections: this.activeConnections.size,
idleConnections,
oldConnections,
maxIdleTime: this.maxIdleTime,
maxConnectionAge: this.maxConnectionAge,
connections: connections.map(conn => ({
createdAt: new Date(conn.createdAt).toISOString(),
lastUsed: new Date(conn.lastUsed).toISOString(),
idleTime: now - conn.lastUsed,
age: now - conn.createdAt,
userId: conn.userId,
sessionId: conn.sessionId
}))
};
}
// 停止清理程序
public stop() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
// 清理所有連線
this.forceCleanupAll();
console.log('⏹️ 連線生命週期管理器已停止');
}
// 設置清理參數
public setCleanupParams(maxIdleTime?: number, maxConnectionAge?: number) {
if (maxIdleTime) this.maxIdleTime = maxIdleTime;
if (maxConnectionAge) this.maxConnectionAge = maxConnectionAge;
console.log(`⚙️ 更新清理參數: 空閒時間=${this.maxIdleTime}ms, 最大年齡=${this.maxConnectionAge}ms`);
}
}
// 導出單例實例
export const connectionLifecycleManager = ConnectionLifecycleManager.getInstance();

View File

@@ -5,6 +5,7 @@
import { NextRequest, NextResponse } from 'next/server';
import { db } from './database';
import { dbMonitor } from './database-monitor';
import { dbShutdownManager } from './database-shutdown-manager';
// 連線池狀態追蹤
let connectionCount = 0;
@@ -135,22 +136,24 @@ export function startConnectionMonitoring() {
console.log('🔍 資料庫連線監控已啟動');
}
// 優雅的關閉
// 優雅的關閉(使用新的關閉管理器)
export async function gracefulShutdown() {
console.log('🔄 正在優雅關閉資料庫連線...');
try {
dbMonitor.stopMonitoring();
await db.close();
console.log('✅ 資料庫連線已關閉');
} catch (error) {
console.error('❌ 關閉資料庫連線時發生錯誤:', error);
}
console.log('🔄 使用資料庫關閉管理器進行優雅關閉...');
await dbShutdownManager.gracefulShutdown();
}
// 處理程序退出事件
if (typeof process !== 'undefined') {
process.on('SIGINT', gracefulShutdown);
process.on('SIGTERM', gracefulShutdown);
process.on('exit', gracefulShutdown);
// 強制關閉
export function forceShutdown() {
console.log('🚨 強制關閉資料庫連線...');
dbShutdownManager.forceShutdown();
}
// 獲取關閉狀態
export function getShutdownStatus() {
return dbShutdownManager.getShutdownStatus();
}
// 測試關閉機制
export async function testShutdown() {
return await dbShutdownManager.testShutdown();
}

View File

@@ -50,21 +50,25 @@ export class DatabaseMonitor {
// 檢查連線狀態
private async checkConnectionStatus() {
let connection = null;
try {
// 使用單一連線來檢查狀態,避免建立多個連線
connection = await db.getConnection();
// 檢查當前連線數
const statusResult = await db.queryOne(`
const [statusResult] = await connection.execute(`
SHOW STATUS LIKE 'Threads_connected'
`);
const currentConnections = statusResult?.Value || 0;
const currentConnections = statusResult[0]?.Value || 0;
this.connectionCount = parseInt(currentConnections);
// 檢查最大連線數
const maxConnResult = await db.queryOne(`
const [maxConnResult] = await connection.execute(`
SHOW VARIABLES LIKE 'max_connections'
`);
this.maxConnections = parseInt(maxConnResult?.Value || '100');
this.maxConnections = parseInt(maxConnResult[0]?.Value || '100');
// 檢查連線使用率
const usagePercentage = (this.connectionCount / this.maxConnections) * 100;
@@ -80,43 +84,68 @@ export class DatabaseMonitor {
}
// 檢查長時間運行的查詢
await this.checkLongRunningQueries();
await this.checkLongRunningQueries(connection);
} catch (error) {
console.error('❌ 資料庫監控錯誤:', error);
} finally {
// 確保釋放連線
if (connection) {
try {
connection.release();
} catch (releaseError) {
console.error('❌ 釋放監控連線失敗:', releaseError);
}
}
}
}
// 檢查長時間運行的查詢
private async checkLongRunningQueries() {
private async checkLongRunningQueries(connection?: any) {
try {
const longQueries = await db.query(`
SELECT
ID,
USER,
HOST,
DB,
COMMAND,
TIME,
STATE,
INFO
FROM information_schema.PROCESSLIST
WHERE TIME > 30
AND COMMAND != 'Sleep'
ORDER BY TIME DESC
LIMIT 5
`);
// 如果沒有傳入連線,則建立一個新的
let tempConnection = connection;
let shouldRelease = false;
if (!tempConnection) {
tempConnection = await db.getConnection();
shouldRelease = true;
}
try {
const [longQueries] = await tempConnection.execute(`
SELECT
ID,
USER,
HOST,
DB,
COMMAND,
TIME,
STATE,
INFO
FROM information_schema.PROCESSLIST
WHERE TIME > 30
AND COMMAND != 'Sleep'
ORDER BY TIME DESC
LIMIT 5
`);
if (longQueries.length > 0) {
console.warn(`⚠️ 發現 ${longQueries.length} 個長時間運行的查詢:`);
longQueries.forEach((query, index) => {
console.warn(` ${index + 1}. 用戶: ${query.USER}, 時間: ${query.TIME}s, 狀態: ${query.STATE}`);
if (query.INFO && query.INFO.length > 100) {
console.warn(` 查詢: ${query.INFO.substring(0, 100)}...`);
} else if (query.INFO) {
console.warn(` 查詢: ${query.INFO}`);
}
});
if (longQueries.length > 0) {
console.warn(`⚠️ 發現 ${longQueries.length} 個長時間運行的查詢:`);
longQueries.forEach((query: any, index: number) => {
console.warn(` ${index + 1}. 用戶: ${query.USER}, 時間: ${query.TIME}s, 狀態: ${query.STATE}`);
if (query.INFO && query.INFO.length > 100) {
console.warn(` 查詢: ${query.INFO.substring(0, 100)}...`);
} else if (query.INFO) {
console.warn(` 查詢: ${query.INFO}`);
}
});
}
} finally {
// 只有當我們建立了新連線時才釋放
if (shouldRelease && tempConnection) {
tempConnection.release();
}
}
} catch (error) {
console.error('❌ 檢查長時間查詢時發生錯誤:', error);

View File

@@ -0,0 +1,89 @@
// =====================================================
// 智能資料庫服務範例
// =====================================================
import { smartPool } from './smart-connection-pool';
// 這是一個範例,展示如何使用智能連線池
// 你可以將現有的資料庫服務改為使用這個方式
export class SmartDatabaseService {
// 智能查詢範例
static async getUserById(userId: string, requestId?: string) {
return await smartPool.executeQueryOne(
'SELECT * FROM users WHERE id = ?',
[userId],
{
userId,
sessionId: requestId,
requestId: `getUserById_${Date.now()}`
}
);
}
// 智能插入範例
static async createUser(userData: any, requestId?: string) {
return await smartPool.executeInsert(
'INSERT INTO users (name, email, department) VALUES (?, ?, ?)',
[userData.name, userData.email, userData.department],
{
userId: userData.id,
sessionId: requestId,
requestId: `createUser_${Date.now()}`
}
);
}
// 智能更新範例
static async updateUser(userId: string, userData: any, requestId?: string) {
return await smartPool.executeUpdate(
'UPDATE users SET name = ?, email = ?, department = ? WHERE id = ?',
[userData.name, userData.email, userData.department, userId],
{
userId,
sessionId: requestId,
requestId: `updateUser_${Date.now()}`
}
);
}
// 智能刪除範例
static async deleteUser(userId: string, requestId?: string) {
return await smartPool.executeDelete(
'DELETE FROM users WHERE id = ?',
[userId],
{
userId,
sessionId: requestId,
requestId: `deleteUser_${Date.now()}`
}
);
}
// 獲取連線統計
static getConnectionStats() {
return smartPool.getConnectionStats();
}
// 強制清理連線
static forceCleanup() {
return smartPool.forceCleanup();
}
}
// 使用範例:
/*
// 在 API 路由中使用
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const userId = searchParams.get('userId');
const requestId = request.headers.get('x-request-id') || 'unknown';
try {
const user = await SmartDatabaseService.getUserById(userId, requestId);
return NextResponse.json({ success: true, data: user });
} catch (error) {
return NextResponse.json({ success: false, error: error.message }, { status: 500 });
}
}
*/

View File

@@ -0,0 +1,197 @@
// =====================================================
// 資料庫關閉管理器
// =====================================================
import { db } from './database';
import { dbFailover } from './database-failover';
import { dbMonitor } from './database-monitor';
import { smartPool } from './smart-connection-pool';
export class DatabaseShutdownManager {
private static instance: DatabaseShutdownManager;
private isShuttingDown = false;
private shutdownHandlers: (() => Promise<void>)[] = [];
private constructor() {
this.registerShutdownHandlers();
}
public static getInstance(): DatabaseShutdownManager {
if (!DatabaseShutdownManager.instance) {
DatabaseShutdownManager.instance = new DatabaseShutdownManager();
}
return DatabaseShutdownManager.instance;
}
// 註冊關閉處理器
private registerShutdownHandlers() {
// 添加資料庫關閉處理器
this.addShutdownHandler('database', async () => {
console.log('🔄 正在關閉主要資料庫連線池...');
try {
await db.close();
console.log('✅ 主要資料庫連線池已關閉');
} catch (error) {
console.error('❌ 關閉主要資料庫連線池時發生錯誤:', error);
}
});
// 添加備援資料庫關閉處理器
this.addShutdownHandler('failover', async () => {
console.log('🔄 正在關閉備援資料庫連線池...');
try {
await dbFailover.close();
console.log('✅ 備援資料庫連線池已關閉');
} catch (error) {
console.error('❌ 關閉備援資料庫連線池時發生錯誤:', error);
}
});
// 添加監控服務關閉處理器
this.addShutdownHandler('monitor', async () => {
console.log('🔄 正在停止資料庫監控服務...');
try {
dbMonitor.stopMonitoring();
console.log('✅ 資料庫監控服務已停止');
} catch (error) {
console.error('❌ 停止資料庫監控服務時發生錯誤:', error);
}
});
// 添加智能連線池關閉處理器
this.addShutdownHandler('smart-pool', async () => {
console.log('🔄 正在清理智能連線池...');
try {
smartPool.forceCleanup();
console.log('✅ 智能連線池已清理');
} catch (error) {
console.error('❌ 清理智能連線池時發生錯誤:', error);
}
});
// 註冊系統信號處理器
this.registerSystemHandlers();
}
// 添加關閉處理器
public addShutdownHandler(name: string, handler: () => Promise<void>) {
this.shutdownHandlers.push(async () => {
try {
console.log(`🔄 執行關閉處理器: ${name}`);
await handler();
console.log(`✅ 關閉處理器完成: ${name}`);
} catch (error) {
console.error(`❌ 關閉處理器失敗: ${name}`, error);
}
});
}
// 註冊系統信號處理器
private registerSystemHandlers() {
if (typeof process === 'undefined') return;
// SIGINT (Ctrl+C)
process.on('SIGINT', () => {
console.log('\n🛑 收到 SIGINT 信號,開始優雅關閉...');
this.gracefulShutdown();
});
// SIGTERM (終止信號)
process.on('SIGTERM', () => {
console.log('\n🛑 收到 SIGTERM 信號,開始優雅關閉...');
this.gracefulShutdown();
});
// 未捕獲的異常
process.on('uncaughtException', (error) => {
console.error('❌ 未捕獲的異常:', error);
this.gracefulShutdown();
});
// 未處理的 Promise 拒絕
process.on('unhandledRejection', (reason, promise) => {
console.error('❌ 未處理的 Promise 拒絕:', reason);
this.gracefulShutdown();
});
// 程序退出
process.on('exit', () => {
if (!this.isShuttingDown) {
console.log('🛑 程序即將退出,強制關閉資料庫連線...');
this.forceShutdown();
}
});
console.log('✅ 系統信號處理器已註冊');
}
// 優雅關閉
public async gracefulShutdown() {
if (this.isShuttingDown) {
console.log('⚠️ 關閉程序已在進行中,跳過重複請求');
return;
}
this.isShuttingDown = true;
console.log('🔄 開始優雅關閉資料庫連線...');
const startTime = Date.now();
try {
// 並行執行所有關閉處理器
await Promise.allSettled(
this.shutdownHandlers.map(handler => handler())
);
const duration = Date.now() - startTime;
console.log(`✅ 資料庫連線關閉完成,耗時: ${duration}ms`);
} catch (error) {
console.error('❌ 優雅關閉過程中發生錯誤:', error);
} finally {
// 強制退出程序
setTimeout(() => {
console.log('🛑 強制退出程序');
process.exit(0);
}, 5000); // 5秒後強制退出
}
}
// 強制關閉
public forceShutdown() {
console.log('🚨 強制關閉所有資料庫連線...');
// 同步執行關閉處理器(不等待)
this.shutdownHandlers.forEach(handler => {
try {
handler();
} catch (error) {
console.error('❌ 強制關閉時發生錯誤:', error);
}
});
console.log('✅ 強制關閉完成');
}
// 獲取關閉狀態
public getShutdownStatus() {
return {
isShuttingDown: this.isShuttingDown,
handlerCount: this.shutdownHandlers.length,
registeredHandlers: this.shutdownHandlers.map((_, index) => `handler-${index}`)
};
}
// 測試關閉機制
public async testShutdown() {
console.log('🧪 測試資料庫關閉機制...');
await this.gracefulShutdown();
}
}
// 導出單例實例
export const dbShutdownManager = DatabaseShutdownManager.getInstance();
// 導出便捷函數
export const gracefulShutdown = () => dbShutdownManager.gracefulShutdown();
export const forceShutdown = () => dbShutdownManager.forceShutdown();

View File

@@ -0,0 +1,175 @@
// =====================================================
// 緊急連線清理工具
// =====================================================
import { db } from './database';
import { dbFailover } from './database-failover';
import { dbMonitor } from './database-monitor';
export class EmergencyConnectionCleanup {
private static instance: EmergencyConnectionCleanup;
private constructor() {}
public static getInstance(): EmergencyConnectionCleanup {
if (!EmergencyConnectionCleanup.instance) {
EmergencyConnectionCleanup.instance = new EmergencyConnectionCleanup();
}
return EmergencyConnectionCleanup.instance;
}
// 立即停止所有監控和清理所有連線
public async emergencyCleanup() {
console.log('🚨 執行緊急連線清理...');
try {
// 1. 立即停止所有監控
console.log('⏹️ 停止資料庫監控...');
dbMonitor.stopMonitoring();
// 2. 強制關閉所有連線池
console.log('🔌 關閉主要資料庫連線池...');
await db.close();
console.log('🔌 關閉備援資料庫連線池...');
await dbFailover.close();
// 3. 等待一段時間讓連線完全關閉
console.log('⏳ 等待連線關閉...');
await new Promise(resolve => setTimeout(resolve, 2000));
// 4. 檢查連線狀態
await this.checkConnectionStatus();
console.log('✅ 緊急清理完成');
} catch (error) {
console.error('❌ 緊急清理失敗:', error);
}
}
// 檢查連線狀態(不建立新連線)
private async checkConnectionStatus() {
try {
// 使用一個臨時連線來檢查狀態
const tempConnection = await db.getConnection();
try {
const [statusResult] = await tempConnection.execute(`
SHOW STATUS LIKE 'Threads_connected'
`);
const [maxConnResult] = await tempConnection.execute(`
SHOW VARIABLES LIKE 'max_connections'
`);
const currentConnections = statusResult[0]?.Value || 0;
const maxConnections = maxConnResult[0]?.Value || 100;
const usagePercentage = (currentConnections / maxConnections) * 100;
console.log(`📊 清理後連線狀態: ${currentConnections}/${maxConnections} (${usagePercentage.toFixed(1)}%)`);
if (currentConnections > 5) {
console.warn(`⚠️ 仍有 ${currentConnections} 個連線未關閉`);
} else {
console.log('✅ 連線已成功清理');
}
} finally {
tempConnection.release();
}
} catch (error) {
console.error('❌ 檢查連線狀態失敗:', error);
}
}
// 強制殺死所有資料庫連線
public async forceKillAllConnections() {
console.log('💀 強制殺死所有資料庫連線...');
try {
const tempConnection = await db.getConnection();
try {
// 獲取所有連線ID
const [connections] = await tempConnection.execute(`
SELECT ID FROM information_schema.PROCESSLIST
WHERE USER = ? AND COMMAND != 'Sleep'
`, [process.env.DB_USER || 'A999']);
console.log(`🔍 找到 ${connections.length} 個活躍連線`);
// 殺死所有連線(除了當前連線)
for (const conn of connections) {
if (conn.ID !== tempConnection.threadId) {
try {
await tempConnection.execute(`KILL ${conn.ID}`);
console.log(`💀 已殺死連線 ${conn.ID}`);
} catch (error) {
console.log(`⚠️ 無法殺死連線 ${conn.ID}:`, error.message);
}
}
}
// 等待連線關閉
await new Promise(resolve => setTimeout(resolve, 1000));
// 再次檢查狀態
await this.checkConnectionStatus();
} finally {
tempConnection.release();
}
} catch (error) {
console.error('❌ 強制殺死連線失敗:', error);
}
}
// 獲取連線詳情
public async getConnectionDetails() {
try {
const tempConnection = await db.getConnection();
try {
const [connections] = await tempConnection.execute(`
SELECT
ID,
USER,
HOST,
DB,
COMMAND,
TIME,
STATE,
INFO
FROM information_schema.PROCESSLIST
WHERE USER = ?
ORDER BY TIME DESC
`, [process.env.DB_USER || 'A999']);
console.log('📋 當前資料庫連線詳情:');
connections.forEach((conn, index) => {
console.log(`${index + 1}. ID: ${conn.ID}, 用戶: ${conn.USER}, 時間: ${conn.TIME}s, 狀態: ${conn.STATE}`);
if (conn.INFO && conn.INFO.length > 50) {
console.log(` 查詢: ${conn.INFO.substring(0, 50)}...`);
} else if (conn.INFO) {
console.log(` 查詢: ${conn.INFO}`);
}
});
return connections;
} finally {
tempConnection.release();
}
} catch (error) {
console.error('❌ 獲取連線詳情失敗:', error);
return [];
}
}
}
// 導出單例實例
export const emergencyCleanup = EmergencyConnectionCleanup.getInstance();

View File

@@ -0,0 +1,251 @@
// =====================================================
// 基於 IP 的連線管理器
// =====================================================
import mysql from 'mysql2/promise';
import { db } from './database';
export class IPBasedConnectionManager {
private static instance: IPBasedConnectionManager;
private clientIP: string | null = null;
private connectionTracker = new Map<string, {
connectionId: string;
createdAt: number;
lastUsed: number;
userAgent?: string;
}>();
private constructor() {
this.detectClientIP();
}
public static getInstance(): IPBasedConnectionManager {
if (!IPBasedConnectionManager.instance) {
IPBasedConnectionManager.instance = new IPBasedConnectionManager();
}
return IPBasedConnectionManager.instance;
}
// 檢測客戶端 IP
private detectClientIP() {
// 在瀏覽器環境中,我們無法直接獲取真實 IP
// 但我們可以通過其他方式來識別連線
if (typeof window !== 'undefined') {
// 客戶端:生成一個唯一標識符
this.clientIP = this.generateClientId();
console.log('🖥️ 客戶端標識符:', this.clientIP);
} else {
// 服務端:嘗試從環境變數或請求中獲取
this.clientIP = process.env.CLIENT_IP || 'server-side';
console.log('🖥️ 服務端標識符:', this.clientIP);
}
}
// 生成客戶端唯一標識符
private generateClientId(): string {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2);
return `client_${timestamp}_${random}`;
}
// 設置客戶端 IP從請求中獲取
public setClientIP(ip: string) {
this.clientIP = ip;
console.log('🖥️ 設置客戶端 IP:', ip);
}
// 獲取客戶端 IP
public getClientIP(): string | null {
return this.clientIP;
}
// 註冊連線(帶 IP 標識)
public registerConnection(connectionId: string, metadata?: {
userAgent?: string;
requestId?: string;
}) {
if (!this.clientIP) return;
this.connectionTracker.set(connectionId, {
connectionId,
createdAt: Date.now(),
lastUsed: Date.now(),
userAgent: metadata?.userAgent,
});
console.log(`📝 註冊連線: ${connectionId} (IP: ${this.clientIP})`);
}
// 更新連線使用時間
public updateConnectionUsage(connectionId: string) {
const conn = this.connectionTracker.get(connectionId);
if (conn) {
conn.lastUsed = Date.now();
}
}
// 釋放連線
public releaseConnection(connectionId: string) {
const conn = this.connectionTracker.get(connectionId);
if (conn) {
this.connectionTracker.delete(connectionId);
console.log(`🗑️ 釋放連線: ${connectionId} (IP: ${this.clientIP})`);
}
}
// 根據 IP 清理連線
public async cleanupConnectionsByIP(targetIP?: string): Promise<{
success: boolean;
killedCount: number;
message: string;
}> {
const ipToClean = targetIP || this.clientIP;
if (!ipToClean) {
return {
success: false,
killedCount: 0,
message: '無法識別客戶端 IP'
};
}
console.log(`🧹 開始清理 IP ${ipToClean} 的連線...`);
let connection = null;
let killedCount = 0;
try {
// 建立臨時連線
connection = await db.getConnection();
// 獲取指定 IP 的所有連線(使用更寬鬆的匹配)
const [connections] = await connection.execute(`
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO
FROM information_schema.PROCESSLIST
WHERE USER = ? AND (HOST LIKE ? OR HOST LIKE ? OR HOST LIKE ?)
ORDER BY TIME DESC
`, [
process.env.DB_USER || 'A999',
`%${ipToClean}%`, // 包含 IP 的 HOST
`%${ipToClean}.%`, // IP 開頭的 HOST
`%${ipToClean}-%` // IP 帶連字符的 HOST
]);
console.log(`🔍 找到 ${connections.length} 個來自 ${ipToClean} 的連線`);
// 顯示連線詳情
connections.forEach((conn: any, index: number) => {
console.log(`${index + 1}. ID: ${conn.ID}, HOST: ${conn.HOST}, 時間: ${conn.TIME}s, 狀態: ${conn.STATE}`);
});
// 殺死指定 IP 的連線(除了當前連線)
for (const conn of connections) {
if (conn.ID !== connection.threadId) {
try {
await connection.execute(`KILL ${conn.ID}`);
console.log(`💀 已殺死連線 ${conn.ID} (HOST: ${conn.HOST})`);
killedCount++;
} catch (error: any) {
console.log(`⚠️ 無法殺死連線 ${conn.ID}: ${error.message}`);
}
}
}
// 清理本地追蹤的連線
this.connectionTracker.clear();
console.log(`✅ 已清理 ${killedCount} 個來自 ${ipToClean} 的連線`);
return {
success: true,
killedCount,
message: `已清理 ${killedCount} 個來自 ${ipToClean} 的連線`
};
} catch (error) {
console.error('❌ 清理連線失敗:', error);
return {
success: false,
killedCount: 0,
message: `清理失敗: ${error instanceof Error ? error.message : '未知錯誤'}`
};
} finally {
if (connection) {
connection.release();
}
}
}
// 獲取指定 IP 的連線狀態
public async getConnectionsByIP(targetIP?: string): Promise<{
ip: string;
connectionCount: number;
connections: any[];
}> {
const ipToCheck = targetIP || this.clientIP;
if (!ipToCheck) {
return {
ip: 'unknown',
connectionCount: 0,
connections: []
};
}
let connection = null;
try {
connection = await db.getConnection();
const [connections] = await connection.execute(`
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO
FROM information_schema.PROCESSLIST
WHERE USER = ? AND HOST LIKE ?
ORDER BY TIME DESC
`, [process.env.DB_USER || 'A999', `%${ipToCheck}%`]);
return {
ip: ipToCheck,
connectionCount: connections.length,
connections: connections
};
} catch (error) {
console.error('❌ 獲取連線狀態失敗:', error);
return {
ip: ipToCheck,
connectionCount: 0,
connections: []
};
} finally {
if (connection) {
connection.release();
}
}
}
// 清理所有本地追蹤的連線
public cleanupLocalConnections() {
const count = this.connectionTracker.size;
this.connectionTracker.clear();
console.log(`🧹 已清理 ${count} 個本地追蹤的連線`);
return count;
}
// 獲取本地連線統計
public getLocalConnectionStats() {
return {
clientIP: this.clientIP,
trackedConnections: this.connectionTracker.size,
connections: Array.from(this.connectionTracker.values()).map(conn => ({
connectionId: conn.connectionId,
createdAt: new Date(conn.createdAt).toISOString(),
lastUsed: new Date(conn.lastUsed).toISOString(),
userAgent: conn.userAgent
}))
};
}
}
// 導出單例實例
export const ipConnectionManager = IPBasedConnectionManager.getInstance();

View File

@@ -0,0 +1,202 @@
// =====================================================
// 智能連線池包裝器
// =====================================================
import { db } from './database';
import { dbFailover } from './database-failover';
import { connectionLifecycleManager } from './connection-lifecycle-manager';
export class SmartConnectionPool {
private static instance: SmartConnectionPool;
private connectionCounter = 0;
private constructor() {}
public static getInstance(): SmartConnectionPool {
if (!SmartConnectionPool.instance) {
SmartConnectionPool.instance = new SmartConnectionPool();
}
return SmartConnectionPool.instance;
}
// 獲取智能連線
public async getSmartConnection(metadata?: {
userId?: string;
sessionId?: string;
requestId?: string;
}): Promise<{
connection: any;
connectionId: string;
release: () => void;
}> {
const connectionId = `conn_${++this.connectionCounter}_${Date.now()}`;
try {
// 獲取實際連線
const connection = await db.getConnection();
// 註冊到生命週期管理器
connectionLifecycleManager.registerConnection(connectionId, connection, metadata);
// 創建包裝的釋放函數
const release = () => {
connectionLifecycleManager.releaseConnection(connectionId);
};
console.log(`🔗 獲取智能連線: ${connectionId}`);
return {
connection,
connectionId,
release
};
} catch (error) {
console.error(`❌ 獲取智能連線失敗: ${connectionId}`, error);
throw error;
}
}
// 執行智能查詢
public async executeQuery<T = any>(
sql: string,
params?: any[],
metadata?: {
userId?: string;
sessionId?: string;
requestId?: string;
}
): Promise<T[]> {
const { connection, connectionId, release } = await this.getSmartConnection(metadata);
try {
// 更新連線使用時間
connectionLifecycleManager.updateConnectionUsage(connectionId);
// 執行查詢
const [rows] = await connection.execute(sql, params);
return rows as T[];
} catch (error) {
console.error(`❌ 智能查詢失敗: ${connectionId}`, error);
throw error;
} finally {
// 確保釋放連線
release();
}
}
// 執行智能單一查詢
public async executeQueryOne<T = any>(
sql: string,
params?: any[],
metadata?: {
userId?: string;
sessionId?: string;
requestId?: string;
}
): Promise<T | null> {
const results = await this.executeQuery<T>(sql, params, metadata);
return results.length > 0 ? results[0] : null;
}
// 執行智能插入
public async executeInsert(
sql: string,
params?: any[],
metadata?: {
userId?: string;
sessionId?: string;
requestId?: string;
}
): Promise<any> {
const { connection, connectionId, release } = await this.getSmartConnection(metadata);
try {
// 更新連線使用時間
connectionLifecycleManager.updateConnectionUsage(connectionId);
// 執行插入
const [result] = await connection.execute(sql, params);
return result;
} catch (error) {
console.error(`❌ 智能插入失敗: ${connectionId}`, error);
throw error;
} finally {
// 確保釋放連線
release();
}
}
// 執行智能更新
public async executeUpdate(
sql: string,
params?: any[],
metadata?: {
userId?: string;
sessionId?: string;
requestId?: string;
}
): Promise<any> {
const { connection, connectionId, release } = await this.getSmartConnection(metadata);
try {
// 更新連線使用時間
connectionLifecycleManager.updateConnectionUsage(connectionId);
// 執行更新
const [result] = await connection.execute(sql, params);
return result;
} catch (error) {
console.error(`❌ 智能更新失敗: ${connectionId}`, error);
throw error;
} finally {
// 確保釋放連線
release();
}
}
// 執行智能刪除
public async executeDelete(
sql: string,
params?: any[],
metadata?: {
userId?: string;
sessionId?: string;
requestId?: string;
}
): Promise<any> {
const { connection, connectionId, release } = await this.getSmartConnection(metadata);
try {
// 更新連線使用時間
connectionLifecycleManager.updateConnectionUsage(connectionId);
// 執行刪除
const [result] = await connection.execute(sql, params);
return result;
} catch (error) {
console.error(`❌ 智能刪除失敗: ${connectionId}`, error);
throw error;
} finally {
// 確保釋放連線
release();
}
}
// 獲取連線統計
public getConnectionStats() {
return connectionLifecycleManager.getConnectionStats();
}
// 強制清理所有連線
public forceCleanup() {
connectionLifecycleManager.forceCleanupAll();
}
// 設置清理參數
public setCleanupParams(maxIdleTime?: number, maxConnectionAge?: number) {
connectionLifecycleManager.setCleanupParams(maxIdleTime, maxConnectionAge);
}
}
// 導出單例實例
export const smartPool = SmartConnectionPool.getInstance();

View File

@@ -0,0 +1,153 @@
// =====================================================
// 智能 IP 連線池
// =====================================================
import { db } from './database';
import { ipConnectionManager } from './ip-based-connection-manager';
export class SmartIPConnectionPool {
private static instance: SmartIPConnectionPool;
private connectionCounter = 0;
private constructor() {}
public static getInstance(): SmartIPConnectionPool {
if (!SmartIPConnectionPool.instance) {
SmartIPConnectionPool.instance = new SmartIPConnectionPool();
}
return SmartIPConnectionPool.instance;
}
// 設置客戶端 IP
public setClientIP(ip: string) {
ipConnectionManager.setClientIP(ip);
}
// 獲取智能連線(帶 IP 追蹤)
public async getSmartConnection(metadata?: {
userAgent?: string;
requestId?: string;
clientIP?: string;
}): Promise<{
connection: any;
connectionId: string;
release: () => void;
}> {
const connectionId = `conn_${++this.connectionCounter}_${Date.now()}`;
// 設置客戶端 IP
if (metadata?.clientIP) {
ipConnectionManager.setClientIP(metadata.clientIP);
}
try {
// 獲取實際連線
const connection = await db.getConnection();
// 註冊到 IP 連線管理器
ipConnectionManager.registerConnection(connectionId, {
userAgent: metadata?.userAgent,
requestId: metadata?.requestId
});
// 創建包裝的釋放函數
const release = () => {
ipConnectionManager.releaseConnection(connectionId);
};
console.log(`🔗 獲取智能 IP 連線: ${connectionId}`);
return {
connection,
connectionId,
release
};
} catch (error) {
console.error(`❌ 獲取智能 IP 連線失敗: ${connectionId}`, error);
throw error;
}
}
// 執行智能查詢(帶 IP 追蹤)
public async executeQuery<T = any>(
sql: string,
params?: any[],
metadata?: {
userAgent?: string;
requestId?: string;
clientIP?: string;
}
): Promise<T[]> {
const { connection, connectionId, release } = await this.getSmartConnection(metadata);
try {
// 更新連線使用時間
ipConnectionManager.updateConnectionUsage(connectionId);
// 執行查詢
const [rows] = await connection.execute(sql, params);
return rows as T[];
} catch (error) {
console.error(`❌ 智能 IP 查詢失敗: ${connectionId}`, error);
throw error;
} finally {
// 確保釋放連線
release();
}
}
// 執行智能單一查詢
public async executeQueryOne<T = any>(
sql: string,
params?: any[],
metadata?: {
userAgent?: string;
requestId?: string;
clientIP?: string;
}
): Promise<T | null> {
const results = await this.executeQuery<T>(sql, params, metadata);
return results.length > 0 ? results[0] : null;
}
// 清理當前 IP 的所有連線
public async cleanupCurrentIPConnections(): Promise<{
success: boolean;
killedCount: number;
message: string;
}> {
return await ipConnectionManager.cleanupConnectionsByIP();
}
// 清理指定 IP 的所有連線
public async cleanupIPConnections(targetIP: string): Promise<{
success: boolean;
killedCount: number;
message: string;
}> {
return await ipConnectionManager.cleanupConnectionsByIP(targetIP);
}
// 獲取當前 IP 的連線狀態
public async getCurrentIPConnections() {
return await ipConnectionManager.getConnectionsByIP();
}
// 獲取指定 IP 的連線狀態
public async getIPConnections(targetIP: string) {
return await ipConnectionManager.getConnectionsByIP(targetIP);
}
// 獲取本地連線統計
public getLocalConnectionStats() {
return ipConnectionManager.getLocalConnectionStats();
}
// 清理本地追蹤的連線
public cleanupLocalConnections() {
return ipConnectionManager.cleanupLocalConnections();
}
}
// 導出單例實例
export const smartIPPool = SmartIPConnectionPool.getInstance();

208
lib/smart-ip-detector.ts Normal file
View File

@@ -0,0 +1,208 @@
// =====================================================
// 智能 IP 偵測器
// =====================================================
import { NextRequest } from 'next/server';
export interface IPDetectionResult {
detectedIP: string;
confidence: 'high' | 'medium' | 'low';
source: string;
allCandidates: string[];
isPublicIP: boolean;
}
export class SmartIPDetector {
private static instance: SmartIPDetector;
public static getInstance(): SmartIPDetector {
if (!SmartIPDetector.instance) {
SmartIPDetector.instance = new SmartIPDetector();
}
return SmartIPDetector.instance;
}
// 檢查是否為公網 IP
private isPublicIP(ip: string): boolean {
if (!ip || ip === 'unknown') return false;
// 本地地址
if (ip === '127.0.0.1' || ip === '::1' || ip === 'localhost') return false;
// 私有地址範圍
if (ip.startsWith('192.168.') ||
ip.startsWith('10.') ||
ip.startsWith('172.') ||
ip.startsWith('169.254.')) return false;
// IPv6 本地地址
if (ip.startsWith('fe80:') ||
ip.startsWith('::1') ||
ip.startsWith('::ffff:127.0.0.1')) return false;
return true;
}
// 從 x-forwarded-for 解析 IP
private parseForwardedFor(forwarded: string): string[] {
if (!forwarded) return [];
return forwarded
.split(',')
.map(ip => ip.trim())
.filter(ip => ip && ip !== 'unknown');
}
// 智能偵測客戶端 IP
public detectClientIP(request: NextRequest): IPDetectionResult {
const candidates: string[] = [];
const sources: { [key: string]: string } = {};
// 1. 收集所有可能的 IP 來源
const headers = {
'x-forwarded-for': request.headers.get('x-forwarded-for'),
'x-real-ip': request.headers.get('x-real-ip'),
'cf-connecting-ip': request.headers.get('cf-connecting-ip'),
'x-client-ip': request.headers.get('x-client-ip'),
'x-forwarded': request.headers.get('x-forwarded'),
'x-cluster-client-ip': request.headers.get('x-cluster-client-ip'),
'x-original-forwarded-for': request.headers.get('x-original-forwarded-for'),
'x-remote-addr': request.headers.get('x-remote-addr'),
'remote-addr': request.headers.get('remote-addr'),
'client-ip': request.headers.get('client-ip'),
};
// 2. 從各個標頭提取 IP
Object.entries(headers).forEach(([header, value]) => {
if (value) {
if (header === 'x-forwarded-for') {
const ips = this.parseForwardedFor(value);
ips.forEach(ip => {
candidates.push(ip);
sources[ip] = header;
});
} else {
candidates.push(value);
sources[value] = header;
}
}
});
// 3. 添加 NextRequest 的 IP (如果可用)
const nextIP = (request as any).ip;
if (nextIP) {
candidates.push(nextIP);
sources[nextIP] = 'next-request';
}
// 4. 去重並過濾
const uniqueCandidates = [...new Set(candidates)].filter(ip =>
ip && ip !== 'unknown' && ip !== '::1' && ip !== '127.0.0.1'
);
console.log('🔍 IP 偵測候選:', {
candidates: uniqueCandidates,
sources: sources,
allHeaders: Object.fromEntries(request.headers.entries())
});
// 5. 智能選擇最佳 IP
let selectedIP = 'unknown';
let confidence: 'high' | 'medium' | 'low' = 'low';
let source = 'unknown';
// 優先級 1: Cloudflare IP (最高可信度)
const cfIP = uniqueCandidates.find(ip => sources[ip] === 'cf-connecting-ip');
if (cfIP && this.isPublicIP(cfIP)) {
selectedIP = cfIP;
confidence = 'high';
source = 'cf-connecting-ip';
}
// 優先級 2: 其他代理標頭中的公網 IP
if (selectedIP === 'unknown') {
const proxyHeaders = ['x-real-ip', 'x-client-ip', 'x-cluster-client-ip'];
for (const header of proxyHeaders) {
const ip = uniqueCandidates.find(candidate => sources[candidate] === header);
if (ip && this.isPublicIP(ip)) {
selectedIP = ip;
confidence = 'high';
source = header;
break;
}
}
}
// 優先級 3: x-forwarded-for 中的第一個公網 IP
if (selectedIP === 'unknown') {
const forwardedIPs = uniqueCandidates.filter(ip => sources[ip] === 'x-forwarded-for');
const publicForwardedIP = forwardedIPs.find(ip => this.isPublicIP(ip));
if (publicForwardedIP) {
selectedIP = publicForwardedIP;
confidence = 'medium';
source = 'x-forwarded-for';
}
}
// 優先級 4: 任何公網 IP
if (selectedIP === 'unknown') {
const publicIP = uniqueCandidates.find(ip => this.isPublicIP(ip));
if (publicIP) {
selectedIP = publicIP;
confidence = 'medium';
source = sources[publicIP] || 'unknown';
}
}
// 優先級 5: 任何非本地 IP
if (selectedIP === 'unknown') {
const nonLocalIP = uniqueCandidates.find(ip =>
ip !== '::1' && ip !== '127.0.0.1' && !ip.startsWith('192.168.') &&
!ip.startsWith('10.') && !ip.startsWith('172.')
);
if (nonLocalIP) {
selectedIP = nonLocalIP;
confidence = 'low';
source = sources[nonLocalIP] || 'unknown';
}
}
// 優先級 6: 第一個候選 IP
if (selectedIP === 'unknown' && uniqueCandidates.length > 0) {
selectedIP = uniqueCandidates[0];
confidence = 'low';
source = sources[selectedIP] || 'unknown';
}
const result: IPDetectionResult = {
detectedIP: selectedIP,
confidence,
source,
allCandidates: uniqueCandidates,
isPublicIP: this.isPublicIP(selectedIP)
};
console.log('🎯 IP 偵測結果:', result);
return result;
}
// 驗證 IP 格式
public isValidIP(ip: string): boolean {
if (!ip || ip === 'unknown') return false;
// IPv4 格式檢查
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
if (ipv4Regex.test(ip)) {
const parts = ip.split('.').map(Number);
return parts.every(part => part >= 0 && part <= 255);
}
// IPv6 格式檢查(簡化)
const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
return ipv6Regex.test(ip);
}
}
// 導出單例實例
export const smartIPDetector = SmartIPDetector.getInstance();

View File

@@ -0,0 +1,186 @@
// =====================================================
// 終極連線殺手 - 強制清理所有連線
// =====================================================
import mysql from 'mysql2/promise';
export class UltimateConnectionKiller {
private static instance: UltimateConnectionKiller;
private constructor() {}
public static getInstance(): UltimateConnectionKiller {
if (!UltimateConnectionKiller.instance) {
UltimateConnectionKiller.instance = new UltimateConnectionKiller();
}
return UltimateConnectionKiller.instance;
}
// 終極清理 - 殺死所有連線
public async ultimateKill() {
console.log('💀 執行終極連線清理...');
try {
// 1. 建立一個臨時連線來執行清理
const tempConnection = await mysql.createConnection({
host: process.env.DB_HOST || '122.100.99.161',
port: parseInt(process.env.DB_PORT || '43306'),
user: process.env.DB_USER || 'A999',
password: process.env.DB_PASSWORD || '1023',
database: process.env.DB_NAME || 'db_AI_Platform',
charset: 'utf8mb4',
timezone: '+08:00',
});
try {
// 2. 獲取所有連線
const [connections] = await tempConnection.execute(`
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO
FROM information_schema.PROCESSLIST
WHERE USER = ?
ORDER BY TIME DESC
`, [process.env.DB_USER || 'A999']);
console.log(`🔍 找到 ${connections.length} 個連線需要清理`);
// 3. 殺死所有連線(除了當前連線)
let killedCount = 0;
for (const conn of connections) {
if (conn.ID !== tempConnection.threadId) {
try {
await tempConnection.execute(`KILL ${conn.ID}`);
console.log(`💀 已殺死連線 ${conn.ID} (用戶: ${conn.USER}, 時間: ${conn.TIME}s)`);
killedCount++;
} catch (error: any) {
console.log(`⚠️ 無法殺死連線 ${conn.ID}: ${error.message}`);
}
}
}
console.log(`✅ 已殺死 ${killedCount} 個連線`);
// 4. 等待連線完全關閉
console.log('⏳ 等待連線完全關閉...');
await new Promise(resolve => setTimeout(resolve, 3000));
// 5. 再次檢查狀態
const [finalConnections] = await tempConnection.execute(`
SELECT COUNT(*) as count FROM information_schema.PROCESSLIST
WHERE USER = ?
`, [process.env.DB_USER || 'A999']);
const remainingConnections = finalConnections[0].count;
console.log(`📊 清理後剩餘連線: ${remainingConnections}`);
if (remainingConnections <= 1) {
console.log('🎉 連線清理成功!');
} else {
console.warn(`⚠️ 仍有 ${remainingConnections - 1} 個連線未清理`);
}
return {
success: true,
killedCount,
remainingConnections: remainingConnections - 1, // 減去當前連線
message: `已殺死 ${killedCount} 個連線,剩餘 ${remainingConnections - 1}`
};
} finally {
// 6. 關閉臨時連線
await tempConnection.end();
console.log('🔌 臨時連線已關閉');
}
} catch (error) {
console.error('❌ 終極清理失敗:', error);
return {
success: false,
error: error instanceof Error ? error.message : '未知錯誤'
};
}
}
// 檢查連線狀態
public async checkStatus() {
try {
const tempConnection = await mysql.createConnection({
host: process.env.DB_HOST || '122.100.99.161',
port: parseInt(process.env.DB_PORT || '43306'),
user: process.env.DB_USER || 'A999',
password: process.env.DB_PASSWORD || '1023',
database: process.env.DB_NAME || 'db_AI_Platform',
charset: 'utf8mb4',
timezone: '+08:00',
});
try {
// 獲取連線統計
const [statusResult] = await tempConnection.execute(`
SHOW STATUS LIKE 'Threads_connected'
`);
const [maxConnResult] = await tempConnection.execute(`
SHOW VARIABLES LIKE 'max_connections'
`);
const [connections] = await tempConnection.execute(`
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO
FROM information_schema.PROCESSLIST
WHERE USER = ?
ORDER BY TIME DESC
`, [process.env.DB_USER || 'A999']);
const currentConnections = statusResult[0]?.Value || 0;
const maxConnections = maxConnResult[0]?.Value || 100;
const usagePercentage = (currentConnections / maxConnections) * 100;
return {
currentConnections: parseInt(currentConnections),
maxConnections: parseInt(maxConnections),
usagePercentage,
connectionDetails: connections,
connectionCount: connections.length
};
} finally {
await tempConnection.end();
}
} catch (error) {
console.error('❌ 檢查狀態失敗:', error);
return null;
}
}
// 強制重啟資料庫連線
public async forceRestart() {
console.log('🔄 強制重啟資料庫連線...');
try {
// 1. 先殺死所有連線
await this.ultimateKill();
// 2. 等待一段時間
console.log('⏳ 等待系統穩定...');
await new Promise(resolve => setTimeout(resolve, 5000));
// 3. 檢查狀態
const status = await this.checkStatus();
if (status && status.connectionCount <= 1) {
console.log('✅ 資料庫連線重啟成功');
return { success: true, message: '資料庫連線重啟成功' };
} else {
console.warn('⚠️ 資料庫連線重啟後仍有連線存在');
return { success: false, message: '重啟後仍有連線存在' };
}
} catch (error) {
console.error('❌ 強制重啟失敗:', error);
return { success: false, error: error instanceof Error ? error.message : '未知錯誤' };
}
}
}
// 導出單例實例
export const ultimateKiller = UltimateConnectionKiller.getInstance();