'use client'; import * as React from 'react'; import { useCallback, useEffect, useState } from 'react'; import { useTranslations } from 'next-intl'; import { toast } from 'sonner'; import { HardDrive, RefreshCw, Trash2 } from 'lucide-react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Skeleton } from '@/components/ui/skeleton'; import { Label } from '@/components/ui/label'; import { Input } from '@/components/ui/input'; import { Switch } from '@/components/ui/switch'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import type { CacheStatus } from '@/lib/services/admin'; import services from '@/lib/services'; /** * 格式化字节大小 */ const formatBytes = (bytes: number) => { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; /** * 格式化数字,每3位加逗号 */ const formatNumber = (num: number | string) => { if (num === undefined || num === null) return '0'; return num.toString().replace(/\B(?=(\d{3})+(?!\B))/g, ','); }; interface CacheManagerProps { refreshTrigger: number; } export function CacheManager({ refreshTrigger }: CacheManagerProps) { const t = useTranslations('admin.database.cache'); const [cacheStatus, setCacheStatus] = useState(null); const [loadingCache, setLoadingCache] = useState(true); const [savingConfig, setSavingConfig] = useState(false); const [clearingCache, setClearingCache] = useState(false); const [showClearConfirm, setShowClearConfirm] = useState(false); // 策略配置表单状态 const [maxSizeMB, setMaxSizeMB] = useState('100'); const [ttlMinutes, setTtlMinutes] = useState('60'); const [lruEnabled, setLruEnabled] = useState(true); // 获取磁盘缓存状态 const fetchCacheStatus = useCallback( async (isSilent = false) => { if (!isSilent) setLoadingCache(true); try { const data = await services.adminCache.getCacheStatus(); setCacheStatus(data); setMaxSizeMB(data.max_size_mb.toString()); setTtlMinutes(data.ttl_minutes.toString()); setLruEnabled(data.lru_enabled); } catch (err) { toast.error(t('fetchCacheStatusFailed'), { description: err instanceof Error ? err.message : t('unknownError'), }); } finally { setLoadingCache(false); } }, [t], ); // 保存配置 const handleSaveConfig = async (e: React.FormEvent) => { e.preventDefault(); const size = parseInt(maxSizeMB, 10); const ttl = parseInt(ttlMinutes, 10); if (isNaN(size) || size < 1) { toast.error(t('saveFailed'), { description: t('maxSizeMustBePositive'), }); return; } if (isNaN(ttl) || ttl < 0) { toast.error(t('saveFailed'), { description: t('ttlMustBeNonNegative'), }); return; } setSavingConfig(true); try { await services.adminCache.updateCacheConfig({ max_size_mb: size, ttl_minutes: ttl, lru_enabled: lruEnabled, }); toast.success(t('saveSuccess'), { description: t('cachePolicyHotUpdated'), }); await fetchCacheStatus(true); } catch (err) { toast.error(t('saveConfigFailed'), { description: err instanceof Error ? err.message : t('unknownError'), }); } finally { setSavingConfig(false); } }; // 清空磁盘缓存数据 const handleClearCache = async () => { setClearingCache(true); try { await services.adminCache.clearCache(); toast.success(t('clearSuccess'), { description: t('cacheDataCleared') }); setShowClearConfirm(false); await fetchCacheStatus(true); } catch (err) { toast.error(t('clearCacheFailed'), { description: err instanceof Error ? err.message : t('unknownError'), }); } finally { setClearingCache(false); } }; // 初始化拉取 useEffect(() => { fetchCacheStatus(); }, [fetchCacheStatus]); // 当外部刷新触发器改变时自动刷新 useEffect(() => { fetchCacheStatus(true); }, [refreshTrigger, fetchCacheStatus]); return (
{t('cacheManagement')}
{t('cacheManagementDesc')}
{loadingCache && !cacheStatus ? (
) : cacheStatus ? (
{/* 左边:状态区 (2/5 cols) */}

{t('runtimeStatus')}

{/* 已占空间 */}

{t('usedSpace')}

{formatBytes(cacheStatus.total_size)}

{/* Key数量 */}

{t('cacheKeysCount')}

{formatNumber(cacheStatus.keys_count)}{' '} {t('files')}

{/* 存储路径 */}

{t('cacheBaseDirectory')}

{cacheStatus.base_path}
{/* 右边:配置区 (3/5 cols) */}

{t('policyConfig')}

setMaxSizeMB(e.target.value)} className='h-8 text-xs bg-background/50 border-border/40' placeholder={t('maxSizePlaceholder')} required />

{t('maxSizeHint')}

setTtlMinutes(e.target.value)} className='h-8 text-xs bg-background/50 border-border/40' placeholder={t('ttlPlaceholder')} required />

{t('ttlHint')}

{t('enableLruDesc')}
) : (
{t('noCacheStatus')}
)}
{/* 清除缓存确认对话框 */} {t('confirmClearAllCache')} {t('confirmClearDesc')}
); }