import { useEffect, useState } from 'react'; import { CheckCircle2, Copy, Database, Globe, HardDrive, Layers3, RefreshCw, Server, ShieldCheck, XCircle } from 'lucide-react'; import { motion } from 'motion/react'; import { cn } from '@/src/lib/utils'; import { formatBytes, formatDateTime } from '@/src/lib/format'; import { getAdminFilesystem, type AdminFilesystemResponse } from '@/src/lib/admin-filesystem'; const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { staggerChildren: 0.05, }, }, }; const itemVariants = { hidden: { y: 14, opacity: 0 }, show: { y: 0, opacity: 1 }, }; function statusClass(active: boolean) { return active ? 'bg-green-500/10 text-green-600 dark:text-green-400 border-green-500/20' : 'bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/20'; } function statusIcon(active: boolean) { return active ? : ; } function booleanLabel(active: boolean) { return active ? '启用' : '停用'; } function infoRow(label: string, value: string) { return (
{label}
{value || '-'}
); } function capabilityRow(label: string, active: boolean) { return (
{label} {statusIcon(active)} {booleanLabel(active)}
); } function sectionTitle(title: string, subtitle: string) { return (

{title}

{subtitle}

); } export default function AdminFilesystem() { const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [filesystem, setFilesystem] = useState(null); async function loadFilesystem() { setError(''); try { setFilesystem(await getAdminFilesystem()); } catch (err) { setError(err instanceof Error ? err.message : '加载文件系统信息失败'); } finally { setLoading(false); } } useEffect(() => { void loadFilesystem(); }, []); async function copyText(value: string) { if (!value) { return; } try { await navigator.clipboard.writeText(value); } catch { window.alert('复制失败,请手动复制。'); } } const overviewCards = filesystem ? [ { label: '存储提供者', value: filesystem.overview.storageProvider, icon: , tone: 'blue', }, { label: '文件总数', value: String(filesystem.overview.totalFiles), icon: , tone: 'green', }, { label: '对象总数', value: String(filesystem.overview.totalBlobs), icon: , tone: 'purple', }, { label: '实体总数', value: String(filesystem.overview.totalEntities), icon: , tone: 'amber', }, ] : []; const capabilityEntries = filesystem ? [ ['直传', filesystem.defaultPolicy.capabilities.directUpload], ['分片上传', filesystem.defaultPolicy.capabilities.multipartUpload], ['签名下载', filesystem.defaultPolicy.capabilities.signedDownloadUrl], ['服务端代理下载', filesystem.defaultPolicy.capabilities.serverProxyDownload], ['原生缩略图', filesystem.defaultPolicy.capabilities.thumbnailNative], ['友好下载名', filesystem.defaultPolicy.capabilities.friendlyDownloadName], ['需要 CORS', filesystem.defaultPolicy.capabilities.requiresCors], ['内部端点', filesystem.defaultPolicy.capabilities.supportsInternalEndpoint], ] as const : []; return (

文件系统

存储概览 / 上传模式 / 媒体处理 / 缓存 / WebDAV

{error ?
{error}
: null} {loading && !filesystem ? (
正在读取文件系统快照...
) : filesystem ? ( {overviewCards.map((card) => (
{card.icon}

{card.value}

{card.label}

))}
{sectionTitle('默认存储策略', '当前系统选择的默认分发与对象存储规则')}
{statusIcon(filesystem.defaultPolicy.enabled)} {filesystem.defaultPolicy.enabled ? '默认策略启用' : '默认策略停用'} {filesystem.defaultPolicy.defaultPolicy ? 'DEFAULT' : 'NON-DEFAULT'}
{infoRow('ID', String(filesystem.defaultPolicy.id))} {infoRow('名称', filesystem.defaultPolicy.name)} {infoRow('类型', filesystem.defaultPolicy.type)} {infoRow('访问模式', filesystem.defaultPolicy.privateBucket ? '私有桶' : '公开桶')} {infoRow('Bucket', filesystem.defaultPolicy.bucketName || '-')} {infoRow('Endpoint', filesystem.defaultPolicy.endpoint || '-')} {infoRow('Region', filesystem.defaultPolicy.region || '-')} {infoRow('Prefix', filesystem.defaultPolicy.prefix || '-')} {infoRow('凭证模式', filesystem.defaultPolicy.credentialMode)} {infoRow('策略上限', formatBytes(filesystem.defaultPolicy.maxSizeBytes))} {infoRow('创建时间', formatDateTime(filesystem.defaultPolicy.createdAt))} {infoRow('更新时间', formatDateTime(filesystem.defaultPolicy.updatedAt))}
能力矩阵
{capabilityEntries.map(([label, active]) => capabilityRow(label, active))}
单对象最大值 {formatBytes(filesystem.defaultPolicy.capabilities.maxObjectSize)}
{sectionTitle('上传模式矩阵', '前端只展示服务端暴露的实际可用上传路径')}
{[ { label: '代理上传', active: filesystem.upload.proxyUpload, note: '客户端经由后端转发,适合受控或兼容性场景。' }, { label: '直传单文件', active: filesystem.upload.directSingleUpload, note: '单文件直接命中存储端,适合小文件快速上传。' }, { label: '直传分片', active: filesystem.upload.directMultipartUpload, note: '大文件分片写入,适合稳定传输与断点续传。' }, ].map((item) => (
{item.label}
{item.note}
{statusIcon(item.active)} {booleanLabel(item.active)}
))}
有效最大文件大小 {formatBytes(filesystem.upload.effectiveMaxFileSizeBytes)}
{sectionTitle('媒体处理', '缩略图与元数据采集能力快照')}
元数据提取
文件入库后是否自动提取媒体信息。
{statusIcon(filesystem.mediaProcessing.metadataExtractionEnabled)} {booleanLabel(filesystem.mediaProcessing.metadataExtractionEnabled)}
原生缩略图
是否直接由存储或后端生成缩略图结果。
{statusIcon(filesystem.mediaProcessing.nativeThumbnailSupport)} {booleanLabel(filesystem.mediaProcessing.nativeThumbnailSupport)}
{sectionTitle('缓存状态', '文件列表与目录版本的缓存后端')}
{infoRow('缓存后端', filesystem.cache.backend)} {infoRow('文件列表 TTL', `${filesystem.cache.filesListTtlSeconds} 秒`)} {infoRow('目录版本 TTL', `${filesystem.cache.directoryVersionTtlSeconds} 秒`)}
{sectionTitle('WebDAV 状态', '只读挂载与外部客户端访问能力')}
WebDAV 服务
管理台仅展示后端当前是否暴露 WebDAV。
{filesystem.webdav.enabled ? : } {booleanLabel(filesystem.webdav.enabled)}
说明

当前页面仅展示文件系统快照,不提供就地编辑。所有可变配置仍由系统设置与存储策略页面管理。

) : null}
); }