mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 23:27:17 +08:00
286 lines
7.3 KiB
Vue
286 lines
7.3 KiB
Vue
<script setup lang="ts">
|
|
import backup from '@/api/panel/backup'
|
|
import type { MessageReactive } from 'naive-ui'
|
|
import { NButton, NDataTable, NFlex, NInput, NPopconfirm } from 'naive-ui'
|
|
import { useGettext } from 'vue3-gettext'
|
|
|
|
import app from '@/api/panel/app'
|
|
import website from '@/api/panel/website'
|
|
import { formatDateTime } from '@/utils'
|
|
import UploadModal from '@/views/backup/UploadModal.vue'
|
|
|
|
const { $gettext } = useGettext()
|
|
const type = defineModel<string>('type', { type: String, required: true })
|
|
|
|
let messageReactive: MessageReactive | null = null
|
|
|
|
const uploadModal = ref(false)
|
|
|
|
const createModal = ref(false)
|
|
const createModel = ref({
|
|
target: '',
|
|
path: ''
|
|
})
|
|
|
|
const restoreModal = ref(false)
|
|
const restoreModel = ref({
|
|
file: '',
|
|
target: ''
|
|
})
|
|
|
|
const websites = ref<any>([])
|
|
|
|
const columns: any = [
|
|
{
|
|
title: $gettext('Filename'),
|
|
key: 'name',
|
|
minWidth: 200,
|
|
resizable: true,
|
|
ellipsis: { tooltip: true }
|
|
},
|
|
{
|
|
title: $gettext('Size'),
|
|
key: 'size',
|
|
width: 160,
|
|
ellipsis: { tooltip: true }
|
|
},
|
|
{
|
|
title: $gettext('Update Date'),
|
|
key: 'time',
|
|
width: 200,
|
|
ellipsis: { tooltip: true },
|
|
render(row: any) {
|
|
return formatDateTime(row.time)
|
|
}
|
|
},
|
|
{
|
|
title: $gettext('Actions'),
|
|
key: 'actions',
|
|
width: 260,
|
|
hideInExcel: true,
|
|
render(row: any) {
|
|
return [
|
|
h(
|
|
NButton,
|
|
{
|
|
size: 'small',
|
|
type: 'warning',
|
|
secondary: true,
|
|
onClick: () => {
|
|
restoreModel.value.file = row.path
|
|
restoreModal.value = true
|
|
}
|
|
},
|
|
{
|
|
default: () => $gettext('Restore')
|
|
}
|
|
),
|
|
h(
|
|
NPopconfirm,
|
|
{
|
|
onPositiveClick: () => handleDelete(row.name)
|
|
},
|
|
{
|
|
default: () => {
|
|
return $gettext('Are you sure you want to delete this backup?')
|
|
},
|
|
trigger: () => {
|
|
return h(
|
|
NButton,
|
|
{
|
|
size: 'small',
|
|
type: 'error',
|
|
style: 'margin-left: 15px;'
|
|
},
|
|
{
|
|
default: () => $gettext('Delete')
|
|
}
|
|
)
|
|
}
|
|
}
|
|
)
|
|
]
|
|
}
|
|
}
|
|
]
|
|
|
|
const { loading, data, page, total, pageSize, pageCount, refresh } = usePagination(
|
|
(page, pageSize) => backup.list(type.value, page, pageSize),
|
|
{
|
|
initialData: { total: 0, list: [] },
|
|
initialPageSize: 20,
|
|
total: (res: any) => res.total,
|
|
data: (res: any) => res.items
|
|
}
|
|
)
|
|
|
|
const handleCreate = () => {
|
|
useRequest(backup.create(type.value, createModel.value.target, createModel.value.path)).onSuccess(
|
|
() => {
|
|
createModal.value = false
|
|
window.$bus.emit('backup:refresh')
|
|
window.$message.success($gettext('Created successfully'))
|
|
}
|
|
)
|
|
}
|
|
|
|
const handleRestore = () => {
|
|
messageReactive = window.$message.loading($gettext('Restoring...'), {
|
|
duration: 0
|
|
})
|
|
|
|
useRequest(backup.restore(type.value, restoreModel.value.file, restoreModel.value.target))
|
|
.onSuccess(() => {
|
|
refresh()
|
|
window.$message.success($gettext('Restored successfully'))
|
|
})
|
|
.onComplete(() => {
|
|
messageReactive?.destroy()
|
|
})
|
|
}
|
|
|
|
const handleDelete = async (file: string) => {
|
|
useRequest(backup.delete(type.value, file)).onSuccess(() => {
|
|
refresh()
|
|
window.$message.success($gettext('Deleted successfully'))
|
|
})
|
|
}
|
|
|
|
watch(
|
|
type,
|
|
(newType) => {
|
|
if (newType === 'website') {
|
|
createModel.value.target = websites.value[0]?.value || ''
|
|
restoreModel.value.target = websites.value[0]?.value || ''
|
|
} else {
|
|
createModel.value.target = ''
|
|
restoreModel.value.target = ''
|
|
}
|
|
refresh()
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
onMounted(() => {
|
|
useRequest(app.isInstalled('nginx')).onSuccess(({ data }) => {
|
|
if (data) {
|
|
useRequest(website.list('all', 1, 10000)).onSuccess(({ data }: { data: any }) => {
|
|
for (const item of data.items) {
|
|
websites.value.push({
|
|
label: item.name,
|
|
value: item.name
|
|
})
|
|
}
|
|
if (type.value === 'website') {
|
|
createModel.value.target = websites.value[0]?.value
|
|
restoreModel.value.target = websites.value[0]?.value
|
|
}
|
|
})
|
|
}
|
|
})
|
|
refresh()
|
|
window.$bus.on('backup:refresh', refresh)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.$bus.off('backup:refresh')
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<n-flex vertical :size="20">
|
|
<n-flex>
|
|
<n-button type="primary" @click="createModal = true">{{
|
|
$gettext('Create Backup')
|
|
}}</n-button>
|
|
<n-button type="primary" @click="uploadModal = true" ghost>{{
|
|
$gettext('Upload Backup')
|
|
}}</n-button>
|
|
</n-flex>
|
|
<n-data-table
|
|
striped
|
|
remote
|
|
:scroll-x="1000"
|
|
:loading="loading"
|
|
:columns="columns"
|
|
:data="data"
|
|
:row-key="(row: any) => row.name"
|
|
v-model:page="page"
|
|
v-model:pageSize="pageSize"
|
|
:pagination="{
|
|
page: page,
|
|
pageCount: pageCount,
|
|
pageSize: pageSize,
|
|
itemCount: total,
|
|
showQuickJumper: true,
|
|
showSizePicker: true,
|
|
pageSizes: [20, 50, 100, 200]
|
|
}"
|
|
/>
|
|
</n-flex>
|
|
<n-modal
|
|
v-model:show="createModal"
|
|
preset="card"
|
|
:title="$gettext('Create Backup')"
|
|
style="width: 60vw"
|
|
size="huge"
|
|
:bordered="false"
|
|
:segmented="false"
|
|
@close="createModal = false"
|
|
>
|
|
<n-form :model="createModel">
|
|
<n-form-item v-if="type == 'website'" path="name" :label="$gettext('Website')">
|
|
<n-select
|
|
v-model:value="createModel.target"
|
|
:options="websites"
|
|
:placeholder="$gettext('Select website')"
|
|
/>
|
|
</n-form-item>
|
|
<n-form-item v-if="type != 'website'" path="name" :label="$gettext('Database Name')">
|
|
<n-input
|
|
v-model:value="createModel.target"
|
|
type="text"
|
|
@keydown.enter.prevent
|
|
:placeholder="$gettext('Enter database name')"
|
|
/>
|
|
</n-form-item>
|
|
<n-form-item path="path" :label="$gettext('Save Directory')">
|
|
<n-input
|
|
v-model:value="createModel.path"
|
|
type="text"
|
|
@keydown.enter.prevent
|
|
:placeholder="$gettext('Leave empty to use default path')"
|
|
/>
|
|
</n-form-item>
|
|
</n-form>
|
|
<n-button type="info" block @click="handleCreate">{{ $gettext('Submit') }}</n-button>
|
|
</n-modal>
|
|
<n-modal
|
|
v-model:show="restoreModal"
|
|
preset="card"
|
|
:title="$gettext('Restore Backup')"
|
|
style="width: 60vw"
|
|
size="huge"
|
|
:bordered="false"
|
|
:segmented="false"
|
|
@close="restoreModal = false"
|
|
>
|
|
<n-form :model="restoreModel">
|
|
<n-form-item v-if="type == 'website'" path="name" :label="$gettext('Website')">
|
|
<n-select
|
|
v-model:value="restoreModel.target"
|
|
:options="websites"
|
|
:placeholder="$gettext('Select website')"
|
|
/>
|
|
</n-form-item>
|
|
<n-form-item v-if="type != 'website'" path="name" :label="$gettext('Database')">
|
|
<n-input v-model:value="restoreModel.target" type="text" @keydown.enter.prevent />
|
|
</n-form-item>
|
|
</n-form>
|
|
<n-button type="info" block @click="handleRestore">{{ $gettext('Submit') }}</n-button>
|
|
</n-modal>
|
|
<upload-modal v-model:show="uploadModal" v-model:type="type" />
|
|
</template>
|
|
|
|
<style scoped lang="scss"></style>
|