From 3f53cbd8f680c5659c53bf709fdd044c5dcb59c7 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Fri, 10 Mar 2023 01:37:22 +0100 Subject: [PATCH] fix(backend/DriveService): convert WebP/AVIF to WebP (#10239) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(backend/DriveService): convert transparent WebP/AVIF to PNG * webpにする その希望が複数ありましたので * Update packages/backend/src/core/DriveService.ts Co-authored-by: Acid Chicken (硫酸鶏) * update test * webpはwebpublicにできる --------- Co-authored-by: Acid Chicken (硫酸鶏) Co-authored-by: tamaina --- packages/backend/src/core/DriveService.ts | 12 ++-- .../src/core/activitypub/ApRendererService.ts | 68 +++++++++--------- packages/backend/test/e2e/endpoints.ts | 41 ++++++++++- .../backend/test/resources/with-alpha.avif | Bin 0 -> 10032 bytes .../backend/test/resources/with-alpha.webp | Bin 0 -> 4984 bytes .../backend/test/resources/without-alpha.avif | Bin 0 -> 3982 bytes .../backend/test/resources/without-alpha.webp | Bin 0 -> 4474 bytes packages/backend/test/utils.ts | 7 +- 8 files changed, 86 insertions(+), 42 deletions(-) create mode 100644 packages/backend/test/resources/with-alpha.avif create mode 100644 packages/backend/test/resources/with-alpha.webp create mode 100644 packages/backend/test/resources/without-alpha.avif create mode 100644 packages/backend/test/resources/without-alpha.webp diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index f4a06faeb..c3835faa3 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -299,7 +299,7 @@ export class DriveService { } satisfyWebpublic = !!( - type !== 'image/svg+xml' && type !== 'image/webp' && type !== 'image/avif' && + type !== 'image/svg+xml' && type !== 'image/avif' && !(metadata.exif ?? metadata.iptc ?? metadata.xmp ?? metadata.tifftagPhotoshop) && metadata.width && metadata.width <= 2048 && metadata.height && metadata.height <= 2048 @@ -319,11 +319,11 @@ export class DriveService { this.registerLogger.info('creating web image'); try { - if (['image/jpeg', 'image/webp', 'image/avif'].includes(type)) { + if (type === 'image/jpeg') { webpublic = await this.imageProcessingService.convertSharpToJpeg(img, 2048, 2048); - } else if (['image/png'].includes(type)) { - webpublic = await this.imageProcessingService.convertSharpToPng(img, 2048, 2048); - } else if (['image/svg+xml'].includes(type)) { + } else if (['image/webp', 'image/avif'].includes(type)) { + webpublic = await this.imageProcessingService.convertSharpToWebp(img, 2048, 2048); + } else if (['image/png', 'image/svg+xml'].includes(type)) { webpublic = await this.imageProcessingService.convertSharpToPng(img, 2048, 2048); } else { this.registerLogger.debug('web image not created (not an required image)'); @@ -749,7 +749,7 @@ export class DriveService { }: UploadFromUrlArgs): Promise { // Create temp file const [path, cleanup] = await createTemp(); - + try { // write content at URL to temp file const { filename: name } = await this.downloadService.downloadUrl(url, path); diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 6a1f233bd..4601eca2f 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -116,7 +116,7 @@ export class ApRendererService { if (block.blockee?.uri == null) { throw new Error('renderBlock: missing blockee uri'); } - + return { type: 'Block', id: `${this.config.url}/blocks/${block.id}`, @@ -134,10 +134,10 @@ export class ApRendererService { published: note.createdAt.toISOString(), object, } as ICreate; - + if (object.to) activity.to = object.to; if (object.cc) activity.cc = object.cc; - + return activity; } @@ -155,7 +155,7 @@ export class ApRendererService { public renderDocument(file: DriveFile): IApDocument { return { type: 'Document', - mediaType: file.type, + mediaType: file.webpublicType ?? file.type, url: this.driveFileEntityService.getPublicUrl(file), name: file.comment, }; @@ -297,16 +297,16 @@ export class ApRendererService { const items = await this.driveFilesRepository.findBy({ id: In(ids) }); return ids.map(id => items.find(item => item.id === id)).filter(item => item != null) as DriveFile[]; }; - + let inReplyTo; let inReplyToNote: Note | null; - + if (note.replyId) { inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId }); - + if (inReplyToNote != null) { const inReplyToUser = await this.usersRepository.findOneBy({ id: inReplyToNote.userId }); - + if (inReplyToUser != null) { if (inReplyToNote.uri) { inReplyTo = inReplyToNote.uri; @@ -322,24 +322,24 @@ export class ApRendererService { } else { inReplyTo = null; } - + let quote; - + if (note.renoteId) { const renote = await this.notesRepository.findOneBy({ id: note.renoteId }); - + if (renote) { quote = renote.uri ? renote.uri : `${this.config.url}/notes/${renote.id}`; } } - + const attributedTo = `${this.config.url}/users/${note.userId}`; - + const mentions = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri); - + let to: string[] = []; let cc: string[] = []; - + if (note.visibility === 'public') { to = ['https://www.w3.org/ns/activitystreams#Public']; cc = [`${attributedTo}/followers`].concat(mentions); @@ -352,44 +352,44 @@ export class ApRendererService { } else { to = mentions; } - + const mentionedUsers = note.mentions.length > 0 ? await this.usersRepository.findBy({ id: In(note.mentions), }) : []; - + const hashtagTags = (note.tags ?? []).map(tag => this.renderHashtag(tag)); const mentionTags = mentionedUsers.map(u => this.renderMention(u)); - + const files = await getPromisedFiles(note.fileIds); - + const text = note.text ?? ''; let poll: Poll | null = null; - + if (note.hasPoll) { poll = await this.pollsRepository.findOneBy({ noteId: note.id }); } - + let apText = text; - + if (quote) { apText += `\n\nRE: ${quote}`; } - + const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; - + const content = this.apMfmService.getNoteHtml(Object.assign({}, note, { text: apText, })); - + const emojis = await this.getEmojis(note.emojis); const apemojis = emojis.map(emoji => this.renderEmoji(emoji)); - + const tag = [ ...hashtagTags, ...mentionTags, ...apemojis, ]; - + const asPoll = poll ? { type: 'Question', content: this.apMfmService.getNoteHtml(Object.assign({}, note, { @@ -601,7 +601,7 @@ export class ApRendererService { if (typeof x === 'object' && x.id == null) { x.id = `${this.config.url}/${uuid()}`; } - + return Object.assign({ '@context': [ 'https://www.w3.org/ns/activitystreams', @@ -634,18 +634,18 @@ export class ApRendererService { ], }, x as T & { id: string; }); } - + @bindThis public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); - + const ldSignature = this.ldSignatureService.use(); ldSignature.debug = false; activity = await ldSignature.signRsaSignature2017(activity, keypair.privateKey, `${this.config.url}/users/${user.id}#main-key`); - + return activity; } - + /** * Render OrderedCollectionPage * @param id URL of self @@ -686,11 +686,11 @@ export class ApRendererService { type: 'OrderedCollection', totalItems, }; - + if (first) page.first = first; if (last) page.last = last; if (orderedItems) page.orderedItems = orderedItems; - + return page; } diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts index c13009373..653520cf5 100644 --- a/packages/backend/test/e2e/endpoints.ts +++ b/packages/backend/test/e2e/endpoints.ts @@ -4,7 +4,7 @@ import * as assert from 'assert'; // node-fetch only supports it's own Blob yet // https://github.com/node-fetch/node-fetch/pull/1664 import { Blob } from 'node-fetch'; -import { startServer, signup, post, api, uploadFile } from '../utils.js'; +import { startServer, signup, post, api, uploadFile, simpleGet } from '../utils.js'; import type { INestApplicationContext } from '@nestjs/common'; describe('Endpoints', () => { @@ -439,6 +439,45 @@ describe('Endpoints', () => { assert.strictEqual(res.body.name, 'image.svg'); assert.strictEqual(res.body.type, 'image/svg+xml'); }); + + for (const type of ['webp', 'avif']) { + const mediaType = `image/${type}`; + + const getWebpublicType = async (user: any, fileId: string): Promise => { + // drive/files/create does not expose webpublicType directly, so get it by posting it + const res = await post(user, { + text: mediaType, + fileIds: [fileId], + }); + const apRes = await simpleGet(`notes/${res.id}`, 'application/activity+json'); + assert.strictEqual(apRes.status, 200); + assert.ok(Array.isArray(apRes.body.attachment)); + return apRes.body.attachment[0].mediaType; + }; + + test(`透明な${type}ファイルを作成できる`, async () => { + const path = `with-alpha.${type}`; + const res = await uploadFile(alice, { path }); + + assert.strictEqual(res.status, 200); + assert.strictEqual(res.body.name, path); + assert.strictEqual(res.body.type, mediaType); + + const webpublicType = await getWebpublicType(alice, res.body.id); + assert.strictEqual(webpublicType, 'image/webp'); + }); + + test(`透明じゃない${type}ファイルを作成できる`, async () => { + const path = `without-alpha.${type}`; + const res = await uploadFile(alice, { path }); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.body.name, path); + assert.strictEqual(res.body.type, mediaType); + + const webpublicType = await getWebpublicType(alice, res.body.id); + assert.strictEqual(webpublicType, 'image/webp'); + }); + } }); describe('drive/files/update', () => { diff --git a/packages/backend/test/resources/with-alpha.avif b/packages/backend/test/resources/with-alpha.avif new file mode 100644 index 0000000000000000000000000000000000000000..05f494212eff008216ce34d939b545aeaf288e88 GIT binary patch literal 10032 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 literal 0 HcmV?d00001 diff --git a/packages/backend/test/resources/with-alpha.webp b/packages/backend/test/resources/with-alpha.webp new file mode 100644 index 0000000000000000000000000000000000000000..d7b0d70b7fdbbd9ef4be7e2d680aeee7b7821d5a GIT binary patch literal 4984 zcmYkAbyO5w^T&4=mTm!oC6-1yrIrvx36X9kq`M_nSXerQMY>nIM3EK{1QbL>IwU2e zq?d->7oYd}o%c6q=FI1wJ2Us*|GwwyYdm=1bqfG6QB%=1)|D|O0RR9DH-Y=d?>*90 z=O6+Afcr7m&CbnO+{>3GZFh=)%2l|}Hr@D6@y{mrHhHPk>X&u79j8nPNyj%*eOYbW z?OCx|0Cfza2#1S#U22_>@=e-pu|DW{?&E|wyvUGS?y=cH--&{SiOwHg{yS_In@I*H zp0oAW$)&1HcExF~y_I~1xFFlUhJs#W_@@vsNKs;f^ibpGUKX_*<7STnHymAFX6JHI1aUEI2mHY<8sj5e@=?6vu+o@bx961xWP z7xZ*4on2-~4D#8j1Me#v!g#VxE_AmR?QVf*YIY@@izJ!FrAU_(AMB}BnQEUydPnih z$PyTXTCG#|UYX;&3i5dRYEQK&rO`fp`wmUWc{OzGMafxd8in&N$IRJdpDLz!JJkeY zvFmV2PAyl>QzBx4e0xI$3gQ^LTXlfSX!>!~^4(X@rBZdG7`RySo^}G&PknieCT-QP zEc|C^6Yei3>u(udLD<{>ym26SmE13qDae*`<&giv61o}0?;?QN*AC7uX^$b3-;`i+ zZi~Ott~%{Ma1akNrR403tqETBqw3i%i!|o^5kl`Q_^^QY*fe4l!lT>u9sl;XXH-7! zwL%dQ3or30IA?)A6?@N)2Fjcz#RVw7s-h` z^yERP72aDViEEK;^1LR z8|_phj?NulwWoob6ENgOMEoF6dxp+IKFWK3XX%zz@b7uPJ`NWj31Z50yn+KBX-aN0 zEo}mxy%nx-i$}d5iUc9m8q3Sx1}WgjXrrDxFYmf6NhQDG(=HV%ZU4ph{dv{n{VaaU z#J4KEP@*--{DN#i=$MGb}I3DE14%d99=)pHom%oE6rT&_+yXfB?61uJi2;EJRM z-=7wKy^03b=~R|Rz8%|i{D~Zmcv_P70Lt5*{iwNe1E}9wB*|tM1s|02*M$Mol8SvN zwJckd3lAwUM8jQbYvCg5WS3hh>jJ?<8!Kf8G79S7Cv7*CR;Z)f02#dt6=XxISu)t5Y#v39FslSbSUOqHrY_CTc ztFBEP)Ig*J*5v{jx!;C*>aLq82u4@T;n4<`M;;vHl%sXm)l)xx^&*bi)9`TenNC?E zN8lMi88jA1MFnLQIYzd>l51L1y;5+I;`&{z9{7QsF(z%kkxZSH@Xm|eIj^k8><6;Z z#X$Y!A1;!@DLvc-#e$>B$5QmwjMmbszgPx-CBCi*W6r;m*c(W1I;PINEH_8bP z-mWw#+KaNHmW4}O^sSYlV-|9nTB2O2b9TA_D^Whw1vB|WEm1*hi!v&rN+JRR^ab1| zMm($zh@(4DLCzu_R)8q~My=+h6h-Suz~LiAbWBn_vKuq_`oDG%I&TBQ(FIF-2LjzT?W=%at$O+5vJtv1V)pdhQ@_ zp1yAU1jL*Day2u`0-eM0#8|?$`^TRpItrH~@i@i-K{lnp-tC;Knnx-Fha^&3)(UT< zs$OJIci5WlfQg?kPw3J`9?B4uo06mM-8Ft}O(PkqOe=Mr*jD_oG zOjH}kMizU{PfjhB;l!S!G;y(=by0FZ7L7L~SJ(W(V(0g&!r3JRCQIiC^*%%V3 z04if#L|%ZmEmEIy%!OFxh+m11H>N!IA~4q#~l$5U1-!>GF3KR*{NhA z5~~@VzF(n|#2HHFE+F{c4ur>k_bNJ`(h1b2{}Nr<+!EkCMdDlyR8CO`aH>1k2HD&c%cnH;`CE$9{QI1Re? zqd|ELr=u3>YUV?g9b%scfCq@65FJ{ev<~htkIiov`9h$gG3}=971-P;sGzR<3txyz z#ya>X9fi!X*RBX5m)0Zsz2SXt*B{FJXhoqZg{h?v9TgdL8-yo{w6(&A>Wh-fQ^pOS zv(`F27wheh^A@D)BM2Xxw9aBCa!=fFzH7bDOAUY)TH*A?CT`QMt}6p|Z%8blBe*y$ zjxFj&J3uvW#DgddkPqYs00BTn7P6>&NLLC}n6*s7-Mzk1sI%E^N9M#&G3BeBaY}8u zv@qO${mIc)Icy>RIppN>a!OzJ59V?N@g8A~4ab&WWa4mFO8mH#P_YvLf*ebHoq2&c zqCCM3E8%b_`&Pg4E+`K?PpoD-&t7Qbgs}5}@QzI|cuRN}(uYcCot;WJtRZ3s4hRMy zAUMLU-0O$ew@*CJJFh>6<19P(aaGs23j(a#)f6^nM+divqP+fyW-Z2bDQH}5V)6DZ zFnDK#rr-%kEad~v1!ZPeDF9aJy1IrCO}JG_qtmCvy!$`%uQk`lUlY049`2`d@g!Mz zjO2P#&cTmUe1zQwNfFWLGDlpyKR#s5(4x%Le%1DQnVow`S+yG~IT$}C){3v!FPGZC zl9rf5=17(hTsY*_;OMwq^v=ZjL3E}Pb&_Cq;l!G2`21h{egtJRxa};PasB1<-}T(D zjmqiwpTd?$NoD+)v_2g;gY)EWi(67AsPsyje;M+4P7Y>*#B!MJI4?4}xhC64R%$j) zH>+T6Pe>KeVM&}GF~-xeg9IRCgAiqQ8-AM0I{)|x*2mZ?i2K-C-Y7+@5C?K+2T!Vlvd}In_ma;$XAV&)H)WjCX-umhq?Q7b zUa_|JUf11VD=Ttq+<7J3d?HT(<1@HG46f(LhR`1KBP7pf(Z;*sm~u*LB<q| zcs92&FrpGw7deJxFlQk{-wAht)_#@t{n8qyuYJp8jqGxbOi}lLKHPS+={xb$rbE{EsQ57ZQU3&8yuS%gcfzXgXKxOJV0Xt`zD zWOLgUTbZ$NjxhEA8A!=|`A#%40Aj=Mf3mr>aj;x4}xPxKhtCSrb(D9VLHgcfnb1qZTy z!%R8KXGT_>z(%$?8@g@(z=hR@docR=%KowO1_?thhwd+r1RKVOTKWw?HH$CAzA>4} z(0|L+lw^GENDAiMzDFhQ+RFmx?p=!Q7HPiAszq7yJQkhoZ%3A(#gg|BdAY?r99pZs*vNgJ?chL@I%ZVDy}sG9ObhcwSJ%drYJUf^j%>Uoh4gdLuxVJm=0{Yn$x7}Ob0h!>0}Up4*}WpU!z|) zF_V&Q@vHUHuLuJ6>3Y~{2K&G3&#d|eZSIpv(`=o4ndW^f>ov7=>C7~N=h!z;C(@HI zO@x8?cGCvt`m=R0(UQ0z`=8O@-Ms0l;lqsr&({NuIQPt>qmR0zHZKw8yD9_5nb9D8 zq{4nd`qMsN;cdDTl3M=8TgAb$ndZGB@xR~qA$=?*7o{*-G>7kkq8(WJE{}^c6;xMS znxi~kSfW(9YFkvPTXH43JOp-%D!fLkNI<)vwKDzBx+~L5%OM=j_Br`%xA7((Tj7Mf z3_kDZ0Bejr+|-sEcheIe3OS?OySg)ogmPt_O`Xi?L|2rG%wCoT;eBs@`|ZrKm<(T| ziZubZ4;~Solktb?k0s6W0gp|W-YCX{BI+f`q+f+JcYrYPQZ&7T!1ChtaI8 z3};Yc+YIV4Qkq+#1}kh6Bzy+`twNN6eX9kes?pOnMAseOpxhHEy)pop&3X>>KF+KP zZrzGY^89e#!gmXkEdPaf$EZl!&Th!o><7hb`I<>PosSB_HZ8alxT*#IeYzHx7Vry9 zAt5k_(2ELA?-gngG^F=f+P<}8>9@P)80ip_Io=!!HIp(?7UAW)~OaK+zmPPiK&RZgN?oo z^DpD_3}#k5o=r8=rqdx%aeVIwUEP2Rt!pIa)$V*QZW|F&zqJl;$D8d|_c8|n@Cf|c zIT_EhuYZXgedOnX*jk8+*jV6br+ZVqM$pYTDDLCVNWw8T3tRQws-#_MO$DDUj$={8 zK*U!|eq#^Qrprd!B)7JuHxe|^Y<+l{!nyK|~ z9|Zsvu1ggzL=Af@lbk4B{!jw8FF7k$M(}zp%#aNE=Kg5J2jk@fl}U&jF*iHzX$d5Q zPJel8By8I4!inbKocibMB~q)`zAfM=Mc8qn+>)nyWv1ke_`7kIzV)`4sqs${?K@5L z{0fcxtE11VB;RtsU{(q^(=vL37?Uh@r4V>oYjw)_ab`@F*YYV0FPE}aZ;3XGCml;Q z7#4`fcO;y?__$>e-F_;rQNYDF9W3Vs24Lr25bs_DQOcZ@W5FGo}LOB>|Of0*qz z`w1xu4LE&CQ^Lf+OYkzI;~|}NWXjXdK&z0{{KE-05lHrAQTkyaBk0PLl-)%zUfA+iA@M%hdb!EP6_UJi~r@Sx(}nvv#{~xf8)%<%CJ_Hl-pS?FeX_96 zHa}(MFk^K${aEVj@W#2>9O4boPiEOhBvMn$lPUiU4d0cQ%_DNJ&B%*T*(9CgdL)7D z?CFSJh_ujN8}T(K*!e1#03b+<7Nw%dz#T3pGI#A?1|)DZ&*WvA8_*p1t0*>0P${w<;`!@O)vq$|77DE zhWyKxH_Z2M4d{l&fFJYv~6U$ef6zjOXCd)YX9_z8R1`$46IMTNxy zU;qF`2*AIo5EYjY1)~3VG6?{VoQ<~c`3_Lj|Mgye*XY_j4gnc2H!M7FX= z{O|nw{r~^x^?JVV_wzjOXM8^Z05Ccr{ovL%9+DA}G(Zki_>Hx^!3J9lD_Juj%94BOeA?*Jh0088+ zc3!qv0qAOJ01S?RV-*i!a9a;7-h_FD*Tr}=v%Kx*wHq!=nkgcDJ~ zVZJaN!6PEVC8%vZToLZJuD)0`82}2wrs!dCH)|~747-#71P+gg#NNU0W@ml%R_f|^ z09-F2Kn244-z^Lvf-7!}t=#&{EwP;EuLk!5LLe|efkoWE!J_f6^gdXQGb^m)F8&YU z)-ZP@zq>t>MU-EVUlK6v==*J|)>C0kT#jZo@7r=;F|v*iw{h z$--5sEsx;&YWYQ^np`h!?cAEm7XsOX-^R9FrXcM`iY*;e-rJqljhaPcnFCLyvZfxeRJZ z*%EGE7W4@Yt3zm&dY)hW884Ti|Er@rw=^wWSbD21Qx!n2wL5stqE)b|-3G8{MZqG&i@L^xQf= zh|#zEnniD^@0nO(v12$ey>DfOlMc|(cV0f!xxBkxYRRl>HD zpm=9iG6Mu;GX5SRw-$V`R&vZEc*yzWvZjxA@X`r&p{b^g3wgnC++7bzG z-;|U*HbVDxUMe6^-aVegwde7-Ax^xc29HyuE%O|70;DCk*wPlBnt{SbZWA6%5VR`k zB!C2Lm!9-M)*Q-7^~pCg&&G8}V-6}R)Ylfi_Ia@&E{_Vh*}bjTff;=gPJNiIs6|K& zO6MpTE%1WjnVP=3Y!vjvIGacucYNizd(EaRH{-%m$$hTQbdIo zx2o&4v#mPNeJQ)7Lh=3}Oo&j&QDH=TJ~~8cYG~l)StF!Sz_#Q^$F>cvbQzM>hVikn z!?0!>)q&gTj8cfV%$%Y7F!zq8AD_0gveTa#M^nZc6TbB?+8c`dvHjh>UchYqtNi{B zf9iYDQ*dG?f@rv2b`EF5sV3FS%PS-3HO!`KcVIf8KV;3B!YpR-IMy;t*!qdj3$pe6jwEpX@iGrvkCDKb7;n-dV^P6Q8iV$I9;oQ7}f$dIJ z^sC@>iJIbH-}kAQK^JdyTg!=54i={v5F046q0Q27B&5TbXhyc8j6~kbPJ%J0KX*Z zYW;ZjjsB-!6Af;?n(f)%8xP=iqnhWwy`!bZsnNDZ-u^@0Osm1}Ek`A_Go;Nd220v9Aq$QnwHluP#HGbB@}(P1$%B1 zN|efSeJ&}yNCE913X?1vaSN_KFz@zQubfWG9gfXO9*pba6Zz+5*&av7H-TnxJ^bTp zkpL;=agV+m5-}>s!Y@ve;OfXW{iUUOsD$=mlv9mtj6AP{TJ|9D z)x&3H7=HI$5?6KOlHO$HApkgw>ynC%o;VLPmuW)I_(NT`FI~$E5=0kDq*?sNd5@EY z@B;PT07_5aF29P5-WZ7$4UdD0i_|zvMsJ01_$%}p2Mqr6Z4or^SjMv2vCDi>u-pwD z?3tqmed)`QJ;0Wx(~613msN?Di}7m9ei3@aFyyvKP+Pv1_N5X7@u^B|QN#6rDXmJ5hFY-tqQ$k;aK$I4XP&UrO>}A`$AJ+wXpc zMn|682gW8&8xU+^SljC}@|=hS24~0&Ky^1W)l5g1 z)NC&9q)UxSX?bYiecCLgBB`5vBj1A%h=*ai(gWg9Qy0c`8#GjN$dmDgX`c7>xU8Qv z{`xF$Iaju7yV~zI7g0u0sjTtxAKQIfP`tSao|W-t%JA*l73FG4yk2GOE&N#{dJrQM z9b$d{Ya{5fEMdVch{Z5YSvx}3asSmZ^7R%RP0};;*t5$fw- zVoL{PI5M3on=;@piLwjZY45*v5mjQA9>gdkkJC-Z#EWHlPkWED*rr?t1~%S4O4UF{;6&qFx|k#^)`{@~&RqHeyN zlGG(~?Km9-FG9n2UJ#8(GdNrJ+)2;MJDOC0Xm?2UqU-+4rf?#G;<}-7b{d7*7Jp)=ktJVMg^Zv9FZcshW!ZlO$L^+JhEOp+7L6vU zHGQuvQInFLX?lcJoX?Ym>P)^6J&UtNi4BOh?bfF(Bq0k`SjI$clZ{9*hJ7pvp-WCv z@q^cEKE{i!JBh`&8B0>MYhSk&MJg3chQ;JIN~=*NOg5Zk|6WJh zSpEB*I@V)W2^;@yRu}mQTL0-dT3*RZ7REPWd?@ICQ><3pG&x#LTu8SReBI!PpVzpkZxy-n{72;dJ^vwU6!(H|qd2Bq zw*1bu+3F--_i($^=L|}0m;Fh~CB4^DVJF!MtZE!VtyY=ef%gAT&8%bR?at7%xMnDes$ztk3GxX2^gDfz7$(_8MsXw7>eAF3Ev`*vxRI8V{X zlJyzkbM@ap0-}Ar{FlWc@|16r{Ih?Of2;hYB7FUhXT?r<FT441hb9Nqw+$o}d z@ky$=v2K=EGf%RJyj6t8V5sS|djz<8A)mCNNzc4C#Z%G|pPsLAK zTbyNaotu?ctn0#T{LJ$>N&+aQ%megJCUQLW+0z>sGPNtj%sKmgI@L|mmP(PCr43LC zMXL?u8ebzh=RP`@JAKk~m0ZirDI}@r^MoG##E=q;6MKdW@6`HT_rX+XAMfv8x0~OH zxG#7p<`CI3g4{iRQabx$H6m7@FJdXt5l#)w585F;_MqG&#XTOn(LdysZ({lt zCi?Kl_jdfUIN08|``MV3^*M2DpN$X&sJ%x}d&KTAY8DkKT9i=K z-m^#%`FZ=j-}n2T>)h9KpL6c#KG${rybWPmS}*AV05eT>eN%mTb1DD;0KNqA@47tU z^SS~6)WHCGz?%Rd0I0%E6Q&kg_!eL*pD(-noHTyDQiON5qx7QFCrh8smq2|acm_F7 zIN%2OQ{a%(yL$oq6yo2Yc0nhE#uM>#Rw9u|SPB{?bV4JDG#C4Xs{aOm=|hyJxk8oz7uZ$Qth&!2Zf`xsxP2tFSZ?4%sL6{d!TdZAGs3L8%xN=dD!3E?(4*xJ1wLCY5 zTtIqlL(fdM98Q}waz#r|y#HIaaMf9@WCVZvHn;@w?#5u@?7{=l<_v93u@V6UVG|OS z_f>j!D-)oByo<_h^4T*C2rDC<*~0F7GsLEj2u|Ra+h&=mr@n?ej(;GJxNARh-z;7r ztpM4m|L1xY#sZV?FY_P_^+B|U{l*%by%Mz}-#5LY*nn@2R%&SiF<+U?jg8~0+AVF9 zNEE#q-lv4okie=LX`^Uf*d+27xK~O4{P}b0oZlk5OFpNy6&fB5*K5auL?rTLa|G_B z=~5P>(6!i*WW-^hy6wobkE|T2gs3@Nig!zPp}9_cD*=OoY^X=~EXVf!;53#da~jdu z5^i-4x{bPPey-wUI=s#T^Wyn#_I*?4sRnVTW%8N9W@w`_j!r;3YyCw$LG9=(RxEYF1u%-fxlMa@uQgPwWRJztc4b)bEeq zYj5*0R{QP43+G>UgnG;@++;^Rm3gE9_`Jf6P3GENwAY58XiSTN8IS9=DwPb4SG;DK zayMee^BY{hOC*}+Wc}t%w=VyNY_ktqA&;3S@)aAV!P^?lbW!Mn(qT#jO zM65J(-)o*TC)`Y4qf4)zdDfdF6ZM+s_sq>LtsN@G-Y18h!>KYgrDP6hp)aL@!M*)J)e~%WPLG{8c~Xe#TvWQl?iZpjUp%JfLZ6^z2sG z*Xv4N=n{@530o)+?>Q9N+r<6Og~RGyxL>17Lxh;LmwVG=CtU>^$#6>Sz&KT_?$w`a z5~GFXKk}y{v)bi6C^F&oMR%BD)Mck73WAvK%zWd?jNh=uCA#YPAzW5Pht#S|J~u~d zQ{_kA^7<_q5@5<$e_d*jY2bH_$C7N(0;Tc&J5C*a%twb83TtAeQ}13qAq&~*$j;%3 z+FST8B2uPTF_IwGbzY@g+c3%~I5Z!9a#WX}Bf7IbJ#bJD&7Ses}3>3+PoEFPNNBL19qYVl%b?GQIv&K2z(901r=L<>2tE z;uW^{@g17(az(UNZGnFrEEq#hqDB|onhKohq5b8quX7rj1DX-Sm3Yd}2cg7Yjz6YY z*spe*^z>*kJ9Kz*zgyEVZi$AJ#&!Vo+mP&24t6-s^Ek@k8#J?iDYXj9Od?1$S zz0o6JK=DXn(Kjm!q%iLRNiJqA0Oz&!1yP;L%?` zH??{n8#CQQ*=|f>@5lIQgh$EyTCTAPQ&S0&l0>f_4zsHFNmhS}lY3sEd#LFDhfyj=)okFCeR((uG4OuP zvhEYx<~L<=Z^v=nu+eR+?g9DcUlUo{0-nu^LJTZk0<@^$15L<$H9rv~ZDE1H^X5U4 z&(yDIeD75g#bP!t&Ko@Cj!Y5G_q;vp_|bB&QnEBVQ!Wy_ZoZ>;57>6`>nsS>JtqxX z8oC>YoBjj3>#i2!DP<}ZPt}mS!QAX#qRN!n}(7nZ0Y-p#iAaLbZQ!&Ds z#5Sl3Mb7RQ7D3vlw(}F_D3=gsLW@YIFAf&L_>OSwy$N_l%>sVpmAa@g;z(i|yvG@c zodia3XGjm8*KShh=Nd=Zq~9{cp$GWgJgT<4rXv~Q0c}eicP%tkAYftXY7=@^mKq9& z3~&7U^}cvUI*=!6H=`PL>b@Rk@Wxr$Nus9T9}J$u*w>$rnxI`WvAQg{8Pt{HZRPts zJ)vQLY?{aCQ&OQ}wghH{DP^ZSyKoGUuKnTmU;?JS7MA!Euj(77yK%ey>$DlHRc+(c zby|cjDch%LiW+Z|{dj1NsFqT#xi#mfF!kewMFGTjrnn0}uo`83?aa7cFLcpE-#7?| zLgT9qp5d82xQDjku}sOm*Iq@<+_CP!gS}^-U5N_!nXkxA+F{Qb*i@PF*kwtfnq0RN znUdo}SZN0Z6p)ct4OZ|Bd@SkTUaH`NU?ebVyUI!3E_XH=)D^)@=4+ghP0;$hf+C%e zj0)99O1Yi4;H3ST0;`-FE}&&7q=c3+gwCvKggr2(tjgNrBk0)^eS-cWxFLnb?o*OO z-vN^R&$J=?5%79FNkl)}a6(MUI%W79a^^p_P+AWJ+ix_A6rQsx(|G^n$B8F*DCva?)M= z=72VH-vjj3?W+cOhBX;sP6hZC>V)wpGau2=p$-)noB%M*Nl1^sc`oA1*>KKU_0F{p z%g=n`(Qa2TzyM61mcvnG4KyG2hx@1>TKINP&5*$O;p_w!S6B`1zs=z{VL0-B%;3?} z2px%dZ#G#zyW~#Y;BuOt2J}8dVwj@zk1m_|>SdsCJPYCbW`c;477|AP)MoGd+XD{zO|><0FfrbwBkmxZfpc=pK?7&MRqvuh5ndA=D@I8?x9nT|Jl!; znzi0%!j(;U+hM~!4(4!LrIjsMqZq?t9pA+Z4c}s0CbvnxZ>b?d7B}zU`v<)XA7;2w z9xy%#4E*%kND3P-abn%r=UUJ8^|G-0xihR*CA$?)qw-wIEF5CTiy~5on zbRJ6RaitmY>4{abm+i%xuGvq%XpN0xd7>TevME{|Xk2}W5`GLk6i%ML%6*V3j7CW`R8?P?jL1rgbeN?{hSq$4i0t zFoxz;+*)!dZo=KThR{ zf;7*kx=s>cd8ifF-mpV4!Mlq9HSm6brNm&YH*b5+tIZdHh%m`Eu4&R@q)m@&akZeT!Tond89+xZkY z1yKWJ*3ymi1$=z*%m#r;%pB&ic4-;7*R|^P;ZVc_vO%*qaqH-O7Q^i9WqxN7!$b3X zV6geI+)#1llDaK$XHVo6Oe8JmEFGa`BEWdOd&oWJCRq(UWD#VZMF{o{1=7;X!6yLZQOehRnUQi@V{A5x*|mW}dj*?qXT zXaf$|(m0@3K_2vTVPX;2j$X$oIMlxuGbY_%$_efIk(1C99HjewC{LL`QeJtsS57Lp z;GJeAV$=Cl;W-6mwK$Gmr9+#Iw`IEZFn(&>LzFXdl-wh7*hKiKcR1D*esIe(Jkp-& z8>H2C3bw7YyS8*<2^x*Yp1ZcP-!C23X4{%le%hj?B>Y4T(jzWG#_lKBGcYLQQ-dmiwMri|H+kYlisbS#U5cl{e z0{zjG*UeuY{XVGHLvG#1%MqOVL8m8Xmmx~HDCY33{0yF-uX&S!JlchwTI)HDj|^I= zZs8_{xqJ3Sgvam5t>=xCpxA1^vS%LZ-%$bg8jE5dX4}9KRKalMYwglMW>rVIpcuan zi_Ft<3WPv*$Zr|V{#LMk4=^rCRbTC{msg6x>u}g;NE{-+XqO8HRMABO87CP*_@ zKG-jX4XFfq9PnIM4H01U=)m{Mi%wt;;PghK#3HbTZT#iA<@mA5WM)!~4rqPagey*y zb|`E)A=-)|dp*tsEn_-tHhi-3YCB7YN3T+#Dq+M=wSFb)N@Q$Ht^_%i#?`MNa4( zwLg1}cwARmgJX`wGxnyW!a^<2yQInwW};k%*m2x(H5(>xbJZtxVnW|&W{Y?Ke$TCf zG^$-RxB0O&rHI-$*tc3-Uc#~2aT>>a)WxW!m)uZaN(sheaOXI;O>m>KFIaFMIUU8V z({n^Ovt$M((pq3nJCj39GOE!v#_-{mVaG&iqrL WO9AMwgQ0@_CHvP+QbOXN+y4NnZExrR literal 0 HcmV?d00001 diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 37e5ae10d..d1a5d6d94 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -204,7 +204,12 @@ export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status: redirect: 'manual', }); - const body = res.headers.get('content-type') === 'application/json; charset=utf-8' + const jsonTypes = [ + 'application/json; charset=utf-8', + 'application/activity+json; charset=utf-8', + ]; + + const body = jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() : null;