Files
rustfs-console/components/replication/ReplicationConfig.vue
T
cxymds c3bfd2d34b Refactory/bucketsetting (#45)
* feat: refactory bucket setting

* fix: translate

* feat:permission

* feat : lockfile

* fix: update lockfile

* fix: i18n variable
2026-01-29 20:05:03 +08:00

200 lines
5.2 KiB
Vue

<template>
<div class="flex flex-col gap-4">
<div class="flex items-center justify-between">
<h2 class="text-lg font-medium">{{ t('Bucket Replication') }}</h2>
<div class="flex gap-2">
<Button variant="outline" @click="openForm">
<Icon name="ri:add-line" class="size-4" />
<span>{{ t('Add Replication Rule') }}</span>
</Button>
<Button variant="outline" @click="() => loadReplication()">
<Icon name="ri:refresh-line" class="size-4" />
<span>{{ t('Refresh') }}</span>
</Button>
</div>
</div>
<DataTable
:table="table"
:is-loading="loading"
:empty-title="t('No Data')"
:empty-description="t('Add replication rules to sync objects across buckets.')"
/>
<replication-new-form ref="addFormRef" :bucketName="bucketName" @success="() => loadReplication()" />
</div>
</template>
<script lang="ts" setup>
import { Button } from '@/components/ui/button'
import { Icon } from '#components'
import DataTable from '@/components/data-table/data-table.vue'
import { useDataTable } from '@/components/data-table/useDataTable'
import { Badge } from '@/components/ui/badge'
import { useBucket } from '@/composables/useBucket'
import type { ColumnDef } from '@tanstack/vue-table'
import { h, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
const props = defineProps<{
bucketName: string
}>()
const { t } = useI18n()
const message = useMessage()
const dialog = useDialog()
const { getBucketReplication, putBucketReplication, deleteBucketReplication, deleteRemoteReplicationTarget } =
useBucket({})
interface ReplicationRule {
ID?: string
Status?: string
Priority?: number
Filter?: {
Prefix?: string
}
Destination?: {
Bucket?: string
StorageClass?: string
}
}
const rules = ref<ReplicationRule[]>([])
const loading = ref(false)
const columns: ColumnDef<ReplicationRule>[] = [
{
accessorKey: 'ID',
header: () => t('Rule ID'),
cell: ({ row }) => h('span', row.original.ID || '-'),
},
{
accessorKey: 'Status',
header: () => t('Status'),
cell: ({ row }) =>
h(Badge, { variant: row.original.Status === 'Enabled' ? 'secondary' : 'outline' }, () =>
row.original.Status === 'Enabled' ? t('Enabled') : t('Disabled')
),
},
{
accessorKey: 'Priority',
header: () => t('Priority'),
cell: ({ row }) => h('span', String(row.original.Priority ?? '-')),
},
{
id: 'Filter',
header: () => t('Prefix'),
cell: ({ row }) => h('span', row.original.Filter?.Prefix || '-'),
},
{
id: 'destination-bucket',
header: () => t('Destination Bucket'),
cell: ({ row }) => {
const bucketArn = row.original.Destination?.Bucket || ''
return h('span', bucketArn.replace(/^arn:aws:s3:::/, '') || '-')
},
},
{
id: 'destination-storage',
header: () => t('Storage Class'),
cell: ({ row }) => h('span', row.original.Destination?.StorageClass || '-'),
},
{
id: 'actions',
header: () => t('Actions'),
enableSorting: false,
cell: ({ row }) =>
h('div', { class: 'flex items-center gap-2' }, [
h(
Button,
{
variant: 'outline',
size: 'sm',
onClick: () => confirmDelete(row.original),
},
() => [h(Icon, { name: 'ri:delete-bin-7-line', class: 'size-4' }), h('span', t('Delete'))]
),
]),
},
]
const { table } = useDataTable<ReplicationRule>({
data: rules,
columns,
getRowId: row => row.ID ?? JSON.stringify(row),
})
const loadReplication = async () => {
if (!props.bucketName) {
rules.value = []
return
}
loading.value = true
try {
const res = await getBucketReplication(props.bucketName)
rules.value = res?.ReplicationConfiguration?.Rules ?? []
} catch (error) {
rules.value = []
} finally {
loading.value = false
}
}
watch(
() => props.bucketName,
() => {
loadReplication()
},
{ immediate: true }
)
const confirmDelete = (rule: ReplicationRule) => {
dialog.error({
title: t('Warning'),
content: t('Are you sure you want to delete this replication rule?'),
positiveText: t('Confirm'),
negativeText: t('Cancel'),
onPositiveClick: () => handleRowDelete(rule),
})
}
const handleRowDelete = async (rule: ReplicationRule) => {
const remaining = rules.value.filter(item => item !== rule)
if (!props.bucketName) {
return
}
try {
if (remaining.length === 0) {
await deleteBucketReplication(props.bucketName)
await deleteRemoteReplicationTarget(props.bucketName, rule.Destination?.Bucket ?? '')
} else {
// 获取当前配置以获取 Role 字段
const currentConfig = await getBucketReplication(props.bucketName)
const role = currentConfig?.ReplicationConfiguration?.Role
if (!role) {
throw new Error('Replication configuration Role is missing')
}
await putBucketReplication(props.bucketName, {
Role: role,
Rules: remaining,
})
}
message.success(t('Delete Success'))
await loadReplication()
} catch (error: any) {
message.error(error?.message || t('Delete Failed'))
}
}
const addFormRef = ref<{ open: () => void }>()
const openForm = () => {
addFormRef.value?.open()
}
</script>