From 83b4da282a3d17e4da1192a09f7b6c5aef02c037 Mon Sep 17 00:00:00 2001 From: ajnart Date: Mon, 6 Jun 2022 17:13:17 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8Password=20/=20Login=20Page=20Fixes=20?= =?UTF-8?q?#174?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/[slug].tsx | 5 +- src/pages/_app.tsx | 4 +- src/pages/_middleware.ts | 15 ++++ src/pages/api/configs/tryPassword.tsx | 25 ++++++ src/pages/index.tsx | 5 +- src/pages/login.tsx | 111 ++++++++++++++++++++++++++ 6 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 src/pages/_middleware.ts create mode 100644 src/pages/api/configs/tryPassword.tsx create mode 100644 src/pages/login.tsx diff --git a/src/pages/[slug].tsx b/src/pages/[slug].tsx index d2577e01a..3417ea0f6 100644 --- a/src/pages/[slug].tsx +++ b/src/pages/[slug].tsx @@ -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 ( - <> + - + ); } diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 5cbf81843..ca0346781 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -44,9 +44,7 @@ export default function App(props: AppProps & { colorScheme: ColorScheme }) { > - - - + diff --git a/src/pages/_middleware.ts b/src/pages/_middleware.ts new file mode 100644 index 000000000..0975e7c9e --- /dev/null +++ b/src/pages/_middleware.ts @@ -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); +} diff --git a/src/pages/api/configs/tryPassword.tsx b/src/pages/api/configs/tryPassword.tsx new file mode 100644 index 000000000..18432739b --- /dev/null +++ b/src/pages/api/configs/tryPassword.tsx @@ -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', + }); +}; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 9ae8c33ff..c982f3940 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -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 ( - <> + - + ); } diff --git a/src/pages/login.tsx b/src/pages/login.tsx new file mode 100644 index 000000000..b6042106b --- /dev/null +++ b/src/pages/login.tsx @@ -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 ( + + ({ fontFamily: `Greycliff CF, ${theme.fontFamily}`, fontWeight: 900 })} + > + Welcome back! + + + Please enter the{' '} + href="#" size="sm" onClick={(event) => event.preventDefault()}> + password + + + + +
{ + 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: , + 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: , + autoClose: 2000, + }); + } + }, 500); + }); + })} + > + + + onClick={(event) => event.preventDefault()} href="#" size="sm"> + Forgot password? + + + + +
+
+ ); +}