Skip to content

Commit

Permalink
feat: duplicate site config to remote #70
Browse files Browse the repository at this point in the history
  • Loading branch information
0xJacky committed May 15, 2023
1 parent bc6a25c commit 6c38b11
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 12 deletions.
3 changes: 3 additions & 0 deletions frontend/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ declare module '@vue/runtime-core' {
ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
AButton: typeof import('ant-design-vue/es')['Button']
ACard: typeof import('ant-design-vue/es')['Card']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup']
ACol: typeof import('ant-design-vue/es')['Col']
ACollapse: typeof import('ant-design-vue/es')['Collapse']
ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
Expand Down Expand Up @@ -70,6 +72,7 @@ declare module '@vue/runtime-core' {
FooterToolbarFooterToolBar: typeof import('./src/components/FooterToolbar/FooterToolBar.vue')['default']
LogoLogo: typeof import('./src/components/Logo/Logo.vue')['default']
NginxControlNginxControl: typeof import('./src/components/NginxControl/NginxControl.vue')['default']
NodeSelectorNodeSelector: typeof import('./src/components/NodeSelector/NodeSelector.vue')['default']
PageHeaderPageHeader: typeof import('./src/components/PageHeader/PageHeader.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/api/curd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class Curd {
return http.get(this.baseUrl + (id ? '/' + id : ''))
}

_save(id: any = null, data: any) {
return http.post(this.baseUrl + (id ? '/' + id : ''), data)
_save(id: any = null, data: any, config: any = undefined) {
return http.post(this.baseUrl + (id ? '/' + id : ''), data, config)
}

_destroy(id: any = null) {
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/EnvIndicator/EnvIndicator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ watch(node_id, () => {
align-items: center;
justify-content: space-between;
.env-name {
max-width: 50px;
}
.ant-tag {
cursor: pointer;
margin-right: 0;
Expand Down
51 changes: 51 additions & 0 deletions frontend/src/components/NodeSelector/NodeSelector.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script setup lang="ts">
import {computed, ref} from 'vue'
import environment from '@/api/environment'
import {useGettext} from 'vue3-gettext'
const {$gettext} = useGettext()
const props = defineProps(['target', 'map'])
const emit = defineEmits(['update:target'])
const data = ref([])
const data_map = ref({})
environment.get_list().then(r => {
data.value = r.data
r.data.forEach(node => {
data_map[node.id] = node
})
})
const value = computed({
get() {
return props.target
},
set(v) {
if (typeof props.map === 'object') {
v.forEach(id => {
if (id !== 0) props.map[id] = data_map[id].name
})
}
emit('update:target', v)
}
})
</script>

<template>
<a-checkbox-group v-model:value="value" style="width: 100%">
<a-row>
<a-col :span="8">
<a-checkbox :value="0">{{ $gettext('Local') }}</a-checkbox>
</a-col>
<a-col :span="8" v-for="node in data">
<a-checkbox :value="node.id">{{ node.name }}</a-checkbox>
</a-col>
</a-row>
</a-checkbox-group>
</template>

<style scoped lang="less">
</style>
6 changes: 5 additions & 1 deletion frontend/src/pinia/moudule/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ export const useSettingsStore = defineStore('settings', {
name: 'Local'
}
}),
getters: {},
getters: {
is_remote(): boolean {
return this.environment.id !== 0
}
},
actions: {
set_language(lang: string) {
this.language = lang
Expand Down
58 changes: 49 additions & 9 deletions frontend/src/views/domain/SiteDuplicate.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
<script setup lang="ts">
import {computed, nextTick, reactive, ref, watch} from 'vue'
import {useGettext} from 'vue3-gettext'
import {Form, message} from 'ant-design-vue'
import {Form, message, notification} from 'ant-design-vue'
import gettext from '@/gettext'
import domain from '@/api/domain'
import NodeSelector from '@/components/NodeSelector/NodeSelector.vue'
import {useSettingsStore} from '@/pinia'
const {$gettext} = useGettext()
const props = defineProps(['visible', 'name'])
const emit = defineEmits(['update:visible', 'duplicated'])
const settings = useSettingsStore()
const show = computed({
get() {
return props.visible
Expand All @@ -19,7 +23,7 @@ const show = computed({
}
})
const modelRef = reactive({name: ''})
const modelRef = reactive({name: '', target: []})
const rulesRef = reactive({
name: [
Expand All @@ -28,23 +32,55 @@ const rulesRef = reactive({
message: () => $gettext('Please input name, ' +
'this will be used as the filename of the new configuration!')
}
],
target: [
{
required: true,
message: () => $gettext('Please select at least one node!')
}
]
})
const {validate, validateInfos, clearValidate} = Form.useForm(modelRef, rulesRef)
const loading = ref(false)
const node_map = reactive({})
function onSubmit() {
validate().then(async () => {
loading.value = true
domain.duplicate(props.name, {name: modelRef.name}).then(() => {
message.success($gettext('Duplicated successfully'))
show.value = false
emit('duplicated')
}).catch((e: any) => {
message.error($gettext(e?.message ?? 'Server error'))
modelRef.target.forEach(id => {
if (id === 0) {
domain.duplicate(props.name, {name: modelRef.name}).then(() => {
message.success($gettext('Duplicate to local successfully'))
show.value = false
emit('duplicated')
}).catch((e: any) => {
message.error($gettext(e?.message ?? 'Server error'))
})
} else {
// get source content
domain.get(props.name).then(r => {
domain.save(modelRef.name, {
name: modelRef.name,
content: r.config
}, {headers: {'X-Node-ID': id}}).then(() => {
notification.success({
message: $gettext('Duplicate successfully'),
description:
$gettext('Duplicate %{conf_name} to %{node_name} successfully',
{conf_name: props.name, node_name: node_map[id]})
})
}).catch(e => {
notification.error({
message: $gettext('Duplicate failed'),
description: $gettext(e.message)
})
})
})
}
})
loading.value = false
Expand All @@ -54,6 +90,7 @@ function onSubmit() {
watch(() => props.visible, (v) => {
if (v) {
modelRef.name = ''
modelRef.target = [0]
nextTick(() => clearValidate())
}
})
Expand All @@ -65,11 +102,14 @@ watch(() => gettext.current, () => {

<template>
<a-modal :title="$gettext('Duplicate')" v-model:visible="show" @ok="onSubmit"
:confirm-loading="loading">
:confirm-loading="loading" :mask="null">
<a-form layout="vertical">
<a-form-item :label="$gettext('Name')" v-bind="validateInfos.name">
<a-input v-model:value="modelRef.name"/>
</a-form-item>
<a-form-item v-if="!settings.is_remote" :label="$gettext('Target')" v-bind="validateInfos.target">
<node-selector v-model:target="modelRef.target" :map="node_map"/>
</a-form-item>
</a-form>
</a-modal>
</template>
Expand Down

0 comments on commit 6c38b11

Please sign in to comment.