From 4c46a36c75fb805f8a8021de3e94f9d55a01de49 Mon Sep 17 00:00:00 2001 From: Emily Date: Sat, 14 Sep 2024 17:07:46 +0200 Subject: [PATCH] add anomaly + fix billing + add emails templates --- dashboard/app.vue | 2 +- dashboard/components/CVerticalNavigation.vue | 23 ++- .../components/dashboard/ActionableChart.vue | 13 +- dashboard/components/dashboard/TopSection.vue | 30 +++- dashboard/components/settings/General.vue | 11 +- dashboard/layouts/dashboard.vue | 1 + dashboard/pages/analyst.vue | 23 ++- dashboard/server/api/keys/create.post.ts | 2 +- dashboard/server/api/metrics/test.ts | 6 - .../pay/[project_id]/create-onetime.post.ts | 4 + .../api/pay/[project_id]/create.post.ts | 4 + dashboard/server/api/pay/webhook.post.ts | 1 + dashboard/server/api/project/delete.delete.ts | 2 + dashboard/server/init.ts | 10 ++ dashboard/server/services/AnomalyService.ts | 148 ++++++++++++++++++ producer/src/deprecated.ts | 2 +- producer/src/index.ts | 2 +- .../schema/anomalies/AnomalyDomainSchema.ts | 16 ++ .../schema/anomalies/AnomalyEventsSchema.ts | 16 ++ shared/schema/anomalies/AnomalyVisitSchema.ts | 16 ++ shared/services/EmailService.ts | 36 +++++ ...y-dns-email.html => AnomalyDomainEmail.ts} | 12 +- ...-usage-email.html => AnomalyUsageEmail.ts} | 4 +- 23 files changed, 336 insertions(+), 48 deletions(-) delete mode 100644 dashboard/server/api/metrics/test.ts create mode 100644 dashboard/server/services/AnomalyService.ts create mode 100644 shared/schema/anomalies/AnomalyDomainSchema.ts create mode 100644 shared/schema/anomalies/AnomalyEventsSchema.ts create mode 100644 shared/schema/anomalies/AnomalyVisitSchema.ts rename shared/services/email_templates/{anomaly-dns-email.html => AnomalyDomainEmail.ts} (80%) rename shared/services/email_templates/{anomaly-usage-email.html => AnomalyUsageEmail.ts} (95%) diff --git a/dashboard/app.vue b/dashboard/app.vue index 544f4d3..9a7010f 100644 --- a/dashboard/app.vue +++ b/dashboard/app.vue @@ -20,7 +20,7 @@ const { visible } = usePricingDrawer(); + class="bg-black fixed right-0 top-0 w-full xl:w-[60vw] xl:min-w-[65rem] h-full z-[20]" v-if="visible"> diff --git a/dashboard/components/CVerticalNavigation.vue b/dashboard/components/CVerticalNavigation.vue index 407ebc7..92e41b1 100644 --- a/dashboard/components/CVerticalNavigation.vue +++ b/dashboard/components/CVerticalNavigation.vue @@ -28,6 +28,7 @@ const route = useRoute(); const props = defineProps(); const { isAdmin } = useUserRoles(); +const loggedUser = useLoggedUser() const debugMode = process.dev; @@ -101,8 +102,15 @@ function onLogout() { } const { projects } = useProjectsList(); +const { data: guestProjects } = useGuestProjectsList() const activeProject = useActiveProject(); +const selectorProjects = computed(() => { + const result: TProject[] = []; + if (projects.value) result.push(...projects.value); + if (guestProjects.value) result.push(...guestProjects.value); + return result; +}); const { data: maxProjects } = useFetch("/api/user/max_projects", { headers: computed(() => { @@ -121,6 +129,11 @@ const isPremium = computed(() => { return activeProject.value?.premium; }) +function isProjectMine(owner?: string) { + if (!owner) return false; + if (!loggedUser.value?.logged) return; + return loggedUser.value.id == owner; +} const pricingDrawer = usePricingDrawer(); @@ -152,14 +165,14 @@ const pricingDrawer = usePricingDrawer(); base: 'hover:!bg-lyx-widget-lighter cursor-pointer', active: '!bg-lyx-widget-lighter' } - }" class="w-full" v-if="projects" v-model="selected" :options="projects"> + }" class="w-full" v-if="selectorProjects" v-model="selected" :options="selectorProjects"> @@ -168,7 +181,10 @@ const pricingDrawer = usePricingDrawer();
Litlyx logo
-
{{ activeProject?.name || '???' }}
+
+ {{ activeProject?.name || '-' }} + {{ !isProjectMine(activeProject?.owner?.toString()) ? '(Guest)' : '' }} +
@@ -179,6 +195,7 @@ const pricingDrawer = usePricingDrawer(); +
diff --git a/dashboard/components/dashboard/ActionableChart.vue b/dashboard/components/dashboard/ActionableChart.vue index 3759ef2..8fad86d 100644 --- a/dashboard/components/dashboard/ActionableChart.vue +++ b/dashboard/components/dashboard/ActionableChart.vue @@ -106,11 +106,11 @@ const externalTooltipElement = ref(null); function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'line' | 'bar'> }) { const { chart, tooltip } = context; const tooltipEl = externalTooltipElement.value; - - currentTooltipData.value.visits = (tooltip.dataPoints.find(e=> e.datasetIndex == 0)?.raw) as number; - currentTooltipData.value.sessions = (tooltip.dataPoints.find(e=> e.datasetIndex == 1)?.raw) as number; - currentTooltipData.value.events = ((tooltip.dataPoints.find(e=> e.datasetIndex == 2)?.raw) as any)?.r2 as number; - + + currentTooltipData.value.visits = (tooltip.dataPoints.find(e => e.datasetIndex == 0)?.raw) as number; + currentTooltipData.value.sessions = (tooltip.dataPoints.find(e => e.datasetIndex == 1)?.raw) as number; + currentTooltipData.value.events = ((tooltip.dataPoints.find(e => e.datasetIndex == 2)?.raw) as any)?.r2 as number; + currentTooltipData.value.date = new Date(allDatesFull.value[tooltip.dataPoints[0].dataIndex]).toLocaleDateString(); if (!tooltipEl) return; @@ -118,11 +118,12 @@ function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'li tooltipEl.style.opacity = '0'; return; } - const { offsetLeft: positionX, offsetTop: positionY } = chart.canvas; + const { left: positionX, top: positionY } = chart.canvas.getBoundingClientRect(); tooltipEl.style.opacity = '1'; tooltipEl.style.left = positionX + tooltip.caretX + 'px'; tooltipEl.style.top = positionY + tooltip.caretY + 'px'; tooltipEl.style.padding = tooltip.options.padding + 'px ' + tooltip.options.padding + 'px'; + } diff --git a/dashboard/components/dashboard/TopSection.vue b/dashboard/components/dashboard/TopSection.vue index d7702f4..4de15d6 100644 --- a/dashboard/components/dashboard/TopSection.vue +++ b/dashboard/components/dashboard/TopSection.vue @@ -13,6 +13,19 @@ function copyProjectId() { navigator.clipboard.writeText((activeProject.value?._id || 0).toString()); createAlert('Success', 'Project id copied successfully.', 'far fa-circle-check', 5000); } + + +function showAnomalyInfoAlert() { + createAlert('AI Anomaly Detector info', + `Anomaly detector is running. It helps you detect a spike in visits or events, it could mean an + attack or simply higher traffic due to good performance. Additionally, it can detect if someone is + stealing parts of your website and hosting a duplicate version—an unfortunately common practice. + Litlyx will notify you via email with actionable advices`, + 'far fa-bug', + 10000 + ) + +} @@ -29,8 +42,10 @@ function copyProjectId() {
Project:
-
{{ activeProject?.name || 'Loading...' }}
+
{{ activeProject?.name || 'Loading...' }} +
+
Project id:
@@ -38,9 +53,20 @@ function copyProjectId() { {{ activeProject?._id || 'Loading...' }}
- +
+ +
+
+
AI Anomaly Detector
+
+ +
+
+ \ No newline at end of file diff --git a/dashboard/components/settings/General.vue b/dashboard/components/settings/General.vue index c1736b3..803a2bc 100644 --- a/dashboard/components/settings/General.vue +++ b/dashboard/components/settings/General.vue @@ -14,7 +14,6 @@ const entries: SettingsTemplateEntry[] = [ const activeProject = useActiveProject(); const projectNameInputVal = ref(activeProject.value?.name || ''); - const apiKeys = ref([]); const newApiKeyName = ref(''); @@ -145,13 +144,15 @@ function copyProjectId() { -