mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 07:25:48 +01:00
✨ Add ad guard home (#937)
* ✨ Add add guard home * ✨ Add request for blocked domains and fix request for blocked queries * ♻️ PR feedback * ✅ Fix tests
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
import { ApiCheck, AssertionBuilder } from 'checkly/constructs';
|
||||
|
||||
const homepageApiCheck = new ApiCheck('homepage-api-check-1', {
|
||||
name: 'Fetch Book List',
|
||||
alertChannels: [],
|
||||
degradedResponseTime: 10000,
|
||||
maxResponseTime: 20000,
|
||||
request: {
|
||||
url: 'https://danube-web.shop/api/books',
|
||||
method: 'GET',
|
||||
followRedirects: true,
|
||||
skipSSL: false,
|
||||
assertions: [
|
||||
AssertionBuilder.statusCode().equals(200),
|
||||
AssertionBuilder.jsonBody('$[0].id').isNotNull(),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export default homepageApiCheck;
|
||||
@@ -1,11 +0,0 @@
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
// You can override the default Playwright test timeout of 30s
|
||||
// test.setTimeout(60_000);
|
||||
|
||||
test('Checkly Homepage', async ({ page }: { page: Page }) => {
|
||||
const response = await page.goto('https://danube-web.shop');
|
||||
expect(response?.status()).toBeLessThan(400);
|
||||
await expect(page).toHaveTitle(/Danube WebShop/);
|
||||
await page.screenshot({ path: 'homepage.jpg' });
|
||||
});
|
||||
@@ -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",
|
||||
@@ -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": {
|
||||
|
||||
@@ -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';
|
||||
@@ -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[] => {
|
||||
@@ -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"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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']),
|
||||
@@ -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);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import Consola from 'consola';
|
||||
import { getCookie } from 'cookies-next';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
@@ -5,12 +6,15 @@ 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,
|
||||
@@ -26,9 +30,9 @@ export const Get = async (request: NextApiRequest, response: NextApiResponse) =>
|
||||
const app = applicableApps[i];
|
||||
|
||||
try {
|
||||
switch (app.integration?.type) {
|
||||
case 'pihole': {
|
||||
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;
|
||||
@@ -39,8 +43,38 @@ export const Get = async (request: NextApiRequest, response: NextApiResponse) =>
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ describe('media-requests api', () => {
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('fetch and return requests in response', async () => {
|
||||
it('fetch and return requests in response with external url', async () => {
|
||||
// Arrange
|
||||
const { req, res } = createMocks({
|
||||
method: 'GET',
|
||||
@@ -108,7 +108,16 @@ describe('media-requests api', () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
} as ConfigType);
|
||||
widgets: [
|
||||
{
|
||||
id: 'hjeruijgrig',
|
||||
type: 'media-requests-list',
|
||||
properties: {
|
||||
replaceLinksWithExternalHost: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
} as unknown as ConfigType);
|
||||
const logSpy = vi.spyOn(Consola, 'error');
|
||||
|
||||
fetchMock.mockResponse((request) => {
|
||||
@@ -287,6 +296,235 @@ describe('media-requests api', () => {
|
||||
'https://image.tmdb.org/t/p/w600_and_h900_bestv2//hf4j0928gq543njgh8935nqh8.jpg',
|
||||
status: 2,
|
||||
type: 'movie',
|
||||
userLink: 'http://my-overseerr.external/users/1',
|
||||
userName: 'Example User',
|
||||
userProfilePicture: 'http://my-overseerr.external//os_logo_square.png',
|
||||
},
|
||||
]);
|
||||
|
||||
expect(logSpy).not.toHaveBeenCalled();
|
||||
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('fetch and return requests in response with internal url', async () => {
|
||||
// Arrange
|
||||
const { req, res } = createMocks({
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
vi.mock('./../../../../tools/config/getConfig.ts', () => ({
|
||||
get getConfig() {
|
||||
return mockedGetConfig;
|
||||
},
|
||||
}));
|
||||
mockedGetConfig.mockReturnValue({
|
||||
apps: [
|
||||
{
|
||||
url: 'http://my-overseerr.local',
|
||||
behaviour: {
|
||||
externalUrl: 'http://my-overseerr.external',
|
||||
},
|
||||
integration: {
|
||||
type: 'overseerr',
|
||||
properties: [
|
||||
{
|
||||
field: 'apiKey',
|
||||
type: 'private',
|
||||
value: 'abc',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
widgets: [
|
||||
{
|
||||
id: 'hjeruijgrig',
|
||||
type: 'media-requests-list',
|
||||
properties: {
|
||||
replaceLinksWithExternalHost: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
} as unknown as ConfigType);
|
||||
const logSpy = vi.spyOn(Consola, 'error');
|
||||
|
||||
fetchMock.mockResponse((request) => {
|
||||
if (request.url === 'http://my-overseerr.local/api/v1/request?take=25&skip=0&sort=added') {
|
||||
return JSON.stringify({
|
||||
pageInfo: { pages: 3, pageSize: 20, results: 42, page: 1 },
|
||||
results: [
|
||||
{
|
||||
id: 44,
|
||||
status: 2,
|
||||
createdAt: '2023-04-06T19:38:45.000Z',
|
||||
updatedAt: '2023-04-06T19:38:45.000Z',
|
||||
type: 'movie',
|
||||
is4k: false,
|
||||
serverId: 0,
|
||||
profileId: 4,
|
||||
tags: [],
|
||||
isAutoRequest: false,
|
||||
media: {
|
||||
downloadStatus: [],
|
||||
downloadStatus4k: [],
|
||||
id: 999,
|
||||
mediaType: 'movie',
|
||||
tmdbId: 99999999,
|
||||
tvdbId: null,
|
||||
imdbId: null,
|
||||
status: 5,
|
||||
status4k: 1,
|
||||
createdAt: '2023-02-06T19:38:45.000Z',
|
||||
updatedAt: '2023-02-06T20:00:04.000Z',
|
||||
lastSeasonChange: '2023-08-06T19:38:45.000Z',
|
||||
mediaAddedAt: '2023-05-14T06:30:34.000Z',
|
||||
serviceId: 0,
|
||||
serviceId4k: null,
|
||||
externalServiceId: 32,
|
||||
externalServiceId4k: null,
|
||||
externalServiceSlug: '000000000000',
|
||||
externalServiceSlug4k: null,
|
||||
ratingKey: null,
|
||||
ratingKey4k: null,
|
||||
jellyfinMediaId: '0000',
|
||||
jellyfinMediaId4k: null,
|
||||
mediaUrl:
|
||||
'http://your-jellyfin.local/web/index.html#!/details?id=mn8q2j4gq038g&context=home&serverId=jf83fj34gm340g',
|
||||
serviceUrl: 'http://your-jellyfin.local/movie/0000',
|
||||
},
|
||||
seasons: [],
|
||||
modifiedBy: {
|
||||
permissions: 2,
|
||||
warnings: [],
|
||||
id: 1,
|
||||
email: 'example-user@homarr.dev',
|
||||
plexUsername: null,
|
||||
jellyfinUsername: 'example-user',
|
||||
username: null,
|
||||
recoveryLinkExpirationDate: null,
|
||||
userType: 3,
|
||||
plexId: null,
|
||||
jellyfinUserId: '00000000000000000',
|
||||
jellyfinDeviceId: '111111111111111111',
|
||||
jellyfinAuthToken: '2222222222222222222',
|
||||
plexToken: null,
|
||||
avatar: '/os_logo_square.png',
|
||||
movieQuotaLimit: null,
|
||||
movieQuotaDays: null,
|
||||
tvQuotaLimit: null,
|
||||
tvQuotaDays: null,
|
||||
createdAt: '2022-07-03T19:53:08.000Z',
|
||||
updatedAt: '2022-07-03T19:53:08.000Z',
|
||||
requestCount: 34,
|
||||
displayName: 'Example User',
|
||||
},
|
||||
requestedBy: {
|
||||
permissions: 2,
|
||||
warnings: [],
|
||||
id: 1,
|
||||
email: 'example-user@homarr.dev',
|
||||
plexUsername: null,
|
||||
jellyfinUsername: 'example-user',
|
||||
username: null,
|
||||
recoveryLinkExpirationDate: null,
|
||||
userType: 3,
|
||||
plexId: null,
|
||||
jellyfinUserId: '00000000000000000',
|
||||
jellyfinDeviceId: '111111111111111111',
|
||||
jellyfinAuthToken: '2222222222222222222',
|
||||
plexToken: null,
|
||||
avatar: '/os_logo_square.png',
|
||||
movieQuotaLimit: null,
|
||||
movieQuotaDays: null,
|
||||
tvQuotaLimit: null,
|
||||
tvQuotaDays: null,
|
||||
createdAt: '2022-07-03T19:53:08.000Z',
|
||||
updatedAt: '2022-07-03T19:53:08.000Z',
|
||||
requestCount: 34,
|
||||
displayName: 'Example User',
|
||||
},
|
||||
seasonCount: 0,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (request.url === 'http://my-overseerr.local/api/v1/movie/99999999') {
|
||||
return JSON.stringify({
|
||||
id: 0,
|
||||
adult: false,
|
||||
budget: 0,
|
||||
genres: [
|
||||
{
|
||||
id: 18,
|
||||
name: 'Dashboards',
|
||||
},
|
||||
],
|
||||
relatedVideos: [],
|
||||
originalLanguage: 'jp',
|
||||
originalTitle: 'Homarrrr Movie',
|
||||
popularity: 9.352,
|
||||
productionCompanies: [],
|
||||
productionCountries: [],
|
||||
releaseDate: '2023-12-08',
|
||||
releases: {
|
||||
results: [],
|
||||
},
|
||||
revenue: 0,
|
||||
spokenLanguages: [
|
||||
{
|
||||
english_name: 'Japanese',
|
||||
iso_639_1: 'jp',
|
||||
name: '日本語',
|
||||
},
|
||||
],
|
||||
status: 'Released',
|
||||
title: 'Homarr Movie',
|
||||
video: false,
|
||||
voteAverage: 9.999,
|
||||
voteCount: 0,
|
||||
backdropPath: '/mhjq8jr0qgrjnghnh.jpg',
|
||||
homepage: '',
|
||||
imdbId: 'tt0000000',
|
||||
overview: 'A very cool movie',
|
||||
posterPath: '/hf4j0928gq543njgh8935nqh8.jpg',
|
||||
runtime: 97,
|
||||
tagline: '',
|
||||
credits: {},
|
||||
collection: null,
|
||||
externalIds: {
|
||||
facebookId: null,
|
||||
imdbId: null,
|
||||
instagramId: null,
|
||||
twitterId: null,
|
||||
},
|
||||
watchProviders: [],
|
||||
keywords: [],
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(`Bad url: ${request.url}`));
|
||||
});
|
||||
|
||||
// Act
|
||||
await MediaRequestsRoute(req, res);
|
||||
|
||||
// Assert
|
||||
expect(res._getStatusCode()).toBe(200);
|
||||
expect(res.finished).toBe(true);
|
||||
expect(JSON.parse(res._getData())).toEqual([
|
||||
{
|
||||
airDate: '2023-12-08',
|
||||
backdropPath: 'https://image.tmdb.org/t/p/original//mhjq8jr0qgrjnghnh.jpg',
|
||||
createdAt: '2023-04-06T19:38:45.000Z',
|
||||
href: 'http://my-overseerr.local/movie/99999999',
|
||||
id: 44,
|
||||
name: 'Homarrrr Movie',
|
||||
posterPath:
|
||||
'https://image.tmdb.org/t/p/w600_and_h900_bestv2//hf4j0928gq543njgh8935nqh8.jpg',
|
||||
status: 2,
|
||||
type: 'movie',
|
||||
userLink: 'http://my-overseerr.local/users/1',
|
||||
userName: 'Example User',
|
||||
userProfilePicture: 'http://my-overseerr.local//os_logo_square.png',
|
||||
|
||||
@@ -51,7 +51,7 @@ const Get = async (request: NextApiRequest, response: NextApiResponse) => {
|
||||
type: item.type,
|
||||
name: genericItem.name,
|
||||
userName: item.requestedBy.displayName,
|
||||
userProfilePicture: constructAvatarUrl(app, item),
|
||||
userProfilePicture: constructAvatarUrl(appUrl, item),
|
||||
userLink: `${appUrl}/users/${item.requestedBy.id}`,
|
||||
airDate: genericItem.airDate,
|
||||
status: item.status,
|
||||
@@ -75,7 +75,7 @@ const Get = async (request: NextApiRequest, response: NextApiResponse) => {
|
||||
return response.status(200).json(mediaRequests);
|
||||
};
|
||||
|
||||
const constructAvatarUrl = (app: ConfigAppType, item: OverseerrResponseItem) => {
|
||||
const constructAvatarUrl = (appUrl: string, item: OverseerrResponseItem) => {
|
||||
const isAbsolute =
|
||||
item.requestedBy.avatar.startsWith('http://') || item.requestedBy.avatar.startsWith('https://');
|
||||
|
||||
@@ -83,7 +83,7 @@ const constructAvatarUrl = (app: ConfigAppType, item: OverseerrResponseItem) =>
|
||||
return item.requestedBy.avatar;
|
||||
}
|
||||
|
||||
return `${app.url}/${item.requestedBy.avatar}`;
|
||||
return `${appUrl}/${item.requestedBy.avatar}`;
|
||||
};
|
||||
|
||||
const retrieveDetailsForItem = async (
|
||||
|
||||
41
src/tools/server/sdk/adGuard/adGuard.schema.ts
Normal file
41
src/tools/server/sdk/adGuard/adGuard.schema.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const adGuardApiStatsResponseSchema = z.object({
|
||||
time_units: z.enum(['hours']),
|
||||
top_queried_domains: z.array(z.record(z.string(), z.number())),
|
||||
top_clients: z.array(z.record(z.string(), z.number())),
|
||||
top_blocked_domains: z.array(z.record(z.string(), z.number())),
|
||||
dns_queries: z.array(z.number()),
|
||||
blocked_filtering: z.array(z.number()),
|
||||
replaced_safebrowsing: z.array(z.number()),
|
||||
replaced_parental: z.array(z.number()),
|
||||
num_dns_queries: z.number().min(0),
|
||||
num_blocked_filtering: z.number().min(0),
|
||||
num_replaced_safebrowsing: z.number().min(0),
|
||||
num_replaced_safesearch: z.number().min(0),
|
||||
num_replaced_parental: z.number().min(0),
|
||||
avg_processing_time: z.number().min(0).max(1),
|
||||
});
|
||||
|
||||
export const adGuardApiStatusResponseSchema = z.object({
|
||||
version: z.string(),
|
||||
language: z.string(),
|
||||
dns_addresses: z.array(z.string()),
|
||||
dns_port: z.number().positive(),
|
||||
http_port: z.number().positive(),
|
||||
protection_disabled_duration: z.number(),
|
||||
protection_enabled: z.boolean(),
|
||||
dhcp_available: z.boolean(),
|
||||
running: z.boolean(),
|
||||
});
|
||||
|
||||
export const adGuardApiFilteringStatusSchema = z.object({
|
||||
filters: z.array(z.object({
|
||||
url: z.string().url(),
|
||||
name: z.string(),
|
||||
last_updated: z.string().optional(),
|
||||
id: z.number().nonnegative(),
|
||||
rules_count: z.number().nonnegative(),
|
||||
enabled: z.boolean(),
|
||||
})),
|
||||
});
|
||||
95
src/tools/server/sdk/adGuard/adGuard.ts
Normal file
95
src/tools/server/sdk/adGuard/adGuard.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { trimStringEnding } from '../../../shared/strings';
|
||||
import {
|
||||
adGuardApiFilteringStatusSchema,
|
||||
adGuardApiStatsResponseSchema,
|
||||
adGuardApiStatusResponseSchema,
|
||||
} from './adGuard.schema';
|
||||
|
||||
export class AdGuard {
|
||||
private readonly baseHostName: string;
|
||||
|
||||
constructor(
|
||||
hostname: string,
|
||||
private readonly username: string,
|
||||
private readonly password: string
|
||||
) {
|
||||
this.baseHostName = trimStringEnding(hostname, ['/#', '/']);
|
||||
}
|
||||
|
||||
async getStats(): Promise<AdGuardStatsType> {
|
||||
const response = await fetch(`${this.baseHostName}/control/stats`, {
|
||||
headers: {
|
||||
Authorization: `Basic ${this.getAuthorizationHeaderValue()}`,
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return adGuardApiStatsResponseSchema.parseAsync(data);
|
||||
}
|
||||
|
||||
async getStatus() {
|
||||
const response = await fetch(`${this.baseHostName}/control/status`, {
|
||||
headers: {
|
||||
Authorization: `Basic ${this.getAuthorizationHeaderValue()}`,
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return adGuardApiStatusResponseSchema.parseAsync(data);
|
||||
}
|
||||
|
||||
async getCountFilteringDomains() {
|
||||
const response = await fetch(`${this.baseHostName}/control/filtering/status`, {
|
||||
headers: {
|
||||
Authorization: `Basic ${this.getAuthorizationHeaderValue()}`,
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const schemaData = await adGuardApiFilteringStatusSchema.parseAsync(data);
|
||||
|
||||
return schemaData.filters
|
||||
.filter((filter) => filter.enabled)
|
||||
.reduce((sum, filter) => filter.rules_count + sum, 0);
|
||||
}
|
||||
|
||||
async disable() {
|
||||
await this.changeProtectionStatus(false);
|
||||
}
|
||||
async enable() {
|
||||
await this.changeProtectionStatus(false);
|
||||
}
|
||||
|
||||
private async changeProtectionStatus(newStatus: boolean, duration = 0) {
|
||||
await fetch(`${this.baseHostName}/control/protection`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
enabled: newStatus,
|
||||
duration,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
private getAuthorizationHeaderValue() {
|
||||
return Buffer.from(`${this.username}:${this.password}`).toString('base64');
|
||||
}
|
||||
}
|
||||
|
||||
export type AdGuardStatsType = {
|
||||
time_units: string;
|
||||
top_queried_domains: { [key: string]: number }[];
|
||||
top_clients: { [key: string]: number }[];
|
||||
top_blocked_domains: { [key: string]: number }[];
|
||||
dns_queries: number[];
|
||||
blocked_filtering: number[];
|
||||
replaced_safebrowsing: number[];
|
||||
replaced_parental: number[];
|
||||
num_dns_queries: number;
|
||||
num_blocked_filtering: number;
|
||||
num_replaced_safebrowsing: number;
|
||||
num_replaced_safesearch: number;
|
||||
num_replaced_parental: number;
|
||||
avg_processing_time: number;
|
||||
};
|
||||
@@ -1,10 +1,11 @@
|
||||
import { trimStringEnding } from '../../../shared/strings';
|
||||
import { PiHoleApiStatusChangeResponse, PiHoleApiSummaryResponse } from './piHole.type';
|
||||
|
||||
export class PiHoleClient {
|
||||
private readonly baseHostName: string;
|
||||
|
||||
constructor(hostname: string, private readonly apiToken: string) {
|
||||
this.baseHostName = this.trimStringEnding(hostname, ['/admin/index.php', '/admin', '/']);
|
||||
this.baseHostName = trimStringEnding(hostname, ['/admin/index.php', '/admin', '/']);
|
||||
}
|
||||
|
||||
async getSummary() {
|
||||
@@ -60,15 +61,4 @@ export class PiHoleClient {
|
||||
|
||||
return json as PiHoleApiStatusChangeResponse;
|
||||
}
|
||||
|
||||
private trimStringEnding(original: string, toTrimIfExists: string[]) {
|
||||
for (let i = 0; i < toTrimIfExists.length; i += 1) {
|
||||
if (!original.endsWith(toTrimIfExists[i])) {
|
||||
continue;
|
||||
}
|
||||
return original.substring(0, original.indexOf(toTrimIfExists[i]));
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
}
|
||||
|
||||
10
src/tools/shared/strings.ts
Normal file
10
src/tools/shared/strings.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export const trimStringEnding = (original: string, toTrimIfExists: string[]) => {
|
||||
for (let i = 0; i < toTrimIfExists.length; i += 1) {
|
||||
if (!original.endsWith(toTrimIfExists[i])) {
|
||||
continue;
|
||||
}
|
||||
return original.substring(0, original.indexOf(toTrimIfExists[i]));
|
||||
}
|
||||
|
||||
return original;
|
||||
};
|
||||
@@ -45,7 +45,8 @@ export type IntegrationType =
|
||||
| 'plex'
|
||||
| 'jellyfin'
|
||||
| 'nzbGet'
|
||||
| 'pihole';
|
||||
| 'pihole'
|
||||
| 'adGuardHome';
|
||||
|
||||
export type AppIntegrationType = {
|
||||
type: IntegrationType | null;
|
||||
@@ -86,6 +87,7 @@ export const integrationFieldProperties: {
|
||||
jellyfin: ['username', 'password'],
|
||||
plex: ['apiKey'],
|
||||
pihole: ['password'],
|
||||
adGuardHome: ['username', 'password'],
|
||||
};
|
||||
|
||||
export type IntegrationFieldDefinitionType = {
|
||||
|
||||
@@ -136,7 +136,7 @@ function DnsHoleSummaryWidgetTile({ widget }: DnsHoleSummaryWidgetProps) {
|
||||
<Stack align="center" spacing="xs">
|
||||
<IconSearch size={30} />
|
||||
<div>
|
||||
<Text align="center">{formatNumber(data.dnsQueriesToday, 0)}</Text>
|
||||
<Text align="center">{formatNumber(data.dnsQueriesToday, 3)}</Text>
|
||||
<Text align="center" lh={1.2} size="sm">
|
||||
{t('card.metrics.queriesToday')}
|
||||
</Text>
|
||||
|
||||
328
yarn.lock
328
yarn.lock
@@ -1561,22 +1561,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@playwright/test@npm:^1.33.0":
|
||||
version: 1.33.0
|
||||
resolution: "@playwright/test@npm:1.33.0"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
fsevents: 2.3.2
|
||||
playwright-core: 1.33.0
|
||||
dependenciesMeta:
|
||||
fsevents:
|
||||
optional: true
|
||||
bin:
|
||||
playwright: cli.js
|
||||
checksum: cec3215fc92c1cb9f5bfba357ea1cbe97b54979ab82f9d34a2287b1687cda5e0966b8ea7290dcd35416e18668e56d5781b6b8c4cec64baf12f3ae8dde0f68f5e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polka/url@npm:^1.0.0-next.20":
|
||||
version: 1.0.0-next.21
|
||||
resolution: "@polka/url@npm:1.0.0-next.21"
|
||||
@@ -2060,7 +2044,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/chai@npm:*, @types/chai@npm:^4.3.4":
|
||||
"@types/chai@npm:*, @types/chai@npm:^4.3.5":
|
||||
version: 4.3.5
|
||||
resolution: "@types/chai@npm:4.3.5"
|
||||
checksum: c8f26a88c6b5b53a3275c7f5ff8f107028e3cbb9ff26795fff5f3d9dea07106a54ce9e2dce5e40347f7c4cc35657900aaf0c83934a25a1ae12e61e0f5516e431
|
||||
@@ -2560,34 +2544,46 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/expect@npm:0.29.8":
|
||||
version: 0.29.8
|
||||
resolution: "@vitest/expect@npm:0.29.8"
|
||||
"@vitest/expect@npm:0.31.1":
|
||||
version: 0.31.1
|
||||
resolution: "@vitest/expect@npm:0.31.1"
|
||||
dependencies:
|
||||
"@vitest/spy": 0.29.8
|
||||
"@vitest/utils": 0.29.8
|
||||
"@vitest/spy": 0.31.1
|
||||
"@vitest/utils": 0.31.1
|
||||
chai: ^4.3.7
|
||||
checksum: a80f9c352a979eb46690be2ea54b5ca391d3575b4053be80c1359325fb0cea913d6217f48d54e64ff5dda3b15bd7a6873a5f8128e8c098f7ebad1365d4065c5e
|
||||
checksum: 0d1e135ae753d913231eae830da00ee42afca53d354898fb43f97e82398dcf17298c02e9989dd6b19b9b2909989248ef76d203d63f6af6f9159dc96959ea654b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/runner@npm:0.29.8":
|
||||
version: 0.29.8
|
||||
resolution: "@vitest/runner@npm:0.29.8"
|
||||
"@vitest/runner@npm:0.31.1":
|
||||
version: 0.31.1
|
||||
resolution: "@vitest/runner@npm:0.31.1"
|
||||
dependencies:
|
||||
"@vitest/utils": 0.29.8
|
||||
"@vitest/utils": 0.31.1
|
||||
concordance: ^5.0.4
|
||||
p-limit: ^4.0.0
|
||||
pathe: ^1.1.0
|
||||
checksum: 8305370ff6c3fc6aea7189bd138ee4ff0e040a959c0fe6ab64bcb9e70ae5bf836b8dc058b1de288aa75c9d1cd648e5f112e7cd5691c03b7a1d32466d8bfc71a9
|
||||
checksum: cc8702e21b799d5e941409cb2afe6d0e576b4f3ac99df4a1393a8cd11b57f6b0b06e756cc24e2739812d095fbfd0824e22e861dbd6a71769ca387d485ade6fb5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/spy@npm:0.29.8":
|
||||
version: 0.29.8
|
||||
resolution: "@vitest/spy@npm:0.29.8"
|
||||
"@vitest/snapshot@npm:0.31.1":
|
||||
version: 0.31.1
|
||||
resolution: "@vitest/snapshot@npm:0.31.1"
|
||||
dependencies:
|
||||
tinyspy: ^1.0.2
|
||||
checksum: 7b1607b696275bf94a497e92d7d10c466b9b3d08726bbedb3735bdf57f003763a9516e328af22746829526ce573f87eb6119ab64ce7db95794b2d220aa53b607
|
||||
magic-string: ^0.30.0
|
||||
pathe: ^1.1.0
|
||||
pretty-format: ^27.5.1
|
||||
checksum: de05fa9136864f26f0804baf3ae8068f67de28015f29047329c84e67fb33be7305c9e52661b016e834d30f4081c136b3b6d8d4054c024a5d52b22a7f90fc4be0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/spy@npm:0.31.1":
|
||||
version: 0.31.1
|
||||
resolution: "@vitest/spy@npm:0.31.1"
|
||||
dependencies:
|
||||
tinyspy: ^2.1.0
|
||||
checksum: 8b06cf25fcc028c16106ec82f4ceb84d6dfa04d06f651bca4738ce2b99796d1fc4e0c10319767240755eff8ede2bff9d31d5a901fe92828d319c65001581137b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2604,15 +2600,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/utils@npm:0.29.8":
|
||||
version: 0.29.8
|
||||
resolution: "@vitest/utils@npm:0.29.8"
|
||||
"@vitest/utils@npm:0.31.1":
|
||||
version: 0.31.1
|
||||
resolution: "@vitest/utils@npm:0.31.1"
|
||||
dependencies:
|
||||
cli-truncate: ^3.1.0
|
||||
diff: ^5.1.0
|
||||
concordance: ^5.0.4
|
||||
loupe: ^2.3.6
|
||||
pretty-format: ^27.5.1
|
||||
checksum: fa18cccb6ab5295e43a1a43b9c022f070646a893adb0561c50b3e0c39f05ea74cbf379aef22ef485ea9acbf2bb8f0a224d457fd4f16b9e1bf509c13052c7f08b
|
||||
checksum: 58016c185455e3814632cb77e37368c846bde5e342f8b4a66fa229bde64f455ca39abebc9c12e2483696ee38bc17b3c4300379f7a3b18d1087f24f474448a8d8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2665,7 +2660,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"acorn@npm:^8.0.4, acorn@npm:^8.4.1, acorn@npm:^8.8.0, acorn@npm:^8.8.1, acorn@npm:^8.8.2":
|
||||
"acorn@npm:^8.0.4, acorn@npm:^8.4.1, acorn@npm:^8.8.0, acorn@npm:^8.8.2":
|
||||
version: 8.8.2
|
||||
resolution: "acorn@npm:8.8.2"
|
||||
bin:
|
||||
@@ -2777,13 +2772,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ansi-regex@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "ansi-regex@npm:6.0.1"
|
||||
checksum: 1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ansi-styles@npm:^3.2.1":
|
||||
version: 3.2.1
|
||||
resolution: "ansi-styles@npm:3.2.1"
|
||||
@@ -2809,13 +2797,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ansi-styles@npm:^6.0.0":
|
||||
version: 6.2.1
|
||||
resolution: "ansi-styles@npm:6.2.1"
|
||||
checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ansicolors@npm:~0.3.2":
|
||||
version: 0.3.2
|
||||
resolution: "ansicolors@npm:0.3.2"
|
||||
@@ -3132,6 +3113,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"blueimp-md5@npm:^2.10.0":
|
||||
version: 2.19.0
|
||||
resolution: "blueimp-md5@npm:2.19.0"
|
||||
checksum: 28095dcbd2c67152a2938006e8d7c74c3406ba6556071298f872505432feb2c13241b0476644160ee0a5220383ba94cb8ccdac0053b51f68d168728f9c382530
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"brace-expansion@npm:^1.1.7":
|
||||
version: 1.1.11
|
||||
resolution: "brace-expansion@npm:1.1.11"
|
||||
@@ -3532,16 +3520,6 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cli-truncate@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "cli-truncate@npm:3.1.0"
|
||||
dependencies:
|
||||
slice-ansi: ^5.0.0
|
||||
string-width: ^5.0.0
|
||||
checksum: c3243e41974445691c63f8b405df1d5a24049dc33d324fe448dc572e561a7b772ae982692900b1a5960901cc4fc7def25a629b9c69a4208ee89d12ab3332617a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cli-width@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "cli-width@npm:3.0.0"
|
||||
@@ -3683,6 +3661,22 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"concordance@npm:^5.0.4":
|
||||
version: 5.0.4
|
||||
resolution: "concordance@npm:5.0.4"
|
||||
dependencies:
|
||||
date-time: ^3.1.0
|
||||
esutils: ^2.0.3
|
||||
fast-diff: ^1.2.0
|
||||
js-string-escape: ^1.0.1
|
||||
lodash: ^4.17.15
|
||||
md5-hex: ^3.0.1
|
||||
semver: ^7.3.2
|
||||
well-known-symbols: ^2.0.0
|
||||
checksum: 749153ba711492feb7c3d2f5bb04c107157440b3e39509bd5dd19ee7b3ac751d1e4cd75796d9f702e0a713312dbc661421c68aa4a2c34d5f6d91f47e3a1c64a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"conf@npm:10.2.0":
|
||||
version: 10.2.0
|
||||
resolution: "conf@npm:10.2.0"
|
||||
@@ -3997,6 +3991,15 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"date-time@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "date-time@npm:3.1.0"
|
||||
dependencies:
|
||||
time-zone: ^1.0.0
|
||||
checksum: f9cfcd1b15dfeabab15c0b9d18eb9e4e2d9d4371713564178d46a8f91ad577a290b5178b80050718d02d9c0cf646f8a875011e12d1ed05871e9f72c72c8a8fe6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dayjs@npm:^1.11.7":
|
||||
version: 1.11.7
|
||||
resolution: "dayjs@npm:1.11.7"
|
||||
@@ -4174,13 +4177,6 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"diff@npm:^5.1.0":
|
||||
version: 5.1.0
|
||||
resolution: "diff@npm:5.1.0"
|
||||
checksum: c7bf0df7c9bfbe1cf8a678fd1b2137c4fb11be117a67bc18a0e03ae75105e8533dbfb1cda6b46beb3586ef5aed22143ef9d70713977d5fb1f9114e21455fba90
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dir-glob@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "dir-glob@npm:3.0.1"
|
||||
@@ -4337,13 +4333,6 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eastasianwidth@npm:^0.2.0":
|
||||
version: 0.2.0
|
||||
resolution: "eastasianwidth@npm:0.2.0"
|
||||
checksum: 7d00d7cd8e49b9afa762a813faac332dee781932d6f2c848dc348939c4253f1d4564341b7af1d041853bc3f32c2ef141b58e0a4d9862c17a7f08f68df1e0f1ed
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ejs@npm:^3.1.6, ejs@npm:^3.1.8":
|
||||
version: 3.1.9
|
||||
resolution: "ejs@npm:3.1.9"
|
||||
@@ -4971,7 +4960,7 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"esutils@npm:^2.0.2":
|
||||
"esutils@npm:^2.0.2, esutils@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "esutils@npm:2.0.3"
|
||||
checksum: 22b5b08f74737379a840b8ed2036a5fb35826c709ab000683b092d9054e5c2a82c27818f12604bfc2a9a76b90b6834ef081edbc1c7ae30d1627012e067c6ec87
|
||||
@@ -5016,6 +5005,13 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-diff@npm:^1.2.0":
|
||||
version: 1.3.0
|
||||
resolution: "fast-diff@npm:1.3.0"
|
||||
checksum: d22d371b994fdc8cce9ff510d7b8dc4da70ac327bcba20df607dd5b9cae9f908f4d1028f5fe467650f058d1e7270235ae0b8230809a262b4df587a3b3aa216c3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.9":
|
||||
version: 3.2.12
|
||||
resolution: "fast-glob@npm:3.2.12"
|
||||
@@ -5278,7 +5274,7 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fsevents@npm:2.3.2, fsevents@npm:~2.3.2":
|
||||
"fsevents@npm:~2.3.2":
|
||||
version: 2.3.2
|
||||
resolution: "fsevents@npm:2.3.2"
|
||||
dependencies:
|
||||
@@ -5288,7 +5284,7 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fsevents@patch:fsevents@2.3.2#~builtin<compat/fsevents>, fsevents@patch:fsevents@~2.3.2#~builtin<compat/fsevents>":
|
||||
"fsevents@patch:fsevents@~2.3.2#~builtin<compat/fsevents>":
|
||||
version: 2.3.2
|
||||
resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin<compat/fsevents>::version=2.3.2&hash=df0bf1"
|
||||
dependencies:
|
||||
@@ -5745,7 +5741,6 @@ checkly@latest:
|
||||
"@next/eslint-plugin-next": ^13.0.0
|
||||
"@nivo/core": ^0.80.0
|
||||
"@nivo/line": ^0.80.0
|
||||
"@playwright/test": ^1.33.0
|
||||
"@react-native-async-storage/async-storage": ^1.18.1
|
||||
"@tabler/icons-react": ^2.18.0
|
||||
"@tanstack/query-async-storage-persister": ^4.27.1
|
||||
@@ -5806,7 +5801,7 @@ checkly@latest:
|
||||
typescript: ^5.0.4
|
||||
uuid: ^8.3.2
|
||||
video.js: ^8.0.3
|
||||
vitest: ^0.29.3
|
||||
vitest: ^0.31.1
|
||||
vitest-fetch-mock: ^0.2.2
|
||||
xml-js: ^1.6.11
|
||||
yarn: ^1.22.19
|
||||
@@ -6245,13 +6240,6 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-fullwidth-code-point@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "is-fullwidth-code-point@npm:4.0.0"
|
||||
checksum: 8ae89bf5057bdf4f57b346fb6c55e9c3dd2549983d54191d722d5c739397a903012cc41a04ee3403fd872e811243ef91a7c5196da7b5841dc6b6aae31a264a8d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-function@npm:^1.0.1":
|
||||
version: 1.0.2
|
||||
resolution: "is-function@npm:1.0.2"
|
||||
@@ -6606,6 +6594,13 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"js-string-escape@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "js-string-escape@npm:1.0.1"
|
||||
checksum: f11e0991bf57e0c183b55c547acec85bd2445f043efc9ea5aa68b41bd2a3e7d3ce94636cb233ae0d84064ba4c1a505d32e969813c5b13f81e7d4be12c59256fe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "js-tokens@npm:4.0.0"
|
||||
@@ -6827,7 +6822,7 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"local-pkg@npm:^0.4.2":
|
||||
"local-pkg@npm:^0.4.3":
|
||||
version: 0.4.3
|
||||
resolution: "local-pkg@npm:0.4.3"
|
||||
checksum: 7825aca531dd6afa3a3712a0208697aa4a5cd009065f32e3fb732aafcc42ed11f277b5ac67229222e96f4def55197171cdf3d5522d0381b489d2e5547b407d55
|
||||
@@ -6972,6 +6967,15 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"magic-string@npm:^0.30.0":
|
||||
version: 0.30.0
|
||||
resolution: "magic-string@npm:0.30.0"
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec": ^1.4.13
|
||||
checksum: 7bdf22e27334d8a393858a16f5f840af63a7c05848c000fd714da5aa5eefa09a1bc01d8469362f25cc5c4a14ec01b46557b7fff8751365522acddf21e57c488d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"make-dir@npm:^3.0.0":
|
||||
version: 3.1.0
|
||||
resolution: "make-dir@npm:3.1.0"
|
||||
@@ -7012,6 +7016,15 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"md5-hex@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "md5-hex@npm:3.0.1"
|
||||
dependencies:
|
||||
blueimp-md5: ^2.10.0
|
||||
checksum: 6799a19e8bdd3e0c2861b94c1d4d858a89220488d7885c1fa236797e367d0c2e5f2b789e05309307083503f85be3603a9686a5915568a473137d6b4117419cc2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"media-typer@npm:0.3.0":
|
||||
version: 0.3.0
|
||||
resolution: "media-typer@npm:0.3.0"
|
||||
@@ -7253,7 +7266,7 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mlly@npm:^1.1.0, mlly@npm:^1.2.0":
|
||||
"mlly@npm:^1.2.0":
|
||||
version: 1.2.1
|
||||
resolution: "mlly@npm:1.2.1"
|
||||
dependencies:
|
||||
@@ -8088,15 +8101,6 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"playwright-core@npm:1.33.0":
|
||||
version: 1.33.0
|
||||
resolution: "playwright-core@npm:1.33.0"
|
||||
bin:
|
||||
playwright: cli.js
|
||||
checksum: 5fb7bda06a8b73b56b85b5a0b8f711211dde57a375d9379289e22239b2de879c6d93c8fdc9ba44b932bf100914ab1ca1a55697ad88440fdd0a39101fc020b77f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:8.4.14":
|
||||
version: 8.4.14
|
||||
resolution: "postcss@npm:8.4.14"
|
||||
@@ -8857,7 +8861,7 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8":
|
||||
"semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8":
|
||||
version: 7.5.1
|
||||
resolution: "semver@npm:7.5.1"
|
||||
dependencies:
|
||||
@@ -8961,16 +8965,6 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"slice-ansi@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "slice-ansi@npm:5.0.0"
|
||||
dependencies:
|
||||
ansi-styles: ^6.0.0
|
||||
is-fullwidth-code-point: ^4.0.0
|
||||
checksum: 7e600a2a55e333a21ef5214b987c8358fe28bfb03c2867ff2cbf919d62143d1812ac27b4297a077fdaf27a03da3678e49551c93e35f9498a3d90221908a1180e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"smart-buffer@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "smart-buffer@npm:4.2.0"
|
||||
@@ -9013,13 +9007,6 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"source-map@npm:^0.6.1":
|
||||
version: 0.6.1
|
||||
resolution: "source-map@npm:0.6.1"
|
||||
checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"split-ca@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "split-ca@npm:1.0.1"
|
||||
@@ -9085,7 +9072,7 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"std-env@npm:^3.3.1":
|
||||
"std-env@npm:^3.3.1, std-env@npm:^3.3.2":
|
||||
version: 3.3.3
|
||||
resolution: "std-env@npm:3.3.3"
|
||||
checksum: 6665f6d8bd63aae432d3eb9abbd7322847ad0d902603e6dce1e8051b4f42ceeb4f7f96a4faf70bb05ce65ceee2dc982502b701575c8a58b1bfad29f3dbb19f81
|
||||
@@ -9126,17 +9113,6 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"string-width@npm:^5.0.0":
|
||||
version: 5.1.2
|
||||
resolution: "string-width@npm:5.1.2"
|
||||
dependencies:
|
||||
eastasianwidth: ^0.2.0
|
||||
emoji-regex: ^9.2.2
|
||||
strip-ansi: ^7.0.1
|
||||
checksum: 7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"string.prototype.matchall@npm:^4.0.8":
|
||||
version: 4.0.8
|
||||
resolution: "string.prototype.matchall@npm:4.0.8"
|
||||
@@ -9220,15 +9196,6 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strip-ansi@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "strip-ansi@npm:7.0.1"
|
||||
dependencies:
|
||||
ansi-regex: ^6.0.1
|
||||
checksum: 257f78fa433520e7f9897722731d78599cb3fce29ff26a20a5e12ba4957463b50a01136f37c43707f4951817a75e90820174853d6ccc240997adc5df8f966039
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strip-bom@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "strip-bom@npm:3.0.0"
|
||||
@@ -9252,7 +9219,7 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strip-literal@npm:^1.0.0":
|
||||
"strip-literal@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "strip-literal@npm:1.0.1"
|
||||
dependencies:
|
||||
@@ -9436,24 +9403,31 @@ checkly@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinybench@npm:^2.3.1":
|
||||
"time-zone@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "time-zone@npm:1.0.0"
|
||||
checksum: e46f5a69b8c236dcd8e91e29d40d4e7a3495ed4f59888c3f84ce1d9678e20461421a6ba41233509d47dd94bc18f1a4377764838b21b584663f942b3426dcbce8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinybench@npm:^2.5.0":
|
||||
version: 2.5.0
|
||||
resolution: "tinybench@npm:2.5.0"
|
||||
checksum: 284bb9428f197ec8b869c543181315e65e41ccfdad3c4b6c916bb1fdae1b5c6785661b0d90cf135b48d833b03cb84dc5357b2d33ec65a1f5971fae0ab2023821
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinypool@npm:^0.4.0":
|
||||
version: 0.4.0
|
||||
resolution: "tinypool@npm:0.4.0"
|
||||
checksum: 8abcac9e784793499f1eeeace8290c026454b9d7338c74029ce6a821643bab8dcab7caeb4051e39006baf681d6a62d57c3319e9c0f6e2317a45ab0fdbd76ee26
|
||||
"tinypool@npm:^0.5.0":
|
||||
version: 0.5.0
|
||||
resolution: "tinypool@npm:0.5.0"
|
||||
checksum: 4e0dfd8f28666d541c1d92304222edc4613f05d74fe2243c8520d466a2cc6596011a7072c1c41a7de7522351b82fda07e8038198e8f43673d8d69401c5903f8c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinyspy@npm:^1.0.2":
|
||||
version: 1.1.1
|
||||
resolution: "tinyspy@npm:1.1.1"
|
||||
checksum: 4ea908fdfddb92044c4454193ec543f5980ced0bd25c5b3d240a94c1511e47e765ad39cd13ae6d3370fb730f62038eedc357f55e4e239416e126bc418f0eee79
|
||||
"tinyspy@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "tinyspy@npm:2.1.0"
|
||||
checksum: cb83c1f74a79dd5934018bad94f60a304a29d98a2d909ea45fc367f7b80b21b0a7d8135a2ce588deb2b3ba56c7c607258b2a03e6001d89e4d564f9a95cc6a81f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -10020,19 +9994,19 @@ turbo@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vite-node@npm:0.29.8":
|
||||
version: 0.29.8
|
||||
resolution: "vite-node@npm:0.29.8"
|
||||
"vite-node@npm:0.31.1":
|
||||
version: 0.31.1
|
||||
resolution: "vite-node@npm:0.31.1"
|
||||
dependencies:
|
||||
cac: ^6.7.14
|
||||
debug: ^4.3.4
|
||||
mlly: ^1.1.0
|
||||
mlly: ^1.2.0
|
||||
pathe: ^1.1.0
|
||||
picocolors: ^1.0.0
|
||||
vite: ^3.0.0 || ^4.0.0
|
||||
bin:
|
||||
vite-node: vite-node.mjs
|
||||
checksum: b0981d4d63b1f373579eb9da69ca5af9123bf27c81ac246c541cdecf879ef4ef542e0b521cb6ceaafd5ead2cc3d243105d1fb8bf076953d42a6b2203607ce928
|
||||
checksum: f70ffa3f6dcb4937cdc99f59bf360d42de83c556ba9a19eb1c3504ef20db4c1d1afa644d9a8e63240e851c0c95773b64c526bdb3eb4794b5e941ddcd57124aa9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -10084,33 +10058,34 @@ turbo@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vitest@npm:^0.29.3":
|
||||
version: 0.29.8
|
||||
resolution: "vitest@npm:0.29.8"
|
||||
"vitest@npm:^0.31.1":
|
||||
version: 0.31.1
|
||||
resolution: "vitest@npm:0.31.1"
|
||||
dependencies:
|
||||
"@types/chai": ^4.3.4
|
||||
"@types/chai": ^4.3.5
|
||||
"@types/chai-subset": ^1.3.3
|
||||
"@types/node": "*"
|
||||
"@vitest/expect": 0.29.8
|
||||
"@vitest/runner": 0.29.8
|
||||
"@vitest/spy": 0.29.8
|
||||
"@vitest/utils": 0.29.8
|
||||
acorn: ^8.8.1
|
||||
"@vitest/expect": 0.31.1
|
||||
"@vitest/runner": 0.31.1
|
||||
"@vitest/snapshot": 0.31.1
|
||||
"@vitest/spy": 0.31.1
|
||||
"@vitest/utils": 0.31.1
|
||||
acorn: ^8.8.2
|
||||
acorn-walk: ^8.2.0
|
||||
cac: ^6.7.14
|
||||
chai: ^4.3.7
|
||||
concordance: ^5.0.4
|
||||
debug: ^4.3.4
|
||||
local-pkg: ^0.4.2
|
||||
local-pkg: ^0.4.3
|
||||
magic-string: ^0.30.0
|
||||
pathe: ^1.1.0
|
||||
picocolors: ^1.0.0
|
||||
source-map: ^0.6.1
|
||||
std-env: ^3.3.1
|
||||
strip-literal: ^1.0.0
|
||||
tinybench: ^2.3.1
|
||||
tinypool: ^0.4.0
|
||||
tinyspy: ^1.0.2
|
||||
std-env: ^3.3.2
|
||||
strip-literal: ^1.0.1
|
||||
tinybench: ^2.5.0
|
||||
tinypool: ^0.5.0
|
||||
vite: ^3.0.0 || ^4.0.0
|
||||
vite-node: 0.29.8
|
||||
vite-node: 0.31.1
|
||||
why-is-node-running: ^2.2.2
|
||||
peerDependencies:
|
||||
"@edge-runtime/vm": "*"
|
||||
@@ -10140,7 +10115,7 @@ turbo@latest:
|
||||
optional: true
|
||||
bin:
|
||||
vitest: vitest.mjs
|
||||
checksum: 203e33bf093fdb99a6832c905a6c78175bb15313e06e1dcfbeb010a0e3efb8ff0aba4d317efedb4de76bd0086691bbd2c4bc7d6631f60fb1634b96832cba144f
|
||||
checksum: b3f64a36102edc5b8594c085da648c838c0d275c620bd3b780624f936903b9c06579d6ef137fe9859e468f16deb8f154a50f009093119f9adb8b60ff1b7597ee
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -10200,6 +10175,13 @@ turbo@latest:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"well-known-symbols@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "well-known-symbols@npm:2.0.0"
|
||||
checksum: 4f54bbc3012371cb4d228f436891b8e7536d34ac61a57541890257e96788608e096231e0121ac24d08ef2f908b3eb2dc0adba35023eaeb2a7df655da91415402
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"whatwg-encoding@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "whatwg-encoding@npm:2.0.0"
|
||||
|
||||
Reference in New Issue
Block a user