// ===================================================== // 客戶端連線清理腳本 // ===================================================== 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 { // 確保在瀏覽器環境中執行 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; } }