"use client"; import { useState, useEffect, useCallback, useMemo, useRef } from "react"; import Card from "./Card"; import ProxyLogDetail from "./ProxyLogDetail"; import { TYPE_COLORS, LEVEL_COLORS, PROVIDER_COLORS, getProxyStatusStyle as getStatusStyle, } from "@/shared/constants/colors"; import { formatTime, formatDuration as formatLatency, truncateUrl, } from "@/shared/utils/formatting"; const STATUS_FILTERS = [ { key: "all", label: "All" }, { key: "error", label: "Errors", icon: "error" }, { key: "ok", label: "Success", icon: "check_circle" }, { key: "timeout", label: "Timeout", icon: "timer_off" }, ]; const COLUMNS = [ { key: "status", label: "Status" }, { key: "proxy", label: "Proxy" }, { key: "tls", label: "TLS" }, { key: "type", label: "Type" }, { key: "level", label: "Level" }, { key: "provider", label: "Provider" }, { key: "target", label: "Target" }, { key: "latency", label: "Latency" }, { key: "ip", label: "Public IP" }, { key: "time", label: "Time" }, ]; const DEFAULT_VISIBLE = Object.fromEntries(COLUMNS.map((c) => [c.key, true])); export default function ProxyLogger() { const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(true); const [recording, setRecording] = useState(true); const [search, setSearch] = useState(""); const [activeFilter, setActiveFilter] = useState("all"); const [selectedType, setSelectedType] = useState(""); const [selectedProvider, setSelectedProvider] = useState(""); const [selectedLevel, setSelectedLevel] = useState(""); const [sortBy, setSortBy] = useState("newest"); const [selectedLog, setSelectedLog] = useState(null); const intervalRef = useRef(null); const hasLoadedRef = useRef(false); const [visibleColumns, setVisibleColumns] = useState(() => { if (typeof window === "undefined") return DEFAULT_VISIBLE; try { const saved = localStorage.getItem("proxyLoggerVisibleColumns"); return saved ? { ...DEFAULT_VISIBLE, ...JSON.parse(saved) } : DEFAULT_VISIBLE; } catch { return DEFAULT_VISIBLE; } }); const toggleColumn = useCallback((key) => { setVisibleColumns((prev) => { const next = { ...prev, [key]: !prev[key] }; try { localStorage.setItem("proxyLoggerVisibleColumns", JSON.stringify(next)); } catch {} return next; }); }, []); const fetchLogs = useCallback( async (showLoading = false) => { if (showLoading) setLoading(true); try { const params = new URLSearchParams(); if (search) params.set("search", search); if (activeFilter === "error") params.set("status", "error"); if (activeFilter === "ok") params.set("status", "ok"); if (activeFilter === "timeout") params.set("status", "timeout"); if (selectedType) params.set("type", selectedType); if (selectedProvider) params.set("provider", selectedProvider); if (selectedLevel) params.set("level", selectedLevel); params.set("limit", "300"); const res = await fetch(`/api/usage/proxy-logs?${params}`); if (res.ok) { const data = await res.json(); setLogs(data); } } catch (error) { console.error("Failed to fetch proxy logs:", error); } finally { if (showLoading) setLoading(false); } }, [search, activeFilter, selectedType, selectedProvider, selectedLevel] ); useEffect(() => { const showLoading = !hasLoadedRef.current; hasLoadedRef.current = true; fetchLogs(showLoading); }, [fetchLogs]); useEffect(() => { if (intervalRef.current) clearInterval(intervalRef.current); if (recording) { intervalRef.current = setInterval(() => fetchLogs(false), 3000); } return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, [recording, fetchLogs]); const sortedLogs = useMemo(() => { const arr = [...logs]; arr.sort((a, b) => { switch (sortBy) { case "oldest": return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(); case "latency_desc": return (b.latencyMs || 0) - (a.latencyMs || 0); case "latency_asc": return (a.latencyMs || 0) - (b.latencyMs || 0); case "newest": default: return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(); } }); return arr; }, [logs, sortBy]); const uniqueProviders = [...new Set(logs.map((l) => l.provider).filter(Boolean))].sort(); const uniqueTypes = [...new Set(logs.map((l) => l.proxy?.type).filter(Boolean))].sort(); const uniqueLevels = [...new Set(logs.map((l) => l.level).filter(Boolean))].sort(); const totalCount = logs.length; const okCount = logs.filter((l) => l.status === "success").length; const errorCount = logs.filter((l) => l.status === "error").length; const timeoutCount = logs.filter((l) => l.status === "timeout").length; const directCount = logs.filter((l) => l.level === "direct").length; const tlsCount = logs.filter((l) => l.tlsFingerprint).length; return (
| Status | )} {visibleColumns.proxy && (Proxy | )} {visibleColumns.tls && (TLS | )} {visibleColumns.type && (Type | )} {visibleColumns.level && (Level | )} {visibleColumns.provider && (Provider | )} {visibleColumns.target && (Target | )} {visibleColumns.latency && (Latency | )} {visibleColumns.ip && (Public IP | )} {visibleColumns.time && (Time | )}
|---|---|---|---|---|---|---|---|---|---|
| {log.status} | )} {visibleColumns.proxy && ({log.proxy ? `${log.proxy.host}:${log.proxy.port}` : "—"} | )} {visibleColumns.tls && ({log.tlsFingerprint ? ( 🔒 TLS ) : ( — )} | )} {visibleColumns.type && ({typeColor.label} | )} {visibleColumns.level && ({levelColor.label} | )} {visibleColumns.provider && ({log.provider ? ( {providerColor.label} ) : ( — )} | )} {visibleColumns.target && ({truncateUrl(log.targetUrl)} | )} {visibleColumns.latency && ({formatLatency(log.latencyMs)} | )} {visibleColumns.ip && ({log.publicIp || "—"} | )} {visibleColumns.time && ({formatTime(log.timestamp)} | )}