Add family tabs filters
This commit is contained in:
parent
53da0f60cb
commit
eeec4e7a63
|
@ -0,0 +1,295 @@
|
|||
<!--
|
||||
Copyright or © or Copr. happyDNS (2020)
|
||||
|
||||
contact@happydns.org
|
||||
|
||||
This software is a computer program whose purpose is to provide a modern
|
||||
interface to interact with DNS systems.
|
||||
|
||||
This software is governed by the CeCILL license under French law and abiding
|
||||
by the rules of distribution of free software. You can use, modify and/or
|
||||
redistribute the software under the terms of the CeCILL license as
|
||||
circulated by CEA, CNRS and INRIA at the following URL
|
||||
"http://www.cecill.info".
|
||||
|
||||
As a counterpart to the access to the source code and rights to copy, modify
|
||||
and redistribute granted by the license, users are provided only with a
|
||||
limited warranty and the software's author, the holder of the economic
|
||||
rights, and the successive licensors have only limited liability.
|
||||
|
||||
In this respect, the user's attention is drawn to the risks associated with
|
||||
loading, using, modifying and/or developing or reproducing the software by
|
||||
the user in light of its specific status of free software, that may mean
|
||||
that it is complicated to manipulate, and that also therefore means that it
|
||||
is reserved for developers and experienced professionals having in-depth
|
||||
computer knowledge. Users are therefore encouraged to load and test the
|
||||
software's suitability as regards their requirements in conditions enabling
|
||||
the security of their systems and/or data to be ensured and, more generally,
|
||||
to use and operate it in the same conditions as regards security.
|
||||
|
||||
The fact that you are presently reading this means that you have had
|
||||
knowledge of the CeCILL license and that you accept its terms.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<b-tabs :content-class="contentClass">
|
||||
<b-tab title="All" active>
|
||||
<b-list-group>
|
||||
<b-list-group-item v-for="(svc, idx) in availableNewServices()" :key="idx" :active="value === idx" button @click="$emit('input', idx)">
|
||||
{{ svc.name }}
|
||||
<small class="text-muted">{{ svc.description }}</small>
|
||||
<b-badge v-for="(categorie, idcat) in svc.categories" :key="idcat" variant="gray" class="float-right ml-1">
|
||||
{{ categorie }}
|
||||
</b-badge>
|
||||
</b-list-group-item>
|
||||
<b-list-group-item v-for="(svc, idx) in disabledNewServices()" :key="idx" :active="value === idx" disabled @click="$emit('input', idx)">
|
||||
<span :title="svc.description">{{ svc.name }}</span> <small class="font-italic text-danger">{{ filteredNewServices[idx] }}</small>
|
||||
<b-badge v-for="(categorie, idcat) in svc.categories" :key="idcat" variant="gray" class="float-right ml-1">
|
||||
{{ categorie }}
|
||||
</b-badge>
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
</b-tab>
|
||||
<b-tab v-for="(family, idxf) in families" :key="idxf" :title="family.label">
|
||||
<b-list-group>
|
||||
<b-list-group-item v-for="(svc, idx) in availableNewServices(family.family)" :key="idx" :active="value === idx" button @click="$emit('input', idx)">
|
||||
{{ svc.name }}
|
||||
<small class="text-muted">{{ svc.description }}</small>
|
||||
<b-badge v-for="(categorie, idcat) in svc.categories" :key="idcat" variant="gray" class="float-right ml-1">
|
||||
{{ categorie }}
|
||||
</b-badge>
|
||||
</b-list-group-item>
|
||||
<b-list-group-item v-for="(svc, idx) in disabledNewServices(family.family)" :key="idx" :active="value === idx" disabled @click="$emit('input', idx)">
|
||||
<span :title="svc.description">{{ svc.name }}</span> <small class="font-italic text-danger">{{ filteredNewServices[idx] }}</small>
|
||||
<b-badge v-for="(categorie, idcat) in svc.categories" :key="idcat" variant="gray" class="float-right ml-1">
|
||||
{{ categorie }}
|
||||
</b-badge>
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SourcesApi from '@/services/SourcesApi'
|
||||
|
||||
export default {
|
||||
name: 'HFamilyTabs',
|
||||
|
||||
props: {
|
||||
contentClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
domain: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
dn: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
myServices: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
services: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
availableResourceTypes: [],
|
||||
families: [
|
||||
{
|
||||
label: 'Services',
|
||||
family: 'abstract'
|
||||
},
|
||||
{
|
||||
label: 'Providers',
|
||||
family: 'provider'
|
||||
},
|
||||
{
|
||||
label: 'Raw DNS resources',
|
||||
family: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isLoading () {
|
||||
return this.availableResourceTypes.length === 0
|
||||
},
|
||||
|
||||
filteredNewServices () {
|
||||
return this.analyzeRestrictions(this.services)
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
SourcesApi.getAvailableResourceTypes(this.domain.id_source)
|
||||
.then(
|
||||
(response) => {
|
||||
this.availableResourceTypes = response.data
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
methods: {
|
||||
analyzeRestrictions (allServices) {
|
||||
var ret = {}
|
||||
|
||||
for (const type in allServices) {
|
||||
const svc = allServices[type]
|
||||
|
||||
if (svc.restrictions) {
|
||||
// Handle NeedTypes restriction: hosting provider need to support given types.
|
||||
if (svc.restrictions.needTypes) {
|
||||
for (const k in svc.restrictions.needTypes) {
|
||||
if (this.availableResourceTypes.indexOf(svc.restrictions.needTypes[k]) < 0) {
|
||||
ret[type] = 'is not available on this domain name hosting provider.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Alone restriction: only nearAlone are allowed.
|
||||
if (svc.restrictions.alone && this.myServices.services[this.dn]) {
|
||||
var found = false
|
||||
for (const k in this.myServices.services[this.dn]) {
|
||||
const s = this.myServices.services[this.dn][k]
|
||||
if (s._svctype !== type && allServices[s._svctype].restrictions && !allServices[s._svctype].restrictions.nearAlone) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ret[type] = 'has to be the only one in the subdomain.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Exclusive restriction: service can't be present along with another listed one.
|
||||
if (svc.restrictions.exclusive && this.myServices.services[this.dn]) {
|
||||
found = null
|
||||
for (const k in this.myServices.services[this.dn]) {
|
||||
const s = this.myServices.services[this.dn][k]
|
||||
for (const i in svc.restrictions.exclusive) {
|
||||
if (s._svctype === svc.restrictions.exclusive[i]) {
|
||||
found = s._svctype
|
||||
break
|
||||
}
|
||||
}
|
||||
if (found != null) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ret[type] = 'cannot be present along with ' + allServices[found].name + '.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Check reverse Exclusivity
|
||||
found = null
|
||||
for (const k in this.myServices.services[this.dn]) {
|
||||
const s = this.services[this.myServices.services[this.dn][k]._svctype]
|
||||
if (!s.restrictions || !s.restrictions.exclusive) {
|
||||
continue
|
||||
}
|
||||
for (const i in s.restrictions.exclusive) {
|
||||
if (svc._svctype === s.restrictions.exclusive[i]) {
|
||||
found = s._svctype
|
||||
break
|
||||
}
|
||||
}
|
||||
if (found != null) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ret[type] = 'cannot be present along with ' + allServices[found].name + '.'
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle rootOnly restriction.
|
||||
if (svc.restrictions.rootOnly && this.dn !== '') {
|
||||
ret[type] = 'can only be present at the root of your domain.'
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle Single restriction: only one instance of the service per subdomain.
|
||||
if (svc.restrictions.single && this.myServices.services[this.dn]) {
|
||||
found = false
|
||||
for (const k in this.myServices.services[this.dn]) {
|
||||
const s = this.myServices.services[this.dn][k]
|
||||
if (s._svctype === type) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ret[type] = 'can only be present once per subdomain.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Handle presence of Alone and Leaf service in subdomain already.
|
||||
var oneAlone = null
|
||||
var oneLeaf = null
|
||||
for (const k in this.myServices.services[this.dn]) {
|
||||
const s = this.myServices.services[this.dn][k]
|
||||
if (this.services[s._svctype] && this.services[s._svctype].restrictions && this.services[s._svctype].restrictions.alone) {
|
||||
oneAlone = s._svctype
|
||||
}
|
||||
if (this.services[s._svctype] && this.services[s._svctype].restrictions && this.services[s._svctype].restrictions.leaf) {
|
||||
oneLeaf = s._svctype
|
||||
}
|
||||
}
|
||||
if (oneAlone && oneAlone !== type && !svc.restrictions.nearAlone) {
|
||||
ret[type] = 'cannot be present along with ' + allServices[oneAlone].name + ', that requires to be the only one in this subdomain.'
|
||||
continue
|
||||
}
|
||||
if (oneLeaf && oneLeaf !== type && !svc.restrictions.glue) {
|
||||
ret[type] = 'cannot be present along with ' + allServices[oneAlone].name + ', that requires to don\'t have subdomains.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
},
|
||||
|
||||
availableNewServices (family) {
|
||||
var ret = {}
|
||||
|
||||
for (const type in this.services) {
|
||||
if (this.filteredNewServices[type] == null && (family === undefined || family === this.services[type].family)) {
|
||||
ret[type] = this.services[type]
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
},
|
||||
|
||||
disabledNewServices (family) {
|
||||
var ret = {}
|
||||
|
||||
for (const type in this.services) {
|
||||
if (this.filteredNewServices[type] != null && (family === undefined || family === this.services[type].family)) {
|
||||
ret[type] = this.services[type]
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -62,33 +62,7 @@
|
|||
<b-input v-model="dn" autofocus class="text-monospace" placeholder="new.subdomain" :state="newDomainState" @update="validateNewSubdomain" />
|
||||
</b-input-group>
|
||||
</p>
|
||||
<b-tabs v-else-if="step === 1" class="mb-2" content-class="mt-3">
|
||||
<b-tab title="Services" active>
|
||||
<p>
|
||||
Select a new service to add to <span class="text-monospace">{{ dn | fqdn(domain.domain) }}</span>:
|
||||
</p>
|
||||
<b-list-group v-if="step === 1">
|
||||
<b-list-group-item v-for="(svc, idx) in availableNewServices" :key="idx" :active="svcSelected === idx" button @click="svcSelected = idx">
|
||||
{{ svc.name }}
|
||||
<small class="text-muted">{{ svc.description }}</small>
|
||||
<b-badge v-for="(categorie, idcat) in svc.categories" :key="idcat" variant="gray" class="float-right ml-1">
|
||||
{{ categorie }}
|
||||
</b-badge>
|
||||
</b-list-group-item>
|
||||
<b-list-group-item v-for="(svc, idx) in disabledNewServices" :key="idx" :active="svcSelected === idx" disabled @click="svcSelected = idx">
|
||||
<span :title="svc.description">{{ svc.name }}</span> <small class="font-italic text-danger">{{ filteredNewServices[idx] }}</small>
|
||||
<b-badge v-for="(categorie, idcat) in svc.categories" :key="idcat" variant="gray" class="float-right ml-1">
|
||||
{{ categorie }}
|
||||
</b-badge>
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
</b-tab>
|
||||
<b-tab title="Providers">
|
||||
<p>
|
||||
Select a new provider to add to <span class="text-monospace">{{ dn | fqdn(domain.domain) }}</span>:
|
||||
</p>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
<h-family-tabs v-else-if="step === 1" v-model="svcSelected" class="mb-2" content-class="mt-3" :domain="domain" :dn="dn" :my-services="myServices" :services="services" />
|
||||
<div v-else-if="step === 2">
|
||||
<h-custom-form v-if="form" ref="addModalResources" v-model="svcData.Service" :form="form" :services="services" :type="svcSelected" />
|
||||
<b-spinner v-else label="Spinning" />
|
||||
|
@ -100,7 +74,6 @@
|
|||
<script>
|
||||
import CustomForm from '@/mixins/customForm'
|
||||
import ServicesApi from '@/services/ServicesApi'
|
||||
import SourcesApi from '@/services/SourcesApi'
|
||||
import ValidateDomain from '@/mixins/validateDomain'
|
||||
import ZoneApi from '@/services/ZoneApi'
|
||||
|
||||
|
@ -108,7 +81,8 @@ export default {
|
|||
name: 'HModalAddService',
|
||||
|
||||
components: {
|
||||
hCustomForm: () => import('@/components/hCustomForm')
|
||||
hCustomForm: () => import('@/components/hCustomForm'),
|
||||
hFamilyTabs: () => import('@/components/hFamilyTabs')
|
||||
},
|
||||
|
||||
mixins: [CustomForm, ValidateDomain],
|
||||
|
@ -135,7 +109,6 @@ export default {
|
|||
data: function () {
|
||||
return {
|
||||
addServiceInProgress: false,
|
||||
availableResourceTypes: [],
|
||||
deleteServiceInProgress: false,
|
||||
dn: '',
|
||||
newDomainState: null,
|
||||
|
@ -146,149 +119,7 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isLoading () {
|
||||
return this.availableResourceTypes.length > 0
|
||||
},
|
||||
|
||||
availableNewServices () {
|
||||
var ret = {}
|
||||
|
||||
for (const type in this.services) {
|
||||
if (this.filteredNewServices[type] == null) {
|
||||
ret[type] = this.services[type]
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
},
|
||||
|
||||
disabledNewServices () {
|
||||
var ret = {}
|
||||
|
||||
for (const type in this.services) {
|
||||
if (this.filteredNewServices[type] != null) {
|
||||
ret[type] = this.services[type]
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
},
|
||||
|
||||
filteredNewServices () {
|
||||
return this.analyzeRestrictions(this.services)
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
SourcesApi.getAvailableResourceTypes(this.domain.id_source)
|
||||
.then(
|
||||
(response) => {
|
||||
this.availableResourceTypes = response.data
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
methods: {
|
||||
analyzeRestrictions (allServices) {
|
||||
var ret = {}
|
||||
|
||||
for (const type in allServices) {
|
||||
const svc = allServices[type]
|
||||
|
||||
if (svc.restrictions) {
|
||||
// Handle NeedTypes restriction: hosting provider need to support given types.
|
||||
if (svc.restrictions.needTypes) {
|
||||
for (const k in svc.restrictions.needTypes) {
|
||||
if (this.availableResourceTypes.indexOf(svc.restrictions.needTypes[k]) < 0) {
|
||||
ret[type] = 'is not available on this domain name hosting provider.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Alone restriction: only nearAlone are allowed.
|
||||
if (svc.restrictions.alone && this.myServices.services[this.dn]) {
|
||||
var found = false
|
||||
for (const k in this.myServices.services[this.dn]) {
|
||||
const s = this.myServices.services[this.dn][k]
|
||||
if (s._svctype !== type && allServices[s._svctype].restrictions && !allServices[s._svctype].restrictions.nearAlone) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ret[type] = 'has to be the only one in the subdomain.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Exclusive restriction: service can't be present along with another listed one.
|
||||
if (svc.restrictions.exclusive && this.myServices.services[this.dn]) {
|
||||
found = null
|
||||
for (const k in this.myServices.services[this.dn]) {
|
||||
const s = this.myServices.services[this.dn][k]
|
||||
for (const i in svc.restrictions.exclusive) {
|
||||
if (s._svctype === svc.restrictions.exclusive[i]) {
|
||||
found = s._svctype
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ret[type] = 'cannot be present along with ' + allServices[found].name + '.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Handle rootOnly restriction.
|
||||
if (svc.restrictions.rootOnly && this.dn !== '') {
|
||||
ret[type] = 'can only be present at the root of your domain.'
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle Single restriction: only one instance of the service per subdomain.
|
||||
if (svc.restrictions.single && this.myServices.services[this.dn]) {
|
||||
found = false
|
||||
for (const k in this.myServices.services[this.dn]) {
|
||||
const s = this.myServices.services[this.dn][k]
|
||||
if (s._svctype === type) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ret[type] = 'can only be present once per subdomain.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Handle presence of Alone and Leaf service in subdomain already.
|
||||
var oneAlone = null
|
||||
var oneLeaf = null
|
||||
for (const k in this.myServices.services[this.dn]) {
|
||||
const s = this.myServices.services[this.dn][k]
|
||||
if (this.services[s._svctype] && this.services[s._svctype].restrictions && this.services[s._svctype].restrictions.alone) {
|
||||
oneAlone = s._svctype
|
||||
}
|
||||
if (this.services[s._svctype] && this.services[s._svctype].restrictions && this.services[s._svctype].restrictions.leaf) {
|
||||
oneLeaf = s._svctype
|
||||
}
|
||||
}
|
||||
if (oneAlone && oneAlone !== type && !svc.restrictions.nearAlone) {
|
||||
ret[type] = 'cannot be present along with ' + allServices[oneAlone].name + ', that requires to be the only one in this subdomain.'
|
||||
continue
|
||||
}
|
||||
if (oneLeaf && oneLeaf !== type && !svc.restrictions.glue) {
|
||||
ret[type] = 'cannot be present along with ' + allServices[oneAlone].name + ', that requires to don\'t have subdomains.'
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
},
|
||||
|
||||
deleteService (service) {
|
||||
this.deleteServiceInProgress = true
|
||||
ZoneApi.deleteZoneService(this.domain.domain, this.zoneId, service)
|
||||
|
|
|
@ -78,6 +78,8 @@ func RegisterService(creator ServiceCreator, analyzer ServiceAnalyzer, infos Ser
|
|||
name := baseType.String()
|
||||
log.Println("Registering new service:", name)
|
||||
|
||||
infos.Type = name
|
||||
|
||||
svc := &Svc{
|
||||
creator,
|
||||
analyzer,
|
||||
|
|
|
@ -66,6 +66,7 @@ type ServiceRestrictions struct {
|
|||
|
||||
type ServiceInfos struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"_svctype"`
|
||||
Description string `json:"description"`
|
||||
Family string `json:"family"`
|
||||
Categories []string `json:"categories"`
|
||||
|
|
Loading…
Reference in New Issue