// ===================================================== // 智能連線清理器 // ===================================================== import mysql from 'mysql2/promise'; import { db } from './database'; import { smartIPDetector } from './smart-ip-detector'; import { NextRequest } from 'next/server'; export interface CleanupResult { success: boolean; killedCount: number; message: string; details: { userRealIP?: string; infrastructureIPs?: string[]; cleanedConnections: Array<{ id: number; host: string; time: number; state: string; }>; }; } export class SmartConnectionCleaner { private static instance: SmartConnectionCleaner; public static getInstance(): SmartConnectionCleaner { if (!SmartConnectionCleaner.instance) { SmartConnectionCleaner.instance = new SmartConnectionCleaner(); } return SmartConnectionCleaner.instance; } // 智能清理連線 public async smartCleanup(request: NextRequest): Promise { try { // 1. 使用智能 IP 偵測器獲取真實 IP const ipDetection = smartIPDetector.detectClientIP(request); const userRealIP = ipDetection.detectedIP; console.log('🧠 智能清理開始:', { detectedIP: userRealIP, confidence: ipDetection.confidence, source: ipDetection.source, isUserRealIP: ipDetection.isUserRealIP, infrastructureIPs: ipDetection.infrastructureIPs }); // 2. 建立資料庫連線 const connection = await db.getConnection(); let killedCount = 0; const cleanedConnections: Array<{ id: number; host: string; time: number; state: string; }> = []; try { // 3. 獲取所有相關連線 const [allConnections] = await connection.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(`🔍 找到 ${allConnections.length} 個總連線`); // 4. 分類連線 const userConnections = allConnections.filter((conn: any) => this.isUserConnection(conn.HOST, userRealIP) ); const infrastructureConnections = allConnections.filter((conn: any) => this.isInfrastructureConnection(conn.HOST) ); console.log(`👤 用戶連線: ${userConnections.length} 個`); console.log(`🏗️ 基礎設施連線: ${infrastructureConnections.length} 個`); // 5. 清理用戶連線(優先) if (userConnections.length > 0) { console.log(`🧹 開始清理用戶連線 (IP: ${userRealIP})`); for (const conn of userConnections) { if (conn.ID !== connection.threadId) { try { await connection.execute(`KILL ${conn.ID}`); console.log(`💀 已殺死用戶連線 ${conn.ID} (HOST: ${conn.HOST})`); killedCount++; cleanedConnections.push({ id: conn.ID, host: conn.HOST, time: conn.TIME, state: conn.STATE || 'Sleep' }); } catch (error: any) { console.log(`⚠️ 無法殺死用戶連線 ${conn.ID}: ${error.message}`); } } } } // 6. 清理基礎設施連線(可選,根據配置) const shouldCleanInfrastructure = process.env.CLEAN_INFRASTRUCTURE_CONNECTIONS === 'true'; if (shouldCleanInfrastructure && infrastructureConnections.length > 0) { console.log(`🧹 開始清理基礎設施連線`); for (const conn of infrastructureConnections) { if (conn.ID !== connection.threadId) { try { await connection.execute(`KILL ${conn.ID}`); console.log(`💀 已殺死基礎設施連線 ${conn.ID} (HOST: ${conn.HOST})`); killedCount++; cleanedConnections.push({ id: conn.ID, host: conn.HOST, time: conn.TIME, state: conn.STATE || 'Sleep' }); } catch (error: any) { console.log(`⚠️ 無法殺死基礎設施連線 ${conn.ID}: ${error.message}`); } } } } console.log(`✅ 智能清理完成: 共清理 ${killedCount} 個連線`); return { success: true, killedCount, message: `智能清理完成: 共清理 ${killedCount} 個連線`, details: { userRealIP: userRealIP !== 'unknown' ? userRealIP : undefined, infrastructureIPs: ipDetection.infrastructureIPs, cleanedConnections } }; } finally { connection.release(); } } catch (error) { console.error('❌ 智能清理失敗:', error); return { success: false, killedCount: 0, message: `智能清理失敗: ${error instanceof Error ? error.message : '未知錯誤'}`, details: { cleanedConnections: [] } }; } } // 檢查是否為用戶連線 private isUserConnection(host: string, userIP: string): boolean { if (!host || !userIP || userIP === 'unknown') return false; // 直接匹配用戶 IP if (host.includes(userIP)) return true; // 匹配用戶 IP 的變體格式 const ipVariants = [ userIP, userIP.replace(/\./g, '-'), // 61.227.253.171 -> 61-227-253-171 userIP.replace(/\./g, '_'), // 61.227.253.171 -> 61_227_253_171 ]; return ipVariants.some(variant => host.includes(variant)); } // 檢查是否為基礎設施連線 private isInfrastructureConnection(host: string): boolean { if (!host) return false; // AWS EC2 實例 if (host.includes('ec2-') && host.includes('.amazonaws.com')) return true; // 其他基礎設施模式 const infrastructurePatterns = [ '.amazonaws.com', '.vercel.app', 'vercel', 'cloudflare', 'fastly.com', 'cloudfront.net' ]; return infrastructurePatterns.some(pattern => host.includes(pattern)); } // 獲取連線統計 public async getConnectionStats(): Promise<{ total: number; user: number; infrastructure: number; other: number; details: Array<{ id: number; host: string; time: number; state: string; type: 'user' | 'infrastructure' | 'other'; }>; }> { try { const connection = await db.getConnection(); try { const [connections] = await connection.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 stats = { total: connections.length, user: 0, infrastructure: 0, other: 0, details: [] as Array<{ id: number; host: string; time: number; state: string; type: 'user' | 'infrastructure' | 'other'; }> }; for (const conn of connections) { let type: 'user' | 'infrastructure' | 'other' = 'other'; if (this.isInfrastructureConnection(conn.HOST)) { type = 'infrastructure'; stats.infrastructure++; } else if (this.isUserConnection(conn.HOST, 'unknown')) { type = 'user'; stats.user++; } else { stats.other++; } stats.details.push({ id: conn.ID, host: conn.HOST, time: conn.TIME, state: conn.STATE || 'Sleep', type }); } return stats; } finally { connection.release(); } } catch (error) { console.error('❌ 獲取連線統計失敗:', error); return { total: 0, user: 0, infrastructure: 0, other: 0, details: [] }; } } } // 導出單例實例 export const smartConnectionCleaner = SmartConnectionCleaner.getInstance();