Merge branch 'trunk' into feature/oauth-authentik
This commit is contained in:
commit
53d66596db
11 changed files with 86 additions and 43 deletions
|
@ -23,8 +23,8 @@ services:
|
|||
env_file:
|
||||
- .env.local
|
||||
volumes:
|
||||
- '$PWD/uploads:/zipline/uploads'
|
||||
- '$PWD/public:/zipline/public'
|
||||
- './uploads:/zipline/uploads'
|
||||
- './public:/zipline/public'
|
||||
depends_on:
|
||||
- 'postgres'
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ services:
|
|||
- CORE_LOGGER=true
|
||||
volumes:
|
||||
- './uploads:/zipline/uploads'
|
||||
- '$PWD/public:/zipline/public'
|
||||
- './public:/zipline/public'
|
||||
depends_on:
|
||||
- 'postgres'
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
import FileModal from 'components/File/FileModal';
|
||||
import MutedText from 'components/MutedText';
|
||||
import useFetch from 'lib/hooks/useFetch';
|
||||
import { usePaginatedFiles, useRecent } from 'lib/queries/files';
|
||||
import { PaginatedFilesOptions, usePaginatedFiles, useRecent } from 'lib/queries/files';
|
||||
import { useStats } from 'lib/queries/stats';
|
||||
import { userSelector } from 'lib/recoil/user';
|
||||
import { bytesToHuman } from 'lib/utils/bytes';
|
||||
|
@ -45,32 +45,24 @@ export default function Dashboard({ disableMediaPreview, exifEnabled, compress }
|
|||
})();
|
||||
}, [page]);
|
||||
|
||||
const files = usePaginatedFiles(page, 'none');
|
||||
|
||||
// sorting
|
||||
const [sortStatus, setSortStatus] = useState<DataTableSortStatus>({
|
||||
columnAccessor: 'date',
|
||||
columnAccessor: 'createdAt',
|
||||
direction: 'asc',
|
||||
});
|
||||
const [records, setRecords] = useState(files.data);
|
||||
|
||||
useEffect(() => {
|
||||
setRecords(files.data);
|
||||
}, [files.data]);
|
||||
const files = usePaginatedFiles(page, {
|
||||
filter: 'none',
|
||||
|
||||
useEffect(() => {
|
||||
if (!records || records.length === 0) return;
|
||||
|
||||
const sortedRecords = [...records].sort((a, b) => {
|
||||
if (sortStatus.direction === 'asc') {
|
||||
return a[sortStatus.columnAccessor] > b[sortStatus.columnAccessor] ? 1 : -1;
|
||||
}
|
||||
|
||||
return a[sortStatus.columnAccessor] < b[sortStatus.columnAccessor] ? 1 : -1;
|
||||
});
|
||||
|
||||
setRecords(sortedRecords);
|
||||
}, [sortStatus]);
|
||||
// only query for correct results if there is more than one page
|
||||
// otherwise, querying has no effect
|
||||
...(numFiles > 1
|
||||
? {
|
||||
sortBy: sortStatus.columnAccessor as PaginatedFilesOptions['sortBy'],
|
||||
order: sortStatus.direction,
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
||||
// file modal on click
|
||||
const [open, setOpen] = useState(false);
|
||||
|
@ -203,7 +195,7 @@ export default function Dashboard({ disableMediaPreview, exifEnabled, compress }
|
|||
),
|
||||
},
|
||||
]}
|
||||
records={records ?? []}
|
||||
records={files.data ?? []}
|
||||
fetching={files.isLoading}
|
||||
loaderBackgroundBlur={5}
|
||||
loaderVariant='dots'
|
||||
|
|
|
@ -37,7 +37,9 @@ export default function FilePagation({ disableMediaPreview, exifEnabled, queryPa
|
|||
})();
|
||||
}, [page]);
|
||||
|
||||
const pages = usePaginatedFiles(page, !checked ? 'media' : null);
|
||||
const pages = usePaginatedFiles(page, {
|
||||
filter: !checked ? 'media' : 'none',
|
||||
});
|
||||
|
||||
if (pages.isSuccess && pages.data.length === 0) {
|
||||
if (page > 1 && numPages > 0) {
|
||||
|
|
|
@ -11,7 +11,10 @@ import PendingFilesModal from './PendingFilesModal';
|
|||
export default function Files({ disableMediaPreview, exifEnabled, queryPage, compress }) {
|
||||
const [favoritePage, setFavoritePage] = useState(1);
|
||||
const [favoriteNumPages, setFavoriteNumPages] = useState(0);
|
||||
const favoritePages = usePaginatedFiles(favoritePage, 'media', true);
|
||||
const favoritePages = usePaginatedFiles(favoritePage, {
|
||||
filter: 'media',
|
||||
favorite: true,
|
||||
});
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
|
|
|
@ -63,13 +63,13 @@ export async function sendUpload(user: User, file: File, raw_link: string, link:
|
|||
thumbnail:
|
||||
isImage && parsed.embed.thumbnail
|
||||
? {
|
||||
url: parsed.url,
|
||||
url: raw_link,
|
||||
}
|
||||
: null,
|
||||
image:
|
||||
isImage && parsed.embed.image
|
||||
? {
|
||||
url: parsed.url,
|
||||
url: raw_link,
|
||||
}
|
||||
: null,
|
||||
},
|
||||
|
|
|
@ -75,7 +75,7 @@ export const getServerSideProps: GetServerSideProps<ServerSideProps> = async (ct
|
|||
user_registration: config.features.user_registration,
|
||||
oauth_registration: config.features.oauth_registration,
|
||||
oauth_providers: JSON.stringify(oauth_providers),
|
||||
bypass_local_login: config.oauth.bypass_local_login,
|
||||
bypass_local_login: config.oauth?.bypass_local_login ?? false,
|
||||
chunks_size: config.chunks.chunks_size,
|
||||
max_size: config.chunks.max_size,
|
||||
totp_enabled: config.mfa.totp_enabled,
|
||||
|
|
|
@ -33,13 +33,23 @@ export const useFiles = (query: { [key: string]: string } = {}) => {
|
|||
);
|
||||
});
|
||||
};
|
||||
export const usePaginatedFiles = (page?: number, filter = 'media', favorite = null) => {
|
||||
const queryBuilder = new URLSearchParams({
|
||||
|
||||
export type PaginatedFilesOptions = {
|
||||
filter: 'media' | 'none';
|
||||
favorite: boolean;
|
||||
sortBy: 'createdAt' | 'views' | 'expiresAt' | 'size' | 'name' | 'mimetype';
|
||||
order: 'asc' | 'desc';
|
||||
};
|
||||
|
||||
export const usePaginatedFiles = (page?: number, options?: Partial<PaginatedFilesOptions>) => {
|
||||
const queryString = new URLSearchParams({
|
||||
page: Number(page || '1').toString(),
|
||||
filter,
|
||||
...(favorite !== null && { favorite: favorite.toString() }),
|
||||
});
|
||||
const queryString = queryBuilder.toString();
|
||||
filter: options?.filter ?? 'none',
|
||||
// ...(options?.favorite !== null && { favorite: options?.favorite?.toString() }),
|
||||
favorite: options.favorite ? 'true' : '',
|
||||
sortBy: options.sortBy ?? '',
|
||||
order: options.order ?? '',
|
||||
}).toString();
|
||||
|
||||
return useQuery<UserFilesResponse[]>(['files', queryString], async () => {
|
||||
return fetch('/api/user/paged?' + queryString)
|
||||
|
|
|
@ -2,8 +2,10 @@ import { Button, Stack, Title, Tooltip } from '@mantine/core';
|
|||
import MutedText from 'components/MutedText';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function FiveHundred() {
|
||||
const { asPath } = useRouter();
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
|
@ -24,9 +26,13 @@ export default function FiveHundred() {
|
|||
<Tooltip label={"Take a look at Zipline's logs and the browser console for more info"}>
|
||||
<MutedText>Internal server error</MutedText>
|
||||
</Tooltip>
|
||||
<Button component={Link} href='/dashboard'>
|
||||
Head to the Dashboard
|
||||
</Button>
|
||||
{asPath === '/dashboard' ? (
|
||||
<Button onClick={() => window.location.reload()}>Attempt Refresh</Button>
|
||||
) : (
|
||||
<Button component={Link} href='/dashboard'>
|
||||
Head to the Dashboard
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -255,7 +255,12 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
response.files.push(responseUrl);
|
||||
|
||||
if (zconfig.discord?.upload) {
|
||||
await sendUpload(user, fileUpload, `${domain}/r/${invis ? invis.invis : fileUpload.name}`, responseUrl);
|
||||
await sendUpload(
|
||||
user,
|
||||
fileUpload,
|
||||
`${domain}/r/${invis ? invis.invis : encodeURI(fileUpload.name)}`,
|
||||
responseUrl
|
||||
);
|
||||
}
|
||||
|
||||
if (zconfig.exif.enabled && zconfig.exif.remove_gps && fileUpload.mimetype.startsWith('image/')) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { Prisma } from '@prisma/client';
|
||||
import { s } from '@sapphire/shapeshift';
|
||||
import config from 'lib/config';
|
||||
import prisma from 'lib/prisma';
|
||||
import { formatRootUrl } from 'lib/utils/urls';
|
||||
|
@ -5,12 +7,27 @@ import { NextApiReq, NextApiRes, UserExtended, withZipline } from 'middleware/wi
|
|||
|
||||
const pageCount = 16;
|
||||
|
||||
const sortByValidator = s.enum(
|
||||
...([
|
||||
'createdAt',
|
||||
'views',
|
||||
'expiresAt',
|
||||
'size',
|
||||
'name',
|
||||
'mimetype',
|
||||
] satisfies (keyof Prisma.FileOrderByWithRelationInput)[])
|
||||
);
|
||||
|
||||
const orderValidator = s.enum('asc', 'desc');
|
||||
|
||||
async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||
const { page, filter, count, favorite } = req.query as {
|
||||
const { page, filter, count, favorite, ...rest } = req.query as {
|
||||
page: string;
|
||||
filter: string;
|
||||
count: string;
|
||||
favorite: string;
|
||||
sortBy: string;
|
||||
order: string;
|
||||
};
|
||||
|
||||
const where = {
|
||||
|
@ -33,7 +50,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
|||
},
|
||||
],
|
||||
}),
|
||||
};
|
||||
} satisfies Prisma.FileWhereInput;
|
||||
|
||||
if (count) {
|
||||
const count = await prisma.file.count({
|
||||
|
@ -48,6 +65,14 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
|||
if (!page) return res.badRequest('no page');
|
||||
if (isNaN(Number(page))) return res.badRequest('page is not a number');
|
||||
|
||||
// validate sortBy
|
||||
const sortBy = sortByValidator.run(rest.sortBy || 'createdAt');
|
||||
if (!sortBy.isOk()) return res.badRequest('invalid sortBy option');
|
||||
|
||||
// validate order
|
||||
const order = orderValidator.run(rest.order || 'desc');
|
||||
if (!sortBy.isOk()) return res.badRequest('invalid order option');
|
||||
|
||||
const files: {
|
||||
favorite: boolean;
|
||||
createdAt: Date;
|
||||
|
@ -63,7 +88,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
|||
}[] = await prisma.file.findMany({
|
||||
where,
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
[sortBy.value]: order.value,
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
|
|
Loading…
Reference in a new issue