feat: more options for text uploads & password protect them

This commit is contained in:
diced 2022-10-24 11:10:32 -07:00
parent d432b388f6
commit 4552643ff8
No known key found for this signature in database
GPG key ID: 370BD1BA142842D1
4 changed files with 125 additions and 5 deletions

View file

@ -1,9 +1,9 @@
import { Button, Group, Select, Tabs, Title } from '@mantine/core'; import { Button, Group, PasswordInput, Select, Tabs, Title, Tooltip } from '@mantine/core';
import { Prism } from '@mantine/prism'; import { Prism } from '@mantine/prism';
import { Language } from 'prism-react-renderer'; import { Language } from 'prism-react-renderer';
import { showNotification, updateNotification } from '@mantine/notifications'; import { showNotification, updateNotification } from '@mantine/notifications';
import CodeInput from 'components/CodeInput'; import CodeInput from 'components/CodeInput';
import { ImageIcon, TypeIcon, UploadIcon } from 'components/icons'; import { ClockIcon, ImageIcon, TypeIcon, UploadIcon } from 'components/icons';
import Link from 'components/Link'; import Link from 'components/Link';
import exts from 'lib/exts'; import exts from 'lib/exts';
import { userSelector } from 'lib/recoil/user'; import { userSelector } from 'lib/recoil/user';
@ -15,10 +15,47 @@ export default function Upload() {
const [value, setValue] = useState(''); const [value, setValue] = useState('');
const [lang, setLang] = useState('txt'); const [lang, setLang] = useState('txt');
const [password, setPassword] = useState('');
const [expires, setExpires] = useState('never');
const handleUpload = async () => { const handleUpload = async () => {
const file = new File([value], 'text.' + lang); const file = new File([value], 'text.' + lang);
const expires_at =
expires === 'never'
? null
: new Date(
{
'5min': Date.now() + 5 * 60 * 1000,
'10min': Date.now() + 10 * 60 * 1000,
'15min': Date.now() + 15 * 60 * 1000,
'30min': Date.now() + 30 * 60 * 1000,
'1h': Date.now() + 60 * 60 * 1000,
'2h': Date.now() + 2 * 60 * 60 * 1000,
'3h': Date.now() + 3 * 60 * 60 * 1000,
'4h': Date.now() + 4 * 60 * 60 * 1000,
'5h': Date.now() + 5 * 60 * 60 * 1000,
'6h': Date.now() + 6 * 60 * 60 * 1000,
'8h': Date.now() + 8 * 60 * 60 * 1000,
'12h': Date.now() + 12 * 60 * 60 * 1000,
'1d': Date.now() + 24 * 60 * 60 * 1000,
'3d': Date.now() + 3 * 24 * 60 * 60 * 1000,
'5d': Date.now() + 5 * 24 * 60 * 60 * 1000,
'7d': Date.now() + 7 * 24 * 60 * 60 * 1000,
'1w': Date.now() + 7 * 24 * 60 * 60 * 1000,
'1.5w': Date.now() + 1.5 * 7 * 24 * 60 * 60 * 1000,
'2w': Date.now() + 2 * 7 * 24 * 60 * 60 * 1000,
'3w': Date.now() + 3 * 7 * 24 * 60 * 60 * 1000,
'1m': Date.now() + 30 * 24 * 60 * 60 * 1000,
'1.5m': Date.now() + 1.5 * 30 * 24 * 60 * 60 * 1000,
'2m': Date.now() + 2 * 30 * 24 * 60 * 60 * 1000,
'3m': Date.now() + 3 * 30 * 24 * 60 * 60 * 1000,
'6m': Date.now() + 6 * 30 * 24 * 60 * 60 * 1000,
'8m': Date.now() + 8 * 30 * 24 * 60 * 60 * 1000,
'1y': Date.now() + 365 * 24 * 60 * 60 * 1000,
}[expires]
);
showNotification({ showNotification({
id: 'upload-text', id: 'upload-text',
title: 'Uploading...', title: 'Uploading...',
@ -58,6 +95,9 @@ export default function Upload() {
req.setRequestHeader('Authorization', user.token); req.setRequestHeader('Authorization', user.token);
req.setRequestHeader('UploadText', 'true'); req.setRequestHeader('UploadText', 'true');
expires !== 'never' && req.setRequestHeader('Expires-At', 'date=' + expires_at.toISOString());
password !== '' && req.setRequestHeader('Password', password);
req.send(body); req.send(body);
}; };
@ -99,6 +139,51 @@ export default function Upload() {
icon={<TypeIcon />} icon={<TypeIcon />}
searchable searchable
/> />
<Tooltip label='Add a password to your files (optional, leave blank for none)'>
<PasswordInput
style={{ width: '252px' }}
placeholder='Password'
value={password}
onChange={(e) => setPassword(e.currentTarget.value)}
/>
</Tooltip>
<Tooltip label='Set an expiration date for your files (optional, defaults to never)'>
<Select
value={expires}
onChange={(e) => setExpires(e)}
icon={<ClockIcon size={14} />}
data={[
{ value: 'never', label: 'Never' },
{ value: '5min', label: '5 minutes' },
{ value: '10min', label: '10 minutes' },
{ value: '15min', label: '15 minutes' },
{ value: '30min', label: '30 minutes' },
{ value: '1h', label: '1 hour' },
{ value: '2h', label: '2 hours' },
{ value: '3h', label: '3 hours' },
{ value: '4h', label: '4 hours' },
{ value: '5h', label: '5 hours' },
{ value: '6h', label: '6 hours' },
{ value: '8h', label: '8 hours' },
{ value: '12h', label: '12 hours' },
{ value: '1d', label: '1 day' },
{ value: '3d', label: '3 days' },
{ value: '5d', label: '5 days' },
{ value: '7d', label: '7 days' },
{ value: '1w', label: '1 week' },
{ value: '1.5w', label: '1.5 weeks' },
{ value: '2w', label: '2 weeks' },
{ value: '3w', label: '3 weeks' },
{ value: '1m', label: '1 month' },
{ value: '1.5m', label: '1.5 months' },
{ value: '2m', label: '2 months' },
{ value: '3m', label: '3 months' },
{ value: '6m', label: '6 months' },
{ value: '8m', label: '8 months' },
{ value: '1y', label: '1 year' },
]}
/>
</Tooltip>
<Button <Button
leftIcon={<UploadIcon />} leftIcon={<UploadIcon />}
onClick={handleUpload} onClick={handleUpload}

View file

@ -4,4 +4,4 @@ if (!global.prisma) {
if (!process.env.ZIPLINE_DOCKER_BUILD) global.prisma = new PrismaClient(); if (!process.env.ZIPLINE_DOCKER_BUILD) global.prisma = new PrismaClient();
} }
export default global.prisma; export default global.prisma as PrismaClient;

View file

@ -6,10 +6,13 @@ import config from 'lib/config';
import prisma from 'lib/prisma'; import prisma from 'lib/prisma';
import { parse } from 'lib/utils/client'; import { parse } from 'lib/utils/client';
import exts from 'lib/exts'; import exts from 'lib/exts';
import { Image } from '@prisma/client';
import { useRouter } from 'next/router';
export default function EmbeddedImage({ image, user, pass }) { export default function EmbeddedImage({ image, user, pass, prismRender }) {
const dataURL = (route: string) => `${route}/${image.file}`; const dataURL = (route: string) => `${route}/${image.file}`;
const router = useRouter();
const [opened, setOpened] = useState(pass); const [opened, setOpened] = useState(pass);
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [error, setError] = useState(''); const [error, setError] = useState('');
@ -22,6 +25,7 @@ export default function EmbeddedImage({ image, user, pass }) {
if (res.ok) { if (res.ok) {
setError(''); setError('');
if (prismRender) return router.push(`/code/${image.file}?password=${password}`);
updateImage(`/api/auth/image?id=${image.id}&password=${password}`); updateImage(`/api/auth/image?id=${image.id}&password=${password}`);
setOpened(false); setOpened(false);
} else { } else {
@ -193,13 +197,25 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
image.created_at = image.created_at.toString(); image.created_at = image.created_at.toString();
const prismRender = Object.keys(exts).includes(image.file.split('.').pop()); const prismRender = Object.keys(exts).includes(image.file.split('.').pop());
if (prismRender) if (prismRender && !image.password)
return { return {
redirect: { redirect: {
destination: `/code/${image.file}`, destination: `/code/${image.file}`,
permanent: true, permanent: true,
}, },
}; };
else if (prismRender && image.password) {
const pass = image.password ? true : false;
delete image.password;
return {
props: {
image,
user,
pass,
prismRender: true,
},
};
}
if (!image.mimetype.startsWith('image') && !image.mimetype.startsWith('video')) { if (!image.mimetype.startsWith('image') && !image.mimetype.startsWith('video')) {
const { default: datasource } = await import('lib/datasource'); const { default: datasource } = await import('lib/datasource');

View file

@ -1,7 +1,9 @@
import { Prism } from '@mantine/prism'; import { Prism } from '@mantine/prism';
import prisma from 'lib/prisma';
import exts from 'lib/exts'; import exts from 'lib/exts';
import { streamToString } from 'lib/utils/streams'; import { streamToString } from 'lib/utils/streams';
import { GetServerSideProps } from 'next'; import { GetServerSideProps } from 'next';
import { checkPassword } from 'lib/util';
type CodeProps = { type CodeProps = {
code: string; code: string;
@ -33,6 +35,23 @@ export const getServerSideProps: GetServerSideProps<CodeProps> = async (context)
notFound: true, notFound: true,
}; };
const file = await prisma.image.findFirst({
where: {
file: context.params.id as string,
},
});
if (!file) return { notFound: true };
if (file.password && !context.query.password)
return {
notFound: true,
};
if (file.password && context.query.password) {
const valid = await checkPassword(context.query.password as string, file.password);
if (!valid) return { notFound: true };
}
context.res.setHeader('Cache-Control', 'public, max-age=2628000, stale-while-revalidate=86400'); context.res.setHeader('Cache-Control', 'public, max-age=2628000, stale-while-revalidate=86400');
return { return {