forked from FoundKeyGang/FoundKey
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:
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 { DriveFiles, DriveFolders } from '@/models/index.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
import { makePaginationQuery } from '../../common/make-pagination-query.js';
|
import { makePaginationQuery } from '../../common/make-pagination-query.js';
|
||||||
|
import { db } from '@/db/postgre.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['drive'],
|
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,
|
requireCredential: true,
|
||||||
|
|
||||||
|
@ -31,40 +33,83 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: {
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
type: 'integer',
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
minimum: 1,
|
||||||
folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
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: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const foldersQuery = makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId)
|
let orderBy = 'type ASC, id DESC';
|
||||||
.andWhere('folder.userId = :userId', { userId: user.id });
|
switch (ps.sort) {
|
||||||
const filesQuery = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId)
|
case '+created':
|
||||||
.andWhere('file.userId = :userId', { userId: user.id });
|
orderBy = '"createdAt" DESC';
|
||||||
|
break;
|
||||||
if (ps.folderId) {
|
case '-created':
|
||||||
foldersQuery.andWhere('folder.parentId = :parentId', { parentId: ps.folderId });
|
orderBy = '"createdAt" ASC';
|
||||||
filesQuery.andWhere('file.parentId = :parentId', { parentId: ps.folderId });
|
break;
|
||||||
} else {
|
case '+name':
|
||||||
foldersQuery.andWhere('folder.parentId IS NULL');
|
orderBy = 'name DESC';
|
||||||
filesQuery.andWhere('file.parentId IS NULL');
|
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([
|
const [folders, files] = await Promise.all([
|
||||||
filesQuery.take(ps.limit - folders.length).getMany(),
|
DriveFolders.findBy({
|
||||||
...(folders.map(folder => DriveFolders.pack(folder))),
|
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 [
|
return merged;
|
||||||
...packedFolders,
|
|
||||||
...packedFiles,
|
|
||||||
];
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue