Skip to content

Commit

Permalink
✨ Add ad guard home (#937)
Browse files Browse the repository at this point in the history
* ✨ Add add guard home

* ✨ Add request for blocked domains and fix request for blocked queries

* ♻️ PR feedback

* ✅ Fix tests
  • Loading branch information
manuel-rw committed May 20, 2023
1 parent 85dfb5b commit fb52c4b
Show file tree
Hide file tree
Showing 15 changed files with 646 additions and 257 deletions.
20 changes: 0 additions & 20 deletions __checks__/api.check.ts

This file was deleted.

11 changes: 0 additions & 11 deletions __checks__/homepage.spec.ts

This file was deleted.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
"devDependencies": {
"@next/bundle-analyzer": "^13.0.0",
"@next/eslint-plugin-next": "^13.0.0",
"@playwright/test": "^1.33.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/dockerode": "^3.3.9",
Expand Down Expand Up @@ -110,7 +109,7 @@
"turbo": "latest",
"typescript": "^5.0.4",
"video.js": "^8.0.3",
"vitest": "^0.29.3",
"vitest": "^0.31.1",
"vitest-fetch-mock": "^0.2.2"
},
"resolutions": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @next/next/no-img-element */
import { Group, Select, SelectItem, Text } from '@mantine/core';
import { Group, Image, Select, SelectItem, Text } from '@mantine/core';
import { UseFormReturnType } from '@mantine/form';
import { useTranslation } from 'next-i18next';
import { forwardRef } from 'react';
Expand Down Expand Up @@ -87,9 +87,14 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => {
},
{
value: 'pihole',
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pihole.png',
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pi-hole.png',
label: 'PiHole',
},
{
value: 'adGuardHome',
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/adguard-home.png',
label: 'AdGuard Home',
},
].filter((x) => Object.keys(integrationFieldProperties).includes(x.value));

const getNewProperties = (value: string | null): AppIntegrationPropertyType[] => {
Expand Down Expand Up @@ -133,11 +138,12 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => {
}
icon={
form.values.integration?.type && (
<img
<Image
src={data.find((x) => x.value === form.values.integration?.type)?.image}
alt="integration"
width={20}
height={20}
fit="contain"
/>
)
}
Expand All @@ -160,7 +166,7 @@ const SelectItemComponent = forwardRef<HTMLDivElement, ItemProps>(
({ image, label, description, ...others }: ItemProps, ref) => (
<div ref={ref} {...others}>
<Group noWrap>
<img src={image} alt="integration icon" width={20} height={20} />
<Image src={image} alt="integration icon" width={20} height={20} fit="contain" />

<div>
<Text size="sm">{label}</Text>
Expand Down
49 changes: 36 additions & 13 deletions src/pages/api/modules/dns-hole/control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { getConfig } from '../../../../tools/config/getConfig';
import { findAppProperty } from '../../../../tools/client/app-properties';
import { PiHoleClient } from '../../../../tools/server/sdk/pihole/piHole';
import { ConfigAppType } from '../../../../types/app';
import { AdGuard } from '../../../../tools/server/sdk/adGuard/adGuard';

const getQuerySchema = z.object({
status: z.enum(['enabled', 'disabled']),
Expand All @@ -21,29 +23,50 @@ export const Post = async (request: NextApiRequest, response: NextApiResponse) =
return;
}

const applicableApps = config.apps.filter((x) => x.integration?.type === 'pihole');
const applicableApps = config.apps.filter(
(x) => x.integration?.type && ['pihole', 'adGuardHome'].includes(x.integration?.type)
);

for (let i = 0; i < applicableApps.length; i += 1) {
const app = applicableApps[i];

const pihole = new PiHoleClient(
app.url,
findAppProperty(app, 'password')
);

switch (parseResult.data.status) {
case 'enabled':
await pihole.enable();
break;
case 'disabled':
await pihole.disable();
break;
if (app.integration?.type === 'pihole') {
await processPiHole(app, parseResult.data.status === 'disabled');
return;
}

await processAdGuard(app, parseResult.data.status === 'disabled');
}

response.status(200).json({});
};

const processAdGuard = async (app: ConfigAppType, enable: boolean) => {
const adGuard = new AdGuard(
app.url,
findAppProperty(app, 'username'),
findAppProperty(app, 'password')
);

if (enable) {
await adGuard.disable();
return;
}

await adGuard.enable();
};

const processPiHole = async (app: ConfigAppType, enable: boolean) => {
const pihole = new PiHoleClient(app.url, findAppProperty(app, 'password'));

if (enable) {
await pihole.enable();
return;
}

await pihole.disable();
};

export default async (request: NextApiRequest, response: NextApiResponse) => {
if (request.method === 'POST') {
return Post(request, response);
Expand Down
64 changes: 49 additions & 15 deletions src/pages/api/modules/dns-hole/summary.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
/* eslint-disable no-await-in-loop */
import Consola from 'consola';
import { getCookie } from 'cookies-next';
import { NextApiRequest, NextApiResponse } from 'next';
import { findAppProperty } from '../../../../tools/client/app-properties';
import { getConfig } from '../../../../tools/config/getConfig';
import { PiHoleClient } from '../../../../tools/server/sdk/pihole/piHole';
import { AdStatistics } from '../../../../widgets/dnshole/type';
import { AdGuard } from '../../../../tools/server/sdk/adGuard/adGuard';

export const Get = async (request: NextApiRequest, response: NextApiResponse) => {
const configName = getCookie('config-name', { req: request });
const config = getConfig(configName?.toString() ?? 'default');

const applicableApps = config.apps.filter((x) => x.integration?.type === 'pihole');
const applicableApps = config.apps.filter(
(x) => x.integration?.type && ['pihole', 'adGuardHome'].includes(x.integration?.type)
);

const data: AdStatistics = {
domainsBeingBlocked: 0,
Expand All @@ -26,21 +30,51 @@ export const Get = async (request: NextApiRequest, response: NextApiResponse) =>
const app = applicableApps[i];

try {
const piHole = new PiHoleClient(app.url, findAppProperty(app, 'password'));

// eslint-disable-next-line no-await-in-loop
const summary = await piHole.getSummary();

data.domainsBeingBlocked += summary.domains_being_blocked;
data.adsBlockedToday += summary.ads_blocked_today;
data.dnsQueriesToday += summary.dns_queries_today;
data.status.push({
status: summary.status,
appId: app.id,
});
adsBlockedTodayPercentageArr.push(summary.ads_percentage_today);
switch (app.integration?.type) {
case 'pihole': {
const piHole = new PiHoleClient(app.url, findAppProperty(app, 'password'));
const summary = await piHole.getSummary();

data.domainsBeingBlocked += summary.domains_being_blocked;
data.adsBlockedToday += summary.ads_blocked_today;
data.dnsQueriesToday += summary.dns_queries_today;
data.status.push({
status: summary.status,
appId: app.id,
});
adsBlockedTodayPercentageArr.push(summary.ads_percentage_today);
break;
}
case 'adGuardHome': {
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);
data.adsBlockedToday = blockedQueriesToday;
data.domainsBeingBlocked += countFilteredDomains;
data.dnsQueriesToday += queriesToday;
data.status.push({
status: status.protection_enabled ? 'enabled' : 'disabled',
appId: app.id,
});
adsBlockedTodayPercentageArr.push((queriesToday / blockedQueriesToday) * 100);
break;
}
default: {
Consola.error(`Integration communication for app ${app.id} failed: unknown type`);
break;
}
}
} catch (err) {
Consola.error(`Failed to communicate with PiHole at ${app.url}: ${err}`);
Consola.error(`Failed to communicate with DNS hole at ${app.url}: ${err}`);
}
}

Expand Down

0 comments on commit fb52c4b

Please sign in to comment.