fix: Handle base64 and remote images when creating a file (#5740)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 });
|
||||
|
||||
Reference in New Issue
Block a user