refactor: rework async DomainDns

This commit is contained in:
axolotle 2024-08-13 00:25:03 +02:00
parent 20bbdb7e86
commit e99a50e5fc
2 changed files with 81 additions and 70 deletions

View file

@ -216,3 +216,18 @@ export type Diagnosis = {
description: string description: string
}[] }[]
} }
// DOMAINS
export type DNSRecord = {
name: string
type: string
managed_by_yunohost: boolean
old_content: string
content: string
spaces: string // Added when formating
}
export type DNSCategories = Record<
'create' | 'update' | 'delete' | 'unchanged',
DNSRecord[]
>

View file

@ -4,8 +4,16 @@ import { useI18n } from 'vue-i18n'
import api from '@/api' import api from '@/api'
import { useAutoModal } from '@/composables/useAutoModal' import { useAutoModal } from '@/composables/useAutoModal'
import { isEmptyValue } from '@/helpers/commons' import type { Obj, StateVariant } from '@/types/commons'
import { useInitialQueries } from '@/composables/useInitialQueries' import type { DNSCategories, DNSRecord } from '@/types/core/api'
type DNSChanges = {
action: 'create' | 'update' | 'delete'
icon: string
variant: string
records: DNSRecord[]
}
type DNSError = { icon: string; variant: StateVariant; message: string }
const props = defineProps<{ const props = defineProps<{
name: string name: string
@ -13,79 +21,70 @@ const props = defineProps<{
const { t } = useI18n() const { t } = useI18n()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading } = useInitialQueries(
[{ uri: `domains/${props.name}/dns/suggest` }], const dnsConfig = await api.get<string>({
{ onQueriesResponse }, uri: `domains/${props.name}/dns/suggest`,
) initial: true,
})
const showAutoConfigCard = ref(true) const showAutoConfigCard = ref(true)
const showManualConfigCard = ref(false) const showManualConfigCard = ref(false)
const dnsConfig = ref('') const dnsChanges = ref<DNSChanges[] | null>(null)
const dnsChanges = ref(undefined) const dnsErrors = ref<DNSError[]>([])
const dnsErrors = ref(undefined) const dnsZone = ref<DNSRecord[] | null>(null)
const dnsZone = ref(undefined) const force = ref<boolean | null>(null)
const force = ref(null)
getDnsChanges() getDnsChanges()
// FIXME other Suspense? get types
function onQueriesResponse(suggestedConfig: any) {
dnsConfig.value = suggestedConfig
}
function getDnsChanges() { function getDnsChanges() {
loading.value = true
return api return api
.post({ .post({
uri: `domains/${props.name}/dns/push?dry_run`, uri: `domains/${props.name}/dns/push?dry_run`,
showModal: false, showModal: true,
websocket: false, websocket: false,
}) })
.then((dnsChanges) => { .then((dnsCategories: DNSCategories) => {
function getLongest(arr, key) { let canForce = false
function getLongest(arr: Obj[], key: string) {
return arr.reduce((acc, obj) => { return arr.reduce((acc, obj) => {
if (obj[key].length > acc) return obj[key].length if (obj[key].length > acc) return obj[key].length
return acc return acc
}, 0) }, 0)
} }
const changes = [] function addSpace(str: string, len: number, prepend: boolean = false) {
let canForce = false return (prepend ? str : '') + ' '.repeat(len - str.length + 1)
const categories = [ }
{ action: 'create', icon: 'plus', variant: 'success' },
{ action: 'update', icon: 'exchange', variant: 'warning' }, function formatRecords(records?: DNSRecord[]) {
{ action: 'delete', icon: 'minus', variant: 'danger' }, if (!records) return null
]
categories.forEach((category) => {
const records = dnsChanges[category.action]
if (records && records.length > 0) {
const longestName = getLongest(records, 'name') const longestName = getLongest(records, 'name')
const longestType = getLongest(records, 'type') const longestType = getLongest(records, 'type')
records.forEach((record) => { return records.map((record) => {
record.name = record.name = addSpace(record.name, longestName, true)
record.name + ' '.repeat(longestName - record.name.length + 1) record.spaces = addSpace(record.type, longestType)
record.spaces = ' '.repeat(longestType - record.type.length + 1) if (record.managed_by_yunohost) canForce = true
if (record.managed_by_yunohost === false) canForce = true return record
}) })
changes.push({ ...category, records })
}
})
const unchanged = dnsChanges.unchanged
if (unchanged) {
const longestName = getLongest(unchanged, 'name')
const longestType = getLongest(unchanged, 'type')
unchanged.forEach((record) => {
record.name =
record.name + ' '.repeat(longestName - record.name.length + 1)
record.spaces = ' '.repeat(longestType - record.type.length + 1)
})
dnsZone.value = unchanged
} }
const categories = [
{ action: 'create' as const, icon: 'plus', variant: 'success' },
{ action: 'update' as const, icon: 'exchange', variant: 'warning' },
{ action: 'delete' as const, icon: 'minus', variant: 'danger' },
]
const changes = categories
.map((category) => {
const records = formatRecords(dnsCategories[category.action])
if (!records) return null
return { ...category, records }
})
.filter((dnsChange) => dnsChange !== null) as DNSChanges[]
dnsZone.value = formatRecords(dnsCategories.unchanged)
dnsChanges.value = changes.length > 0 ? changes : null dnsChanges.value = changes.length > 0 ? changes : null
force.value = canForce ? false : null force.value = canForce ? false : null
loading.value = false
}) })
.catch((err) => { .catch((err) => {
if (err.name !== 'APIBadRequestError') throw err if (err.name !== 'APIBadRequestError') throw err
@ -100,7 +99,6 @@ function getDnsChanges() {
showManualConfigCard.value = true showManualConfigCard.value = true
showAutoConfigCard.value = false showAutoConfigCard.value = false
} }
loading.value = false
}) })
} }
@ -115,24 +113,22 @@ async function pushDnsChanges() {
uri: `domains/${props.name}/dns/push${force.value ? '?force' : ''}`, uri: `domains/${props.name}/dns/push${force.value ? '?force' : ''}`,
humanKey: { key: 'domains.push_dns_changes', name: props.name }, humanKey: { key: 'domains.push_dns_changes', name: props.name },
}) })
.then(async (responseData) => { .then(async (responseData: Obj<string[]>) => {
await getDnsChanges() await getDnsChanges()
if (!isEmptyValue(responseData)) {
dnsErrors.value = Object.keys(responseData).reduce((acc, key) => { dnsErrors.value = Object.keys(responseData).reduce((acc, key) => {
const args = const args =
key === 'warnings' key === 'warnings'
? { icon: 'warning', variant: 'warning' } ? { icon: 'warning', variant: 'warning' as const }
: { icon: 'ban', variant: 'danger' } : { icon: 'ban', variant: 'danger' as const }
responseData[key].forEach((message) => acc.push({ ...args, message })) responseData[key].forEach((message) => acc.push({ ...args, message }))
return acc return acc
}, []) }, [] as DNSError[])
}
}) })
} }
</script> </script>
<template> <template>
<ViewBase :loading="loading" skeleton="CardInfoSkeleton"> <div>
<section v-if="showAutoConfigCard" class="panel-section"> <section v-if="showAutoConfigCard" class="panel-section">
<BCardTitle title-tag="h3"> <BCardTitle title-tag="h3">
{{ $t('domain.dns.auto_config') }} {{ $t('domain.dns.auto_config') }}
@ -176,7 +172,7 @@ async function pushDnsChanges() {
:title=" :title="
managed_by_yunohost === false && force !== true managed_by_yunohost === false && force !== true
? $t('domain.dns.auto_config_ignored') ? $t('domain.dns.auto_config_ignored')
: null : undefined
" "
> >
<YIcon :iname="icon" :class="'text-' + variant" /> <YIcon :iname="icon" :class="'text-' + variant" />
@ -203,7 +199,7 @@ async function pushDnsChanges() {
/> />
<!-- CONFIG ERROR ALERT --> <!-- CONFIG ERROR ALERT -->
<template v-if="dnsErrors && dnsErrors.length"> <template v-if="dnsErrors.length">
<ReadOnlyAlertItem <ReadOnlyAlertItem
v-for="({ variant, icon, message }, i) in dnsErrors" v-for="({ variant, icon, message }, i) in dnsErrors"
:key="i" :key="i"
@ -268,7 +264,7 @@ async function pushDnsChanges() {
<pre class="log">{{ dnsConfig }}</pre> <pre class="log">{{ dnsConfig }}</pre>
</section> </section>
</ViewBase> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>