feat(pages): add urls page
This commit is contained in:
parent
212c69d303
commit
f1c46da47d
5 changed files with 162 additions and 1 deletions
|
@ -35,6 +35,7 @@ import {
|
|||
Logout as LogoutIcon,
|
||||
PeopleAlt as UsersIcon,
|
||||
Brush as BrushIcon,
|
||||
Link as URLIcon,
|
||||
} from '@material-ui/icons';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import Backdrop from './Backdrop';
|
||||
|
@ -55,6 +56,11 @@ const items = [
|
|||
text: 'Files',
|
||||
link: '/dashboard/files',
|
||||
},
|
||||
{
|
||||
icon: <URLIcon />,
|
||||
text: 'URLs',
|
||||
link: '/dashboard/urls',
|
||||
},
|
||||
{
|
||||
icon: <UploadIcon />,
|
||||
text: 'Upload',
|
||||
|
|
92
src/components/pages/Urls.tsx
Normal file
92
src/components/pages/Urls.tsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Grid, Card, CardHeader, Box, Typography, IconButton, Link } from '@material-ui/core';
|
||||
import { ContentCopy as CopyIcon, DeleteForever as DeleteIcon } from '@material-ui/icons';
|
||||
|
||||
import Backdrop from 'components/Backdrop';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import Alert from 'components/Alert';
|
||||
import copy from 'copy-to-clipboard';
|
||||
|
||||
export default function Urls() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [urls, setURLS] = useState([]);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [severity, setSeverity] = useState('success');
|
||||
const [message, setMessage] = useState('Deleted');
|
||||
|
||||
const updatePages = async () => {
|
||||
setLoading(true);
|
||||
const urls = await useFetch('/api/user/urls');
|
||||
|
||||
setURLS(urls);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const deleteURL = async u => {
|
||||
const url = await useFetch('/api/user/urls', 'DELETE', { id: u.id });
|
||||
if (url.error) {
|
||||
setSeverity('error');
|
||||
setMessage('Error: ' + url.error);
|
||||
setOpen(true);
|
||||
} else {
|
||||
setSeverity('success');
|
||||
setMessage(`Deleted ${u.vanity ?? u.id}`);
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
updatePages();
|
||||
};
|
||||
|
||||
const copyURL = u => {
|
||||
copy(`${window.location.protocol}//${window.location.host}${u.url}`);
|
||||
setSeverity('success');
|
||||
setMessage(`Copied URL: ${window.location.protocol}//${window.location.host}${u.url}`);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updatePages();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Backdrop open={loading}/>
|
||||
<Alert open={open} setOpen={setOpen} message={message} severity={severity} />
|
||||
|
||||
{!urls.length ? (
|
||||
<Box
|
||||
display='flex'
|
||||
justifyContent='center'
|
||||
alignItems='center'
|
||||
pt={2}
|
||||
pb={3}
|
||||
>
|
||||
<Typography variant='h4'>No URLs</Typography>
|
||||
</Box>
|
||||
) : <Typography variant='h4'>URLs</Typography>}
|
||||
|
||||
<Grid container spacing={2}>
|
||||
{urls.length ? urls.map(url => (
|
||||
<Grid item xs={12} sm={3} key={url.id}>
|
||||
<Card sx={{ maxWidth: '100%' }}>
|
||||
<CardHeader
|
||||
action={
|
||||
<>
|
||||
<IconButton aria-label='copy' onClick={() => copyURL(url)}>
|
||||
<CopyIcon />
|
||||
</IconButton>
|
||||
<IconButton aria-label='delete' onClick={() => deleteURL(url)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
title={url.vanity ?? url.id}
|
||||
subheader={<Link href={url.destination}>{url.destination}</Link>}
|
||||
/>
|
||||
</Card>
|
||||
</Grid>
|
||||
)) : null}
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -54,7 +54,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
|
||||
|
||||
// @ts-ignore
|
||||
images.map(image => image.url = `/r/${image.file}`);
|
||||
images.map(image => image.url = `${config.uploader.route}/${image.file}`);
|
||||
if (req.query.filter && req.query.filter === 'media') images = images.filter(x => /^(video|audio|image)/.test(x.mimetype));
|
||||
|
||||
return res.json(req.query.paged ? chunk(images, 16) : images);
|
||||
|
|
41
src/pages/api/user/urls.ts
Normal file
41
src/pages/api/user/urls.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { NextApiReq, NextApiRes, withZipline } from 'middleware/withZipline';
|
||||
import prisma from 'lib/prisma';
|
||||
import config from 'lib/config';
|
||||
import Logger from 'lib/logger';
|
||||
|
||||
async function handler(req: NextApiReq, res: NextApiRes) {
|
||||
const user = await req.user();
|
||||
if (!user) return res.forbid('not logged in');
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
if (!req.body.id) return res.error('no url id');
|
||||
|
||||
const url = await prisma.url.delete({
|
||||
where: {
|
||||
id: req.body.id,
|
||||
},
|
||||
});
|
||||
|
||||
Logger.get('url').info(`User ${user.username} (${user.id}) deleted a url ${url.destination} (${url.id})`);
|
||||
|
||||
return res.json(url);
|
||||
} else {
|
||||
let urls = await prisma.url.findMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
select: {
|
||||
created_at: true,
|
||||
id: true,
|
||||
destination: true,
|
||||
vanity: true,
|
||||
},
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
urls.map(url => url.url = `${config.urls.route}/${url.vanity ?? url.id}`);
|
||||
return res.json(urls);
|
||||
}
|
||||
}
|
||||
|
||||
export default withZipline(handler);
|
22
src/pages/dashboard/urls.tsx
Normal file
22
src/pages/dashboard/urls.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import useLogin from 'hooks/useLogin';
|
||||
import Layout from 'components/Layout';
|
||||
import Urls from 'components/pages/Urls';
|
||||
|
||||
export default function UrlsPage() {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
return (
|
||||
<Layout
|
||||
user={user}
|
||||
loading={loading}
|
||||
noPaper={false}
|
||||
>
|
||||
<Urls />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
UrlsPage.title = 'Zipline - URLs';
|
Loading…
Reference in a new issue