feat(storage): add dynamic storage config and migration

Move storage backend configuration from startup YAML to system_config-backed runtime configuration. Add local, S3-compatible, R2, MinIO, OSS, and WebDAV backend support.

Add a storage migration async task using the existing task dispatch framework. Migration target config is carried in task payload, and maintenance mode is derived from task execution state.

Split upload file management and storage operations, add the admin storage configuration tab, and update migrations and Swagger docs.
This commit is contained in:
ryan
2026-06-13 14:55:15 +08:00
parent 4bf8a4806e
commit 9a18bea324
46 changed files with 2005 additions and 743 deletions
+9 -3
View File
@@ -6,6 +6,7 @@ import {motion} from "motion/react"
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs"
import {FileStats} from "./file-stats"
import {FileList} from "./file-list"
import {StorageConfigTab} from "./storage-config-tab"
export function FilesMain() {
const [activeTab, setActiveTab] = React.useState("stats")
@@ -15,7 +16,7 @@ export function FilesMain() {
initial={{ opacity: 0, y: 15 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35, ease: "easeOut" }}
className="py-6 space-y-6 w-full"
className="flex w-full flex-col gap-6 py-6"
>
{/* 顶部标题区 */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 border-b pb-5">
@@ -29,10 +30,11 @@ export function FilesMain() {
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full space-y-6">
<TabsList className="grid w-fit grid-cols-2">
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex w-full flex-col gap-6">
<TabsList className="grid w-fit grid-cols-3">
<TabsTrigger value="stats">文件存储信息</TabsTrigger>
<TabsTrigger value="list">文件列表</TabsTrigger>
<TabsTrigger value="storage">存储配置</TabsTrigger>
</TabsList>
{/* ──────── TAB 1: 统计看板 ──────── */}
@@ -44,6 +46,10 @@ export function FilesMain() {
<TabsContent value="list" className="outline-hidden">
<FileList />
</TabsContent>
<TabsContent value="storage" className="outline-hidden">
<StorageConfigTab />
</TabsContent>
</Tabs>
</motion.div>
)