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",
|
||||
"dns": "DNS",
|
||||
"domain": {
|
||||
"cert": {
|
||||
"types": {
|
||||
"self-signed": "Self-signed",
|
||||
"lets-encrypt": "Let's Encrypt",
|
||||
"other-unknown": "Other/Unknown"
|
||||
},
|
||||
"valid_for": "valid for {days}"
|
||||
},
|
||||
"config": {
|
||||
"edit": "Edit 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_ok": "Automatic configuration seems to be OK!",
|
||||
"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",
|
||||
"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",
|
||||
|
@ -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_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": {
|
||||
"parent_domain": "Parent domain",
|
||||
"subdomain": "Subdomain",
|
||||
"main_domain": "Main domain"
|
||||
},
|
||||
"toggle_subdomains": "Toggle subdomains"
|
||||
|
@ -554,8 +588,10 @@
|
|||
"browse": "Browse",
|
||||
"collapse": "Collapse",
|
||||
"default": "Default",
|
||||
"link": "Link",
|
||||
"none": "None",
|
||||
"separator": ", "
|
||||
"separator": ", ",
|
||||
"valid": "Valid"
|
||||
},
|
||||
"wrong_password_or_username": "Wrong password or username",
|
||||
"yes": "Yes",
|
||||
|
|
|
@ -68,7 +68,10 @@ body {
|
|||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.tooltip { top: 0; }
|
||||
// Descriptive list (<b-row /> elems with <b-col> inside)
|
||||
// FIXME REMOVE when every infos switch to `DescriptionRow`
|
||||
.row-line {
|
||||
@include media-breakpoint-up(md) {
|
||||
&:hover {
|
||||
|
|
|
@ -22,6 +22,7 @@ export default {
|
|||
state: () => ({
|
||||
main_domain: undefined,
|
||||
domains: undefined, // Array
|
||||
domains_details: {},
|
||||
users: undefined, // basic user data: Object {username: {data}}
|
||||
users_details: {}, // precise user data: Object {username: {data}}
|
||||
groups: undefined,
|
||||
|
@ -33,6 +34,22 @@ export default {
|
|||
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 }]) {
|
||||
state.domains.push(domain)
|
||||
},
|
||||
|
|
|
@ -1,83 +1,221 @@
|
|||
<template>
|
||||
<view-base :queries="queries" skeleton="card-list-skeleton">
|
||||
<card :title="name" icon="globe">
|
||||
<!-- VISIT -->
|
||||
<p>{{ $t('domain_visit_url', { url: 'https://' + name }) }}</p>
|
||||
<b-button variant="success" :href="'https://' + name" target="_blank">
|
||||
<icon iname="external-link" /> {{ $t('domain_visit') }}
|
||||
</b-button>
|
||||
<hr>
|
||||
<view-base :queries="queries" @queries-response="onQueriesResponse" skeleton="card-list-skeleton">
|
||||
<!-- INFO CARD -->
|
||||
<card v-if="domain" :title="name" icon="globe">
|
||||
<template #header-buttons>
|
||||
<!-- DEFAULT DOMAIN -->
|
||||
<b-button v-if="!isMainDomain" @click="setAsDefaultDomain" variant="info">
|
||||
<icon iname="star" /> {{ $t('set_default') }}
|
||||
</b-button>
|
||||
|
||||
<!-- DEFAULT DOMAIN -->
|
||||
<p>{{ $t('domain_default_desc') }}</p>
|
||||
<p v-if="isMainDomain" class="alert alert-info">
|
||||
<icon iname="star" /> {{ $t('domain_default_longdesc') }}
|
||||
</p>
|
||||
<b-button v-else variant="info" @click="setAsDefaultDomain">
|
||||
<icon iname="star" /> {{ $t('set_default') }}
|
||||
</b-button>
|
||||
<hr>
|
||||
<!-- DELETE DOMAIN -->
|
||||
<b-button @click="deleteDomain" :disabled="isMainDomain" variant="danger">
|
||||
<icon iname="trash-o" /> {{ $t('delete') }}
|
||||
</b-button>
|
||||
</template>
|
||||
|
||||
<!-- DOMAIN CONFIG -->
|
||||
<p>{{ $t('domain.config.edit') }}</p>
|
||||
<b-button variant="warning" :to="{ name: 'domain-config', param: { name } }">
|
||||
<icon iname="cog" /> {{ $t('domain.config.title') }}
|
||||
</b-button>
|
||||
<hr>
|
||||
<!-- DOMAIN LINK -->
|
||||
<description-row :term="$t('words.link')">
|
||||
<b-link :href="'https://' + name" target="_blank">
|
||||
https://{{ name }}
|
||||
</b-link>
|
||||
</description-row>
|
||||
|
||||
<!-- DNS CONFIG -->
|
||||
<p>{{ $t('domain.dns.edit') }}</p>
|
||||
<b-button variant="warning" :to="{ name: 'domain-dns', param: { name } }">
|
||||
<icon iname="globe" /> {{ $t('domain_dns_config') }}
|
||||
</b-button>
|
||||
<hr>
|
||||
<!-- DOMAIN TYPE -->
|
||||
<description-row
|
||||
:term="$t('domain.info.domain_type')"
|
||||
>
|
||||
<span v-if="isMainDomain">
|
||||
<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 -->
|
||||
<p>{{ $t('domain_delete_longdesc') }}</p>
|
||||
<p
|
||||
v-if="isMainDomain" class="alert alert-info"
|
||||
v-html="$t('domain_delete_forbidden_desc', { domain: name })"
|
||||
/>
|
||||
<b-button v-else variant="danger" @click="deleteDomain">
|
||||
<icon iname="trash-o" /> {{ $t('delete') }}
|
||||
</b-button>
|
||||
<!-- DOMAIN CERT AUTHORITY -->
|
||||
<description-row :term="$t('domain.info.certificate_authority')">
|
||||
<icon :iname="cert.status.icon" :variant="cert.status.variant" class="mr-1" />
|
||||
{{ $t('domain.cert.types.' + cert.authority) }}
|
||||
({{ $t('domain.cert.valid_for', { days: $tc('day_validity', cert.validity) }) }})
|
||||
</description-row>
|
||||
|
||||
<!-- DOMAIN DNS METHOD -->
|
||||
<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>
|
||||
|
||||
<config-panels v-if="config.panels" v-bind="config" @submit="applyConfig" />
|
||||
</view-base>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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 {
|
||||
name: 'DomainInfo',
|
||||
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
components: {
|
||||
ConfigPanels
|
||||
},
|
||||
|
||||
data: () => {
|
||||
props: {
|
||||
name: { type: String, required: true }
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
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: {
|
||||
...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 () {
|
||||
if (!this.mainDomain) return
|
||||
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: {
|
||||
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 () {
|
||||
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_delete', { name: this.name }))
|
||||
if (!confirmed) return
|
||||
|
|
Loading…
Reference in a new issue