298 lines
9.8 KiB
TypeScript
298 lines
9.8 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Alert, AlertDescription } from '@/components/ui/alert';
|
|
import { Loader2, Database, CheckCircle, AlertTriangle, RefreshCw, Trash2 } from 'lucide-react';
|
|
|
|
interface IPDetectionResult {
|
|
detectedIP: string;
|
|
confidence: 'high' | 'medium' | 'low';
|
|
source: string;
|
|
isPublicIP: boolean;
|
|
allCandidates: string[];
|
|
}
|
|
|
|
interface ConnectionStatus {
|
|
ip: string;
|
|
connectionCount: number;
|
|
connections: any[];
|
|
}
|
|
|
|
export default function AutoIPTestPage() {
|
|
const [ipDetection, setIpDetection] = useState<IPDetectionResult | null>(null);
|
|
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [message, setMessage] = useState<string>('');
|
|
const [error, setError] = useState<string>('');
|
|
|
|
// 自動偵測 IP
|
|
const detectIP = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await fetch('/api/debug-ip');
|
|
const data = await response.json();
|
|
|
|
if (data.success && data.data.smartDetection) {
|
|
setIpDetection(data.data.smartDetection);
|
|
setMessage(`IP 偵測成功: ${data.data.smartDetection.detectedIP}`);
|
|
setError('');
|
|
} else {
|
|
setError('IP 偵測失敗');
|
|
}
|
|
} catch (err) {
|
|
setError('IP 偵測錯誤: ' + (err instanceof Error ? err.message : '未知錯誤'));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
// 獲取連線狀態
|
|
const getConnectionStatus = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await fetch('/api/ip-cleanup?action=status');
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
setConnectionStatus(data.data);
|
|
setMessage(`連線狀態獲取成功: ${data.data.connectionCount} 個連線`);
|
|
setError('');
|
|
} else {
|
|
setError('獲取連線狀態失敗');
|
|
}
|
|
} catch (err) {
|
|
setError('獲取連線狀態錯誤: ' + (err instanceof Error ? err.message : '未知錯誤'));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
// 清理當前 IP 連線
|
|
const cleanupConnections = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await fetch('/api/ip-cleanup', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ action: 'cleanup-current' }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
setMessage(`清理成功: ${data.message}`);
|
|
setError('');
|
|
// 重新獲取連線狀態
|
|
await getConnectionStatus();
|
|
} else {
|
|
setError(data.error || '清理失敗');
|
|
}
|
|
} catch (err) {
|
|
setError('清理錯誤: ' + (err instanceof Error ? err.message : '未知錯誤'));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
// 組件載入時自動偵測
|
|
useEffect(() => {
|
|
detectIP();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="container mx-auto p-6 space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-bold">自動 IP 偵測測試</h1>
|
|
<p className="text-muted-foreground">
|
|
測試智能 IP 偵測和自動連線清理功能
|
|
</p>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button
|
|
onClick={detectIP}
|
|
disabled={loading}
|
|
variant="outline"
|
|
>
|
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
|
|
偵測 IP
|
|
</Button>
|
|
<Button
|
|
onClick={getConnectionStatus}
|
|
disabled={loading}
|
|
variant="outline"
|
|
>
|
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Database className="h-4 w-4" />}
|
|
檢查連線
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* IP 偵測結果 */}
|
|
{ipDetection && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<CheckCircle className="h-5 w-5" />
|
|
智能 IP 偵測結果
|
|
</CardTitle>
|
|
<CardDescription>
|
|
系統自動偵測到的客戶端 IP 地址
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="flex items-center gap-4">
|
|
<Badge
|
|
variant={ipDetection.isPublicIP ? "default" : "secondary"}
|
|
className="text-lg px-3 py-1"
|
|
>
|
|
{ipDetection.detectedIP}
|
|
</Badge>
|
|
<Badge
|
|
variant={
|
|
ipDetection.confidence === 'high' ? "default" :
|
|
ipDetection.confidence === 'medium' ? "secondary" : "destructive"
|
|
}
|
|
className="text-lg px-3 py-1"
|
|
>
|
|
可信度: {ipDetection.confidence}
|
|
</Badge>
|
|
<Badge variant="outline" className="text-lg px-3 py-1">
|
|
來源: {ipDetection.source}
|
|
</Badge>
|
|
</div>
|
|
|
|
{ipDetection.allCandidates.length > 1 && (
|
|
<div>
|
|
<p className="text-sm font-medium mb-2">所有候選 IP:</p>
|
|
<div className="flex flex-wrap gap-2">
|
|
{ipDetection.allCandidates.map((ip, index) => (
|
|
<Badge
|
|
key={index}
|
|
variant={ip === ipDetection.detectedIP ? "default" : "outline"}
|
|
className="text-sm"
|
|
>
|
|
{ip}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
{/* 連線狀態 */}
|
|
{connectionStatus && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Database className="h-5 w-5" />
|
|
連線狀態
|
|
</CardTitle>
|
|
<CardDescription>
|
|
當前 IP 的資料庫連線狀態
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="flex items-center gap-4">
|
|
<Badge variant="default" className="text-lg px-3 py-1">
|
|
IP: {connectionStatus.ip}
|
|
</Badge>
|
|
<Badge
|
|
variant={connectionStatus.connectionCount > 0 ? "destructive" : "default"}
|
|
className="text-lg px-3 py-1"
|
|
>
|
|
連線數: {connectionStatus.connectionCount}
|
|
</Badge>
|
|
</div>
|
|
|
|
{connectionStatus.connections.length > 0 && (
|
|
<div>
|
|
<p className="text-sm font-medium mb-2">連線詳情:</p>
|
|
<div className="space-y-2 max-h-48 overflow-y-auto">
|
|
{connectionStatus.connections.map((conn, index) => (
|
|
<div key={index} className="flex items-center justify-between p-2 border rounded text-sm">
|
|
<span>ID: {conn.ID}</span>
|
|
<span>HOST: {conn.HOST}</span>
|
|
<span>時間: {conn.TIME}s</span>
|
|
<span>狀態: {conn.STATE || 'Sleep'}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
{/* 操作按鈕 */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>操作</CardTitle>
|
|
<CardDescription>
|
|
測試自動 IP 偵測和連線清理功能
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex gap-4">
|
|
<Button
|
|
onClick={cleanupConnections}
|
|
disabled={loading || !ipDetection}
|
|
variant="destructive"
|
|
className="flex-1"
|
|
>
|
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Trash2 className="h-4 w-4" />}
|
|
清理我的連線
|
|
</Button>
|
|
|
|
<Button
|
|
onClick={getConnectionStatus}
|
|
disabled={loading}
|
|
variant="outline"
|
|
className="flex-1"
|
|
>
|
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
|
|
重新檢查
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 使用說明 */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>使用說明</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-2 text-sm text-muted-foreground">
|
|
<p>• <strong>自動偵測:</strong> 頁面載入時會自動偵測你的 IP 地址</p>
|
|
<p>• <strong>智能算法:</strong> 使用多種方法偵測最準確的 IP 地址</p>
|
|
<p>• <strong>連線檢查:</strong> 檢查你的 IP 在資料庫中的連線狀態</p>
|
|
<p>• <strong>自動清理:</strong> 點擊「清理我的連線」來清理你的 IP 連線</p>
|
|
<p>• <strong>關閉頁面:</strong> 關閉此頁面時會自動觸發連線清理</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 訊息顯示 */}
|
|
{message && (
|
|
<Alert>
|
|
<CheckCircle className="h-4 w-4" />
|
|
<AlertDescription>{message}</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
|
|
{error && (
|
|
<Alert variant="destructive">
|
|
<AlertTriangle className="h-4 w-4" />
|
|
<AlertDescription>{error}</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|