fix: Handle base64 and remote images when creating a file (#5740)

This commit is contained in:
Tom Moor
2023-08-26 09:15:14 -04:00
committed by GitHub
parent c643f62d96
commit 78ad1b867a
15 changed files with 292 additions and 198 deletions

View File

@@ -1,51 +1,98 @@
import { Transaction } from "sequelize";
import { v4 as uuidv4 } from "uuid";
import { AttachmentPreset } from "@shared/types";
import { Attachment, Event, User } from "@server/models";
import AttachmentHelper from "@server/models/helpers/AttachmentHelper";
import FileStorage from "@server/storage/files";
type BaseProps = {
id?: string;
name: string;
user: User;
source?: "import";
preset: AttachmentPreset;
ip?: string;
transaction?: Transaction;
};
type UrlProps = BaseProps & {
url: string;
};
type BufferProps = BaseProps & {
buffer: Buffer;
type: string;
};
type Props = UrlProps | BufferProps;
export default async function attachmentCreator({
id,
name,
type,
buffer,
user,
source,
preset,
ip,
transaction,
}: {
id?: string;
name: string;
type: string;
buffer: Buffer;
user: User;
source?: "import";
ip?: string;
transaction?: Transaction;
}) {
const key = `uploads/${user.id}/${uuidv4()}/${name}`;
const acl = process.env.AWS_S3_ACL || "private";
const url = await FileStorage.upload({
body: buffer,
contentType: type,
contentLength: buffer.length,
key,
...rest
}: Props): Promise<Attachment | undefined> {
const acl = AttachmentHelper.presetToAcl(preset);
const key = AttachmentHelper.getKey({
acl,
id: uuidv4(),
name,
userId: user.id,
});
const attachment = await Attachment.create(
{
id,
let attachment;
if ("url" in rest) {
const { url } = rest;
const res = await FileStorage.uploadFromUrl(url, key, acl);
if (!res) {
return;
}
attachment = await Attachment.create(
{
id,
key,
acl,
size: res.contentLength,
contentType: res.contentType,
teamId: user.teamId,
userId: user.id,
},
{
transaction,
}
);
} else {
const { buffer, type } = rest;
await FileStorage.upload({
body: buffer,
contentType: type,
contentLength: buffer.length,
key,
acl,
url,
size: buffer.length,
contentType: type,
teamId: user.teamId,
userId: user.id,
},
{
transaction,
}
);
});
attachment = await Attachment.create(
{
id,
key,
acl,
size: buffer.length,
contentType: type,
teamId: user.teamId,
userId: user.id,
},
{
transaction,
}
);
}
await Event.create(
{
name: "attachments.create",
@@ -62,5 +109,6 @@ export default async function attachmentCreator({
transaction,
}
);
return attachment;
}

View File

@@ -84,7 +84,15 @@ export default async function documentCreator({
title: templateDocument
? DocumentHelper.replaceTemplateVariables(templateDocument.title, user)
: title,
text: templateDocument ? templateDocument.text : text,
text: await DocumentHelper.replaceImagesWithAttachments(
DocumentHelper.replaceTemplateVariables(
templateDocument ? templateDocument.text : text,
user
),
user,
ip,
transaction
),
state,
},
{
@@ -112,7 +120,11 @@ export default async function documentCreator({
);
if (publish) {
await document.publish(user.id, collectionId!, { transaction });
if (!collectionId) {
throw new Error("Collection ID is required to publish");
}
await document.publish(user.id, collectionId, { transaction });
await Event.create(
{
name: "documents.publish",

View File

@@ -1,6 +1,5 @@
import path from "path";
import emojiRegex from "emoji-regex";
import escapeRegExp from "lodash/escapeRegExp";
import truncate from "lodash/truncate";
import mammoth from "mammoth";
import quotedPrintable from "quoted-printable";
@@ -10,12 +9,10 @@ import parseTitle from "@shared/utils/parseTitle";
import { DocumentValidation } from "@shared/validations";
import { traceFunction } from "@server/logging/tracing";
import { User } from "@server/models";
import DocumentHelper from "@server/models/helpers/DocumentHelper";
import ProsemirrorHelper from "@server/models/helpers/ProsemirrorHelper";
import dataURItoBuffer from "@server/utils/dataURItoBuffer";
import parseImages from "@server/utils/parseImages";
import turndownService from "@server/utils/turndown";
import { FileImportError, InvalidRequestError } from "../errors";
import attachmentCreator from "./attachmentCreator";
interface ImportableFile {
type: string;
@@ -207,26 +204,12 @@ async function documentImporter({
// to match our hardbreak parser.
text = text.replace(/<br>/gi, "\\n");
// find data urls, convert to blobs, upload and write attachments
const images = parseImages(text);
const dataURIs = images.filter((href) => href.startsWith("data:"));
for (const uri of dataURIs) {
const name = "imported";
const { buffer, type } = dataURItoBuffer(uri);
const attachment = await attachmentCreator({
name,
type,
buffer,
user,
ip,
transaction,
});
text = text.replace(
new RegExp(escapeRegExp(uri), "g"),
attachment.redirectUrl
);
}
text = await DocumentHelper.replaceImagesWithAttachments(
text,
user,
ip,
transaction
);
// It's better to truncate particularly long titles than fail the import
title = truncate(title, { length: DocumentValidation.maxTitleLength });