mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-15 17:56:21 +01:00
Merge pull request #535 from ajnart/new-update-indicator
New update indicator
This commit is contained in:
@@ -61,6 +61,7 @@
|
|||||||
"next": "12.2.0",
|
"next": "12.2.0",
|
||||||
"next-i18next": "^11.3.0",
|
"next-i18next": "^11.3.0",
|
||||||
"nzbget-api": "^0.0.3",
|
"nzbget-api": "^0.0.3",
|
||||||
|
"ping": "^0.4.2",
|
||||||
"prism-react-renderer": "^1.3.5",
|
"prism-react-renderer": "^1.3.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@@ -75,6 +76,7 @@
|
|||||||
"@next/eslint-plugin-next": "^12.1.4",
|
"@next/eslint-plugin-next": "^12.1.4",
|
||||||
"@types/dockerode": "^3.3.9",
|
"@types/dockerode": "^3.3.9",
|
||||||
"@types/node": "17.0.1",
|
"@types/node": "17.0.1",
|
||||||
|
"@types/ping": "^0.4.1",
|
||||||
"@types/react": "17.0.1",
|
"@types/react": "17.0.1",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
||||||
|
|||||||
@@ -113,12 +113,6 @@
|
|||||||
"advancedOptions": {
|
"advancedOptions": {
|
||||||
"title": "Advanced options",
|
"title": "Advanced options",
|
||||||
"form": {
|
"form": {
|
||||||
"httpStatusCodes": {
|
|
||||||
"label": "HTTP Status Codes",
|
|
||||||
"placeholder": "Select valid status codes",
|
|
||||||
"clearButtonLabel": "Clear selection",
|
|
||||||
"nothingFound": "Nothing found"
|
|
||||||
},
|
|
||||||
"openServiceInNewTab": {
|
"openServiceInNewTab": {
|
||||||
"label": "Open service in new tab"
|
"label": "Open service in new tab"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
Image,
|
Image,
|
||||||
LoadingOverlay,
|
LoadingOverlay,
|
||||||
Modal,
|
Modal,
|
||||||
MultiSelect,
|
|
||||||
PasswordInput,
|
PasswordInput,
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
@@ -25,7 +24,7 @@ import { useTranslation } from 'next-i18next';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { useConfig } from '../../tools/state';
|
import { useConfig } from '../../tools/state';
|
||||||
import { tryMatchPort, ServiceTypeList, StatusCodes, Config } from '../../tools/types';
|
import { tryMatchPort, ServiceTypeList, Config } from '../../tools/types';
|
||||||
import apiKeyPaths from './apiKeyPaths.json';
|
import apiKeyPaths from './apiKeyPaths.json';
|
||||||
import Tip from '../layout/Tip';
|
import Tip from '../layout/Tip';
|
||||||
|
|
||||||
@@ -121,7 +120,6 @@ export function AddAppShelfItemForm(props: AddAppShelfItemFormProps) {
|
|||||||
password: props.password ?? undefined,
|
password: props.password ?? undefined,
|
||||||
openedUrl: props.openedUrl ?? undefined,
|
openedUrl: props.openedUrl ?? undefined,
|
||||||
ping: props.ping ?? true,
|
ping: props.ping ?? true,
|
||||||
status: props.status ?? ['200'],
|
|
||||||
newTab: props.newTab ?? true,
|
newTab: props.newTab ?? true,
|
||||||
},
|
},
|
||||||
validate: {
|
validate: {
|
||||||
@@ -139,12 +137,6 @@ export function AddAppShelfItemForm(props: AddAppShelfItemFormProps) {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
status: (value: string[]) => {
|
|
||||||
if (!value.length) {
|
|
||||||
return t('modal.form.validation.noStatusCodeSelected');
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -190,12 +182,6 @@ export function AddAppShelfItemForm(props: AddAppShelfItemFormProps) {
|
|||||||
if (newForm.openedUrl === '') newForm.openedUrl = undefined;
|
if (newForm.openedUrl === '') newForm.openedUrl = undefined;
|
||||||
if (newForm.category === null) newForm.category = undefined;
|
if (newForm.category === null) newForm.category = undefined;
|
||||||
if (newForm.ping === true) newForm.ping = undefined;
|
if (newForm.ping === true) newForm.ping = undefined;
|
||||||
if (
|
|
||||||
(newForm.status.length === 1 && newForm.status[0] === '200') ||
|
|
||||||
newForm.ping === false
|
|
||||||
) {
|
|
||||||
delete newForm.status;
|
|
||||||
}
|
|
||||||
// If service already exists, update it.
|
// If service already exists, update it.
|
||||||
if (config.services && config.services.find((s) => s.id === newForm.id)) {
|
if (config.services && config.services.find((s) => s.id === newForm.id)) {
|
||||||
setConfig({
|
setConfig({
|
||||||
@@ -451,26 +437,10 @@ export function AddAppShelfItemForm(props: AddAppShelfItemFormProps) {
|
|||||||
<Space h="sm" />
|
<Space h="sm" />
|
||||||
<Stack>
|
<Stack>
|
||||||
<Switch
|
<Switch
|
||||||
label={t('modal.tabs.advancedOptions.form.ping.label')}
|
label="Ping service"
|
||||||
defaultChecked={form.values.ping}
|
defaultChecked={form.values.ping}
|
||||||
{...form.getInputProps('ping')}
|
{...form.getInputProps('ping')}
|
||||||
/>
|
/>
|
||||||
{form.values.ping && (
|
|
||||||
<MultiSelect
|
|
||||||
required
|
|
||||||
label={t('modal.tabs.advancedOptions.form.httpStatusCodes.label')}
|
|
||||||
data={StatusCodes}
|
|
||||||
placeholder={t('modal.tabs.advancedOptions.form.httpStatusCodes.placeholder')}
|
|
||||||
clearButtonLabel={t(
|
|
||||||
'modal.tabs.advancedOptions.form.httpStatusCodes.clearButtonLabel'
|
|
||||||
)}
|
|
||||||
nothingFound={t('modal.tabs.advancedOptions.form.httpStatusCodes.nothingFound')}
|
|
||||||
defaultValue={['200']}
|
|
||||||
clearable
|
|
||||||
searchable
|
|
||||||
{...form.getInputProps('status')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Switch
|
<Switch
|
||||||
label={t('modal.tabs.advancedOptions.form.openServiceInNewTab.label')}
|
label={t('modal.tabs.advancedOptions.form.openServiceInNewTab.label')}
|
||||||
defaultChecked={form.values.newTab}
|
defaultChecked={form.values.newTab}
|
||||||
|
|||||||
@@ -1,15 +1,30 @@
|
|||||||
import { ActionIcon, Title, Tooltip, Drawer, Tabs, ScrollArea } from '@mantine/core';
|
import {
|
||||||
import { useHotkeys } from '@mantine/hooks';
|
ActionIcon,
|
||||||
import { useState } from 'react';
|
Title,
|
||||||
import { IconSettings } from '@tabler/icons';
|
Tooltip,
|
||||||
|
Drawer,
|
||||||
|
Tabs,
|
||||||
|
ScrollArea,
|
||||||
|
Indicator,
|
||||||
|
Alert,
|
||||||
|
Notification,
|
||||||
|
Anchor,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useElementSize, useHotkeys, useViewportSize } from '@mantine/hooks';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { IconInfoCircle, IconSettings } from '@tabler/icons';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import AdvancedSettings from './AdvancedSettings';
|
import AdvancedSettings from './AdvancedSettings';
|
||||||
import CommonSettings from './CommonSettings';
|
import CommonSettings from './CommonSettings';
|
||||||
import Credits from './Credits';
|
import Credits from './Credits';
|
||||||
|
import { CURRENT_VERSION, REPO_URL } from '../../../data/constants';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { NextLink } from '@mantine/next';
|
||||||
|
|
||||||
function SettingsMenu(props: any) {
|
function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: string }) {
|
||||||
const { t } = useTranslation('settings/common');
|
const { t } = useTranslation('settings/common');
|
||||||
|
const { height, width } = useViewportSize();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue="Common">
|
<Tabs defaultValue="Common">
|
||||||
@@ -18,13 +33,16 @@ function SettingsMenu(props: any) {
|
|||||||
<Tabs.Tab value="Customizations">{t('tabs.customizations')}</Tabs.Tab>
|
<Tabs.Tab value="Customizations">{t('tabs.customizations')}</Tabs.Tab>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<Tabs.Panel data-autofocus value="Common">
|
<Tabs.Panel data-autofocus value="Common">
|
||||||
<ScrollArea style={{ height: '78vh' }} offsetScrollbars>
|
<ScrollArea style={{ height: height - 100 }} offsetScrollbars>
|
||||||
|
{newVersionAvailable && <NewUpdateIndicator newVersionAvailable={newVersionAvailable} />}
|
||||||
<CommonSettings />
|
<CommonSettings />
|
||||||
|
<Credits />
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
<Tabs.Panel value="Customizations">
|
<Tabs.Panel value="Customizations">
|
||||||
<ScrollArea style={{ height: '78vh' }} offsetScrollbars>
|
<ScrollArea style={{ height: height - 120 }} offsetScrollbars>
|
||||||
<AdvancedSettings />
|
<AdvancedSettings />
|
||||||
|
<Credits />
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
@@ -34,6 +52,17 @@ function SettingsMenu(props: any) {
|
|||||||
export function SettingsMenuButton(props: any) {
|
export function SettingsMenuButton(props: any) {
|
||||||
useHotkeys([['ctrl+L', () => setOpened(!opened)]]);
|
useHotkeys([['ctrl+L', () => setOpened(!opened)]]);
|
||||||
const { t } = useTranslation('settings/common');
|
const { t } = useTranslation('settings/common');
|
||||||
|
const [newVersionAvailable, setNewVersionAvailable] = useState<string>('');
|
||||||
|
useEffect(() => {
|
||||||
|
// Fetch Data here when component first mounted
|
||||||
|
fetch(`https://api.github.com/repos/${REPO_URL}/releases/latest`).then((res) => {
|
||||||
|
res.json().then((data) => {
|
||||||
|
if (data.tag_name > CURRENT_VERSION) {
|
||||||
|
setNewVersionAvailable(data.tag_name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [CURRENT_VERSION]);
|
||||||
|
|
||||||
const [opened, setOpened] = useState(false);
|
const [opened, setOpened] = useState(false);
|
||||||
|
|
||||||
@@ -47,10 +76,10 @@ export function SettingsMenuButton(props: any) {
|
|||||||
opened={props.opened || opened}
|
opened={props.opened || opened}
|
||||||
onClose={() => setOpened(false)}
|
onClose={() => setOpened(false)}
|
||||||
>
|
>
|
||||||
<SettingsMenu />
|
<SettingsMenu newVersionAvailable={newVersionAvailable} />
|
||||||
<Credits />
|
|
||||||
</Drawer>
|
</Drawer>
|
||||||
<Tooltip label={t('tooltip')}>
|
<Tooltip label={t('tooltip')}>
|
||||||
|
<Indicator size={15} color="blue" withBorder processing disabled={!newVersionAvailable}>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="default"
|
variant="default"
|
||||||
radius="md"
|
radius="md"
|
||||||
@@ -61,7 +90,33 @@ export function SettingsMenuButton(props: any) {
|
|||||||
>
|
>
|
||||||
<IconSettings />
|
<IconSettings />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
|
</Indicator>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NewUpdateIndicator({ newVersionAvailable }: { newVersionAvailable: string }) {
|
||||||
|
return (
|
||||||
|
<Notification
|
||||||
|
mt={10}
|
||||||
|
icon={<IconInfoCircle size={25} />}
|
||||||
|
disallowClose
|
||||||
|
color="teal"
|
||||||
|
radius="md"
|
||||||
|
title="New update available"
|
||||||
|
hidden={newVersionAvailable === ''}
|
||||||
|
>
|
||||||
|
Version{' '}
|
||||||
|
<b>
|
||||||
|
<Anchor
|
||||||
|
target="_blank"
|
||||||
|
href={`https://github.com/ajnart/homarr/releases/tag/${newVersionAvailable}`}
|
||||||
|
>
|
||||||
|
{newVersionAvailable}
|
||||||
|
</Anchor>
|
||||||
|
</b>{' '}
|
||||||
|
is available ! Current version: {CURRENT_VERSION}
|
||||||
|
</Notification>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
import React, { useEffect } from 'react';
|
|
||||||
import { createStyles, Footer as FooterComponent } from '@mantine/core';
|
|
||||||
import { showNotification } from '@mantine/notifications';
|
|
||||||
import { IconAlertCircle as AlertCircle } from '@tabler/icons';
|
|
||||||
import { CURRENT_VERSION, REPO_URL } from '../../../data/constants';
|
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
|
||||||
footer: {
|
|
||||||
borderTop: `1px solid ${
|
|
||||||
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[2]
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
|
|
||||||
inner: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: `${theme.spacing.md}px ${theme.spacing.md}px`,
|
|
||||||
|
|
||||||
[theme.fn.smallerThan('sm')]: {
|
|
||||||
flexDirection: 'column',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
links: {
|
|
||||||
[theme.fn.smallerThan('sm')]: {
|
|
||||||
marginTop: theme.spacing.lg,
|
|
||||||
marginBottom: theme.spacing.sm,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface FooterCenteredProps {
|
|
||||||
links: { link: string; label: string }[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Footer({ links }: FooterCenteredProps) {
|
|
||||||
useEffect(() => {
|
|
||||||
// Fetch Data here when component first mounted
|
|
||||||
fetch(`https://api.github.com/repos/${REPO_URL}/releases/latest`).then((res) => {
|
|
||||||
res.json().then((data) => {
|
|
||||||
if (data.tag_name > CURRENT_VERSION) {
|
|
||||||
showNotification({
|
|
||||||
color: 'yellow',
|
|
||||||
autoClose: false,
|
|
||||||
title: 'New version available',
|
|
||||||
icon: <AlertCircle />,
|
|
||||||
message: `Version ${data.tag_name} is available, update now!`,
|
|
||||||
});
|
|
||||||
} else if (data.tag_name < CURRENT_VERSION) {
|
|
||||||
showNotification({
|
|
||||||
color: 'orange',
|
|
||||||
autoClose: 5000,
|
|
||||||
title: 'You are using a development version',
|
|
||||||
icon: <AlertCircle />,
|
|
||||||
message: 'This version of Homarr is still in development! Bugs are expected 🐛',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FooterComponent
|
|
||||||
height="auto"
|
|
||||||
style={{
|
|
||||||
background: 'none',
|
|
||||||
border: 'none',
|
|
||||||
clear: 'both',
|
|
||||||
}}
|
|
||||||
children={undefined}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { AppShell, createStyles } from '@mantine/core';
|
import { AppShell, createStyles } from '@mantine/core';
|
||||||
import { Header } from './header/Header';
|
import { Header } from './header/Header';
|
||||||
import { Footer } from './Footer';
|
|
||||||
import Aside from './Aside';
|
import Aside from './Aside';
|
||||||
import Navbar from './Navbar';
|
import Navbar from './Navbar';
|
||||||
import { HeaderConfig } from './header/HeaderConfig';
|
import { HeaderConfig } from './header/HeaderConfig';
|
||||||
@@ -30,7 +29,6 @@ export default function Layout({ children, style }: any) {
|
|||||||
header={<Header />}
|
header={<Header />}
|
||||||
navbar={widgetPosition ? <Navbar /> : undefined}
|
navbar={widgetPosition ? <Navbar /> : undefined}
|
||||||
aside={widgetPosition ? undefined : <Aside />}
|
aside={widgetPosition ? undefined : <Aside />}
|
||||||
footer={<Footer links={[]} />}
|
|
||||||
>
|
>
|
||||||
<HeaderConfig />
|
<HeaderConfig />
|
||||||
<Background />
|
<Background />
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export default function PingComponent(props: any) {
|
|||||||
<motion.div
|
<motion.div
|
||||||
style={{ position: 'absolute', bottom: 20, right: 20 }}
|
style={{ position: 'absolute', bottom: 20, right: 20 }}
|
||||||
animate={{
|
animate={{
|
||||||
scale: isOnline === 'online' ? [1, 0.8, 1] : 1,
|
scale: isOnline === 'online' ? [1, 0.7, 1] : 1,
|
||||||
}}
|
}}
|
||||||
transition={{ repeat: Infinity, duration: 2.5, ease: 'easeInOut' }}
|
transition={{ repeat: Infinity, duration: 2.5, ease: 'easeInOut' }}
|
||||||
>
|
>
|
||||||
@@ -78,7 +78,7 @@ export default function PingComponent(props: any) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Indicator
|
<Indicator
|
||||||
size={13}
|
size={15}
|
||||||
color={isOnline === 'online' ? 'green' : isOnline === 'down' ? 'red' : 'yellow'}
|
color={isOnline === 'online' ? 'green' : isOnline === 'down' ? 'red' : 'yellow'}
|
||||||
>
|
>
|
||||||
{null}
|
{null}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import axios from 'axios';
|
import ping from 'ping';
|
||||||
import https from 'https';
|
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
async function Get(req: NextApiRequest, res: NextApiResponse) {
|
async function Get(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
|||||||
@@ -35,28 +35,6 @@ interface ConfigModule {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StatusCodes = [
|
|
||||||
{ value: '200', label: '200 - OK', group: 'Sucessful responses' },
|
|
||||||
{ value: '204', label: '204 - No Content', group: 'Sucessful responses' },
|
|
||||||
{ value: '301', label: '301 - Moved Permanently', group: 'Redirection responses' },
|
|
||||||
{ value: '302', label: '302 - Found / Moved Temporarily', group: 'Redirection responses' },
|
|
||||||
{ value: '304', label: '304 - Not Modified', group: 'Redirection responses' },
|
|
||||||
{ value: '307', label: '307 - Temporary Redirect', group: 'Redirection responses' },
|
|
||||||
{ value: '308', label: '308 - Permanent Redirect', group: 'Redirection responses' },
|
|
||||||
{ value: '400', label: '400 - Bad Request', group: 'Client error responses' },
|
|
||||||
{ value: '401', label: '401 - Unauthorized', group: 'Client error responses' },
|
|
||||||
{ value: '403', label: '403 - Forbidden', group: 'Client error responses' },
|
|
||||||
{ value: '404', label: '404 - Not Found', group: 'Client error responses' },
|
|
||||||
{ value: '405', label: '405 - Method Not Allowed', group: 'Client error responses' },
|
|
||||||
{ value: '408', label: '408 - Request Timeout', group: 'Client error responses' },
|
|
||||||
{ value: '410', label: '410 - Gone', group: 'Client error responses' },
|
|
||||||
{ value: '429', label: '429 - Too Many Requests', group: 'Client error responses' },
|
|
||||||
{ value: '500', label: '500 - Internal Server Error', group: 'Server error responses' },
|
|
||||||
{ value: '502', label: '502 - Bad Gateway', group: 'Server error responses' },
|
|
||||||
{ value: '503', label: '503 - Service Unavailable', group: 'Server error responses' },
|
|
||||||
{ value: '054', label: '504 - Gateway Timeout Error', group: 'Server error responses' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const Targets = [
|
export const Targets = [
|
||||||
{ value: '_blank', label: 'New Tab' },
|
{ value: '_blank', label: 'New Tab' },
|
||||||
{ value: '_top', label: 'Same Window' },
|
{ value: '_top', label: 'Same Window' },
|
||||||
|
|||||||
28
yarn.lock
28
yarn.lock
@@ -2179,6 +2179,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/ping@npm:^0.4.1":
|
||||||
|
version: 0.4.1
|
||||||
|
resolution: "@types/ping@npm:0.4.1"
|
||||||
|
checksum: 9b94837fe66df70558c5a42b0e0c8371b4950ab56b96c42c8df809ff2cf52477dd0a7e01d2e6b38af8bb6683b3dcb54587960b96b4b1f3d40fdb529aea348ad0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/prettier@npm:^2.1.5":
|
"@types/prettier@npm:^2.1.5":
|
||||||
version: 2.6.3
|
version: 2.6.3
|
||||||
resolution: "@types/prettier@npm:2.6.3"
|
resolution: "@types/prettier@npm:2.6.3"
|
||||||
@@ -4843,6 +4850,7 @@ __metadata:
|
|||||||
"@tanstack/react-query": ^4.2.1
|
"@tanstack/react-query": ^4.2.1
|
||||||
"@types/dockerode": ^3.3.9
|
"@types/dockerode": ^3.3.9
|
||||||
"@types/node": 17.0.1
|
"@types/node": 17.0.1
|
||||||
|
"@types/ping": ^0.4.1
|
||||||
"@types/react": 17.0.1
|
"@types/react": 17.0.1
|
||||||
"@types/uuid": ^8.3.4
|
"@types/uuid": ^8.3.4
|
||||||
"@typescript-eslint/eslint-plugin": ^5.30.7
|
"@typescript-eslint/eslint-plugin": ^5.30.7
|
||||||
@@ -4874,6 +4882,7 @@ __metadata:
|
|||||||
next: 12.2.0
|
next: 12.2.0
|
||||||
next-i18next: ^11.3.0
|
next-i18next: ^11.3.0
|
||||||
nzbget-api: ^0.0.3
|
nzbget-api: ^0.0.3
|
||||||
|
ping: ^0.4.2
|
||||||
prettier: ^2.7.1
|
prettier: ^2.7.1
|
||||||
prism-react-renderer: ^1.3.5
|
prism-react-renderer: ^1.3.5
|
||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
@@ -6897,6 +6906,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ping@npm:^0.4.2":
|
||||||
|
version: 0.4.2
|
||||||
|
resolution: "ping@npm:0.4.2"
|
||||||
|
dependencies:
|
||||||
|
q: 1.x
|
||||||
|
underscore: ^1.12.0
|
||||||
|
checksum: 43992c76fb3294734248753f2028d9fab3b919dbfae79a5ea6df7e81fc2d6d555dd0b195d6c3dbc5c89aa9dba1cd8eb58d5ecedad103ecfee64df516e5f3665b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"pirates@npm:^4.0.4":
|
"pirates@npm:^4.0.4":
|
||||||
version: 4.0.5
|
version: 4.0.5
|
||||||
resolution: "pirates@npm:4.0.5"
|
resolution: "pirates@npm:4.0.5"
|
||||||
@@ -7064,7 +7083,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"q@npm:^1.4.1":
|
"q@npm:1.x, q@npm:^1.4.1":
|
||||||
version: 1.5.1
|
version: 1.5.1
|
||||||
resolution: "q@npm:1.5.1"
|
resolution: "q@npm:1.5.1"
|
||||||
checksum: 147baa93c805bc1200ed698bdf9c72e9e42c05f96d007e33a558b5fdfd63e5ea130e99313f28efc1783e90e6bdb4e48b67a36fcc026b7b09202437ae88a1fb12
|
checksum: 147baa93c805bc1200ed698bdf9c72e9e42c05f96d007e33a558b5fdfd63e5ea130e99313f28efc1783e90e6bdb4e48b67a36fcc026b7b09202437ae88a1fb12
|
||||||
@@ -8177,6 +8196,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"underscore@npm:^1.12.0":
|
||||||
|
version: 1.13.6
|
||||||
|
resolution: "underscore@npm:1.13.6"
|
||||||
|
checksum: d5cedd14a9d0d91dd38c1ce6169e4455bb931f0aaf354108e47bd46d3f2da7464d49b2171a5cf786d61963204a42d01ea1332a903b7342ad428deaafaf70ec36
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"unique-filename@npm:^1.1.1":
|
"unique-filename@npm:^1.1.1":
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
resolution: "unique-filename@npm:1.1.1"
|
resolution: "unique-filename@npm:1.1.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user