shields update

This commit is contained in:
Emily
2025-03-20 16:04:00 +01:00
parent afda29997d
commit 87c9aca5c4
34 changed files with 793 additions and 12 deletions

View File

@@ -54,7 +54,7 @@ onMounted(() => {
<div class="flex overflow-x-auto hide-scrollbars">
<div class="flex">
<div v-for="(tab, index) of items" @click="onChangeTab(index)"
class="px-6 pb-3 poppins font-medium text-lyx-lightmode-text dark:text-lyx-text-darker border-b-[1px] border-lyx-text-darker"
class="px-6 whitespace-nowrap pb-3 poppins font-medium text-lyx-lightmode-text dark:text-lyx-text-darker border-b-[1px] border-lyx-text-darker"
:class="{
'dark:!border-[#FFFFFF] dark:!text-[#FFFFFF] !border-lyx-primary !text-lyx-primary': activeTabIndex === index,
'hover:border-lyx-lightmode-text-dark hover:text-lyx-lightmode-text-dark/60 dark:hover:border-lyx-text-dark dark:hover:text-lyx-text-dark cursor-pointer': activeTabIndex !== index

View File

@@ -11,5 +11,5 @@ const widgetStyle = computed(() => {
</script>
<template>
<div :style="widgetStyle" class="bg-lyx-widget-light"></div>
<div :style="widgetStyle" class="dark:bg-lyx-widget-light bg-lyx-lightmode-widget"></div>
</template>

View File

@@ -9,7 +9,23 @@ const avgDuration = computed(() => {
return (backendData.value.durations.durations.reduce((a: any, e: any) => a + parseInt(e[1]), 0) / backendData.value.durations.durations.length);
})
const labels = new Array(650).fill('-');
const labels = computed(() => {
if (!backendData?.value?.durations) return [];
const sizes = new Map<string, number>();
for (const e of backendData.value.durations.durations) {
if (!sizes.has(e[0])) {
sizes.set(e[0], 0);
} else {
const data = sizes.get(e[0]) ?? 0;
sizes.set(e[0], data + 1);
}
}
const max = Array.from(sizes.values()).reduce((a, e) => a > e ? a : e, 0);
return new Array(max).fill('-');
});
const durationsDatasets = computed(() => {
if (!backendData?.value?.durations) return [];
@@ -26,7 +42,7 @@ const durationsDatasets = computed(() => {
datasets.push({
points: consumerDurations.map((e: any) => {
return 1000 / parseInt(e[1])
return 1000 / parseInt(e[1])
}),
color: colors[i],
chartType: 'line',
@@ -45,7 +61,7 @@ const durationsDatasets = computed(() => {
<div class="cursor-default flex justify-center w-full">
<div v-if="backendData" class="flex flex-col mt-8 gap-6 px-20 items-center w-full">
<div v-if="backendData && !backendPending" class="flex flex-col mt-8 gap-6 px-20 items-center w-full">
<div class="flex gap-8">
<div> Queue size: {{ backendData.queue?.size || 'ERROR' }} </div>

View File

@@ -373,8 +373,9 @@ const legendClasses = ref<string[]>([
</div>
<div class="mt-3 font-normal flex flex-col text-[.9rem] dark:text-lyx-text-dark text-lyx-lightmode-text-dark"
v-if="(currentTooltipData as any).sessions > (currentTooltipData as any).visits">
<div> Unique visitors is greater than visits. </div>
<div> This can indicate bot traffic. </div>
<div> Unique visitors are higher than total visits </div>
<div> which often means bots (automated scripts or crawlers)</div>
<div> are inflating the numbers.</div>
</div>
<!-- <div class="bg-lyx-background-lighter h-[2px] w-full my-2"> </div> -->
</LyxUiCard>

View File

@@ -0,0 +1,67 @@
<script lang="ts" setup>
const emit = defineEmits(['success', 'cancel']);
const domain = ref<string>('');
const canAddDomain = computed(() => {
return domain.value.trim().length > 0;
})
async function addDomain() {
if (!canAddDomain.value) return;
try {
const res = await $fetch('/api/shields/domains/add', {
method: 'POST',
headers: useComputedHeaders({}).value,
body: JSON.stringify({ domain: domain.value })
});
domain.value = '';
emit('success');
} catch (ex: any) {
alert(ex.message);
emit('cancel');
}
}
</script>
<template>
<UModal :ui="{
strategy: 'override',
overlay: {
background: 'bg-lyx-background/85'
},
background: 'dark:bg-lyx-widget bg-lyx-lightmode-widget-light',
ring: 'border-solid border-[1px] border-[#262626]'
}">
<div class="h-full flex flex-col gap-2 p-4">
<div class="flex flex-col gap-3">
<div class="font-semibold text-[1.1rem]"> Add Domain to Allow List </div>
<LyxUiInput class="px-2 py-1" v-model="domain"></LyxUiInput>
<div class="flex flex-col gap-2 dark:text-lyx-text-dark text-lyx-lightmode-text-dark">
<div>
<div> You can use a wildcard (*) to match multiple hostnames. </div>
<div> For example, *.domain.com will only record traffic on the main domain and all the
subdomains.
</div>
</div>
<div> NB: Once added, we will start rejecting traffic from non-matching hostnames within a few
minutes.</div>
</div>
<div class="flex">
<LyxUiButton class="w-full text-center" :disabled="!canAddDomain" @click="addDomain()" type="primary">
Add domain
</LyxUiButton>
</div>
</div>
</div>
</UModal>
</template>

View File

@@ -0,0 +1,56 @@
<script lang="ts" setup>
const emit = defineEmits(['success', 'cancel']);
const props = defineProps<{ domain: string }>();
async function deleteDomain() {
if (!props.domain) return;
try {
const res = await $fetch('/api/shields/domains/delete', {
method: 'DELETE',
headers: useComputedHeaders({}).value,
body: JSON.stringify({ domain: props.domain })
});
emit('success');
} catch (ex: any) {
alert(ex.message);
emit('cancel');
}
}
</script>
<template>
<UModal :ui="{
strategy: 'override',
overlay: {
background: 'bg-lyx-background/85'
},
background: 'dark:bg-lyx-widget bg-lyx-lightmode-widget-light',
ring: 'border-solid border-[1px] border-[#262626]'
}">
<div class="h-full flex flex-col gap-2 p-4">
<div class="flex flex-col gap-3">
<div class="font-semibold text-[1.1rem]"> Domain delete </div>
<div> Are you sure to delete the whitelisted domain
<span class="font-semibold">{{ props.domain }}</span>
</div>
<div class="flex justify-end gap-2">
<LyxUiButton type="secondary" @click="emit('cancel')">
Cancel
</LyxUiButton>
<LyxUiButton @click="deleteDomain()" type="danger">
Delete
</LyxUiButton>
</div>
</div>
</div>
</UModal>
</template>

View File

@@ -0,0 +1,101 @@
<script lang="ts" setup>
import { DialogShieldsAddDomain, DialogShieldsDeleteDomain } from '#components';
definePageMeta({ layout: 'dashboard' });
const { project } = useProject();
const { data: blackAddresses, refresh: refreshAddresses, pending: pendingAddresses } = useFetch('/api/shields/ip/list', {
headers: useComputedHeaders({})
});
const toast = useToast()
const modal = useModal();
function showAddDomainModal() {
modal.open(DialogShieldsAddDomain, {
onSuccess: () => {
refreshAddresses();
modal.close();
toast.add({
id: 'shield_domain_add_success',
title: 'Success',
description: 'Whitelist updated with the new domain',
timeout: 5000
});
},
onCancel: () => {
modal.close();
}
})
}
function showDeleteDomainModal(domain: string) {
modal.open(DialogShieldsDeleteDomain, {
domain,
onSuccess: () => {
refreshAddresses();
modal.close();
toast.add({
id: 'shield_domain_remove_success',
title: 'Deleted',
description: 'Whitelist domain deleted successfully',
timeout: 5000
});
},
onCancel: () => {
modal.close();
}
})
}
</script>
<template>
<div class="py-4 flex">
<LyxUiCard class="w-full mx-2">
<div>
<div class="text-[1.2rem] font-semibold"> IP Block Llist </div>
<div class="dark:text-lyx-text-dark text-lyx-lightmode-text-dark">
Reject incoming traffic from specific IP addresses
</div>
</div>
<LyxUiSeparator class="my-3"></LyxUiSeparator>
<div class="flex justify-end pb-3">
<LyxUiButton type="primary" @click="showAddDomainModal()"> Add Domain </LyxUiButton>
</div>
<div class="flex justify-center pb-8 text-[1.2rem]" v-if="pendingAddresses">
<i class="fas fa-loader animate-spin"></i>
</div>
<div v-if="!pendingAddresses && blackAddresses && blackAddresses.length == 0"
class="flex flex-col items-center pb-8">
<div>
No domain rules configured for this project.
</div>
<div class="font-semibold">
Traffic from all domains is currently accepted.
</div>
</div>
<div v-if="!pendingAddresses && blackAddresses && blackAddresses.length > 0"
class="grid grid-cols-[auto_auto_auto_auto] px-10">
<div class="col-span-3">Domain</div>
<div>Actions</div>
<LyxUiSeparator class="col-span-4 my-3"></LyxUiSeparator>
<template v-for="domain of blackAddresses">
<div class="col-span-3 mb-3">{{ domain }}</div>
<div> <i @click="showDeleteDomainModal(domain)"
class="far fa-trash cursor-pointer hover:text-lyx-text-dark"></i> </div>
</template>
</div>
</LyxUiCard>
</div>
</template>

View File

@@ -0,0 +1,101 @@
<script lang="ts" setup>
import { DialogShieldsAddDomain, DialogShieldsDeleteDomain } from '#components';
definePageMeta({ layout: 'dashboard' });
const { project } = useProject();
const { data: allowedDomains, refresh: refreshDomains, pending: pendingDomains } = useFetch('/api/shields/domains/list', {
headers: useComputedHeaders({})
});
const toast = useToast()
const modal = useModal();
function showAddDomainModal() {
modal.open(DialogShieldsAddDomain, {
onSuccess: () => {
refreshDomains();
modal.close();
toast.add({
id: 'shield_domain_add_success',
title: 'Success',
description: 'Whitelist updated with the new domain',
timeout: 5000
});
},
onCancel: () => {
modal.close();
}
})
}
function showDeleteDomainModal(domain: string) {
modal.open(DialogShieldsDeleteDomain, {
domain,
onSuccess: () => {
refreshDomains();
modal.close();
toast.add({
id: 'shield_domain_remove_success',
title: 'Deleted',
description: 'Whitelist domain deleted successfully',
timeout: 5000
});
},
onCancel: () => {
modal.close();
}
})
}
</script>
<template>
<div class="py-4 flex">
<LyxUiCard class="w-full mx-2">
<div>
<div class="text-[1.2rem] font-semibold"> Domains allow list </div>
<div class="dark:text-lyx-text-dark text-lyx-lightmode-text-dark">
Accept incoming traffic only from familiar domains
</div>
</div>
<LyxUiSeparator class="my-3"></LyxUiSeparator>
<div class="flex justify-end pb-3">
<LyxUiButton type="primary" @click="showAddDomainModal()"> Add Domain </LyxUiButton>
</div>
<div class="flex justify-center pb-8 text-[1.2rem]" v-if="pendingDomains">
<i class="fas fa-loader animate-spin"></i>
</div>
<div v-if="!pendingDomains && allowedDomains && allowedDomains.length == 0"
class="flex flex-col items-center pb-8">
<div>
No domain rules configured for this project.
</div>
<div class="font-semibold">
Traffic from all domains is currently accepted.
</div>
</div>
<div v-if="!pendingDomains && allowedDomains && allowedDomains.length > 0"
class="grid grid-cols-[auto_auto_auto_auto] px-10">
<div class="col-span-3">Domain</div>
<div>Actions</div>
<LyxUiSeparator class="col-span-4 my-3"></LyxUiSeparator>
<template v-for="domain of allowedDomains">
<div class="col-span-3 mb-3">{{ domain }}</div>
<div> <i @click="showDeleteDomainModal(domain)"
class="far fa-trash cursor-pointer hover:text-lyx-text-dark"></i> </div>
</template>
</div>
</LyxUiCard>
</div>
</template>