🔀 Merge branch 'dev' into overseerr-integration
This commit is contained in:
@@ -58,7 +58,7 @@ const useStyles = createStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export function Illustration(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
function Illustration(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
const theme = useMantineTheme();
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 362 145" {...props}>
|
||||
@@ -70,7 +70,7 @@ export function Illustration(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
);
|
||||
}
|
||||
|
||||
export default function NothingFoundBackground() {
|
||||
export default function Custom404() {
|
||||
const { classes } = useStyles();
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,6 +6,7 @@ import AppShelf from '../components/AppShelf/AppShelf';
|
||||
import LoadConfigComponent from '../components/Config/LoadConfig';
|
||||
import { Config } from '../tools/types';
|
||||
import { useConfig } from '../tools/state';
|
||||
import Layout from '../components/layout/Layout';
|
||||
|
||||
export async function getServerSideProps(
|
||||
context: GetServerSidePropsContext
|
||||
@@ -46,9 +47,9 @@ export default function HomePage(props: any) {
|
||||
setConfig(initialConfig);
|
||||
}, [initialConfig]);
|
||||
return (
|
||||
<>
|
||||
<Layout>
|
||||
<AppShelf />
|
||||
<LoadConfigComponent />
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,51 +1,67 @@
|
||||
import { GetServerSidePropsContext } from 'next';
|
||||
import { useState } from 'react';
|
||||
import { AppProps } from 'next/app';
|
||||
import { getCookie, setCookies } from 'cookies-next';
|
||||
import { getCookie, setCookie } from 'cookies-next';
|
||||
import Head from 'next/head';
|
||||
import { MantineProvider, ColorScheme, ColorSchemeProvider } from '@mantine/core';
|
||||
import { MantineProvider, ColorScheme, ColorSchemeProvider, MantineTheme } from '@mantine/core';
|
||||
import { NotificationsProvider } from '@mantine/notifications';
|
||||
import { useHotkeys } from '@mantine/hooks';
|
||||
import Layout from '../components/layout/Layout';
|
||||
import { ConfigProvider } from '../tools/state';
|
||||
import { theme } from '../tools/theme';
|
||||
import { styles } from '../tools/styles';
|
||||
import { ColorTheme } from '../tools/color';
|
||||
|
||||
export default function App(props: AppProps & { colorScheme: ColorScheme }) {
|
||||
export default function App(this: any, props: AppProps & { colorScheme: ColorScheme }) {
|
||||
const { Component, pageProps } = props;
|
||||
const [colorScheme, setColorScheme] = useState<ColorScheme>(props.colorScheme);
|
||||
|
||||
const [primaryColor, setPrimaryColor] = useState<MantineTheme['primaryColor']>('red');
|
||||
const [secondaryColor, setSecondaryColor] = useState<MantineTheme['primaryColor']>('orange');
|
||||
const [primaryShade, setPrimaryShade] = useState<MantineTheme['primaryShade']>(6);
|
||||
const colorTheme = {
|
||||
primaryColor,
|
||||
secondaryColor,
|
||||
setPrimaryColor,
|
||||
setSecondaryColor,
|
||||
primaryShade,
|
||||
setPrimaryShade,
|
||||
};
|
||||
|
||||
const toggleColorScheme = (value?: ColorScheme) => {
|
||||
const nextColorScheme = value || (colorScheme === 'dark' ? 'light' : 'dark');
|
||||
setColorScheme(nextColorScheme);
|
||||
setCookies('color-scheme', nextColorScheme, { maxAge: 60 * 60 * 24 * 30 });
|
||||
setCookie('color-scheme', nextColorScheme, { maxAge: 60 * 60 * 24 * 30 });
|
||||
};
|
||||
useHotkeys([['mod+J', () => toggleColorScheme()]]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Homarr 🦞</title>
|
||||
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
|
||||
<link rel="shortcut icon" href="/favicon.svg" />
|
||||
</Head>
|
||||
|
||||
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
|
||||
<MantineProvider
|
||||
theme={{
|
||||
...theme,
|
||||
colorScheme,
|
||||
}}
|
||||
withGlobalStyles
|
||||
withNormalizeCSS
|
||||
>
|
||||
<NotificationsProvider limit={4} position="bottom-left">
|
||||
<ConfigProvider>
|
||||
<Layout>
|
||||
<ColorTheme.Provider value={colorTheme}>
|
||||
<MantineProvider
|
||||
theme={{
|
||||
...theme,
|
||||
primaryColor,
|
||||
primaryShade,
|
||||
colorScheme,
|
||||
}}
|
||||
styles={{
|
||||
...styles,
|
||||
}}
|
||||
withGlobalStyles
|
||||
withNormalizeCSS
|
||||
>
|
||||
<NotificationsProvider limit={4} position="bottom-left">
|
||||
<ConfigProvider>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
</ConfigProvider>
|
||||
</NotificationsProvider>
|
||||
</MantineProvider>
|
||||
</ConfigProvider>
|
||||
</NotificationsProvider>
|
||||
</MantineProvider>
|
||||
</ColorTheme.Provider>
|
||||
</ColorSchemeProvider>
|
||||
</>
|
||||
);
|
||||
|
||||
16
src/pages/_middleware.ts
Normal file
16
src/pages/_middleware.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { NextFetchEvent, NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
export function middleware(req: NextRequest, ev: NextFetchEvent) {
|
||||
const isCorrectPassword = req.cookies.password === process.env.PASSWORD;
|
||||
const url = req.nextUrl.clone();
|
||||
if (
|
||||
!isCorrectPassword &&
|
||||
url.pathname !== '/login' &&
|
||||
process.env.PASSWORD &&
|
||||
url.pathname !== '/api/configs/tryPassword'
|
||||
) {
|
||||
url.pathname = '/login';
|
||||
return NextResponse.rewrite(url);
|
||||
}
|
||||
}
|
||||
25
src/pages/api/configs/tryPassword.tsx
Normal file
25
src/pages/api/configs/tryPassword.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
function Post(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { tried } = req.body;
|
||||
// Try to match the password with the PASSWORD env variable
|
||||
if (tried === process.env.PASSWORD) {
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
});
|
||||
}
|
||||
return res.status(200).json({
|
||||
success: false,
|
||||
});
|
||||
}
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
// Filter out if the reuqest is a POST or a GET
|
||||
if (req.method === 'POST') {
|
||||
return Post(req, res);
|
||||
}
|
||||
return res.status(405).json({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed',
|
||||
});
|
||||
};
|
||||
58
src/pages/api/docker/container/[id].tsx
Normal file
58
src/pages/api/docker/container/[id].tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import Docker from 'dockerode';
|
||||
|
||||
const docker = new Docker();
|
||||
|
||||
async function Get(req: NextApiRequest, res: NextApiResponse) {
|
||||
// Get the slug of the request
|
||||
const { id } = req.query as { id: string };
|
||||
const { action } = req.query;
|
||||
// Get the action on the request (start, stop, restart)
|
||||
if (action !== 'start' && action !== 'stop' && action !== 'restart' && action !== 'remove') {
|
||||
return res.status(400).json({
|
||||
statusCode: 400,
|
||||
message: 'Invalid action',
|
||||
});
|
||||
}
|
||||
if (!id) {
|
||||
return res.status(400).json({
|
||||
message: 'Missing ID',
|
||||
});
|
||||
}
|
||||
// Get the container with the ID
|
||||
const container = docker.getContainer(id);
|
||||
const startAction = async () => {
|
||||
switch (action) {
|
||||
case 'remove':
|
||||
return container.remove();
|
||||
case 'start':
|
||||
return container.start();
|
||||
case 'stop':
|
||||
return container.stop();
|
||||
case 'restart':
|
||||
return container.restart();
|
||||
default:
|
||||
return Promise;
|
||||
}
|
||||
};
|
||||
try {
|
||||
await startAction();
|
||||
return res.status(200).json({
|
||||
statusCode: 200,
|
||||
message: `Container ${id} ${action}ed`,
|
||||
});
|
||||
} catch (err) {
|
||||
return res.status(500).json(err);
|
||||
}
|
||||
}
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
// Filter out if the reuqest is a Put or a GET
|
||||
if (req.method === 'GET') {
|
||||
return Get(req, res);
|
||||
}
|
||||
return res.status(405).json({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed',
|
||||
});
|
||||
};
|
||||
24
src/pages/api/docker/containers.tsx
Normal file
24
src/pages/api/docker/containers.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import Docker from 'dockerode';
|
||||
|
||||
async function Get(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const docker = new Docker();
|
||||
const containers = await docker.listContainers({ all: true });
|
||||
res.status(200).json(containers);
|
||||
} catch (err) {
|
||||
res.status(500).json({ err });
|
||||
}
|
||||
}
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
// Filter out if the reuqest is a POST or a GET
|
||||
if (req.method === 'GET') {
|
||||
return Get(req, res);
|
||||
}
|
||||
return res.status(405).json({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed',
|
||||
});
|
||||
};
|
||||
83
src/pages/api/modules/calendar.ts
Normal file
83
src/pages/api/modules/calendar.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import axios from 'axios';
|
||||
import { getCookie } from 'cookies-next';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { getConfig } from '../../../tools/getConfig';
|
||||
import { Config } from '../../../tools/types';
|
||||
|
||||
async function Post(req: NextApiRequest, res: NextApiResponse) {
|
||||
// Parse req.body as a ServiceItem
|
||||
const { id } = req.body;
|
||||
const { type } = req.query;
|
||||
const configName = getCookie('config-name', { req });
|
||||
const { config }: { config: Config } = getConfig(configName?.toString() ?? 'default').props;
|
||||
// Find service with serviceId in config
|
||||
const service = config.services.find((service) => service.id === id);
|
||||
if (!service) {
|
||||
return res.status(500).json({
|
||||
statusCode: 500,
|
||||
message: 'Missing service',
|
||||
});
|
||||
}
|
||||
|
||||
const nextMonth = new Date(new Date().setMonth(new Date().getMonth() + 2)).toISOString();
|
||||
const lastMonth = new Date(new Date().setMonth(new Date().getMonth() - 2)).toISOString();
|
||||
const TypeToUrl: { service: string; url: string }[] = [
|
||||
{
|
||||
service: 'sonarr',
|
||||
url: '/api/calendar',
|
||||
},
|
||||
{
|
||||
service: 'radarr',
|
||||
url: '/api/v3/calendar',
|
||||
},
|
||||
{
|
||||
service: 'lidarr',
|
||||
url: '/api/v1/calendar',
|
||||
},
|
||||
{
|
||||
service: 'readarr',
|
||||
url: '/api/v1/calendar',
|
||||
},
|
||||
];
|
||||
if (!type) {
|
||||
return res.status(400).json({
|
||||
message: 'Missing required parameter in url: type',
|
||||
});
|
||||
}
|
||||
if (!service) {
|
||||
return res.status(400).json({
|
||||
message: 'Missing required parameter in body: service',
|
||||
});
|
||||
}
|
||||
// Match the type to the correct url
|
||||
const url = TypeToUrl.find((x) => x.service === type);
|
||||
if (!url) {
|
||||
return res.status(400).json({
|
||||
message: 'Invalid type',
|
||||
});
|
||||
}
|
||||
// Get the origin URL
|
||||
let { href: origin } = new URL(service.url);
|
||||
if (origin.endsWith('/')) {
|
||||
origin = origin.slice(0, -1);
|
||||
}
|
||||
const pined = `${origin}${url?.url}?apiKey=${service.apiKey}&end=${nextMonth}&start=${lastMonth}`;
|
||||
return axios
|
||||
.get(`${origin}${url?.url}?apiKey=${service.apiKey}&end=${nextMonth}&start=${lastMonth}`)
|
||||
.then((response) => res.status(200).json(response.data))
|
||||
.catch((e) => res.status(500).json(e));
|
||||
// // Make a request to the URL
|
||||
// const response = await axios.get(url);
|
||||
// // Return the response
|
||||
}
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
// Filter out if the reuqest is a POST or a GET
|
||||
if (req.method === 'POST') {
|
||||
return Post(req, res);
|
||||
}
|
||||
return res.status(405).json({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed',
|
||||
});
|
||||
};
|
||||
@@ -1,42 +1,66 @@
|
||||
import { Deluge } from '@ctrl/deluge';
|
||||
import { QBittorrent } from '@ctrl/qbittorrent';
|
||||
import { NormalizedTorrent } from '@ctrl/shared-torrent';
|
||||
import { Transmission } from '@ctrl/transmission';
|
||||
import { getCookie } from 'cookies-next';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { getConfig } from '../../../tools/getConfig';
|
||||
import { Config } from '../../../tools/types';
|
||||
|
||||
async function Post(req: NextApiRequest, res: NextApiResponse) {
|
||||
// Get the type of service from the request url
|
||||
const { dlclient } = req.query;
|
||||
const { body } = req;
|
||||
// Get login, password and url from the body
|
||||
const { username, password, url } = body;
|
||||
if (!dlclient || (!username && !password) || !url) {
|
||||
return res.status(400).json({
|
||||
error: 'Wrong request',
|
||||
const configName = getCookie('config-name', { req });
|
||||
const { config }: { config: Config } = getConfig(configName?.toString() ?? 'default').props;
|
||||
const qBittorrentServices = config.services.filter((service) => service.type === 'qBittorrent');
|
||||
const delugeServices = config.services.filter((service) => service.type === 'Deluge');
|
||||
const transmissionServices = config.services.filter((service) => service.type === 'Transmission');
|
||||
|
||||
const torrents: NormalizedTorrent[] = [];
|
||||
|
||||
if (!qBittorrentServices && !delugeServices && !transmissionServices) {
|
||||
return res.status(500).json({
|
||||
statusCode: 500,
|
||||
message: 'Missing services',
|
||||
});
|
||||
}
|
||||
let client: Deluge | QBittorrent;
|
||||
switch (dlclient) {
|
||||
case 'qbit':
|
||||
client = new QBittorrent({
|
||||
baseUrl: new URL(url).origin,
|
||||
username,
|
||||
password,
|
||||
});
|
||||
break;
|
||||
case 'deluge':
|
||||
client = new Deluge({
|
||||
baseUrl: new URL(url).origin,
|
||||
password,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
return res.status(400).json({
|
||||
error: 'Wrong request',
|
||||
});
|
||||
try {
|
||||
await Promise.all(
|
||||
qBittorrentServices.map((service) =>
|
||||
new QBittorrent({
|
||||
baseUrl: service.url,
|
||||
username: service.username,
|
||||
password: service.password,
|
||||
})
|
||||
.getAllData()
|
||||
.then((e) => torrents.push(...e.torrents))
|
||||
)
|
||||
);
|
||||
await Promise.all(
|
||||
delugeServices.map((service) =>
|
||||
new Deluge({
|
||||
baseUrl: service.url,
|
||||
password: 'password' in service ? service.password : '',
|
||||
})
|
||||
.getAllData()
|
||||
.then((e) => torrents.push(...e.torrents))
|
||||
)
|
||||
);
|
||||
// Map transmissionServices
|
||||
await Promise.all(
|
||||
transmissionServices.map((service) =>
|
||||
new Transmission({
|
||||
baseUrl: service.url,
|
||||
username: 'username' in service ? service.username : '',
|
||||
password: 'password' in service ? service.password : '',
|
||||
})
|
||||
.getAllData()
|
||||
.then((e) => torrents.push(...e.torrents))
|
||||
)
|
||||
);
|
||||
} catch (e: any) {
|
||||
return res.status(401).json(e);
|
||||
}
|
||||
const data = await client.getAllData();
|
||||
res.status(200).json({
|
||||
torrents: data.torrents,
|
||||
});
|
||||
return res.status(200).json(torrents);
|
||||
}
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
|
||||
@@ -7,10 +7,14 @@ async function Get(req: NextApiRequest, res: NextApiResponse) {
|
||||
await axios
|
||||
.get(url as string)
|
||||
.then((response) => {
|
||||
res.status(200).json(response.data);
|
||||
res.status(response.status).json(response.statusText);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).json(error);
|
||||
if (error.response) {
|
||||
res.status(error.response.status).json(error.response.statusText);
|
||||
} else {
|
||||
res.status(500).json('Server Error');
|
||||
}
|
||||
});
|
||||
// // Make a request to the URL
|
||||
// const response = await axios.get(url);
|
||||
|
||||
19
src/pages/api/modules/search.ts
Normal file
19
src/pages/api/modules/search.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import axios from 'axios';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
async function Get(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { q } = req.query;
|
||||
const response = await axios.get(`https://duckduckgo.com/ac/?q=${q}`);
|
||||
res.status(200).json(response.data);
|
||||
}
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
// Filter out if the reuqest is a POST or a GET
|
||||
if (req.method === 'GET') {
|
||||
return Get(req, res);
|
||||
}
|
||||
return res.status(405).json({
|
||||
statusCode: 405,
|
||||
message: 'Method not allowed',
|
||||
});
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getCookie, setCookies } from 'cookies-next';
|
||||
import { getCookie, setCookie } from 'cookies-next';
|
||||
import { GetServerSidePropsContext } from 'next';
|
||||
import { useEffect } from 'react';
|
||||
import AppShelf from '../components/AppShelf/AppShelf';
|
||||
@@ -7,6 +7,8 @@ import { Config } from '../tools/types';
|
||||
import { useConfig } from '../tools/state';
|
||||
import { migrateToIdConfig } from '../tools/migrate';
|
||||
import { getConfig } from '../tools/getConfig';
|
||||
import { useColorTheme } from '../tools/color';
|
||||
import Layout from '../components/layout/Layout';
|
||||
|
||||
export async function getServerSideProps({
|
||||
req,
|
||||
@@ -14,7 +16,7 @@ export async function getServerSideProps({
|
||||
}: GetServerSidePropsContext): Promise<{ props: { config: Config } }> {
|
||||
let cookie = getCookie('config-name', { req, res });
|
||||
if (!cookie) {
|
||||
setCookies('config-name', 'default', {
|
||||
setCookie('config-name', 'default', {
|
||||
req,
|
||||
res,
|
||||
maxAge: 60 * 60 * 24 * 30,
|
||||
@@ -28,14 +30,17 @@ export async function getServerSideProps({
|
||||
export default function HomePage(props: any) {
|
||||
const { config: initialConfig }: { config: Config } = props;
|
||||
const { setConfig } = useConfig();
|
||||
const { setPrimaryColor, setSecondaryColor } = useColorTheme();
|
||||
useEffect(() => {
|
||||
const migratedConfig = migrateToIdConfig(initialConfig);
|
||||
setPrimaryColor(migratedConfig.settings.primaryColor || 'red');
|
||||
setSecondaryColor(migratedConfig.settings.secondaryColor || 'orange');
|
||||
setConfig(migratedConfig);
|
||||
}, [initialConfig]);
|
||||
return (
|
||||
<>
|
||||
<Layout>
|
||||
<AppShelf />
|
||||
<LoadConfigComponent />
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
115
src/pages/login.tsx
Normal file
115
src/pages/login.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import React from 'react';
|
||||
import { PasswordInput, Anchor, Paper, Title, Text, Container, Group, Button } from '@mantine/core';
|
||||
import { setCookie } from 'cookies-next';
|
||||
import { useForm } from '@mantine/hooks';
|
||||
import { showNotification, updateNotification } from '@mantine/notifications';
|
||||
import axios from 'axios';
|
||||
import { IconCheck, IconX } from '@tabler/icons';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
// TODO: Add links to the wiki articles about the login process.
|
||||
export default function AuthenticationTitle() {
|
||||
const router = useRouter();
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
password: '',
|
||||
},
|
||||
});
|
||||
return (
|
||||
<Container
|
||||
size="lg"
|
||||
style={{
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Group>
|
||||
<Title
|
||||
align="center"
|
||||
sx={(theme) => ({ fontFamily: `Greycliff CF, ${theme.fontFamily}`, fontWeight: 900 })}
|
||||
>
|
||||
Welcome back!
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
<Text color="dimmed" size="sm" align="center" mt={5}>
|
||||
Please enter the{' '}
|
||||
<Anchor<'a'> href="#" size="sm" onClick={(event) => event.preventDefault()}>
|
||||
password
|
||||
</Anchor>
|
||||
</Text>
|
||||
|
||||
<Paper
|
||||
withBorder
|
||||
shadow="md"
|
||||
p={30}
|
||||
mt={30}
|
||||
radius="md"
|
||||
style={{ width: '100%', maxWidth: 420 }}
|
||||
>
|
||||
<form
|
||||
onSubmit={form.onSubmit((values) => {
|
||||
setCookie('password', values.password, {
|
||||
maxAge: 60 * 60 * 24 * 30,
|
||||
sameSite: 'lax',
|
||||
});
|
||||
showNotification({
|
||||
id: 'load-data',
|
||||
loading: true,
|
||||
title: 'Checking your password',
|
||||
message: 'Your password is being checked...',
|
||||
autoClose: false,
|
||||
disallowClose: true,
|
||||
});
|
||||
axios
|
||||
.post('/api/configs/tryPassword', {
|
||||
tried: values.password,
|
||||
})
|
||||
.then((res) => {
|
||||
setTimeout(() => {
|
||||
if (res.data.success === true) {
|
||||
router.push('/');
|
||||
updateNotification({
|
||||
id: 'load-data',
|
||||
color: 'teal',
|
||||
title: 'Password correct, redirecting you...',
|
||||
message: undefined,
|
||||
icon: <IconCheck />,
|
||||
autoClose: 1000,
|
||||
});
|
||||
}
|
||||
if (res.data.success === false) {
|
||||
updateNotification({
|
||||
id: 'load-data',
|
||||
color: 'red',
|
||||
title: 'Password is wrong, please try again.',
|
||||
message: undefined,
|
||||
icon: <IconX />,
|
||||
autoClose: 2000,
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
})}
|
||||
>
|
||||
<PasswordInput
|
||||
id="password"
|
||||
label="Password"
|
||||
placeholder="Your password"
|
||||
required
|
||||
autoFocus
|
||||
mt="md"
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
<Button fullWidth type="submit" mt="xl">
|
||||
Sign in
|
||||
</Button>
|
||||
</form>
|
||||
</Paper>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user