diff --git a/src/server/api/routers/dns-hole.ts b/src/server/api/routers/dns-hole.ts index 410aef26b..13d98e962 100644 --- a/src/server/api/routers/dns-hole.ts +++ b/src/server/api/routers/dns-hole.ts @@ -1,10 +1,11 @@ import { z } from 'zod'; -import { createTRPCRouter, publicProcedure } from '../trpc'; +import { findAppProperty } from '~/tools/client/app-properties'; import { getConfig } from '~/tools/config/getConfig'; import { AdGuard } from '~/tools/server/sdk/adGuard/adGuard'; -import { ConfigAppType } from '~/types/app'; -import { findAppProperty } from '~/tools/client/app-properties'; import { PiHoleClient } from '~/tools/server/sdk/pihole/piHole'; +import { ConfigAppType } from '~/types/app'; +import { AdStatistics } from '~/widgets/dnshole/type'; +import { createTRPCRouter, publicProcedure } from '../trpc'; export const dnsHoleRouter = createTRPCRouter({ control: publicProcedure @@ -32,6 +33,52 @@ export const dnsHoleRouter = createTRPCRouter({ }) ); }), + summary: publicProcedure + .input( + z.object({ + configName: z.string(), + }) + ) + .query(async ({ input }) => { + const config = getConfig(input.configName); + + const applicableApps = config.apps.filter( + (x) => x.integration?.type && ['pihole', 'adGuardHome'].includes(x.integration?.type) + ); + + const result = await Promise.all( + applicableApps.map(async (app) => + app.integration?.type === 'pihole' + ? collectPiHoleSummary(app) + : collectAdGuardSummary(app) + ) + ); + + const data = result.reduce( + (prev: AdStatistics, curr) => ({ + domainsBeingBlocked: prev.domainsBeingBlocked + curr.domainsBeingBlocked, + adsBlockedToday: prev.adsBlockedToday + curr.adsBlockedToday, + dnsQueriesToday: prev.dnsQueriesToday + curr.dnsQueriesToday, + status: [...prev.status, curr.status], + adsBlockedTodayPercentage: 0, + }), + { + domainsBeingBlocked: 0, + adsBlockedToday: 0, + adsBlockedTodayPercentage: 0, + dnsQueriesToday: 0, + status: [], + } + ); + + //const data: AdStatistics = ; + + data.adsBlockedTodayPercentage = data.adsBlockedToday / data.dnsQueriesToday; + if (Number.isNaN(data.adsBlockedTodayPercentage)) { + data.adsBlockedTodayPercentage = 0; + } + return data; + }), }); const processAdGuard = async (app: ConfigAppType, enable: boolean) => { @@ -59,3 +106,45 @@ const processPiHole = async (app: ConfigAppType, enable: boolean) => { await pihole.disable(); }; + +const collectPiHoleSummary = async (app: ConfigAppType) => { + const piHole = new PiHoleClient(app.url, findAppProperty(app, 'password')); + const summary = await piHole.getSummary(); + + return { + domainsBeingBlocked: summary.domains_being_blocked, + adsBlockedToday: summary.ads_blocked_today, + dnsQueriesToday: summary.dns_queries_today, + status: { + status: summary.status, + appId: app.id, + }, + adsBlockedTodayPercentage: summary.ads_percentage_today, + }; +}; + +const collectAdGuardSummary = async (app: ConfigAppType) => { + const adGuard = new AdGuard( + app.url, + findAppProperty(app, 'username'), + findAppProperty(app, 'password') + ); + + const stats = await adGuard.getStats(); + const status = await adGuard.getStatus(); + const countFilteredDomains = await adGuard.getCountFilteringDomains(); + + const blockedQueriesToday = stats.blocked_filtering.reduce((prev, sum) => prev + sum, 0); + const queriesToday = stats.dns_queries.reduce((prev, sum) => prev + sum, 0); + + return { + domainsBeingBlocked: countFilteredDomains, + adsBlockedToday: blockedQueriesToday, + dnsQueriesToday: queriesToday, + status: { + status: status.protection_enabled ? ('enabled' as const) : ('disabled' as const), + appId: app.id, + }, + adsBlockedTodayPercentage: (queriesToday / blockedQueriesToday) * 100, + }; +}; diff --git a/src/widgets/dnshole/DnsHoleControls.tsx b/src/widgets/dnshole/DnsHoleControls.tsx index f7f0c8469..b4d1e1077 100644 --- a/src/widgets/dnshole/DnsHoleControls.tsx +++ b/src/widgets/dnshole/DnsHoleControls.tsx @@ -5,10 +5,10 @@ import { useConfigContext } from '../../config/provider'; import { defineWidget } from '../helper'; import { WidgetLoading } from '../loading'; import { IWidget } from '../widgets'; -import { useDnsHoleSummeryQuery } from './query'; import { PiholeApiSummaryType } from './type'; import { queryClient } from '../../tools/server/configurations/tanstack/queryClient.tool'; import { api } from '~/utils/api'; +import { useDnsHoleSummeryQuery } from './DnsHoleSummary'; const definition = defineWidget({ id: 'dns-hole-controls', @@ -30,7 +30,7 @@ interface DnsHoleControlsWidgetProps { } function DnsHoleControlsWidgetTile({ widget }: DnsHoleControlsWidgetProps) { - const { isInitialLoading, data, refetch } = useDnsHoleSummeryQuery(); + const { isInitialLoading, data } = useDnsHoleSummeryQuery(); const { mutateAsync } = useDnsHoleControlMutation(); const { t } = useTranslation('common'); diff --git a/src/widgets/dnshole/DnsHoleSummary.tsx b/src/widgets/dnshole/DnsHoleSummary.tsx index ae382ffab..ece6792cb 100644 --- a/src/widgets/dnshole/DnsHoleSummary.tsx +++ b/src/widgets/dnshole/DnsHoleSummary.tsx @@ -1,4 +1,3 @@ -import { useTranslation } from 'next-i18next'; import { Card, Center, Container, Stack, Text } from '@mantine/core'; import { IconAd, @@ -7,11 +6,13 @@ import { IconSearch, IconWorldWww, } from '@tabler/icons-react'; +import { useTranslation } from 'next-i18next'; +import { useConfigContext } from '~/config/provider'; +import { api } from '~/utils/api'; +import { formatNumber } from '../../tools/client/math'; import { defineWidget } from '../helper'; import { WidgetLoading } from '../loading'; import { IWidget } from '../widgets'; -import { formatNumber } from '../../tools/client/math'; -import { useDnsHoleSummeryQuery } from './query'; const definition = defineWidget({ id: 'dns-hole-summary', @@ -179,4 +180,17 @@ function DnsHoleSummaryWidgetTile({ widget }: DnsHoleSummaryWidgetProps) { ); } +export const useDnsHoleSummeryQuery = () => { + const { name: configName } = useConfigContext(); + + return api.dnsHole.summary.useQuery( + { + configName: configName!, + }, + { + refetchInterval: 3 * 60 * 1000, + } + ); +}; + export default definition; diff --git a/src/widgets/dnshole/query.ts b/src/widgets/dnshole/query.ts deleted file mode 100644 index 7b6d76d76..000000000 --- a/src/widgets/dnshole/query.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { AdStatistics } from './type'; - -export const useDnsHoleSummeryQuery = () => - useQuery({ - queryKey: ['dns-hole-summary'], - queryFn: async () => { - const response = await fetch('/api/modules/dns-hole/summary'); - return (await response.json()) as AdStatistics; - }, - refetchInterval: 3 * 60 * 1000, - });