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 #362
This commit is contained in:
parent
94d1cf75aa
commit
68f9e3e0dd
1 changed files with 70 additions and 25 deletions
|
@ -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;
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue