refactor: rework async ToolLog

This commit is contained in:
axolotle 2024-08-13 00:30:09 +02:00
parent 732578156a
commit 27f87eeb17
2 changed files with 104 additions and 87 deletions

View file

@ -242,6 +242,33 @@ export type Firewall = {
} }
} }
// LOGS
export type LogInfo = {
description: string
name: string
metadata_path: string
metadata: {
ended_at: string | null
env: Obj
error: string | null
interface: 'cli' | 'api'
operation: string
parent: string | null
related_to: string[][]
started_at: string
success: boolean | '?'
yunohost_version: string
suboperations: {
name: string
description: string
success: boolean | '?'
}[]
}
log_path: string
logs: string[]
}
// DOMAINS // DOMAINS
export type DNSRecord = { export type DNSRecord = {

View file

@ -1,68 +1,61 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { useRouter } from 'vue-router'
import api, { objectToParams } from '@/api' import api, { objectToParams } from '@/api'
import type { APIQuery } from '@/api/api'
import { useInitialQueries } from '@/composables/useInitialQueries'
import { escapeHtml } from '@/helpers/commons' import { escapeHtml } from '@/helpers/commons'
import { readableDate } from '@/helpers/filters/date' import { readableDate } from '@/helpers/filters/date'
import type { LogInfo } from '@/types/core/api'
const props = defineProps<{ const props = withDefaults(
name: string defineProps<{
}>() name: string
n?: number
}>(),
{
n: 25,
},
)
const numberOfLines = ref(25) const router = useRouter()
const queries = computed<APIQuery[]>(() => {
const queryString = objectToParams({ const { description, logs, moreLogsAvailable, info } = await api
filter_irrelevant: '', .fetch<LogInfo>({
with_suboperations: '', uri: `logs/${props.name}?${objectToParams({
number: numberOfLines.value, filter_irrelevant: '',
with_suboperations: '',
number: props.n || 25,
})}`,
}) })
return [{ uri: `logs/${props.name}?${queryString}` }] .then((log) => {
}) const levels = ['ERROR', 'WARNING', 'SUCCESS', 'INFO']
const { loading, refetch } = useInitialQueries(queries, { const logs = log.logs
onQueriesResponse, .map((line) => {
}) const escaped = escapeHtml(line)
for (const level of levels) {
// Log data if (line.includes(level + ' -')) {
const description = ref() return `<span class="alert-${
const info = ref({}) level === 'ERROR' ? 'danger' : level.toLowerCase()
const logs = ref() }">${escaped}</span>`
// Logs line display }
const moreLogsAvailable = ref(false)
function onQueriesResponse(log: any) {
if (log.logs.length === numberOfLines.value) {
moreLogsAvailable.value = true
numberOfLines.value *= 10
} else {
moreLogsAvailable.value = false
}
description.value = log.description
const levels = ['ERROR', 'WARNING', 'SUCCESS', 'INFO']
logs.value = log.logs
.map((line) => {
const escaped = escapeHtml(line)
for (const level of levels) {
if (line.includes(level + ' -')) {
return `<span class="alert-${
level === 'ERROR' ? 'danger' : level.toLowerCase()
}">${escaped}</span>`
} }
} return escaped
return escaped })
}) .join('\n')
.join('\n') const { started_at, ended_at, error, suboperations } = log.metadata
// eslint-disable-next-line return {
const { started_at, ended_at, error, success, suboperations } = log.metadata description: log.description,
const info_ = { path: log.log_path, started_at, ended_at } logs,
if (!success) info_.error = error moreLogsAvailable: log.logs.length === props.n || 25,
if (suboperations && suboperations.length) info_.suboperations = suboperations info: {
// eslint-disable-next-line path: log.log_path,
if (!ended_at) delete info_.ended_at started_at: readableDate(started_at),
info.value = info ended_at: ended_at ? readableDate(ended_at) : null,
} error: error ? !!error : null,
suboperations:
suboperations && suboperations.length ? suboperations : null,
},
}
})
function shareLogs() { function shareLogs() {
api api
@ -78,40 +71,38 @@ function shareLogs() {
</script> </script>
<template> <template>
<ViewBase :loading="loading" skeleton="CardInfoSkeleton"> <div>
<!-- INFO CARD --> <!-- INFO CARD -->
<YCard :title="description" icon="info-circle"> <YCard :title="description" icon="info-circle">
<BRow <template v-for="(value, prop) in info" :key="prop">
v-for="(value, prop) in info" <BRow v-if="value !== null" no-gutters class="row-line">
:key="prop" <BCol md="3" xl="2">
no-gutters <strong>{{ $t('logs_' + prop) }}</strong>
class="row-line" </BCol>
>
<BCol md="3" xl="2">
<strong>{{ $t('logs_' + prop) }}</strong>
</BCol>
<BCol> <BCol>
<span v-if="prop.endsWith('_at')">{{ readableDate(value) }}</span> <div v-if="prop === 'suboperations'">
<div
<div v-else-if="prop === 'suboperations'"> v-for="operation in value as LogInfo['metadata']['suboperations']"
<div v-for="operation in value" :key="operation.name"> :key="operation.name"
<YIcon
v-if="operation.success !== true"
iname="times"
class="text-danger"
/>
<BLink
:to="{ name: 'tool-log', params: { name: operation.name } }"
> >
{{ operation.description }} <YIcon
</BLink> v-if="operation.success !== true"
iname="times"
class="text-danger"
/>
<BLink
:to="{ name: 'tool-log', params: { name: operation.name } }"
>
{{ operation.description }}
</BLink>
</div>
</div> </div>
</div>
<span v-else>{{ value }}</span> <span v-else>{{ value }}</span>
</BCol> </BCol>
</BRow> </BRow>
</template>
</YCard> </YCard>
<div v-if="info.error" class="alert alert-danger my-5"> <div v-if="info.error" class="alert alert-danger my-5">
@ -129,9 +120,8 @@ function shareLogs() {
<BButton <BButton
v-if="moreLogsAvailable" v-if="moreLogsAvailable"
variant="white"
class="w-100 rounded-0" class="w-100 rounded-0"
@click="refetch(false)" @click="router.replace({ params: { n: props.n * 10 } })"
> >
<YIcon iname="plus" /> {{ $t('logs_more') }} <YIcon iname="plus" /> {{ $t('logs_more') }}
</BButton> </BButton>
@ -143,5 +133,5 @@ function shareLogs() {
</YCard> </YCard>
<p class="w-100 px-5 py-2 mb-0" v-html="$t('text_selection_is_disabled')" /> <p class="w-100 px-5 py-2 mb-0" v-html="$t('text_selection_is_disabled')" />
</ViewBase> </div>
</template> </template>