mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Domain detail page.
This commit is contained in:
+16
-16
@@ -14,23 +14,23 @@
|
||||
</v-container>
|
||||
</v-main>
|
||||
<v-snackbar
|
||||
v-model="snackbar"
|
||||
:color="notificationColor"
|
||||
:timeout="notificationTimeout"
|
||||
top
|
||||
>
|
||||
{{ notification }}
|
||||
v-model="snackbar"
|
||||
:color="notificationColor"
|
||||
:timeout="notificationTimeout"
|
||||
top
|
||||
>
|
||||
{{ notification }}
|
||||
|
||||
<template v-slot:action="{ attrs }">
|
||||
<v-btn
|
||||
color="white"
|
||||
text
|
||||
v-bind="attrs"
|
||||
@click="snackbar = false"
|
||||
>
|
||||
<translate>Close</translate>
|
||||
</v-btn>
|
||||
</template>
|
||||
<template v-slot:action="{ attrs }">
|
||||
<v-btn
|
||||
color="white"
|
||||
text
|
||||
v-bind="attrs"
|
||||
@click="snackbar = false"
|
||||
>
|
||||
<translate>Close</translate>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<v-chip :color="chipType"
|
||||
@click="$emit('click', $event)"
|
||||
small>
|
||||
{{ label }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['status'],
|
||||
computed: {
|
||||
label () {
|
||||
if (this.status === 'disabled') {
|
||||
return this.$gettext('Disabled')
|
||||
}
|
||||
if (this.status === 'pending') {
|
||||
return this.$gettext('Pending')
|
||||
}
|
||||
if (this.status === 'critical') {
|
||||
return this.$gettext('Problem')
|
||||
}
|
||||
if (this.status === 'ok') {
|
||||
return this.$gettext('Valid')
|
||||
}
|
||||
return this.$gettext('Unknown')
|
||||
},
|
||||
chipType () {
|
||||
if (this.status === 'disabled') {
|
||||
return ''
|
||||
}
|
||||
if (this.status === 'pending') {
|
||||
return 'info'
|
||||
}
|
||||
if (this.status === 'critical') {
|
||||
return 'error'
|
||||
}
|
||||
if (this.status === 'ok') {
|
||||
return 'success'
|
||||
}
|
||||
return 'warning'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="headline">
|
||||
<translate>DNS configuration for </translate> {{ domain.name }}
|
||||
</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p>Here is an example of DNS configuration (bind format). Replace values between [ ] by the appropriate ones.</p>
|
||||
<v-alert
|
||||
border="left"
|
||||
colored-border
|
||||
color="primary lighten-3"
|
||||
elevation="2"
|
||||
dense
|
||||
>
|
||||
<div class="title">MX</div>
|
||||
<pre>
|
||||
mail.{{ domain.name }}. IN A <strong>[<translate>IP address of your Modoboa server</translate>]</strong>
|
||||
@ IN MX 10 mail.{{ domain.name }}.</pre>
|
||||
</v-alert>
|
||||
<v-alert
|
||||
border="left"
|
||||
colored-border
|
||||
color="primary lighten-3"
|
||||
elevation="2"
|
||||
dense
|
||||
>
|
||||
<div class="title">SPF</div>
|
||||
<pre>
|
||||
@ IN TXT "v=spf1 mx ~all"</pre>
|
||||
</v-alert>
|
||||
|
||||
<v-alert
|
||||
border="left"
|
||||
colored-border
|
||||
color="primary lighten-3"
|
||||
elevation="2"
|
||||
dense
|
||||
>
|
||||
<div class="title">DMARC</div>
|
||||
<pre>
|
||||
_dmarc.{{ domain.name }}. IN TXT "v=DMARC1; p=quarantine; pct=100"</pre>
|
||||
</v-alert>
|
||||
<v-alert
|
||||
border="left"
|
||||
colored-border
|
||||
color="primary lighten-3"
|
||||
elevation="2"
|
||||
dense
|
||||
>
|
||||
<div class="title">Auto configuration</div>
|
||||
<pre>
|
||||
autoconfig.{{ domain.name }}. IN CNAME <strong>[<translate>hostname of your automx server</translate>]</strong>
|
||||
autodiscover.{{ domain.name }}. IN CNAME <strong>[<translate>hostname of your automx server</translate>]</strong></pre>
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="grey darken-1" text @click="close">
|
||||
<translate>Close</translate>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['domain'],
|
||||
methods: {
|
||||
close () {
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="headline">DNS</span>
|
||||
<v-btn icon :title="'DNS configuration help'|translate" @click="showConfigHelp = true">
|
||||
<v-icon>mdi-information-outline</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="6"><translate>Status</translate></v-col>
|
||||
<v-col cols="6">
|
||||
<dns-status-chip :status="domain.dns_global_status" @click="showDetail = true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="6"><translate>DKIM key</translate></v-col>
|
||||
<v-col cols="6">
|
||||
<div v-if="domain.dkim_public_key">
|
||||
<v-btn color="primary" small>
|
||||
<translate>Show key</translate>
|
||||
</v-btn>
|
||||
<v-btn icon><v-icon>mdi-refresh</v-icon></v-btn>
|
||||
</div>
|
||||
<translate v-else>Not generated</translate>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
||||
<v-dialog v-model="showConfigHelp"
|
||||
max-width="800px"
|
||||
persistent
|
||||
>
|
||||
<domain-dns-config :domain="domain" @close="showConfigHelp = false" />
|
||||
</v-dialog>
|
||||
<v-dialog v-model="showDetail"
|
||||
persistent
|
||||
max-width="800px">
|
||||
<dns-detail @close="showDetail = false" :domain="domain" />
|
||||
</v-dialog>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DomainDNSConfig from './DomainDNSConfig'
|
||||
import DNSDetail from './DNSDetail'
|
||||
import DNSStatusChip from './DNSStatusChip'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
'dns-detail': DNSDetail,
|
||||
'dns-status-chip': DNSStatusChip,
|
||||
'domain-dns-config': DomainDNSConfig
|
||||
},
|
||||
props: ['domain'],
|
||||
data () {
|
||||
return {
|
||||
showConfigHelp: false,
|
||||
showDetail: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -39,9 +39,10 @@
|
||||
<v-checkbox />
|
||||
</td>
|
||||
<td>
|
||||
<router-link :to="{ name: 'DomainDetail', params: { pk: item.pk } }">
|
||||
<router-link :to="{ name: 'DomainDetail', params: { id: item.pk } }">
|
||||
{{ item.name }}
|
||||
</router-link>
|
||||
<translate v-if="!item.enabled" class="ml-2 grey--text">(disabled)</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ item.domainalias_count }} aliases
|
||||
@@ -54,11 +55,7 @@
|
||||
</v-btn>
|
||||
</td>
|
||||
<td>
|
||||
<v-chip :color="getDNSTagType(item.dns_global_status)"
|
||||
@click="openDNSDetail(item)"
|
||||
small>
|
||||
{{ getDNSLabel(item.dns_global_status) }}
|
||||
</v-chip>
|
||||
<dns-status-chip :status="item.dns_global_status" @click="openDNSDetail(item)" />
|
||||
</td>
|
||||
<td>
|
||||
<v-progress-linear v-model="item.allocated_quota_in_percent" />
|
||||
@@ -94,7 +91,12 @@
|
||||
</tr>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<confirm-dialog ref="confirm" />
|
||||
<confirm-dialog ref="confirm">
|
||||
<v-checkbox v-model="keepDomainFolder"
|
||||
:label="'Do not delete domain folder'|translate"
|
||||
hide-details
|
||||
/>
|
||||
</confirm-dialog>
|
||||
<v-dialog v-model="showDNSdetail"
|
||||
persistent
|
||||
max-width="800px">
|
||||
@@ -134,6 +136,7 @@ import domainApi from '@/api/domains'
|
||||
import AdministratorList from './AdministratorList'
|
||||
import ConfirmDialog from '@/components/layout/ConfirmDialog'
|
||||
import DNSDetail from '@/components/domains/DNSDetail'
|
||||
import DNSStatusChip from './DNSStatusChip'
|
||||
import DomainAliasForm from '@/components/domains/DomainAliasForm'
|
||||
import DomainForm from '@/components/domains/DomainForm'
|
||||
import MenuItems from '@/components/tools/MenuItems'
|
||||
@@ -143,6 +146,7 @@ export default {
|
||||
AdministratorList,
|
||||
ConfirmDialog,
|
||||
'dns-detail': DNSDetail,
|
||||
'dns-status-chip': DNSStatusChip,
|
||||
DomainAliasForm,
|
||||
DomainForm,
|
||||
MenuItems
|
||||
@@ -158,13 +162,14 @@ export default {
|
||||
{ label: this.$gettext('Delete'), icon: 'mdi-delete-outline', onClick: this.deleteDomain, color: 'red' }
|
||||
],
|
||||
headers: [
|
||||
{ text: 'Name', value: 'name' },
|
||||
{ text: 'Aliases', value: 'domainalias_count' },
|
||||
{ text: 'DNS status', value: 'dns_global_status', sortable: false, align: 'center' },
|
||||
{ text: 'Sending limit', value: 'message_limit' },
|
||||
{ text: 'Quota', value: 'allocated_quota_in_percent' },
|
||||
{ text: this.$gettext('Name'), value: 'name' },
|
||||
{ text: this.$gettext('Aliases'), value: 'domainalias_count' },
|
||||
{ text: this.$gettext('DNS status'), value: 'dns_global_status', sortable: false, align: 'center' },
|
||||
{ text: this.$gettext('Sending limit'), value: 'message_limit' },
|
||||
{ text: this.$gettext('Quota'), value: 'allocated_quota_in_percent' },
|
||||
{ text: this.$gettext('Actions'), value: 'actions', sortable: false, align: 'right' }
|
||||
],
|
||||
keepDomainFolder: false,
|
||||
selectedDomain: null,
|
||||
selectedDomainAlias: null,
|
||||
showAdminList: false,
|
||||
@@ -200,8 +205,10 @@ export default {
|
||||
if (!confirm) {
|
||||
return
|
||||
}
|
||||
this.$store.dispatch('domains/deleteDomain', { id: domain.pk }).then(resp => {
|
||||
bus.$emit('notification', { msg: this.$gettext('Domain delete') })
|
||||
const data = { keep_folder: this.keepDomainFolder }
|
||||
this.$store.dispatch('domains/deleteDomain', { id: domain.pk, data }).then(resp => {
|
||||
bus.$emit('notification', { msg: this.$gettext('Domain deleted') })
|
||||
this.keepDomainFolder = false
|
||||
})
|
||||
},
|
||||
domainAliasDeleted () {
|
||||
@@ -220,36 +227,6 @@ export default {
|
||||
this.selectedDomainAlias = alias
|
||||
this.showAliasForm = true
|
||||
},
|
||||
getDNSLabel (value) {
|
||||
if (value === 'disabled') {
|
||||
return this.$gettext('Disabled')
|
||||
}
|
||||
if (value === 'pending') {
|
||||
return this.$gettext('Pending')
|
||||
}
|
||||
if (value === 'critical') {
|
||||
return this.$gettext('Problem')
|
||||
}
|
||||
if (value === 'ok') {
|
||||
return this.$gettext('Valid')
|
||||
}
|
||||
return this.$gettext('Unknown')
|
||||
},
|
||||
getDNSTagType (value) {
|
||||
if (value === 'disabled') {
|
||||
return ''
|
||||
}
|
||||
if (value === 'pending') {
|
||||
return 'info'
|
||||
}
|
||||
if (value === 'critical') {
|
||||
return 'error'
|
||||
}
|
||||
if (value === 'ok') {
|
||||
return 'success'
|
||||
}
|
||||
return 'warning'
|
||||
},
|
||||
loadAliases ({ item, value }) {
|
||||
if (!value) {
|
||||
return
|
||||
@@ -277,4 +254,7 @@ export default {
|
||||
.v-text-field--outlined >>> fieldset {
|
||||
border-color: #BFC5D2;
|
||||
}
|
||||
.v-input--checkbox >>> .v-label {
|
||||
font-size: 0.875rem !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="6"><translate>Creation date</translate></v-col>
|
||||
<v-col cols="6">{{ domain.creation|date }}</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="6"><translate>Last modification date</translate></v-col>
|
||||
<v-col cols="6">{{ domain.last_modification|date }}</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="6"><translate>Quota</translate></v-col>
|
||||
<v-col cols="6">{{ domain.quota }} MB</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="6"><translate>Default mailbox quota</translate></v-col>
|
||||
<v-col cols="6">{{ domain.default_mailbox_quota }} MB</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="domain.sending_limit !== undefined">
|
||||
<v-col cols="6"><translate>Daily sending limit</translate></v-col>
|
||||
<v-col cols="6">{{ domain.sending_limit }} <translate>messages</translate></v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="6"><translate>Mailboxes</translate></v-col>
|
||||
<v-col cols="6">{{ domain.mailbox_count }}</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="6"><translate>Aliases</translate></v-col>
|
||||
<v-col cols="6">{{ domain.mbalias_count }}</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['domain']
|
||||
}
|
||||
</script>
|
||||
@@ -14,18 +14,20 @@
|
||||
<v-card-text
|
||||
v-show="!!message"
|
||||
class="pa-4"
|
||||
v-html="message"
|
||||
></v-card-text>
|
||||
>
|
||||
{{ message }}
|
||||
<slot></slot>
|
||||
</v-card-text>
|
||||
<v-card-actions class="pt-3">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
v-if="!options.noconfirm"
|
||||
:color="this.options.color"
|
||||
@click.native="cancel"
|
||||
>
|
||||
{{ options.cancelLabel }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
:color="this.options.color"
|
||||
@click.native="agree">
|
||||
{{ options.agreeLabel }}
|
||||
</v-btn>
|
||||
|
||||
@@ -15,24 +15,32 @@
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<template v-for="(param, index) in displayableParams(element.parameters)">
|
||||
<v-checkbox :key="index"
|
||||
:label="param.label"
|
||||
v-model="parameters[param.name]"
|
||||
v-if="param.widget === 'BooleanField'"
|
||||
/>
|
||||
<v-switch :key="index"
|
||||
:label="param.label"
|
||||
v-model="parameters[param.name]"
|
||||
:hint="param.help_text"
|
||||
persistent-hint
|
||||
class="my-2"
|
||||
v-if="param.widget === 'BooleanField'"
|
||||
/>
|
||||
<v-select :key="index"
|
||||
:label="param.label"
|
||||
v-model="parameters[param.name]"
|
||||
:items="param.choices"
|
||||
:hint="param.help_text"
|
||||
persistent-hint
|
||||
class="my-2"
|
||||
v-else-if="param.widget === 'ChoiceField'"
|
||||
/>
|
||||
<v-text-field
|
||||
:key="index"
|
||||
:label="param.label"
|
||||
:hint="param.help_text"
|
||||
persistent-hint
|
||||
v-model="parameters[param.name]"
|
||||
:error="formErrors[param.name] !== undefined"
|
||||
:error-messages="formErrors[param.name]"
|
||||
class="my-2"
|
||||
v-else
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -25,6 +25,14 @@ const routes = [
|
||||
requiresAuth: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/domains/:id',
|
||||
name: 'DomainDetail',
|
||||
component: () => import('../views/Domain.vue'),
|
||||
meta: {
|
||||
requiresAuth: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/parameters/:app',
|
||||
name: 'ParametersEdit',
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-toolbar flat>
|
||||
<v-toolbar-title><translate>Domain</translate> {{ domain.name }}</v-toolbar-title>
|
||||
<v-btn color="primary" icon @click="showEditForm = true"><v-icon>mdi-circle-edit-outline</v-icon></v-btn>
|
||||
<v-btn icon @click="showAdminList = true" :title="'Show administrator list'|translate">
|
||||
<v-icon>mdi-account-supervisor</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
<v-layout>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<domain-summary :domain="domain" />
|
||||
<div class="mt-4" />
|
||||
<domain-dns-summary :domain="domain" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-layout>
|
||||
<v-dialog v-model="showEditForm"
|
||||
persistent
|
||||
max-width="800px"
|
||||
>
|
||||
<domain-form :domain="domain" @close="showEditForm = false" />
|
||||
</v-dialog>
|
||||
<v-dialog v-model="showAdminList"
|
||||
persistent
|
||||
max-width="800px"
|
||||
>
|
||||
<administrator-list
|
||||
:domain="domain"
|
||||
@close="showAdminList = false"
|
||||
/>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import domains from '@/api/domains'
|
||||
import AdministratorList from '@/components/domains/AdministratorList'
|
||||
import DomainForm from '@/components/domains/DomainForm'
|
||||
import DomainDNSSummary from '@/components/domains/DomainDNSSummary'
|
||||
import DomainSummary from '@/components/domains/DomainSummary'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AdministratorList,
|
||||
DomainForm,
|
||||
'domain-dns-summary': DomainDNSSummary,
|
||||
DomainSummary
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
domain: {},
|
||||
showEditForm: false,
|
||||
showAdminList: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
domains.getDomain(this.$route.params.id).then(resp => {
|
||||
this.domain = resp.data
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-toolbar {
|
||||
background-color: #f7f8fa !important;
|
||||
}
|
||||
</style>
|
||||
@@ -52,12 +52,13 @@ class DomainSerializer(serializers.ModelSerializer):
|
||||
"enable_dkim", "dkim_key_selector", "dkim_key_length",
|
||||
"dkim_public_key", "dkim_private_key_path",
|
||||
"mailbox_count", "mbalias_count", "domainalias_count",
|
||||
"dns_global_status", "message_limit"
|
||||
"dns_global_status", "message_limit", "creation",
|
||||
"last_modification"
|
||||
)
|
||||
read_only_fields = (
|
||||
"pk", "dkim_public_key", "dns_global_status", "allocated_quota_in_percent"
|
||||
"mailbox_count", "mbalias_count", "domainalias_count",
|
||||
"enable_dns_checks"
|
||||
"enable_dns_checks", "creation", "last_modification"
|
||||
)
|
||||
|
||||
def validate_name(self, value):
|
||||
|
||||
@@ -334,13 +334,6 @@ GLOBAL_PARAMETERS_STRUCT = collections.OrderedDict([
|
||||
" renamed or removed within Modoboa"
|
||||
)
|
||||
}),
|
||||
("mailboxes_owner", {
|
||||
"label": ugettext_lazy("Mailboxes owner"),
|
||||
"display": "handle_mailboxes=true",
|
||||
"help_text": ugettext_lazy(
|
||||
"The UNIX account who owns mailboxes on the filesystem"
|
||||
)
|
||||
}),
|
||||
("default_domain_quota", {
|
||||
"label": ugettext_lazy("Default domain quota"),
|
||||
"help_text": ugettext_lazy(
|
||||
|
||||
Reference in New Issue
Block a user