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 (
);
}
function capabilityRow(label: string, active: boolean) {
return (
{label}
{statusIcon(active)}
{booleanLabel(active)}
);
}
function sectionTitle(title: string, subtitle: string) {
return (
);
}
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) => (
{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}
);
}