From 37581ea831d10efd070a11192fb7d0cb2711dc99 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 20 Jun 2022 00:33:46 +0900 Subject: [PATCH] feat: Add Badge Image to Push Notification (#8012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix * nanka iroiro * wip * wip * fix lint * fix loginId * fix * refactor * refactor * remove follow action * clean up * Revert "remove follow action" This reverts commit defbb416480905af2150d1c92f10d8e1d1288c0a. * Revert "clean up" This reverts commit f94919cb9cff41e274044fc69c56ad36a33974f2. * remove fetch specification * renoteの条件追加 * apiFetch => cli * bypass fetch? * fix * refactor: use path alias * temp: add submodule * remove submodule * enhane: unison-reloadに指定したパスに移動できるように * null * null * feat: ログインするアカウントのIDをクエリ文字列で指定する機能 * null * await? * rename * rename * Update read.ts * merge * get-note-summary * fix * swパッケージに * add missing packages * fix getNoteSummary * add webpack-cli * :v: * remove plugins * sw-inject分離したがテストしてない * fix notification.vue * remove a blank line * disconnect intersection observer * disconnect2 * fix notification.vue * remove a blank line * disconnect intersection observer * disconnect2 * fix * :v: * clean up config * typesを戻した * backend/src/web/index.ts * notification-badges * add scripts * change create-notification.ts * Update packages/client/src/components/notification.vue Co-authored-by: Acid Chicken (硫酸鶏) * disconnect * oops * Failed to load the script unexpectedly回避 sw.jsとlib.tsを分離してみた * truncate notification * Update packages/client/src/ui/_common_/common.vue Co-authored-by: syuilo * clean up * clean up * refactor * キャッシュ対策 * Truncate push notification message * fix * クライアントがあったらストリームに接続しているということなので通知しない判定の位置を修正 * components/drive-file-thumbnail.vue * components/drive-select-dialog.vue * components/drive-window.vue * merge * fix * Service Workerのビルドにesbuildを使うようにする * return createEmptyNotification() * fix * fix * i18n.ts * update * :v: * remove ts-loader * fix * fix * enhance: Service Workerを常に登録するように * pollEnded * pollEnded * URLをsw.jsに戻す * clean up * fix lint * changelog * alpha-test * also with twemoji * add isMimeImage function * catch * Colour => Color * char2file => char2filePath * Update autocomplete.vue * remove clone? Co-authored-by: Acid Chicken (硫酸鶏) Co-authored-by: syuilo --- CHANGELOG.md | 1 + .../assets/notification-badges/LICENSE | 5 ++ .../backend/assets/notification-badges/at.png | Bin 0 -> 1752 bytes .../assets/notification-badges/check.png | Bin 0 -> 577 bytes .../clipboard-check-solid.png | Bin 0 -> 1402 bytes .../assets/notification-badges/clock.png | Bin 0 -> 1131 bytes .../assets/notification-badges/comments.png | Bin 0 -> 1134 bytes .../notification-badges/id-card-alt.png | Bin 0 -> 844 bytes .../assets/notification-badges/null.png | Bin 0 -> 174 bytes .../assets/notification-badges/plus.png | Bin 0 -> 507 bytes .../assets/notification-badges/poll-h.png | Bin 0 -> 689 bytes .../notification-badges/quote-right.png | Bin 0 -> 772 bytes .../assets/notification-badges/reply.png | Bin 0 -> 930 bytes .../assets/notification-badges/retweet.png | Bin 0 -> 798 bytes .../assets/notification-badges/user-plus.png | Bin 0 -> 991 bytes packages/backend/src/misc/is-mime-image.ts | 8 +++ .../backend/src/server/proxy/proxy-media.ts | 49 ++++++++++++++-- packages/backend/src/server/web/index.ts | 44 ++++++++++++++ .../client/src/components/autocomplete.vue | 12 +--- .../client/src/components/global/emoji.vue | 9 +-- packages/client/src/scripts/twemoji-base.ts | 11 ++++ .../sw/src/scripts/create-notification.ts | 54 +++++++++++++++++- packages/sw/src/scripts/twemoji-base.ts | 12 ++++ packages/sw/src/scripts/url.ts | 13 +++++ 24 files changed, 197 insertions(+), 21 deletions(-) create mode 100644 packages/backend/assets/notification-badges/LICENSE create mode 100644 packages/backend/assets/notification-badges/at.png create mode 100644 packages/backend/assets/notification-badges/check.png create mode 100644 packages/backend/assets/notification-badges/clipboard-check-solid.png create mode 100644 packages/backend/assets/notification-badges/clock.png create mode 100644 packages/backend/assets/notification-badges/comments.png create mode 100644 packages/backend/assets/notification-badges/id-card-alt.png create mode 100644 packages/backend/assets/notification-badges/null.png create mode 100644 packages/backend/assets/notification-badges/plus.png create mode 100644 packages/backend/assets/notification-badges/poll-h.png create mode 100644 packages/backend/assets/notification-badges/quote-right.png create mode 100644 packages/backend/assets/notification-badges/reply.png create mode 100644 packages/backend/assets/notification-badges/retweet.png create mode 100644 packages/backend/assets/notification-badges/user-plus.png create mode 100644 packages/backend/src/misc/is-mime-image.ts create mode 100644 packages/sw/src/scripts/twemoji-base.ts create mode 100644 packages/sw/src/scripts/url.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c34699375..639d5f2ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ You should also include the user name that made the change. - Server: Add rate limit to i/notifications @tamaina - Client: Improve files page of control panel @syuilo - Improve player detection in URL preview @mei23 +- Add Badge Image to Push Notification #8012 @tamaina ### Bugfixes - Fix GenerateVideoThumbnail failed @mei23 diff --git a/packages/backend/assets/notification-badges/LICENSE b/packages/backend/assets/notification-badges/LICENSE new file mode 100644 index 000000000..841c4c682 --- /dev/null +++ b/packages/backend/assets/notification-badges/LICENSE @@ -0,0 +1,5 @@ +Font Awesome Icons +------------------------- + +Ⓒ Font Awesome +CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/) diff --git a/packages/backend/assets/notification-badges/at.png b/packages/backend/assets/notification-badges/at.png new file mode 100644 index 0000000000000000000000000000000000000000..d1492856de2b2f837a24a439eaa13631283aa87a GIT binary patch literal 1752 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 IM6N<$g3e8FJOBUy literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/clock.png b/packages/backend/assets/notification-badges/clock.png new file mode 100644 index 0000000000000000000000000000000000000000..9323f8f3070dd4ef538f7df33d54975c449d7cf6 GIT binary patch literal 1131 zcmV-x1eE)UP)i3Y$w051RofH8pm^gqn>KX0r+!CuK7!p-*O z9EeIVE^1V;W#ff&4c}r18~mkE19;;6EBz>+DEzL_;56P{~o zbB$-e;`f;498p7da*fT`G^Q+|k^fb{#z#Dx&cP4LHGigw=2k>hu#=XwIjPviu`LcN z*w#(jl+=8Ipi8P@_gkdRkm3Nr$+<@P_1>4sL#S}=EB})N5|*Q@*8DxbzA^O(q_!0X z*fw|Tc30EiNQKb(nivYn!I?~Sp0;(-Kz7nq2eej#I!BzaVrJ-gjzUuFh=`q01zX#s z2XqwDdiwy)PDs|%oC6pUVLnihFFiR2@b&tjB^X4`0gSL~rXbV=;!6l%MBs$n5(qCP zfWvoz0#EHPA%GDF=Yp0%aA^UwzQQZ;UHv5lFv9aLVCnly48Ut$&=CkNHGqCkG+XOg zS^y(<$`EV$DJcN6C7St3*HK98UE}7OfU)~2Er1cW)BFQXAnYmuXqRB-`d?ZA#wD1! z{+AYja~|jkgk2?o!6o<(KyW;YK+x3!czZ^m2?Sj&0KF8e2?Sj&fMzMSArN%60N!>+ z8v;R>4uE|TR$>5VDYnD_*vIr1KtmuXb^rkPdF8hf^dJzlN9PWrCm27K&@2V!5Wu%@ z8*73wtOmd)&czr9A1d&aq_;B!(gzxR*dK9v2Zmrg>QfZ+)3BMrFSQ`l3ohU|uW^aCaOEGAnHLIJ%FvQe*sX{8)A+rR z4&@U7zO;;ikkbw+E5CY%QDR4wl2JbF z%Km;0GfjZsPV@wW$&EeYJ+GiXz|F|D75G=s1Ov;3O_0ML$nCME1UI*vW8iL0*V!Us z-QPkkudt+K=o7ghuzu7@iXuPcaCb((=8OHh62mv8FKhk`rZ$=O4VJfAOj$ml0* zeV~y?5XBk)3C25Ozy*x|L;LpSc*68^~+#5Hx@gEPy`5mNRh?f8W002ovPDHLkV1iQt4c7nw literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/comments.png b/packages/backend/assets/notification-badges/comments.png new file mode 100644 index 0000000000000000000000000000000000000000..bc8a1c35b4423bc08af88c3c72d4d29c6e81c6bf GIT binary patch literal 1134 zcmV-!1d;oRP) z>yfK4428cED1j1E0wtsbN=OHkuq9B!mOu%VKnawv_lMzz&0?N@2%|G|e(X#(v5$o; z%ZdUDg+ifFC=?2XLZMJ7!XX24-~l{=V)#3YHlhTPVT64F4bTD|&;vg!?1$HW17Da$ z(#2W9D0qbtdyCL}*#R|XQFTEEWEjyq+zYW^8u?2L+!l^o* zk#kL#v&Pb$ME>v~P4P#*+)3pgIWpq!;im#L>{aky7>0wyet z#f(n2Q2q2mXaw|^b7_gCvr@;2)b^e8KQYEehHju5T`dxk`~$D7F@zjzo*ilJFxm5%-(6lW=$N{WdLvMnZ4SML^lf_q07# z|4|E=xCHjhAHrOCiNzdKAz<WXp!V#J^=q2?<@?`#X;^FyW5=6ZbBNiA;3wE$s4u*3c}SKk?e z)vM1=fIvRkW4FQ(USRR+GbioS@sjwi!_FmI-B8L2x{l?XeaN}bW=wO^wf?mWlh73+ zv-?1*XMu+0z7jFYbN(F4Qmhf`ST8+~HSb>=aZXyUr^oo%wQed^R`dk3E3GSV%N z6ulXLq9nbip>ruvC{)B3&LwNN@`Qtg%m0zhFrK3!bo4fvXHXKS#X^s<-Z?+McOk?d z&bFe&Z(^^*oKXt4r4mZE8^>ir)2$dU{=Yc(gmkjS7WAp-?Ck3WY+UP}~Ur0DhV)Yg-p@t^fc407*qoM6N<$f)7F> A<^TWy literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/id-card-alt.png b/packages/backend/assets/notification-badges/id-card-alt.png new file mode 100644 index 0000000000000000000000000000000000000000..67e1410e34cb2a6ec1a17ebd89dc15fdfff87205 GIT binary patch literal 844 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>U>5dtaSW-L^LFmT-qi*IEz4QF z7PL%p;M8msy42_mV`?-CRoLg;bL9{)Y>V9d+f#Y&BIh&jCdjn$vP-lj0@0cPrV7Rk zhCTHcv~#juf)1G1G8o8B*nR6qY{kQf$GR=dQ!MJ9oDYqAVP732_jhvK^sot*`Ahg0 z?A_9IW+TIi2!<3L1|u;B$*YVu5BzUNG00vrPgUN(M#P=Nb1#wCB8SgU{OpJq7K;kG`IV}<%^ zhFvSn@;?g(gqty%6x!Z9!ns0wHp8nGV)?<`AlA8xbNVIcx(Kv%H=BU$few?`jOq*ea+oCw-z(Vj8%3uzCZSMW?gmLGpir%Ra<^= zzuuW#o%x;h1>3O;CzP2Z4EG3Ep0{gYyKY|ZJ?#pUPv(V^=lK(3=B=Nw z+U|yBPRzmm%2G?}`}UjpeqSv=p_KK80ncHBlF$6IQhO)62ZpQ%<`M=^S3j3^P6anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Ktgnp|vd$@?2>|`|Cv*S+ literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/plus.png b/packages/backend/assets/notification-badges/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..05362c122bd2162d5c5ed95c7e8f2d40d9e7eff4 GIT binary patch literal 507 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>U_9jM;uumf=k48txwjnz94=}) zs_syken99-qgNoOR-$(Rr;elQ4faWPGaJgLKZxi3$NX%X#N?k=5^iRl6I47e-Pou6 zNKo*6$B&ycEB~4bZ%*=?9?`d}{L9|Tb^F8TM)mFYW&Gxw+V$;mzVX~W#ag!W|K-Xp z&!2lj<}YI$koI{0aeJUL^8p?PV}>&f2Bb1JR6Q?j*?dNZ-A*X8R_3A3<-Z z^}k6y;5YrX=UQ~E@iPLS>mnCG9pu;2H8Vsv};7j_L$APM%n zkN+1fjC!p+QHb5P@4}^TzTwz({5AboFyt=akR{03trk AK>z>% literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/poll-h.png b/packages/backend/assets/notification-badges/poll-h.png new file mode 100644 index 0000000000000000000000000000000000000000..3b7ded66597679a8d19ef9eccc09834fa7a4f222 GIT binary patch literal 689 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>V5;zRaSW-L^LDOd;o|@S7wIMi zjwVGN7Uu|#!~~8;Ale|nbWngr(bA)R&8e-?w)Kd_RpCf1U?AZyfd->s6^7i ze~XveomrFj=hXjW`qq#4U5vKhA;xk4sP@ikhTpqoOF!t{J9(G+Lw5bLh4)$enGWuj z&~lL7HQVIXfxAlr+25)4S{0aQUTNIzxtQ;cuT<%Syh|&X%O|_Z?wD;7*=)|B|6`f8 ztNOL%X;n9ua_w+!;}6=%GHcDj?35o0-UivHnL<{Gzhmmsv~J+~dMMVxFqJ)EVcZUG zjb(2bKuYSQe;u8E;)7$2V%ewVUJ(p`+|KgzU4~nL;fBekKh)oMq;6FDAJwFm{Xy4g z564W^3n{#D%7?or{8ZZf)WVkKPVAD!Wl9DyHLX z#G&##k&9329AFE;>b(?Rxt+?}7Cl>jXPMRmG0D0`&YVD>UO(|cSkykp+LtkMh4>35 zweYeBI#;+$7$<967l^%L-PLe%s%%9skNv~#3z^gI^n7f+vEZVR*8lU@Ls)MxVE6+w zEM?c(X?s3y|MBFfLOK5d{=(BHUktW;H$I5vu|2_iV|H-+{JYkBzv}CJ_57axB+#(+&H7xe92 SI188*89ZJ6T-G@yGywp^oij%O literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/quote-right.png b/packages/backend/assets/notification-badges/quote-right.png new file mode 100644 index 0000000000000000000000000000000000000000..0fa483765481dd43d88a470c45614c6a782c748b GIT binary patch literal 772 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>U^?yT;uumf=k46Y!rKl4ZuT5a z2^?(-9Bl#|i9iyF0yx?dIGQH>n~-+tOi&&r|;B z94N8168IRwuuMBD>3u}!x4U;mbtiB)^i0jTAH$Gxh_#4u!bYLDv$-35b~xxU7=2Y2kjtq>!w7%UqmcW-jyl zxkm4AN8K;SlHN}{r7AD}^7a>5Z1qNJTfNPz--iXVxZQ02mfZfjux!4?N1Mg2K=ZUe zy-GU2SpH(@@8$bS_t<%}T^6mJQt_{=?`2oj|~E)R(Q^ z`Xojvl405ErB7nt`t3;E73DS0OO^XT$<_&bR{mX?<}-c!lai1P0zT8HKUnnD#z$m^ z9{<6}jmxL2285rzym9}N- zO_i)L5QVP%(gBzNya8AMF94%vKzE{pOZd+Ry%giR1rN9x|0(&0X0_-pjsdX2pZuTw zZ-_7x!jgZ82r?l&@iah?4#0x=KVoI=s8yaFaQhp2;tw;F81zx17ch18f6Z?!Qij`3x;Q`y!0-9qC09=Q}Q&Tx5eSEIjAsuhM^}8N$Pv!u3 z;5EU`ss(P>X%&+jGo~a~g}6?m=EF zjF2;6tsA-{^{TTKI>;IDtQ`A4QV#N3LTbaA{ZU5_u~(ffA$1bmSC0EX48j7nO7Z>0 zjQr$RU4VUr9OTVv(S1f@f1_6aKjG_;UTE)U1SAYFwSx{}f-5&0T#GiLz2pP=1~gNC z&Wb^v0j-tP(=j4F`5W4Ns!v7E+k%|^0;KK(IQxZ2Jx<~57s4Vw`4*i0BBUP1sh;~> ziIfW_{x38^Vmlx%yu!pRz#CY4#EDse+4Tq$Q~wvG7%xp|{a?YlUxbkQ&*T^6Tm5J9 z3*$532`0Zd2A^9^(ccawenz-bbKKgo&nJWsLI{cG7r>uR1s%&lUjP6A07*qoM6N<$ Ef-jw?4FCWD literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/retweet.png b/packages/backend/assets/notification-badges/retweet.png new file mode 100644 index 0000000000000000000000000000000000000000..dc6106048107a8459bd6efc921835d9bf2e25d9b GIT binary patch literal 798 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>V0!B5;uumf=k4s=jh_qzTKid4 z6c!3;Efkv2z^oF(Ikf@IpK#%~@^<6e%HJDjr)=h(lb-hfV9qz2DgBceS_GUp6p;x@ z-iB+{g7-a`AMhp2xq7wKHQt3e;lTcf!A^?X4lQR%R(TlQBslAXrlPUo!{r_sj9(by zCfe#||A>w@3_8?5@xr4S2lYD*)ZKqXaqj#zRpavZ8*?6Ji@lLuwXBZivrD4jK9@r$ zH>`L4y2+w6qD_Bdf#JK_3`W&M(>*Fgq7E-)nZolwSS3aH0Z^UO`z^~BG1(lR?(u@* z&c8O@NLLma#hOkQ{xhs|7SzwL)tskXD|&Xdx1@&gGbXFV$(28N5B2k(e9XAFrTUb^ zOhfSkQL~wc=Eu#IVr+P)B^|_e>P+IghRPoeaSRI6KZJ%eY>+=w{lj4n149K@)z+2F zsY^N7{;heEUsioTa$F|LGT@9V5*Ic(uJUdN9 zTtT(HL23a*y2b%Xu7WnEISbL44Z??Xr%QkPU+XQp;N{XqVT`)}?tl61!jaV}!Z>%+ z^e!pZEfMz%|Hf4Mt!daBcTy-zxZv2ovhS*#hD=h9>)l^DY~`%5KdH&6D{jxnDq9x@j(#$C-{bJyEx+lR)-+!7oA#_rzIvgRRk zDThT@%bSPHUpatcrRSrXJbyUEGR0YMj_@?#mS5q0pyTh{@|~(P+W24amsMO-n4>wJ z zYq6_95QJL-5|Ds|NH`LZ5C=$z1SCWP5|DrdB;@{BwnF6sS=gQJ3Cvg3Rrzs}Gc(P; zcr1Wn7=~dOhG7_nVHidM;11vcpaakY7$@}ro&ehEZ%R&x=cfSB;9dOJ4t3LajRbfj z_L{J!N8a$y#SOrKG)Bxd0{F|9#yPknU8`7=bz=b(a09ZW5uf9Wu|ZBV%#<(Ok>3oj zdxrdeia*GY8Q)|B{X_w`zF?a1f;%7#xY-Wd46jBwSM?O&cU7OQq?AlpJDOUDwGy}~ zC)r5FxMU5bjfu|3x46(^c2inEl=5o=snFbI4T5=Bfic_!V_2jM42+>(Fz?Xmej^yeB9Fkp7-|Lc zZUSS-Zb%&%7(e@`s#d{x~ctiaul-^|f9pOyx8#H&KBA-WOrVxB_mm*?#;VKRffA)<|~&stoZa9Jxu(o3m$Ow`M|!dboq7Jd)@JDhw&$ zl1q6iD+3gtC)xq);he(s7fZH0lyGh)JE1a2?O|r&?5R})X*v1l~l$QG#1S} z`mjXZNvK^3>dK<-By`1<`8Rb@6^&$A3F^wC4(w)#GDFzOYZVQ4;#x(6ow!!fU?;9s zG}wu26%BUcT1A7MxK`1)(hUuvlHT5F6T4FQzuKsZMsiyS=<1@sI>}BjzNKQ%crDv2 zv8C$mJh@*8sZAXenK7q^8|cqI`o#FPL4 literal 0 HcmV?d00001 diff --git a/packages/backend/src/misc/is-mime-image.ts b/packages/backend/src/misc/is-mime-image.ts new file mode 100644 index 000000000..8993ede33 --- /dev/null +++ b/packages/backend/src/misc/is-mime-image.ts @@ -0,0 +1,8 @@ +import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; + +const dictionary = { + 'safe-file': FILE_TYPE_BROWSERSAFE, + 'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/svg+xml'], +}; + +export const isMimeImage = (mime: string, type: keyof typeof dictionary): boolean => dictionary[type].includes(mime); diff --git a/packages/backend/src/server/proxy/proxy-media.ts b/packages/backend/src/server/proxy/proxy-media.ts index 48887bf12..ca036e8fd 100644 --- a/packages/backend/src/server/proxy/proxy-media.ts +++ b/packages/backend/src/server/proxy/proxy-media.ts @@ -1,13 +1,16 @@ import * as fs from 'node:fs'; import Koa from 'koa'; -import { serverLogger } from '../index.js'; +import sharp from 'sharp'; import { IImage, convertToWebp } from '@/services/drive/image-processor.js'; import { createTemp } from '@/misc/create-temp.js'; import { downloadUrl } from '@/misc/download-url.js'; import { detectType } from '@/misc/get-file-info.js'; import { StatusError } from '@/misc/fetch.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; +import { serverLogger } from '../index.js'; +import { isMimeImage } from '@/misc/is-mime-image.js'; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export async function proxyMedia(ctx: Koa.Context) { const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url; @@ -23,14 +26,50 @@ export async function proxyMedia(ctx: Koa.Context) { await downloadUrl(url, path); const { mime, ext } = await detectType(path); + const isConvertibleImage = isMimeImage(mime, 'sharp-convertible-image'); let image: IImage; - if ('static' in ctx.query && ['image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/svg+xml'].includes(mime)) { + if ('static' in ctx.query && isConvertibleImage) { image = await convertToWebp(path, 498, 280); - } else if ('preview' in ctx.query && ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/svg+xml'].includes(mime)) { + } else if ('preview' in ctx.query && isConvertibleImage) { image = await convertToWebp(path, 200, 200); - } else if (['image/svg+xml'].includes(mime)) { + } else if ('badge' in ctx.query) { + if (!isConvertibleImage) { + // 画像でないなら404でお茶を濁す + throw new StatusError('Unexpected mime', 404); + } + + const mask = sharp(path) + .resize(96, 96, { + fit: 'inside', + withoutEnlargement: false, + }) + .greyscale() + .normalise() + .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast + .flatten({ background: '#000' }) + .toColorspace('b-w'); + + const stats = await mask.clone().stats(); + + if (stats.entropy < 0.1) { + // エントロピーがあまりない場合は404にする + throw new StatusError('Skip to provide badge', 404); + } + + const data = sharp({ + create: { width: 96, height: 96, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } }, + }) + .pipelineColorspace('b-w') + .boolean(await mask.png().toBuffer(), 'eor'); + + image = { + data: await data.png().toBuffer(), + ext: 'png', + type: 'image/png', + }; + } else if (mime === 'image/svg+xml') { image = await convertToWebp(path, 2048, 2048, 1); } else if (!mime.startsWith('image/') || !FILE_TYPE_BROWSERSAFE.includes(mime)) { throw new StatusError('Rejected type', 403, 'Rejected type'); @@ -48,7 +87,7 @@ export async function proxyMedia(ctx: Koa.Context) { } catch (e) { serverLogger.error(`${e}`); - if (e instanceof StatusError && e.isClientError) { + if (e instanceof StatusError && (e.statusCode === 302 || e.isClientError)) { ctx.status = e.statusCode; } else { ctx.status = 500; diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index 2feee72be..be95becb6 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -11,6 +11,7 @@ import Router from '@koa/router'; import send from 'koa-send'; import favicon from 'koa-favicon'; import views from 'koa-views'; +import sharp from 'sharp'; import { createBullBoard } from '@bull-board/api'; import { BullAdapter } from '@bull-board/api/bullAdapter.js'; import { KoaAdapter } from '@bull-board/koa'; @@ -140,6 +141,49 @@ router.get('/twemoji/(.*)', async ctx => { }); }); +router.get('/twemoji-badge/(.*)', async ctx => { + const path = ctx.path.replace('/twemoji-badge/', ''); + + if (!path.match(/^[0-9a-f-]+\.png$/)) { + ctx.status = 404; + return; + } + + const mask = await sharp( + `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/${path.replace('.png', '')}.svg`, + { density: 1000 }, + ) + .resize(488, 488) + .greyscale() + .normalise() + .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast + .flatten({ background: '#000' }) + .extend({ + top: 12, + bottom: 12, + left: 12, + right: 12, + background: '#000', + }) + .toColorspace('b-w') + .png() + .toBuffer(); + + const buffer = await sharp({ + create: { width: 512, height: 512, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } }, + }) + .pipelineColorspace('b-w') + .boolean(mask, 'eor') + .resize(96, 96) + .png() + .toBuffer(); + + ctx.set('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); + ctx.set('Cache-Control', 'max-age=2592000'); + ctx.set('Content-Type', 'image/png'); + ctx.body = buffer; +}); + // ServiceWorker router.get(`/sw.js`, async ctx => { await send(ctx as any, `/sw.js`, { diff --git a/packages/client/src/components/autocomplete.vue b/packages/client/src/components/autocomplete.vue index 1e4a4506f..ae708026e 100644 --- a/packages/client/src/components/autocomplete.vue +++ b/packages/client/src/components/autocomplete.vue @@ -35,6 +35,7 @@