mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
update DomainInfo page with moar data and config panel
This commit is contained in:
parent
b7bc950719
commit
dcb9534e77
4 changed files with 242 additions and 48 deletions
|
@ -144,6 +144,14 @@
|
||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"dns": "DNS",
|
"dns": "DNS",
|
||||||
"domain": {
|
"domain": {
|
||||||
|
"cert": {
|
||||||
|
"types": {
|
||||||
|
"self-signed": "Self-signed",
|
||||||
|
"lets-encrypt": "Let's Encrypt",
|
||||||
|
"other-unknown": "Other/Unknown"
|
||||||
|
},
|
||||||
|
"valid_for": "valid for {days}"
|
||||||
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"edit": "Edit domain configuration",
|
"edit": "Edit domain configuration",
|
||||||
"title": "Domain configuration"
|
"title": "Domain configuration"
|
||||||
|
@ -153,6 +161,19 @@
|
||||||
"auto_config_ignored": "ignored, won't be changed by YunoHost unless you check the overwrite option",
|
"auto_config_ignored": "ignored, won't be changed by YunoHost unless you check the overwrite option",
|
||||||
"auto_config_ok": "Automatic configuration seems to be OK!",
|
"auto_config_ok": "Automatic configuration seems to be OK!",
|
||||||
"auto_config_zone": "Current DNS zone",
|
"auto_config_zone": "Current DNS zone",
|
||||||
|
"semi_auto_status": {
|
||||||
|
"activable": "Activable",
|
||||||
|
"activated": "Activated",
|
||||||
|
"has_changes": "Has changes",
|
||||||
|
"unavailable": "Unavailable"
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"auto": "Automatic",
|
||||||
|
"handled_in_parent": "Handled in parent domain",
|
||||||
|
"manual": "Manual",
|
||||||
|
"none": "None",
|
||||||
|
"semi_auto": "Semi-automatic"
|
||||||
|
},
|
||||||
"edit": "Edit DNS configuration",
|
"edit": "Edit DNS configuration",
|
||||||
"info": "The automatic DNS records configuration is an experimental feature. <br>Consider saving your current DNS zone from your DNS registrar's interface before pushing records from here.",
|
"info": "The automatic DNS records configuration is an experimental feature. <br>Consider saving your current DNS zone from your DNS registrar's interface before pushing records from here.",
|
||||||
"manual_config": "Suggested DNS records for manual configuration",
|
"manual_config": "Suggested DNS records for manual configuration",
|
||||||
|
@ -161,7 +182,20 @@
|
||||||
"push_force_confirm": "Are you sure you want to force push all suggested dns records? Be aware that it may overwrite manually or important default records set by you or your registrar.",
|
"push_force_confirm": "Are you sure you want to force push all suggested dns records? Be aware that it may overwrite manually or important default records set by you or your registrar.",
|
||||||
"push_force_warning": "It looks like some DNS records that YunoHost would have set are already in the registrar configuration. You can use the overwrite option if you know what you are doing."
|
"push_force_warning": "It looks like some DNS records that YunoHost would have set are already in the registrar configuration. You can use the overwrite option if you know what you are doing."
|
||||||
},
|
},
|
||||||
|
"explain": {
|
||||||
|
"main_domain": "The main domain is the domain from which users can connect to the portal (via \"{domain}/yunohost/sso\").<br>Therefore, it is not possible to delete it.<br>If you want to delete \"{domain}\", you will first have to choose or add another domain and set it as the main domain."
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"apps_on_domain": "Apps installed on domain",
|
||||||
|
"certificate_authority": "SSL Certification authority",
|
||||||
|
"dns_config_method": "DNS config method",
|
||||||
|
"dns_semi_auto_config_feature": "Semi-auto DNS configuration feature",
|
||||||
|
"domain_type": "Domain type",
|
||||||
|
"registrar": "Registrar"
|
||||||
|
},
|
||||||
"types": {
|
"types": {
|
||||||
|
"parent_domain": "Parent domain",
|
||||||
|
"subdomain": "Subdomain",
|
||||||
"main_domain": "Main domain"
|
"main_domain": "Main domain"
|
||||||
},
|
},
|
||||||
"toggle_subdomains": "Toggle subdomains"
|
"toggle_subdomains": "Toggle subdomains"
|
||||||
|
@ -554,8 +588,10 @@
|
||||||
"browse": "Browse",
|
"browse": "Browse",
|
||||||
"collapse": "Collapse",
|
"collapse": "Collapse",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
|
"link": "Link",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"separator": ", "
|
"separator": ", ",
|
||||||
|
"valid": "Valid"
|
||||||
},
|
},
|
||||||
"wrong_password_or_username": "Wrong password or username",
|
"wrong_password_or_username": "Wrong password or username",
|
||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
|
|
|
@ -68,7 +68,10 @@ body {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tooltip { top: 0; }
|
||||||
// Descriptive list (<b-row /> elems with <b-col> inside)
|
// Descriptive list (<b-row /> elems with <b-col> inside)
|
||||||
|
// FIXME REMOVE when every infos switch to `DescriptionRow`
|
||||||
.row-line {
|
.row-line {
|
||||||
@include media-breakpoint-up(md) {
|
@include media-breakpoint-up(md) {
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
@ -22,6 +22,7 @@ export default {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
main_domain: undefined,
|
main_domain: undefined,
|
||||||
domains: undefined, // Array
|
domains: undefined, // Array
|
||||||
|
domains_details: {},
|
||||||
users: undefined, // basic user data: Object {username: {data}}
|
users: undefined, // basic user data: Object {username: {data}}
|
||||||
users_details: {}, // precise user data: Object {username: {data}}
|
users_details: {}, // precise user data: Object {username: {data}}
|
||||||
groups: undefined,
|
groups: undefined,
|
||||||
|
@ -33,6 +34,22 @@ export default {
|
||||||
state.domains = domains
|
state.domains = domains
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'SET_DOMAINS_DETAILS' (state, [name, { domains }]) {
|
||||||
|
Vue.set(state.domains_details, name, domains[name])
|
||||||
|
},
|
||||||
|
|
||||||
|
'UPDATE_DOMAINS_DETAILS' (state, payload) {
|
||||||
|
// FIXME use a common function to execute the same code ?
|
||||||
|
this.commit('SET_DOMAINS_DETAILS', payload)
|
||||||
|
},
|
||||||
|
|
||||||
|
'DEL_DOMAINS_DETAILS' (state, [name]) {
|
||||||
|
Vue.delete(state.domains_details, name)
|
||||||
|
if (state.domains) {
|
||||||
|
Vue.delete(state.domains, name)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
'ADD_DOMAINS' (state, [{ domain }]) {
|
'ADD_DOMAINS' (state, [{ domain }]) {
|
||||||
state.domains.push(domain)
|
state.domains.push(domain)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,83 +1,221 @@
|
||||||
<template>
|
<template>
|
||||||
<view-base :queries="queries" skeleton="card-list-skeleton">
|
<view-base :queries="queries" @queries-response="onQueriesResponse" skeleton="card-list-skeleton">
|
||||||
<card :title="name" icon="globe">
|
<!-- INFO CARD -->
|
||||||
<!-- VISIT -->
|
<card v-if="domain" :title="name" icon="globe">
|
||||||
<p>{{ $t('domain_visit_url', { url: 'https://' + name }) }}</p>
|
<template #header-buttons>
|
||||||
<b-button variant="success" :href="'https://' + name" target="_blank">
|
<!-- DEFAULT DOMAIN -->
|
||||||
<icon iname="external-link" /> {{ $t('domain_visit') }}
|
<b-button v-if="!isMainDomain" @click="setAsDefaultDomain" variant="info">
|
||||||
</b-button>
|
<icon iname="star" /> {{ $t('set_default') }}
|
||||||
<hr>
|
</b-button>
|
||||||
|
|
||||||
<!-- DEFAULT DOMAIN -->
|
<!-- DELETE DOMAIN -->
|
||||||
<p>{{ $t('domain_default_desc') }}</p>
|
<b-button @click="deleteDomain" :disabled="isMainDomain" variant="danger">
|
||||||
<p v-if="isMainDomain" class="alert alert-info">
|
<icon iname="trash-o" /> {{ $t('delete') }}
|
||||||
<icon iname="star" /> {{ $t('domain_default_longdesc') }}
|
</b-button>
|
||||||
</p>
|
</template>
|
||||||
<b-button v-else variant="info" @click="setAsDefaultDomain">
|
|
||||||
<icon iname="star" /> {{ $t('set_default') }}
|
|
||||||
</b-button>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<!-- DOMAIN CONFIG -->
|
<!-- DOMAIN LINK -->
|
||||||
<p>{{ $t('domain.config.edit') }}</p>
|
<description-row :term="$t('words.link')">
|
||||||
<b-button variant="warning" :to="{ name: 'domain-config', param: { name } }">
|
<b-link :href="'https://' + name" target="_blank">
|
||||||
<icon iname="cog" /> {{ $t('domain.config.title') }}
|
https://{{ name }}
|
||||||
</b-button>
|
</b-link>
|
||||||
<hr>
|
</description-row>
|
||||||
|
|
||||||
<!-- DNS CONFIG -->
|
<!-- DOMAIN TYPE -->
|
||||||
<p>{{ $t('domain.dns.edit') }}</p>
|
<description-row
|
||||||
<b-button variant="warning" :to="{ name: 'domain-dns', param: { name } }">
|
:term="$t('domain.info.domain_type')"
|
||||||
<icon iname="globe" /> {{ $t('domain_dns_config') }}
|
>
|
||||||
</b-button>
|
<span v-if="isMainDomain">
|
||||||
<hr>
|
<icon iname="star" />
|
||||||
|
<explain-what
|
||||||
|
id="explain-main-domain"
|
||||||
|
:title="$t('domain.types.main_domain')"
|
||||||
|
:content="$t('domain.explain.main_domain', { domain: name })"
|
||||||
|
>{{ $t('domain.types.main_domain') }}</explain-what>
|
||||||
|
,
|
||||||
|
</span>
|
||||||
|
{{ $t('domain.types.' + (parentName ? 'subdomain' : 'parent_domain' )) }}
|
||||||
|
</description-row>
|
||||||
|
|
||||||
<!-- DELETE -->
|
<!-- DOMAIN CERT AUTHORITY -->
|
||||||
<p>{{ $t('domain_delete_longdesc') }}</p>
|
<description-row :term="$t('domain.info.certificate_authority')">
|
||||||
<p
|
<icon :iname="cert.status.icon" :variant="cert.status.variant" class="mr-1" />
|
||||||
v-if="isMainDomain" class="alert alert-info"
|
{{ $t('domain.cert.types.' + cert.authority) }}
|
||||||
v-html="$t('domain_delete_forbidden_desc', { domain: name })"
|
({{ $t('domain.cert.valid_for', { days: $tc('day_validity', cert.validity) }) }})
|
||||||
/>
|
</description-row>
|
||||||
<b-button v-else variant="danger" @click="deleteDomain">
|
|
||||||
<icon iname="trash-o" /> {{ $t('delete') }}
|
<!-- DOMAIN DNS METHOD -->
|
||||||
</b-button>
|
<description-row v-if="dns" :term="$t('domain.info.dns_config_method')">
|
||||||
|
{{ $t('domain.dns.methods.' + dns.method) }}
|
||||||
|
|
||||||
|
<template v-if="dns.method === 'semi_auto'">
|
||||||
|
(<icon :iname="semiAuto.icon" :variant="semiAuto.variant" class="mr-1" />
|
||||||
|
{{ $t('domain.dns.semi_auto_status.' + dns.semi_auto_status) }})
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="dns.method === 'handled_in_parent'">
|
||||||
|
<b-button
|
||||||
|
variant="outline-dark" size="xs" class="py-0 ml-1"
|
||||||
|
:to="{ name: 'domain-info', params: { name: parentName }}"
|
||||||
|
>
|
||||||
|
{{ parentName }}
|
||||||
|
</b-button>
|
||||||
|
</template>
|
||||||
|
</description-row>
|
||||||
|
|
||||||
|
<!-- DOMAIN SEMI-AUTO OPTIONAL FEATURE STATUS -->
|
||||||
|
<description-row
|
||||||
|
v-if="!['semi_auto', 'auto', 'handled_in_parent', 'none'].includes(dns.method)"
|
||||||
|
:term="$t('domain.info.dns_semi_auto_config_feature')"
|
||||||
|
>
|
||||||
|
<icon :iname="semiAuto.icon" :variant="semiAuto.variant" class="mr-1" />
|
||||||
|
{{ $t('domain.dns.semi_auto_status.' + dns.semi_auto_status) }}
|
||||||
|
</description-row>
|
||||||
|
|
||||||
|
<!-- DOMAIN REGISTRAR -->
|
||||||
|
<description-row v-if="dns.registrar" :term="$t('domain.info.registrar')" :details="dns.registrar" />
|
||||||
|
|
||||||
|
<!-- DOMAIN APPS -->
|
||||||
|
<description-row :term="$t('domain.info.apps_on_domain')">
|
||||||
|
<b-button-group
|
||||||
|
v-for="app in domain.apps" :key="app.id"
|
||||||
|
size="sm" class="mr-2"
|
||||||
|
>
|
||||||
|
<b-button class="py-0 font-weight-bold" variant="outline-dark" :to="{ name: 'app-info', params: { id: app.id }}">
|
||||||
|
{{ app.name }}
|
||||||
|
</b-button>
|
||||||
|
<b-button
|
||||||
|
variant="outline-dark" class="py-0 px-1"
|
||||||
|
:href="'https://' + name + app.path" target="_blank"
|
||||||
|
>
|
||||||
|
<span class="sr-only">{{ $t('app.visit_app') }}</span>
|
||||||
|
<icon iname="external-link" />
|
||||||
|
</b-button>
|
||||||
|
</b-button-group>
|
||||||
|
|
||||||
|
{{ domain.apps ? '' : $t('words.none') }}
|
||||||
|
</description-row>
|
||||||
</card>
|
</card>
|
||||||
|
|
||||||
|
<config-panels v-if="config.panels" v-bind="config" @submit="applyConfig" />
|
||||||
</view-base>
|
</view-base>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
import api from '@/api'
|
import api, { objectToParams } from '@/api'
|
||||||
|
import {
|
||||||
|
formatFormData,
|
||||||
|
formatYunoHostConfigPanels
|
||||||
|
} from '@/helpers/yunohostArguments'
|
||||||
|
import ConfigPanels from '@/components/ConfigPanels'
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DomainInfo',
|
name: 'DomainInfo',
|
||||||
|
|
||||||
props: {
|
components: {
|
||||||
name: {
|
ConfigPanels
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => {
|
props: {
|
||||||
|
name: { type: String, required: true }
|
||||||
|
},
|
||||||
|
|
||||||
|
data () {
|
||||||
return {
|
return {
|
||||||
queries: [
|
queries: [
|
||||||
['GET', { uri: 'domains/main', storeKey: 'main_domain' }]
|
['GET', { uri: 'domains', storeKey: 'domains' }],
|
||||||
]
|
['GET', { uri: 'domains/main', storeKey: 'main_domain' }],
|
||||||
|
['GET', { uri: 'domains', storeKey: 'domains_details', param: this.name }],
|
||||||
|
['GET', `domains/${this.name}/config?full`]
|
||||||
|
],
|
||||||
|
config: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['mainDomain']),
|
...mapGetters(['mainDomain']),
|
||||||
|
|
||||||
|
domain () {
|
||||||
|
return this.$store.getters.domain(this.name)
|
||||||
|
},
|
||||||
|
|
||||||
|
parentName () {
|
||||||
|
return this.$store.getters.highestDomainParentName(this.name)
|
||||||
|
},
|
||||||
|
|
||||||
|
certStatus () {
|
||||||
|
const cert = this.domain.certificate
|
||||||
|
const trad = (cert.validity < 15 ? 'renew.' : '') + cert.authority
|
||||||
|
if (cert.validity <= 0) {
|
||||||
|
return { trad: 'invalid', icon: 'times', variant: 'danger' }
|
||||||
|
} else if (cert.authority === 'other-unknown') {
|
||||||
|
return cert.validity < 15
|
||||||
|
? { trad, icon: 'exclamation', variant: 'danger' }
|
||||||
|
: { trad, icon: 'check', variant: 'success' }
|
||||||
|
} else if (cert.authority === 'lets-encrypt') {
|
||||||
|
return { trad, icon: 'thumbs-up', variant: 'success' }
|
||||||
|
}
|
||||||
|
return { trad, icon: 'exclamation', variant: 'warning' }
|
||||||
|
},
|
||||||
|
|
||||||
|
cert () {
|
||||||
|
return {
|
||||||
|
...this.domain.certificate,
|
||||||
|
status: this.certStatus
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
dns () {
|
||||||
|
return this.domain.dns
|
||||||
|
},
|
||||||
|
|
||||||
isMainDomain () {
|
isMainDomain () {
|
||||||
if (!this.mainDomain) return
|
if (!this.mainDomain) return
|
||||||
return this.name === this.mainDomain
|
return this.name === this.mainDomain
|
||||||
|
},
|
||||||
|
|
||||||
|
semiAuto () {
|
||||||
|
const status = this.dns.semi_auto_status
|
||||||
|
if (!status) return
|
||||||
|
if (status === 'unavailable') return { icon: 'times', variant: 'danger' }
|
||||||
|
if (status === 'activable') return { icon: 'check', variant: 'info' }
|
||||||
|
if (status === 'activated') return { icon: 'check', variant: 'success' }
|
||||||
|
// FIXME mutate status on push --dry_run (misconfigured + has_diff)
|
||||||
|
if (status === 'has_changes') return { icon: 'wrench', variant: 'warning' }
|
||||||
|
if (status === 'misconfigured') return { icon: 'times', variant: 'danger' }
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
onQueriesResponse (domains, mainDomain, domain, config) {
|
||||||
|
this.config = formatYunoHostConfigPanels(config)
|
||||||
|
},
|
||||||
|
|
||||||
|
async applyConfig (id_) {
|
||||||
|
const formatedData = await formatFormData(
|
||||||
|
this.config.forms[id_],
|
||||||
|
{ removeEmpty: false, removeNull: true, multipart: false }
|
||||||
|
)
|
||||||
|
|
||||||
|
api.put(
|
||||||
|
`domains/${this.name}/config`,
|
||||||
|
{ key: id_, args: objectToParams(formatedData) },
|
||||||
|
{ key: 'domains.update_config', name: this.name }
|
||||||
|
).then(() => {
|
||||||
|
this.$refs.view.fetchQueries({ triggerLoading: true })
|
||||||
|
}).catch(err => {
|
||||||
|
if (err.name !== 'APIBadRequestError') throw err
|
||||||
|
const panel = this.config.panels.find(({ id }) => id_ === id)
|
||||||
|
if (err.data.name) {
|
||||||
|
this.config.errors[id_][err.data.name].message = err.message
|
||||||
|
} else this.$set(panel, 'serverError', err.message)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
async deleteDomain () {
|
async deleteDomain () {
|
||||||
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_delete', { name: this.name }))
|
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_delete', { name: this.name }))
|
||||||
if (!confirmed) return
|
if (!confirmed) return
|
||||||
|
|
Loading…
Reference in a new issue