"use client" import { useEffect, useState, useCallback } from "react" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { useToast } from "@/components/ui/use-toast" import { Switch } from "@/components/ui/switch" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { useRouter } from "next/navigation" interface CacheStats { total_items: number total_size: number hit_count: number miss_count: number hit_rate: number bytes_saved: number enabled: boolean } interface CacheConfig { max_age: number cleanup_tick: number max_cache_size: number } interface CacheData { proxy: CacheStats mirror: CacheStats fixedPath: CacheStats } interface CacheConfigs { proxy: CacheConfig mirror: CacheConfig fixedPath: CacheConfig } function formatBytes(bytes: number) { const units = ['B', 'KB', 'MB', 'GB'] let size = bytes let unitIndex = 0 while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024 unitIndex++ } return `${size.toFixed(2)} ${units[unitIndex]}` } export default function CachePage() { const [stats, setStats] = useState(null) const [configs, setConfigs] = useState(null) const [loading, setLoading] = useState(true) const { toast } = useToast() const router = useRouter() const fetchStats = useCallback(async () => { try { const token = localStorage.getItem("token") if (!token) { router.push("/login") return } const response = await fetch("/admin/api/cache/stats", { headers: { 'Authorization': `Bearer ${token}` } }) if (response.status === 401) { localStorage.removeItem("token") router.push("/login") return } if (!response.ok) throw new Error("获取缓存统计失败") const data = await response.json() setStats(data) } catch (error) { toast({ title: "错误", description: error instanceof Error ? error.message : "获取缓存统计失败", variant: "destructive", }) } finally { setLoading(false) } }, [toast, router]) const fetchConfigs = useCallback(async () => { try { const token = localStorage.getItem("token") if (!token) { router.push("/login") return } const response = await fetch("/admin/api/cache/config", { headers: { 'Authorization': `Bearer ${token}` } }) if (response.status === 401) { localStorage.removeItem("token") router.push("/login") return } if (!response.ok) throw new Error("获取缓存配置失败") const data = await response.json() setConfigs(data) } catch (error) { toast({ title: "错误", description: error instanceof Error ? error.message : "获取缓存配置失败", variant: "destructive", }) } }, [toast, router]) useEffect(() => { // 立即获取一次数据 fetchStats() fetchConfigs() // 设置定时刷新 const interval = setInterval(fetchStats, 1000) return () => clearInterval(interval) }, [fetchStats, fetchConfigs]) const handleToggleCache = async (type: "proxy" | "mirror" | "fixedPath", enabled: boolean) => { try { const token = localStorage.getItem("token") if (!token) { router.push("/login") return } const response = await fetch("/admin/api/cache/enable", { method: "POST", headers: { "Content-Type": "application/json", 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ type, enabled }), }) if (response.status === 401) { localStorage.removeItem("token") router.push("/login") return } if (!response.ok) throw new Error("切换缓存状态失败") toast({ title: "成功", description: `${type === "proxy" ? "代理" : type === "mirror" ? "镜像" : "固定路径"}缓存已${enabled ? "启用" : "禁用"}`, }) fetchStats() } catch (error) { toast({ title: "错误", description: error instanceof Error ? error.message : "切换缓存状态失败", variant: "destructive", }) } } const handleUpdateConfig = async (type: "proxy" | "mirror" | "fixedPath", config: CacheConfig) => { try { const token = localStorage.getItem("token") if (!token) { router.push("/login") return } const response = await fetch("/admin/api/cache/config", { method: "POST", headers: { "Content-Type": "application/json", 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ type, config }), }) if (response.status === 401) { localStorage.removeItem("token") router.push("/login") return } if (!response.ok) throw new Error("更新缓存配置失败") toast({ title: "成功", description: "缓存配置已更新", }) fetchConfigs() } catch (error) { toast({ title: "错误", description: error instanceof Error ? error.message : "更新缓存配置失败", variant: "destructive", }) } } const handleClearCache = async (type: "proxy" | "mirror" | "fixedPath" | "all") => { try { const token = localStorage.getItem("token") if (!token) { router.push("/login") return } const response = await fetch("/admin/api/cache/clear", { method: "POST", headers: { "Content-Type": "application/json", 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ type }), }) if (response.status === 401) { localStorage.removeItem("token") router.push("/login") return } if (!response.ok) throw new Error("清理缓存失败") toast({ title: "成功", description: "缓存已清理", }) fetchStats() } catch (error) { toast({ title: "错误", description: error instanceof Error ? error.message : "清理缓存失败", variant: "destructive", }) } } const renderCacheConfig = (type: "proxy" | "mirror" | "fixedPath") => { if (!configs) return null const config = configs[type] return (

缓存配置

{ const newConfigs = { ...configs } newConfigs[type].max_age = parseInt(e.target.value) setConfigs(newConfigs) }} onBlur={() => handleUpdateConfig(type, config)} />
{ const newConfigs = { ...configs } newConfigs[type].cleanup_tick = parseInt(e.target.value) setConfigs(newConfigs) }} onBlur={() => handleUpdateConfig(type, config)} />
{ const newConfigs = { ...configs } newConfigs[type].max_cache_size = parseInt(e.target.value) setConfigs(newConfigs) }} onBlur={() => handleUpdateConfig(type, config)} />
) } if (loading) { return (
加载中...
正在获取缓存统计信息
) } return (
{/* 代理缓存 */} 代理缓存
handleToggleCache("proxy", checked)} />
缓存项数量
{stats?.proxy.total_items ?? 0}
总大小
{formatBytes(stats?.proxy.total_size ?? 0)}
命中次数
{stats?.proxy.hit_count ?? 0}
未命中次数
{stats?.proxy.miss_count ?? 0}
命中率
{(stats?.proxy.hit_rate ?? 0).toFixed(2)}%
节省带宽
{formatBytes(stats?.proxy.bytes_saved ?? 0)}
{renderCacheConfig("proxy")}
{/* 镜像缓存 */} 镜像缓存
handleToggleCache("mirror", checked)} />
缓存项数量
{stats?.mirror.total_items ?? 0}
总大小
{formatBytes(stats?.mirror.total_size ?? 0)}
命中次数
{stats?.mirror.hit_count ?? 0}
未命中次数
{stats?.mirror.miss_count ?? 0}
命中率
{(stats?.mirror.hit_rate ?? 0).toFixed(2)}%
节省带宽
{formatBytes(stats?.mirror.bytes_saved ?? 0)}
{renderCacheConfig("mirror")}
{/* 固定路径缓存 */} 固定路径缓存
handleToggleCache("fixedPath", checked)} />
缓存项数量
{stats?.fixedPath.total_items ?? 0}
总大小
{formatBytes(stats?.fixedPath.total_size ?? 0)}
命中次数
{stats?.fixedPath.hit_count ?? 0}
未命中次数
{stats?.fixedPath.miss_count ?? 0}
命中率
{(stats?.fixedPath.hit_rate ?? 0).toFixed(2)}%
节省带宽
{formatBytes(stats?.fixedPath.bytes_saved ?? 0)}
{renderCacheConfig("fixedPath")}
) }