Files
OpenGFW/web/frontend/src/views/AnalyzersView.vue
T

104 lines
3.3 KiB
Vue
Raw Normal View History

2026-07-27 08:09:11 +08:00
<script setup lang="ts">
import { Boxes, FunctionSquare } from 'lucide-vue-next'
import { computed, onMounted, ref } from 'vue'
import PageHeader from '@/components/PageHeader.vue'
import {
Badge,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Skeleton,
} from '@/components/ui'
import { useLive } from '@/composables/useLive'
import { t } from '@/i18n'
import { api } from '@/lib/api'
import { compact } from '@/lib/format'
import type { MetaInfo } from '@/lib/types'
const { metrics } = useLive()
const meta = ref<MetaInfo | null>(null)
onMounted(async () => {
try {
meta.value = await api.meta()
} catch {
meta.value = { analyzers: [], modifiers: [], actions: [], functions: [] }
}
})
const hits = computed(() => {
const map = new Map<string, number>()
for (const item of metrics.value?.analyzers ?? []) map.set(item.name, item.count)
return map
})
const analyzers = computed(() =>
[...(meta.value?.analyzers ?? [])].sort(
(a, b) => (hits.value.get(b.name) ?? 0) - (hits.value.get(a.name) ?? 0),
),
)
</script>
<template>
<div>
<PageHeader :title="t('analyzers.title')" :description="t('analyzers.subtitle')" />
<div v-if="!meta" class="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
<Skeleton v-for="i in 8" :key="i" class="h-24" />
</div>
<template v-else>
<div class="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
<Card v-for="analyzer in analyzers" :key="analyzer.name" class="p-3.5 sm:p-4">
<div class="flex items-center justify-between gap-2">
<span class="truncate font-mono text-sm font-medium">{{ analyzer.name }}</span>
<Badge :variant="analyzer.proto === 'udp' ? 'udp' : 'tcp'" class="uppercase">
{{ analyzer.proto }}
</Badge>
</div>
<p class="mt-2 text-xl font-semibold tabular-nums">
{{ compact(hits.get(analyzer.name) ?? 0) }}
</p>
<p class="text-muted-foreground text-xs">{{ t('analyzers.hits') }}</p>
</Card>
</div>
<div class="mt-3 grid gap-3 md:grid-cols-2">
<Card>
<CardHeader>
<CardTitle class="flex items-center gap-2">
<Boxes class="size-4" />
{{ t('analyzers.modifiers') }}
</CardTitle>
<CardDescription>{{ t('analyzers.modifiersDesc') }}</CardDescription>
</CardHeader>
<CardContent class="flex flex-wrap gap-1.5">
<Badge v-for="name in meta.modifiers" :key="name" variant="secondary" class="font-mono">
{{ name }}
</Badge>
<span v-if="!meta.modifiers.length" class="text-muted-foreground text-xs">—</span>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle class="flex items-center gap-2">
<FunctionSquare class="size-4" />
{{ t('analyzers.functions') }}
</CardTitle>
<CardDescription>{{ t('analyzers.functionsDesc') }}</CardDescription>
</CardHeader>
<CardContent class="flex flex-wrap gap-1.5">
<Badge v-for="name in meta.functions" :key="name" variant="secondary" class="font-mono">
{{ name }}()
</Badge>
</CardContent>
</Card>
</div>
</template>
</div>
</template>