From 73ca45c95db4a638a3ea562d8ce6575ee600f1ba Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 6 Aug 2024 15:26:05 +0530 Subject: [PATCH 01/15] fix: update custom emoji issues Signed-off-by: Abhinav Kumar --- .../app/api/server/lib/getUploadFormData.ts | 57 +++++++++++++++++-- apps/meteor/app/api/server/v1/emoji-custom.ts | 14 +++-- .../emoji-custom/client/lib/emojiCustom.ts | 5 +- .../server/lib/insertOrUpdateEmoji.ts | 14 +++-- .../server/lib/uploadEmojiCustom.ts | 15 +++-- apps/meteor/app/emoji/client/helpers.ts | 5 +- apps/meteor/app/emoji/lib/rocketchat.ts | 2 + .../views/admin/customEmoji/CustomEmoji.tsx | 2 +- .../admin/customEmoji/EditCustomEmoji.tsx | 3 +- apps/meteor/server/models/raw/EmojiCustom.ts | 10 ++++ .../tests/end-to-end/api/emoji-custom.ts | 41 ++++++++++--- packages/core-typings/src/IEmojiCustom.ts | 1 + .../src/models/IEmojiCustomModel.ts | 1 + 13 files changed, 138 insertions(+), 32 deletions(-) diff --git a/apps/meteor/app/api/server/lib/getUploadFormData.ts b/apps/meteor/app/api/server/lib/getUploadFormData.ts index 3136a6c16e13..9d8cead39a2b 100644 --- a/apps/meteor/app/api/server/lib/getUploadFormData.ts +++ b/apps/meteor/app/api/server/lib/getUploadFormData.ts @@ -17,6 +17,14 @@ type UploadResult = { fields: K; }; +type OptionalUploadResult = + | UploadResult + | ({ + [P in keyof Omit, 'fields'>]: undefined; + } & { + fields: K; + }); + export async function getUploadFormData< T extends string, K extends Record = Record, @@ -27,8 +35,37 @@ export async function getUploadFormData< field?: T; validate?: V; sizeLimit?: number; + optional: true; + }, +): Promise>; + +export async function getUploadFormData< + T extends string, + K extends Record = Record, + V extends ValidateFunction = ValidateFunction, +>( + { request }: { request: Request }, + options?: { + field?: T; + validate?: V; + sizeLimit?: number; + optional?: false | undefined; + }, +): Promise>; + +export async function getUploadFormData< + T extends string, + K extends Record = Record, + V extends ValidateFunction = ValidateFunction, +>( + { request }: { request: Request }, + options: { + field?: T; + validate?: V; + sizeLimit?: number; + optional?: boolean; } = {}, -): Promise> { +): Promise | OptionalUploadResult> { const limits = { files: 1, ...(options.sizeLimit && options.sizeLimit > -1 && { fileSize: options.sizeLimit }), @@ -37,9 +74,9 @@ export async function getUploadFormData< const bb = busboy({ headers: request.headers, defParamCharset: 'utf8', limits }); const fields = Object.create(null) as K; - let uploadedFile: UploadResult | undefined; + let uploadedFile: UploadResult | OptionalUploadResult | undefined; - let returnResult = (_value: UploadResult) => { + let returnResult = (_value: UploadResult | OptionalUploadResult) => { // noop }; let returnError = (_error?: Error | string | null | undefined) => { @@ -48,10 +85,22 @@ export async function getUploadFormData< function onField(fieldname: keyof K, value: K[keyof K]) { fields[fieldname] = value; + uploadedFile = { + fields, + encoding: undefined, + filename: undefined, + fieldname: undefined, + mimetype: undefined, + fileBuffer: undefined, + file: undefined, + }; } function onEnd() { if (!uploadedFile) { + return returnError(new MeteorError('No file or fields were uploaded')); + } + if (!('file' in uploadedFile) && !options.optional) { return returnError(new MeteorError('No file uploaded')); } if (options.validate !== undefined && !options.validate(fields)) { @@ -121,7 +170,7 @@ export async function getUploadFormData< request.pipe(bb); - return new Promise((resolve, reject) => { + return new Promise | OptionalUploadResult>((resolve, reject) => { returnResult = resolve; returnError = reject; }); diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index 261743e6c0a9..1106471272dd 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -143,7 +143,7 @@ API.v1.addRoute( { request: this.request, }, - { field: 'emoji', sizeLimit: settings.get('FileUpload_MaxFileSize') }, + { field: 'emoji', sizeLimit: settings.get('FileUpload_MaxFileSize'), optional: true }, ); const { fields, fileBuffer, mimetype } = emoji; @@ -160,9 +160,11 @@ API.v1.addRoute( fields.previousName = emojiToUpdate.name; fields.previousExtension = emojiToUpdate.extension; fields.aliases = fields.aliases || ''; - const newFile = Boolean(emoji && fileBuffer.length); - if (fields.newFile) { + let newFile = false; + + if (fileBuffer?.length) { + newFile = true; const isUploadable = await Media.isImage(fileBuffer); if (!isUploadable) { throw new Meteor.Error('emoji-is-not-image', "Emoji file provided cannot be uploaded since it's not an image"); @@ -184,9 +186,9 @@ API.v1.addRoute( newFile, }; - await insertOrUpdateEmoji(this.userId, emojiData); - if (fields.newFile) { - await uploadEmojiCustomWithBuffer(this.userId, fileBuffer, mimetype, emojiData); + const updatedEmojiData = await insertOrUpdateEmoji(this.userId, emojiData); + if (newFile && fileBuffer) { + await uploadEmojiCustomWithBuffer(this.userId, fileBuffer, mimetype, updatedEmojiData); } return API.v1.success(); }, diff --git a/apps/meteor/app/emoji-custom/client/lib/emojiCustom.ts b/apps/meteor/app/emoji-custom/client/lib/emojiCustom.ts index ee186ebd4e15..05fb8cd4a04a 100644 --- a/apps/meteor/app/emoji-custom/client/lib/emojiCustom.ts +++ b/apps/meteor/app/emoji-custom/client/lib/emojiCustom.ts @@ -18,7 +18,7 @@ const isSetNotNull = (fn: () => unknown) => { return value !== null && value !== undefined; }; -const getEmojiUrlFromName = (name: string, extension: string) => { +const getEmojiUrlFromName = (name: string, extension: string, etag?: string) => { if (name == null) { return; } @@ -27,7 +27,7 @@ const getEmojiUrlFromName = (name: string, extension: string) => { const random = (Session as unknown as { keys: Record }).keys[key] ?? 0; - return getURL(`/emoji-custom/${encodeURIComponent(name)}.${extension}?_dc=${random}`); + return getURL(`/emoji-custom/${encodeURIComponent(name)}.${extension}?_dc=${random}${etag ? `&etag=${etag}` : ''}`); }; export const deleteEmojiCustom = (emojiData: IEmoji) => { @@ -123,6 +123,7 @@ const customRender = (html: string) => { return `${shortname}`; }); diff --git a/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts b/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts index 7e838baee9b0..24362a24095a 100644 --- a/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts +++ b/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts @@ -8,7 +8,7 @@ import { trim } from '../../../../lib/utils/stringUtils'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; -type EmojiData = { +export type EmojiData = { _id?: string; name: string; aliases: string; @@ -33,7 +33,6 @@ export async function insertOrUpdateEmoji(userId: string | null, emojiData: Emoj } emojiData.name = limax(emojiData.name, { replacement: '_' }); - emojiData.aliases = limax(emojiData.aliases, { replacement: '_' }); // allow all characters except colon, whitespace, comma, >, <, &, ", ', /, \, (, ) // more practical than allowing specific sets of characters; also allows foreign languages @@ -61,7 +60,13 @@ export async function insertOrUpdateEmoji(userId: string | null, emojiData: Emoj field: 'Alias_Set', }); } - aliases = _.without(emojiData.aliases.split(/[\s,]/).filter(Boolean), emojiData.name); + aliases = _.without( + emojiData.aliases + .split(/\s*,\s*/) + .filter(Boolean) + .map((alias) => limax(alias, { replacement: '_' })), + emojiData.name, + ); } emojiData.extension = emojiData.extension === 'svg+xml' ? 'png' : emojiData.extension; @@ -119,7 +124,8 @@ export async function insertOrUpdateEmoji(userId: string | null, emojiData: Emoj const rs = await RocketChatFileEmojiCustomInstance.getFileWithReadStream( encodeURIComponent(`${emojiData.previousName}.${emojiData.previousExtension}`), ); - if (rs !== null) { + + if (rs) { await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.name}.${emojiData.extension}`)); const ws = RocketChatFileEmojiCustomInstance.createWriteStream( encodeURIComponent(`${emojiData.name}.${emojiData.previousExtension}`), diff --git a/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts index 07633eaa1a7d..90bddd610923 100644 --- a/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts @@ -1,4 +1,6 @@ import { api, Media } from '@rocket.chat/core-services'; +import { EmojiCustom } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; import limax from 'limax'; import { Meteor } from 'meteor/meteor'; import sharp from 'sharp'; @@ -41,9 +43,12 @@ export async function uploadEmojiCustomWithBuffer( throw new Meteor.Error('not_authorized'); } + if (!Array.isArray(emojiData.aliases)) { + // delete aliases for notification purposes. here, it is a string or undefined rather than an array + delete emojiData.aliases; + } + emojiData.name = limax(emojiData.name, { replacement: '_' }); - // delete aliases for notification purposes. here, it is a string rather than an array - delete emojiData.aliases; const file = await getFile(buffer, emojiData.extension); emojiData.extension = emojiData.extension === 'svg+xml' ? 'png' : emojiData.extension; @@ -67,8 +72,10 @@ export async function uploadEmojiCustomWithBuffer( encodeURIComponent(`${emojiData.name}.${emojiData.extension}`), contentType, ); - ws.on('end', () => { - setTimeout(() => api.broadcast('emoji.updateCustom', emojiData), 500); + ws.on('end', async () => { + const etag = Random.hexString(6); + await EmojiCustom.setETagByIdOrName(emojiData.name, etag); + setTimeout(() => api.broadcast('emoji.updateCustom', { ...emojiData, etag }), 500); resolve(); }); diff --git a/apps/meteor/app/emoji/client/helpers.ts b/apps/meteor/app/emoji/client/helpers.ts index 35badda26a73..f1499c907695 100644 --- a/apps/meteor/app/emoji/client/helpers.ts +++ b/apps/meteor/app/emoji/client/helpers.ts @@ -149,7 +149,10 @@ export const removeFromRecent = (emoji: string, recentEmojis: string[], setRecen setRecentEmojis(recentEmojis); }; -export const updateRecent = (recentList: string[]) => { +export const updateRecent = (recentList: string | string[]) => { + if (!Array.isArray(recentList)) { + recentList = [recentList]; + } const recentPkgList: string[] = emoji.packages.base.emojisByCategory.recent; recentList?.forEach((_emoji) => { !recentPkgList.includes(_emoji) && recentPkgList.push(_emoji); diff --git a/apps/meteor/app/emoji/lib/rocketchat.ts b/apps/meteor/app/emoji/lib/rocketchat.ts index f5d33cce3de0..1c5b515407ed 100644 --- a/apps/meteor/app/emoji/lib/rocketchat.ts +++ b/apps/meteor/app/emoji/lib/rocketchat.ts @@ -31,6 +31,7 @@ export type EmojiPackages = { aliases?: string[]; aliasOf?: undefined; extension?: string; + etag?: string; } | { emojiPackage: string; @@ -38,6 +39,7 @@ export type EmojiPackages = { extension?: undefined; aliases?: undefined; shortnames?: undefined; + etag?: string; }; }; }; diff --git a/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx index 36d08ab6e382..4c02776b80d5 100644 --- a/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx @@ -95,7 +95,7 @@ const CustomEmoji = ({ onClick, reload }: CustomEmojiProps) => { {emojis.name} - {emojis.aliases} + {Array.isArray(emojis.aliases) ? emojis.aliases.join(', ') : emojis.aliases} ))} diff --git a/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx index 7d7363d2424c..285c1a98513b 100644 --- a/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx @@ -30,6 +30,7 @@ type EditCustomEmojiProps = { name: string; aliases: string[]; extension: string; + etag?: string; }; }; @@ -51,7 +52,7 @@ const EditCustomEmoji = ({ close, onChange, data, ...props }: EditCustomEmojiPro } if (data) { - return absoluteUrl(`/emoji-custom/${encodeURIComponent(data.name)}.${data.extension}`); + return absoluteUrl(`/emoji-custom/${encodeURIComponent(data.name)}.${data.extension}${data.etag ? `?etag=${data.etag}` : ''}`); } return null; diff --git a/apps/meteor/server/models/raw/EmojiCustom.ts b/apps/meteor/server/models/raw/EmojiCustom.ts index ec3c6390f64d..2a91e8c99a5d 100644 --- a/apps/meteor/server/models/raw/EmojiCustom.ts +++ b/apps/meteor/server/models/raw/EmojiCustom.ts @@ -68,6 +68,16 @@ export class EmojiCustomRaw extends BaseRaw implements IEmojiCusto return this.updateOne({ _id }, update); } + setETagByIdOrName(idOrName: string, etag: string): Promise { + const update = { + $set: { + etag, + }, + }; + + return this.updateOne({ $or: [{ _id: idOrName }, { name: idOrName }] }, update); + } + // INSERT create(data: InsertionModel): Promise>> { return this.insertOne(data); diff --git a/apps/meteor/tests/end-to-end/api/emoji-custom.ts b/apps/meteor/tests/end-to-end/api/emoji-custom.ts index 091b24452446..469a58208370 100644 --- a/apps/meteor/tests/end-to-end/api/emoji-custom.ts +++ b/apps/meteor/tests/end-to-end/api/emoji-custom.ts @@ -1,4 +1,4 @@ -import type { ICustomEmojiDescriptor } from '@rocket.chat/core-typings'; +import type { IEmojiCustom } from '@rocket.chat/core-typings'; import { assert, expect } from 'chai'; import { before, describe, it, after } from 'mocha'; @@ -8,7 +8,7 @@ import { imgURL } from '../../data/interactions'; describe('[EmojiCustom]', () => { const customEmojiName = `my-custom-emoji-${Date.now()}`; - let withoutAliases: ICustomEmojiDescriptor; + let withoutAliases: IEmojiCustom; before((done) => getCredentials(done)); @@ -69,7 +69,7 @@ describe('[EmojiCustom]', () => { }); }); - let createdCustomEmoji: ICustomEmojiDescriptor; + let createdCustomEmoji: IEmojiCustom; describe('[/emoji-custom.update]', () => { before((done) => { @@ -82,8 +82,8 @@ describe('[EmojiCustom]', () => { expect(res.body.emojis).to.have.property('update').and.to.be.a('array').and.to.not.have.lengthOf(0); expect(res.body.emojis).to.have.property('remove').and.to.be.a('array').and.to.have.lengthOf(0); - const _createdCustomEmoji = (res.body.emojis.update as ICustomEmojiDescriptor[]).find((emoji) => emoji.name === customEmojiName); - const _withoutAliases = (res.body.emojis.update as ICustomEmojiDescriptor[]).find( + const _createdCustomEmoji = (res.body.emojis.update as IEmojiCustom[]).find((emoji) => emoji.name === customEmojiName); + const _withoutAliases = (res.body.emojis.update as IEmojiCustom[]).find( (emoji) => emoji.name === `${customEmojiName}-without-aliases`, ); @@ -95,7 +95,7 @@ describe('[EmojiCustom]', () => { }) .end(done); }); - it('successfully:', () => { + describe('successfully:', () => { it('should update the custom emoji without a file', (done) => { void request .post(api('emoji-custom.update')) @@ -143,8 +143,31 @@ describe('[EmojiCustom]', () => { }) .end(done); }); + + it('should change the etag when the custom emoji image is updated', async () => { + const prevEtag = createdCustomEmoji.etag; + + await request + .post(api('emoji-custom.update')) + .set(credentials) + .attach('emoji', imgURL) + .field({ + _id: createdCustomEmoji._id, + name: customEmojiName, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }); + + const emojis = await request.get(api(`emoji-custom.all`)).set(credentials).expect(200); + const updatedCustomEmoji = emojis.body.emojis.find((emoji: IEmojiCustom) => emoji._id === createdCustomEmoji._id); + expect(updatedCustomEmoji.etag).not.to.be.equal(prevEtag); + }); }); - it('should throw error when:', () => { + + describe('should throw error when:', () => { it('the fields does not include "_id"', (done) => { void request .post(api('emoji-custom.update')) @@ -178,13 +201,13 @@ describe('[EmojiCustom]', () => { }) .end(done); }); - it('the filename is wrong', (done) => { + it('the emoji file field is wrong', (done) => { void request .post(api('emoji-custom.update')) .set(credentials) .attach('emojiwrong', imgURL) .field({ - _id: 'invalid-id', + _id: createdCustomEmoji._id, name: 'my-custom-emoji-without-aliases', }) .expect('Content-Type', 'application/json') diff --git a/packages/core-typings/src/IEmojiCustom.ts b/packages/core-typings/src/IEmojiCustom.ts index 98bbc9121d47..bc9b02fe818d 100644 --- a/packages/core-typings/src/IEmojiCustom.ts +++ b/packages/core-typings/src/IEmojiCustom.ts @@ -4,4 +4,5 @@ export interface IEmojiCustom extends IRocketChatRecord { name: string; aliases: string[]; extension: string; + etag?: string; } diff --git a/packages/model-typings/src/models/IEmojiCustomModel.ts b/packages/model-typings/src/models/IEmojiCustomModel.ts index 30f0323c1ec7..bc5bd332ba79 100644 --- a/packages/model-typings/src/models/IEmojiCustomModel.ts +++ b/packages/model-typings/src/models/IEmojiCustomModel.ts @@ -9,6 +9,7 @@ export interface IEmojiCustomModel extends IBaseModel { setName(_id: string, name: string): Promise; setAliases(_id: string, aliases: string[]): Promise; setExtension(_id: string, extension: string): Promise; + setETagByIdOrName(idOrName: string, etag: string): Promise; create(data: InsertionModel): Promise>>; countByNameOrAlias(name: string): Promise; } From 97a37d6a467060d02b7467f3b9591c76218aa3f4 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 6 Aug 2024 15:27:51 +0530 Subject: [PATCH 02/15] added changeset Signed-off-by: Abhinav Kumar --- .changeset/green-papayas-thank.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/green-papayas-thank.md diff --git a/.changeset/green-papayas-thank.md b/.changeset/green-papayas-thank.md new file mode 100644 index 000000000000..19df47515a78 --- /dev/null +++ b/.changeset/green-papayas-thank.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/model-typings': patch +'@rocket.chat/core-typings': patch +'@rocket.chat/meteor': patch +--- + +Fixed: 🛠️ Resolved issues with updating custom emojis, including file upload and image caching problems. From 8e74bd763c11c28eb1a26036e42685b0845c0121 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Thu, 22 Aug 2024 20:42:35 +0530 Subject: [PATCH 03/15] added improvements Signed-off-by: Abhinav Kumar --- .../app/api/server/lib/getUploadFormData.ts | 6 ++--- .../server/lib/insertOrUpdateEmoji.ts | 17 +++++------- .../server/lib/uploadEmojiCustom.ts | 26 ++++++++++--------- apps/meteor/server/models/raw/EmojiCustom.ts | 14 ++++++++-- .../src/models/IEmojiCustomModel.ts | 3 ++- 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/apps/meteor/app/api/server/lib/getUploadFormData.ts b/apps/meteor/app/api/server/lib/getUploadFormData.ts index 9d8cead39a2b..ed84e75ad9c2 100644 --- a/apps/meteor/app/api/server/lib/getUploadFormData.ts +++ b/apps/meteor/app/api/server/lib/getUploadFormData.ts @@ -65,7 +65,7 @@ export async function getUploadFormData< sizeLimit?: number; optional?: boolean; } = {}, -): Promise | OptionalUploadResult> { +): Promise> { const limits = { files: 1, ...(options.sizeLimit && options.sizeLimit > -1 && { fileSize: options.sizeLimit }), @@ -74,9 +74,9 @@ export async function getUploadFormData< const bb = busboy({ headers: request.headers, defParamCharset: 'utf8', limits }); const fields = Object.create(null) as K; - let uploadedFile: UploadResult | OptionalUploadResult | undefined; + let uploadedFile: OptionalUploadResult | undefined; - let returnResult = (_value: UploadResult | OptionalUploadResult) => { + let returnResult = (_value: OptionalUploadResult) => { // noop }; let returnError = (_error?: Error | string | null | undefined) => { diff --git a/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts b/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts index 24362a24095a..85a9648cf6d9 100644 --- a/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts +++ b/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts @@ -2,7 +2,6 @@ import { api } from '@rocket.chat/core-services'; import { EmojiCustom } from '@rocket.chat/models'; import limax from 'limax'; import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; import { trim } from '../../../../lib/utils/stringUtils'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; @@ -11,7 +10,7 @@ import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; export type EmojiData = { _id?: string; name: string; - aliases: string; + aliases?: string; extension: string; previousName?: string; previousExtension?: string; @@ -41,7 +40,7 @@ export async function insertOrUpdateEmoji(userId: string | null, emojiData: Emoj // silently strip colon; this allows for uploading :emojiname: as emojiname emojiData.name = emojiData.name.replace(/:/g, ''); - emojiData.aliases = emojiData.aliases.replace(/:/g, ''); + emojiData.aliases = emojiData.aliases?.replace(/:/g, ''); if (nameValidation.test(emojiData.name)) { throw new Meteor.Error('error-input-is-not-a-valid-field', `${emojiData.name} is not a valid name`, { @@ -60,13 +59,11 @@ export async function insertOrUpdateEmoji(userId: string | null, emojiData: Emoj field: 'Alias_Set', }); } - aliases = _.without( - emojiData.aliases - .split(/\s*,\s*/) - .filter(Boolean) - .map((alias) => limax(alias, { replacement: '_' })), - emojiData.name, - ); + aliases = emojiData.aliases + .split(/\s*,\s*/) + .filter(Boolean) + .map((alias) => limax(alias, { replacement: '_' })) + .filter((alias) => alias !== emojiData.name); } emojiData.extension = emojiData.extension === 'svg+xml' ? 'png' : emojiData.extension; diff --git a/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts index 90bddd610923..33fe6f0ce316 100644 --- a/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts @@ -8,6 +8,7 @@ import sharp from 'sharp'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFile } from '../../../file/server'; import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; +import type { EmojiData } from './insertOrUpdateEmoji'; const getFile = async (file: Buffer, extension: string) => { if (extension !== 'svg+xml') { @@ -17,17 +18,14 @@ const getFile = async (file: Buffer, extension: string) => { return sharp(file).png().toBuffer(); }; -type EmojiData = { - _id?: string; - name: string; - aliases?: string | string[]; - extension: string; - previousName?: string; - previousExtension?: string; - newFile?: boolean; -}; +export type EmojiDataWithAliases = Omit & { aliases?: string | string[] }; -export async function uploadEmojiCustom(userId: string | null, binaryContent: string, contentType: string, emojiData: EmojiData) { +export async function uploadEmojiCustom( + userId: string | null, + binaryContent: string, + contentType: string, + emojiData: EmojiDataWithAliases, +) { return uploadEmojiCustomWithBuffer(userId, Buffer.from(binaryContent, 'binary'), contentType, emojiData); } @@ -35,7 +33,7 @@ export async function uploadEmojiCustomWithBuffer( userId: string | null, buffer: Buffer, contentType: string, - emojiData: EmojiData, + emojiData: EmojiDataWithAliases, ): Promise { // technically, since this method doesnt have any datatype validations, users can // upload videos as emojis. The FE won't play them, but they will waste space for sure. @@ -74,7 +72,11 @@ export async function uploadEmojiCustomWithBuffer( ); ws.on('end', async () => { const etag = Random.hexString(6); - await EmojiCustom.setETagByIdOrName(emojiData.name, etag); + if (emojiData._id) { + await EmojiCustom.setETagById(emojiData._id, etag); + } else { + await EmojiCustom.setETagByName(emojiData.name, etag); + } setTimeout(() => api.broadcast('emoji.updateCustom', { ...emojiData, etag }), 500); resolve(); }); diff --git a/apps/meteor/server/models/raw/EmojiCustom.ts b/apps/meteor/server/models/raw/EmojiCustom.ts index 2a91e8c99a5d..a4f4aaaf5e63 100644 --- a/apps/meteor/server/models/raw/EmojiCustom.ts +++ b/apps/meteor/server/models/raw/EmojiCustom.ts @@ -68,14 +68,24 @@ export class EmojiCustomRaw extends BaseRaw implements IEmojiCusto return this.updateOne({ _id }, update); } - setETagByIdOrName(idOrName: string, etag: string): Promise { + setETagById(_id: string, etag: string): Promise { const update = { $set: { etag, }, }; - return this.updateOne({ $or: [{ _id: idOrName }, { name: idOrName }] }, update); + return this.updateOne({ _id }, update); + } + + setETagByName(name: string, etag: string): Promise { + const update = { + $set: { + etag, + }, + }; + + return this.updateOne({ name }, update); } // INSERT diff --git a/packages/model-typings/src/models/IEmojiCustomModel.ts b/packages/model-typings/src/models/IEmojiCustomModel.ts index bc5bd332ba79..879d7314bd5a 100644 --- a/packages/model-typings/src/models/IEmojiCustomModel.ts +++ b/packages/model-typings/src/models/IEmojiCustomModel.ts @@ -9,7 +9,8 @@ export interface IEmojiCustomModel extends IBaseModel { setName(_id: string, name: string): Promise; setAliases(_id: string, aliases: string[]): Promise; setExtension(_id: string, extension: string): Promise; - setETagByIdOrName(idOrName: string, etag: string): Promise; + setETagById(_id: string, etag: string): Promise; + setETagByName(name: string, etag: string): Promise; create(data: InsertionModel): Promise>>; countByNameOrAlias(name: string): Promise; } From 22ae1829b84e5728065ca251ce5183aee6a644fd Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Thu, 22 Aug 2024 23:51:52 +0530 Subject: [PATCH 04/15] added improvements Signed-off-by: Abhinav Kumar --- .../app/api/server/lib/getUploadFormData.ts | 2 +- apps/meteor/app/api/server/v1/emoji-custom.ts | 3 +-- .../tests/end-to-end/api/emoji-custom.ts | 22 +++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/apps/meteor/app/api/server/lib/getUploadFormData.ts b/apps/meteor/app/api/server/lib/getUploadFormData.ts index ed84e75ad9c2..00b78e8de88f 100644 --- a/apps/meteor/app/api/server/lib/getUploadFormData.ts +++ b/apps/meteor/app/api/server/lib/getUploadFormData.ts @@ -170,7 +170,7 @@ export async function getUploadFormData< request.pipe(bb); - return new Promise | OptionalUploadResult>((resolve, reject) => { + return new Promise>((resolve, reject) => { returnResult = resolve; returnError = reject; }); diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index 1106471272dd..6328260be78c 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -161,10 +161,9 @@ API.v1.addRoute( fields.previousExtension = emojiToUpdate.extension; fields.aliases = fields.aliases || ''; - let newFile = false; + const newFile = !!fileBuffer?.length; if (fileBuffer?.length) { - newFile = true; const isUploadable = await Media.isImage(fileBuffer); if (!isUploadable) { throw new Meteor.Error('emoji-is-not-image', "Emoji file provided cannot be uploaded since it's not an image"); diff --git a/apps/meteor/tests/end-to-end/api/emoji-custom.ts b/apps/meteor/tests/end-to-end/api/emoji-custom.ts index 469a58208370..07285b928f8f 100644 --- a/apps/meteor/tests/end-to-end/api/emoji-custom.ts +++ b/apps/meteor/tests/end-to-end/api/emoji-custom.ts @@ -397,6 +397,28 @@ describe('[EmojiCustom]', () => { }) .end(done); }); + + it('should return the emoji even when no etag is passed (for old emojis)', async () => { + const res = await request.get(`/emoji-custom/${createdCustomEmoji.name}.png`).set(credentials).expect(200); + + expect(res.headers).to.have.property('content-type', 'image/png'); + expect(res.headers).to.have.property('cache-control', 'public, max-age=31536000'); + expect(res.headers).to.have.property('content-disposition', 'inline'); + }); + + it('should return success if the etag is invalid', async () => { + const res = await request + .get(`/emoji-custom/${createdCustomEmoji.name}.png?etag=1234`) + .set(credentials) + .set({ + 'if-none-match': 'invalid-etag', + }) + .expect(200); + + expect(res.headers).to.have.property('content-type', 'image/png'); + expect(res.headers).to.have.property('cache-control', 'public, max-age=31536000'); + expect(res.headers).to.have.property('content-disposition', 'inline'); + }); }); describe('[/emoji-custom.delete]', () => { From f3e270f8ee162be56829d71eae60c8bb38c9c11e Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 23 Aug 2024 00:27:31 +0530 Subject: [PATCH 05/15] removed setEtagByName Signed-off-by: Abhinav Kumar --- apps/meteor/app/api/server/v1/emoji-custom.ts | 11 +++++------ .../app/emoji-custom/server/lib/uploadEmojiCustom.ts | 8 ++------ apps/meteor/server/models/raw/EmojiCustom.ts | 10 ---------- .../model-typings/src/models/IEmojiCustomModel.ts | 1 - 4 files changed, 7 insertions(+), 23 deletions(-) diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index 6328260be78c..9453211e0f24 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -114,16 +114,15 @@ API.v1.addRoute( fields.extension = extension; try { - await Meteor.callAsync('insertOrUpdateEmoji', { - ...fields, - newFile: true, - aliases: fields.aliases || '', - }); - await Meteor.callAsync('uploadEmojiCustom', fileBuffer, mimetype, { + const emojiData = await insertOrUpdateEmoji(this.userId, { ...fields, newFile: true, aliases: fields.aliases || '', + name: fields.name, + extension: fields.extension, }); + + await uploadEmojiCustomWithBuffer(this.userId, fileBuffer, mimetype, emojiData); } catch (e) { SystemLogger.error(e); return API.v1.failure(); diff --git a/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts index 33fe6f0ce316..5418dca12645 100644 --- a/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts @@ -18,7 +18,7 @@ const getFile = async (file: Buffer, extension: string) => { return sharp(file).png().toBuffer(); }; -export type EmojiDataWithAliases = Omit & { aliases?: string | string[] }; +export type EmojiDataWithAliases = Omit & { _id: string; aliases?: string | string[] }; export async function uploadEmojiCustom( userId: string | null, @@ -72,11 +72,7 @@ export async function uploadEmojiCustomWithBuffer( ); ws.on('end', async () => { const etag = Random.hexString(6); - if (emojiData._id) { - await EmojiCustom.setETagById(emojiData._id, etag); - } else { - await EmojiCustom.setETagByName(emojiData.name, etag); - } + await EmojiCustom.setETagById(emojiData._id, etag); setTimeout(() => api.broadcast('emoji.updateCustom', { ...emojiData, etag }), 500); resolve(); }); diff --git a/apps/meteor/server/models/raw/EmojiCustom.ts b/apps/meteor/server/models/raw/EmojiCustom.ts index a4f4aaaf5e63..abdfe8f0d667 100644 --- a/apps/meteor/server/models/raw/EmojiCustom.ts +++ b/apps/meteor/server/models/raw/EmojiCustom.ts @@ -78,16 +78,6 @@ export class EmojiCustomRaw extends BaseRaw implements IEmojiCusto return this.updateOne({ _id }, update); } - setETagByName(name: string, etag: string): Promise { - const update = { - $set: { - etag, - }, - }; - - return this.updateOne({ name }, update); - } - // INSERT create(data: InsertionModel): Promise>> { return this.insertOne(data); diff --git a/packages/model-typings/src/models/IEmojiCustomModel.ts b/packages/model-typings/src/models/IEmojiCustomModel.ts index 879d7314bd5a..541711897171 100644 --- a/packages/model-typings/src/models/IEmojiCustomModel.ts +++ b/packages/model-typings/src/models/IEmojiCustomModel.ts @@ -10,7 +10,6 @@ export interface IEmojiCustomModel extends IBaseModel { setAliases(_id: string, aliases: string[]): Promise; setExtension(_id: string, extension: string): Promise; setETagById(_id: string, etag: string): Promise; - setETagByName(name: string, etag: string): Promise; create(data: InsertionModel): Promise>>; countByNameOrAlias(name: string): Promise; } From e6e4551511f96e3c9b79ddf35c46f5f15831dff0 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 22 Aug 2024 13:03:23 -0600 Subject: [PATCH 06/15] Update apps/meteor/app/api/server/v1/emoji-custom.ts --- apps/meteor/app/api/server/v1/emoji-custom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index 9453211e0f24..c2811cffd19d 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -162,7 +162,7 @@ API.v1.addRoute( const newFile = !!fileBuffer?.length; - if (fileBuffer?.length) { + if (newFile) { const isUploadable = await Media.isImage(fileBuffer); if (!isUploadable) { throw new Meteor.Error('emoji-is-not-image', "Emoji file provided cannot be uploaded since it's not an image"); From 8347c29defb0557bb6369bb896d8090429ab5e44 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 22 Aug 2024 13:04:08 -0600 Subject: [PATCH 07/15] Update apps/meteor/app/api/server/v1/emoji-custom.ts --- apps/meteor/app/api/server/v1/emoji-custom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index c2811cffd19d..75db97930625 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -185,7 +185,7 @@ API.v1.addRoute( }; const updatedEmojiData = await insertOrUpdateEmoji(this.userId, emojiData); - if (newFile && fileBuffer) { + if (newFile) { await uploadEmojiCustomWithBuffer(this.userId, fileBuffer, mimetype, updatedEmojiData); } return API.v1.success(); From 1ba098b135ab8c42825c5f336f4a8d26294f6ca2 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 23 Aug 2024 12:54:03 +0530 Subject: [PATCH 08/15] type fix Signed-off-by: Abhinav Kumar --- apps/meteor/app/api/server/v1/emoji-custom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index 75db97930625..949aa2a38c88 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -160,7 +160,7 @@ API.v1.addRoute( fields.previousExtension = emojiToUpdate.extension; fields.aliases = fields.aliases || ''; - const newFile = !!fileBuffer?.length; + const newFile = !!fileBuffer && fileBuffer.length > 0; if (newFile) { const isUploadable = await Media.isImage(fileBuffer); From 802343475e8d06936926e7beb7a9ed84a72d8c7f Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 23 Aug 2024 08:14:18 -0600 Subject: [PATCH 09/15] ts --- apps/meteor/app/api/server/v1/emoji-custom.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index 949aa2a38c88..d9c7bbe845b5 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -1,9 +1,11 @@ import { Media } from '@rocket.chat/core-services'; +import type { IEmojiCustom } from '@rocket.chat/core-typings'; import { EmojiCustom } from '@rocket.chat/models'; import { isEmojiCustomList } from '@rocket.chat/rest-typings'; import { Meteor } from 'meteor/meteor'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import type { EmojiData } from '../../../emoji-custom/server/lib/insertOrUpdateEmoji'; import { insertOrUpdateEmoji } from '../../../emoji-custom/server/lib/insertOrUpdateEmoji'; import { uploadEmojiCustomWithBuffer } from '../../../emoji-custom/server/lib/uploadEmojiCustom'; import { settings } from '../../../settings/server'; @@ -151,41 +153,39 @@ API.v1.addRoute( throw new Meteor.Error('The required "_id" query param is missing.'); } - const emojiToUpdate = await EmojiCustom.findOneById(fields._id); + const emojiToUpdate = await EmojiCustom.findOneById>(fields._id, { + projection: { name: 1, extension: 1 }, + }); if (!emojiToUpdate) { throw new Meteor.Error('Emoji not found.'); } - fields.previousName = emojiToUpdate.name; - fields.previousExtension = emojiToUpdate.extension; - fields.aliases = fields.aliases || ''; - - const newFile = !!fileBuffer && fileBuffer.length > 0; + const emojiData: EmojiData = { + previousName: emojiToUpdate.name, + previousExtension: emojiToUpdate.extension, + aliases: fields.aliases || '', + name: fields.name, + extension: fields.extension, + _id: fields._id, + newFile: false, + }; - if (newFile) { + const isNewFile = fileBuffer?.length && !!mimetype; + if (isNewFile) { + emojiData.newFile = isNewFile; const isUploadable = await Media.isImage(fileBuffer); if (!isUploadable) { throw new Meteor.Error('emoji-is-not-image', "Emoji file provided cannot be uploaded since it's not an image"); } const [, extension] = mimetype.split('/'); - fields.extension = extension; + emojiData.extension = extension; } else { - fields.extension = emojiToUpdate.extension; + emojiData.extension = emojiToUpdate.extension; } - const emojiData = { - name: fields.name, - _id: fields._id, - aliases: fields.aliases, - extension: fields.extension, - previousName: fields.previousName, - previousExtension: fields.previousExtension, - newFile, - }; - const updatedEmojiData = await insertOrUpdateEmoji(this.userId, emojiData); - if (newFile) { + if (isNewFile) { await uploadEmojiCustomWithBuffer(this.userId, fileBuffer, mimetype, updatedEmojiData); } return API.v1.success(); From b6a9b097176fa8167e8e30487239abc40de6ccae Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 23 Aug 2024 10:35:57 -0600 Subject: [PATCH 10/15] ts --- apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts | 4 ++-- apps/meteor/server/models/raw/EmojiCustom.ts | 4 ++-- packages/model-typings/src/models/IEmojiCustomModel.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts index 5418dca12645..0ef5b55e9f71 100644 --- a/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts @@ -18,7 +18,7 @@ const getFile = async (file: Buffer, extension: string) => { return sharp(file).png().toBuffer(); }; -export type EmojiDataWithAliases = Omit & { _id: string; aliases?: string | string[] }; +export type EmojiDataWithAliases = Omit & { aliases?: string | string[] }; export async function uploadEmojiCustom( userId: string | null, @@ -72,7 +72,7 @@ export async function uploadEmojiCustomWithBuffer( ); ws.on('end', async () => { const etag = Random.hexString(6); - await EmojiCustom.setETagById(emojiData._id, etag); + await EmojiCustom.setETagByName(emojiData.name, etag); setTimeout(() => api.broadcast('emoji.updateCustom', { ...emojiData, etag }), 500); resolve(); }); diff --git a/apps/meteor/server/models/raw/EmojiCustom.ts b/apps/meteor/server/models/raw/EmojiCustom.ts index abdfe8f0d667..539f3b0f0b57 100644 --- a/apps/meteor/server/models/raw/EmojiCustom.ts +++ b/apps/meteor/server/models/raw/EmojiCustom.ts @@ -68,14 +68,14 @@ export class EmojiCustomRaw extends BaseRaw implements IEmojiCusto return this.updateOne({ _id }, update); } - setETagById(_id: string, etag: string): Promise { + setETagByName(name: string, etag: string): Promise { const update = { $set: { etag, }, }; - return this.updateOne({ _id }, update); + return this.updateOne({ name }, update); } // INSERT diff --git a/packages/model-typings/src/models/IEmojiCustomModel.ts b/packages/model-typings/src/models/IEmojiCustomModel.ts index 541711897171..5bae4d9eb9ba 100644 --- a/packages/model-typings/src/models/IEmojiCustomModel.ts +++ b/packages/model-typings/src/models/IEmojiCustomModel.ts @@ -9,7 +9,7 @@ export interface IEmojiCustomModel extends IBaseModel { setName(_id: string, name: string): Promise; setAliases(_id: string, aliases: string[]): Promise; setExtension(_id: string, extension: string): Promise; - setETagById(_id: string, etag: string): Promise; + setETagByName(name: string, etag: string): Promise; create(data: InsertionModel): Promise>>; countByNameOrAlias(name: string): Promise; } From 96c147cf246056ef6dc0a386684f5af1fec700d4 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 13 Sep 2024 12:20:09 +0530 Subject: [PATCH 11/15] revert unrelated fixes Signed-off-by: Abhinav Kumar --- apps/meteor/app/emoji/client/helpers.ts | 5 +---- apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/meteor/app/emoji/client/helpers.ts b/apps/meteor/app/emoji/client/helpers.ts index f1499c907695..35badda26a73 100644 --- a/apps/meteor/app/emoji/client/helpers.ts +++ b/apps/meteor/app/emoji/client/helpers.ts @@ -149,10 +149,7 @@ export const removeFromRecent = (emoji: string, recentEmojis: string[], setRecen setRecentEmojis(recentEmojis); }; -export const updateRecent = (recentList: string | string[]) => { - if (!Array.isArray(recentList)) { - recentList = [recentList]; - } +export const updateRecent = (recentList: string[]) => { const recentPkgList: string[] = emoji.packages.base.emojisByCategory.recent; recentList?.forEach((_emoji) => { !recentPkgList.includes(_emoji) && recentPkgList.push(_emoji); diff --git a/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx index 4c02776b80d5..36d08ab6e382 100644 --- a/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx @@ -95,7 +95,7 @@ const CustomEmoji = ({ onClick, reload }: CustomEmojiProps) => { {emojis.name} - {Array.isArray(emojis.aliases) ? emojis.aliases.join(', ') : emojis.aliases} + {emojis.aliases} ))} From 3b313794c7b2784fd9c98919feb3a401c0dbea0d Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 25 Oct 2024 18:30:10 +0530 Subject: [PATCH 12/15] modified changeset Signed-off-by: Abhinav Kumar --- .changeset/green-papayas-thank.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/green-papayas-thank.md b/.changeset/green-papayas-thank.md index 19df47515a78..22547db942ef 100644 --- a/.changeset/green-papayas-thank.md +++ b/.changeset/green-papayas-thank.md @@ -4,4 +4,4 @@ '@rocket.chat/meteor': patch --- -Fixed: 🛠️ Resolved issues with updating custom emojis, including file upload and image caching problems. +Fixes an issue where updating custom emojis didn’t work as expected, ensuring that uploaded emojis now update correctly and display without any caching problems. From c8ca4026aeb464454c1e14d5082e076f27a731d3 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 1 Nov 2024 15:38:58 +0530 Subject: [PATCH 13/15] minor changes Signed-off-by: Abhinav Kumar --- .../app/api/server/lib/getUploadFormData.ts | 20 +++++++++---------- apps/meteor/app/api/server/v1/emoji-custom.ts | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/meteor/app/api/server/lib/getUploadFormData.ts b/apps/meteor/app/api/server/lib/getUploadFormData.ts index 00b78e8de88f..1630afe8cae2 100644 --- a/apps/meteor/app/api/server/lib/getUploadFormData.ts +++ b/apps/meteor/app/api/server/lib/getUploadFormData.ts @@ -17,7 +17,7 @@ type UploadResult = { fields: K; }; -type OptionalUploadResult = +type UploadResultWithOptionalFile = | UploadResult | ({ [P in keyof Omit, 'fields'>]: undefined; @@ -35,9 +35,9 @@ export async function getUploadFormData< field?: T; validate?: V; sizeLimit?: number; - optional: true; + fileOptional: true; }, -): Promise>; +): Promise>; export async function getUploadFormData< T extends string, @@ -49,7 +49,7 @@ export async function getUploadFormData< field?: T; validate?: V; sizeLimit?: number; - optional?: false | undefined; + fileOptional?: false | undefined; }, ): Promise>; @@ -63,9 +63,9 @@ export async function getUploadFormData< field?: T; validate?: V; sizeLimit?: number; - optional?: boolean; + fileOptional?: boolean; } = {}, -): Promise> { +): Promise> { const limits = { files: 1, ...(options.sizeLimit && options.sizeLimit > -1 && { fileSize: options.sizeLimit }), @@ -74,9 +74,9 @@ export async function getUploadFormData< const bb = busboy({ headers: request.headers, defParamCharset: 'utf8', limits }); const fields = Object.create(null) as K; - let uploadedFile: OptionalUploadResult | undefined; + let uploadedFile: UploadResultWithOptionalFile | undefined; - let returnResult = (_value: OptionalUploadResult) => { + let returnResult = (_value: UploadResultWithOptionalFile) => { // noop }; let returnError = (_error?: Error | string | null | undefined) => { @@ -100,7 +100,7 @@ export async function getUploadFormData< if (!uploadedFile) { return returnError(new MeteorError('No file or fields were uploaded')); } - if (!('file' in uploadedFile) && !options.optional) { + if (!('file' in uploadedFile) && !options.fileOptional) { return returnError(new MeteorError('No file uploaded')); } if (options.validate !== undefined && !options.validate(fields)) { @@ -170,7 +170,7 @@ export async function getUploadFormData< request.pipe(bb); - return new Promise>((resolve, reject) => { + return new Promise>((resolve, reject) => { returnResult = resolve; returnError = reject; }); diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index d9c7bbe845b5..5b255b6ef0c1 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -144,7 +144,7 @@ API.v1.addRoute( { request: this.request, }, - { field: 'emoji', sizeLimit: settings.get('FileUpload_MaxFileSize'), optional: true }, + { field: 'emoji', sizeLimit: settings.get('FileUpload_MaxFileSize'), fileOptional: true }, ); const { fields, fileBuffer, mimetype } = emoji; From 20763aca76e6af804d0d6d493ad0c5bdcecbd1b7 Mon Sep 17 00:00:00 2001 From: Tasso Date: Tue, 12 Nov 2024 16:46:37 -0300 Subject: [PATCH 14/15] Concatenate aliases --- apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx index 36d08ab6e382..0e6bb2a7687d 100644 --- a/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx @@ -95,7 +95,7 @@ const CustomEmoji = ({ onClick, reload }: CustomEmojiProps) => { {emojis.name} - {emojis.aliases} + {emojis.aliases.join(', ')} ))} From 2d8f5707d7e19fabc75bb26dfac4d920b7baeaea Mon Sep 17 00:00:00 2001 From: Tasso Date: Tue, 12 Nov 2024 17:57:25 -0300 Subject: [PATCH 15/15] Remove outdated cache busting parameter --- apps/meteor/app/emoji-custom/client/lib/emojiCustom.ts | 9 ++------- apps/meteor/app/utils/client/getURL.ts | 8 +++++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/meteor/app/emoji-custom/client/lib/emojiCustom.ts b/apps/meteor/app/emoji-custom/client/lib/emojiCustom.ts index 0ddf5e6d703d..8b24cd0c29c7 100644 --- a/apps/meteor/app/emoji-custom/client/lib/emojiCustom.ts +++ b/apps/meteor/app/emoji-custom/client/lib/emojiCustom.ts @@ -1,7 +1,6 @@ import type { IEmoji } from '@rocket.chat/core-typings'; import { escapeRegExp } from '@rocket.chat/string-helpers'; import { Meteor } from 'meteor/meteor'; -import { Session } from 'meteor/session'; import { onLoggedIn } from '../../../../client/lib/loggedIn'; import { emoji, removeFromRecent, replaceEmojiInRecent } from '../../../emoji/client'; @@ -19,15 +18,11 @@ const isSetNotNull = (fn: () => unknown) => { }; const getEmojiUrlFromName = (name: string, extension: string, etag?: string) => { - if (name == null) { + if (!name) { return; } - const key = `emoji_random_${name}` as const; - - const random = (Session as unknown as { keys: Record }).keys[key] ?? 0; - - return getURL(`/emoji-custom/${encodeURIComponent(name)}.${extension}?_dc=${random}${etag ? `&etag=${etag}` : ''}`); + return getURL(`/emoji-custom/${encodeURIComponent(name)}.${extension}${etag ? `?etag=${etag}` : ''}`); }; export const deleteEmojiCustom = (emojiData: IEmoji) => { diff --git a/apps/meteor/app/utils/client/getURL.ts b/apps/meteor/app/utils/client/getURL.ts index 040b6dfa9dc2..42970bc38869 100644 --- a/apps/meteor/app/utils/client/getURL.ts +++ b/apps/meteor/app/utils/client/getURL.ts @@ -4,7 +4,13 @@ import { Info } from '../rocketchat.info'; export const getURL = function ( path: string, // eslint-disable-next-line @typescript-eslint/naming-convention - params: Record = {}, + params: { + cdn?: boolean; + full?: boolean; + cloud?: boolean; + cloud_route?: string; + cloud_params?: Record; + } = {}, cloudDeepLinkUrl?: string, cacheKey?: boolean, ): string {