️ Put back preview service (#75)

* Put back the preview service
* Fix imports linter
This commit is contained in:
Romaric Mourgues
2023-06-12 11:09:36 +02:00
committed by GitHub
parent b7b9abbc97
commit d17972b2f1
16 changed files with 811 additions and 19 deletions
@@ -13,6 +13,12 @@ import {
ExecutionContext,
} from "../../../core/platform/framework/api/crud-service";
import gr from "../../global-resolver";
import {
PreviewClearMessageQueueRequest,
PreviewMessageQueueRequest,
} from "../../../services/previews/types";
import { PreviewFinishedProcessor } from "./preview";
import _ from "lodash";
export class FileServiceImpl {
version: "1";
@@ -23,6 +29,9 @@ export class FileServiceImpl {
async init(): Promise<this> {
try {
await Promise.all([(this.repository = await gr.database.getRepository<File>("files", File))]);
gr.platformServices.messageQueue.processor.addHandler(
new PreviewFinishedProcessor(this, this.repository),
);
} catch (err) {
logger.error("Error while initializing files service", err);
}
@@ -106,6 +115,60 @@ export class FileServiceImpl {
if (entity.upload_data.chunks === 1 && totalUploadedSize) {
entity.upload_data.size = totalUploadedSize;
await this.repository.save(entity, context);
/** Send preview generation task */
if (entity.upload_data.size < this.max_preview_file_size) {
const document: PreviewMessageQueueRequest["document"] = {
id: JSON.stringify(_.pick(entity, "id", "company_id")),
provider: gr.platformServices.storage.getConnectorType(),
path: getFilePath(entity),
encryption_algo: this.algorithm,
encryption_key: entity.encryption_key,
chunks: entity.upload_data.chunks,
filename: entity.metadata.name,
mime: entity.metadata.mime,
};
const output = {
provider: gr.platformServices.storage.getConnectorType(),
path: `${getFilePath(entity)}/thumbnails/`,
encryption_algo: this.algorithm,
encryption_key: entity.encryption_key,
pages: 10,
};
entity.metadata.thumbnails_status = "waiting";
await this.repository.save(entity, context);
if (!options?.ignoreThumbnails) {
try {
await gr.platformServices.messageQueue.publish<PreviewMessageQueueRequest>(
"services:preview",
{
data: { document, output },
},
);
if (options.waitForThumbnail) {
entity = await gr.services.files.getFile(
{
id: entity.id,
company_id: context.company.id,
},
context,
{ waitForThumbnail: true },
);
}
} catch (err) {
entity.metadata.thumbnails_status = "error";
await this.repository.save(entity, context);
logger.warn({ err }, "Previewing - Error while sending ");
}
}
}
/** End preview generation task generation */
}
}
@@ -227,6 +290,22 @@ export class FileServiceImpl {
totalChunks: fileToDelete.upload_data.chunks,
});
if (fileToDelete.thumbnails.length > 0) {
await gr.platformServices.messageQueue.publish<PreviewClearMessageQueueRequest>(
"services:preview:clear",
{
data: {
document: {
id: JSON.stringify(_.pick(fileToDelete, "id", "company_id")),
provider: gr.platformServices.storage.getConnectorType(),
path: `${path}/thumbnails/`,
thumbnails_number: fileToDelete.thumbnails.length,
},
},
},
);
}
return new DeleteResult("files", fileToDelete, true);
}
}
@@ -0,0 +1,67 @@
import { logger, TdriveContext } from "../../../core/platform/framework";
import { PreviewMessageQueueCallback } from "../../../../src/services/previews/types";
import Repository from "../../../../src/core/platform/services/database/services/orm/repository/repository";
import { File } from "../entities/file";
import { FileServiceImpl } from "./index";
import { MessageQueueHandler } from "../../../core/platform/services/message-queue/api";
import { ExecutionContext } from "../../../core/platform/framework/api/crud-service";
/**
* Update the file metadata and upload the thumbnails in storage
*/
export class PreviewFinishedProcessor
implements MessageQueueHandler<PreviewMessageQueueCallback, string>
{
constructor(readonly service: FileServiceImpl, private repository: Repository<File>) {}
async init(_context?: TdriveContext): Promise<this> {
return this;
}
readonly topics = {
in: "services:preview:callback",
};
readonly options = {
unique: true,
ack: true,
};
name = "FilePreviewProcessor";
validate(message: PreviewMessageQueueCallback): boolean {
return !!(message && message.document);
}
async process(message: PreviewMessageQueueCallback, context?: ExecutionContext): Promise<string> {
logger.info(
`${this.name} - Updating file metadata with preview generation ${message.thumbnails.length}`,
);
const pk: { company_id: string; id: string } = JSON.parse(message.document.id);
const entity = await this.repository.findOne(pk, {}, context);
if (!entity) {
logger.info(`This file ${message.document.id} does not exists anymore.`);
return;
}
entity.thumbnails = (message.thumbnails || []).map((thumb, index) => {
return {
index,
id: thumb.path.split("/").pop(),
size: thumb.size,
type: thumb.type,
width: thumb.width,
height: thumb.height,
url: `/internal/services/files/v1/companies/${entity.company_id}/files/${entity.id}/thumbnails/${index}`,
};
});
if (!entity.metadata) entity.metadata = {};
entity.metadata.thumbnails_status = "done";
await this.repository.save(entity, context);
return "done";
}
}
@@ -5,4 +5,5 @@ export type UploadOptions = {
totalChunks: number;
chunkNumber: number;
waitForThumbnail: boolean;
ignoreThumbnails: boolean;
};
@@ -28,6 +28,7 @@ export class FileController {
filename: q.resumableFilename || q.filename || file?.filename || undefined,
type: q.resumableType || q.type || file?.mimetype || undefined,
waitForThumbnail: q.thumbnail_sync,
ignoreThumbnails: q.ignore_thumbnails,
};
const id = request.params.id;