"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { Bot, ShieldAlert, KeyRound, Radar } from "lucide-react"; import { AdminStatCard } from "@/components/admin"; import { AdminChartCard, NamedBarChart, SingleAreaChart } from "@/components/admin/charts"; import { apiStatsSecurity, type VisitSecurity } from "@/lib/api"; import { RangeSegmented, StatsState, TableEmpty, formatInt } from "../ui"; export default function SecurityPage() { const [range, setRange] = useState<"7d" | "30d">("7d"); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); useEffect(() => { let alive = true; setLoading(true); apiStatsSecurity(range) .then((d) => { if (alive) { setData(d); setError(""); } }) .catch(() => alive && setError("加载失败,请稍后重试")) .finally(() => alive && setLoading(false)); return () => { alive = false; }; }, [range]); if (!data) return ; const botTrend = data.bot_trend.map((d) => ({ date: d.date, pv: d.pv })); return (

爬虫与异常探测请求仅记录明细,不计入 PV/UV 统计。

{error ? : null}
s + b.count, 0))} icon={} /> s + b.count, 0))} hint="状态码 ≥400 的请求" icon={} color="var(--gold)" soft="var(--gold-soft)" /> } color="var(--ink-2)" soft="var(--panel-2)" /> } color="var(--danger)" soft="var(--danger-soft, var(--panel-2))" />
{data.bots.length ? ( ({ label: b.key, count: b.count }))} /> ) : ( )} {botTrend.length ? ( ) : ( )}
{data.probe_status.length ? ( ({ label: s.key, count: s.count }))} /> ) : ( )} {data.probe_ips.length ? (
    {data.probe_ips.map((ip) => (
  • {ip.key} {formatInt(ip.count)} 查明细
  • ))}
) : ( )}
); }