restructure file tree

This commit is contained in:
renzynx 2022-12-18 08:31:01 +07:00
parent d96d7582c5
commit a85753aa03
49 changed files with 347 additions and 281 deletions

View File

@ -1,101 +0,0 @@
import LoadingPage from '@components/LoadingPage';
import { ROUTES } from '@lib/constants';
import { useRegister } from '@lib/hooks';
import { ServerSettings } from '@lib/types';
import {
Paper,
Stack,
Group,
TextInput,
PasswordInput,
Anchor,
Button,
Text,
} from '@mantine/core';
import { IconUser } from '@tabler/icons';
function SignUpForm({ settings }: { settings: ServerSettings }) {
const { form, loading, register } = useRegister();
if (!settings) {
return <LoadingPage />;
}
return (
<Paper
w={{
base: '95%',
lg: 450,
md: 420,
sm: 450,
xs: 450,
}}
pt="xl"
pb="xl"
pr="lg"
pl="lg"
withBorder={true}
>
{!settings.REGISTRATION_ENABLED ? (
<Text size="xl" weight={500} align="center">
Registration is currently closed, please check back later.
</Text>
) : (
<>
<Text size="xl" weight={500} mb="xl">
Welcome to Bliss V2, sign up with
</Text>
<form onSubmit={form.onSubmit((values) => register(values))}>
<Stack spacing={10}>
<Group spacing={5}>
<Text weight="bold">Credentials</Text>
<IconUser size={20} />
</Group>
{settings.INVITE_MODE && (
<TextInput
{...form.getInputProps('invite')}
withAsterisk
id="invite"
label="Invite Code"
/>
)}
<TextInput
{...form.getInputProps('username')}
id="username"
label="Username"
description="Leave empty to get a random username"
/>
<TextInput
{...form.getInputProps('email')}
id="email"
withAsterisk
label="Email Address"
/>
<PasswordInput
{...form.getInputProps('password')}
withAsterisk
id="password"
label="Password"
/>
<Group pt="lg" position="apart" align="center">
<Anchor href={ROUTES.SIGN_IN} size="xs" color="dimmed">
Already have an account?
</Anchor>
<Button
variant="light"
color="violet"
type="submit"
loading={loading}
>
Sign up
</Button>
</Group>
</Stack>
</form>
</>
)}
</Paper>
);
}
export default SignUpForm;

View File

@ -1,4 +0,0 @@
import SignInForm from './SignInForm';
import SignUpForm from './SignUpForm';
export { SignInForm, SignUpForm };

View File

@ -0,0 +1,32 @@
import LoadingPage from '@components/pages/LoadingPage';
import { ROUTES } from '@lib/constants';
import { useIsAuth } from '@lib/hooks';
import React, { FC, Suspense } from 'react';
import dynamic from 'next/dynamic';
const Layout = dynamic(() => import('..'), { suspense: true });
const ProtectedWrapper: FC<{ children: any; withLayout?: boolean }> = ({
children,
withLayout,
}) => {
const currentUrl =
typeof window !== 'undefined'
? `${window.location.protocol}//${window.location.host}${window.location.pathname}`
: '';
const { data, isLoading } = useIsAuth({
redirectTo: ROUTES.SIGN_IN,
callbackUrl: encodeURIComponent(currentUrl),
});
if (isLoading) return <LoadingPage color="yellow" />;
return withLayout ? (
<Suspense fallback={<LoadingPage color="yellow" />}>
<Layout user={data!}>{children}</Layout>
</Suspense>
) : (
children
);
};
export default ProtectedWrapper;

View File

@ -0,0 +1,38 @@
import { useCreateInvite } from '@lib/hooks/useInviteCode';
import { Invite } from '@lib/types';
import { Stack, Button, Group, PasswordInput, CopyButton } from '@mantine/core';
import React, { FC } from 'react';
const InvitePage: FC<{ invites: Invite[] }> = ({ invites }) => {
const { create, isLoading } = useCreateInvite();
return (
<Stack>
<Button
loading={isLoading}
w={{ base: '100%', lg: '12rem', md: '12rem', sm: '12rem' }}
color="teal"
onClick={() => create()}
>
Create invite
</Button>
{invites.map((invite, idx) => (
<Group key={idx} sx={{ width: '100%' }} spacing={5}>
<PasswordInput
w={{ base: '100%', lg: 420, md: 400, sm: 400 }}
value={invite.token}
/>
<CopyButton value={invite.token}>
{({ copied, copy }) => (
<Button color={copied ? 'teal' : 'blue'} onClick={copy}>
{copied ? 'Copied invite' : 'Copy invite'}
</Button>
)}
</CopyButton>
</Group>
))}
</Stack>
);
};
export default InvitePage;

View File

@ -1,21 +1,11 @@
import { useSetServerSettings } from '@lib/hooks/useSetServerSettings'; import { useSetServerSettings } from '@lib/hooks/useSetServerSettings';
import { ServerSettings } from '@lib/types'; import { ServerSettings } from '@lib/types';
import { import { Stack, Button, SimpleGrid, Switch } from '@mantine/core';
Stack,
SegmentedControl,
Button,
Text,
SimpleGrid,
} from '@mantine/core';
import { showNotification } from '@mantine/notifications'; import { showNotification } from '@mantine/notifications';
import React from 'react'; import React from 'react';
const AdminForm = ({ REGISTRATION_ENABLED, INVITE_MODE }: ServerSettings) => { const ServerPage = (data: ServerSettings) => {
const { isLoading, update, form } = useSetServerSettings({ const { isLoading, update, form } = useSetServerSettings(data);
INVITE_MODE: INVITE_MODE.toString(),
REGISTRATION_ENABLED: REGISTRATION_ENABLED.toString(),
});
return ( return (
<form <form
onSubmit={form.onSubmit((values) => { onSubmit={form.onSubmit((values) => {
@ -40,23 +30,23 @@ const AdminForm = ({ REGISTRATION_ENABLED, INVITE_MODE }: ServerSettings) => {
]} ]}
> >
<Stack spacing={0}> <Stack spacing={0}>
<Text fw="bold">Turn on or off site registration </Text> <Switch
<SegmentedControl label="Turn on or off user registration."
size="lg"
radius="lg"
onLabel="ON"
offLabel="OFF"
checked={form.values.REGISTRATION_ENABLED}
{...form.getInputProps('REGISTRATION_ENABLED')} {...form.getInputProps('REGISTRATION_ENABLED')}
data={[
{ label: 'On', value: 'true' },
{ label: 'Off', value: 'false' },
]}
/> />
</Stack> <Switch
<Stack spacing={0}> label="Turn on or off invite mode."
<Text fw="bold">Enable or disable invite mode on the site</Text> size="lg"
<SegmentedControl radius="lg"
onLabel="ON"
offLabel="OFF"
checked={form.values.INVITE_MODE}
{...form.getInputProps('INVITE_MODE')} {...form.getInputProps('INVITE_MODE')}
data={[
{ label: 'Enable', value: 'true' },
{ label: 'Disable', value: 'false' },
]}
/> />
</Stack> </Stack>
</SimpleGrid> </SimpleGrid>
@ -73,4 +63,4 @@ const AdminForm = ({ REGISTRATION_ENABLED, INVITE_MODE }: ServerSettings) => {
); );
}; };
export default AdminForm; export default ServerPage;

View File

@ -0,0 +1,129 @@
import LoadingPage from '@components/pages/LoadingPage';
import { ROUTES } from '@lib/constants';
import { useRegister } from '@lib/hooks';
import { ServerSettings } from '@lib/types';
import {
Paper,
Stack,
Group,
TextInput,
PasswordInput,
Anchor,
Button,
Text,
Center,
Tooltip,
UnstyledButton,
} from '@mantine/core';
import { IconArrowBack, IconUser } from '@tabler/icons';
import router from 'next/router';
function SignUpPage({ settings }: { settings: ServerSettings }) {
const { form, loading, register } = useRegister();
if (!settings) {
return <LoadingPage />;
}
return (
<>
<Tooltip label="Home page">
<UnstyledButton
sx={(t) => ({
position: 'absolute',
top: 15,
left: 15,
background: t.colors.dark[5],
padding: 13,
display: 'grid',
placeItems: 'center',
borderRadius: t.radius.md,
':hover': {
background: t.colors.dark[4],
},
})}
onClick={() => router.push(ROUTES.HOME)}
>
<IconArrowBack />
</UnstyledButton>
</Tooltip>
<Center sx={{ height: '100vh', width: '100vw' }}>
<Paper
w={{
base: '95%',
lg: 450,
md: 420,
sm: 450,
xs: 450,
}}
pt="xl"
pb="xl"
pr="lg"
pl="lg"
withBorder={true}
>
{!settings.REGISTRATION_ENABLED ? (
<Text size="xl" weight={500} align="center">
Registration is currently closed, please check back later.
</Text>
) : (
<>
<Text size="xl" weight={500} mb="xl">
Welcome to Bliss V2, sign up with
</Text>
<form onSubmit={form.onSubmit((values) => register(values))}>
<Stack spacing={10}>
<Group spacing={5}>
<Text weight="bold">Credentials</Text>
<IconUser size={20} />
</Group>
{settings.INVITE_MODE && (
<TextInput
{...form.getInputProps('invite')}
withAsterisk
id="invite"
label="Invite Code"
/>
)}
<TextInput
{...form.getInputProps('username')}
id="username"
label="Username"
description="Leave empty to get a random username"
/>
<TextInput
{...form.getInputProps('email')}
id="email"
withAsterisk
label="Email Address"
/>
<PasswordInput
{...form.getInputProps('password')}
withAsterisk
id="password"
label="Password"
/>
<Group pt="lg" position="apart" align="center">
<Anchor href={ROUTES.SIGN_IN} size="xs" color="dimmed">
Already have an account?
</Anchor>
<Button
variant="light"
color="violet"
type="submit"
loading={loading}
>
Sign up
</Button>
</Group>
</Stack>
</form>
</>
)}
</Paper>
</Center>
</>
);
}
export default SignUpPage;

View File

@ -0,0 +1,4 @@
import SignInForm from './SignInForm';
import SignUpPage from './SignUpPage';
export { SignInForm, SignUpPage };

View File

@ -1,4 +1,4 @@
import LoadingPage from '@components/LoadingPage'; import LoadingPage from '@components/pages/LoadingPage';
import { useGetUserFiles } from '@lib/hooks'; import { useGetUserFiles } from '@lib/hooks';
import { IFile } from '@lib/types'; import { IFile } from '@lib/types';
import { import {

View File

@ -0,0 +1,18 @@
import Navbar from '@layouts/Navbar';
import Hero from './Hero';
import { FC } from 'react';
import { SessionUser } from '@lib/types';
import dynamic from 'next/dynamic';
const Sidebar = dynamic(() => import('@layouts/Sidebar'));
const HomePage: FC<{ user?: SessionUser }> = ({ user }) => {
return (
<>
<Navbar user={user} />
{user && <Sidebar />}
<Hero />
</>
);
};
export default HomePage;

View File

@ -4,12 +4,13 @@ import { IconCheck, IconCloudUpload, IconDownload, IconX } from '@tabler/icons';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { uploadStyles } from './styles'; import { uploadStyles } from './styles';
import { API_ROUTES, API_URL, CHUNK_SIZE } from '@lib/constants'; import { API_ROUTES, API_URL, CHUNK_SIZE, USER_LIMIT } from '@lib/constants';
import axios from 'axios'; import axios from 'axios';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { userAtom } from '@lib/atoms'; import { userAtom } from '@lib/atoms';
import { showNotification } from '@mantine/notifications'; import { showNotification } from '@mantine/notifications';
const ProgressCard = dynamic(() => import('./ProgressCard')); const ProgressCard = dynamic(() => import('./ProgressCard'));
import { ACCEPT_TYPE } from '@lib/constants';
const UploadZone = () => { const UploadZone = () => {
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
@ -129,8 +130,14 @@ const UploadZone = () => {
}} }}
className={classes.dropzone} className={classes.dropzone}
radius="md" radius="md"
// accept={MIME_TYPES} accept={ACCEPT_TYPE}
maxSize={5000 * 1024 ** 2} maxSize={
USER_LIMIT(
!!user?.createdAt,
user?.role === 'OWNER' || user?.role === 'ADMIN'
) *
1024 ** 2
}
> >
<div style={{ pointerEvents: 'none' }}> <div style={{ pointerEvents: 'none' }}>
<Group position="center"> <Group position="center">

View File

@ -1,4 +1,4 @@
import { serializeURL } from './utils'; import { MIME_TYPES } from '@mantine/dropzone';
export enum ROUTES { export enum ROUTES {
HOME = '/', HOME = '/',
@ -28,7 +28,7 @@ export enum API_ROUTES {
CHANGE_USERNAME = '/users/change-username', CHANGE_USERNAME = '/users/change-username',
SEND_VERIFICATION_EMAIL = '/users/verify/send', SEND_VERIFICATION_EMAIL = '/users/verify/send',
VERIFY_EMAIL = '/users/verify', VERIFY_EMAIL = '/users/verify',
CHECK_CLOSED = '/auth/check-register', SERVER_SETTINGS = '/server-settings',
UPDATE_SERVER_SETTINGS = '/admin/server-settings', UPDATE_SERVER_SETTINGS = '/admin/server-settings',
INVITE_CODE = '/admin/invites', INVITE_CODE = '/admin/invites',
} }
@ -59,9 +59,7 @@ export const flameshotScript = (token: string) => `
IMAGEPATH="$HOME/Pictures/" IMAGEPATH="$HOME/Pictures/"
IMAGENAME="$name-$(date +%s%N | sha256sum | base64 | head -c 32 ; echo)" IMAGENAME="$name-$(date +%s%N | sha256sum | base64 | head -c 32 ; echo)"
KEY="${token}" KEY="${token}"
DOMAIN="${serializeURL( DOMAIN="${process.env.NEXT_PUBLIC_API_URL}/upload"
process.env.NEXT_PUBLIC_API_URL!
)}" # Your upload domain (without http:// or https://)
flameshot config -f "$IMAGENAME" flameshot config -f "$IMAGENAME"
flameshot gui -r -p "$IMAGEPATH" > /dev/null flameshot gui -r -p "$IMAGEPATH" > /dev/null
@ -76,28 +74,61 @@ if [ -f "$FILE" ]; then
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "User-Agent: ShareX/13.4.0" \ -H "User-Agent: ShareX/13.4.0" \
-H "Authorization: $KEY" \ -H "Authorization: $KEY" \
-F "file=@$IMAGEPATH$IMAGENAME.png" "https://$DOMAIN/" | grep -Po '(?<="resource":")[^"]+') -F "file=@$IMAGEPATH$IMAGENAME.png" "$DOMAIN" | grep -Po '(?<="resource":")[^"]+')
# printf instead of echo as echo appends a newline
printf "%s" "$URL" | xclip -sel clip printf "%s" "$URL" | xclip -sel clip
rm "$IMAGEPATH$IMAGENAME.png" # Delete the image locally rm "$IMAGEPATH$IMAGENAME.png"
else else
echo "Aborted." echo "Aborted."
fi fi
`; `;
export const MIME_TYPES = [ // export const MIME_TYPES = [
'image/png', // 'image/png',
'image/gif', // 'image/gif',
'image/jpeg', // 'image/jpeg',
'image/svg+xml', // 'image/svg+xml',
'image/webp', // 'image/webp',
'audio/ogg', // 'audio/ogg',
'audio/wave', // 'audio/wave',
'audio/wav', // 'audio/wav',
'audio/x-wav', // 'audio/x-wav',
'audio/x-pn-wav', // 'audio/x-pn-wav',
// 'audio/mp3',
// 'audio/mpeg',
// ];
export const ACCEPT_TYPE = [
MIME_TYPES.png,
MIME_TYPES.jpeg,
MIME_TYPES.mp4,
MIME_TYPES.svg,
MIME_TYPES.webp,
MIME_TYPES.gif,
MIME_TYPES.zip,
MIME_TYPES.pdf,
MIME_TYPES.docx,
MIME_TYPES.pptx,
MIME_TYPES.xlsx,
'text/plain',
'audio/mp3', 'audio/mp3',
'audio/wav',
'audio/ogg',
'audio/flac',
'audio/aac',
'audio/m4a',
'audio/mp4',
'audio/mpeg', 'audio/mpeg',
'audio/webm',
'audio/3gpp',
'audio/3gpp2',
'audio/aac',
'audio/aiff',
'audio/amr',
'audio/basic',
'audio/midi',
'audio/mid',
'audio/m4a',
'video/mp4', 'video/mp4',
'video/quicktime', 'video/quicktime',
'video/ogg', 'video/ogg',
@ -114,5 +145,13 @@ export const MIME_TYPES = [
'multipart/x-zip', 'multipart/x-zip',
]; ];
export const USER_LIMIT = (verified: boolean, admin = false) => {
if (admin) return Infinity;
return verified
? +(process.env.NEXT_PUBLIC_USER_VERIFED_LIMIT ?? 2000)
: +(process.env.NEXT_PUBLIC_USER_NOT_VERIFED_LIMIT ?? 500);
};
export const CHUNK_SIZE = export const CHUNK_SIZE =
+(process.env.NEXT_PUBLIC_CHUNK_SIZE ?? 10) * 1024 ** 2; // default: 10mb +(process.env.NEXT_PUBLIC_CHUNK_SIZE ?? 10) * 1024 ** 2; // default: 10mb

View File

@ -7,17 +7,19 @@ import axios from 'axios';
import { useForm } from '@mantine/form'; import { useForm } from '@mantine/form';
import { toErrorMap } from '@lib/utils'; import { toErrorMap } from '@lib/utils';
export const useSetServerSettings = (initialData: ServerSettings) => { export const useSetServerSettings = (initialValues: ServerSettings) => {
const form = useForm<ServerSettings>({ const form = useForm<ServerSettings>({ initialValues });
initialValues: initialData,
});
const { data, mutate, isLoading, error } = useMutation( const { data, mutate, isLoading, error } = useMutation(
['server-settings'], ['server-settings'],
(data: Partial<ServerSettings>) => (data: Partial<ServerSettings>) =>
axios axios
.post(API_URL + API_ROUTES.UPDATE_SERVER_SETTINGS, data, { .post<ServerSettings>(
withCredentials: true, API_URL + API_ROUTES.UPDATE_SERVER_SETTINGS,
}) data,
{
withCredentials: true,
}
)
.then((res) => { .then((res) => {
updateNotification({ updateNotification({
id: 'server-settings', id: 'server-settings',
@ -27,10 +29,7 @@ export const useSetServerSettings = (initialData: ServerSettings) => {
icon: <IconCheck />, icon: <IconCheck />,
}); });
form.setValues({ form.setValues({ ...res.data });
INVITE_MODE: res.data.INVITE_MODE.toString(),
REGISTRATION_ENABLED: res.data.REGISTRATION_ENABLED.toString(),
});
return res.data; return res.data;
}) })

View File

@ -116,8 +116,8 @@ export interface EmbedSettings {
} }
export interface ServerSettings { export interface ServerSettings {
REGISTRATION_ENABLED: string; REGISTRATION_ENABLED: boolean;
INVITE_MODE: string; INVITE_MODE: boolean;
} }
type Token = 'INVITE_CODE' | 'EMAIL_VERIFICATION'; type Token = 'INVITE_CODE' | 'EMAIL_VERIFICATION';

View File

@ -1,4 +1,4 @@
import { NotFoundTitle } from '@components/404Page'; import { NotFoundTitle } from '@pages/404Page';
import React from 'react'; import React from 'react';
const NotFound = () => { const NotFound = () => {

View File

@ -1,14 +1,10 @@
import { ROUTES } from '@lib/constants';
import { useIsAuth } from '@lib/hooks';
import { CustomAppProps } from '@lib/types'; import { CustomAppProps } from '@lib/types';
import { MantineProvider } from '@mantine/core'; import { MantineProvider } from '@mantine/core';
import { NotificationsProvider } from '@mantine/notifications'; import { NotificationsProvider } from '@mantine/notifications';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head'; import Head from 'next/head';
import { Suspense } from 'react'; const ProtectedWrapper = dynamic(() => import('@layouts/ProtectedWrapper'));
const LoadingPage = dynamic(() => import('@components/LoadingPage'));
const Layout = dynamic(() => import('@components/Layout'), { suspense: true });
export default function App({ Component, pageProps }: CustomAppProps) { export default function App({ Component, pageProps }: CustomAppProps) {
const queryClient = new QueryClient(); const queryClient = new QueryClient();
@ -34,9 +30,9 @@ export default function App({ Component, pageProps }: CustomAppProps) {
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<NotificationsProvider> <NotificationsProvider>
{Component.options?.auth ? ( {Component.options?.auth ? (
<Auth withLayout={Component.options.withLayout}> <ProtectedWrapper withLayout={Component.options.withLayout}>
<Component {...pageProps} /> <Component {...pageProps} />
</Auth> </ProtectedWrapper>
) : ( ) : (
<Component {...pageProps} /> <Component {...pageProps} />
)} )}
@ -46,30 +42,3 @@ export default function App({ Component, pageProps }: CustomAppProps) {
</> </>
); );
} }
function Auth({
children,
withLayout,
}: {
children: any;
withLayout?: boolean;
}) {
const currentUrl =
typeof window !== 'undefined'
? `${window.location.protocol}//${window.location.host}${window.location.pathname}`
: '';
const { data, isLoading } = useIsAuth({
redirectTo: ROUTES.SIGN_IN,
callbackUrl: encodeURIComponent(currentUrl),
});
if (isLoading) return <LoadingPage color="yellow" />;
return withLayout ? (
<Suspense fallback={<LoadingPage color="yellow" />}>
<Layout user={data!}>{children}</Layout>
</Suspense>
) : (
children
);
}

View File

@ -12,7 +12,7 @@ import {
//! fix weird hydration issue //! fix weird hydration issue
// TODO: find a better way to fix hydration issue // TODO: find a better way to fix hydration issue
const SignInForm = dynamic( const SignInForm = dynamic(
() => import('@components/Authentication').then((mod) => mod.SignInForm), () => import('@components/pages/AuthPage').then((mod) => mod.SignInForm),
{ ssr: false } { ssr: false }
); );

View File

@ -1,47 +1,22 @@
import { SignUpForm } from '@components/Authentication'; import { SignUpPage } from '@pages/AuthPage';
import { API_ROUTES, API_URL, ROUTES } from '@lib/constants'; import { API_ROUTES, API_URL } from '@lib/constants';
import { ServerSettings } from '@lib/types'; import { ServerSettings } from '@lib/types';
import { Center, Tooltip, UnstyledButton } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons';
import axios from 'axios'; import axios from 'axios';
import { InferGetServerSidePropsType, NextPage } from 'next'; import { InferGetServerSidePropsType, NextPage } from 'next';
import router from 'next/router';
const SignUp: NextPage< const SignUp: NextPage<
InferGetServerSidePropsType<typeof getServerSideProps> InferGetServerSidePropsType<typeof getServerSideProps>
> = (data) => { > = (data) => {
return ( return (
<> <>
<Tooltip label="Home page"> <SignUpPage settings={data} />
<UnstyledButton
sx={(t) => ({
position: 'absolute',
top: 15,
left: 15,
background: t.colors.dark[5],
padding: 13,
display: 'grid',
placeItems: 'center',
borderRadius: t.radius.md,
':hover': {
background: t.colors.dark[4],
},
})}
onClick={() => router.push(ROUTES.HOME)}
>
<IconArrowBack />
</UnstyledButton>
</Tooltip>
<Center sx={{ height: '100vh', width: '100vw' }}>
<SignUpForm settings={data} />
</Center>
</> </>
); );
}; };
export const getServerSideProps = async () => { export const getServerSideProps = async () => {
const resp = await axios.get<ServerSettings>( const resp = await axios.get<ServerSettings>(
API_URL + API_ROUTES.CHECK_CLOSED API_URL + API_ROUTES.SERVER_SETTINGS
); );
return { return {

View File

@ -1,12 +1,11 @@
import InvitePage from '@components/pages/AdminPage/InvitePage';
import { API_ROUTES, API_URL, APP_NAME } from '@lib/constants'; import { API_ROUTES, API_URL, APP_NAME } from '@lib/constants';
import { useCreateInvite } from '@lib/hooks/useInviteCode';
import { import {
CustomNextPage, CustomNextPage,
Invite, Invite,
ServerSettings, ServerSettings,
SessionUser, SessionUser,
} from '@lib/types'; } from '@lib/types';
import { Button, CopyButton, Group, PasswordInput, Stack } from '@mantine/core';
import axios from 'axios'; import axios from 'axios';
import { GetServerSidePropsContext } from 'next'; import { GetServerSidePropsContext } from 'next';
import Head from 'next/head'; import Head from 'next/head';
@ -14,39 +13,13 @@ import Head from 'next/head';
const AdminDash: CustomNextPage<ServerSettings & { invites: Invite[] }> = ( const AdminDash: CustomNextPage<ServerSettings & { invites: Invite[] }> = (
data data
) => { ) => {
const { create, isLoading } = useCreateInvite();
return ( return (
<> <>
<Head> <Head>
<title>{APP_NAME} | Admin</title> <title>{APP_NAME} | Admin</title>
</Head> </Head>
<Stack> <InvitePage invites={data.invites} />
<Button
loading={isLoading}
w={{ base: '100%', lg: '12rem', md: '12rem', sm: '12rem' }}
color="teal"
onClick={() => create()}
>
Create invite
</Button>
{data.invites.map((invite, idx) => (
<Group key={idx} sx={{ width: '100%' }} spacing={5}>
<PasswordInput
w={{ base: '100%', lg: 420, md: 400, sm: 400 }}
value={invite.token}
/>
<CopyButton value={invite.token}>
{({ copied, copy }) => (
<Button color={copied ? 'teal' : 'blue'} onClick={copy}>
{copied ? 'Copied invite' : 'Copy invite'}
</Button>
)}
</CopyButton>
</Group>
))}
</Stack>
</> </>
); );
}; };

View File

@ -1,13 +1,13 @@
import AdminForm from '@components/Authentication/AdminForm'; import ServerPage from '@pages/AdminPage/ServerPage';
import { API_ROUTES, API_URL } from '@lib/constants'; import { API_ROUTES, API_URL } from '@lib/constants';
import { CustomNextPage, SessionUser } from '@lib/types'; import { CustomNextPage, ServerSettings, SessionUser } from '@lib/types';
import axios from 'axios'; import axios from 'axios';
import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next'; import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
const Owner: CustomNextPage< const Owner: CustomNextPage<
InferGetServerSidePropsType<typeof getServerSideProps> InferGetServerSidePropsType<typeof getServerSideProps>
> = ({ data }) => { > = ({ data }) => {
return <AdminForm {...data} />; return <ServerPage {...data} />;
}; };
export default Owner; export default Owner;
@ -29,7 +29,9 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
}; };
} }
const serverData = await axios.get(API_URL + API_ROUTES.CHECK_CLOSED); const serverData = await axios.get<ServerSettings>(
API_URL + API_ROUTES.SERVER_SETTINGS
);
return { return {
props: { data: serverData.data }, props: { data: serverData.data },

View File

@ -1,5 +1,5 @@
import QuickActions from '@components/QuickActions'; import QuickActions from '@pages/DashboardPage';
import StatisticCard from '@components/StatisticCard'; import StatisticCard from '@layouts/StatisticCard';
import { API_ROUTES, API_URL, APP_NAME } from '@lib/constants'; import { API_ROUTES, API_URL, APP_NAME } from '@lib/constants';
import { CustomNextPage } from '@lib/types'; import { CustomNextPage } from '@lib/types';
import { Group } from '@mantine/core'; import { Group } from '@mantine/core';

View File

@ -1,4 +1,4 @@
import UploadZone from '@components/Upload'; import UploadZone from '@pages/UploadPage';
import { APP_NAME } from '@lib/constants'; import { APP_NAME } from '@lib/constants';
import { CustomNextPage } from '@lib/types'; import { CustomNextPage } from '@lib/types';
import Head from 'next/head'; import Head from 'next/head';

View File

@ -1,20 +1,15 @@
import Hero from '@components/Hero'; import HomePage from '@pages/HomePage';
import Navbar from '@components/Layout/Navbar';
import { API_ROUTES, API_URL } from '@lib/constants'; import { API_ROUTES, API_URL } from '@lib/constants';
import { SessionUser } from '@lib/types'; import { SessionUser } from '@lib/types';
import axios from 'axios'; import axios from 'axios';
import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next'; import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
import dynamic from 'next/dynamic';
const Sidebar = dynamic(() => import('@components/Layout/Sidebar'));
const Home = ( const Home = (
props: InferGetServerSidePropsType<typeof getServerSideProps> props: InferGetServerSidePropsType<typeof getServerSideProps>
) => { ) => {
return ( return (
<> <>
<Navbar user={props?.user} /> <HomePage {...props} />
{props.user && <Sidebar />}
<Hero />
</> </>
); );
}; };

View File

@ -1,4 +1,4 @@
import ProfilePage from '@components/ProfilePage'; import ProfilePage from '@pages/ProfilePage';
import { APP_NAME } from '@lib/constants'; import { APP_NAME } from '@lib/constants';
import { CustomNextPage } from '@lib/types'; import { CustomNextPage } from '@lib/types';
import Head from 'next/head'; import Head from 'next/head';

View File

@ -1,5 +1,5 @@
import EmbedForm from '@components/Settings/EmbedForm'; import EmbedForm from '@pages/SettingPage/EmbedForm';
import EmbedPreview from '@components/Settings/EmbedPreview'; import EmbedPreview from '@pages/SettingPage/EmbedPreview';
import { API_URL, API_ROUTES } from '@lib/constants'; import { API_URL, API_ROUTES } from '@lib/constants';
import { useUpdateEmbedSettings } from '@lib/hooks'; import { useUpdateEmbedSettings } from '@lib/hooks';
import { CustomNextPage, EmbedSettings } from '@lib/types'; import { CustomNextPage, EmbedSettings } from '@lib/types';

View File

@ -18,7 +18,8 @@
"paths": { "paths": {
"@lib/*": ["lib/*"], "@lib/*": ["lib/*"],
"@components/*": ["components/*"], "@components/*": ["components/*"],
"@pages/*": ["pages/*"] "@pages/*": ["components/pages/*"],
"@layouts/*": ["components/layouts/*"]
} }
}, },
"include": ["next-env.d.ts", "env.d.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", "env.d.ts", "**/*.ts", "**/*.tsx"],