mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
refactor: rework async ToolFirewall
This commit is contained in:
parent
b9154908b4
commit
732578156a
2 changed files with 99 additions and 77 deletions
|
@ -229,6 +229,19 @@ export type Diagnosis = {
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIREWALL
|
||||||
|
|
||||||
|
type Protocols = { TCP: number[]; UDP: number[] }
|
||||||
|
export type Firewall = {
|
||||||
|
ipv4: Protocols
|
||||||
|
ipv6: Protocols
|
||||||
|
uPnP: Protocols & {
|
||||||
|
TCP_TO_CLOSE: number[]
|
||||||
|
UDP_TO_CLOSE: number[]
|
||||||
|
enabled: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DOMAINS
|
// DOMAINS
|
||||||
|
|
||||||
export type DNSRecord = {
|
export type DNSRecord = {
|
||||||
|
|
|
@ -1,21 +1,63 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import { APIBadRequestError, type APIError } from '@/api/errors'
|
import { APIBadRequestError, type APIError } from '@/api/errors'
|
||||||
import { useForm } from '@/composables/form'
|
import { useForm } from '@/composables/form'
|
||||||
import { useAutoModal } from '@/composables/useAutoModal'
|
import { useAutoModal } from '@/composables/useAutoModal'
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
|
||||||
import { toEntries } from '@/helpers/commons'
|
|
||||||
import { between, integer, required } from '@/helpers/validators'
|
import { between, integer, required } from '@/helpers/validators'
|
||||||
|
import type { Firewall } from '@/types/core/api'
|
||||||
import type { FieldProps, FormFieldDict } from '@/types/form'
|
import type { FieldProps, FormFieldDict } from '@/types/form'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const modalConfirm = useAutoModal()
|
const modalConfirm = useAutoModal()
|
||||||
const { loading, refetch } = useInitialQueries([{ uri: '/firewall?raw' }], {
|
const { protocols, upnpEnabled } = await api
|
||||||
onQueriesResponse,
|
.fetch<Firewall>({ uri: 'firewall?raw' })
|
||||||
})
|
.then((firewall) => {
|
||||||
|
const portTypes = ['TCP', 'UDP'] as const
|
||||||
|
const protocolsTypes = ['ipv4', 'ipv6', 'uPnP'] as const
|
||||||
|
const ports = Object.values(firewall).reduce(
|
||||||
|
(ports, protocols) => {
|
||||||
|
for (const type of portTypes) {
|
||||||
|
for (const port of protocols[type]) {
|
||||||
|
ports[type].add(port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ports
|
||||||
|
},
|
||||||
|
{ TCP: new Set<number>(), UDP: new Set<number>() },
|
||||||
|
)
|
||||||
|
|
||||||
|
type Row = { port: number } & Record<keyof Firewall, boolean>
|
||||||
|
const tables = {
|
||||||
|
TCP: [] as Row[],
|
||||||
|
UDP: [] as Row[],
|
||||||
|
}
|
||||||
|
for (const protocol of portTypes) {
|
||||||
|
for (const port of ports[protocol]) {
|
||||||
|
const row = { port } as Row
|
||||||
|
for (const connection of protocolsTypes) {
|
||||||
|
row[connection] = firewall[connection][protocol].includes(port)
|
||||||
|
}
|
||||||
|
tables[protocol].push(row)
|
||||||
|
}
|
||||||
|
tables[protocol].sort((a, b) => (a.port < b.port ? -1 : 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
protocols: reactive(tables),
|
||||||
|
upnpEnabled: ref(firewall.uPnP.enabled),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const upnpError = ref('')
|
||||||
|
const tableFields = [
|
||||||
|
{ key: 'port', label: t('port') },
|
||||||
|
{ key: 'ipv4', label: t('ipv4') },
|
||||||
|
{ key: 'ipv6', label: t('ipv6') },
|
||||||
|
{ key: 'uPnP', label: t('upnp') },
|
||||||
|
]
|
||||||
|
|
||||||
type Form = {
|
type Form = {
|
||||||
action: 'allow' | 'disallow'
|
action: 'allow' | 'disallow'
|
||||||
|
@ -84,50 +126,12 @@ const fields = {
|
||||||
|
|
||||||
const { v } = useForm(form, fields)
|
const { v } = useForm(form, fields)
|
||||||
|
|
||||||
// Ports tables data
|
async function togglePort({
|
||||||
const protocols = ref()
|
action,
|
||||||
const protocolsFields = toEntries(fields).map(([key, { label }]) => ({
|
port,
|
||||||
key,
|
protocol,
|
||||||
label,
|
connection,
|
||||||
}))
|
}: Form): Promise<boolean> {
|
||||||
|
|
||||||
// uPnP
|
|
||||||
const upnpEnabled = ref()
|
|
||||||
const upnpError = ref('')
|
|
||||||
|
|
||||||
function onQueriesResponse(data: any) {
|
|
||||||
const ports = Object.values(data).reduce(
|
|
||||||
(ports_, protocols_) => {
|
|
||||||
for (const type of ['TCP', 'UDP']) {
|
|
||||||
for (const port of protocols_[type]) {
|
|
||||||
ports_[type].add(port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ports
|
|
||||||
},
|
|
||||||
{ TCP: new Set(), UDP: new Set() },
|
|
||||||
)
|
|
||||||
|
|
||||||
const tables = {
|
|
||||||
TCP: [],
|
|
||||||
UDP: [],
|
|
||||||
}
|
|
||||||
for (const protocol of ['TCP', 'UDP']) {
|
|
||||||
for (const port of ports[protocol]) {
|
|
||||||
const row = { port }
|
|
||||||
for (const connection of ['ipv4', 'ipv6', 'uPnP']) {
|
|
||||||
row[connection] = data[connection][protocol].includes(port)
|
|
||||||
}
|
|
||||||
tables[protocol].push(row)
|
|
||||||
}
|
|
||||||
tables[protocol].sort((a, b) => (a.port < b.port ? -1 : 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
protocols.value = tables
|
|
||||||
upnpEnabled.value = data.uPnP.enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
async function togglePort({ action, port, protocol, connection }) {
|
|
||||||
const confirmed = await modalConfirm(
|
const confirmed = await modalConfirm(
|
||||||
t('confirm_firewall_' + action, {
|
t('confirm_firewall_' + action, {
|
||||||
port,
|
port,
|
||||||
|
@ -135,9 +139,7 @@ async function togglePort({ action, port, protocol, connection }) {
|
||||||
connection,
|
connection,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
if (!confirmed) {
|
if (!confirmed) return false
|
||||||
return Promise.resolve(confirmed)
|
|
||||||
}
|
|
||||||
|
|
||||||
const actionTrad = t({ allow: 'open', disallow: 'close' }[action])
|
const actionTrad = t({ allow: 'open', disallow: 'close' }[action])
|
||||||
return api
|
return api
|
||||||
|
@ -152,10 +154,10 @@ async function togglePort({ action, port, protocol, connection }) {
|
||||||
},
|
},
|
||||||
showModal: false,
|
showModal: false,
|
||||||
})
|
})
|
||||||
.then(() => confirmed)
|
.then(() => true)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleUpnp(value) {
|
async function toggleUpnp() {
|
||||||
const action = upnpEnabled.value ? 'disable' : 'enable'
|
const action = upnpEnabled.value ? 'disable' : 'enable'
|
||||||
const confirmed = await modalConfirm(t('confirm_upnp_' + action))
|
const confirmed = await modalConfirm(t('confirm_upnp_' + action))
|
||||||
if (!confirmed) return
|
if (!confirmed) return
|
||||||
|
@ -167,7 +169,7 @@ async function toggleUpnp(value) {
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// FIXME Couldn't test when it works.
|
// FIXME Couldn't test when it works.
|
||||||
refetch(false)
|
api.refetch()
|
||||||
})
|
})
|
||||||
.catch((err: APIError) => {
|
.catch((err: APIError) => {
|
||||||
if (!(err instanceof APIBadRequestError)) throw err
|
if (!(err instanceof APIBadRequestError)) throw err
|
||||||
|
@ -175,38 +177,43 @@ async function toggleUpnp(value) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTablePortToggling(port, protocol, connection, index, value) {
|
function onTablePortToggling(
|
||||||
protocols.value[protocol][index][connection] = value
|
{ port, protocol, connection }: Omit<Form, 'action'>,
|
||||||
|
index: number,
|
||||||
|
value: boolean,
|
||||||
|
) {
|
||||||
|
const protocols_ =
|
||||||
|
protocol === 'Both' ? (['TCP', 'UDP'] as const) : [protocol]
|
||||||
|
protocols_.forEach((protocol) => {
|
||||||
|
protocols[protocol][index][connection] = value
|
||||||
|
})
|
||||||
const action = value ? 'allow' : 'disallow'
|
const action = value ? 'allow' : 'disallow'
|
||||||
togglePort({ action, port, protocol, connection }).then((toggled) => {
|
togglePort({ action, port, protocol, connection }).then((confirmed) => {
|
||||||
// Revert change on cancel
|
// Revert change on cancel
|
||||||
if (!toggled) {
|
if (!confirmed) {
|
||||||
protocols.value[protocol][index][connection] = !value
|
protocols_.forEach((protocol) => {
|
||||||
|
protocols[protocol][index][connection] = !value
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFormPortToggling() {
|
function onFormPortToggling() {
|
||||||
togglePort(form).then((toggled) => {
|
togglePort(form.value).then((confirmed) => {
|
||||||
if (toggled) refetch(false)
|
// TODO: update data instead of refetch?
|
||||||
|
if (confirmed) api.refetch()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ViewBase :loading="loading" skeleton="CardFormSkeleton">
|
<div>
|
||||||
<!-- PORTS -->
|
<!-- PORTS -->
|
||||||
<YCard :title="$t('ports')" icon="shield">
|
<YCard :title="$t('ports')" icon="shield">
|
||||||
<div v-for="(items, protocol) in protocols" :key="protocol">
|
<div v-for="(items, protocol) in protocols" :key="protocol">
|
||||||
<h5>{{ $t(protocol) }}</h5>
|
<h5>{{ $t(protocol) }}</h5>
|
||||||
|
|
||||||
<BTable
|
<BTable :fields="tableFields" :items="items" small striped responsive>
|
||||||
:fields="protocolsFields"
|
|
||||||
:items="items"
|
|
||||||
small
|
|
||||||
striped
|
|
||||||
responsive
|
|
||||||
>
|
|
||||||
<!-- PORT CELL -->
|
<!-- PORT CELL -->
|
||||||
<template #cell(port)="data">
|
<template #cell(port)="data">
|
||||||
{{ data.value }}
|
{{ data.value }}
|
||||||
|
@ -216,13 +223,15 @@ function onFormPortToggling() {
|
||||||
<template #cell()="data">
|
<template #cell()="data">
|
||||||
<BFormCheckbox
|
<BFormCheckbox
|
||||||
v-if="data.field.key !== 'uPnP'"
|
v-if="data.field.key !== 'uPnP'"
|
||||||
:modelValue="data.value"
|
:model-value="data.value as boolean"
|
||||||
switch
|
switch
|
||||||
@update:modelValue="
|
@update:model-value="
|
||||||
onTablePortToggling(
|
onTablePortToggling(
|
||||||
data.item.port,
|
{
|
||||||
|
port: data.item.port,
|
||||||
protocol,
|
protocol,
|
||||||
data.field.key,
|
connection: data.field.key as Form['connection'],
|
||||||
|
},
|
||||||
data.index,
|
data.index,
|
||||||
$event,
|
$event,
|
||||||
)
|
)
|
||||||
|
@ -278,7 +287,7 @@ function onFormPortToggling() {
|
||||||
</BButton>
|
</BButton>
|
||||||
</template>
|
</template>
|
||||||
</YCard>
|
</YCard>
|
||||||
</ViewBase>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
Loading…
Reference in a new issue