feat: file size (#308)
* feat: baseline support for file sizes * feat: script to add file sizes
This commit is contained in:
parent
8e44b71614
commit
912e439645
13 changed files with 460 additions and 6 deletions
|
@ -23,7 +23,8 @@
|
||||||
"scripts:import-dir": "node --enable-source-maps dist/scripts/import-dir",
|
"scripts:import-dir": "node --enable-source-maps dist/scripts/import-dir",
|
||||||
"scripts:list-users": "node --enable-source-maps dist/scripts/list-users",
|
"scripts:list-users": "node --enable-source-maps dist/scripts/list-users",
|
||||||
"scripts:set-user": "node --enable-source-maps dist/scripts/set-user",
|
"scripts:set-user": "node --enable-source-maps dist/scripts/set-user",
|
||||||
"scripts:clear-zero-byte": "node --enable-source-maps dist/scripts/clear-zero-byte"
|
"scripts:clear-zero-byte": "node --enable-source-maps dist/scripts/clear-zero-byte",
|
||||||
|
"scripts:query-size": "node --enable-source-maps dist/scripts/query-size"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
|
|
2
prisma/migrations/20230226051016_file_size/migration.sql
Normal file
2
prisma/migrations/20230226051016_file_size/migration.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "File" ADD COLUMN "size" INTEGER NOT NULL DEFAULT 0;
|
|
@ -53,6 +53,7 @@ model File {
|
||||||
originalName String?
|
originalName String?
|
||||||
mimetype String @default("image/png")
|
mimetype String @default("image/png")
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
size Int @default(0)
|
||||||
expiresAt DateTime?
|
expiresAt DateTime?
|
||||||
maxViews Int?
|
maxViews Int?
|
||||||
views Int @default(0)
|
views Int @default(0)
|
||||||
|
|
398
src/components/File.tsx
Normal file
398
src/components/File.tsx
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Card,
|
||||||
|
Group,
|
||||||
|
LoadingOverlay,
|
||||||
|
Modal,
|
||||||
|
Select,
|
||||||
|
SimpleGrid,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useClipboard } from '@mantine/hooks';
|
||||||
|
import { showNotification } from '@mantine/notifications';
|
||||||
|
import useFetch from 'hooks/useFetch';
|
||||||
|
import { useFileDelete, useFileFavorite } from 'lib/queries/files';
|
||||||
|
import { useFolders } from 'lib/queries/folders';
|
||||||
|
import { bytesToHuman } from 'lib/utils/bytes';
|
||||||
|
import { relativeTime } from 'lib/utils/client';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import {
|
||||||
|
CalendarIcon,
|
||||||
|
ClockIcon,
|
||||||
|
CopyIcon,
|
||||||
|
CrossIcon,
|
||||||
|
DeleteIcon,
|
||||||
|
DownloadIcon,
|
||||||
|
ExternalLinkIcon,
|
||||||
|
EyeIcon,
|
||||||
|
HardDriveIcon,
|
||||||
|
FileIcon,
|
||||||
|
FolderMinusIcon,
|
||||||
|
FolderPlusIcon,
|
||||||
|
HashIcon,
|
||||||
|
ImageIcon,
|
||||||
|
InfoIcon,
|
||||||
|
StarIcon,
|
||||||
|
} from './icons';
|
||||||
|
import MutedText from './MutedText';
|
||||||
|
import Type from './Type';
|
||||||
|
|
||||||
|
export function FileMeta({ Icon, title, subtitle, ...other }) {
|
||||||
|
return other.tooltip ? (
|
||||||
|
<Group>
|
||||||
|
<Icon size={24} />
|
||||||
|
<Tooltip label={other.tooltip}>
|
||||||
|
<Stack spacing={1}>
|
||||||
|
<Text>{title}</Text>
|
||||||
|
<MutedText size='md'>{subtitle}</MutedText>
|
||||||
|
</Stack>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
|
) : (
|
||||||
|
<Group>
|
||||||
|
<Icon size={24} />
|
||||||
|
<Stack spacing={1}>
|
||||||
|
<Text>{title}</Text>
|
||||||
|
<MutedText size='md'>{subtitle}</MutedText>
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function File({
|
||||||
|
image,
|
||||||
|
disableMediaPreview,
|
||||||
|
exifEnabled,
|
||||||
|
refreshImages,
|
||||||
|
reducedActions = false,
|
||||||
|
}) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [overrideRender, setOverrideRender] = useState(false);
|
||||||
|
const deleteFile = useFileDelete();
|
||||||
|
const favoriteFile = useFileFavorite();
|
||||||
|
const clipboard = useClipboard();
|
||||||
|
|
||||||
|
const folders = useFolders();
|
||||||
|
|
||||||
|
const loading = deleteFile.isLoading || favoriteFile.isLoading;
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
deleteFile.mutate(image.id, {
|
||||||
|
onSuccess: () => {
|
||||||
|
showNotification({
|
||||||
|
title: 'File Deleted',
|
||||||
|
message: '',
|
||||||
|
color: 'green',
|
||||||
|
icon: <DeleteIcon />,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onError: (res: any) => {
|
||||||
|
showNotification({
|
||||||
|
title: 'Failed to delete file',
|
||||||
|
message: res.error,
|
||||||
|
color: 'red',
|
||||||
|
icon: <CrossIcon />,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onSettled: () => {
|
||||||
|
setOpen(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
clipboard.copy(`${window.location.protocol}//${window.location.host}${image.url}`);
|
||||||
|
setOpen(false);
|
||||||
|
showNotification({
|
||||||
|
title: 'Copied to clipboard',
|
||||||
|
message: '',
|
||||||
|
icon: <CopyIcon />,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFavorite = async () => {
|
||||||
|
favoriteFile.mutate(
|
||||||
|
{ id: image.id, favorite: !image.favorite },
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
showNotification({
|
||||||
|
title: 'Image is now ' + (!image.favorite ? 'favorited' : 'unfavorited'),
|
||||||
|
message: '',
|
||||||
|
icon: <StarIcon />,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onError: (res: any) => {
|
||||||
|
showNotification({
|
||||||
|
title: 'Failed to favorite file',
|
||||||
|
message: res.error,
|
||||||
|
color: 'red',
|
||||||
|
icon: <CrossIcon />,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const inFolder = image.folderId;
|
||||||
|
|
||||||
|
const refresh = () => {
|
||||||
|
refreshImages();
|
||||||
|
folders.refetch();
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeFromFolder = async () => {
|
||||||
|
const res = await useFetch('/api/user/folders/' + image.folderId, 'DELETE', {
|
||||||
|
file: Number(image.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
if (!res.error) {
|
||||||
|
showNotification({
|
||||||
|
title: 'Removed from folder',
|
||||||
|
message: res.name,
|
||||||
|
color: 'green',
|
||||||
|
icon: <FolderMinusIcon />,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
showNotification({
|
||||||
|
title: 'Failed to remove from folder',
|
||||||
|
message: res.error,
|
||||||
|
color: 'red',
|
||||||
|
icon: <CrossIcon />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addToFolder = async (t) => {
|
||||||
|
const res = await useFetch('/api/user/folders/' + t, 'POST', {
|
||||||
|
file: Number(image.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
if (!res.error) {
|
||||||
|
showNotification({
|
||||||
|
title: 'Added to folder',
|
||||||
|
message: res.name,
|
||||||
|
color: 'green',
|
||||||
|
icon: <FolderPlusIcon />,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
showNotification({
|
||||||
|
title: 'Failed to add to folder',
|
||||||
|
message: res.error,
|
||||||
|
color: 'red',
|
||||||
|
icon: <CrossIcon />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createFolder = (t) => {
|
||||||
|
useFetch('/api/user/folders', 'POST', {
|
||||||
|
name: t,
|
||||||
|
add: [Number(image.id)],
|
||||||
|
}).then((res) => {
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
if (!res.error) {
|
||||||
|
showNotification({
|
||||||
|
title: 'Created & added to folder',
|
||||||
|
message: res.name,
|
||||||
|
color: 'green',
|
||||||
|
icon: <FolderPlusIcon />,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
showNotification({
|
||||||
|
title: 'Failed to create folder',
|
||||||
|
message: res.error,
|
||||||
|
color: 'red',
|
||||||
|
icon: <CrossIcon />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { value: t, label: t };
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal opened={open} onClose={() => setOpen(false)} title={<Title>{image.name}</Title>} size='xl'>
|
||||||
|
<LoadingOverlay visible={loading} />
|
||||||
|
<Stack>
|
||||||
|
<Type
|
||||||
|
file={image}
|
||||||
|
src={`/r/${encodeURI(image.name)}`}
|
||||||
|
alt={image.name}
|
||||||
|
popup
|
||||||
|
sx={{ minHeight: 200 }}
|
||||||
|
style={{ minHeight: 200 }}
|
||||||
|
disableMediaPreview={false}
|
||||||
|
overrideRender={overrideRender}
|
||||||
|
setOverrideRender={setOverrideRender}
|
||||||
|
/>
|
||||||
|
<SimpleGrid
|
||||||
|
my='md'
|
||||||
|
cols={3}
|
||||||
|
breakpoints={[
|
||||||
|
{ maxWidth: 600, cols: 1 },
|
||||||
|
{ maxWidth: 900, cols: 2 },
|
||||||
|
{ maxWidth: 1200, cols: 3 },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<FileMeta Icon={FileIcon} title='Name' subtitle={image.name} />
|
||||||
|
<FileMeta Icon={ImageIcon} title='Type' subtitle={image.mimetype} />
|
||||||
|
<FileMeta Icon={HardDriveIcon} title='Size' subtitle={bytesToHuman(image.size || 0)} />
|
||||||
|
<FileMeta Icon={EyeIcon} title='Views' subtitle={image?.views?.toLocaleString()} />
|
||||||
|
{image.maxViews && (
|
||||||
|
<FileMeta
|
||||||
|
Icon={EyeIcon}
|
||||||
|
title='Max views'
|
||||||
|
subtitle={image?.maxViews?.toLocaleString()}
|
||||||
|
tooltip={`This file will be deleted after being viewed ${image?.maxViews?.toLocaleString()} times.`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<FileMeta
|
||||||
|
Icon={CalendarIcon}
|
||||||
|
title='Uploaded'
|
||||||
|
subtitle={relativeTime(new Date(image.createdAt))}
|
||||||
|
tooltip={new Date(image?.createdAt).toLocaleString()}
|
||||||
|
/>
|
||||||
|
{image.expiresAt && !reducedActions && (
|
||||||
|
<FileMeta
|
||||||
|
Icon={ClockIcon}
|
||||||
|
title='Expires'
|
||||||
|
subtitle={relativeTime(new Date(image.expiresAt))}
|
||||||
|
tooltip={new Date(image.expiresAt).toLocaleString()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<FileMeta Icon={HashIcon} title='ID' subtitle={image.id} />
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Group position='apart' my='md'>
|
||||||
|
<Group position='left'>
|
||||||
|
{exifEnabled && !reducedActions && (
|
||||||
|
<Tooltip label='View Metadata'>
|
||||||
|
<ActionIcon
|
||||||
|
color='blue'
|
||||||
|
variant='filled'
|
||||||
|
onClick={() => window.open(`/dashboard/metadata/${image.id}`, '_blank')}
|
||||||
|
>
|
||||||
|
<InfoIcon />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{reducedActions ? null : inFolder && !folders.isLoading ? (
|
||||||
|
<Tooltip
|
||||||
|
label={`Remove from folder "${
|
||||||
|
folders.data.find((f) => f.id === image.folderId)?.name ?? ''
|
||||||
|
}"`}
|
||||||
|
>
|
||||||
|
<ActionIcon
|
||||||
|
color='red'
|
||||||
|
variant='filled'
|
||||||
|
onClick={removeFromFolder}
|
||||||
|
loading={folders.isLoading}
|
||||||
|
>
|
||||||
|
<FolderMinusIcon />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<Tooltip label='Add to folder'>
|
||||||
|
<Select
|
||||||
|
onChange={addToFolder}
|
||||||
|
placeholder='Add to folder'
|
||||||
|
data={[
|
||||||
|
...(folders.data ? folders.data : []).map((folder) => ({
|
||||||
|
value: String(folder.id),
|
||||||
|
label: `${folder.id}: ${folder.name}`,
|
||||||
|
})),
|
||||||
|
]}
|
||||||
|
searchable
|
||||||
|
creatable
|
||||||
|
getCreateLabel={(query) => `Create folder "${query}"`}
|
||||||
|
onCreate={createFolder}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
<Group position='right'>
|
||||||
|
{reducedActions ? null : (
|
||||||
|
<>
|
||||||
|
<Tooltip label='Delete file'>
|
||||||
|
<ActionIcon color='red' variant='filled' onClick={handleDelete}>
|
||||||
|
<DeleteIcon />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip label={image.favorite ? 'Unfavorite' : 'Favorite'}>
|
||||||
|
<ActionIcon
|
||||||
|
color={image.favorite ? 'yellow' : 'gray'}
|
||||||
|
variant='filled'
|
||||||
|
onClick={handleFavorite}
|
||||||
|
>
|
||||||
|
<StarIcon />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Tooltip label='Open in new tab'>
|
||||||
|
<ActionIcon color='blue' variant='filled' onClick={() => window.open(image.url, '_blank')}>
|
||||||
|
<ExternalLinkIcon />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip label='Copy URL'>
|
||||||
|
<ActionIcon color='blue' variant='filled' onClick={handleCopy}>
|
||||||
|
<CopyIcon />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip label='Download'>
|
||||||
|
<ActionIcon
|
||||||
|
color='blue'
|
||||||
|
variant='filled'
|
||||||
|
onClick={() => window.open(`/r/${encodeURI(image.name)}?download=true`, '_blank')}
|
||||||
|
>
|
||||||
|
<DownloadIcon />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Modal>
|
||||||
|
<Card sx={{ maxWidth: '100%', height: '100%' }} shadow='md'>
|
||||||
|
<Card.Section>
|
||||||
|
<LoadingOverlay visible={loading} />
|
||||||
|
<Type
|
||||||
|
file={image}
|
||||||
|
sx={{
|
||||||
|
minHeight: 200,
|
||||||
|
maxHeight: 320,
|
||||||
|
fontSize: 70,
|
||||||
|
width: '100%',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
minHeight: 200,
|
||||||
|
maxHeight: 320,
|
||||||
|
fontSize: 70,
|
||||||
|
width: '100%',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
src={`/r/${encodeURI(image.name)}`}
|
||||||
|
alt={image.name}
|
||||||
|
onClick={() => setOpen(true)}
|
||||||
|
disableMediaPreview={disableMediaPreview}
|
||||||
|
/>
|
||||||
|
</Card.Section>
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
5
src/components/icons/HardDriveIcon.tsx
Normal file
5
src/components/icons/HardDriveIcon.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { HardDrive } from 'react-feather';
|
||||||
|
|
||||||
|
export default function HardDriveIcon({ ...props }) {
|
||||||
|
return <HardDrive size={15} {...props} />;
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ import FolderPlusIcon from './FolderPlusIcon';
|
||||||
import GlobeIcon from './GlobeIcon';
|
import GlobeIcon from './GlobeIcon';
|
||||||
import LockIcon from './LockIcon';
|
import LockIcon from './LockIcon';
|
||||||
import UnlockIcon from './UnlockIcon';
|
import UnlockIcon from './UnlockIcon';
|
||||||
|
import HardDriveIcon from './HardDriveIcon';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ActivityIcon,
|
ActivityIcon,
|
||||||
|
@ -86,4 +87,5 @@ export {
|
||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
LockIcon,
|
LockIcon,
|
||||||
UnlockIcon,
|
UnlockIcon,
|
||||||
|
HardDriveIcon,
|
||||||
};
|
};
|
||||||
|
|
|
@ -299,6 +299,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
||||||
expiresAt: expiry,
|
expiresAt: expiry,
|
||||||
maxViews: fileMaxViews,
|
maxViews: fileMaxViews,
|
||||||
originalName: req.headers['original-name'] ? file.originalname ?? null : null,
|
originalName: req.headers['original-name'] ? file.originalname ?? null : null,
|
||||||
|
size: file.size,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
expiresAt: Date;
|
expiresAt: Date;
|
||||||
maxViews: number;
|
maxViews: number;
|
||||||
views: number;
|
views: number;
|
||||||
|
size: number;
|
||||||
}[] = await prisma.file.findMany({
|
}[] = await prisma.file.findMany({
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
@ -99,6 +100,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
favorite: true,
|
favorite: true,
|
||||||
views: true,
|
views: true,
|
||||||
maxViews: true,
|
maxViews: true,
|
||||||
|
size: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
maxViews: number;
|
maxViews: number;
|
||||||
views: number;
|
views: number;
|
||||||
folderId: number;
|
folderId: number;
|
||||||
|
size: number;
|
||||||
password: string | boolean;
|
password: string | boolean;
|
||||||
}[] = await prisma.file.findMany({
|
}[] = await prisma.file.findMany({
|
||||||
where,
|
where,
|
||||||
|
@ -74,6 +75,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
views: true,
|
views: true,
|
||||||
maxViews: true,
|
maxViews: true,
|
||||||
folderId: true,
|
folderId: true,
|
||||||
|
size: true,
|
||||||
password: true,
|
password: true,
|
||||||
},
|
},
|
||||||
skip: page ? (Number(page) - 1) * pageCount : undefined,
|
skip: page ? (Number(page) - 1) * pageCount : undefined,
|
||||||
|
|
|
@ -25,6 +25,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
views: true,
|
views: true,
|
||||||
maxViews: true,
|
maxViews: true,
|
||||||
folderId: true,
|
folderId: true,
|
||||||
|
size: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
38
src/scripts/query-size.ts
Normal file
38
src/scripts/query-size.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import config from 'lib/config';
|
||||||
|
import datasource from 'lib/datasource';
|
||||||
|
import { migrations } from 'server/util';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
process.env.DATABASE_URL = config.core.database_url;
|
||||||
|
await migrations();
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
const files = await prisma.file.findMany();
|
||||||
|
|
||||||
|
console.log(`The script will attempt to query the size of ${files.length} files.`);
|
||||||
|
|
||||||
|
for (let i = 0; i !== files.length; ++i) {
|
||||||
|
const file = files[i];
|
||||||
|
const size = await datasource.size(file.name);
|
||||||
|
if (size === 0) {
|
||||||
|
console.log(`File ${file.name} has a size of 0 bytes. Ignoring...`);
|
||||||
|
} else {
|
||||||
|
console.log(`File ${file.name} has a size of ${size} bytes. Updating...`);
|
||||||
|
await prisma.file.update({
|
||||||
|
where: {
|
||||||
|
id: file.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Done.');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
|
@ -25,10 +25,6 @@
|
||||||
},
|
},
|
||||||
"incremental": true
|
"incremental": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||||
"next-env.d.ts",
|
|
||||||
"**/*.ts",
|
|
||||||
"**/*.tsx",
|
|
||||||
],
|
|
||||||
"exclude": ["node_modules", "dist", ".yarn", ".next"]
|
"exclude": ["node_modules", "dist", ".yarn", ".next"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,4 +39,9 @@ export default defineConfig([
|
||||||
outDir: 'dist/scripts',
|
outDir: 'dist/scripts',
|
||||||
...opts,
|
...opts,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
entryPoints: ['src/scripts/query-size.ts'],
|
||||||
|
outDir: 'dist/scripts',
|
||||||
|
...opts,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
Loading…
Reference in a new issue