mirror of https://github.com/halo-dev/halo
feat: support export database. (halo-dev/console#101)
parent
d1e520a476
commit
2c98963fbc
|
@ -6,7 +6,7 @@ const backupApi = {}
|
||||||
|
|
||||||
backupApi.importMarkdown = (formData, uploadProgress, cancelToken) => {
|
backupApi.importMarkdown = (formData, uploadProgress, cancelToken) => {
|
||||||
return service({
|
return service({
|
||||||
url: `${baseUrl}/import/markdown`,
|
url: `${baseUrl}/markdown`,
|
||||||
timeout: 8640000, // 24 hours
|
timeout: 8640000, // 24 hours
|
||||||
data: formData, // form data
|
data: formData, // form data
|
||||||
onUploadProgress: uploadProgress,
|
onUploadProgress: uploadProgress,
|
||||||
|
@ -15,24 +15,49 @@ backupApi.importMarkdown = (formData, uploadProgress, cancelToken) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
backupApi.backupHalo = () => {
|
backupApi.backupWorkDir = () => {
|
||||||
return service({
|
return service({
|
||||||
url: `${baseUrl}/halo`,
|
url: `${baseUrl}/work-dir`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
timeout: 8640000 // 24 hours
|
timeout: 8640000 // 24 hours
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
backupApi.listHaloBackups = () => {
|
backupApi.listWorkDirBackups = () => {
|
||||||
return service({
|
return service({
|
||||||
url: `${baseUrl}/halo`,
|
url: `${baseUrl}/work-dir`,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
backupApi.deleteHaloBackup = filename => {
|
backupApi.deleteWorkDirBackup = filename => {
|
||||||
return service({
|
return service({
|
||||||
url: `${baseUrl}/halo`,
|
url: `${baseUrl}/work-dir`,
|
||||||
|
params: {
|
||||||
|
filename: filename
|
||||||
|
},
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
backupApi.exportData = () => {
|
||||||
|
return service({
|
||||||
|
url: `${baseUrl}/data`,
|
||||||
|
method: 'post',
|
||||||
|
timeout: 8640000 // 24 hours
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
backupApi.listExportedData = () => {
|
||||||
|
return service({
|
||||||
|
url: `${baseUrl}/data`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
backupApi.deleteExportedData = filename => {
|
||||||
|
return service({
|
||||||
|
url: `${baseUrl}/data`,
|
||||||
params: {
|
params: {
|
||||||
filename: filename
|
filename: filename
|
||||||
},
|
},
|
||||||
|
|
|
@ -64,12 +64,27 @@
|
||||||
<div slot="title">
|
<div slot="title">
|
||||||
<a-icon type="hdd" /> 博客备份
|
<a-icon type="hdd" /> 博客备份
|
||||||
</div>
|
</div>
|
||||||
<p style="min-height: 50px;">备份全站数据,支持下载到本地</p>
|
<p style="min-height: 50px;">支持备份全站数据和数据导出,支持下载到本地</p>
|
||||||
<a-button
|
|
||||||
type="primary"
|
<a-dropdown style="float:right">
|
||||||
style="float:right"
|
<a-menu slot="overlay">
|
||||||
@click="backupDrawerVisible = true"
|
<a-menu-item
|
||||||
>备份</a-button>
|
key="1"
|
||||||
|
@click="backupWorkDirDrawerVisible = true"
|
||||||
|
>
|
||||||
|
整站备份
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item
|
||||||
|
key="2"
|
||||||
|
@click="exportDataDrawerVisible = true"
|
||||||
|
>
|
||||||
|
数据导出
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
<a-button style="margin-left: 8px"> 备份
|
||||||
|
<a-icon type="down" />
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col
|
<a-col
|
||||||
|
@ -111,7 +126,8 @@
|
||||||
:uploadHandler="uploadHandler"
|
:uploadHandler="uploadHandler"
|
||||||
></FilePondUpload>
|
></FilePondUpload>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<BackupDrawer v-model="backupDrawerVisible"></BackupDrawer>
|
<BackupWorkDirDrawer v-model="backupWorkDirDrawerVisible"></BackupWorkDirDrawer>
|
||||||
|
<ExportDataDrawer v-model="exportDataDrawerVisible"></ExportDataDrawer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -119,13 +135,15 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import backupApi from '@/api/backup'
|
import backupApi from '@/api/backup'
|
||||||
import BackupDrawer from './components/BackupDrawer'
|
import BackupWorkDirDrawer from './components/BackupWorkDirDrawer'
|
||||||
|
import ExportDataDrawer from './components/ExportDataDrawer'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { BackupDrawer },
|
components: { BackupWorkDirDrawer, ExportDataDrawer },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
backupDrawerVisible: false,
|
backupWorkDirDrawerVisible: false,
|
||||||
|
exportDataDrawerVisible: false,
|
||||||
markdownUpload: false,
|
markdownUpload: false,
|
||||||
uploadHandler: backupApi.importMarkdown
|
uploadHandler: backupApi.importMarkdown
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<a-drawer
|
<a-drawer
|
||||||
title="博客备份"
|
title="整站备份"
|
||||||
:width="isMobile()?'100%':'480'"
|
:width="isMobile()?'100%':'480'"
|
||||||
closable
|
closable
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
type="dashed"
|
type="dashed"
|
||||||
icon="reload"
|
icon="reload"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="handleBAckupRefreshClick"
|
@click="handleBackupRefreshClick"
|
||||||
>刷新</a-button>
|
>刷新</a-button>
|
||||||
</div>
|
</div>
|
||||||
</a-drawer>
|
</a-drawer>
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||||
import backupApi from '@/api/backup'
|
import backupApi from '@/api/backup'
|
||||||
export default {
|
export default {
|
||||||
name: 'BackupDrawer',
|
name: 'BackupWorkDirDrawer',
|
||||||
mixins: [mixin, mixinDevice],
|
mixins: [mixin, mixinDevice],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -105,15 +105,15 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
visible: function(newValue, oldValue) {
|
visible: function(newValue, oldValue) {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
this.getBackups()
|
this.listBackups()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getBackups() {
|
listBackups() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
backupApi
|
backupApi
|
||||||
.listHaloBackups()
|
.listWorkDirBackups()
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.backups = response.data.data
|
this.backups = response.data.data
|
||||||
})
|
})
|
||||||
|
@ -122,10 +122,10 @@ export default {
|
||||||
handleBackupClick() {
|
handleBackupClick() {
|
||||||
this.backuping = true
|
this.backuping = true
|
||||||
backupApi
|
backupApi
|
||||||
.backupHalo()
|
.backupWorkDir()
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.$notification.success({ message: '备份成功!' })
|
this.$message.success('备份成功!')
|
||||||
this.getBackups()
|
this.listBackups()
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.backuping = false
|
this.backuping = false
|
||||||
|
@ -134,15 +134,15 @@ export default {
|
||||||
handleBackupDeleteClick(filename) {
|
handleBackupDeleteClick(filename) {
|
||||||
this.deleting = true
|
this.deleting = true
|
||||||
backupApi
|
backupApi
|
||||||
.deleteHaloBackup(filename)
|
.deleteWorkDirBackup(filename)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.$notification.success({ message: '删除成功!' })
|
this.$message.success('删除成功!')
|
||||||
this.getBackups()
|
this.listBackups()
|
||||||
})
|
})
|
||||||
.finally(() => (this.deleting = false))
|
.finally(() => (this.deleting = false))
|
||||||
},
|
},
|
||||||
handleBAckupRefreshClick() {
|
handleBackupRefreshClick() {
|
||||||
this.getBackups()
|
this.listBackups()
|
||||||
},
|
},
|
||||||
onClose() {
|
onClose() {
|
||||||
this.$emit('close', false)
|
this.$emit('close', false)
|
|
@ -0,0 +1,152 @@
|
||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
title="数据导出"
|
||||||
|
:width="isMobile()?'100%':'480'"
|
||||||
|
closable
|
||||||
|
:visible="visible"
|
||||||
|
destroyOnClose
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<a-row
|
||||||
|
type="flex"
|
||||||
|
align="middle"
|
||||||
|
>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-alert
|
||||||
|
message="注意:导出后的数据文件存储在临时文件中,重启服务器会造成备份文件的丢失,所以请尽快下载!"
|
||||||
|
banner
|
||||||
|
closable
|
||||||
|
/>
|
||||||
|
<a-divider>历史文件</a-divider>
|
||||||
|
<a-list
|
||||||
|
itemLayout="vertical"
|
||||||
|
size="small"
|
||||||
|
:dataSource="files"
|
||||||
|
>
|
||||||
|
<a-list-item
|
||||||
|
slot="renderItem"
|
||||||
|
slot-scope="file"
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
slot="extra"
|
||||||
|
type="link"
|
||||||
|
style="color: red"
|
||||||
|
icon="delete"
|
||||||
|
:loading="deleting"
|
||||||
|
@click="handleFileDeleteClick(file.filename)"
|
||||||
|
>删除</a-button>
|
||||||
|
<a-list-item-meta>
|
||||||
|
<a
|
||||||
|
slot="title"
|
||||||
|
:href="file.downloadUrl"
|
||||||
|
>
|
||||||
|
<a-icon
|
||||||
|
type="schedule"
|
||||||
|
style="color: #52c41a"
|
||||||
|
/>
|
||||||
|
{{ file.filename }}
|
||||||
|
</a>
|
||||||
|
<p slot="description">{{ file.updateTime | timeAgo }}/{{ file.fileSize | fileSizeFormat }}</p>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
<div
|
||||||
|
v-if="loading"
|
||||||
|
class="loading-container"
|
||||||
|
style="position: absolute;bottom: 40px; width: 100%;text-align: center;"
|
||||||
|
>
|
||||||
|
<a-spin />
|
||||||
|
</div>
|
||||||
|
</a-list>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-divider class="divider-transparent" />
|
||||||
|
<div class="bottom-control">
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
icon="download"
|
||||||
|
style="marginRight: 8px"
|
||||||
|
:loading="backuping"
|
||||||
|
@click="handleExportClick"
|
||||||
|
>备份</a-button>
|
||||||
|
<a-button
|
||||||
|
type="dashed"
|
||||||
|
icon="reload"
|
||||||
|
:loading="loading"
|
||||||
|
@click="handleFilesRefreshClick"
|
||||||
|
>刷新</a-button>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||||
|
import backupApi from '@/api/backup'
|
||||||
|
export default {
|
||||||
|
name: 'ExportDataDrawer',
|
||||||
|
mixins: [mixin, mixinDevice],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
backuping: false,
|
||||||
|
loading: false,
|
||||||
|
deleting: false,
|
||||||
|
files: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'visible',
|
||||||
|
event: 'close'
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible: function(newValue, oldValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.listFiles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
listFiles() {
|
||||||
|
this.loading = true
|
||||||
|
backupApi
|
||||||
|
.listExportedData()
|
||||||
|
.then(response => {
|
||||||
|
this.files = response.data.data
|
||||||
|
})
|
||||||
|
.finally(() => (this.loading = false))
|
||||||
|
},
|
||||||
|
handleExportClick() {
|
||||||
|
this.backuping = true
|
||||||
|
backupApi
|
||||||
|
.exportData()
|
||||||
|
.then(response => {
|
||||||
|
this.$message.success('导出成功!')
|
||||||
|
this.listFiles()
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.backuping = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleFileDeleteClick(filename) {
|
||||||
|
this.deleting = true
|
||||||
|
backupApi
|
||||||
|
.deleteExportedData(filename)
|
||||||
|
.then(response => {
|
||||||
|
this.$message.success('删除成功!')
|
||||||
|
this.listFiles()
|
||||||
|
})
|
||||||
|
.finally(() => (this.deleting = false))
|
||||||
|
},
|
||||||
|
handleFilesRefreshClick() {
|
||||||
|
this.listFiles()
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
this.$emit('close', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
Loading…
Reference in New Issue