feat: public folders
This commit is contained in:
parent
6955d83b0c
commit
fc02dc02e8
11 changed files with 255 additions and 71 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Folder" ADD COLUMN "public" BOOLEAN NOT NULL DEFAULT false;
|
|
@ -30,6 +30,7 @@ model User {
|
||||||
model Folder {
|
model Folder {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String
|
name String
|
||||||
|
public Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
Group,
|
Group,
|
||||||
LoadingOverlay,
|
LoadingOverlay,
|
||||||
Modal,
|
Modal,
|
||||||
Paper,
|
|
||||||
Select,
|
Select,
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
Stack,
|
Stack,
|
||||||
|
@ -15,10 +14,9 @@ import {
|
||||||
import { useClipboard } from '@mantine/hooks';
|
import { useClipboard } from '@mantine/hooks';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import { invalidateFiles, useFileDelete, useFileFavorite } from 'lib/queries/files';
|
import { useFileDelete, useFileFavorite } from 'lib/queries/files';
|
||||||
import { invalidateFolders, useFolders } from 'lib/queries/folders';
|
import { useFolders } from 'lib/queries/folders';
|
||||||
import { relativeTime } from 'lib/utils/client';
|
import { relativeTime } from 'lib/utils/client';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
CalendarIcon,
|
CalendarIcon,
|
||||||
|
@ -30,12 +28,12 @@ import {
|
||||||
ExternalLinkIcon,
|
ExternalLinkIcon,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
FileIcon,
|
FileIcon,
|
||||||
HashIcon,
|
|
||||||
ImageIcon,
|
|
||||||
StarIcon,
|
|
||||||
InfoIcon,
|
|
||||||
FolderMinusIcon,
|
FolderMinusIcon,
|
||||||
FolderPlusIcon,
|
FolderPlusIcon,
|
||||||
|
HashIcon,
|
||||||
|
ImageIcon,
|
||||||
|
InfoIcon,
|
||||||
|
StarIcon,
|
||||||
} from './icons';
|
} from './icons';
|
||||||
import MutedText from './MutedText';
|
import MutedText from './MutedText';
|
||||||
import Type from './Type';
|
import Type from './Type';
|
||||||
|
@ -62,13 +60,18 @@ export function FileMeta({ Icon, title, subtitle, ...other }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function File({ image, disableMediaPreview, exifEnabled, refreshImages }) {
|
export default function File({
|
||||||
|
image,
|
||||||
|
disableMediaPreview,
|
||||||
|
exifEnabled,
|
||||||
|
refreshImages,
|
||||||
|
reducedActions = false,
|
||||||
|
}) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [overrideRender, setOverrideRender] = useState(false);
|
const [overrideRender, setOverrideRender] = useState(false);
|
||||||
const deleteFile = useFileDelete();
|
const deleteFile = useFileDelete();
|
||||||
const favoriteFile = useFileFavorite();
|
const favoriteFile = useFileFavorite();
|
||||||
const clipboard = useClipboard();
|
const clipboard = useClipboard();
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const folders = useFolders();
|
const folders = useFolders();
|
||||||
|
|
||||||
|
@ -257,7 +260,7 @@ export default function File({ image, disableMediaPreview, exifEnabled, refreshI
|
||||||
subtitle={relativeTime(new Date(image.createdAt))}
|
subtitle={relativeTime(new Date(image.createdAt))}
|
||||||
tooltip={new Date(image?.createdAt).toLocaleString()}
|
tooltip={new Date(image?.createdAt).toLocaleString()}
|
||||||
/>
|
/>
|
||||||
{image.expiresAt && (
|
{image.expiresAt && !reducedActions && (
|
||||||
<FileMeta
|
<FileMeta
|
||||||
Icon={ClockIcon}
|
Icon={ClockIcon}
|
||||||
title='Expires'
|
title='Expires'
|
||||||
|
@ -271,7 +274,7 @@ export default function File({ image, disableMediaPreview, exifEnabled, refreshI
|
||||||
|
|
||||||
<Group position='apart' my='md'>
|
<Group position='apart' my='md'>
|
||||||
<Group position='left'>
|
<Group position='left'>
|
||||||
{exifEnabled && (
|
{exifEnabled && !reducedActions && (
|
||||||
<Tooltip label='View Metadata'>
|
<Tooltip label='View Metadata'>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
color='blue'
|
color='blue'
|
||||||
|
@ -282,7 +285,7 @@ export default function File({ image, disableMediaPreview, exifEnabled, refreshI
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{inFolder && !folders.isLoading ? (
|
{reducedActions ? null : inFolder && !folders.isLoading ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
label={`Remove from folder "${
|
label={`Remove from folder "${
|
||||||
folders.data.find((f) => f.id === image.folderId)?.name ?? ''
|
folders.data.find((f) => f.id === image.folderId)?.name ?? ''
|
||||||
|
@ -317,6 +320,8 @@ export default function File({ image, disableMediaPreview, exifEnabled, refreshI
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
<Group position='right'>
|
<Group position='right'>
|
||||||
|
{reducedActions ? null : (
|
||||||
|
<>
|
||||||
<Tooltip label='Delete file'>
|
<Tooltip label='Delete file'>
|
||||||
<ActionIcon color='red' variant='filled' onClick={handleDelete}>
|
<ActionIcon color='red' variant='filled' onClick={handleDelete}>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
|
@ -332,6 +337,8 @@ export default function File({ image, disableMediaPreview, exifEnabled, refreshI
|
||||||
<StarIcon />
|
<StarIcon />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<Tooltip label='Open in new tab'>
|
<Tooltip label='Open in new tab'>
|
||||||
<ActionIcon color='blue' variant='filled' onClick={() => window.open(image.url, '_blank')}>
|
<ActionIcon color='blue' variant='filled' onClick={() => window.open(image.url, '_blank')}>
|
||||||
|
|
5
src/components/icons/LockIcon.tsx
Normal file
5
src/components/icons/LockIcon.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { Lock } from 'react-feather';
|
||||||
|
|
||||||
|
export default function LockIcon({ ...props }) {
|
||||||
|
return <Lock size={15} {...props} />;
|
||||||
|
}
|
5
src/components/icons/UnlockIcon.tsx
Normal file
5
src/components/icons/UnlockIcon.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { Unlock } from 'react-feather';
|
||||||
|
|
||||||
|
export default function UnlockIcon({ ...props }) {
|
||||||
|
return <Unlock size={15} {...props} />;
|
||||||
|
}
|
|
@ -39,6 +39,8 @@ import FolderIcon from './FolderIcon';
|
||||||
import FolderMinusIcon from './FolderMinusIcon';
|
import FolderMinusIcon from './FolderMinusIcon';
|
||||||
import FolderPlusIcon from './FolderPlusIcon';
|
import FolderPlusIcon from './FolderPlusIcon';
|
||||||
import GlobeIcon from './GlobeIcon';
|
import GlobeIcon from './GlobeIcon';
|
||||||
|
import LockIcon from './LockIcon';
|
||||||
|
import UnlockIcon from './UnlockIcon';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ActivityIcon,
|
ActivityIcon,
|
||||||
|
@ -82,4 +84,6 @@ export {
|
||||||
FolderMinusIcon,
|
FolderMinusIcon,
|
||||||
FolderPlusIcon,
|
FolderPlusIcon,
|
||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
|
LockIcon,
|
||||||
|
UnlockIcon,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,8 @@ import { ActionIcon, Avatar, Card, Group, SimpleGrid, Skeleton, Stack, Title, To
|
||||||
import { useClipboard } from '@mantine/hooks';
|
import { useClipboard } from '@mantine/hooks';
|
||||||
import { useModals } from '@mantine/modals';
|
import { useModals } from '@mantine/modals';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { CopyIcon, DeleteIcon, FileIcon, PlusIcon } from 'components/icons';
|
import { DeleteIcon, FileIcon, PlusIcon, LockIcon, UnlockIcon, LinkIcon, CopyIcon } from 'components/icons';
|
||||||
|
import Link from 'components/Link';
|
||||||
import MutedText from 'components/MutedText';
|
import MutedText from 'components/MutedText';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import { useFolders } from 'lib/queries/folders';
|
import { useFolders } from 'lib/queries/folders';
|
||||||
|
@ -65,6 +66,30 @@ export default function Folders({ disableMediaPreview, exifEnabled }) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const makePublic = async (folder) => {
|
||||||
|
const res = await useFetch(`/api/user/folders/${folder.id}`, 'PATCH', {
|
||||||
|
public: folder.public ? false : true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.error) {
|
||||||
|
showNotification({
|
||||||
|
title: 'Made folder public',
|
||||||
|
message: `Made folder ${folder.name} ${folder.public ? 'private' : 'public'}`,
|
||||||
|
color: 'green',
|
||||||
|
icon: <UnlockIcon />,
|
||||||
|
});
|
||||||
|
folders.refetch();
|
||||||
|
} else {
|
||||||
|
showNotification({
|
||||||
|
title: 'Failed to make folder public/private',
|
||||||
|
message: res.error,
|
||||||
|
color: 'red',
|
||||||
|
icon: <UnlockIcon />,
|
||||||
|
});
|
||||||
|
folders.refetch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CreateFolderModal
|
<CreateFolderModal
|
||||||
|
@ -101,6 +126,7 @@ export default function Folders({ disableMediaPreview, exifEnabled }) {
|
||||||
<Stack spacing={0}>
|
<Stack spacing={0}>
|
||||||
<Title>{folder.name}</Title>
|
<Title>{folder.name}</Title>
|
||||||
<MutedText size='sm'>ID: {folder.id}</MutedText>
|
<MutedText size='sm'>ID: {folder.id}</MutedText>
|
||||||
|
<MutedText size='sm'>Public: {folder.public ? 'Yes' : 'No'}</MutedText>
|
||||||
<Tooltip label={new Date(folder.createdAt).toLocaleString()}>
|
<Tooltip label={new Date(folder.createdAt).toLocaleString()}>
|
||||||
<div>
|
<div>
|
||||||
<MutedText size='sm'>
|
<MutedText size='sm'>
|
||||||
|
@ -117,7 +143,15 @@ export default function Folders({ disableMediaPreview, exifEnabled }) {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
<Stack>
|
<Group>
|
||||||
|
<Tooltip label={folder.public ? 'Make folder private' : 'Make folder public'}>
|
||||||
|
<ActionIcon
|
||||||
|
aria-label={folder.public ? 'make private' : 'make public'}
|
||||||
|
onClick={() => makePublic(folder)}
|
||||||
|
>
|
||||||
|
{folder.public ? <LockIcon /> : <UnlockIcon />}
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
aria-label='view files'
|
aria-label='view files'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -127,10 +161,28 @@ export default function Folders({ disableMediaPreview, exifEnabled }) {
|
||||||
>
|
>
|
||||||
<FileIcon />
|
<FileIcon />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
|
<ActionIcon
|
||||||
|
aria-label='copy link'
|
||||||
|
onClick={() => {
|
||||||
|
clipboard.copy(`${window.location.origin}/folder/${folder.id}`);
|
||||||
|
showNotification({
|
||||||
|
title: 'Copied folder link',
|
||||||
|
message: (
|
||||||
|
<>
|
||||||
|
Copied <Link href={`/folder/${folder.id}`}>folder link</Link> to clipboard
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
color: 'green',
|
||||||
|
icon: <CopyIcon />,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LinkIcon />
|
||||||
|
</ActionIcon>
|
||||||
<ActionIcon aria-label='delete' onClick={() => deleteFolder(folder)}>
|
<ActionIcon aria-label='delete' onClick={() => deleteFolder(folder)}>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Stack>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</Card>
|
</Card>
|
||||||
))
|
))
|
||||||
|
|
|
@ -8,6 +8,7 @@ export type UserFoldersResponse = {
|
||||||
userId: number;
|
userId: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
public: boolean;
|
||||||
files?: UserFilesResponse[];
|
files?: UserFilesResponse[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,46 +41,6 @@ export const useFolder = (id: string, withFiles: boolean = false) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// export function useFileDelete() {
|
|
||||||
// // '/api/user/files', 'DELETE', { id: image.id }
|
|
||||||
// return useMutation(
|
|
||||||
// async (id: string) => {
|
|
||||||
// return fetch('/api/user/files', {
|
|
||||||
// method: 'DELETE',
|
|
||||||
// body: JSON.stringify({ id }),
|
|
||||||
// headers: {
|
|
||||||
// 'content-type': 'application/json',
|
|
||||||
// },
|
|
||||||
// }).then((res) => res.json());
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// onSuccess: () => {
|
|
||||||
// queryClient.refetchQueries(['files']);
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function useFileFavorite() {
|
|
||||||
// // /api/user/files', 'PATCH', { id: image.id, favorite: !image.favorite }
|
|
||||||
// return useMutation(
|
|
||||||
// async (data: { id: string; favorite: boolean }) => {
|
|
||||||
// return fetch('/api/user/files', {
|
|
||||||
// method: 'PATCH',
|
|
||||||
// body: JSON.stringify(data),
|
|
||||||
// headers: {
|
|
||||||
// 'content-type': 'application/json',
|
|
||||||
// },
|
|
||||||
// }).then((res) => res.json());
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// onSuccess: () => {
|
|
||||||
// queryClient.refetchQueries(['files']);
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
export function invalidateFolders() {
|
export function invalidateFolders() {
|
||||||
return queryClient.invalidateQueries(['folders']);
|
return queryClient.invalidateQueries(['folders']);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
userId: true,
|
userId: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
|
public: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
userId: true,
|
userId: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
|
public: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -96,6 +98,40 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res.json(folder);
|
||||||
|
} else if (req.method === 'PATCH') {
|
||||||
|
const { public: publicFolder } = req.body as { public?: string };
|
||||||
|
|
||||||
|
const folder = await prisma.folder.update({
|
||||||
|
where: {
|
||||||
|
id: idParsed,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
public: !!publicFolder,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
files: !!req.query.files,
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
userId: true,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true,
|
||||||
|
public: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (req.query.files) {
|
||||||
|
for (let i = 0; i !== folder.files.length; ++i) {
|
||||||
|
const file = folder.files[i];
|
||||||
|
delete file.password;
|
||||||
|
|
||||||
|
(folder.files[i] as unknown as { url: string }).url = formatRootUrl(
|
||||||
|
config.uploader.route,
|
||||||
|
folder.files[i].name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res.json(folder);
|
return res.json(folder);
|
||||||
} else if (req.method === 'DELETE') {
|
} else if (req.method === 'DELETE') {
|
||||||
const deletingFolder = !!req.body.deleteFolder;
|
const deletingFolder = !!req.body.deleteFolder;
|
||||||
|
@ -111,6 +147,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
userId: true,
|
userId: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
|
public: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -167,6 +204,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
userId: true,
|
userId: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
|
public: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -208,6 +246,6 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withZipline(handler, {
|
export default withZipline(handler, {
|
||||||
methods: ['GET', 'POST', 'DELETE'],
|
methods: ['GET', 'POST', 'DELETE', 'PATCH'],
|
||||||
user: true,
|
user: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,6 +69,10 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
userId: true,
|
userId: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
|
public: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
updatedAt: 'desc',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
105
src/pages/folder/[id].tsx
Normal file
105
src/pages/folder/[id].tsx
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import { Container, SimpleGrid, Title } from '@mantine/core';
|
||||||
|
import File from 'components/File';
|
||||||
|
import prisma from 'lib/prisma';
|
||||||
|
import { formatRootUrl } from 'lib/utils/urls';
|
||||||
|
import { GetServerSideProps } from 'next';
|
||||||
|
|
||||||
|
type LimitedFolder = {
|
||||||
|
files: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
createdAt: Date | string;
|
||||||
|
mimetype: string;
|
||||||
|
views: number;
|
||||||
|
}[];
|
||||||
|
user: {
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
name: string;
|
||||||
|
public: boolean;
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
folder: LimitedFolder;
|
||||||
|
uploadRoute: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function EmbeddedFile({ folder }: Props) {
|
||||||
|
return (
|
||||||
|
<Container size='lg'>
|
||||||
|
<Title align='center' my='lg'>
|
||||||
|
Viewing folder: {folder.name}
|
||||||
|
</Title>
|
||||||
|
<SimpleGrid
|
||||||
|
my='md'
|
||||||
|
cols={3}
|
||||||
|
breakpoints={[
|
||||||
|
{ maxWidth: 600, cols: 1 },
|
||||||
|
{ maxWidth: 900, cols: 2 },
|
||||||
|
{ maxWidth: 1200, cols: 3 },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{folder.files.map((file, i) => (
|
||||||
|
<File
|
||||||
|
key={i}
|
||||||
|
image={file}
|
||||||
|
disableMediaPreview={false}
|
||||||
|
exifEnabled={false}
|
||||||
|
refreshImages={null}
|
||||||
|
reducedActions={true}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
|
const { id } = context.params as { id: string };
|
||||||
|
|
||||||
|
if (isNaN(Number(id))) return { notFound: true };
|
||||||
|
|
||||||
|
const folder = await prisma.folder.findFirst({
|
||||||
|
where: {
|
||||||
|
id: Number(id),
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
files: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
mimetype: true,
|
||||||
|
id: true,
|
||||||
|
views: true,
|
||||||
|
createdAt: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
name: true,
|
||||||
|
public: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!folder) return { notFound: true };
|
||||||
|
if (!folder.public) return { notFound: true };
|
||||||
|
|
||||||
|
for (let j = 0; j !== folder.files.length; ++j) {
|
||||||
|
(folder.files[j] as unknown as { url: string }).url = formatRootUrl(
|
||||||
|
config.uploader.route,
|
||||||
|
folder.files[j].name
|
||||||
|
);
|
||||||
|
|
||||||
|
(folder.files[j].createdAt as unknown) = folder.files[j].createdAt.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
folder,
|
||||||
|
uploadRoute: config.uploader.route,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in a new issue