Skip to content

Commit

Permalink
feat: add filter by username for public pastes (#264)
Browse files Browse the repository at this point in the history
  • Loading branch information
bjarneo authored Jan 29, 2024
1 parent e395fd4 commit 115726e
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 20 deletions.
4 changes: 3 additions & 1 deletion public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@
"public": {
"heading": "Public pastes",
"title": "Title",
"expires": "Expires"
"expires": "Expires",
"username": "Username",
"of": "of"
}
}
4 changes: 2 additions & 2 deletions src/client/api/secret.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ export const getSecrets = async () => {
return data.json();
};

export const getPublicSecrets = async () => {
const data = await fetch(`${config.get('api.host')}/secret/public`, {
export const getPublicSecrets = async (username = '') => {
const data = await fetch(`${config.get('api.host')}/secret/public/${username}`, {
method: 'GET',
cache: 'no-cache',
headers: {
Expand Down
9 changes: 9 additions & 0 deletions src/client/routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ const appRouter = createBrowserRouter(
return await getPublicSecrets();
}}
/>
<Route
element={<PublicSecrets />}
path="public/:username"
loader={async ({ params }) => {
const { getPublicSecrets } = await import('./api/secret');

return await getPublicSecrets(params?.username);
}}
/>
<Route path="signin" element={<SignIn />} />
<Route path="signup" element={<SignUp />} />
<Route path="signout" element={<SignOut />} />
Expand Down
4 changes: 3 additions & 1 deletion src/client/routes/account/secrets.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ const Secrets = () => {

const rows = secrets.map((secret) => (
<tr key={secret.id}>
<td>{secret.id}</td>
<td>{secret.isPublic ? secret.title : secret.id}</td>
<td>{getTime(secret.expiresAt)}</td>
<td>{secret.isPublic ? 'Yes' : 'No'}</td>
<td>
<ActionIcon variant="filled" onClick={() => openDeleteModal(secret)}>
<IconTrash size="1rem" />
Expand Down Expand Up @@ -143,6 +144,7 @@ const Secrets = () => {
<tr>
<th>Id</th>
<th>Expires</th>
<th>Public</th>
<th>Delete</th>
</tr>
</thead>
Expand Down
17 changes: 14 additions & 3 deletions src/client/routes/public/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import { Anchor, Container, Group, Stack, Table, Title } from '@mantine/core';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { useTranslation } from 'react-i18next';
import { Link, useLoaderData } from 'react-router-dom';
import { Link, useLoaderData, useParams } from 'react-router-dom';

dayjs.extend(relativeTime);

const PublicSecrets = () => {
const { t } = useTranslation();

const secrets = useLoaderData();

const { username = '' } = useParams();
console.log('username', username);
const getTime = (expiresAt) => {
return dayjs().to(dayjs(expiresAt));
};
Expand All @@ -28,6 +29,15 @@ const PublicSecrets = () => {
: secret.title) || secret.id}
</Anchor>
</td>
<td>
<Anchor
color="gray"
component={Link}
to={`/public/${secret.user?.username ?? 1337}`}
>
{secret.user?.username ?? 'Anonymous'}
</Anchor>
</td>
<td>{getTime(secret.expiresAt)}</td>
</tr>
));
Expand All @@ -36,7 +46,7 @@ const PublicSecrets = () => {
<Container>
<Stack>
<Title order={1} size="h2" align="center">
{t('public.heading')}
{t('public.heading')} {username && `${t('public.of')} ${username}`}
</Title>

<Group position="left">
Expand All @@ -45,6 +55,7 @@ const PublicSecrets = () => {
<tr>
<th>{t('public.title')}</th>
<th>{t('public.expires')}</th>
<th>{t('public.username')}</th>
</tr>
</thead>
<tbody>{rows}</tbody>
Expand Down
2 changes: 1 addition & 1 deletion src/server/controllers/authentication.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import prisma from '../services/prisma.js';

import { compare, hash } from '../helpers/password.js';

const validUsername = /^[A-Za-z0-9_-]*$/is;
export const validUsername = /^(?=.*[a-z])[a-z0-9]+$/is;

const PASSWORD_LENGTH = 5;
const USERNAME_LENGTH = 4;
Expand Down
49 changes: 37 additions & 12 deletions src/server/controllers/secret.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import validator from 'validator';
import getClientIp from '../helpers/client-ip.js';
import { compare, hash } from '../helpers/password.js';
import prisma from '../services/prisma.js';
import { validUsername } from './authentication.js';

import { isValidSecretId } from '../helpers/regexp.js';

Expand Down Expand Up @@ -108,9 +109,14 @@ async function secret(fastify) {
include: { files: true },
});

return reply
.code(200)
.send(secrets.map((secret) => ({ id: secret.id, expiresAt: secret.expiresAt })));
return reply.code(200).send(
secrets.map((secret) => ({
id: secret.id,
expiresAt: secret.expiresAt,
isPublic: secret.isPublic,
title: secret.title,
}))
);
}
);

Expand Down Expand Up @@ -226,26 +232,45 @@ async function secret(fastify) {
return { id, maxViews: data.maxViews };
});

fastify.get('/public', async (_, reply) => {
async function getPublicRoute(request, reply) {
const { username } = request.params;

const where = { isPublic: true };

if (username && !validUsername.test(username)) {
return reply.code(403).send({ error: 'Not a valid username' });
} else {
where.user = { username };
}

const data = await prisma.secret.findMany({
where: { isPublic: true },
where,
orderBy: {
createdAt: 'desc',
},
take: 100,
select: {
id: true,
expiresAt: true,
title: true,
createdAt: true,
user: {
select: {
username: true,
},
},
},
});

if (!data?.length) {
return reply.code(204).send([]);
}

return data.map((secret) => ({
id: secret.id,
expiresAt: secret.expiresAt,
title: secret.title,
createdAt: secret.createdAt,
}));
});
return data;
}

fastify.get('/public/', getPublicRoute);
fastify.get('/public/:username', getPublicRoute);
}

export default secret;

0 comments on commit 115726e

Please sign in to comment.