server: change pagination of drive/show endpoint

This changes the pagination of the drive/show API endpoint to use the
offset variant of pagination and allows to specify a sorting.

closes FoundKeyGang/FoundKey#362
This commit is contained in:
Johann150 2023-03-25 22:54:54 +01:00
parent 94d1cf75aa
commit 68f9e3e0dd
Signed by untrusted user: Johann150
GPG key ID: 9EE6577A2A06F8F1

View file

@ -1,11 +1,13 @@
import { In } from 'typeorm';
import { DriveFiles, DriveFolders } from '@/models/index.js';
import define from '../../define.js';
import { makePaginationQuery } from '../../common/make-pagination-query.js';
import { db } from '@/db/postgre.js';
export const meta = {
tags: ['drive'],
description: "Lists all folders and files in the authenticated user's drive. Folders are always listed first. The limit, if specified, is applied over the total number of elements.",
description: "Lists all folders and files in the authenticated user's drive. Default sorting is folders first, then newest first. The limit, if specified, is applied over the total number of elements.",
requireCredential: true,
@ -31,40 +33,83 @@ export const meta = {
export const paramDef = {
type: 'object',
properties: {
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
limit: {
type: 'integer',
minimum: 1,
maximum: 100,
default: 30
},
offset: {
type: 'integer',
default: 0,
},
sort: {
type: 'string',
enum: [
'+created',
'-created',
'+name',
'-name',
],
},
folderId: {
type: 'string',
format: 'misskey:id',
nullable: true,
default: null
},
},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, user) => {
const foldersQuery = makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId)
.andWhere('folder.userId = :userId', { userId: user.id });
const filesQuery = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId)
.andWhere('file.userId = :userId', { userId: user.id });
if (ps.folderId) {
foldersQuery.andWhere('folder.parentId = :parentId', { parentId: ps.folderId });
filesQuery.andWhere('file.parentId = :parentId', { parentId: ps.folderId });
} else {
foldersQuery.andWhere('folder.parentId IS NULL');
filesQuery.andWhere('file.parentId IS NULL');
let orderBy = 'type ASC, id DESC';
switch (ps.sort) {
case '+created':
orderBy = '"createdAt" DESC';
break;
case '-created':
orderBy = '"createdAt" ASC';
break;
case '+name':
orderBy = 'name DESC';
break;
case '-name':
orderBy = 'name ASC';
break;
}
const folders = await foldersQuery.take(ps.limit).getMany();
// due to the way AID is constructed, we can be sure that the IDs are not duplicated across tables.
const ids = await db.query(
'SELECT id FROM (SELECT id, "userId", "parentId", "createdAt", name, 0 AS type FROM drive_folder'
+ ' UNION SELECT id, "userId", "parentId", "createdAt", name, 1 AS type FROM drive_file) AS x'
+ ' WHERE "userId" = $1 AND "parentId"'
+ (ps.folderId ? '= $4' : 'IS NULL')
+ ' ORDER BY ' + orderBy
+ ' LIMIT $2 OFFSET $3',
[user.id, ps.limit, ps.offset, ...(ps.folderId ? [ps.folderId] : [])]
).then(items => items.map(({ id }) => id));
const [files, ...packedFolders] = await Promise.all([
filesQuery.take(ps.limit - folders.length).getMany(),
...(folders.map(folder => DriveFolders.pack(folder))),
const [folders, files] = await Promise.all([
DriveFolders.findBy({
id: In(ids),
})
.then(folders => Promise.all(folders.map(folder => DriveFolders.pack(folder)))),
DriveFiles.findBy({
id: In(ids),
})
.then(files => DriveFiles.packMany(files, { detail: false, self: true })),
]);
const packedFiles = await DriveFiles.packMany(files, { detail: false, self: true });
// merge folders/files into one array, keeping the original sorting
let merged = [];
for (const folder of folders) {
merged[ids.indexOf(folder.id)] = folder;
}
for (const file of files) {
merged[ids.indexOf(file.id)] = file;
}
return [
...packedFolders,
...packedFiles,
];
return merged;
});