273 lines
7.6 KiB
Vue
273 lines
7.6 KiB
Vue
|
<template lang='pug'>
|
||
|
v-card
|
||
|
v-toolbar(flat, color='primary', dark, dense)
|
||
|
.subtitle-1 {{ $t('admin:utilities.exportTitle') }}
|
||
|
v-card-text
|
||
|
.text-center
|
||
|
img.animated.fadeInUp.wait-p1s(src='/_assets/svg/icon-big-parcel.svg')
|
||
|
.body-2 Export to tarball / file system
|
||
|
v-divider.my-4
|
||
|
.body-2 What do you want to export?
|
||
|
v-checkbox(
|
||
|
v-for='choice of entityChoices'
|
||
|
:key='choice.key'
|
||
|
:label='choice.label'
|
||
|
:value='choice.key'
|
||
|
color='deep-orange darken-2'
|
||
|
hide-details
|
||
|
v-model='entities'
|
||
|
)
|
||
|
template(v-slot:label)
|
||
|
div
|
||
|
strong.deep-orange--text.text--darken-2 {{choice.label}}
|
||
|
.text-caption {{choice.hint}}
|
||
|
v-text-field.mt-7(
|
||
|
outlined
|
||
|
label='Target Folder Path'
|
||
|
hint='Either an absolute path or relative to the Wiki.js installation folder, where exported content will be saved to. Note that the folder MUST be empty!'
|
||
|
persistent-hint
|
||
|
v-model='filePath'
|
||
|
)
|
||
|
|
||
|
v-alert.mt-3(color='deep-orange', outlined, icon='mdi-alert', prominent)
|
||
|
.body-2 Depending on your selection, the archive could contain sensitive data such as site configuration keys and hashed user passwords. Ensure the exported archive is treated accordingly.
|
||
|
.body-2 For example, you may want to encrypt the archive if stored for backup purposes.
|
||
|
|
||
|
v-card-chin
|
||
|
v-btn.px-3(depressed, color='deep-orange darken-2', :disabled='entities.length < 1', @click='startExport').ml-0
|
||
|
v-icon(left, color='white') mdi-database-export
|
||
|
span.white--text Start Export
|
||
|
v-dialog(
|
||
|
v-model='isLoading'
|
||
|
persistent
|
||
|
max-width='350'
|
||
|
)
|
||
|
v-card(color='deep-orange darken-2', dark)
|
||
|
v-card-text.pa-10.text-center
|
||
|
self-building-square-spinner.animated.fadeIn(
|
||
|
:animation-duration='4500'
|
||
|
:size='40'
|
||
|
color='#FFF'
|
||
|
style='margin: 0 auto;'
|
||
|
)
|
||
|
.mt-5.body-1.white--text Exporting...
|
||
|
.caption Please wait, this may take a while
|
||
|
v-progress-linear.mt-5(
|
||
|
color='white'
|
||
|
:value='progress'
|
||
|
stream
|
||
|
rounded
|
||
|
:buffer-value='0'
|
||
|
)
|
||
|
v-dialog(
|
||
|
v-model='isSuccess'
|
||
|
persistent
|
||
|
max-width='350'
|
||
|
)
|
||
|
v-card(color='green darken-2', dark)
|
||
|
v-card-text.pa-10.text-center
|
||
|
v-icon(size='60') mdi-check-circle-outline
|
||
|
.my-5.body-1.white--text Export completed
|
||
|
v-card-actions.green.darken-1
|
||
|
v-spacer
|
||
|
v-btn.px-5(
|
||
|
color='white'
|
||
|
outlined
|
||
|
@click='isSuccess = false'
|
||
|
) Close
|
||
|
v-spacer
|
||
|
v-dialog(
|
||
|
v-model='isFailed'
|
||
|
persistent
|
||
|
max-width='800'
|
||
|
)
|
||
|
v-card(color='red darken-2', dark)
|
||
|
v-toolbar(color='red darken-2', dense)
|
||
|
v-icon mdi-alert
|
||
|
.body-2.pl-3 Export failed
|
||
|
v-spacer
|
||
|
v-btn.px-5(
|
||
|
color='white'
|
||
|
text
|
||
|
@click='isFailed = false'
|
||
|
) Close
|
||
|
v-card-text.pa-5.red.darken-4.white--text
|
||
|
span {{errorMessage}}
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
import { SelfBuildingSquareSpinner } from 'epic-spinners'
|
||
|
|
||
|
import gql from 'graphql-tag'
|
||
|
import _get from 'lodash/get'
|
||
|
|
||
|
export default {
|
||
|
components: {
|
||
|
SelfBuildingSquareSpinner
|
||
|
},
|
||
|
data() {
|
||
|
return {
|
||
|
entities: [],
|
||
|
filePath: './data/export',
|
||
|
isLoading: false,
|
||
|
isSuccess: false,
|
||
|
isFailed: false,
|
||
|
errorMessage: '',
|
||
|
progress: 0
|
||
|
}
|
||
|
},
|
||
|
computed: {
|
||
|
entityChoices () {
|
||
|
return [
|
||
|
{
|
||
|
key: 'assets',
|
||
|
label: 'Assets',
|
||
|
hint: 'Media files such as images, documents, etc.'
|
||
|
},
|
||
|
{
|
||
|
key: 'comments',
|
||
|
label: 'Comments',
|
||
|
hint: 'Comments made using the default comment module only.'
|
||
|
},
|
||
|
{
|
||
|
key: 'navigation',
|
||
|
label: 'Navigation',
|
||
|
hint: 'Sidebar links when using Static or Custom Navigation.'
|
||
|
},
|
||
|
{
|
||
|
key: 'pages',
|
||
|
label: 'Pages',
|
||
|
hint: 'Page content, tags and related metadata.'
|
||
|
},
|
||
|
{
|
||
|
key: 'history',
|
||
|
label: 'Pages History',
|
||
|
hint: 'All previous versions of pages and their related metadata.'
|
||
|
},
|
||
|
{
|
||
|
key: 'settings',
|
||
|
label: 'Settings',
|
||
|
hint: 'Site configuration and modules settings.'
|
||
|
},
|
||
|
{
|
||
|
key: 'groups',
|
||
|
label: 'User Groups',
|
||
|
hint: 'Group permissions and page rules.'
|
||
|
},
|
||
|
{
|
||
|
key: 'users',
|
||
|
label: 'Users',
|
||
|
hint: 'Users metadata and their group memberships.'
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
async checkProgress () {
|
||
|
try {
|
||
|
const respStatus = await this.$apollo.query({
|
||
|
query: gql`
|
||
|
{
|
||
|
system {
|
||
|
exportStatus {
|
||
|
status
|
||
|
progress
|
||
|
message
|
||
|
startedAt
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
`,
|
||
|
fetchPolicy: 'network-only'
|
||
|
})
|
||
|
const respStatusObj = _get(respStatus, 'data.system.exportStatus', {})
|
||
|
if (!respStatusObj) {
|
||
|
throw new Error('An unexpected error occured.')
|
||
|
} else {
|
||
|
switch (respStatusObj.status) {
|
||
|
case 'error': {
|
||
|
throw new Error(respStatusObj.message || 'An unexpected error occured.')
|
||
|
}
|
||
|
case 'running': {
|
||
|
this.progress = respStatusObj.progress || 0
|
||
|
window.requestAnimationFrame(() => {
|
||
|
setTimeout(() => {
|
||
|
this.checkProgress()
|
||
|
}, 5000)
|
||
|
})
|
||
|
break
|
||
|
}
|
||
|
case 'success': {
|
||
|
this.isLoading = false
|
||
|
this.isSuccess = true
|
||
|
break
|
||
|
}
|
||
|
default: {
|
||
|
throw new Error('Invalid export status.')
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} catch (err) {
|
||
|
this.errorMessage = err.message
|
||
|
this.isLoading = false
|
||
|
this.isFailed = true
|
||
|
}
|
||
|
},
|
||
|
async startExport () {
|
||
|
this.isFailed = false
|
||
|
this.isSuccess = false
|
||
|
this.isLoading = true
|
||
|
this.progress = 0
|
||
|
|
||
|
setTimeout(async () => {
|
||
|
try {
|
||
|
// -> Initiate export
|
||
|
const respExport = await this.$apollo.mutate({
|
||
|
mutation: gql`
|
||
|
mutation (
|
||
|
$entities: [String]!
|
||
|
$path: String!
|
||
|
) {
|
||
|
system {
|
||
|
export (
|
||
|
entities: $entities
|
||
|
path: $path
|
||
|
) {
|
||
|
responseResult {
|
||
|
succeeded
|
||
|
message
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
`,
|
||
|
variables: {
|
||
|
entities: this.entities,
|
||
|
path: this.filePath
|
||
|
}
|
||
|
})
|
||
|
|
||
|
const respExportObj = _get(respExport, 'data.system.export', {})
|
||
|
if (!_get(respExportObj, 'responseResult.succeeded', false)) {
|
||
|
this.errorMessage = _get(respExportObj, 'responseResult.message', 'An unexpected error occurred')
|
||
|
this.isLoading = false
|
||
|
this.isFailed = true
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// -> Check for progress
|
||
|
this.checkProgress()
|
||
|
} catch (err) {
|
||
|
this.$store.commit('pushGraphError', err)
|
||
|
this.isLoading = false
|
||
|
}
|
||
|
}, 1500)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<style lang='scss'>
|
||
|
|
||
|
</style>
|