Password / Login Page

Fixes #174
This commit is contained in:
ajnart
2022-06-06 17:13:17 +02:00
parent 9686761c3d
commit 83b4da282a
6 changed files with 158 additions and 7 deletions

View File

@@ -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>
);
}

View File

@@ -44,9 +44,7 @@ export default function App(props: AppProps & { colorScheme: ColorScheme }) {
>
<NotificationsProvider limit={4} position="bottom-left">
<ConfigProvider>
<Layout>
<Component {...pageProps} />
</Layout>
<Component {...pageProps} />
</ConfigProvider>
</NotificationsProvider>
</MantineProvider>

15
src/pages/_middleware.ts Normal file
View File

@@ -0,0 +1,15 @@
import { NextFetchEvent, NextRequest, NextResponse } from 'next/server';
export function middleware(req: NextRequest, ev: NextFetchEvent) {
const ok = req.cookies.password === process.env.PASSWORD;
const url = req.nextUrl.clone();
if (
!ok &&
url.pathname !== '/login' &&
process.env.PASSWORD &&
url.pathname !== '/api/configs/tryPassword'
) {
url.pathname = '/login';
}
return NextResponse.rewrite(url);
}

View 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',
});
};

View File

@@ -7,6 +7,7 @@ import { Config } from '../tools/types';
import { useConfig } from '../tools/state';
import { migrateToIdConfig } from '../tools/migrate';
import { getConfig } from '../tools/getConfig';
import Layout from '../components/layout/Layout';
export async function getServerSideProps({
req,
@@ -33,9 +34,9 @@ export default function HomePage(props: any) {
setConfig(migratedConfig);
}, [initialConfig]);
return (
<>
<Layout>
<AppShelf />
<LoadConfigComponent />
</>
</Layout>
);
}

111
src/pages/login.tsx Normal file
View File

@@ -0,0 +1,111 @@
import React from 'react';
import { PasswordInput, Anchor, Paper, Title, Text, Container, Group, Button } from '@mantine/core';
import { setCookies } from 'cookies-next';
import { useForm } from '@mantine/hooks';
import { showNotification, updateNotification } from '@mantine/notifications';
import axios from 'axios';
import { IconCheck, IconX } from '@tabler/icons';
// TODO: Add links to the wiki articles about the login process.
export default function AuthenticationTitle() {
const form = useForm({
initialValues: {
password: '',
},
});
return (
<Container
size={420}
style={{
height: '100vh',
display: 'flex',
width: 420,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Title
align="center"
sx={(theme) => ({ fontFamily: `Greycliff CF, ${theme.fontFamily}`, fontWeight: 900 })}
>
Welcome back!
</Title>
<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: 420 }}>
<form
onSubmit={form.onSubmit((values) => {
setCookies('password', values.password, {
maxAge: 60 * 60 * 24 * 30,
sameSite: 'strict',
});
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) {
updateNotification({
id: 'load-data',
color: 'teal',
title: 'Password correct',
message:
'Notification will close in 2 seconds, you can close this notification now',
icon: <IconCheck />,
autoClose: 300,
onClose: () => {
window.location.reload();
},
});
}
if (res.data.success === false) {
updateNotification({
id: 'load-data',
color: 'red',
title: 'Password is wrong, please try again.',
message:
'Notification will close in 2 seconds, you can close this notification now',
icon: <IconX />,
autoClose: 2000,
});
}
}, 500);
});
})}
>
<PasswordInput
id="password"
label="Password"
placeholder="Your password"
required
mt="md"
{...form.getInputProps('password')}
/>
<Group position="apart" mt="md">
<Anchor<'a'> onClick={(event) => event.preventDefault()} href="#" size="sm">
Forgot password?
</Anchor>
</Group>
<Button fullWidth type="submit" mt="xl">
Sign in
</Button>
</form>
</Paper>
</Container>
);
}