source: Implement actions on importable after migration domains
This commit is contained in:
parent
518775341e
commit
735d68e7af
|
@ -36,6 +36,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.happydns.org/happydns/config"
|
||||
|
@ -54,6 +55,9 @@ func init() {
|
|||
|
||||
router.GET("/api/sources/:sid/domains", apiAuthHandler(sourceHandler(getDomainsHostedBySource)))
|
||||
|
||||
router.GET("/api/sources/:sid/domains_with_actions", apiAuthHandler(sourceHandler(getDomainsWithActionsHostedBySource)))
|
||||
router.POST("/api/sources/:sid/domains_with_actions", apiAuthHandler(sourceHandler(doDomainsWithActionsHostedBySource)))
|
||||
|
||||
router.GET("/api/sources/:sid/available_resource_types", apiAuthHandler(sourceHandler(getAvailableResourceTypes)))
|
||||
}
|
||||
|
||||
|
@ -227,6 +231,58 @@ func getDomainsHostedBySource(_ *config.Options, req *RequestResources, body io.
|
|||
}
|
||||
}
|
||||
|
||||
func getDomainsWithActionsHostedBySource(_ *config.Options, req *RequestResources, body io.Reader) Response {
|
||||
sr, ok := req.Source.Source.(sources.ListDomainsWithActionsSource)
|
||||
if !ok {
|
||||
return APIErrorResponse{
|
||||
err: fmt.Errorf("Source doesn't support domain listing."),
|
||||
}
|
||||
}
|
||||
|
||||
if domains, err := sr.ListDomainsWithActions(); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
} else {
|
||||
return APIResponse{
|
||||
response: domains,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doDomainsWithActionsHostedBySource(_ *config.Options, req *RequestResources, body io.Reader) Response {
|
||||
sr, ok := req.Source.Source.(sources.ListDomainsWithActionsSource)
|
||||
if !ok {
|
||||
return APIErrorResponse{
|
||||
err: fmt.Errorf("Source doesn't support domain listing."),
|
||||
}
|
||||
}
|
||||
|
||||
var us sources.ImportableDomain
|
||||
err := json.NewDecoder(body).Decode(&us)
|
||||
if err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if len(us.FQDN) == 0 {
|
||||
return APIErrorResponse{
|
||||
err: fmt.Errorf("Domain to act on not filled"),
|
||||
}
|
||||
}
|
||||
|
||||
success, err := sr.ActionOnListedDomain(us.FQDN, us.BtnAction)
|
||||
status := http.StatusBadRequest
|
||||
if success {
|
||||
status = http.StatusOK
|
||||
}
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
status: status,
|
||||
}
|
||||
}
|
||||
|
||||
func getAvailableResourceTypes(_ *config.Options, req *RequestResources, body io.Reader) Response {
|
||||
lrt, ok := req.Source.Source.(sources.LimitedResourceTypesSource)
|
||||
if !ok {
|
||||
|
|
|
@ -48,6 +48,14 @@ export default {
|
|||
return Api().get('/api/sources/' + encodeURIComponent(srcId) + '/domains')
|
||||
},
|
||||
|
||||
listSourceDomainsWithActions (srcId) {
|
||||
return Api().get('/api/sources/' + encodeURIComponent(srcId) + '/domains_with_actions')
|
||||
},
|
||||
|
||||
doSourceDomainsAction (srcId, fqdn, action) {
|
||||
return Api().post('/api/sources/' + encodeURIComponent(srcId) + '/domains_with_actions', { fqdn, action })
|
||||
},
|
||||
|
||||
deleteSource (source) {
|
||||
return Api().delete('/api/sources/' + encodeURIComponent(source._id))
|
||||
}
|
||||
|
|
|
@ -34,12 +34,22 @@
|
|||
<template>
|
||||
<h-zone-list :domains="listImportableDomains" loading-str="wait.asking-domains" :parent-is-loading="isLoading">
|
||||
<template #badges="{ domain }">
|
||||
<b-badge v-if="haveDomain(domain)" class="ml-1" variant="success">
|
||||
<b-badge v-if="domain.state" class="ml-1" :variant="domain.state">
|
||||
<b-icon v-if="domain.state === 'success'" icon="check" />
|
||||
<b-icon v-else-if="domain.state === 'info'" icon="exclamation-circle" />
|
||||
<b-icon v-else-if="domain.state === 'warning'" icon="exclamation-triangle" />
|
||||
<b-icon v-else-if="domain.state === 'danger'" icon="exclamation-octagon" />
|
||||
{{ domain.message }}
|
||||
</b-badge>
|
||||
<b-badge v-else-if="haveDomain(domain)" class="ml-1" variant="success">
|
||||
<b-icon icon="check" /> {{ $t('service.already') }}
|
||||
</b-badge>
|
||||
<b-button v-else type="button" class="ml-1" variant="primary" size="sm" @click="importDomain(domain)">
|
||||
{{ $t('domains.add-now') }}
|
||||
</b-button>
|
||||
<b-button v-if="domain.btn" type="button" class="ml-1" :variant="domain.state" size="sm" @click="doDomainAction(domain)">
|
||||
{{ $t(domain.btn) }}
|
||||
</b-button>
|
||||
</template>
|
||||
<template v-if="noDomainsList" #no-domain>
|
||||
<b-list-group-item class="text-center">
|
||||
|
@ -79,6 +89,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showDomainsWithActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
source: {
|
||||
type: Object,
|
||||
required: true
|
||||
|
@ -87,6 +101,7 @@ export default {
|
|||
|
||||
data: function () {
|
||||
return {
|
||||
domainsWithActions: null,
|
||||
importableDomains: null
|
||||
}
|
||||
},
|
||||
|
@ -101,7 +116,7 @@ export default {
|
|||
return []
|
||||
}
|
||||
|
||||
var ret = this.importableDomains
|
||||
let ret = this.importableDomains
|
||||
|
||||
if (!this.showAlreadyImported) {
|
||||
ret = ret.filter(
|
||||
|
@ -109,7 +124,15 @@ export default {
|
|||
)
|
||||
}
|
||||
|
||||
return ret.map(d => ({ domain: d, id_source: this.source._id }))
|
||||
ret = ret.map(d => ({ domain: d, id_source: this.source._id }))
|
||||
|
||||
if (this.domainsWithActions && this.showDomainsWithActions) {
|
||||
this.domainsWithActions.forEach((dwa) => {
|
||||
ret.push({ domain: dwa.fqdn, state: dwa.state, message: dwa.message, btn: dwa.btn, action: dwa.action, id_source: this.source._id })
|
||||
})
|
||||
}
|
||||
|
||||
return ret
|
||||
},
|
||||
|
||||
noDomainsList () {
|
||||
|
@ -144,20 +167,48 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
doDomainAction (domain) {
|
||||
SourcesApi.doSourceDomainsAction(this.source._id, domain.domain, domain.action)
|
||||
.then(
|
||||
response => {
|
||||
this.$store.dispatch('domains/getAllMyDomains')
|
||||
if (response.data.errmsg) {
|
||||
this.$root.$bvToast.toast(
|
||||
response.data.errmsg, {
|
||||
title: this.$t('domains.attached-new'),
|
||||
autoHideDelay: 5000,
|
||||
variant: 'success',
|
||||
toaster: 'b-toaster-content-right'
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.$root.$bvToast.toast(
|
||||
error.response.data.errmsg, {
|
||||
title: this.$t('errors.domain-attach'),
|
||||
autoHideDelay: 5000,
|
||||
variant: 'danger',
|
||||
toaster: 'b-toaster-content-right'
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
haveDomain (domain) {
|
||||
return this.domains_getAll.find(i => i.domain === domain.domain)
|
||||
},
|
||||
|
||||
importAllDomains () {
|
||||
this.listImportableDomains.forEach((domain) => {
|
||||
if (!this.haveDomain(domain)) {
|
||||
if (!domain.state && !this.haveDomain(domain)) {
|
||||
this.importDomain(domain)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
importDomain (domain) {
|
||||
var vm = this
|
||||
const vm = this
|
||||
this.addDomainToSource(this.source, domain.domain, null, function (data) {
|
||||
vm.$store.dispatch('domains/getAllMyDomains')
|
||||
})
|
||||
|
@ -189,6 +240,24 @@ export default {
|
|||
)
|
||||
this.$router.replace('/sources/' + encodeURIComponent(this.mySource._id))
|
||||
})
|
||||
|
||||
if (!this.showDomainsWithActions || this.sourceSpecs_getAll[this.source._srctype].capabilities.indexOf('ListDomainsWithActions') === -1) {
|
||||
return
|
||||
}
|
||||
|
||||
SourcesApi.listSourceDomainsWithActions(this.source._id)
|
||||
.then(
|
||||
response => (this.domainsWithActions = response.data),
|
||||
error => {
|
||||
this.$root.$bvToast.toast(
|
||||
error.response.data.errmsg, {
|
||||
title: this.$t('errors.domain-access'),
|
||||
autoHideDelay: 5000,
|
||||
variant: 'danger',
|
||||
toaster: 'b-toaster-content-right'
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
"domains": {
|
||||
"kind": "domain",
|
||||
"actions": {
|
||||
"do-migration": "Migrate now",
|
||||
"reimport": "Re-import",
|
||||
"view": "View my zone",
|
||||
"propagate": "Publish my changes",
|
||||
|
|
|
@ -45,6 +45,9 @@ import {
|
|||
BIconChevronLeft,
|
||||
BIconChevronRight,
|
||||
BIconChevronUp,
|
||||
BIconExclamationCircle,
|
||||
BIconExclamationOctagon,
|
||||
BIconExclamationTriangle,
|
||||
BIconGridFill,
|
||||
BIconLink,
|
||||
BIconLink45deg,
|
||||
|
@ -126,6 +129,9 @@ Vue.component('BIconChevronDown', BIconChevronDown)
|
|||
Vue.component('BIconChevronLeft', BIconChevronLeft)
|
||||
Vue.component('BIconChevronRight', BIconChevronRight)
|
||||
Vue.component('BIconChevronUp', BIconChevronUp)
|
||||
Vue.component('BIconExclamationCircle', BIconExclamationCircle)
|
||||
Vue.component('BIconExclamationOctagon', BIconExclamationOctagon)
|
||||
Vue.component('BIconExclamationTriangle', BIconExclamationTriangle)
|
||||
Vue.component('BIconGridFill', BIconGridFill)
|
||||
Vue.component('BIconLink', BIconLink)
|
||||
Vue.component('BIconLink45deg', BIconLink45deg)
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
</b-button>
|
||||
</div>
|
||||
</template>
|
||||
<h-source-list-domains ref="newDomains" :source="filteredSource" @no-domains-list-change="noDomainsList = $event" />
|
||||
<h-source-list-domains ref="newDomains" :source="filteredSource" show-domains-with-actions @no-domains-list-change="noDomainsList = $event" />
|
||||
</b-card>
|
||||
<h-list-group-input-new-domain v-if="$refs.zlist && !$refs.zlist.isLoading && (!filteredSource || noDomainsList)" autofocus class="mt-2" :my-source="filteredSource" />
|
||||
</b-col>
|
||||
|
|
|
@ -51,12 +51,39 @@ type SourceInfos struct {
|
|||
Capabilities []string `json:"capabilities,omitempty"`
|
||||
}
|
||||
|
||||
// ImportableDomain is a structure dedicated to handle actions associated with special domains.
|
||||
type ImportableDomain struct {
|
||||
// FQDN is the domain FQDN.
|
||||
FQDN string `json:"fqdn"`
|
||||
|
||||
// State represents the importable state of the domaine between: danger, warning, info, success.
|
||||
State string `json:"state,omitempty"`
|
||||
|
||||
// Msg is the message to display to the user.
|
||||
Msg string `json:"message,omitempty"`
|
||||
|
||||
// Btn is the label to display on the button (no message = no button).
|
||||
Btn string `json:"btn,omitempty"`
|
||||
|
||||
// BtnAction is the name of the event raised by clicking on the button.
|
||||
BtnAction string `json:"action,omitempty"`
|
||||
}
|
||||
|
||||
// ListDomainsSource are functions to declare when we can retrives a list of managable domains from the given Source.
|
||||
type ListDomainsSource interface {
|
||||
// ListDomains retrieves the list of avaiable domains inside the Source.
|
||||
ListDomains() ([]string, error)
|
||||
}
|
||||
|
||||
// ListDomainsWithActionsSource are functions to declare when we can retrives a list of domains from the given Source, but not directly importable, it requires or can handle actions.
|
||||
type ListDomainsWithActionsSource interface {
|
||||
// ListDomains retrieves the list of avaiable domains inside the Source.
|
||||
ListDomainsWithActions() ([]ImportableDomain, error)
|
||||
|
||||
// ActionOnListedDomain calls the action designated on the button for the given domain.
|
||||
ActionOnListedDomain(string, string) (bool, error)
|
||||
}
|
||||
|
||||
type LimitedResourceTypesSource interface {
|
||||
ListAvailableTypes() []uint16
|
||||
}
|
||||
|
@ -69,6 +96,10 @@ func GetSourceCapabilities(src happydns.Source) (caps []string) {
|
|||
caps = append(caps, "ListDomains")
|
||||
}
|
||||
|
||||
if _, ok := src.(ListDomainsWithActionsSource); ok {
|
||||
caps = append(caps, "ListDomainsWithActions")
|
||||
}
|
||||
|
||||
if _, ok := src.(forms.CustomSettingsForm); ok {
|
||||
caps = append(caps, "CustomSettingsForm")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user