mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-12 02:06:37 +08:00
feat(frontend): support manual storage migration triggering
- Modify storage-config-tab.tsx to separate storage configuration saving from task dispatching. - Save operations now always preserve the currently active storage driver to prevent premature storage engine switching. - Add a manual '开始迁移' button to trigger storage migration explicitly. - Display a warning notice to the operator when the selected storage type differs from the active one.
This commit is contained in:
@@ -134,23 +134,32 @@ export function StorageConfigTab() {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (query.data?.config) {
|
if (query.data?.config) {
|
||||||
setConfig(normalizeConfig(query.data.config))
|
setConfig((current) => {
|
||||||
|
const normalized = normalizeConfig(query.data.config)
|
||||||
|
if (current) {
|
||||||
|
return {
|
||||||
|
...normalized,
|
||||||
|
driver: current.driver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalized
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}, [query.data?.config])
|
}, [query.data?.config])
|
||||||
|
|
||||||
const saveMutation = useMutation({
|
const saveMutation = useMutation({
|
||||||
mutationFn: async (value: StorageConfig) => {
|
mutationFn: async (value: StorageConfig) => {
|
||||||
if (query.data?.config.driver === value.driver) {
|
const activeDriver = query.data?.config.driver || value.driver
|
||||||
await AdminService.updateSystemConfig(storageConfigKey, {
|
const valueToSave = {
|
||||||
value: JSON.stringify(value),
|
...value,
|
||||||
})
|
driver: activeDriver,
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
const payload: StorageMigrationPayload = {target: value}
|
await AdminService.updateSystemConfig(storageConfigKey, {
|
||||||
return AdminService.dispatchTask({task_type: storageMigrationTaskType, payload: JSON.stringify(payload)})
|
value: JSON.stringify(valueToSave),
|
||||||
|
})
|
||||||
},
|
},
|
||||||
onSuccess: (taskID) => {
|
onSuccess: () => {
|
||||||
toast.success(taskID ? "配置已保存,存储迁移任务已下发" : "存储配置已保存")
|
toast.success("存储配置已保存")
|
||||||
void queryClient.invalidateQueries({queryKey: ["admin", "storage-config"]})
|
void queryClient.invalidateQueries({queryKey: ["admin", "storage-config"]})
|
||||||
},
|
},
|
||||||
onError: (error: Error) => {
|
onError: (error: Error) => {
|
||||||
@@ -158,6 +167,24 @@ export function StorageConfigTab() {
|
|||||||
void queryClient.invalidateQueries({queryKey: ["admin", "storage-config"]})
|
void queryClient.invalidateQueries({queryKey: ["admin", "storage-config"]})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const migrateMutation = useMutation({
|
||||||
|
mutationFn: async (value: StorageConfig) => {
|
||||||
|
const payload: StorageMigrationPayload = {target: value}
|
||||||
|
return AdminService.dispatchTask({
|
||||||
|
task_type: storageMigrationTaskType,
|
||||||
|
payload: JSON.stringify(payload),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success("存储迁移任务已下发")
|
||||||
|
void queryClient.invalidateQueries({queryKey: ["admin", "storage-config"]})
|
||||||
|
},
|
||||||
|
onError: (error: Error) => {
|
||||||
|
toast.error(error.message || "下发存储迁移任务失败")
|
||||||
|
void queryClient.invalidateQueries({queryKey: ["admin", "storage-config"]})
|
||||||
|
},
|
||||||
|
})
|
||||||
const runMutation = useMutation({
|
const runMutation = useMutation({
|
||||||
mutationFn: (executionID: string) => AdminService.retryTaskExecution(executionID),
|
mutationFn: (executionID: string) => AdminService.retryTaskExecution(executionID),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -173,7 +200,10 @@ export function StorageConfigTab() {
|
|||||||
|
|
||||||
const migration = query.data?.migration
|
const migration = query.data?.migration
|
||||||
const isReadOnly = migration ? migration.state !== "idle" : false
|
const isReadOnly = migration ? migration.state !== "idle" : false
|
||||||
const isFormDisabled = migration ? (migration.state === "pending" || migration.state === "running") : false
|
const isFormDisabled =
|
||||||
|
(migration ? (migration.state === "pending" || migration.state === "running") : false) ||
|
||||||
|
saveMutation.isPending ||
|
||||||
|
migrateMutation.isPending
|
||||||
|
|
||||||
const updateObject = (driver: "s3" | "r2" | "minio" | "oss", patch: Partial<ObjectStorageConfig>) => {
|
const updateObject = (driver: "s3" | "r2" | "minio" | "oss", patch: Partial<ObjectStorageConfig>) => {
|
||||||
setConfig((current) => current ? {
|
setConfig((current) => current ? {
|
||||||
@@ -276,11 +306,31 @@ export function StorageConfigTab() {
|
|||||||
)}
|
)}
|
||||||
</FieldGroup>
|
</FieldGroup>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="justify-end gap-2">
|
<CardFooter className="justify-end gap-2 flex-col sm:flex-row items-end sm:items-center">
|
||||||
<Button disabled={isFormDisabled || saveMutation.isPending} onClick={() => saveMutation.mutate(config)}>
|
{config.driver !== query.data?.config.driver && (
|
||||||
{saveMutation.isPending ? <Loader2 data-icon="inline-start" className="animate-spin" /> : <Save data-icon="inline-start" />}
|
<span className="text-xs text-amber-500 mr-auto text-left max-w-md">
|
||||||
保存配置
|
⚠️ 您已切换存储类型。请先点击“保存配置”保存各存储端的配置凭据,然后点击“开始迁移”手动执行文件迁移。
|
||||||
</Button>
|
</span>
|
||||||
|
)}
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
disabled={isFormDisabled}
|
||||||
|
onClick={() => saveMutation.mutate(config)}
|
||||||
|
>
|
||||||
|
{saveMutation.isPending ? <Loader2 data-icon="inline-start" className="animate-spin" /> : <Save data-icon="inline-start" />}
|
||||||
|
保存配置
|
||||||
|
</Button>
|
||||||
|
{config.driver !== query.data?.config.driver && (
|
||||||
|
<Button
|
||||||
|
disabled={isFormDisabled}
|
||||||
|
onClick={() => migrateMutation.mutate(config)}
|
||||||
|
>
|
||||||
|
{migrateMutation.isPending ? <Loader2 data-icon="inline-start" className="animate-spin" /> : <Play data-icon="inline-start" />}
|
||||||
|
开始迁移
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user