"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { RefreshCcw, Pause, Play } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; export function LogsPageClient() { const [lines, setLines] = useState([]); const [level, setLevel] = useState(""); const [limit, setLimit] = useState(100); const [autoRefresh, setAutoRefresh] = useState(false); const [error, setError] = useState(""); const timerRef = useRef | null>(null); const load = useCallback(async () => { try { const params = new URLSearchParams({ lines: String(limit) }); if (level) params.set("level", level); const res = await fetch(`/api/ops/view-logs?${params}`); if (res.ok) { const data = (await res.json()) as { lines?: string[] }; setLines(data.lines ?? []); setError(""); } else { setError(await res.text().catch(() => "fetch failed")); } } catch { setError("日志 API 尚未就绪(需要后端支持)"); } }, [level, limit]); useEffect(() => { void load(); }, [load]); useEffect(() => { if (autoRefresh) { timerRef.current = setInterval(load, 5000); } else { if (timerRef.current) clearInterval(timerRef.current); } return () => { if (timerRef.current) clearInterval(timerRef.current); }; }, [autoRefresh, load]); const levelColor = (line: string) => { if (line.includes("ERROR")) return "text-red-400"; if (line.includes("WARNING")) return "text-amber-400"; if (line.includes("INFO")) return "text-slate-300"; return "text-slate-500"; }; return (

日志查看

{error ? (
{error}
) : (
{lines.map((line, i) => (
{line}
))} {lines.length === 0 && (
暂无日志
)}
)}
); }