WIP: relaydomain CRUD in new admin

This commit is contained in:
Antoine Nguyen
2022-03-25 09:19:05 +01:00
parent e7ebf7a6d1
commit 15d9afc4a5
12 changed files with 267 additions and 26 deletions
+9
View File
@@ -0,0 +1,9 @@
import repository from './repository'
const resource = 'transports'
export default {
getAll () {
return repository.get(`/${resource}/`)
}
}
@@ -20,6 +20,9 @@
<template v-slot:form.options="{ step }">
<domain-options-form :ref="`form_${step}`" :domain="domain" @createAdmin="updateCreateAdmin" />
</template>
<template v-slot:form.transport="{ step }">
<domain-transport-form :ref="`form_${step}`" :domain="domain" />
</template>
<template v-slot:item.with_random_password="{ item }">
<template v-if="item.value">
<v-col cols="12" class="highligth white--text">
@@ -52,6 +55,7 @@ import DomainDNSForm from './DomainDNSForm'
import DomainGeneralForm from './DomainGeneralForm'
import DomainLimitationsForm from './DomainLimitationsForm'
import DomainOptionsForm from './DomainOptionsForm'
import DomainTransportForm from './DomainTransportForm'
export default {
props: ['value'],
@@ -60,7 +64,8 @@ export default {
'domain-dns-form': DomainDNSForm,
DomainGeneralForm,
DomainLimitationsForm,
DomainOptionsForm
DomainOptionsForm,
DomainTransportForm
},
computed: {
summarySections () {
@@ -87,12 +92,6 @@ export default {
{ key: this.$gettext('Default mailbox quota'), value: this.domain.default_mailbox_quota },
{ key: this.$gettext('Message sending limit'), value: this.domain.message_sending_limit }
]
},
{
title: this.$gettext('Options'),
items: [
{ key: this.$gettext('Create a domain administrator'), value: this.createAdmin, type: 'yesno' }
]
}
]
if (this.domain.enable_dkim) {
@@ -100,25 +99,53 @@ export default {
result[1].items.push({ key: this.$gettext('DKIM key length'), value: this.domain.dkim_key_length })
}
if (this.createAdmin) {
result[3].items.push({ key: this.$gettext('Administrator name'), value: this.domain.domain_admin.username })
result[3].items.push({
name: 'with_random_password',
key: this.$gettext('Random password'),
value: this.domain.domain_admin.with_random_password,
type: 'yesno'
})
result[3].items.push({
key: this.$gettext('With mailbox'),
value: this.domain.domain_admin.with_mailbox,
type: 'yesno'
})
result[3].items.push({
key: this.$gettext('Create aliases'),
value: this.domain.domain_admin.with_aliases,
type: 'yesno'
result.push({
title: this.$gettext('Options'),
items: [
{ key: this.$gettext('Create a domain administrator'), value: this.createAdmin, type: 'yesno' },
{ key: this.$gettext('Administrator name'), value: this.domain.domain_admin.username },
{
name: 'with_random_password',
key: this.$gettext('Random password'),
value: this.domain.domain_admin.with_random_password,
type: 'yesno'
},
{
key: this.$gettext('With mailbox'),
value: this.domain.domain_admin.with_mailbox,
type: 'yesno'
},
{
key: this.$gettext('Create aliases'),
value: this.domain.domain_admin.with_aliases,
type: 'yesno'
}
]
})
}
if (this.domain.type === 'relaydomain') {
const relayEntry = {
title: this.$gettext('Transport'),
items: [
{ key: this.$gettext('Service'), value: this.domain.transport.service }
]
}
if (this.$refs.form_4.service) {
const service = this.$refs.form_4.service
for (const setting of service.settings) {
const item = { key: setting.label, value: this.domain.transport.settings[`${service.name}_${setting.name}`] }
if (setting.type === 'boolean') {
item.type = 'yesno'
}
relayEntry.items.push(item)
}
}
result.push(relayEntry)
}
return result
},
steps () {
return this.domain.type === 'domain' ? this.domainSteps : this.relaySteps
}
},
data () {
@@ -128,11 +155,17 @@ export default {
domain_admin: {}
},
formErrors: {},
steps: [
domainSteps: [
{ name: 'general', title: this.$gettext('General') },
{ name: 'dns', title: this.$gettext('DNS') },
{ name: 'limitations', title: this.$gettext('Limitations') },
{ name: 'options', title: this.$gettext('Options') }
],
relaySteps: [
{ name: 'general', title: this.$gettext('General') },
{ name: 'dns', title: this.$gettext('DNS') },
{ name: 'limitations', title: this.$gettext('Limitations') },
{ name: 'transport', title: this.$gettext('Transport') }
]
}
},
@@ -160,7 +193,8 @@ export default {
with_random_password: false,
with_mailbox: false,
with_aliases: false
}
},
transport: {}
}
},
close () {
@@ -42,6 +42,14 @@
<router-link :to="{ name: 'DomainDetail', params: { id: item.pk } }">
{{ item.name }}
</router-link>
<v-chip
v-if="item.type === 'relaydomain'"
small
color="primary"
class="ml-2"
>
<translate>Relay</translate>
</v-chip>
<translate v-if="!item.enabled" class="ml-2 grey--text">(disabled)</translate>
</td>
<td>
@@ -0,0 +1,93 @@
<template>
<div>
<validation-observer ref="observer">
<label class="m-label"><translate>Service</translate></label>
<validation-provider
v-slot="{ errors }"
rules="required"
>
<v-autocomplete
v-model="service"
:items="backends"
item-text="name"
return-object
outlined
@change="resetForm"
:error-messages="errors"
/>
</validation-provider>
<template v-if="service">
<template v-for="setting in service.settings">
<div :key="setting.name">
<validation-provider
v-slot="{ errors }"
:rules="setting.required ? 'required' : ''"
>
<template
v-if="setting.type === 'str' || setting.type === 'int'"
>
<label class="m-label">{{ setting.label }}</label>
<v-text-field
v-model="domain.transport.settings[`${service.name}_${setting.name}`]"
outlined
:error-messages="errors"
:type="setting.type === 'int' ? 'number' : 'text'"
/>
</template>
<v-checkbox
v-else-if="setting.type === 'boolean'"
v-model="domain.transport.settings[`${service.name}_${setting.name}`]"
:label="setting.label"
:error-messages="errors"
/>
</validation-provider>
</div>
</template>
</template>
</validation-observer>
</div>
</template>
<script>
import transports from '@/api/transports'
export default {
props: {
domain: Object
},
data () {
return {
backends: [],
service: null
}
},
methods: {
resetForm (value) {
this.$set(this.domain, 'transport', { service: value.name, settings: {} })
for (const setting of value.settings) {
if (setting.default) {
let defaultValue = setting.default
if (setting.type === 'int') {
defaultValue = parseInt(defaultValue)
} else if (setting.type === 'boolean') {
if (defaultValue === 'False') {
defaultValue = false
} else {
defaultValue = true
}
}
this.$set(this.domain.transport.settings, `${value.name}_${setting.name}`, defaultValue)
}
}
}
},
mounted () {
transports.getAll().then(resp => {
this.backends = resp.data
})
},
watch: {
}
}
</script>
+18 -1
View File
@@ -18,6 +18,8 @@ from modoboa.lib import fields as lib_fields
from modoboa.lib import validators, web_utils
from modoboa.lib.sysutils import exec_cmd
from modoboa.parameters import tools as param_tools
from modoboa.transport import models as transport_models
from modoboa.transport.api.v2 import serializers as transport_serializers
from ... import constants, models
@@ -35,16 +37,31 @@ class DomainSerializer(v1_serializers.DomainSerializer):
"""Domain serializer for v2 API."""
domain_admin = CreateDomainAdminSerializer(required=False)
transport = transport_serializers.TransportSerializer(required=False)
class Meta(v1_serializers.DomainSerializer.Meta):
fields = v1_serializers.DomainSerializer.Meta.fields + (
"domain_admin",
"domain_admin", "transport"
)
def validate(self, data):
result = super().validate(data)
if data["type"] == "relaydomain" and not data.get("transport"):
raise serializers.ValidationError({"transport": _("This field is required")})
return result
def create(self, validated_data):
"""Create administrator and other stuff if needed."""
domain_admin = validated_data.pop("domain_admin", None)
transport_def = validated_data.pop("transport", None)
domain = super().create(validated_data)
if transport_def:
transport = transport_models.Transport(**transport_def)
transport.pattern = domain.name
transport.save()
domain.transport = transport
domain.save()
if not domain_admin:
return domain
View File
+42
View File
@@ -0,0 +1,42 @@
"""Transport related serializers."""
from rest_framework import serializers
from ... import backends
from ... import models
class TransportBackendSettingSerializer(serializers.Serializer):
"""Serializer for transport backend setting."""
name = serializers.CharField()
label = serializers.CharField()
type = serializers.CharField(default="str")
required = serializers.BooleanField(default=True)
default = serializers.CharField(default=None)
class TransportBackendSerializer(serializers.Serializer):
"""Serializer for transport backend."""
name = serializers.CharField()
settings = TransportBackendSettingSerializer(many=True)
class TransportSerializer(serializers.ModelSerializer):
"""Serializer for Transport model."""
settings = serializers.DictField(source="_settings")
class Meta:
fields = ("service", "settings")
model = models.Transport
def validate(self, data):
self.backend = backends.manager.get_backend(data["service"])
errors = {}
for field, error in self.backend.clean_fields(data["_settings"]):
errors[field] = error
if errors:
raise serializers.ValidationError(errors)
return data
+12
View File
@@ -0,0 +1,12 @@
"""App related urls."""
from rest_framework import routers
from . import viewsets
router = routers.SimpleRouter()
router.register(
r"transports", viewsets.TransportViewSet, basename="transport")
urlpatterns = router.urls
+25
View File
@@ -0,0 +1,25 @@
"""Transport related viewsets."""
from drf_spectacular.utils import extend_schema
from rest_framework import permissions, response, viewsets
from . import serializers
from ... import backends
class TransportViewSet(viewsets.ViewSet):
"""Viewset for Transport."""
permissions = (permissions.IsAuthenticated, )
@extend_schema(
responses={200: serializers.TransportBackendSerializer}
)
def list(self, request, *args, **kwargs):
"""Return the list of all defined transport backends."""
all_settings = backends.manager.get_all_backend_settings()
result = []
for name, settings in all_settings.items():
serializer = serializers.TransportBackendSerializer({"name": name, "settings": settings})
result.append(serializer.data)
return response.Response(result)
+1
View File
@@ -14,6 +14,7 @@ urlpatterns = [
path('', include("modoboa.relaydomains.api.v1.urls")),
path('', include("modoboa.dnstools.api.v2.urls")),
path('', include("modoboa.maillog.api.v2.urls")),
path('', include("modoboa.transport.api.v2.urls")),
]
urlpatterns += exts_pool.get_urls(category="api")