feat: remove appwrite and add nextjs backend

This commit is contained in:
Elias Schneider
2022-10-09 22:30:32 +02:00
parent 7728351158
commit 4bab33ad8a
153 changed files with 13400 additions and 2811 deletions

View File

@@ -0,0 +1,146 @@
import {
Accordion,
Button,
Col,
Grid,
Group,
NumberInput,
PasswordInput,
Select,
Text,
TextInput,
} from "@mantine/core";
import { useForm, yupResolver } from "@mantine/form";
import { useModals } from "@mantine/modals";
import * as yup from "yup";
import shareService from "../../services/share.service";
import { ShareSecurity } from "../../types/share.type";
const CreateUploadModalBody = ({
uploadCallback,
}: {
uploadCallback: (
id: string,
expiration: string,
security: ShareSecurity
) => void;
}) => {
const modals = useModals();
const validationSchema = yup.object().shape({
link: yup
.string()
.required()
.min(3)
.max(100)
.matches(new RegExp("^[a-zA-Z0-9_-]*$"), {
message: "Can only contain letters, numbers, underscores and hyphens",
}),
password: yup.string().min(3).max(30),
maxViews: yup.number().min(1),
});
const form = useForm({
initialValues: {
link: "",
password: undefined,
maxViews: undefined,
expiration: "1-day",
},
schema: yupResolver(validationSchema),
});
return (
<form
onSubmit={form.onSubmit(async (values) => {
if (!(await shareService.isShareIdAvailable(values.link))) {
form.setFieldError("link", "This link is already in use");
} else {
uploadCallback(values.link, values.expiration, {
password: values.password,
maxViews: values.maxViews,
});
modals.closeAll();
}
})}
>
<Group direction="column" grow>
<Grid align={form.errors.link ? "center" : "flex-end"}>
<Col xs={9}>
<TextInput
variant="filled"
label="Link"
placeholder="myAwesomeShare"
{...form.getInputProps("link")}
/>
</Col>
<Col xs={3}>
<Button
variant="outline"
onClick={() =>
form.setFieldValue(
"link",
Buffer.from(Math.random().toString(), "utf8")
.toString("base64")
.substr(10, 7)
)
}
>
Generate
</Button>
</Col>
</Grid>
<Text
size="xs"
sx={(theme) => ({
color: theme.colors.gray[6],
})}
>
{window.location.origin}/share/
{form.values.link == "" ? "myAwesomeShare" : form.values.link}
</Text>
<Select
label="Expiration"
{...form.getInputProps("expiration")}
data={[
{
value: "10-minutes",
label: "10 Minutes",
},
{ value: "1-hour", label: "1 Hour" },
{ value: "1-day", label: "1 Day" },
{ value: "1-week".toString(), label: "1 Week" },
{ value: "1-month", label: "1 Month" },
]}
/>
<Accordion>
<Accordion.Item
label="Security options"
sx={{ borderBottom: "none" }}
>
<Group direction="column" grow>
<PasswordInput
variant="filled"
placeholder="No password"
label="Password protection"
{...form.getInputProps("password")}
/>
<NumberInput
min={1}
type="number"
variant="filled"
placeholder="No limit"
label="Maximal views"
{...form.getInputProps("maxViews")}
/>
</Group>
</Accordion.Item>
</Accordion>
<Button type="submit">Share</Button>
</Group>
</form>
);
};
export default CreateUploadModalBody;

View File

@@ -0,0 +1,53 @@
import { Button, Tooltip } from "@mantine/core";
import { useEffect, useState } from "react";
import shareService from "../../services/share.service";
const DownloadAllButton = ({ shareId }: { shareId: string }) => {
const [isZipReady, setIsZipReady] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const downloadAll = async () => {
setIsLoading(true);
await shareService
.downloadFile(shareId, "zip")
.then(() => setIsLoading(false));
};
useEffect(() => {
shareService
.getMetaData(shareId)
.then((share) => setIsZipReady(share.isZipReady))
.catch(() => {});
const timer = setInterval(() => {
shareService.getMetaData(shareId).then((share) => {
setIsZipReady(share.isZipReady);
if (share.isZipReady) clearInterval(timer);
}).catch(() => {});
}, 5000);
return () => {
clearInterval(timer);
};
}, []);
if (!isZipReady)
return (
<Tooltip
wrapLines
position="bottom"
width={220}
withArrow
label="The share is preparing. This can take a few minutes."
>
<Button variant="outline" onClick={downloadAll} disabled>
Download all
</Button>
</Tooltip>
);
return (
<Button variant="outline" loading={isLoading} onClick={downloadAll}>
Download all
</Button>
);
};
export default DownloadAllButton;

View File

@@ -0,0 +1,88 @@
import { ActionIcon, Loader, Skeleton, Table } from "@mantine/core";
import { useRouter } from "next/router";
import { CircleCheck, Download } from "tabler-icons-react";
import shareService from "../../services/share.service";
import { byteStringToHumanSizeString } from "../../utils/math/byteStringToHumanSizeString.util";
const FileList = ({
files,
shareId,
isLoading,
}: {
files: any[];
shareId: string;
isLoading: boolean;
}) => {
const router = useRouter();
const skeletonRows = [...Array(5)].map((c, i) => (
<tr key={i}>
<td>
<Skeleton height={30} width={30} />
</td>
<td>
<Skeleton height={14} />
</td>
<td>
<Skeleton height={14} />
</td>
<td>
<Skeleton height={25} width={25} />
</td>
</tr>
));
const rows = files.map((file) => (
<tr key={file.name}>
<td>
{/* <Image
width={30}
height={30}
alt={file.name}
objectFit="cover"
style={{ borderRadius: 3 }}
src={`data:image/png;base64,${new Buffer(file.preview).toString(
"base64"
)}`}
></Image> */}
</td>
<td>{file.name}</td>
<td>{byteStringToHumanSizeString(file.size)}</td>
<td>
{file.uploadingState ? (
file.uploadingState != "finished" ? (
<Loader size={22} />
) : (
<CircleCheck color="green" size={22} />
)
) : (
<ActionIcon
size={25}
onClick={async () => {
await shareService.downloadFile(shareId, file.id);
}}
>
<Download />
</ActionIcon>
)}
</td>
</tr>
));
return (
<Table>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Size</th>
<th></th>
</tr>
</thead>
<tbody>{isLoading ? skeletonRows : rows}</tbody>
</Table>
);
};
export default FileList;

View File

@@ -0,0 +1,58 @@
import { Button, Group, PasswordInput, Text, Title } from "@mantine/core";
import { ModalsContextProps } from "@mantine/modals/lib/context";
import { useState } from "react";
const showEnterPasswordModal = (
modals: ModalsContextProps,
submitCallback: any
) => {
return modals.openModal({
closeOnClickOutside: false,
withCloseButton: false,
closeOnEscape: false,
title: (
<>
<Title order={4}>Password required</Title>
<Text size="sm">
This access this share please enter the password for the share.
</Text>
</>
),
children: <Body submitCallback={submitCallback} />,
});
};
const Body = ({ submitCallback }: { submitCallback: any }) => {
const [password, setPassword] = useState("");
const [passwordWrong, setPasswordWrong] = useState(false);
return (
<>
<Group grow direction="column">
<PasswordInput
variant="filled"
placeholder="Password"
error={passwordWrong && "Wrong password"}
onFocus={() => setPasswordWrong(false)}
onChange={(e) => setPassword(e.target.value)}
value={password}
/>
<Button
onClick={() =>
submitCallback(password)
.then((res: any) => res)
.catch((e: any) => {
const error = e.response.data.message;
if (error == "Wrong password") {
setPasswordWrong(true);
}
})
}
>
Submit
</Button>
</Group>
</>
);
};
export default showEnterPasswordModal;

View File

@@ -0,0 +1,41 @@
import { Button, Group, Text, Title } from "@mantine/core";
import { useModals } from "@mantine/modals";
import { ModalsContextProps } from "@mantine/modals/lib/context";
import { useRouter } from "next/router";
const showErrorModal = (
modals: ModalsContextProps,
title: string,
text: string
) => {
return modals.openModal({
closeOnClickOutside: false,
withCloseButton: false,
closeOnEscape: false,
title: <Title order={4}>{title}</Title>,
children: <Body text={text} />,
});
};
const Body = ({ text }: { text: string }) => {
const modals = useModals();
const router = useRouter();
return (
<>
<Group grow direction="column">
<Text size="sm">{text}</Text>
<Button
onClick={() => {
modals.closeAll();
router.back();
}}
>
Go back
</Button>
</Group>
</>
);
};
export default showErrorModal;