Share with me endpoint (#77)

* 🛠️ remove babel from dependencies
* 🛠️ removes some of the redundant dependencies + versions update
*  separate endpoint for "shared with me"
This commit is contained in:
Anton Shepilov
2023-06-13 17:16:13 +02:00
committed by GitHub
parent d17972b2f1
commit d7fa56cb13
19 changed files with 7163 additions and 8157 deletions
@@ -1,5 +1,5 @@
import * as cron from "node-cron";
import uuid from "node-uuid";
import uuid from "uuid";
import { TdriveService } from "../../framework";
import { CronAPI, CronJob, CronExpression, CronTask } from "./api";
@@ -168,12 +168,12 @@ export const getAccessLevel = async (
const prevalidatedPublicTokenDocumentId = context?.user?.public_token_document_id;
try {
item =
item ||
(await repository.findOne({
if (!item) {
item = await repository.findOne({
id,
company_id: context.company.id,
}));
});
}
if (!item) {
throw Error("Drive item doesn't exist");
@@ -2,8 +2,8 @@ import SearchRepository from "../../../core/platform/services/search/repository"
import { getLogger, logger, TdriveLogger } from "../../../core/platform/framework";
import { CrudException, ListResult } from "../../../core/platform/framework/api/crud-service";
import Repository, {
inType,
comparisonType,
inType,
} from "../../../core/platform/services/database/services/orm/repository/repository";
import { PublicFile } from "../../../services/files/entities/file";
import globalResolver from "../../../services/global-resolver";
@@ -16,15 +16,15 @@ import {
TYPE as DriveTdriveTabRepoType,
} from "../entities/drive-tdrive-tab";
import {
DriveExecutionContext,
CompanyExecutionContext,
DocumentsMessageQueueRequest,
DriveExecutionContext,
DriveFileAccessLevel,
DriveItemDetails,
DriveTdriveTab,
RootType,
SearchDocumentsOptions,
TrashType,
CompanyExecutionContext,
DriveTdriveTab,
DriveFileAccessLevel,
} from "../types";
import {
addDriveItemToArchive,
@@ -783,7 +783,9 @@ export class DocumentsService {
limitStr: "100",
},
$in: [
["access_entities", [context.user.id, context.company.id]],
...(options.onlyDirectlyShared
? [["access_entities", [context.user.id, context.company.id]] as inType]
: []),
...(options.company_id ? [["company_id", [options.company_id]] as inType] : []),
...(options.creator ? [["creator", [options.creator]] as inType] : []),
...(options.mime_type
@@ -819,24 +821,31 @@ export class DocumentsService {
context,
);
// Use Promise.all to check access on each item in parallel
const filteredResult = await Promise.all(
result.getEntities().filter(async item => {
// if this flag is on, the access permissions were checked inside the database
if (!options.onlyDirectlyShared) {
const filteredResult = await this.filter(result.getEntities(), async item => {
try {
// Check access for each item
const hasAccess = await checkAccess(item.id, null, "read", this.repository, context);
// Return true if the user has access
return hasAccess;
return await checkAccess(item.id, null, "read", this.repository, context);
} catch (error) {
this.logger.warn("failed to check item access", error);
return false;
}
}),
);
});
return new ListResult(result.type, filteredResult, result.nextPage);
return new ListResult(result.type, filteredResult, result.nextPage);
} else {
return result;
}
};
private async filter(arr, callback) {
const fail = Symbol();
return (
await Promise.all(arr.map(async item => ((await callback(item)) ? item : fail)))
).filter(i => i !== fail);
}
getTab = async (tabId: string, context: CompanyExecutionContext): Promise<DriveTdriveTab> => {
const tab = await this.driveTdriveTabRepository.findOne(
{ company_id: context.company.id, tab_id: tabId },
@@ -47,6 +47,7 @@ export type SearchDocumentsOptions = {
sort?: SortType;
view?: string;
fields?: string[];
onlyDirectlyShared?: boolean;
};
export type SearchDocumentsBody = {
@@ -55,6 +55,7 @@ export class DocumentsController {
filename: q.resumableFilename || q.filename || file?.filename || undefined,
type: q.resumableType || q.type || file?.mimetype || undefined,
waitForThumbnail: !!q.thumbnail_sync,
ignoreThumbnails: false,
};
createdFile = await globalResolver.services.files.save(null, file, options, context);
@@ -140,6 +141,35 @@ export class DocumentsController {
};
};
sharedWithMe = async (
request: FastifyRequest<{
Params: RequestParams;
Body: SearchDocumentsBody;
Querystring: { public_token?: string };
}>,
): Promise<ListResult<DriveFileDTO>> => {
try {
const context = getDriveExecutionContext(request);
const options: SearchDocumentsOptions = {
...request.body,
company_id: request.body.company_id || context.company.id,
onlyDirectlyShared: true,
};
if (!Object.keys(options).length) {
this.throw500Search();
}
const fileList = await globalResolver.services.documents.documents.search(options, context);
return this.driveFileDTOBuilder.build(fileList, context, options.fields, options.view);
} catch (error) {
logger.error({ error: `${error}` }, "error while searching for document");
this.throw500Search();
}
};
/**
* Return access level of a given user on a given item
*/
@@ -352,6 +382,7 @@ export class DocumentsController {
const options: SearchDocumentsOptions = {
...request.body,
company_id: request.body.company_id || context.company.id,
onlyDirectlyShared: false,
};
if (!Object.keys(options).length) {
@@ -13,7 +13,27 @@ import { getSharedByUser } from "../../services/access-check";
export class DriveFileDTOBuilder {
private views: Map<string, string[]> = new Map([
["default", Object.getOwnPropertyNames(new DriveFile())],
[
"default",
[
"company_id",
"id",
"parent_id",
"is_in_trash",
"is_directory",
"name",
"extension",
"description",
"added",
"last_modified",
"size",
"last_version_cache",
"tags",
"access_info",
"content_keywords",
"creator",
],
],
[
"shared_with_me",
[
@@ -39,6 +59,8 @@ export class DriveFileDTOBuilder {
fields?: string[],
view?: string,
): Promise<ListResult<DriveFileDTO>> {
const file = new DriveFile();
file.id = "1";
if (view) {
fields = this.views.get(view);
}
@@ -87,6 +87,13 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, _options, next)
handler: documentsController.search.bind(documentsController),
});
fastify.route({
method: "POST",
url: `${baseUrl}/shared-with-me`,
preValidation: [fastify.authenticate],
handler: documentsController.sharedWithMe.bind(documentsController),
});
fastify.route({
method: "GET",
url: `${baseUrl}/tabs/:tab_id`,
@@ -1,4 +1,4 @@
import { logger, TwakeContext } from "../../../../../core/platform/framework";
import { logger, TdriveContext } from "../../../../../core/platform/framework";
import { PreviewClearMessageQueueRequest, PreviewMessageQueueCallback } from "../../../types";
import gr from "../../../../global-resolver";
import { MessageQueueHandler } from "../../../../../core/platform/services/message-queue/api";
@@ -22,7 +22,7 @@ export class ClearProcessor
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
init?(context?: TwakeContext): Promise<this> {
init?(context?: TdriveContext): Promise<this> {
throw new Error("Method not implemented.");
}
@@ -10,10 +10,10 @@ import {
} from "../../../../../utils/mime";
import { PreviewMessageQueueRequest, ThumbnailResult } from "../../../types";
import { generateVideoPreview } from "./video";
import { Initializable, TwakeServiceProvider } from "../../../../../core/platform/framework";
import { Initializable, TdriveServiceProvider } from "../../../../../core/platform/framework";
import { cleanFiles } from "../../../../../utils/files";
export class PreviewProcessService implements TwakeServiceProvider, Initializable {
export class PreviewProcessService implements TdriveServiceProvider, Initializable {
name: "PreviewProcessService";
version: "1";
@@ -125,10 +125,10 @@ async function getVideoDimensions(videoPath: string): Promise<{ width: number; h
ffmpeg.ffprobe(videoPath, (err, metadata) => {
if (err) {
reject(err);
} else {
const { width, height } = metadata.streams[0];
resolve({ width, height });
}
const { width, height } = metadata.streams[0];
resolve({ width, height });
});
});
}