From 3cd04c4b81190e4cfe6dc1def8335be02f8b4b37 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sun, 23 May 2021 13:07:11 +0900 Subject: [PATCH 01/28] Fix agent type (#7532) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3d84305649..9e32e6e913 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1536,9 +1536,9 @@ acorn@^8.2.1: integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== agent-base@6: - version "6.0.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.0.tgz#5d0101f19bbfaed39980b22ae866de153b93f09a" - integrity sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw== + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" From 7063a6925fb4cfa47747193e88c059514730ff44 Mon Sep 17 00:00:00 2001 From: okayurisotto <47853651+okayurisotto@users.noreply.github.com> Date: Sun, 23 May 2021 18:55:21 +0900 Subject: [PATCH 02/28] =?UTF-8?q?fix:=20Safari=E3=81=A7=E3=82=82=E3=83=A2?= =?UTF-8?q?=E3=83=BC=E3=83=80=E3=83=AB=E3=81=AE=E3=81=BC=E3=81=8B=E3=81=97?= =?UTF-8?q?=E5=8A=B9=E6=9E=9C=E3=81=8C=E5=8A=B9=E3=81=8F=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F=20(#7530)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/misskey-dev/misskey/issues/7529 --- src/client/style.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/style.scss b/src/client/style.scss index 39bf6ef2d5..dc419bd872 100644 --- a/src/client/style.scss +++ b/src/client/style.scss @@ -146,6 +146,7 @@ hr { width: 100%; height: 100%; background: var(--modalBg); + -webkit-backdrop-filter: var(--modalBgFilter); backdrop-filter: var(--modalBgFilter); } From 47aaf044813662931fbaddd965272267fd94ed6a Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sun, 23 May 2021 18:57:12 +0900 Subject: [PATCH 03/28] Fix search-by-tag (#7531) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix search-by-tag * Revert "Fix search-by-tag" This reverts commit c971d1d5d82f2d8b58fdec76e42f4404339ab83a. * Fix typo * Remove unused var * インジェクションは[]を返すように --- .../api/endpoints/notes/search-by-tag.ts | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index 61f62dd5a6..463c5fff5a 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -104,22 +104,25 @@ export default define(meta, async (ps, me) => { generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); - if (ps.tag) { - if (!safeForSql(ps.tag)) return; - query.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); - } else { - let i = 0; - query.andWhere(new Brackets(qb => { - for (const tags of ps.query!) { - qb.orWhere(new Brackets(qb => { - for (const tag of tags) { - if (!safeForSql(tag)) return; - qb.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); - i++; - } - })); - } - })); + try { + if (ps.tag) { + if (!safeForSql(ps.tag)) throw 'Injection'; + query.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); + } else { + query.andWhere(new Brackets(qb => { + for (const tags of ps.query!) { + qb.orWhere(new Brackets(qb => { + for (const tag of tags) { + if (!safeForSql(tag)) throw 'Injection'; + qb.andWhere(`'{"${normalizeForSearch(tag)}"}' <@ note.tags`); + } + })); + } + })); + } + } catch (e) { + if (e === 'Injection') return []; + throw e; } if (ps.reply != null) { From f85399e355685344d45efa49d220cf1508d0bfd5 Mon Sep 17 00:00:00 2001 From: Sandy Nicko Mac Corzeta <4186454+sandycorzeta@users.noreply.github.com> Date: Sun, 23 May 2021 09:57:33 +0000 Subject: [PATCH 04/28] Add Indonesian to index language (#7528) Co-authored-by: Sandy Nicko Mac Corzeta <sandynicko@windowslive.com> --- locales/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/index.js b/locales/index.js index 727e0e3848..35f9972ff7 100644 --- a/locales/index.js +++ b/locales/index.js @@ -21,6 +21,7 @@ const languages = [ 'en-US', 'es-ES', 'fr-FR', + 'id-ID', 'ja-JP', 'ja-KS', 'kab-KAB', From c06091f78ab507085da3a1a4898b325a43e3201e Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 23 May 2021 21:14:29 +0900 Subject: [PATCH 05/28] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 5cff3619fd..816765af67 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,11 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). To receive updates of this repo, follow [@repo@misskey.io](https://misskey.io/@repo) on fediverse. +Related projects +---------------------------------------------------------------- +- [misskey.js](https://github.com/misskey-dev/misskey.js) - Misskey SDK for JavaScript +- [mfm.js](https://github.com/misskey-dev/mfm.js) - MFM parser + :heart: Backers ---------------------------------------------------------------- <!-- PATREON_START --> From 35f075b8871f240aa94c4a21c77b9759ffd2c1b9 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 23 May 2021 21:28:41 +0900 Subject: [PATCH 06/28] :art: --- assets/about/banner.svg | 17 ++++++----------- assets/banner.afdesign | Bin 65435 -> 73201 bytes 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/assets/about/banner.svg b/assets/about/banner.svg index 31b6ca95e1..75308c0950 100644 --- a/assets/about/banner.svg +++ b/assets/about/banner.svg @@ -1,11 +1,8 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg width="100%" height="100%" viewBox="0 0 1536 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"> - <g> - <g transform="matrix(3,0,0,2,2.27374e-13,0)"> - <path d="M512,20.043C512,14.727 510.592,9.629 508.086,5.87C505.58,2.112 502.182,-0 498.638,0L13.362,0C9.818,-0 6.42,2.112 3.914,5.87C1.408,9.629 0,14.727 -0,20.043L0,491.957C0,497.273 1.408,502.371 3.914,506.13C6.42,509.888 9.818,512 13.362,512L498.638,512C502.182,512 505.58,509.888 508.086,506.13C510.592,502.371 512,497.273 512,491.957L512,20.043Z" style="fill:rgb(158,194,63);"/> - </g> - <g transform="matrix(1,0,0,1,9.6e-05,-50.9623)"> +<svg width="100%" height="100%" viewBox="0 0 1792 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"> + <g transform="matrix(1,0,0,1,128,-57.1399)"> + <g> <g transform="matrix(1,0,0,1,13.2723,-52.3253)"> <g id="_1f44d.svg" serif:id="1f44d.svg" transform="matrix(1.42513,0,0,1.42513,559.915,761.509)"> <g transform="matrix(1,0,0,1,-18,-18)"> @@ -61,14 +58,12 @@ </g> </g> </g> - <g transform="matrix(0.517859,0,0,0.517859,635.428,379.428)"> - <g transform="matrix(1.93103,-0,-0,1.93103,-1227.03,-634.276)"> - <use xlink:href="#_Image2" x="618.087" y="355.389" width="303.176px" height="221.833px" transform="matrix(0.997291,0,0,0.999247,0,0)"/> - </g> + <g transform="matrix(1,-0,-0,1,-128,57.1399)"> + <use xlink:href="#_Image2" x="746.434" y="339.25" width="303.176px" height="221.833px" transform="matrix(0.997291,0,0,0.999247,0,0)"/> </g> </g> </g> <defs> - <image id="_Image2" width="304px" height="222px" xlink:href="data:image/png;base64,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"/> + <image id="_Image2" width="304px" height="222px" xlink:href="data:image/png;base64,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"/> </defs> </svg> diff --git a/assets/banner.afdesign b/assets/banner.afdesign index def3e884d3c1145f398944c81f8bae5723f14a0b..08b5c1b4a0b4720db0f3a7cab153edfa1a103ad1 100644 GIT binary patch delta 18972 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 CS}B$Q delta 11135 zcmc(FXH*k!x9@<66cGyuf{KbDML@cA1VKWT&}#&wH$jTj!7g0_7y$vLLntC8Qj%Z+ z1O%jpj*7I<gpdRRBzOMjUH8lVaMn8Sde6F9naPuxd1mj~<@fBpXK%K!<S-8L01TrW zoB#j>+z-Aj3=8lu9Rh%K=b^K_PfUgGK*xbY&~^mcZU+a3aMbGQ45!QN9hGB|1b}n@ zoQ6JB2>@V<zioPpi$j0|`p*S3xCsXUW@yM{#l{N#WDVH}r~B_+0$-l>j#=1T?ES8~ zcRPG#X*4FO=uGdshbqARB-Re4&gwIBQMI+Tt<SDeB70d={$^ijJ<&2VmdqvS_I!iO z#qadwSfw3v&X+vd!8_Lu9sT~vQNXDXEfR{}XqKNnC`_p)R=*my8kc%U-Mg5uUR~WC z-NL#+ZM}u_4mtch<?LTjWDQwwHaICVE#;k9#D#2?#PR_^WrD$=9?bKyhWN5PGA})3 z|L39SUy5h$zM{3i6F$;tNHc*2A{8!mrPeh7z#Y9ONPQ8FOUsXs2wjgZnNB=?I8OK0 zu_6(y#o%)ha?M>%CZH`L`_*<VEdTmW+b<oGj6HK3bRTAzP#U;hhm=E)ssq4#_!jaQ zjF$PB`)u`az8_1ReLn+p85>BRgydyCwqZomj=B6c;^M5D)!ObMATFo>hLtb2Xo5}K z|I@6zYo8EwXuQ5ija&2HPJOu(k!lXxXzojfWG=bUFLG|%65GcxAvDQC42P;;_C{S< z4zsEufsi7V@h4z=>Q>=9Zs|e*F!j?YvgL|5gB-vP0Jc_p8Zd{Na7edp6%$05J&1K+ zg4h{hHJcjY1^~hX9uNS|-h?#Tc7p}l|4)bDzd7$eH4Xe9bNrv8{4c`bzbCf;7!%<C zRAK)y%Kst^-uOfg0Pe}xh3#(0iqgXz(IZ|P!TmuRTHHT9^dU=YE4UOcrPp7N3s>Zv z^{EfHTl7+W)$jzkBXS&wd#%WkTLsoiX<^(e@XF+FPiq;rt1VbE#nCOwfrHWT=F4}j zz`NGuI>1o}09=O~U)#gv40j(3td18OyBgq7mLPwoWu>vN1ZY^-PLk9AP(sspbKqPt z<i!<F_*P{cr8S7wnUudpu*(#}Ut8c5N*p7M+q>_QM{K+|GCx1{gbS=rznj^9fxKaD zZfRbtUf4Ay-g{bK#6k@x!Ujk_1c0waO=F6Da5LC6hJ>oWe;~;7K)mb0h-r0ZrH-CC zXqVn3Pn)PsT?$?=Q?H@Lx*0dztJY?-9g_?U2>iS;Lhs!e9eqcT_$#-Sbmfe&Z&hWL z*oSu|9Hr!jIegXl?{WQmvOfNSq)qf<W$Y=v!IaP%f-hzNh&%l8mhlgM*n1_oSC2q$ zwBfBhMt(P{V=)zP_>S$~Q<acE+DCd~K|rC7GvQ5gQCrVaV_NvqXfn5Cj)328%%t1R zv=rx^H+d?1sP0S|E6z7r8wZA)JXpOO#iP#sMn2SL%+hF@khM}1rJ|F)u)s7)t77q% zR&mpBuStsZQ9f+>39JWI#%GU4Fp?X2In<bJ-QJyqi}+m_xw$_p`JtV6_bPw_PXKY! zQdi5Akm~6_1S!!5lcQLBWRvSJgqIR%o*%P?tr(k1cmnt~eB8+?TA$tQnZQw)vfQ1| zsXM5b%4qK8TORWiT=Um(@i+XmLHI(Y*V5#^$a&Dua_VXe-?T%2dq$&_<9}dGV~z8! z=m5usDNI1~kLn#kk&u20)dyUeu<>qcM<65R1-BQ}v?~3AKQ`DZ>z`@%3%p;`YQ4WP zPCM@}6#3;tBK|VDL}{6;MXM;}+VFMyKKn{L-Yl^7K$mF3?fc7f(D82$9(7sREIe>e zd~I_3eI@yH%xOUN767OYSMNATPS<XBHUtiFm!<zkD37)19-+{mRf3AOPvbfi^stM8 zB6iSJjWP$NR=bX!a}8F=QR;NHnlc`3{T2PGfF5D%+u;3ElZ!fwE&C>Ob*Fb$Ij>GX zJYbidTypF5!SwW#i*2DSz=dNCIvK}U8+><?Yk5i<VlYdgEsVP8^kr3+tM<tV$v!m# zm>*WP=PY#|gODx+b5~}{-sMd$=xSH(^w654Ce6_2Ut|l7G&X?MyS}2e)|1a?t_ex= z9|oEscWfR|M+H<VZEx6s9{2s8&o<GS*2e$dD=hPIxrl#u>Ep0L`?u`9(2YjK%RqQy z!o4fp!iYWPgN&q;4WFI-NfUWkqKUsFWwSucXSezOT23~of7o^J1(PRa1_6qZErT;$ z+~~cT5Oh@7eo|NsgQ+W$ge6Wy)J)WiBrxuTVb*8qnh{P%_b@~G<0W~2o^>=;WsXv2 zCg9&{$QR4!HDa==40o``hXC1&P&i^+!Nx^iPgvd|>HXj@+zZ3qT8Z575pd4uW-{V! zD^36}flC7`;f&@FU7@x04R&%%=0B+lqrf-o=NmauByuPG-JM?>*|X^;l}_6jrlq4y zz>+2Q{REMleW=t@Zs@>e(sH}ajM!EsG~Ar01_{xE?C`;+^{48-<&PS|8ZFd4eWfM9 zQw@iLi`It4+TXNxvGo!Ey{6NandeJasdop<m&g9gMqCHe1(t~iy;yqKU{@?QjCv%) z<Z4XB+Xny0P7}^8=UVct??*;;^+beXOnI`DukeE5oDpl>wWBpOM^gXp@12CkbNARG z{^0(=+d|(Z!Oo8z@vHPzQS8Dis^l_NvVyA?<8)${?eNF|bKw(8ApOjm?Vdi^j@5Tb zD=~?^Tg*9RCUI2KKk*>=(WmxhPs{iO$;q&`i!8ufhcHc+t9w;8%VbQQ_utIOgLOo@ z=gCA}?V)elq{xT8hh_^W^=E4LS;JgE<;&-471m}ICV!sU_o6!PElR4Y(2MQE%31mA z25+!_?1D7yZXY}_{ULQ|_bdhM_+;b7SO_<gle+kpp1&Y@N~t=c0rGRBt=5%$8!{Ht z)zooPzGdwjUx(G2d0CZk{lcNy*IJcXTAFcv_ms8oWQJPotR!uQbiNXTi(DmUk%TOG zvh?<TJNS;ECQA0g_X|$OQWH<9c@!DdnfZhe9VW&=yn27SEJ>N!o(MVv<~)ix%@~TC zM8*}%7Fm~-U+hfUx7Yw}<yUq+b7J(+4*qG}yo<@7l;!V#P%HgPeM9MI2dJyGIJx+D zV|@|+Txc+>=6hN<ZFE`dl~UBi4-lh1hv8!43_o(!%7eW66hHX1DR9(zHu4yd>&5}R z7#|61OeCqZYoV2=K$@?B!G;wN%=|8|o;7=oS<N&VEwCEziFlOqdiQynxk~n^vMD3Q zG)1Gruy}Aw4Amon>XGW7H>!)=9}Zlej|Y+F*qu|%aUZWS17E#d9tPU{%~9TopHs*2 zEhS?Z&+=k-YZRTXSB1E0w~WTrutrfWF-zF<Zhtrf2)<Sz7gL%Pae07IC7*t)zLpVH zFN!r9|D$@?*?RPugtdo)gDSeGX)!d6_QLR2Na)xsI^}fz)JcAA8%RP)6VuDpcBGf# zr8nMA2SvesLuae0VyK+-jomGEgwhA(Kqx0u$M++ETaj1=UjRD(b7J!w&>Sh_ws_<c zI_k(xBs+-KrRNFgGD>=fnMn-o#n;S$D;)o4-H?EhnOecoaK1UGKTe1|ekdfC-skhn ziTdh(N~U@4kuvH`j@rhk^<}qA<GZ{p-Xn=%Oc^-vL+cAe+mD|KaIJs;BPx%;c9^`8 zgEu4<G8vHvQ5LPI;-3_V!J8ZdRGg=T0T+HBFf7cv7!JE}ysW0i?8fZ_LRV(r-K5LD zch${up05+=Ygz@#hxXjn5lAb@xV}E^+}<w756n;1?o#W|-dxK+^8PA>x%w{r-6Zwm zA#aiNt`5?uJuk;$UXG{wn}Np;8$FFPja)CcBvWvihI+W!NDMyUu{Muhc3$4-_wUA_ z(WHl`=g81d;ipfZByqF+h{;jB{vPJw<Fg~{jv>lAS_Z_%-oT;twC5qef4ju&4PQ~n z)EbC<Nqoc%I9&pO+Vaz1^YZfaH*kIYg~p<V7uXc!<%MC8H?0jc^Ea9m!;=SI_@49r zcv@awK07PRyXi?yKtMpX>Q?R7sH#km5)fVIjG=0EQ{fGZ6Zi=#azbM(X5#u^ju?6u zV&pEp=(Kf=?->aGKM4RAl=RCjE6lwnH6C5KEtloz7vA!Bt1ZlzMb#%>*06L)uA}}< zX>5w=_ugJ_4-fskMXAW)LpR>tzTM<lom)!p^FhUIe4E^E*4RLKB8_4-IfcOSVO5Qg zMQ}trY%g;o2Dmd0*_B@<ynNUS0ZC=(gYCUvnoL1bV`wuygQ^xHtcRNkk|T7c!Dw@V zGxoq7tfqy3$JgM_1%Z+1_37RFavk`E-w1HqQ)#6k3bcBd?_AGC3U!)fi$)1U^0I4Q z#Fv$ksTi!z1yeHki+)#bJdEXzij9!5sj1Sa^e`uGV=wvEfl5E=PftEx+<Yx)Zy{VP zy>*KgCuuiXXC!>Ehdzv7w*K-j+`hktweKfo7+kgwh!X6)7IT2@-Qa%o*4?BnScdye zUY>QWt=~3fMGt?TnTS{<9DYqJy=W)?SH3y)kAKfPxT~xcEmQ3DWfrlQqzEqR0_WUL zTsV$$uQouN9Ufl%zH)fm(B`mXFZ{3u+oO*8hsf#(mpiq#meMF+wMB#t!t*v_*mgR) zj22{T=YJ+T^KNVPt?NqMQ&$SLN1sUvDJC*K?1LiZk7M)HDfz6V?)eYU+RHOyYO{t| zo9em?esJ&p<+E5j>f`EKu$f)VtWBC>?G*;d=OaN6->XA{5K4tyD~opbqZ?{Vv$Lpn z@BqhA$2ig!Bf356Y^KEj`n)31XjU9XCP#+xsRah7PMu0Fa26sWZ1;<I(-J#)w6S!; z0>k14X!C{9n`-H*-Jl0l9s-)bvH`~nO~J_K_?tS*A<gc2*w{+RWag0IYTEom$X;p} z-ZoL4wGfc?)Y@hgHKOS&ofUiwqcKdYk6a?O8V{%BZ7z`y$-1xrFHFJ(sl!nVF8tC0 z?6$VrE#hTlllUZAjxX=sFS_#RNn{#kXJ;deL~5vk$Cx<r%8e0`+6hq&dH!cXK5gZH z;9}UsWqo;hV0hTlO^dPcS%9EIRPVPCO*b!wL*Mf1`kJagLPA1;O@9J^Meu229!wOR zxBu;bf9M^bR<DlsKW5v;Re(GHR&(1K@qnOiEQFzn#Dt<`5Y`adG|k|h`a$tg>AWnA z?0S%Pzt9VACg6oR<|!zhWn6JvOhF$Yrb;HWh4{EQczSv&s;N!F_xbt`Fb=>2DUf9N zgm07?n1(_c<<~Pa&b__84SpOvi(E0pPCsjDl%k5txEQS$lF*_PzjigYtP_KkZD9bL z&;;3p!XG0x#6{9>M}N@tNMJc7IXPJ|WqgW+TA+$7ftJIc=xl)Xc^f7``RxTZffj0e zQ2N{A8N}>rsiQQ8hw=2{TlDgCeqSvW<`>+;5a|D2T`n2&?(OSST43bVOd|Tk+A=3B zubvjf>^S)hAPBzPe#5W(fO~Tn{-GXKrk36Xty+q)lo+kXwTC_PA38cZ>;sl31M(RQ zH6TGq7tnV6M|$5O1ci(9CbqvaV&+@_iTN?|=tYC|+}s?b#-KAl;hlCq#f;BVPijI; zM<GF+XNK%2Q!E66Zg{!aQC(Q#hQL_Y?h3?3p9q*s3_D<s%lvdn`zNci;(b^ue#d-1 z(ZXicAK{xO#lxW*^9wdF$;StBF^tw^c31R^+SORY_LrI!0pdR^o2z7qNvdamti4P! zXrHP{z37yuGD6X0A~^0F4bi$2@ui*p^b%9!=&*rSkOa}=1Ebo7n5OvunQVOtu=Kmp zVNf?e6uJL(qrx^D!NV_gKWBQeFZxEQC>_?3E)ga@GeS3t1x3w!dY?5%92bq-f!tK^ zjk&+Av%baCQ?!LHtpUf_V7sSUNfMFt?|j?a<EdT447d6}&J5+g3J{Qdsuu^*a;?ot zyW2{eUcZD@-!1D{%p20hFv7{yqlh6{9Q||tO8X{0Eh%#mgO8dqJk1yjVCCsC2Mkz% zpB<h=PLQ;a-)*H&ZYl-s99R)(e_uo?`2Zu4P7LJP2yQCtYLZt~CVw<_q9v3h$c)S& zv|ePkjx29Fw4X_O@n4FASojl+vENkImkP&ou2q^k)1h1ttfhQ-LOYq0!7YP6Yig7Y zB6^~#vSSI6O^7MwGZh+L?s08gF8~1i>eXHX__W)TT?0gXFT3URJB<3xXI$<gWk*sx zZmiYBc!v06www%GlH)2H5@ncQkUX^iuw77s!cxKBa|@Zod3y&VrYda2(6;Y*y9PDV z6W3%Bcv&y)uBFil$$=?7Gbk>adI1@j+?gTVDC+YvuXxDQdj+*VvkDMofo2VsQT818 za_U!+{<?%q;x@SBQ4S*Ix3e6Ft246%;u$aFj3Aw2d~%ng-J=g53$RKlafoD;cq_c6 zAUmjQ)>HzgJL$zkSvW2l)?wRj2TM*{NZ@nO7m_d+Z=8GH$Uj*N5dO(>M|wQHda|?C zO6jW|V<}FR-^*;X{J}Tgu{+?WkFf`nlEcBiev0TZdskWbyI{NJJ5GLU$$ok+8NT)H z_T{(Fr24bGz1=iLiD;ApUZp0-#a$mx*oxl-lgRI(+C{cxN*{H9#B$#xrxV^!(A6aw zJWA>_hCqjMS^{Oo*6E-Rh~DpX9X}D&c)bFCebSbP{jC}c2wXTl@;fq?7Bp{^Uh98o ze{gA$cYHAVlf&Nz75@5t-1NpoJ@xadvK5D?>mE<XmRT8(sS-u{Wtu35jCbSp39@HU z7_le$_@TE^kV-5XoI^-c`+l31(#9HRdVa~6oEzy$PwgJ2Bbu+VTy^||`8%-RQaqt% zqgvYao3pkaRJL@!)yZfKt-qlUFDrD*hu<wsNd(`imy_fXRR?Rmovc5^A$LFLWfy_S zhbxa5{mi?5@SLNyQi*G3a&yyjtB^+P!DGEjai)ngnG<{4yISV17S_7sYOg}SFGL)` zua{rxJ1`CHam~{As%=n0WJejI?)UqO-FLXPrlkSymIW7cl3dC*pNEEA2V7ZzB~zoo zR{I?t-tFEYd`(vqN=@}{6MPtjpanvXviN*3r;j&0O0w~7sXoK9seAw0LFQE)Z&2By z^uf~-qbmaKc+Jm0BS!997udzDtZr0T+R;P4jF)$YgzQ^<5cO#hzO=#=*SEvXSWRIB zV?jZtXk`a=8GoLMM}5ZoiP17>+_rT@2c8&I*`TVm>mNh@%D7z}Y}z5e)w<xz=zYX| z9uXyJZn{{CuP&M`of-DXLT_^sneLQ?*w3mmq6GRiEc`tScL*4MrR20{KTe0bP}tp1 z31*l$9*F9jTaI?c_uB-z78$OMm7I`R0B_w=-Q@vZLj~*idVD|L5v8Na8~$#SlVTMI zXJJ`)Y@vK)h1egE;psEtOJ^HVSs$#&g>8Kq(XkiQr$)nkBp#TccWgdjW0w7!1AC5S zY||!LnIzHQUTuFKLlB4OZ_V%e5O<ZQG1{aYZdlbK|8h(N#s2~7WUQIL{TBuB+_X>i z181`o`a_Q%&p%m*J1O?M|D@UfY&vfBQ`SbP)%_W&67rG**e905KQOWCEb`bn!db_h zr{e6qLZ(`I`R|}E+X$1r1I%`qo5TacgtJ{CZaa*tC@?K>up)n|qOxhQtmA&a#E1FA zdiVpA#JlK8Zh~%{A6Xp-4iZfQtdwLcpX8@m6$M`}9Urvo&Fq7T`UzOQ-8mElMT}0( zuI3HM<c0jeh!~Te1x<1UNlAmz;T!meQsLdJGp7(1#P-rY;OuZ~6;#-2zF>Rxx@No3 z(lKa7b@HivuZ*lMshxAiBF})aOHk?_-dj*m$}3JMj5<3^OM!n4*QyR|CMgxmCXKW= ziq?5H=h(|*8%A_y7S57710*QPBMi%!3(`=Q!OEHF6KX%%t+3E$LSCe71e`4_KGOOq zXU4G!OJ@C!3E$LU{T}UQXZwcFid6E}{DA*%RhAZ`VDE@DUH$a=oEfbj8qlJdzEB_` zC0&^u5i&{319ycc_y#g1!qz4x9g9SUR2A^=HEh@<dTa7!wmonyvpdGw$A*LBO59`~ zu(u1nI0A(&0|i{+3PLP?Ri%^FaiPKY^RHipie+hyA8gba4W}0ybB>pj<?Xh{Ogc;d z9@>O*6j@)wUgj%h5S!f|pg1uZZFk4lAK!TY_Wr}2f9ij{!|<Kz%?5&nQQ(090zWlJ za9UGW{=CRE^&Nt3iJbrm5A?|BMoB-*$1_1P|Hey(|E6aB|Gd56aok`9(B5Kc8CSe3 z9h+xNngUMB*>CI-74`FywnlxfawvKhjszO>7;Ihcp9asjk#&GLd1kkeJ9lzfA3Zsr zb9Sh9|IMnz8-iug_LGd)vdA=!$JX<)U6II2o<Bw3Kc?Bd>ehe<wMiIv&ZgAMPJrhw zhlF1$E;8=f(1CSIxNBHR`;6+>#z<Qa&g)pq&(}Ybuj^*1dODLoS-u?~E{R`xSuo>f zC|Fpzsc`0L^RXb7dmKzap8>zw!}8O(mPo>qU+vpyp6Vr)I=<wvgWOVFdfrKuE`jp; z4xiNkv_&9S1D)M0hdatJ^-KhBITB3U609`EiVV-I{Z71+v#(9P0vFicKU|FOM%61D zc-3vJGzG)@Ee&g*#2NnaZO$0bmZ;fuwa$4ApWCN@KQE$>x8bPU>OM%gyvB)~5WKwI z<Z3!};sjs|V*##zQPy{s0xl<)KJm;zd=fatQ#48cZZq3M5+N8ErV9Ggk)Zi3tWdGj zp(n`I)bP!<X7b#_yxpe>9*v=hXUe;{^M)P*zZMN6lS-q~z0U}1IIU~4Jm~PbmT4g> z=3DXx*`Qx?5++i3XJ0y^_#!VQ_NGfe3^us=zS3LJTl&M6)wwL&6V@MiB?YY*I&*?q zKZ*I=_v9}EZD%xaH5ujAQCy%qZ}ra|e0)(A!cU6M3F6=HC>!+|IGiVM@3-jK;YnfJ zXUu9$9UV3U>rDooZWy8p4Lm;^E?z<X$uT%o-pb)ZMkqS#5gA|XdNq{Fms0TQR1fjc z)y2$NDn6v?`a7e{+ZKn?Sk=v_SL7n=Ungp?Jh}f)Iy%U4sQ-J#b#pLH7VhJubBIdu z{qpce7LRY?FIHyYO|5v=D}>~ec`<mzVLohxdpR{EymKjP{F{tb>0#9dVFRyZ%>JWS z3i{z9NP%P>D?ci%{Om4=aZz64AZ=qOJD2i@8*4JNs_|jB5V@d`UMG=R{Ocj{^VdLB zVr|W#YiSAx<3ZScZa&Z<uRl3cVfXW!XYhw7kBokew*J|16No#7%q#6(-ZjkLjF}!E zstLAq`fFXAW~c<$Oj58D563FCSr%QGoqid7&GU~{((o_sXt;hj>&SfU6;rdN<wY5v zJ0|vOn4?}K1H5`5E(Udlq#7CQ$k(}0U!qlY#JRy~1*@&wF{B4h#t60Yuy)hjlcRfw z41%@i4&HH$N<>p6CBR>WTdBqM2UiH+Jd&NW40^hW@xiq#q|q}G-J;4~mm6R?jr(X* z_YSLX7`ju;0sFLuT$$snQ?u>$(&z}|FsoPb%mfIplobn!Q;fB7%_^fJfqqYQL^!qD z-13EG)>XxYN8o`XJMWM_BI`UMO9wYcn$WJ;9<%3EwOGd_JnG~8@m@&GNpO?jnNV2X z>$`1+(5%KEJoh9w9-%Jn34PS-Fqn<lMABCS@lw9ULXkb1)6KqBMa0i-Rk1pJE$q6D zO21uWxOz|5(;f06^`C`y{(X<ze5zZ)B0lbVgOVO`7(8P(AAW2p3x3%S(cy8~&JJH@ zs{8VCgy=gB!SkatWUZp_-z~ahXN{WYMk~+nuBu?SKPN@cdd<?AG<IH{s1jNpm}(Xp zcJwICQ(?&F=BZy2)CHIWZn6MV!xWFG2Ia}phWMv^IGFjx;VJnc!%CCxosrq>9~GV4 z+eF+&Cr~<7f-j6B!?-5i>zDZqHxw~hiU|)_xq}#L1#$eu&&77UM!f#hV>Pw$Z#)(a zyNeFxYHAfr2_L9}LHdg_eO^QNYDT%-MPLU#wrW32m(>E1um#1C9zy*ZySM|<%Jo6= zD1YH}OZd{RXC4~&gEolwvx=O}DOS`)+L#-H?+U&qbqi`;5&Rw+LVq%gKFB%dGuigI zd4I=cH`A=b({`d&3Y{N2yRNjsrbtarMPJ)4Z1!Zxlxx+kyYb@yfFl=n^M<8Ue=0pw z!u%bHo9W|?6Tm%-C=<qQw|Rz+xi~T{<19>>IqtGvyTPyB)|1Q93HfP#=U&v4PnbD_ z5xapHT*z{!Irh+W+FS9heR}74!rzI?*j-OVAujK3&&mw#=Xosolg0;Y+CD0Un5sz+ zJ(x0^LSxkk5(Dl)*_+l|q8g=&!ibS?$|nu|$1cq6$&LEBf%%4CRziROe%;!_Ew(wD zZ?iLRR=)iOEN&v}clO_!z_g<XdZ)l=d(P!my4)2HTr5j9sj#Z3m89xMCLdYl3i(t< za=<jsMvZ{8TNK4t^fd9Q2D03Hd<pnj!mHp!A*9R-ib?26ym^z(o}KV0jl)}fcK*^Q zc>YMd5L*LD9@pwFT_Q9o<V}3FzL1|2PKlzX;&iT<nGqrXgw&SLN*n41Jx*?E6guK6 zvzm~7h&Se{=a#Tq5qKpoDh%n-vVT-AkY>?^_WCPcNcXxOThe^2FeE}JbJ?4Vo9i1% zBUhE2!~t%{j>mIuJyzVBRV(|XS(C_~QGpw_ju3sxB`JaGrlG#5oO@toJ?^)6{F?l^ z<;mIazCUWd>AyodTQ7eFZ=<S!0Apya=y`Q1t4$1xwh<k$^tpuIc7(-0GB59OUn;DL zuV06m`KCBUiAzF3G5xVQp@qpgNoU07V$n$)ot}??hxH!QYc7IcEQPz6n)8Tf^+{x| zn@dcid-(d6Vzip{mo3Q%uEm0N=|c+Ruq=a#{b2hq8|7Z>17JczDW`<f#|yqy(QXQI z@Aa`y3sRDue^jP@E<9}Y`3Y@L<IK>sPFpliOwi8#8OCc)(z)1EDC%ZDeJ5!hjIsAs zW-;_ksw~p!BkpG<;5O%5M<2x4NBwjbIYL0g*{vi(sb>IX2?*2Yi=(qLN}Ebc?MnbZ zuh0*rMW3~)Am2Ny4}+Xqyu>~&O6ECiB%2u3;1+ll?&42H_j~#mS-9A#?LYZ2UKA<! zFp_n2rHZlsSYYPEEpz{v$50Kf*MykH*gG*Vno_RpkbCKM{3WN=dt5i`9-`E%)ap#z zONT0qGpx>5oo0Aomoq49Q<Wq&LsiC0tM;Adj1BW{utw}x=*+YGeE9?lUAD>rbHm|0 z{fv9#Ou^7|t(uMK<;qJnmEWEqlhjwbFznMG-0+R+JAn!x8CG(*8Y5JnS1_$LV`)nl z2+)Rf*t;O?9<YHz1>`cn&c#-~Ki)5LXP0a(L%&%zNuRQzZ4F+$U}$)S;a?UXv*~yc zU^NZ)1{U&_)m$lm?u@a>-vLvXxnt+nCW2Pb^+A*VhL`1=A5hAiHEB;uMD@wEy+&`Z z3&N7iUd5G;s3_opyEoyNrU+73LS0B4B)aPrm+UdUCO+zlk4fuO_fiZsdYeF2kVRR6 z1!A)+>vdwRo!et``J*x6Sd$zj|KVqZ7!W^-rzK~rP~IfCEn4N}y-X|p+V)mXcrs%e z=g>j2lC}xi9}9{h?e>2YYWhL8WVt6S2lWm8)Ulb$V0PQCPV={k?wv|c--=|;x+w+N zmMb`o;*ZUTjHYfL3$-R{xYaM##E|pl603bAkHTaQZ1qrqn2{Wo_OGZAGr1}?@KI60 zH)1|MSckDQCTn7fsrE4|WuA^Ah+^lY_J$|ZhsWO+bQ(tRHXj9i`$R(-TC0{7uW$d_ zkFY8!5wQw<V#$x}K%7|qe2`W`aCn-R!B!Of$6LW>I_b(^9WGCgKUsZ~E<JX7b2Y<v zwgUXeKN<-tCyQkoi?c=V;$-a5;I#zHa?1=Eo)F5aO71z0lI10btx-*pQ;Upaae{2n z>e&!}EYQ5$AyQ`gcUEhGqZqzMiQl7W^QIodwz=}&VWo#KQy6AHw<grgy5`t>oC5X> zaV+&o_PA@yxY8Y%{$Na32Ia8$E>d5nkGNu%J4EX2$dJis2q(OBt_>^%DXWf>{mRY9 ztwTO$Y&274U>MCt)0X`4qOD?CUYX=B0rc;eAqyt&q-Rc*PdUU)H*FfAM61?u#k78j z`aJ3RD669}{>!<@z~*zjvIz+X99J+#@=+Ov+u~Wql34~__Gow$R3Ln-;|`z6=HsnY zkxGAKp}fZpD62990Y2bfB&}X}x{w@y*G=%J$1cia-ZOd^I|3Zf6i!}Dh`g*)JjmhC z>!;)W_psSYo<jDFOXzZ*feVqiv7&#W(CNlojT`V4b#UP6J?p`w<d9bQXk8{UL6!%E zMJ}09#rP$?16(cBTXd3V&w2ZSMZsn(D+wa#U@@{s1tZDB5cGDE*!I%(u*Sykgt25? z78JcJX*X_Y--txsb$?V~{1S!w26L)Z0dP6TfqO%wqydVh_1+#W)9ISP@dcfEoP5=L zoRr_4?OYRH?3?AEzYjK;aYao1gsK}lD;fx#Z1#&w=g=9Unw4c|@27^(S$J0+L>A2j zN=j-)uMG*l1IZyxFRdiX%CtU-s<#<JC0pR<h4m<QAnq|6G`<d%=>AuSQ2zD5PUF8L z0sgN(-G4{<UxdN`zmE@q|AwBwrSmv#K+Pa=R=g2NX{QbAe}<t0=^?a83^UX`SuvDX zR9gM7p25iO^zAdSU;k<*<b7Av{{WensSuef2sM#fca{}_i|VjW5sm+QKp2@K2vZID z(`F6pHz4AxO`!WpdfN}?{?l4HkzNEfM`}tfA0W2~4R%l?WAPsmEZ&;r5XQX!q$t%H zCCUWEWri7y*vr7=r0^=i7hZ0wA&(h$R|d2|4T>D!(DwV_Jk-l6Nxsti+2)*T@C8r` z!Vb-=qfqbS-)TSl5pg=>{@;gR+7}R4Ie<_I>BlZDD~o7c_P&Y4Bc-6LYT2x?!y6}H zLh4W}r~I08{axT?5mY_fL59I5t}L%1DMk~QydSecmrk+i!1zRR%|pXy`wb#Z2R9+^ for4SU4DLssAx_&|MjxT*6#!s*rZ=l~?mYTGRfd`} From c92744c3d36e371ddc8c6f9bbf9d64812ae5ace9 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 24 May 2021 05:50:45 +0900 Subject: [PATCH 07/28] Create SECURITY.md --- SECURITY.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..2c026a5f33 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Reporting Security Issues + +If you discover a security issue in Misskey, please report it by sending an +email to [syuilotan@yahoo.co.jp](mailto:syuilotan@yahoo.co.jp). + +This will allow us to assess the risk, and make a fix available before we add a +bug report to the GitHub repository. + +Thanks for helping make Misskey safe for everyone. From ae2267220bb743808bffaf9a33f3bc6eed75a5b1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 27 May 2021 17:15:08 +0900 Subject: [PATCH 08/28] wip #7533 --- package.json | 1 + src/client/components/drive.vue | 4 +- src/client/components/follow-button.vue | 2 +- src/client/components/notification.vue | 2 +- src/client/components/notifications.vue | 2 +- src/client/components/timeline.vue | 20 +- src/client/init.ts | 8 +- src/client/instance.ts | 16 +- src/client/os.ts | 24 +- src/client/pages/instance/metrics.vue | 4 +- src/client/pages/instance/queue.vue | 2 +- src/client/pages/messaging/index.vue | 2 +- src/client/pages/messaging/messaging-room.vue | 2 +- src/client/pages/reversi/game.vue | 2 +- src/client/pages/reversi/index.vue | 2 +- src/client/scripts/select-file.ts | 4 +- src/client/scripts/stream.ts | 312 ------------------ src/client/ui/_common_/common.vue | 2 +- src/client/ui/chat/timeline.vue | 20 +- src/client/widgets/job-queue.vue | 2 +- src/client/widgets/photos.vue | 2 +- src/client/widgets/server-metric/index.vue | 2 +- yarn.lock | 18 +- 23 files changed, 69 insertions(+), 386 deletions(-) delete mode 100644 src/client/scripts/stream.ts diff --git a/package.json b/package.json index 4f01c78817..610678989f 100644 --- a/package.json +++ b/package.json @@ -174,6 +174,7 @@ "markdown-it-anchor": "7.1.0", "matter-js": "0.17.1", "mfm-js": "0.16.4", + "misskey-js": "0.0.2", "mocha": "8.4.0", "moji": "0.5.1", "ms": "2.1.3", diff --git a/src/client/components/drive.vue b/src/client/components/drive.vue index 06f9cf7806..ca637e3f3d 100644 --- a/src/client/components/drive.vue +++ b/src/client/components/drive.vue @@ -139,7 +139,7 @@ export default defineComponent({ }); } - this.connection = os.stream.useSharedConnection('drive'); + this.connection = os.stream.useChannel('drive'); this.connection.on('fileCreated', this.onStreamDriveFileCreated); this.connection.on('fileUpdated', this.onStreamDriveFileUpdated); @@ -301,7 +301,7 @@ export default defineComponent({ } }).then(({ canceled, result: url }) => { if (canceled) return; - os.api('drive/files/upload_from_url', { + os.api('drive/files/upload-from-url', { url: url, folderId: this.folder ? this.folder.id : undefined }); diff --git a/src/client/components/follow-button.vue b/src/client/components/follow-button.vue index 7199183c66..49bf678491 100644 --- a/src/client/components/follow-button.vue +++ b/src/client/components/follow-button.vue @@ -71,7 +71,7 @@ export default defineComponent({ }, mounted() { - this.connection = os.stream.useSharedConnection('main'); + this.connection = os.stream.useChannel('main'); this.connection.on('follow', this.onFollowChange); this.connection.on('unfollow', this.onFollowChange); diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue index 9badd7a708..c7063b0aa2 100644 --- a/src/client/components/notification.vue +++ b/src/client/components/notification.vue @@ -109,7 +109,7 @@ export default defineComponent({ this.readObserver.observe(this.$el); - this.connection = os.stream.useSharedConnection('main'); + this.connection = os.stream.useChannel('main'); this.connection.on('readAllNotifications', () => this.readObserver.unobserve(this.$el)); } }, diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue index 161419f891..6caf8eb8e3 100644 --- a/src/client/components/notifications.vue +++ b/src/client/components/notifications.vue @@ -87,7 +87,7 @@ export default defineComponent({ }, mounted() { - this.connection = os.stream.useSharedConnection('main'); + this.connection = os.stream.useChannel('main'); this.connection.on('notification', this.onNotification); }, diff --git a/src/client/components/timeline.vue b/src/client/components/timeline.vue index 753eba2ba1..c21e1ec2a6 100644 --- a/src/client/components/timeline.vue +++ b/src/client/components/timeline.vue @@ -92,33 +92,33 @@ export default defineComponent({ this.query = { antennaId: this.antenna }; - this.connection = os.stream.connectToChannel('antenna', { + this.connection = os.stream.useChannel('antenna', { antennaId: this.antenna }); this.connection.on('note', prepend); } else if (this.src == 'home') { endpoint = 'notes/timeline'; - this.connection = os.stream.useSharedConnection('homeTimeline'); + this.connection = os.stream.useChannel('homeTimeline'); this.connection.on('note', prepend); - this.connection2 = os.stream.useSharedConnection('main'); + this.connection2 = os.stream.useChannel('main'); this.connection2.on('follow', onChangeFollowing); this.connection2.on('unfollow', onChangeFollowing); } else if (this.src == 'local') { endpoint = 'notes/local-timeline'; - this.connection = os.stream.useSharedConnection('localTimeline'); + this.connection = os.stream.useChannel('localTimeline'); this.connection.on('note', prepend); } else if (this.src == 'social') { endpoint = 'notes/hybrid-timeline'; - this.connection = os.stream.useSharedConnection('hybridTimeline'); + this.connection = os.stream.useChannel('hybridTimeline'); this.connection.on('note', prepend); } else if (this.src == 'global') { endpoint = 'notes/global-timeline'; - this.connection = os.stream.useSharedConnection('globalTimeline'); + this.connection = os.stream.useChannel('globalTimeline'); this.connection.on('note', prepend); } else if (this.src == 'mentions') { endpoint = 'notes/mentions'; - this.connection = os.stream.useSharedConnection('main'); + this.connection = os.stream.useChannel('main'); this.connection.on('mention', prepend); } else if (this.src == 'directs') { endpoint = 'notes/mentions'; @@ -130,14 +130,14 @@ export default defineComponent({ prepend(note); } }; - this.connection = os.stream.useSharedConnection('main'); + this.connection = os.stream.useChannel('main'); this.connection.on('mention', onNote); } else if (this.src == 'list') { endpoint = 'notes/user-list-timeline'; this.query = { listId: this.list }; - this.connection = os.stream.connectToChannel('userList', { + this.connection = os.stream.useChannel('userList', { listId: this.list }); this.connection.on('note', prepend); @@ -148,7 +148,7 @@ export default defineComponent({ this.query = { channelId: this.channel }; - this.connection = os.stream.connectToChannel('channel', { + this.connection = os.stream.useChannel('channel', { channelId: this.channel }); this.connection.on('note', prepend); diff --git a/src/client/init.ts b/src/client/init.ts index a4465d75c3..19b95fc50e 100644 --- a/src/client/init.ts +++ b/src/client/init.ts @@ -163,8 +163,6 @@ fetchInstance().then(() => { initializeSw(); }); -stream.init($i); - const app = createApp(await ( window.location.search === '?zen' ? import('@client/ui/zen.vue') : !$i ? import('@client/ui/visitor.vue') : @@ -296,7 +294,7 @@ if ($i) { } } - const main = stream.useSharedConnection('main', 'System'); + const main = stream.useChannel('main', 'System'); // 自分の情報が更新されたとき main.on('meUpdated', i => { @@ -358,10 +356,6 @@ if ($i) { sound.play('channel'); }); - main.on('readAllAnnouncements', () => { - updateAccount({ hasUnreadAnnouncement: false }); - }); - // トークンが再生成されたとき // このままではMisskeyが利用できないので強制的にサインアウトさせる main.on('myTokenRegenerated', () => { diff --git a/src/client/instance.ts b/src/client/instance.ts index 024ff1acbd..04d3353208 100644 --- a/src/client/instance.ts +++ b/src/client/instance.ts @@ -1,26 +1,14 @@ import { computed, reactive } from 'vue'; +import * as Misskey from 'misskey-js'; import { api } from './os'; // TODO: 他のタブと永続化されたstateを同期 -export type Instance = { - emojis: { - category: string; - }[]; - ads: { - id: string; - ratio: number; - place: string; - url: string; - imageUrl: string; - }[]; -}; - const data = localStorage.getItem('instance'); // TODO: instanceをリアクティブにするかは再考の余地あり -export const instance: Instance = reactive(data ? JSON.parse(data) : { +export const instance: Misskey.entities.InstanceMetadata = reactive(data ? JSON.parse(data) : { // TODO: set default values }); diff --git a/src/client/os.ts b/src/client/os.ts index b159cf509d..e6355b45b8 100644 --- a/src/client/os.ts +++ b/src/client/os.ts @@ -3,16 +3,16 @@ import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vue'; import { EventEmitter } from 'eventemitter3'; import insertTextAtCursor from 'insert-text-at-cursor'; +import * as Misskey from 'misskey-js'; import * as Sentry from '@sentry/browser'; -import Stream from '@client/scripts/stream'; -import { apiUrl, debug } from '@client/config'; +import { apiUrl, debug, url } from '@client/config'; import MkPostFormDialog from '@client/components/post-form-dialog.vue'; import MkWaitingDialog from '@client/components/waiting-dialog.vue'; import { resolve } from '@client/router'; import { $i } from '@client/account'; import { defaultStore } from '@client/store'; -export const stream = markRaw(new Stream()); +export const stream = markRaw(new Misskey.Stream(url, $i)); export const pendingApiRequestsCount = ref(0); let apiRequestsCount = 0; // for debug @@ -20,7 +20,11 @@ export const apiRequests = ref([]); // for debug export const windows = new Map(); -export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) { +const apiClient = new Misskey.api.APIClient({ + origin: url, +}); + +export const api = ((endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) => { pendingApiRequestsCount.value++; const onFinally = () => { @@ -90,17 +94,15 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st promise.then(onFinally, onFinally); return promise; -} +}) as typeof apiClient.request; -export function apiWithDialog( +export const apiWithDialog = (( endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined, - onSuccess?: (res: any) => void, - onFailure?: (e: Error) => void, -) { +) => { const promise = api(endpoint, data, token); - promiseDialog(promise, onSuccess, onFailure ? onFailure : (e) => { + promiseDialog(promise, null, (e) => { dialog({ type: 'error', text: e.message + '\n' + (e as any).id, @@ -108,7 +110,7 @@ export function apiWithDialog( }); return promise; -} +}) as typeof api; export function promiseDialog<T extends Promise<any>>( promise: T, diff --git a/src/client/pages/instance/metrics.vue b/src/client/pages/instance/metrics.vue index 18cfe5eee2..407cce9e7f 100644 --- a/src/client/pages/instance/metrics.vue +++ b/src/client/pages/instance/metrics.vue @@ -90,7 +90,7 @@ export default defineComponent({ stats: null, serverInfo: null, connection: null, - queueConnection: os.stream.useSharedConnection('queueStats'), + queueConnection: os.stream.useChannel('queueStats'), memUsage: 0, chartCpuMem: null, chartNet: null, @@ -121,7 +121,7 @@ export default defineComponent({ os.api('admin/server-info', {}).then(res => { this.serverInfo = res; - this.connection = os.stream.useSharedConnection('serverStats'); + this.connection = os.stream.useChannel('serverStats'); this.connection.on('stats', this.onStats); this.connection.on('statsLog', this.onStatsLog); this.connection.send('requestLog', { diff --git a/src/client/pages/instance/queue.vue b/src/client/pages/instance/queue.vue index 2dccf48d31..8f56fd74bf 100644 --- a/src/client/pages/instance/queue.vue +++ b/src/client/pages/instance/queue.vue @@ -35,7 +35,7 @@ export default defineComponent({ title: this.$ts.jobQueue, icon: 'fas fa-clipboard-list', }, - connection: os.stream.useSharedConnection('queueStats'), + connection: os.stream.useChannel('queueStats'), } }, diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue index 9f3323f629..832cce5ab9 100644 --- a/src/client/pages/messaging/index.vue +++ b/src/client/pages/messaging/index.vue @@ -63,7 +63,7 @@ export default defineComponent({ }, mounted() { - this.connection = os.stream.useSharedConnection('messagingIndex'); + this.connection = os.stream.useChannel('messagingIndex'); this.connection.on('message', this.onMessage); this.connection.on('read', this.onRead); diff --git a/src/client/pages/messaging/messaging-room.vue b/src/client/pages/messaging/messaging-room.vue index 44bfd6c51d..f1d55ee288 100644 --- a/src/client/pages/messaging/messaging-room.vue +++ b/src/client/pages/messaging/messaging-room.vue @@ -141,7 +141,7 @@ const Component = defineComponent({ this.group = group; } - this.connection = os.stream.connectToChannel('messaging', { + this.connection = os.stream.useChannel('messaging', { otherparty: this.user ? this.user.id : undefined, group: this.group ? this.group.id : undefined, }); diff --git a/src/client/pages/reversi/game.vue b/src/client/pages/reversi/game.vue index 62c99d7755..dc4d11ca4a 100644 --- a/src/client/pages/reversi/game.vue +++ b/src/client/pages/reversi/game.vue @@ -61,7 +61,7 @@ export default defineComponent({ if (this.connection) { this.connection.dispose(); } - this.connection = os.stream.connectToChannel('gamesReversiGame', { + this.connection = os.stream.useChannel('gamesReversiGame', { gameId: this.game.id }); this.connection.on('started', this.onStarted); diff --git a/src/client/pages/reversi/index.vue b/src/client/pages/reversi/index.vue index 37126fca10..dd329084a8 100644 --- a/src/client/pages/reversi/index.vue +++ b/src/client/pages/reversi/index.vue @@ -92,7 +92,7 @@ export default defineComponent({ mounted() { if (this.$i) { - this.connection = os.stream.useSharedConnection('gamesReversi'); + this.connection = os.stream.useChannel('gamesReversi'); this.connection.on('invited', this.onInvited); diff --git a/src/client/scripts/select-file.ts b/src/client/scripts/select-file.ts index c193e7dc71..b8039fb670 100644 --- a/src/client/scripts/select-file.ts +++ b/src/client/scripts/select-file.ts @@ -47,7 +47,7 @@ export function selectFile(src: any, label: string | null, multiple = false) { const marker = Math.random().toString(); // TODO: UUIDとか使う - const connection = os.stream.useSharedConnection('main'); + const connection = os.stream.useChannel('main'); connection.on('urlUploadFinished', data => { if (data.marker === marker) { res(multiple ? [data.file] : data.file); @@ -55,7 +55,7 @@ export function selectFile(src: any, label: string | null, multiple = false) { } }); - os.api('drive/files/upload_from_url', { + os.api('drive/files/upload-from-url', { url: url, marker }); diff --git a/src/client/scripts/stream.ts b/src/client/scripts/stream.ts deleted file mode 100644 index 065059221d..0000000000 --- a/src/client/scripts/stream.ts +++ /dev/null @@ -1,312 +0,0 @@ -import autobind from 'autobind-decorator'; -import { EventEmitter } from 'eventemitter3'; -import ReconnectingWebsocket from 'reconnecting-websocket'; -import { markRaw } from 'vue'; -import { debug, wsUrl } from '@client/config'; -import { query as urlQuery } from '../../prelude/url'; - -/** - * Misskey stream connection - */ -export default class Stream extends EventEmitter { - private stream: ReconnectingWebsocket; - public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing'; - private sharedConnectionPools: Pool[] = []; - private sharedConnections: SharedConnection[] = []; - private nonSharedConnections: NonSharedConnection[] = []; - - @autobind - public init(user): void { - const query = urlQuery({ - i: user?.token, - _t: Date.now(), - }); - - this.stream = new ReconnectingWebsocket(`${wsUrl}?${query}`, '', { minReconnectionDelay: 1 }); // https://github.com/pladaria/reconnecting-websocket/issues/91 - this.stream.addEventListener('open', this.onOpen); - this.stream.addEventListener('close', this.onClose); - this.stream.addEventListener('message', this.onMessage); - } - - @autobind - public useSharedConnection(channel: string, name?: string): SharedConnection { - let pool = this.sharedConnectionPools.find(p => p.channel === channel); - - if (pool == null) { - pool = new Pool(this, channel); - this.sharedConnectionPools.push(pool); - } - - const connection = markRaw(new SharedConnection(this, channel, pool, name)); - this.sharedConnections.push(connection); - return connection; - } - - @autobind - public removeSharedConnection(connection: SharedConnection) { - this.sharedConnections = this.sharedConnections.filter(c => c !== connection); - } - - @autobind - public removeSharedConnectionPool(pool: Pool) { - this.sharedConnectionPools = this.sharedConnectionPools.filter(p => p !== pool); - } - - @autobind - public connectToChannel(channel: string, params?: any): NonSharedConnection { - const connection = markRaw(new NonSharedConnection(this, channel, params)); - this.nonSharedConnections.push(connection); - return connection; - } - - @autobind - public disconnectToChannel(connection: NonSharedConnection) { - this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection); - } - - /** - * Callback of when open connection - */ - @autobind - private onOpen() { - const isReconnect = this.state === 'reconnecting'; - - this.state = 'connected'; - this.emit('_connected_'); - - // チャンネル再接続 - if (isReconnect) { - for (const p of this.sharedConnectionPools) - p.connect(); - for (const c of this.nonSharedConnections) - c.connect(); - } - } - - /** - * Callback of when close connection - */ - @autobind - private onClose() { - if (this.state === 'connected') { - this.state = 'reconnecting'; - this.emit('_disconnected_'); - } - } - - /** - * Callback of when received a message from connection - */ - @autobind - private onMessage(message) { - const { type, body } = JSON.parse(message.data); - - if (type === 'channel') { - const id = body.id; - - let connections: Connection[]; - - connections = this.sharedConnections.filter(c => c.id === id); - - if (connections.length === 0) { - connections = [this.nonSharedConnections.find(c => c.id === id)]; - } - - for (const c of connections.filter(c => c != null)) { - c.emit(body.type, Object.freeze(body.body)); - if (debug) c.inCount++; - } - } else { - this.emit(type, Object.freeze(body)); - } - } - - /** - * Send a message to connection - */ - @autobind - public send(typeOrPayload, payload?) { - const data = payload === undefined ? typeOrPayload : { - type: typeOrPayload, - body: payload - }; - - this.stream.send(JSON.stringify(data)); - } - - /** - * Close this connection - */ - @autobind - public close() { - this.stream.removeEventListener('open', this.onOpen); - this.stream.removeEventListener('message', this.onMessage); - } -} - -let idCounter = 0; - -class Pool { - public channel: string; - public id: string; - protected stream: Stream; - public users = 0; - private disposeTimerId: any; - private isConnected = false; - - constructor(stream: Stream, channel: string) { - this.channel = channel; - this.stream = stream; - - this.id = (++idCounter).toString(); - - this.stream.on('_disconnected_', this.onStreamDisconnected); - } - - @autobind - private onStreamDisconnected() { - this.isConnected = false; - } - - @autobind - public inc() { - if (this.users === 0 && !this.isConnected) { - this.connect(); - } - - this.users++; - - // タイマー解除 - if (this.disposeTimerId) { - clearTimeout(this.disposeTimerId); - this.disposeTimerId = null; - } - } - - @autobind - public dec() { - this.users--; - - // そのコネクションの利用者が誰もいなくなったら - if (this.users === 0) { - // また直ぐに再利用される可能性があるので、一定時間待ち、 - // 新たな利用者が現れなければコネクションを切断する - this.disposeTimerId = setTimeout(() => { - this.disconnect(); - }, 3000); - } - } - - @autobind - public connect() { - if (this.isConnected) return; - this.isConnected = true; - this.stream.send('connect', { - channel: this.channel, - id: this.id - }); - } - - @autobind - private disconnect() { - this.stream.off('_disconnected_', this.onStreamDisconnected); - this.stream.send('disconnect', { id: this.id }); - this.stream.removeSharedConnectionPool(this); - } -} - -abstract class Connection extends EventEmitter { - public channel: string; - protected stream: Stream; - public abstract id: string; - - public name?: string; // for debug - public inCount: number = 0; // for debug - public outCount: number = 0; // for debug - - constructor(stream: Stream, channel: string, name?: string) { - super(); - - this.stream = stream; - this.channel = channel; - this.name = name; - } - - @autobind - public send(id: string, typeOrPayload, payload?) { - const type = payload === undefined ? typeOrPayload.type : typeOrPayload; - const body = payload === undefined ? typeOrPayload.body : payload; - - this.stream.send('ch', { - id: id, - type: type, - body: body - }); - - if (debug) this.outCount++; - } - - public abstract dispose(): void; -} - -class SharedConnection extends Connection { - private pool: Pool; - - public get id(): string { - return this.pool.id; - } - - constructor(stream: Stream, channel: string, pool: Pool, name?: string) { - super(stream, channel, name); - - this.pool = pool; - this.pool.inc(); - } - - @autobind - public send(typeOrPayload, payload?) { - super.send(this.pool.id, typeOrPayload, payload); - } - - @autobind - public dispose() { - this.pool.dec(); - this.removeAllListeners(); - this.stream.removeSharedConnection(this); - } -} - -class NonSharedConnection extends Connection { - public id: string; - protected params: any; - - constructor(stream: Stream, channel: string, params?: any) { - super(stream, channel); - - this.params = params; - this.id = (++idCounter).toString(); - - this.connect(); - } - - @autobind - public connect() { - this.stream.send('connect', { - channel: this.channel, - id: this.id, - params: this.params - }); - } - - @autobind - public send(typeOrPayload, payload?) { - super.send(this.id, typeOrPayload, payload); - } - - @autobind - public dispose() { - this.removeAllListeners(); - this.stream.send('disconnect', { id: this.id }); - this.stream.disconnectToChannel(this); - } -} diff --git a/src/client/ui/_common_/common.vue b/src/client/ui/_common_/common.vue index 785b1631db..1e825e0fe0 100644 --- a/src/client/ui/_common_/common.vue +++ b/src/client/ui/_common_/common.vue @@ -43,7 +43,7 @@ export default defineComponent({ }; if ($i) { - const connection = stream.useSharedConnection('main', 'UI'); + const connection = stream.useChannel('main', 'UI'); connection.on('notification', onNotification); } diff --git a/src/client/ui/chat/timeline.vue b/src/client/ui/chat/timeline.vue index 13032cce09..2245a9d8a5 100644 --- a/src/client/ui/chat/timeline.vue +++ b/src/client/ui/chat/timeline.vue @@ -121,33 +121,33 @@ export default defineComponent({ this.query = { antennaId: this.antenna }; - this.connection = os.stream.connectToChannel('antenna', { + this.connection = os.stream.useChannel('antenna', { antennaId: this.antenna }); this.connection.on('note', prepend); } else if (this.src == 'home') { endpoint = 'notes/timeline'; - this.connection = os.stream.useSharedConnection('homeTimeline'); + this.connection = os.stream.useChannel('homeTimeline'); this.connection.on('note', prepend); - this.connection2 = os.stream.useSharedConnection('main'); + this.connection2 = os.stream.useChannel('main'); this.connection2.on('follow', onChangeFollowing); this.connection2.on('unfollow', onChangeFollowing); } else if (this.src == 'local') { endpoint = 'notes/local-timeline'; - this.connection = os.stream.useSharedConnection('localTimeline'); + this.connection = os.stream.useChannel('localTimeline'); this.connection.on('note', prepend); } else if (this.src == 'social') { endpoint = 'notes/hybrid-timeline'; - this.connection = os.stream.useSharedConnection('hybridTimeline'); + this.connection = os.stream.useChannel('hybridTimeline'); this.connection.on('note', prepend); } else if (this.src == 'global') { endpoint = 'notes/global-timeline'; - this.connection = os.stream.useSharedConnection('globalTimeline'); + this.connection = os.stream.useChannel('globalTimeline'); this.connection.on('note', prepend); } else if (this.src == 'mentions') { endpoint = 'notes/mentions'; - this.connection = os.stream.useSharedConnection('main'); + this.connection = os.stream.useChannel('main'); this.connection.on('mention', prepend); } else if (this.src == 'directs') { endpoint = 'notes/mentions'; @@ -159,14 +159,14 @@ export default defineComponent({ prepend(note); } }; - this.connection = os.stream.useSharedConnection('main'); + this.connection = os.stream.useChannel('main'); this.connection.on('mention', onNote); } else if (this.src == 'list') { endpoint = 'notes/user-list-timeline'; this.query = { listId: this.list }; - this.connection = os.stream.connectToChannel('userList', { + this.connection = os.stream.useChannel('userList', { listId: this.list }); this.connection.on('note', prepend); @@ -178,7 +178,7 @@ export default defineComponent({ this.query = { channelId: this.channel }; - this.connection = os.stream.connectToChannel('channel', { + this.connection = os.stream.useChannel('channel', { channelId: this.channel }); this.connection.on('note', prepend); diff --git a/src/client/widgets/job-queue.vue b/src/client/widgets/job-queue.vue index 31a322e6e2..162ffe9c89 100644 --- a/src/client/widgets/job-queue.vue +++ b/src/client/widgets/job-queue.vue @@ -65,7 +65,7 @@ export default defineComponent({ extends: widget, data() { return { - connection: os.stream.useSharedConnection('queueStats'), + connection: os.stream.useChannel('queueStats'), inbox: { activeSincePrevTick: 0, active: 0, diff --git a/src/client/widgets/photos.vue b/src/client/widgets/photos.vue index 65843385b6..7f6fa82722 100644 --- a/src/client/widgets/photos.vue +++ b/src/client/widgets/photos.vue @@ -48,7 +48,7 @@ export default defineComponent({ }; }, mounted() { - this.connection = os.stream.useSharedConnection('main'); + this.connection = os.stream.useChannel('main'); this.connection.on('driveFileCreated', this.onDriveFileCreated); diff --git a/src/client/widgets/server-metric/index.vue b/src/client/widgets/server-metric/index.vue index 6331b5bdf1..2398e9920f 100644 --- a/src/client/widgets/server-metric/index.vue +++ b/src/client/widgets/server-metric/index.vue @@ -63,7 +63,7 @@ export default defineComponent({ os.api('server-info', {}).then(res => { this.meta = res; }); - this.connection = os.stream.useSharedConnection('serverStats'); + this.connection = os.stream.useChannel('serverStats'); }, unmounted() { this.connection.dispose(); diff --git a/yarn.lock b/yarn.lock index 9e32e6e913..9296aafc4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1290,7 +1290,7 @@ "@vue/compiler-dom" "3.0.11" "@vue/shared" "3.0.11" -"@vue/reactivity@3.0.11": +"@vue/reactivity@3.0.11", "@vue/reactivity@^3.0.11": version "3.0.11" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.11.tgz#07b588349fd05626b17f3500cbef7d4bdb4dbd0b" integrity sha512-SKM3YKxtXHBPMf7yufXeBhCZ4XZDKP9/iXeQSC8bBO3ivBuzAi4aZi0bNoeE2IF2iGfP/AHEt1OU4ARj4ao/Xw== @@ -1899,7 +1899,7 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autobind-decorator@2.4.0: +autobind-decorator@2.4.0, autobind-decorator@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/autobind-decorator/-/autobind-decorator-2.4.0.tgz#ea9e1c98708cf3b5b356f7cf9f10f265ff18239c" integrity sha512-OGYhWUO72V6DafbF8PM8rm3EPbfuyMZcJhtm5/n26IDwO18pohE4eNazLoCGhPiXOCD0gEGmrbU3849QvM8bbw== @@ -4203,7 +4203,7 @@ event-target-shim@^5.0.0: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter3@4.0.7: +eventemitter3@4.0.7, eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -6977,6 +6977,16 @@ minizlib@^2.0.0, minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" +misskey-js@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/misskey-js/-/misskey-js-0.0.2.tgz#233d62e5a326a00dd72f36d63436e6584c8076f2" + integrity sha512-gsq3E9lUepNapK4i/3mmqjobQV6gYlgO1O1rQt401ot3LCYlcaLhlUrwBOFtI+ALMGKgwRgkLlDQhcWgAfHHuQ== + dependencies: + "@vue/reactivity" "^3.0.11" + autobind-decorator "^2.4.0" + eventemitter3 "^4.0.7" + reconnecting-websocket "^4.4.0" + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -9072,7 +9082,7 @@ rechoir@^0.7.0: dependencies: resolve "^1.9.0" -reconnecting-websocket@4.4.0: +reconnecting-websocket@4.4.0, reconnecting-websocket@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783" integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng== From 466c083233d5f44cfcdfdfa02d8a6bb090382400 Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Thu, 27 May 2021 22:40:48 +0900 Subject: [PATCH 09/28] =?UTF-8?q?=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92proxy=E3=81=AB=E9=80=9A?= =?UTF-8?q?=E3=81=99=E3=82=88=E3=81=86=E3=81=AB=20(#7526)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/scripts/get-static-image-url.ts | 5 +++++ src/misc/populate-emojis.ts | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/client/scripts/get-static-image-url.ts b/src/client/scripts/get-static-image-url.ts index e2728d73f4..92c31914c7 100644 --- a/src/client/scripts/get-static-image-url.ts +++ b/src/client/scripts/get-static-image-url.ts @@ -3,6 +3,11 @@ import * as url from '../../prelude/url'; export function getStaticImageUrl(baseUrl: string): string { const u = new URL(baseUrl); + if (u.href.startsWith(`${instanceUrl}/proxy/`)) { + // もう既にproxyっぽそうだったらsearchParams付けるだけ + u.searchParams.set('static', '1'); + return u.href; + } const dummy = `${u.host}${u.pathname}`; // 拡張子がないとキャッシュしてくれないCDNがあるので return `${instanceUrl}/proxy/${dummy}?${url.query({ url: u.href, diff --git a/src/misc/populate-emojis.ts b/src/misc/populate-emojis.ts index 8052c71489..a3f67ccb98 100644 --- a/src/misc/populate-emojis.ts +++ b/src/misc/populate-emojis.ts @@ -5,6 +5,8 @@ import { Note } from '../models/entities/note'; import { Cache } from './cache'; import { isSelfHost, toPunyNullable } from './convert-host'; import { decodeReaction } from './reaction-lib'; +import config from '@/config'; +import { query } from '@/prelude/url'; const cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12); @@ -59,9 +61,12 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu if (emoji == null) return null; + const isLocal = emojiName.endsWith('@.'); + const url = isLocal ? emoji.url : `${config.url}/proxy/image.png?${query({url: emoji.url})}`; + return { name: emojiName, - url: emoji.url, + url, }; } From db3724cf33c402d66700f89b319b423887466757 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 28 May 2021 09:34:42 +0900 Subject: [PATCH 10/28] improve types --- src/server/api/define.ts | 7 +++++-- .../api/endpoints/gallery/posts/create.ts | 3 ++- .../api/endpoints/gallery/posts/update.ts | 3 ++- src/services/chart/core.ts | 21 +++++++++---------- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/server/api/define.ts b/src/server/api/define.ts index 432d5017e8..cba69cfdc4 100644 --- a/src/server/api/define.ts +++ b/src/server/api/define.ts @@ -5,6 +5,8 @@ import { ApiError } from './error'; import { SchemaType } from '@/misc/schema'; import { AccessToken } from '../../models/entities/access-token'; +type NonOptional<T> = T extends undefined ? never : T; + type SimpleUserInfo = { id: ILocalUser['id']; host: ILocalUser['host']; @@ -17,11 +19,12 @@ type SimpleUserInfo = { isSilenced: ILocalUser['isSilenced']; }; -// TODO: defaultが設定されている場合はその型も考慮する type Params<T extends IEndpointMeta> = { [P in keyof T['params']]: NonNullable<T['params']>[P]['transform'] extends Function ? ReturnType<NonNullable<T['params']>[P]['transform']> - : ReturnType<NonNullable<T['params']>[P]['validator']['get']>[0]; + : NonNullable<T['params']>[P]['default'] extends null | number | string + ? NonOptional<ReturnType<NonNullable<T['params']>[P]['validator']['get']>[0]> + : ReturnType<NonNullable<T['params']>[P]['validator']['get']>[0]; }; export type Response = Record<string, any> | void; diff --git a/src/server/api/endpoints/gallery/posts/create.ts b/src/server/api/endpoints/gallery/posts/create.ts index d1ae68b126..ed24a45f88 100644 --- a/src/server/api/endpoints/gallery/posts/create.ts +++ b/src/server/api/endpoints/gallery/posts/create.ts @@ -6,6 +6,7 @@ import { DriveFiles, GalleryPosts } from '../../../../../models'; import { genId } from '../../../../../misc/gen-id'; import { GalleryPost } from '../../../../../models/entities/gallery-post'; import { ApiError } from '../../../error'; +import { DriveFile } from '@/models/entities/drive-file'; export const meta = { tags: ['gallery'], @@ -55,7 +56,7 @@ export default define(meta, async (ps, user) => { id: fileId, userId: user.id }) - ))).filter(file => file != null); + ))).filter((file): file is DriveFile => file != null); if (files.length === 0) { throw new Error(); diff --git a/src/server/api/endpoints/gallery/posts/update.ts b/src/server/api/endpoints/gallery/posts/update.ts index c8bb8d48c9..d9176ea407 100644 --- a/src/server/api/endpoints/gallery/posts/update.ts +++ b/src/server/api/endpoints/gallery/posts/update.ts @@ -5,6 +5,7 @@ import { ID } from '../../../../../misc/cafy-id'; import { DriveFiles, GalleryPosts } from '../../../../../models'; import { GalleryPost } from '../../../../../models/entities/gallery-post'; import { ApiError } from '../../../error'; +import { DriveFile } from '@/models/entities/drive-file'; export const meta = { tags: ['gallery'], @@ -58,7 +59,7 @@ export default define(meta, async (ps, user) => { id: fileId, userId: user.id }) - ))).filter(file => file != null); + ))).filter((file): file is DriveFile => file != null); if (files.length === 0) { throw new Error(); diff --git a/src/services/chart/core.ts b/src/services/chart/core.ts index d956d33bd7..4a554daa78 100644 --- a/src/services/chart/core.ts +++ b/src/services/chart/core.ts @@ -93,7 +93,7 @@ export default abstract class Chart<T extends Record<string, any>> { } @autobind - private static convertFlattenColumnsToObject(x: Record<string, number>) { + private static convertFlattenColumnsToObject(x: Record<string, any>): Record<string, any> { const obj = {} as any; for (const k of Object.keys(x).filter(k => k.startsWith(Chart.columnPrefix))) { // now k is ___x_y_z @@ -285,8 +285,7 @@ export default abstract class Chart<T extends Record<string, any>> { const latest = await this.getLatestLog(group); if (latest != null) { - const obj = Chart.convertFlattenColumnsToObject( - latest as Record<string, any>); + const obj = Chart.convertFlattenColumnsToObject(latest) as T; // 空ログデータを作成 data = this.getNewLog(obj); @@ -474,13 +473,13 @@ export default abstract class Chart<T extends Record<string, any>> { const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); if (log) { - const data = Chart.convertFlattenColumnsToObject(log as Record<string, any>); - chart.unshift(Chart.countUniqueFields(data)); + const data = Chart.convertFlattenColumnsToObject(log); + chart.unshift(Chart.countUniqueFields(data) as T); } else { // 隙間埋め const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); - const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record<string, any>) : null; - chart.unshift(Chart.countUniqueFields(this.getNewLog(data))); + const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null; + chart.unshift(Chart.countUniqueFields(this.getNewLog(data)) as T); } } } else if (span === 'day') { @@ -497,14 +496,14 @@ export default abstract class Chart<T extends Record<string, any>> { if (log) { if (logsForEachDays[currentDayIndex]) { - logsForEachDays[currentDayIndex].unshift(Chart.convertFlattenColumnsToObject(log)); + logsForEachDays[currentDayIndex].unshift(Chart.convertFlattenColumnsToObject(log) as T); } else { - logsForEachDays[currentDayIndex] = [Chart.convertFlattenColumnsToObject(log)]; + logsForEachDays[currentDayIndex] = [Chart.convertFlattenColumnsToObject(log) as T]; } } else { // 隙間埋め const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); - const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record<string, any>) : null; + const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null; const newLog = this.getNewLog(data); if (logsForEachDays[currentDayIndex]) { logsForEachDays[currentDayIndex].unshift(newLog); @@ -516,7 +515,7 @@ export default abstract class Chart<T extends Record<string, any>> { for (const logs of logsForEachDays) { const log = this.aggregate(logs); - chart.unshift(Chart.countUniqueFields(log)); + chart.unshift(Chart.countUniqueFields(log) as T); } } From ffb9646ce9c3d2326a3e922e58702674eb65646c Mon Sep 17 00:00:00 2001 From: nullobsi <me@nullob.si> Date: Thu, 27 May 2021 17:38:09 -0700 Subject: [PATCH 11/28] Add image description support (#7518) * recieve image descriptions under the name property * fix other components * use comment for alt and title * allow editing of file comment * allow editing of file comment in note dialog * federate note comments * use file instead of this * backend should accept comment on update * update now actually accepts comment * allow multiline descriptions * image should also have description attached * Update locales/ja-JP.yml Co-authored-by: rinsuki <428rinsuki+git@gmail.com> * Use custom component with side-by-side image * improve usability on mobile devices * revert changes * Update post-form-attaches.vue * Update drive.file.vue * Update media-caption.vue Co-authored-by: rinsuki <428rinsuki+git@gmail.com> Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> --- locales/ja-JP.yml | 3 + src/client/components/drive.file.vue | 24 ++ src/client/components/image-viewer.vue | 2 +- src/client/components/media-caption.vue | 238 ++++++++++++++++++ src/client/components/media-image.vue | 4 +- src/client/components/post-form-attaches.vue | 25 ++ src/remote/activitypub/models/image.ts | 2 +- src/remote/activitypub/renderer/document.ts | 3 +- src/remote/activitypub/renderer/image.ts | 3 +- .../api/endpoints/drive/files/update.ts | 11 + src/services/drive/upload-from-url.ts | 6 + 11 files changed, 315 insertions(+), 6 deletions(-) create mode 100644 src/client/components/media-caption.vue diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e869f5b015..23f3bf7296 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -279,6 +279,7 @@ emptyDrive: "ドライブは空です" emptyFolder: "フォルダーは空です" unableToDelete: "削除できません" inputNewFileName: "新しいファイル名を入力してください" +inputNewDescription: "新しいキャプションを入力してください" inputNewFolderName: "新しいフォルダ名を入力してください" circularReferenceFolder: "移動先のフォルダーは、移動するフォルダーのサブフォルダーです。" hasChildFilesOrFolders: "このフォルダは空でないため、削除できません。" @@ -546,6 +547,8 @@ disablePlayer: "プレイヤーを閉じる" expandTweet: "ツイートを展開する" themeEditor: "テーマエディター" description: "説明" +describeFile: "キャプションを付ける" +enterFileDescription: "キャプションを入力" author: "作者" leaveConfirm: "未保存の変更があります。破棄しますか?" manage: "管理" diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue index 37b1afc1b3..3d20de23e9 100644 --- a/src/client/components/drive.file.vue +++ b/src/client/components/drive.file.vue @@ -87,6 +87,10 @@ export default defineComponent({ text: this.file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive, icon: this.file.isSensitive ? 'fas fa-eye' : 'fas fa-eye-slash', action: this.toggleSensitive + }, { + text: this.$ts.describeFile, + icon: 'fas fa-i-cursor', + action: this.describe }, null, { text: this.$ts.copyUrl, icon: 'fas fa-link', @@ -150,6 +154,26 @@ export default defineComponent({ }); }, + describe() { + os.popup(import('@client/components/media-caption.vue'), { + title: this.$ts.describeFile, + input: { + placeholder: this.$ts.inputNewDescription, + default: this.file.comment !== null ? this.file.comment : '', + }, + image: this.file + }, { + done: result => { + if (!result || result.canceled) return; + let comment = result.result; + os.api('drive/files/update', { + fileId: this.file.id, + comment: comment.length == 0 ? null : comment + }); + } + }, 'closed'); + }, + toggleSensitive() { os.api('drive/files/update', { fileId: this.file.id, diff --git a/src/client/components/image-viewer.vue b/src/client/components/image-viewer.vue index ec22bd98ec..7701ae926f 100644 --- a/src/client/components/image-viewer.vue +++ b/src/client/components/image-viewer.vue @@ -2,7 +2,7 @@ <MkModal ref="modal" @click="$refs.modal.close()" @closed="$emit('closed')"> <div class="xubzgfga"> <header>{{ image.name }}</header> - <img :src="image.url" :alt="image.name" :title="image.name" @click="$refs.modal.close()"/> + <img :src="image.url" :alt="image.comment" :title="image.comment" @click="$refs.modal.close()"/> <footer> <span>{{ image.type }}</span> <span>{{ bytes(image.size) }}</span> diff --git a/src/client/components/media-caption.vue b/src/client/components/media-caption.vue new file mode 100644 index 0000000000..690927d4c5 --- /dev/null +++ b/src/client/components/media-caption.vue @@ -0,0 +1,238 @@ +<template> + <MkModal ref="modal" @click="done(true)" @closed="$emit('closed')"> + <div class="container"> + <div class="fullwidth top-caption"> + <div class="mk-dialog"> + <header v-if="title"><Mfm :text="title"/></header> + <textarea autofocus v-model="inputValue" :placeholder="input.placeholder" @keydown="onInputKeydown"></textarea> + <div class="buttons" v-if="(showOkButton || showCancelButton)"> + <MkButton inline @click="ok" primary>{{ $ts.ok }}</MkButton> + <MkButton inline @click="cancel" >{{ $ts.cancel }}</MkButton> + </div> + </div> + </div> + <div class="hdrwpsaf fullwidth"> + <header>{{ image.name }}</header> + <img :src="image.url" :alt="image.comment" :title="image.comment" @click="$refs.modal.close()"/> + <footer> + <span>{{ image.type }}</span> + <span>{{ bytes(image.size) }}</span> + <span v-if="image.properties && image.properties.width">{{ number(image.properties.width) }}px × {{ number(image.properties.height) }}px</span> + </footer> + </div> + </div> + </MkModal> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import MkModal from '@client/components/ui/modal.vue'; +import MkButton from '@client/components/ui/button.vue'; +import bytes from '@client/filters/bytes'; +import number from '@client/filters/number'; + +export default defineComponent({ + components: { + MkModal, + MkButton, + }, + + props: { + image: { + type: Object, + required: true, + }, + title: { + type: String, + required: false + }, + input: { + required: true + }, + showOkButton: { + type: Boolean, + default: true + }, + showCancelButton: { + type: Boolean, + default: true + }, + cancelableByBgClick: { + type: Boolean, + default: true + }, + }, + + emits: ['done', 'closed'], + + data() { + return { + inputValue: this.input.default ? this.input.default : null + }; + }, + + mounted() { + document.addEventListener('keydown', this.onKeydown); + }, + + beforeUnmount() { + document.removeEventListener('keydown', this.onKeydown); + }, + + methods: { + bytes, + number, + + done(canceled, result?) { + this.$emit('done', { canceled, result }); + this.$refs.modal.close(); + }, + + async ok() { + if (!this.showOkButton) return; + + const result = this.inputValue; + this.done(false, result); + }, + + cancel() { + this.done(true); + }, + + onBgClick() { + if (this.cancelableByBgClick) { + this.cancel(); + } + }, + + onKeydown(e) { + if (e.which === 27) { // ESC + this.cancel(); + } + }, + + onInputKeydown(e) { + if (e.which === 13) { // Enter + if (e.ctrlKey) { + e.preventDefault(); + e.stopPropagation(); + this.ok(); + } + } + } + } +}); +</script> + +<style lang="scss" scoped> +.container { + display: flex; + width: 100%; + height: 100%; + flex-direction: row; +} +@media (max-width: 850px) { + .container { + flex-direction: column; + } + .top-caption { + padding-bottom: 8px; + } +} +.fullwidth { + width: 100%; + margin: auto; +} +.mk-dialog { + position: relative; + padding: 32px; + min-width: 320px; + max-width: 480px; + box-sizing: border-box; + text-align: center; + background: var(--panel); + border-radius: var(--radius); + margin: auto; + + > header { + margin: 0 0 8px 0; + font-weight: bold; + font-size: 20px; + } + + > .buttons { + margin-top: 16px; + + > * { + margin: 0 8px; + } + } + + > textarea { + display: block; + box-sizing: border-box; + padding: 0 24px; + margin: 0; + width: 100%; + font-size: 16px; + border: none; + border-radius: 0; + background: transparent; + color: var(--fg); + font-family: inherit; + max-width: 100%; + min-width: 100%; + min-height: 90px; + + &:focus { + outline: none; + } + + &:disabled { + opacity: 0.5; + } + } +} +.hdrwpsaf { + display: flex; + flex-direction: column; + height: 100%; + + > header, + > footer { + align-self: center; + display: inline-block; + padding: 6px 9px; + font-size: 90%; + background: rgba(0, 0, 0, 0.5); + border-radius: 6px; + color: #fff; + } + + > header { + margin-bottom: 8px; + opacity: 0.9; + } + + > img { + display: block; + flex: 1; + min-height: 0; + object-fit: contain; + width: 100%; + cursor: zoom-out; + image-orientation: from-image; + } + + > footer { + margin-top: 8px; + opacity: 0.8; + + > span + span { + margin-left: 0.5em; + padding-left: 0.5em; + border-left: solid 1px rgba(255, 255, 255, 0.5); + } + } +} +</style> diff --git a/src/client/components/media-image.vue b/src/client/components/media-image.vue index 267e4debd2..863eb10272 100644 --- a/src/client/components/media-image.vue +++ b/src/client/components/media-image.vue @@ -1,6 +1,6 @@ <template> <div class="qjewsnkg" v-if="hide" @click="hide = false"> - <ImgWithBlurhash class="bg" :hash="image.blurhash" :title="image.name"/> + <ImgWithBlurhash class="bg" :hash="image.blurhash" :title="image.comment" :alt="image.comment"/> <div class="text"> <div> <b><i class="fas fa-exclamation-triangle"></i> {{ $ts.sensitive }}</b> @@ -14,7 +14,7 @@ :title="image.name" @click.prevent="onClick" > - <ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.name" :title="image.name" :cover="false"/> + <ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.comment" :title="image.comment" :cover="false"/> <div class="gif" v-if="image.type === 'image/gif'">GIF</div> </a> <i class="fas fa-eye-slash" @click="hide = true"></i> diff --git a/src/client/components/post-form-attaches.vue b/src/client/components/post-form-attaches.vue index f832ea87b5..27e20fdfa8 100644 --- a/src/client/components/post-form-attaches.vue +++ b/src/client/components/post-form-attaches.vue @@ -89,6 +89,27 @@ export default defineComponent({ file.name = result; }); }, + + async describe(file) { + os.popup(import("@client/components/media-caption.vue"), { + title: this.$ts.describeFile, + input: { + placeholder: this.$ts.inputNewDescription, + default: file.comment !== null ? file.comment : "", + }, + image: file + }, { + done: result => { + if (!result || result.canceled) return; + let comment = result.result; + os.api('drive/files/update', { + fileId: file.id, + comment: comment.length == 0 ? null : comment + }); + } + }, 'closed'); + }, + showFileMenu(file, ev: MouseEvent) { if (this.menu) return; this.menu = os.modalMenu([{ @@ -99,6 +120,10 @@ export default defineComponent({ text: file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive, icon: file.isSensitive ? 'fas fa-eye-slash' : 'fas fa-eye', action: () => { this.toggleSensitive(file) } + }, { + text: this.$ts.describeFile, + icon: 'fas fa-i-cursor', + action: () => { this.describe(file) } }, { text: this.$ts.attachCancel, icon: 'fas fa-times-circle', diff --git a/src/remote/activitypub/models/image.ts b/src/remote/activitypub/models/image.ts index 79fc2bf4a6..7bec1d6030 100644 --- a/src/remote/activitypub/models/image.ts +++ b/src/remote/activitypub/models/image.ts @@ -28,7 +28,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise<Drive const instance = await fetchMeta(); const cache = instance.cacheRemoteFiles; - let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache); + let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache, image.name); if (file.isLink) { // URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、 diff --git a/src/remote/activitypub/renderer/document.ts b/src/remote/activitypub/renderer/document.ts index 4f6ea8c4ee..f6e9dca45d 100644 --- a/src/remote/activitypub/renderer/document.ts +++ b/src/remote/activitypub/renderer/document.ts @@ -4,5 +4,6 @@ import { DriveFiles } from '../../../models'; export default (file: DriveFile) => ({ type: 'Document', mediaType: file.type, - url: DriveFiles.getPublicUrl(file) + url: DriveFiles.getPublicUrl(file), + name: file.comment, }); diff --git a/src/remote/activitypub/renderer/image.ts b/src/remote/activitypub/renderer/image.ts index ce98f98c62..cbd4fbbe68 100644 --- a/src/remote/activitypub/renderer/image.ts +++ b/src/remote/activitypub/renderer/image.ts @@ -4,5 +4,6 @@ import { DriveFiles } from '../../../models'; export default (file: DriveFile) => ({ type: 'Image', url: DriveFiles.getPublicUrl(file), - sensitive: file.isSensitive + sensitive: file.isSensitive, + name: file.comment }); diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts index 6eda83967b..f740fea67e 100644 --- a/src/server/api/endpoints/drive/files/update.ts +++ b/src/server/api/endpoints/drive/files/update.ts @@ -49,6 +49,14 @@ export const meta = { 'ja-JP': 'このメディアが「閲覧注意」(NSFW)かどうか', 'en-US': 'Whether this media is NSFW' } + }, + + comment: { + validator: $.optional.nullable.str, + default: undefined as any, + desc: { + 'ja-JP': 'コメント' + } } }, @@ -92,6 +100,8 @@ export default define(meta, async (ps, user) => { if (ps.name) file.name = ps.name; + if (ps.comment !== undefined) file.comment = ps.comment; + if (ps.isSensitive !== undefined) file.isSensitive = ps.isSensitive; if (ps.folderId !== undefined) { @@ -113,6 +123,7 @@ export default define(meta, async (ps, user) => { await DriveFiles.update(file.id, { name: file.name, + comment: file.comment, folderId: file.folderId, isSensitive: file.isSensitive }); diff --git a/src/services/drive/upload-from-url.ts b/src/services/drive/upload-from-url.ts index 2f4c5aeeaf..2f660d9035 100644 --- a/src/services/drive/upload-from-url.ts +++ b/src/services/drive/upload-from-url.ts @@ -25,6 +25,12 @@ export default async ( name = null; } + // If the comment is same as the name, skip comment + // (image.name is passed in when receiving attachment) + if (comment !== null && name == comment) { + comment = null; + } + // Create temp file const [path, cleanup] = await createTemp(); From 857c88e8d63599f854f55510329083aab9021176 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 28 May 2021 22:52:30 +0900 Subject: [PATCH 12/28] fix type --- src/server/api/endpoints/i/notifications.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index b481fdba8f..c597a28ebb 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -103,9 +103,9 @@ export default define(meta, async (ps, user) => { query.setParameters(followingQuery.getParameters()); } - if (ps.includeTypes?.length > 0) { + if (ps.includeTypes && ps.includeTypes.length > 0) { query.andWhere(`notification.type IN (:...includeTypes)`, { includeTypes: ps.includeTypes }); - } else if (ps.excludeTypes?.length > 0) { + } else if (ps.excludeTypes && ps.excludeTypes.length > 0) { query.andWhere(`notification.type NOT IN (:...excludeTypes)`, { excludeTypes: ps.excludeTypes }); } From 30d2a1d8b654428e949c2584c3c29f41f00f6d2b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 28 May 2021 22:52:38 +0900 Subject: [PATCH 13/28] fix bug --- src/client/init.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/init.ts b/src/client/init.ts index 19b95fc50e..12bb0d58c1 100644 --- a/src/client/init.ts +++ b/src/client/init.ts @@ -294,7 +294,7 @@ if ($i) { } } - const main = stream.useChannel('main', 'System'); + const main = stream.useChannel('main', null, 'System'); // 自分の情報が更新されたとき main.on('meUpdated', i => { From 0eb115bf7e4324e5aabbe64785c2e45bc00ceb2c Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 28 May 2021 22:53:00 +0900 Subject: [PATCH 14/28] Resolve #7538 --- src/server/api/endpoints/i/notifications.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index c597a28ebb..a10dc09df3 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -5,6 +5,7 @@ import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notifications, Followings, Mutings, Users } from '../../../../models'; import { notificationTypes } from '../../../../types'; +import read from '@/services/note/read'; export const meta = { desc: { @@ -116,5 +117,11 @@ export default define(meta, async (ps, user) => { readNotification(user.id, notifications.map(x => x.id)); } + const notes = notifications.filter(notification => ['mention', 'reply', 'quote'].includes(notification.type)).map(notification => notification.note!); + + if (notes.length > 0) { + read(user.id, notes); + } + return await Notifications.packMany(notifications, user.id); }); From d76345264ded05b2f8a1e7b75922914fd5aeb159 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 28 May 2021 22:57:04 +0900 Subject: [PATCH 15/28] fix bug --- src/client/ui/_common_/common.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/ui/_common_/common.vue b/src/client/ui/_common_/common.vue index 1e825e0fe0..8da19a0984 100644 --- a/src/client/ui/_common_/common.vue +++ b/src/client/ui/_common_/common.vue @@ -43,7 +43,7 @@ export default defineComponent({ }; if ($i) { - const connection = stream.useChannel('main', 'UI'); + const connection = stream.useChannel('main', null, 'UI'); connection.on('notification', onNotification); } From 24e7eeca0f912a11d4ec2664731b0f929ac26952 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 30 May 2021 10:05:12 +0900 Subject: [PATCH 16/28] fix local emoji detection https://github.com/misskey-dev/misskey/pull/7526#discussion_r641886612 --- src/misc/populate-emojis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc/populate-emojis.ts b/src/misc/populate-emojis.ts index a3f67ccb98..42aaaf0705 100644 --- a/src/misc/populate-emojis.ts +++ b/src/misc/populate-emojis.ts @@ -61,7 +61,7 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu if (emoji == null) return null; - const isLocal = emojiName.endsWith('@.'); + const isLocal = emoji.host == null; const url = isLocal ? emoji.url : `${config.url}/proxy/image.png?${query({url: emoji.url})}`; return { From 52e54cf0dc87fb9a4bf9ad0abbd8726ff92671d9 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 30 May 2021 10:05:25 +0900 Subject: [PATCH 17/28] refactor --- src/client/components/date-separated-list.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/components/date-separated-list.vue b/src/client/components/date-separated-list.vue index 34085cc070..6a0c7f29f2 100644 --- a/src/client/components/date-separated-list.vue +++ b/src/client/components/date-separated-list.vue @@ -1,11 +1,11 @@ <script lang="ts"> -import { defineComponent, h, TransitionGroup } from 'vue'; +import { defineComponent, h, PropType, TransitionGroup } from 'vue'; import MkAd from '@client/components/global/ad.vue'; export default defineComponent({ props: { items: { - type: Array, + type: Array as PropType<{ id: string; createdAt: string; _shouldInsertAd_: boolean; }[]>, required: true, }, direction: { From 0263539202d16eb6a03484e0ead6d184be90de89 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sun, 30 May 2021 13:48:23 +0900 Subject: [PATCH 18/28] Fix remote drive expire (#7543) --- src/models/repositories/drive-file.ts | 4 ++++ src/services/drive/add-file.ts | 3 ++- src/services/drive/delete-file.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts index 590079fe4a..92bf12a4e0 100644 --- a/src/models/repositories/drive-file.ts +++ b/src/models/repositories/drive-file.ts @@ -59,6 +59,7 @@ export class DriveFileRepository extends Repository<DriveFile> { const { sum } = await this .createQueryBuilder('file') .where('file.userId = :id', { id: id }) + .andWhere('file.isLink = FALSE') .select('SUM(file.size)', 'sum') .getRawOne(); @@ -69,6 +70,7 @@ export class DriveFileRepository extends Repository<DriveFile> { const { sum } = await this .createQueryBuilder('file') .where('file.userHost = :host', { host: toPuny(host) }) + .andWhere('file.isLink = FALSE') .select('SUM(file.size)', 'sum') .getRawOne(); @@ -79,6 +81,7 @@ export class DriveFileRepository extends Repository<DriveFile> { const { sum } = await this .createQueryBuilder('file') .where('file.userHost IS NULL') + .andWhere('file.isLink = FALSE') .select('SUM(file.size)', 'sum') .getRawOne(); @@ -89,6 +92,7 @@ export class DriveFileRepository extends Repository<DriveFile> { const { sum } = await this .createQueryBuilder('file') .where('file.userHost IS NOT NULL') + .andWhere('file.isLink = FALSE') .select('SUM(file.size)', 'sum') .getRawOne(); diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index ad9f753e79..2356a23cbe 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -267,7 +267,8 @@ async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, async function deleteOldFile(user: IRemoteUser) { const q = DriveFiles.createQueryBuilder('file') - .where('file.userId = :userId', { userId: user.id }); + .where('file.userId = :userId', { userId: user.id }) + .andWhere('file.isLink = FALSE'); if (user.avatarId) { q.andWhere('file.id != :avatarId', { avatarId: user.avatarId }); diff --git a/src/services/drive/delete-file.ts b/src/services/drive/delete-file.ts index bb9c0ae2c3..2dd2445321 100644 --- a/src/services/drive/delete-file.ts +++ b/src/services/drive/delete-file.ts @@ -79,7 +79,7 @@ async function postProcess(file: DriveFile, isExpired = false) { url: file.uri, thumbnailUrl: null, webpublicUrl: null, - size: 0, + storedInternal: false, // ローカルプロキシ用 accessKey: uuid(), thumbnailAccessKey: 'thumbnail-' + uuid(), From 948b70cb0f5769cabc758c3c9c496af865b0cfef Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 30 May 2021 16:26:52 +0900 Subject: [PATCH 19/28] fix typo Resolve #7540 --- locales/ja-JP.yml | 4 ++-- src/client/pages/settings/integration.vue | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 23f3bf7296..13f7e331ad 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -311,8 +311,8 @@ monthX: "{month}月" yearX: "{year}年" pages: "ページ" integration: "連携" -connectSerice: "接続する" -disconnectSerice: "切断する" +connectService: "接続する" +disconnectService: "切断する" enableLocalTimeline: "ローカルタイムラインを有効にする" enableGlobalTimeline: "グローバルタイムラインを有効にする" disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。" diff --git a/src/client/pages/settings/integration.vue b/src/client/pages/settings/integration.vue index 2d2be04051..f1c0a88afc 100644 --- a/src/client/pages/settings/integration.vue +++ b/src/client/pages/settings/integration.vue @@ -4,8 +4,8 @@ <div class="_formLabel"><i class="fab fa-twitter"></i> Twitter</div> <div class="_formPanel" style="padding: 16px;"> <p v-if="integrations.twitter">{{ $ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p> - <MkButton v-if="integrations.twitter" @click="disconnectTwitter" danger>{{ $ts.disconnectSerice }}</MkButton> - <MkButton v-else @click="connectTwitter" primary>{{ $ts.connectSerice }}</MkButton> + <MkButton v-if="integrations.twitter" @click="disconnectTwitter" danger>{{ $ts.disconnectService }}</MkButton> + <MkButton v-else @click="connectTwitter" primary>{{ $ts.connectService }}</MkButton> </div> </div> @@ -13,8 +13,8 @@ <div class="_formLabel"><i class="fab fa-discord"></i> Discord</div> <div class="_formPanel" style="padding: 16px;"> <p v-if="integrations.discord">{{ $ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p> - <MkButton v-if="integrations.discord" @click="disconnectDiscord" danger>{{ $ts.disconnectSerice }}</MkButton> - <MkButton v-else @click="connectDiscord" primary>{{ $ts.connectSerice }}</MkButton> + <MkButton v-if="integrations.discord" @click="disconnectDiscord" danger>{{ $ts.disconnectService }}</MkButton> + <MkButton v-else @click="connectDiscord" primary>{{ $ts.connectService }}</MkButton> </div> </div> @@ -22,8 +22,8 @@ <div class="_formLabel"><i class="fab fa-github"></i> GitHub</div> <div class="_formPanel" style="padding: 16px;"> <p v-if="integrations.github">{{ $ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p> - <MkButton v-if="integrations.github" @click="disconnectGithub" danger>{{ $ts.disconnectSerice }}</MkButton> - <MkButton v-else @click="connectGithub" primary>{{ $ts.connectSerice }}</MkButton> + <MkButton v-if="integrations.github" @click="disconnectGithub" danger>{{ $ts.disconnectService }}</MkButton> + <MkButton v-else @click="connectGithub" primary>{{ $ts.connectService }}</MkButton> </div> </div> </FormBase> From 1077c2a63709994b27fba4760463251941501e8b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 30 May 2021 16:42:22 +0900 Subject: [PATCH 20/28] :art: --- src/client/components/notifications.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue index 6caf8eb8e3..092c00f14e 100644 --- a/src/client/components/notifications.vue +++ b/src/client/components/notifications.vue @@ -12,10 +12,10 @@ <XNotification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/> </XList> - <button class="_buttonPrimary" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }"> + <MkButton primary style="margin: var(--margin) auto;" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }"> <template v-if="!moreFetching">{{ $ts.loadMore }}</template> <template v-if="moreFetching"><MkLoading inline/></template> - </button> + </MkButton> </div> </transition> </template> @@ -28,12 +28,14 @@ import XList from './date-separated-list.vue'; import XNote from './note.vue'; import { notificationTypes } from '../../types'; import * as os from '@client/os'; +import MkButton from '@client/components/ui/button.vue'; export default defineComponent({ components: { XNotification, XList, XNote, + MkButton, }, mixins: [ From 798aedfc0f15419c1f37298ce2242d240e0e11fe Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sun, 30 May 2021 20:52:39 +0900 Subject: [PATCH 21/28] Supports Emoji 13.1 (#7539) * Supports Emoji 13.1 * Fix regex --- COPYING | 4 ---- package.json | 2 ++ src/emojilist.json | 5 +++++ src/misc/emoji-regex.ts | 5 ++--- yarn.lock | 8 ++++---- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/COPYING b/COPYING index 5abc3e9895..13c13bf938 100644 --- a/COPYING +++ b/COPYING @@ -6,10 +6,6 @@ And is distributed under The GNU Affero General Public License Version 3, you sh Misskey includes several third-party Open-Source softwares. -Unicode emoji regular expressions by Twitter, Inc. -License: MIT -https://github.com/twitter/twemoji-parser/blob/master/LICENSE.md - Emoji keywords for Unicode 11 and below by Mu-An Chiou License: MIT https://github.com/muan/emojilib/blob/master/LICENSE diff --git a/package.json b/package.json index 610678989f..365fb865b9 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "format": "gulp format" }, "resolutions": { + "mfm-js/twemoji-parser": "13.1.x", "chokidar": "^3.3.1", "constantinople": "^4.0.1", "jsonld/rdf-canonize/node-forge": "0.10.0", @@ -233,6 +234,7 @@ "tsconfig-paths": "3.9.0", "tslint": "6.1.3", "tslint-sonarts": "1.9.0", + "twemoji-parser": "13.1.0", "typeorm": "0.2.32", "typescript": "4.2.4", "ulid": "2.3.0", diff --git a/src/emojilist.json b/src/emojilist.json index 30cf6dd735..75c424ab4b 100644 --- a/src/emojilist.json +++ b/src/emojilist.json @@ -93,6 +93,9 @@ { "category": "face", "char": "🥱", "name": "yawning", "keywords": ["face", "tired", "yawning"] }, { "category": "face", "char": "😴", "name": "sleeping", "keywords": ["face", "tired", "sleepy", "night", "zzz"] }, { "category": "face", "char": "💤", "name": "zzz", "keywords": ["sleepy", "tired", "dream"] }, + { "category": "face", "char": "\uD83D\uDE36\u200D\uD83C\uDF2B\uFE0F", "name": "face_in_clouds", "keywords": [] }, + { "category": "face", "char": "\uD83D\uDE2E\u200D\uD83D\uDCA8", "name": "face_exhaling", "keywords": [] }, + { "category": "face", "char": "\uD83D\uDE35\u200D\uD83D\uDCAB", "name": "face_with_spiral_eyes", "keywords": [] }, { "category": "face", "char": "💩", "name": "poop", "keywords": ["hankey", "shitface", "fail", "turd", "shit"] }, { "category": "face", "char": "😈", "name": "smiling_imp", "keywords": ["devil", "horns"] }, { "category": "face", "char": "👿", "name": "imp", "keywords": ["devil", "angry", "horns"] }, @@ -1219,6 +1222,8 @@ { "category": "symbols", "char": "💘", "name": "cupid", "keywords": ["love", "like", "heart", "affection", "valentines"] }, { "category": "symbols", "char": "💝", "name": "gift_heart", "keywords": ["love", "valentines"] }, { "category": "symbols", "char": "💟", "name": "heart_decoration", "keywords": ["purple-square", "love", "like"] }, + { "category": "symbols", "char": "\u2764\uFE0F\u200D\uD83D\uDD25", "name": "heart_on_fire", "keywords": [] }, + { "category": "symbols", "char": "\u2764\uFE0F\u200D\uD83E\uDE79", "name": "mending_heart", "keywords": [] }, { "category": "symbols", "char": "☮", "name": "peace_symbol", "keywords": ["hippie"] }, { "category": "symbols", "char": "✝", "name": "latin_cross", "keywords": ["christianity"] }, { "category": "symbols", "char": "☪", "name": "star_and_crescent", "keywords": ["islam"] }, diff --git a/src/misc/emoji-regex.ts b/src/misc/emoji-regex.ts index 9f9c360ff1..8b07fbd8f2 100644 --- a/src/misc/emoji-regex.ts +++ b/src/misc/emoji-regex.ts @@ -1,4 +1,3 @@ -// https://github.com/twitter/twemoji-parser/blob/master/src/lib/regex.js d1ea6ccfb72735698f4994e7015accc447eeac8e -export const emojiRegex = /((?:\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d])|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf7c\udf84\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc70\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f|(?:\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc3b\u200d\u2744\ufe0f|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f|\ud83d\udc08\u200d\u2b1b)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c[\udf85\udfc2-\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4-\udeb6\udec0\udecc]|\ud83e[\udd0c\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\udd77\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd-\uddcf\uddd1-\udddd]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf5\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5-\uded7\udeeb\udeec\udef4-\udefc\udfe0-\udfeb]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd1d\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd76\udd78\udd7a-\uddb4\uddb7\uddba\uddbc-\uddcb\uddd0\uddde-\uddff\ude70-\ude74\ude78-\ude7a\ude80-\ude86\ude90-\udea8\udeb0-\udeb6\udec0-\udec2\uded0-\uded6]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f)/; +const twemojiRegex = require('twemoji-parser/dist/lib/regex').default; -export const emojiRegexWithCustom = new RegExp(`(${emojiRegex.source}|:[0-9A-Za-z_]+:)`, 'g'); +export const emojiRegex = new RegExp(`(${twemojiRegex.source})`); diff --git a/yarn.lock b/yarn.lock index 9296aafc4e..b883eac0d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10834,10 +10834,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -twemoji-parser@13.0.x: - version "13.0.0" - resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-13.0.0.tgz#bd9d1b98474f1651dc174696b45cabefdfa405af" - integrity sha512-zMaGdskpH8yKjT2RSE/HwE340R4Fm+fbie4AaqjDa4H/l07YUmAvxkSfNl6awVWNRRQ0zdzLQ8SAJZuY5MgstQ== +twemoji-parser@13.0.x, twemoji-parser@13.1.0, twemoji-parser@13.1.x: + version "13.1.0" + resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-13.1.0.tgz#65e7e449c59258791b22ac0b37077349127e3ea4" + integrity sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" From f1c4e4a10e26c3b4e136e0fd2cb77a243507be00 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 31 May 2021 10:35:12 +0900 Subject: [PATCH 22/28] fix style --- src/client/ui/default.sidebar.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client/ui/default.sidebar.vue b/src/client/ui/default.sidebar.vue index 725fd844d9..c7e2d30c7a 100644 --- a/src/client/ui/default.sidebar.vue +++ b/src/client/ui/default.sidebar.vue @@ -241,7 +241,6 @@ export default defineComponent({ > .text { display: none; } - } } @@ -309,7 +308,7 @@ export default defineComponent({ > .indicator { position: absolute; top: 0; - left: 20px; + left: 0; color: var(--navIndicator); font-size: 8px; animation: blink 1s infinite; From 79cb8364837326bd56d43504599bd5df07dc1758 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 31 May 2021 11:05:34 +0900 Subject: [PATCH 23/28] Update deps --- gulpfile.ts | 2 +- package.json | 58 +++--- yarn.lock | 559 +++++++++++++++++++++++++++++---------------------- 3 files changed, 349 insertions(+), 270 deletions(-) diff --git a/gulpfile.ts b/gulpfile.ts index 660dfb013e..2cdcbdb6fc 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import * as gulp from 'gulp'; -import * as rimraf from 'rimraf'; +import rimraf from 'rimraf'; const replace = require('gulp-replace'); const terser = require('gulp-terser'); const cssnano = require('gulp-cssnano'); diff --git a/package.json b/package.json index 365fb865b9..ca33644052 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@koa/router": "9.0.1", "@sentry/browser": "5.29.2", "@sentry/tracing": "5.29.2", - "@sinonjs/fake-timers": "7.0.5", + "@sinonjs/fake-timers": "7.1.2", "@syuilo/aiscript": "0.11.1", "@types/bcryptjs": "2.4.2", "@types/bull": "3.15.1", @@ -59,23 +59,23 @@ "@types/jsdom": "16.2.10", "@types/jsonld": "1.5.5", "@types/katex": "0.11.0", - "@types/koa": "2.13.1", + "@types/koa": "2.13.3", "@types/koa-bodyparser": "4.3.0", "@types/koa-cors": "0.0.0", "@types/koa-favicon": "2.0.19", "@types/koa-logger": "3.1.1", "@types/koa-mount": "4.0.0", "@types/koa-send": "4.1.2", - "@types/koa-views": "2.0.4", + "@types/koa-views": "7.0.0", "@types/koa__cors": "3.0.2", "@types/koa__multer": "2.0.2", "@types/koa__router": "8.0.4", "@types/markdown-it": "12.0.1", "@types/matter-js": "0.14.12", "@types/mocha": "8.2.2", - "@types/node": "15.3.1", + "@types/node": "15.6.1", "@types/node-fetch": "2.5.10", - "@types/nodemailer": "6.4.1", + "@types/nodemailer": "6.4.2", "@types/nprogress": "0.2.0", "@types/oauth": "0.9.1", "@types/parse5": "6.0.0", @@ -86,12 +86,12 @@ "@types/qrcode": "1.4.0", "@types/random-seed": "0.3.3", "@types/ratelimiter": "3.4.1", - "@types/redis": "2.8.28", + "@types/redis": "2.8.29", "@types/rename": "1.0.3", "@types/request-stats": "3.0.0", "@types/rimraf": "3.0.0", "@types/seedrandom": "2.4.28", - "@types/sharp": "0.28.1", + "@types/sharp": "0.28.2", "@types/sinonjs__fake-timers": "6.0.2", "@types/speakeasy": "2.0.5", "@types/throttle-debounce": "2.1.0", @@ -103,14 +103,14 @@ "@types/webpack-stream": "3.2.12", "@types/websocket": "1.0.2", "@types/ws": "7.4.4", - "@typescript-eslint/parser": "4.24.0", + "@typescript-eslint/parser": "4.25.0", "@vue/compiler-sfc": "3.0.11", "abort-controller": "3.0.0", "apexcharts": "3.26.3", "autobind-decorator": "2.4.0", "autosize": "4.0.4", "autwh": "0.1.0", - "aws-sdk": "2.910.0", + "aws-sdk": "2.918.0", "bcryptjs": "2.4.3", "blurhash": "1.1.3", "broadcast-channel": "3.6.0", @@ -121,20 +121,20 @@ "chart.js": "2.9.4", "cli-highlight": "2.1.11", "commander": "7.2.0", - "concurrently": "6.1.0", + "concurrently": "6.2.0", "content-disposition": "0.5.3", - "core-js": "3.12.1", + "core-js": "3.13.1", "crc-32": "1.2.0", - "css-loader": "5.2.4", - "cssnano": "5.0.3", + "css-loader": "5.2.6", + "cssnano": "5.0.5", "dateformat": "4.5.1", "diskusage": "1.1.3", "escape-regexp": "0.0.1", - "eslint": "7.26.0", - "eslint-plugin-vue": "7.9.0", + "eslint": "7.27.0", + "eslint-plugin-vue": "7.10.0", "eventemitter3": "4.0.7", "feed": "4.2.2", - "file-type": "16.4.0", + "file-type": "16.5.0", "fluent-ffmpeg": "2.1.2", "glob": "7.1.7", "got": "11.8.2", @@ -149,12 +149,12 @@ "http-proxy-agent": "4.0.1", "http-signature": "1.3.5", "https-proxy-agent": "5.0.0", - "idb-keyval": "5.0.5", + "idb-keyval": "5.0.6", "insert-text-at-cursor": "0.3.0", "is-root": "2.1.0", "is-svg": "4.3.1", "js-yaml": "4.1.0", - "jsdom": "16.5.3", + "jsdom": "16.6.0", "json5": "2.2.0", "json5-loader": "4.0.1", "jsonld": "4.0.1", @@ -182,16 +182,16 @@ "multer": "1.4.2", "nested-property": "4.0.0", "node-fetch": "2.6.1", - "nodemailer": "6.6.0", + "nodemailer": "6.6.1", "object-assign-deep": "0.4.0", "os-utils": "0.0.14", "parse5": "6.0.1", "pg": "8.6.0", "portscanner": "2.2.0", - "postcss": "8.2.15", + "postcss": "8.3.0", "postcss-loader": "5.3.0", "prismjs": "1.23.0", - "probe-image-size": "7.1.0", + "probe-image-size": "7.1.1", "promise-limit": "2.7.0", "promise-sequential": "1.1.1", "pug": "3.0.2", @@ -212,31 +212,31 @@ "rimraf": "3.0.2", "rndstr": "1.0.0", "s-age": "1.1.2", - "sass": "1.32.13", + "sass": "1.34.0", "sass-loader": "11.1.1", "seedrandom": "3.0.5", - "sharp": "0.28.2", + "sharp": "0.28.3", "speakeasy": "2.0.0", "stringz": "2.1.0", "style-loader": "2.0.0", "summaly": "2.4.0", "syslog-pro": "1.0.0", - "systeminformation": "5.6.22", + "systeminformation": "5.7.4", "syuilo-password-strength": "0.0.1", "textarea-caret": "3.1.0", "three": "0.117.1", "throttle-debounce": "3.0.1", "tinycolor2": "1.4.2", "tmp": "0.2.1", - "ts-loader": "9.2.1", - "ts-node": "9.1.1", + "ts-loader": "9.2.2", + "ts-node": "10.0.0", "tsc-alias": "1.2.11", "tsconfig-paths": "3.9.0", "tslint": "6.1.3", "tslint-sonarts": "1.9.0", "twemoji-parser": "13.1.0", "typeorm": "0.2.32", - "typescript": "4.2.4", + "typescript": "4.3.2", "ulid": "2.3.0", "uuid": "8.3.2", "v-debounce": "0.1.2", @@ -251,10 +251,10 @@ "vue-svg-loader": "0.17.0-beta.2", "vuedraggable": "4.0.1", "web-push": "3.4.4", - "webpack": "5.37.1", + "webpack": "5.38.1", "webpack-cli": "4.7.0", "websocket": "1.0.34", - "ws": "7.4.5", + "ws": "7.4.6", "xev": "2.0.1" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index b883eac0d3..2d9284c4cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -395,10 +395,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@7.0.5": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.0.5.tgz#558a7f8145a01366c44b3dcbdd7172c05c461564" - integrity sha512-fUt6b15bjV/VW93UP5opNXJxdwZSbK1EdiwnhN7XrQrcpaOhMJpZ/CjwFpM3THpxwA+YviBUJKSuEqKlCK5alw== +"@sinonjs/fake-timers@7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== dependencies: "@sinonjs/commons" "^1.7.0" @@ -440,6 +440,26 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.1.1.tgz#3348564048e7a2d7398c935d466c0414ebb6a669" integrity sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow== +"@tsconfig/node10@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.7.tgz#1eb1de36c73478a2479cc661ef5af1c16d86d606" + integrity sha512-aBvUmXLQbayM4w3A8TrjwrXs4DZ8iduJnuJLLRGdkWlyakCf1q6uHZJBzXoRA/huAEknG5tcUyQxN3A+In5euQ== + +"@tsconfig/node12@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.7.tgz#677bd9117e8164dc319987dd6ff5fc1ba6fbf18b" + integrity sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A== + +"@tsconfig/node14@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.0.tgz#5bd046e508b1ee90bc091766758838741fdefd6e" + integrity sha512-RKkL8eTdPv6t5EHgFKIVQgsDapugbuOptNd9OOunN/HAkzmmTnZELx1kNCK0rSdUYGmiFMM3rRQMAWiyp023LQ== + +"@tsconfig/node16@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" + integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== + "@types/accepts@*": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" @@ -783,14 +803,14 @@ dependencies: "@types/koa" "*" -"@types/koa-views@2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/koa-views/-/koa-views-2.0.4.tgz#3fa87ba13174f9816c6f7701fbf129dfe9855e39" - integrity sha512-aGFBVLiPC7FkXTqHLhnmjKhx3COV+GeJHO9OkLX/p/iAQTgDB5bbnsddx3XgrS6aACWyxR3BpQJVDdSqCNY1lw== +"@types/koa-views@7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@types/koa-views/-/koa-views-7.0.0.tgz#5613450c77ab69c980c47104378da4b7669c5f2e" + integrity sha512-AB/NB+oFHcLOZJYFv3bG5Af8YbwYCD9/zK0WcKALsbjI/FRKrcXTUTC64RebDrkyOkBm3bpCgpGndhAH/3YQ2Q== dependencies: - "@types/koa" "*" + koa-views "*" -"@types/koa@*", "@types/koa@2.13.1", "@types/koa@^2.13.1": +"@types/koa@*", "@types/koa@^2.13.1": version "2.13.1" resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.1.tgz#e29877a6b5ad3744ab1024f6ec75b8cbf6ec45db" integrity sha512-Qbno7FWom9nNqu0yHZ6A0+RWt4mrYBhw3wpBAQ3+IuzGcLlfeYkzZrnMq5wsxulN2np8M4KKeUpTodsOsSad5Q== @@ -804,6 +824,20 @@ "@types/koa-compose" "*" "@types/node" "*" +"@types/koa@2.13.3": + version "2.13.3" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.3.tgz#5b44c0956d7f7bf41f74ccfb530fec60fbed45ca" + integrity sha512-TaujBV+Dhe/FvmSMZJtCFBms+bqQacgUebk/M2C2tq8iGmHE/DDf4DcW2Hc7NqusVZmy5xzrWOjtdPKNP+fTfw== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + "@types/koa__cors@3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-3.0.2.tgz#578917ffca964e98f5e9849996ae1eeda7c15704" @@ -877,10 +911,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== -"@types/node@15.3.1": - version "15.3.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.1.tgz#23a06b87eedb524016616e886b116b8fdcb180af" - integrity sha512-weaeiP4UF4XgF++3rpQhpIJWsCTS4QJw5gvBhQu6cFIxTwyxWIe3xbnrY/o2lTCQ0lsdb8YIUDUvLR4Vuz5rbw== +"@types/node@15.6.1": + version "15.6.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.1.tgz#32d43390d5c62c5b6ec486a9bc9c59544de39a08" + integrity sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA== "@types/node@^14.11.8": version "14.14.44" @@ -892,10 +926,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.0.tgz#3ba770047723b3eeb8dc9fca02cce8a7fb6378da" integrity sha512-w8VZUN/f7SSbvVReb9SWp6cJFevxb4/nkG65yLAya//98WgocKm5PLDAtSs5CtJJJM+kHmJjO/6mmYW4MHShZA== -"@types/nodemailer@6.4.1": - version "6.4.1" - resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.1.tgz#31f96f4410632f781d3613bd1f4293649e423f75" - integrity sha512-8081UY/0XTTDpuGqCnDc8IY+Q3DSg604wB3dBH0CaZlj4nZWHWuxtZ3NRZ9c9WUrz1Vfm6wioAUnqL3bsh49uQ== +"@types/nodemailer@6.4.2": + version "6.4.2" + resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.2.tgz#d8ee254c969e6ad83fb9a0a0df3a817406a3fa3b" + integrity sha512-yhsqg5Xbr8aWdwjFS3QjkniW5/tLpWXtOYQcJdo9qE3DolBxsKzgRCQrteaMY0hos8MklJNSEsMqDpZynGzMNg== dependencies: "@types/node" "*" @@ -991,10 +1025,10 @@ "@types/node" "*" safe-buffer "*" -"@types/redis@2.8.28": - version "2.8.28" - resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.28.tgz#5862b2b64aa7f7cbc76dafd7e6f06992b52c55e3" - integrity sha512-8l2gr2OQ969ypa7hFOeKqtFoY70XkHxISV0pAwmQ2nm6CSPb1brmTmqJCGGrekCo+pAZyWlNXr+Kvo6L/1wijA== +"@types/redis@2.8.29": + version "2.8.29" + resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.29.tgz#f272dba985156e599e2523afb4f5f7e7586b56b5" + integrity sha512-/pjQ9lwnL/t1bEfRHQFEJB3kHCR65tpB19NEWmbqcgGgqrfeGo/9b4tUtHbClxQoy3+g6Esx2QRtV7fk7kBPYg== dependencies: "@types/node" "*" @@ -1038,10 +1072,10 @@ "@types/express-serve-static-core" "*" "@types/mime" "*" -"@types/sharp@0.28.1": - version "0.28.1" - resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.28.1.tgz#dbd702bd6a8d42576b8faffe1c2ec06fdffaaaff" - integrity sha512-Jn9NHCjT3KXjxTpbtaxBI4iL+st5o9pWhMGVpHF+Cs+t/31lohLNXSW9PT9RSl7Ct7yBT2XFu7R/xfo0Y8HH2Q== +"@types/sharp@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.28.2.tgz#75bb9f60714ed752dc88c477ef55310f17c2f08f" + integrity sha512-xXanJalZA3EVSZ49667lBctyao75JiH5tD5/hKYu9u5C3d9rxEqOHvMOtx/oenJUqKKgIwIZEsL0hjmKo2rjJg== dependencies: "@types/node" "*" @@ -1192,48 +1226,48 @@ resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== -"@typescript-eslint/parser@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.24.0.tgz#2e5f1cc78ffefe43bfac7e5659309a92b09a51bd" - integrity sha512-dj1ZIh/4QKeECLb2f/QjRwMmDArcwc2WorWPRlB8UNTZlY1KpTVsbX7e3ZZdphfRw29aTFUSNuGB8w9X5sS97w== +"@typescript-eslint/parser@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.25.0.tgz#6b2cb6285aa3d55bfb263c650739091b0f19aceb" + integrity sha512-OZFa1SKyEJpAhDx8FcbWyX+vLwh7OEtzoo2iQaeWwxucyfbi0mT4DijbOSsTgPKzGHr6GrF2V5p/CEpUH/VBxg== dependencies: - "@typescript-eslint/scope-manager" "4.24.0" - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/typescript-estree" "4.24.0" + "@typescript-eslint/scope-manager" "4.25.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/typescript-estree" "4.25.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.24.0.tgz#38088216f0eaf235fa30ed8cabf6948ec734f359" - integrity sha512-9+WYJGDnuC9VtYLqBhcSuM7du75fyCS/ypC8c5g7Sdw7pGL4NDTbeH38eJPfzIydCHZDoOgjloxSAA3+4l/zsA== +"@typescript-eslint/scope-manager@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.25.0.tgz#9d86a5bcc46ef40acd03d85ad4e908e5aab8d4ca" + integrity sha512-2NElKxMb/0rya+NJG1U71BuNnp1TBd1JgzYsldsdA83h/20Tvnf/HrwhiSlNmuq6Vqa0EzidsvkTArwoq+tH6w== dependencies: - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/visitor-keys" "4.24.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/visitor-keys" "4.25.0" -"@typescript-eslint/types@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.24.0.tgz#6d0cca2048cbda4e265e0c4db9c2a62aaad8228c" - integrity sha512-tkZUBgDQKdvfs8L47LaqxojKDE+mIUmOzdz7r+u+U54l3GDkTpEbQ1Jp3cNqqAU9vMUCBA1fitsIhm7yN0vx9Q== +"@typescript-eslint/types@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.25.0.tgz#0e444a5c5e3c22d7ffa5e16e0e60510b3de5af87" + integrity sha512-+CNINNvl00OkW6wEsi32wU5MhHti2J25TJsJJqgQmJu3B3dYDBcmOxcE5w9cgoM13TrdE/5ND2HoEnBohasxRQ== -"@typescript-eslint/typescript-estree@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.24.0.tgz#b49249679a98014d8b03e8d4b70864b950e3c90f" - integrity sha512-kBDitL/by/HK7g8CYLT7aKpAwlR8doshfWz8d71j97n5kUa5caHWvY0RvEUEanL/EqBJoANev8Xc/mQ6LLwXGA== +"@typescript-eslint/typescript-estree@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.25.0.tgz#942e4e25888736bff5b360d9b0b61e013d0cfa25" + integrity sha512-1B8U07TGNAFMxZbSpF6jqiDs1cVGO0izVkf18Q/SPcUAc9LhHxzvSowXDTvkHMWUVuPpagupaW63gB6ahTXVlg== dependencies: - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/visitor-keys" "4.24.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/visitor-keys" "4.25.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.24.0.tgz#a8fafdc76cad4e04a681a945fbbac4e35e98e297" - integrity sha512-4ox1sjmGHIxjEDBnMCtWFFhErXtKA1Ec0sBpuz0fqf3P+g3JFGyTxxbF06byw0FRsPnnbq44cKivH7Ks1/0s6g== +"@typescript-eslint/visitor-keys@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.25.0.tgz#863e7ed23da4287c5b469b13223255d0fde6aaa7" + integrity sha512-AmkqV9dDJVKP/TcZrbf6s6i1zYXt5Hl8qOLrRDTFfRNae4+LB8A4N3i+FLZPW85zIxRy39BgeWOfMS3HoH5ngg== dependencies: - "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/types" "4.25.0" eslint-visitor-keys "^2.0.0" "@ungap/promise-all-settled@1.1.2": @@ -1525,12 +1559,7 @@ acorn@^8.0.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== -acorn@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.1.tgz#fb0026885b9ac9f48bac1e185e4af472971149ff" - integrity sha512-xYiIVjNuqtKXMxlRMDc6mZUhXehod4a3gbZ1qRlM7icK4EbxUFNLhWoPblCvFtB2Y9CIqHP3CF/rdxLItaQv8g== - -acorn@^8.2.1: +acorn@^8.2.1, acorn@^8.2.4: version "8.2.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0" integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== @@ -1574,6 +1603,16 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b" + integrity sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -1928,10 +1967,10 @@ autwh@0.1.0: dependencies: oauth "0.9.15" -aws-sdk@2.910.0: - version "2.910.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.910.0.tgz#7b5f448c6af2caae6c3c956eb56feea3d6dd9b1a" - integrity sha512-PC4x3qzxXxZEhd4CbcMdso/WnxeQvsiuYXHL+lb5B5d8zdORcXPaNmyIL/2DByyjJ2Ur4tfXgoQVPDKyYLcWEA== +aws-sdk@2.918.0: + version "2.918.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.918.0.tgz#e4323de000262beb762e9c139f89e07282462f17" + integrity sha512-ZjWanOA1Zo664EyWLCnbUlkwCjoRPmSIMx529W4gk1418qo3oCEcvUy1HeibGGIClYnZZ7J4FMQvVDm2+JtHLQ== dependencies: buffer "4.9.2" events "1.1.1" @@ -2182,6 +2221,17 @@ browserslist@^4.16.0: escalade "^3.1.1" node-releases "^1.1.71" +browserslist@^4.16.6: + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== + dependencies: + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" + escalade "^3.1.1" + node-releases "^1.1.71" + buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -2378,7 +2428,7 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0, camelcase@^6.2.0: +camelcase@^6.0.0: version "6.2.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== @@ -2423,6 +2473,11 @@ caniuse-lite@^1.0.30001208: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz#a999014a35cebd4f98c405930a057a0d75352eb9" integrity sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA== +caniuse-lite@^1.0.30001219: + version "1.0.30001230" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71" + integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ== + canonicalize@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.1.tgz#657b4f3fa38a6ecb97a9e5b7b26d7a19cc6e0da9" @@ -2883,10 +2938,10 @@ color@^3.1.3: color-convert "^1.9.1" color-string "^1.5.4" -colord@^1.7.0: - version "1.7.2" - resolved "https://registry.yarnpkg.com/colord/-/colord-1.7.2.tgz#474f0e46333cb5c7a191dbd571edeee19a5f97b6" - integrity sha512-/sQCxy6PEhZbrAn1+NVRRefy3k4jkWQGxk7mo2o0CoNA24jq4ujDc2jXzJ5uXphm/TwfdGOP0w8U+H+9ys4Peg== +colord@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.0.1.tgz#1e7fb1f9fa1cf74f42c58cb9c20320bab8435aa0" + integrity sha512-vm5YpaWamD0Ov6TSG0GGmUIwstrWcfKQV/h2CmbR7PbNu41+qdB5PW9lpzhjedrpm08uuYvcXi0Oel1RLZIJuA== colorette@^1.2.0, colorette@^1.2.1, colorette@^1.2.2: version "1.2.2" @@ -2975,10 +3030,10 @@ concat-stream@^1.5.2, concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concurrently@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.1.0.tgz#00d22525d3fcdce7f34cc7f3c9753f90a57d6208" - integrity sha512-jy+xj49pvqeKjc2TAVXRIhrgPG51eBKDZti0kZ41kaWk9iLbyWBjH6KMFpW7peOLkEymD+ZM83Lx6UEy3N/M9g== +concurrently@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.2.0.tgz#587e2cb8afca7234172d8ea55176088632c4c56d" + integrity sha512-v9I4Y3wFoXCSY2L73yYgwA9ESrQMpRn80jMcqMgHx720Hecz2GZAvTI6bREVST6lkddNypDKRN22qhK0X8Y00g== dependencies: chalk "^4.1.0" date-fns "^2.16.1" @@ -3088,10 +3143,10 @@ core-js-compat@^3.9.1: browserslist "^4.16.3" semver "7.0.0" -core-js@3.12.1: - version "3.12.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112" - integrity sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw== +core-js@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.13.1.tgz#30303fabd53638892062d8b4e802cac7599e9fb7" + integrity sha512-JqveUc4igkqwStL2RTRn/EPFGBOfEZHxJl/8ej1mXJR75V3go2mFF4bmUYkEIT1rveHKnkUlcJX/c+f1TyIovQ== core-util-is@1.0.2, core-util-is@^1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -3174,15 +3229,14 @@ css-declaration-sorter@^6.0.3: dependencies: timsort "^0.3.0" -css-loader@5.2.4: - version "5.2.4" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.4.tgz#e985dcbce339812cb6104ef3670f08f9893a1536" - integrity sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw== +css-loader@5.2.6: + version "5.2.6" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.6.tgz#c3c82ab77fea1f360e587d871a6811f4450cc8d1" + integrity sha512-0wyN5vXMQZu6BvjbrPdUJvkCzGEO24HC7IS7nW4llc6BBFC+zwR9CKtYGv63Puzsg10L/o12inMY5/2ByzfD6w== dependencies: - camelcase "^6.2.0" icss-utils "^5.1.0" loader-utils "^2.0.0" - postcss "^8.2.10" + postcss "^8.2.15" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" @@ -3271,22 +3325,22 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssnano-preset-default@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.0.tgz#3ff4a1c102f72c7976580ce7f1c52325dc4628ed" - integrity sha512-VTO0WAlrEsU0u+A4RwALRs5zSVnD23WgYO8Np3HowBMZTqWIP1GvIWWzs9zZmjNgeL8U+70megRZymb+1yGg/w== +cssnano-preset-default@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.2.tgz#5d4877a91769823c5da6bcebd54996ecdf8aca12" + integrity sha512-spilp8LRw0sacuxiN9A/dyyPr6G/WISKMBKcBD4NMoPV0ENx4DeuWvIIrSx9PII2nJIDCO3kywkqTPreECBVOg== dependencies: css-declaration-sorter "^6.0.3" cssnano-utils "^2.0.1" postcss-calc "^8.0.0" - postcss-colormin "^5.1.0" + postcss-colormin "^5.2.0" postcss-convert-values "^5.0.1" postcss-discard-comments "^5.0.1" postcss-discard-duplicates "^5.0.1" postcss-discard-empty "^5.0.1" postcss-discard-overridden "^5.0.1" postcss-merge-longhand "^5.0.2" - postcss-merge-rules "^5.0.1" + postcss-merge-rules "^5.0.2" postcss-minify-font-values "^5.0.1" postcss-minify-gradients "^5.0.1" postcss-minify-params "^5.0.1" @@ -3303,7 +3357,7 @@ cssnano-preset-default@^5.1.0: postcss-ordered-values "^5.0.1" postcss-reduce-initial "^5.0.1" postcss-reduce-transforms "^5.0.1" - postcss-svgo "^5.0.1" + postcss-svgo "^5.0.2" postcss-unique-selectors "^5.0.1" cssnano-utils@^2.0.1: @@ -3311,13 +3365,13 @@ cssnano-utils@^2.0.1: resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-2.0.1.tgz#8660aa2b37ed869d2e2f22918196a9a8b6498ce2" integrity sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ== -cssnano@5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.3.tgz#3ae26411a176168e7a0ef425f4691579e1aed12a" - integrity sha512-Ga/a+IA6U0F9+MNmqrXgwn1A3btY5jFkla2iurL7i9PTmQmMu2Kc99W9a8KSxT8iLV0aoI+Q5Q9i3SpVAVNnrQ== +cssnano@5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.5.tgz#6b8787123bf4cd5a220a2fa6cb5bc036b0854b48" + integrity sha512-L2VtPXnq6rmcMC9vkBOP131sZu3ccRQI27ejKZdmQiPDpUlFkUbpXHgKN+cibeO1U4PItxVZp1zTIn5dHsXoyg== dependencies: cosmiconfig "^7.0.0" - cssnano-preset-default "^5.1.0" + cssnano-preset-default "^5.1.2" is-resolvable "^1.1.0" cssnano@^3.0.0: @@ -3845,6 +3899,11 @@ electron-to-chromium@^1.3.712: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.717.tgz#78d4c857070755fb58ab64bcc173db1d51cbc25f" integrity sha512-OfzVPIqD1MkJ7fX+yTl2nKyOE4FReeVfMCzzxQS+Kp43hZYwHwThlGP+EGIZRXJsxCM7dqo8Y65NOX/HP12iXQ== +electron-to-chromium@^1.3.723: + version "1.3.742" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.742.tgz#7223215acbbd3a5284962ebcb6df85d88b95f200" + integrity sha512-ihL14knI9FikJmH2XUIDdZFWJxvr14rPSdOhJ7PpS27xbz8qmaRwCwyg/bmFwjWKmWK9QyamiCZVCvXm5CH//Q== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -4031,7 +4090,7 @@ escape-regexp@0.0.1: resolved "https://registry.yarnpkg.com/escape-regexp/-/escape-regexp-0.0.1.tgz#f44bda12d45bbdf9cb7f862ee7e4827b3dd32254" integrity sha1-9EvaEtRbvfnLf4Yu5+SCez3TIlQ= -escape-string-regexp@4.0.0: +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== @@ -4053,17 +4112,17 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-plugin-vue@7.9.0: - version "7.9.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.9.0.tgz#f8e83a2a908f4c43fc8304f5401d4ff671f3d560" - integrity sha512-2Q0qQp5+5h+pZvJKCbG1/jCRUYrdgAz5BYKGyTlp2NU8mx09u3Hp7PsH6d5qef6ojuPoCXMnrbbDxeoplihrSw== +eslint-plugin-vue@7.10.0: + version "7.10.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.10.0.tgz#251749aa99e089e085275f011042c6e74189f89a" + integrity sha512-xdr6e4t/L2moRAeEQ9HKgge/hFq+w9v5Dj+BA54nTAzSFdUyKLiSOdZaRQjCHMY0Pk2WaQBFH9QiWG60xiC+6A== dependencies: eslint-utils "^2.1.0" natural-compare "^1.4.0" semver "^7.3.2" vue-eslint-parser "^7.6.0" -eslint-scope@^5.0.0, eslint-scope@^5.1.1: +eslint-scope@5.1.1, eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -4088,10 +4147,10 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@7.26.0: - version "7.26.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.26.0.tgz#d416fdcdcb3236cd8f282065312813f8c13982f6" - integrity sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg== +eslint@7.27.0: + version "7.27.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.27.0.tgz#665a1506d8f95655c9274d84bd78f7166b07e9c7" + integrity sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.1" @@ -4101,12 +4160,14 @@ eslint@7.26.0: debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" + escape-string-regexp "^4.0.0" eslint-scope "^5.1.1" eslint-utils "^2.1.0" eslint-visitor-keys "^2.0.0" espree "^7.3.1" esquery "^1.4.0" esutils "^2.0.2" + fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" @@ -4118,7 +4179,7 @@ eslint@7.26.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.21" + lodash.merge "^4.6.2" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -4127,7 +4188,7 @@ eslint@7.26.0: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^6.0.4" + table "^6.0.9" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -4342,6 +4403,11 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + fast-glob@^3.1.1: version "3.2.4" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" @@ -4400,15 +4466,14 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-type@16.4.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.4.0.tgz#464197e44bd94a452d77b09085d977ae0dad2df4" - integrity sha512-MDAkwha3wHg11Lp++2T3Gu347eC/DB4r7nYj6iZaf1l7UhGBh2746QKxg0BWC8w2dJsxUEmH8KvLueX+GthN2w== +file-type@16.5.0: + version "16.5.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.0.tgz#16a2626f3b33bac612f6e81e52216f3a7c8e12a2" + integrity sha512-OxgWA9tbL8N/WP00GD1z8O0MiwQKFyWRs1q+3FhjdvcGgKqwxcejyGWso3n4/IMU6DdwV+ARZ4A7TTnPkDcSiw== dependencies: readable-web-to-node-stream "^3.0.0" strtok3 "^6.0.3" token-types "^2.0.0" - typedarray-to-buffer "^3.1.5" fill-range@^4.0.0: version "4.0.0" @@ -5345,10 +5410,10 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -idb-keyval@5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-5.0.5.tgz#1b02984dcf42ad4aa290bf9f10935b485419df34" - integrity sha512-cqi65rrjhgPExI9vmSU7VcYEbHCUfIBY+9YUWxyr0PyGizptFgGFnvZQ0w+tqOXk1lUcGCZGVLfabf7QnR2S0g== +idb-keyval@5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-5.0.6.tgz#62fe4a6703fb5ec86661f41330c94fda65e6d0e6" + integrity sha512-6lJuVbwyo82mKSH6Wq2eHkt9LcbwHAelMIcMe0tP4p20Pod7tTxq9zf0ge2n/YDfMOpDryerfmmYyuQiaFaKOg== ieee754@1.1.13, ieee754@^1.1.13, ieee754@^1.1.4: version "1.1.13" @@ -5736,10 +5801,10 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-potential-custom-element-name@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" - integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== is-promise@^2.0.0: version "2.2.2" @@ -5981,13 +6046,13 @@ jschardet@^2.1.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== -jsdom@16.5.3: - version "16.5.3" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.3.tgz#13a755b3950eb938b4482c407238ddf16f0d2136" - integrity sha512-Qj1H+PEvUsOtdPJ056ewXM4UJPCi4hhLA8wpiz9F2YvsRBhuFsXxtrIFAgGBDynQA9isAMGE91PfUYbdMPXuTA== +jsdom@16.6.0: + version "16.6.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" + integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== dependencies: abab "^2.0.5" - acorn "^8.1.0" + acorn "^8.2.4" acorn-globals "^6.0.0" cssom "^0.4.4" cssstyle "^2.3.0" @@ -5995,12 +6060,13 @@ jsdom@16.5.3: decimal.js "^10.2.1" domexception "^2.0.1" escodegen "^2.0.0" + form-data "^3.0.0" html-encoding-sniffer "^2.0.1" - is-potential-custom-element-name "^1.0.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" nwsapi "^2.2.0" parse5 "6.0.1" - request "^2.88.2" - request-promise-native "^1.0.9" saxes "^5.0.1" symbol-tree "^3.2.4" tough-cookie "^4.0.0" @@ -6010,7 +6076,7 @@ jsdom@16.5.3: whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" whatwg-url "^8.5.0" - ws "^7.4.4" + ws "^7.4.5" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -6038,6 +6104,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -6277,7 +6348,7 @@ koa-slow@2.1.0: lodash.isregexp "3.0.5" q "1.4.1" -koa-views@7.0.1: +koa-views@*, koa-views@7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/koa-views/-/koa-views-7.0.1.tgz#0c8f8e65d5cd2e08249430cb83dc361e49a17a5a" integrity sha512-yS8751DXHXXDbdl/oUZd0PsgnxR0MLiguu77Eqrgu6yawE9Hi99wNKiVENb0Kfgsmvq/8px7YCI+USgxaTB1LA== @@ -6504,6 +6575,11 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -6589,6 +6665,11 @@ lodash.throttle@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -7127,11 +7208,6 @@ nanoid@3.1.20, nanoid@^3.1.20: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== -nanoid@^3.1.22: - version "3.1.22" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" - integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== - nanoid@^3.1.23: version "3.1.23" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" @@ -7217,10 +7293,10 @@ node-abi@^2.21.0: dependencies: semver "^5.4.1" -node-addon-api@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239" - integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw== +node-addon-api@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== node-fetch@2.6.1, node-fetch@^2.6.1: version "2.6.1" @@ -7263,10 +7339,10 @@ node-releases@^1.1.70, node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== -nodemailer@6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.0.tgz#ed47bb572b48d9d0dca3913fdc156203f438f427" - integrity sha512-ikSMDU1nZqpo2WUPE0wTTw/NGGImTkwpJKDIFPZT+YvvR9Sj+ze5wzu95JHkBMglQLoG2ITxU21WukCC/XsFkg== +nodemailer@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.1.tgz#2a05fbf205b897d71bf43884167b5d4d3bd01b99" + integrity sha512-1xzFN3gqv+/qJ6YRyxBxfTYstLNt0FCtZaFRvf4Sg9wxNGWbwFmGXVpfSi6ThGK6aRxAo+KjHtYSW8NvCsNSAg== nofilter@^2.0.3: version "2.0.3" @@ -8045,13 +8121,14 @@ postcss-colormin@^2.1.8: postcss "^5.0.13" postcss-value-parser "^3.2.3" -postcss-colormin@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.1.0.tgz#686da694e5e638eac141364e150eb936b3a8e79f" - integrity sha512-5s+VNfQ5CsR6yvBc6Tc8NK9cxb/c8sA547zy+8ofL04gs/GygmOcVZt2s6jJn7TvntAw0Bp6MDpq4bd51EPwdw== +postcss-colormin@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.2.0.tgz#2b620b88c0ff19683f3349f4cf9e24ebdafb2c88" + integrity sha512-+HC6GfWU3upe5/mqmxuqYZ9B2Wl4lcoUUNkoaX59nEWV4EtADCMiBqui111Bu8R8IvaZTmqmxrqOAqjbHIwXPw== dependencies: - browserslist "^4.16.0" - colord "^1.7.0" + browserslist "^4.16.6" + caniuse-api "^3.0.0" + colord "^2.0.1" postcss-value-parser "^4.1.0" postcss-convert-values@^2.3.4: @@ -8177,12 +8254,12 @@ postcss-merge-rules@^2.0.3: postcss-selector-parser "^2.2.2" vendors "^1.0.0" -postcss-merge-rules@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.1.tgz#4ff61c5089d86845184a0f149e88d687028bef7e" - integrity sha512-UR6R5Ph0c96QB9TMBH3ml8/kvPCThPHepdhRqAbvMRDRHQACPC8iM5NpfIC03+VRMZTGXy4L/BvFzcDFCgb+fA== +postcss-merge-rules@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.2.tgz#d6e4d65018badbdb7dcc789c4f39b941305d410a" + integrity sha512-5K+Md7S3GwBewfB4rjDeol6V/RZ8S+v4B66Zk2gChRqLTCC8yjnHQ601omj9TKftS19OPGqZ/XzoqpzNQQLwbg== dependencies: - browserslist "^4.16.0" + browserslist "^4.16.6" caniuse-api "^3.0.0" cssnano-utils "^2.0.1" postcss-selector-parser "^6.0.5" @@ -8484,10 +8561,10 @@ postcss-svgo@^2.1.1: postcss-value-parser "^3.2.3" svgo "^0.7.0" -postcss-svgo@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.1.tgz#6ed5e01e164e59204978994d844c653a331a8100" - integrity sha512-cD7DFo6tF9i5eWvwtI4irKOHCpmASFS0xvZ5EQIgEdA1AWfM/XiHHY/iss0gcKHhkqwgYmuo2M0KhJLd5Us6mg== +postcss-svgo@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.2.tgz#bc73c4ea4c5a80fbd4b45e29042c34ceffb9257f" + integrity sha512-YzQuFLZu3U3aheizD+B1joQ94vzPfE6BNUcSYuceNxlVnKKsOtdo6hL9/zyC168Q8EwfLSgaDSalsUGa9f2C0A== dependencies: postcss-value-parser "^4.1.0" svgo "^2.3.0" @@ -8529,14 +8606,14 @@ postcss-zindex@^2.0.1: postcss "^5.0.4" uniqs "^2.0.0" -postcss@8.2.15: - version "8.2.15" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65" - integrity sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q== +postcss@8.3.0, postcss@^8.2.15: + version "8.3.0" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.0.tgz#b1a713f6172ca427e3f05ef1303de8b65683325f" + integrity sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ== dependencies: colorette "^1.2.2" nanoid "^3.1.23" - source-map "^0.6.1" + source-map-js "^0.6.2" postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.8, postcss@^5.2.16: version "5.2.18" @@ -8557,15 +8634,6 @@ postcss@^8.1.10: nanoid "^3.1.20" source-map "^0.6.1" -postcss@^8.2.10: - version "8.2.12" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.12.tgz#81248a1a87e0f575cc594a99a08207fd1c4addc4" - integrity sha512-BJnGT5+0q2tzvs6oQfnY2NpEJ7rIXNfBnZtQOKCIsweeWXBXeDd5k31UgTdS3d/c02ouspufn37mTaHWkJyzMQ== - dependencies: - colorette "^1.2.2" - nanoid "^3.1.22" - source-map "^0.6.1" - postgres-array@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" @@ -8657,10 +8725,10 @@ prismjs@1.23.0: optionalDependencies: clipboard "^2.0.0" -probe-image-size@7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.1.0.tgz#2b0f8474c9c0c68f3b640e353fe64127585f0f6b" - integrity sha512-T7F4ZF4iWQJWhj/ijPyh88fHen9UtaUwtV+QMZ1NkmpUSDHmfYlZgRUTOcFKnqi95I6kpGBQ+3pF1dCtVmDeIg== +probe-image-size@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.1.1.tgz#ea3dc7acd69fb041212de12bb390be58c1025917" + integrity sha512-lvpHKqtino5SSrFEKxFncco8raLywljcLbjcE35VIRl5O+u1qLXenNaEfnopYdJgN4PPvvBxdr8b7sEUTvfY/w== dependencies: lodash.merge "^4.6.2" needle "^2.5.2" @@ -9232,13 +9300,6 @@ request-promise-core@1.1.2: dependencies: lodash "^4.17.11" -request-promise-core@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" - integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== - dependencies: - lodash "^4.17.19" - request-promise-native@1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" @@ -9248,15 +9309,6 @@ request-promise-native@1.0.7: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request-promise-native@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" - integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== - dependencies: - request-promise-core "1.1.4" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - request-stats@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/request-stats/-/request-stats-3.0.0.tgz#769155dc8974d78d4a1cb87bbf14eaab985afe25" @@ -9291,7 +9343,7 @@ request@2.88.0: tunnel-agent "^0.6.0" uuid "^3.3.2" -request@^2.88.0, request@^2.88.2: +request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -9332,6 +9384,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" @@ -9524,10 +9581,10 @@ sass-loader@11.1.1: klona "^2.0.4" neo-async "^2.6.2" -sass@1.32.13: - version "1.32.13" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.13.tgz#8d29c849e625a415bce71609c7cf95e15f74ed00" - integrity sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA== +sass@1.34.0: + version "1.34.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.34.0.tgz#e46d5932d8b0ecc4feb846d861f26a578f7f7172" + integrity sha512-rHEN0BscqjUYuomUEaqq3BMgsXqQfkcMVR7UhscsAVub0/spUrZGBMxQXFS2kfiDsPLZw5yuU9iJEFNC2x38Qw== dependencies: chokidar ">=3.0.0 <4.0.0" @@ -9665,14 +9722,14 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -sharp@0.28.2: - version "0.28.2" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.28.2.tgz#31c6ebdf8ddb9b4ca3e30179e3f4a73f0c7474e4" - integrity sha512-CdmySbsQVe/+ZM2j9zzvUfWumM0L0iHj1kpxJMFuyWvSuBULebvGCdOLb1f5vbbBrIGroX714Fx1wiWaKniz4A== +sharp@0.28.3: + version "0.28.3" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.28.3.tgz#ecd74cefd020bee4891bb137c9850ee2ce277a8b" + integrity sha512-21GEP45Rmr7q2qcmdnjDkNP04Ooh5v0laGS5FDpojOO84D1DJwUijLiSq8XNNM6e8aGXYtoYRh3sVNdm8NodMA== dependencies: color "^3.1.3" detect-libc "^1.0.3" - node-addon-api "^3.1.0" + node-addon-api "^3.2.0" prebuild-install "^6.1.2" semver "^7.3.5" simple-get "^3.1.0" @@ -9824,6 +9881,11 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" + integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -10371,25 +10433,27 @@ syslog-pro@1.0.0: dependencies: moment "^2.22.2" -systeminformation@5.6.22: - version "5.6.22" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.6.22.tgz#7751340e2835bd3d0b28694ae273e168b235f1df" - integrity sha512-GJYkEUn2rKKsRG8ieUhnOXkowSTno2s8CD/jkeyi3KErxSq0HCYu+7YYbxirL3/bh7zE3yjy/S52rEnrBjjf7Q== +systeminformation@5.7.4: + version "5.7.4" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.7.4.tgz#13fcc29bd3836cf5043802f0f2532d0c4961a4ea" + integrity sha512-m6oziAu2zP8IMnNZpVWptzpcaXG6x59c2sQB8BI7DTcfBOTLXj5jGgjWxCVP65c5k7Fwhn35yVTCIZEypc1n9A== syuilo-password-strength@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/syuilo-password-strength/-/syuilo-password-strength-0.0.1.tgz#08f71a8f0ecb77db649f3d9a6424510d9d945f52" integrity sha1-CPcajw7Ld9tknz2aZCRRDZ2UX1I= -table@^6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/table/-/table-6.0.4.tgz#c523dd182177e926c723eb20e1b341238188aa0d" - integrity sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw== +table@^6.0.9: + version "6.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== dependencies: - ajv "^6.12.4" - lodash "^4.17.20" + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.truncate "^4.4.2" slice-ansi "^4.0.0" string-width "^4.2.0" + strip-ansi "^6.0.0" tapable@^1.0.0-beta.5: version "1.1.3" @@ -10712,21 +10776,25 @@ tree-kill@^1.2.2: resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== -ts-loader@9.2.1: - version "9.2.1" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.1.tgz#5699af8d10aa3a763aadfced1d4b275a432b76eb" - integrity sha512-BLfLa4paMQyf819haKxxbZqA1aLDqsk8XEZLmd7E1eBa0NsEHYFcTWlUlPmYMGhvD/IMi0NcIW3A/G7vzr3oiA== +ts-loader@9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.2.tgz#416333900621c82d5eb1b1f6dea4114111f096bf" + integrity sha512-hNIhGTQHtNKjOzR2ZtQ2OSVbXPykOae+zostf1IlHCf61Mt41GMJurKNqrYUbzHgpmj6UWRu8eBfb7q0XliV0g== dependencies: chalk "^4.1.0" enhanced-resolve "^5.0.0" micromatch "^4.0.0" semver "^7.3.4" -ts-node@9.1.1: - version "9.1.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" - integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== +ts-node@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.0.0.tgz#05f10b9a716b0b624129ad44f0ea05dac84ba3be" + integrity sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg== dependencies: + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.1" arg "^4.1.0" create-require "^1.1.0" diff "^4.0.1" @@ -10931,10 +10999,10 @@ typeorm@0.2.32: yargs "^16.2.0" zen-observable-ts "^1.0.0" -typescript@4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" - integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== +typescript@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" + integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" @@ -11348,6 +11416,14 @@ watchpack@^2.0.0: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" +watchpack@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce" + integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + web-push@3.4.4: version "3.4.4" resolved "https://registry.yarnpkg.com/web-push/-/web-push-3.4.4.tgz#b11523ada0f4b8c2481f65d1d059acd45ba27ca0" @@ -11413,10 +11489,18 @@ webpack-sources@^2.1.1: source-list-map "^2.0.1" source-map "^0.6.1" -webpack@5.37.1: - version "5.37.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.37.1.tgz#2deb5acd350583c1ab9338471f323381b0b0c14b" - integrity sha512-btZjGy/hSjCAAVHw+cKG+L0M+rstlyxbO2C+BOTaQ5/XAnxkDrP5sVbqWhXgo4pL3X2dcOib6rqCP20Zr9PLow== +webpack-sources@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.0.tgz#9ed2de69b25143a4c18847586ad9eccb19278cfa" + integrity sha512-WyOdtwSvOML1kbgtXbTDnEW0jkJ7hZr/bDByIwszhWd/4XX1A3XMkrbFMsuH4+/MfLlZCUzlAdg4r7jaGKEIgQ== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + +webpack@5.38.1: + version "5.38.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.38.1.tgz#5224c7f24c18e729268d3e3bc97240d6e880258e" + integrity sha512-OqRmYD1OJbHZph6RUMD93GcCZy4Z4wC0ele4FXyYF0J6AxO1vOSuIlU1hkS/lDlR9CDYBz64MZRmdbdnFFoT2g== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.47" @@ -11428,7 +11512,7 @@ webpack@5.37.1: chrome-trace-event "^1.0.2" enhanced-resolve "^5.8.0" es-module-lexer "^0.4.0" - eslint-scope "^5.1.1" + eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" graceful-fs "^4.2.4" @@ -11439,8 +11523,8 @@ webpack@5.37.1: schema-utils "^3.0.0" tapable "^2.1.1" terser-webpack-plugin "^5.1.1" - watchpack "^2.0.0" - webpack-sources "^2.1.1" + watchpack "^2.2.0" + webpack-sources "^2.3.0" webpack@^5: version "5.33.2" @@ -11626,15 +11710,10 @@ write-json-file@^2.3.0: sort-keys "^2.0.0" write-file-atomic "^2.0.0" -ws@7.4.5: - version "7.4.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" - integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== - -ws@^7.4.4: - version "7.4.4" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" - integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== +ws@7.4.6, ws@^7.4.5: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== xev@2.0.1: version "2.0.1" From bb926dd30122de8b9901c0cd81f3872496f495b7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 31 May 2021 11:05:37 +0900 Subject: [PATCH 24/28] fix bug --- src/client/os.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/os.ts b/src/client/os.ts index e6355b45b8..987844b2d2 100644 --- a/src/client/os.ts +++ b/src/client/os.ts @@ -60,7 +60,7 @@ export const api = ((endpoint: string, data: Record<string, any> = {}, token?: s if (res.status === 200) { resolve(body); if (debug) { - log!.res = markRaw(body); + log!.res = markRaw(JSON.parse(JSON.stringify(body))); log!.state = 'success'; } } else if (res.status === 204) { From dc44d9a0a8a8f71b2edf41f8fa3bd36009fb51ae Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 31 May 2021 12:02:02 +0900 Subject: [PATCH 25/28] fix --- src/client/pages/instance/overview.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/pages/instance/overview.vue b/src/client/pages/instance/overview.vue index dca2529e1b..cb9cff9fc5 100644 --- a/src/client/pages/instance/overview.vue +++ b/src/client/pages/instance/overview.vue @@ -92,6 +92,7 @@ export default defineComponent({ version, url, stats: null, + meta: null, fetchStats: () => os.api('stats', {}), fetchServerInfo: () => os.api('admin/server-info', {}), fetchJobs: () => os.api('admin/queue/deliver-delayed', {}), From b608f63a1a530508079318da2252f246fe3dcd9f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 31 May 2021 12:02:13 +0900 Subject: [PATCH 26/28] New Crowdin updates (#7527) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Polish) * New translations ja-JP.yml (Polish) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Ukrainian) * New translations ja-JP.yml (Polish) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Czech) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Korean) --- locales/ar-SA.yml | 2 -- locales/cs-CZ.yml | 2 -- locales/de-DE.yml | 7 +++++-- locales/en-US.yml | 7 +++++-- locales/es-ES.yml | 2 -- locales/fr-FR.yml | 2 -- locales/id-ID.yml | 14 ++++++-------- locales/it-IT.yml | 2 -- locales/ja-KS.yml | 2 -- locales/ko-KR.yml | 7 +++++-- locales/pl-PL.yml | 7 +++++-- locales/ru-RU.yml | 2 -- locales/uk-UA.yml | 2 -- locales/zh-CN.yml | 7 +++++-- locales/zh-TW.yml | 14 +++++++------- 15 files changed, 38 insertions(+), 41 deletions(-) diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 631027cb7a..9c20ea7524 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -259,8 +259,6 @@ monthX: "{month}" yearX: "{year}" pages: "الصفحات" integration: "دمج" -connectSerice: "أوصل" -disconnectSerice: "قطع الاتصال" enableLocalTimeline: "تفعيل الخيط المحلي" enableGlobalTimeline: "تفعيل الخيط الزمني الشامل" disablingTimelinesInfo: "سيتمكن المسؤولون ومن تعديل دائمًا و من الوصول إلى جميع المخططات الزمنية ، حتى إذا لم يتم تمكينها." diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 6d62b5345e..995d8eccd8 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -269,8 +269,6 @@ monthX: "{month}" yearX: "{year}" pages: "Stránky" integration: "Integrace" -connectSerice: "Připojit" -disconnectSerice: "Odpojit" enableLocalTimeline: "Povolit lokální čas" enableGlobalTimeline: "Povolit globální čas" registration: "Registrace" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 1f35b9de72..e3edcf5f40 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -279,6 +279,7 @@ emptyDrive: "Drive ist leer" emptyFolder: "Der Ordner ist leer" unableToDelete: "Nicht löschbar" inputNewFileName: "Gib einen neuen Dateinamen ein" +inputNewDescription: "Gib eine neue Beschreibung ein" inputNewFolderName: "Gib einen neuen Ordnernamen ein" circularReferenceFolder: "Der Zielordner ist ein Unterorder des Ordners, den du verschieben möchtest." hasChildFilesOrFolders: "Dieser Ordner kann nicht gelöscht werden, da er nicht leer ist." @@ -310,8 +311,8 @@ monthX: "{month}" yearX: "{year}" pages: "Seiten" integration: "Integration" -connectSerice: "Verbinden" -disconnectSerice: "Trennen" +connectService: "Verbinden" +disconnectService: "Trennen" enableLocalTimeline: "Lokale Chronik aktivieren" enableGlobalTimeline: "Globale Chronik aktivieren" disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle Chroniken, auch wenn diese deaktiviert sind." @@ -546,6 +547,8 @@ disablePlayer: "Video-Player schließen" expandTweet: "Tweet ausklappen" themeEditor: "Farbthemen-Editor" description: "Beschreibung" +describeFile: "Beschreibung hinzufügen" +enterFileDescription: "Beschreibung eingeben" author: "Autor" leaveConfirm: "Es gibt unspeicherte Änderungen. Möchtest du diese verwerfen?" manage: "Verwaltung" diff --git a/locales/en-US.yml b/locales/en-US.yml index 4cf417d21b..3dd581c8a4 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -279,6 +279,7 @@ emptyDrive: "The drive is empty" emptyFolder: "This folder is empty" unableToDelete: "Unable to delete" inputNewFileName: "Enter a new filename" +inputNewDescription: "Enter new caption" inputNewFolderName: "Enter a new folder name" circularReferenceFolder: "The destination folder is a subfolder of the folder you wish to move." hasChildFilesOrFolders: "Since this folder is not empty, it can not be deleted." @@ -310,8 +311,8 @@ monthX: "{month}" yearX: "{year} /" pages: "Pages" integration: "Integration" -connectSerice: "Connect" -disconnectSerice: "Disconnect" +connectService: "Connect" +disconnectService: "Disconnect" enableLocalTimeline: "Enable local timeline" enableGlobalTimeline: "Enable global timeline" disablingTimelinesInfo: "Admins and Mods will always have access to all timelines, even if they are not enabled." @@ -546,6 +547,8 @@ disablePlayer: "Close video player" expandTweet: "Expand tweet" themeEditor: "Theme editor" description: "Description" +describeFile: "Add caption" +enterFileDescription: "Enter caption" author: "Author" leaveConfirm: "There are unsaved changes. Do you want to discard them?" manage: "Management" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index ae7fba8976..89e170678d 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -309,8 +309,6 @@ monthX: "Mes {month}" yearX: "Año {year}" pages: "Páginas" integration: "Integración" -connectSerice: "Conectarse" -disconnectSerice: "Desconectarse" enableLocalTimeline: "Habilitar linea de tiempo local" enableGlobalTimeline: "Habilitar linea de tiempo global" disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conveniencia el administrador y los moderadores pueden seguir usándolos" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index c62b1c3b6e..174a3a8f5a 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -310,8 +310,6 @@ monthX: "{month}" yearX: "{year}" pages: "Pages" integration: "Intégrations" -connectSerice: "Connecter" -disconnectSerice: "Déconnecter" enableLocalTimeline: "Activer le fil local" enableGlobalTimeline: "Activer le fil global" disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur·rice·s et les modérateur·rice·s pourront toujours y accéder." diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 5f401bc4b1..ed1ac14121 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -1,5 +1,5 @@ --- -_lang_: "Bahasa Jepang" +_lang_: "Bahasa Indonesia" headlineMisskey: "Jaringan terhubung melalui note" introMisskey: "Selamat datang! Misskey adalah perangkat mikroblog tercatu bersifat sumber terbuka.\nMulailah menuliskan catatan, bagikan peristiwa terkini, serta ceritakan segala tentangmu.📡\nTunjukkan juga reaksimu pada catatan pengguna lain.👍\nMari jelajahi dunia baru🚀" monthAndDay: "{day} {month}" @@ -310,8 +310,6 @@ monthX: "{month}" yearX: "{year}" pages: "Halaman" integration: "Integrasi" -connectSerice: "Sambungkan" -disconnectSerice: "Putuskan" enableLocalTimeline: "Nyalakan linimasa lokal" enableGlobalTimeline: "Nyalakan linimasa global" disablingTimelinesInfo: "Admin dan Moderator akan selalu memiliki akses ke semua linimasa meskipun linimasa tersebut tidak diaktifkan." @@ -977,9 +975,9 @@ _theme: infoFg: "Teks informasi" infoWarnBg: "Latar belakang peringatan" infoWarnFg: "Teks peringatan" - cwBg: "Latar belakang tombol CW" - cwFg: "Teks tombol CW" - cwHoverBg: "Latar belakang tombol CW (Mengambang)" + cwBg: "Latar belakang tombol Sembunyikan Konten" + cwFg: "Teks tombol Sembunyikan Konten" + cwHoverBg: "Latar belakang tombol Sembunyikan Konten (Mengambang)" toastBg: "Latar belakang pemberitahuan" toastFg: "Teks pemberitahuan" buttonBg: "Latar belakang tombol" @@ -1122,7 +1120,7 @@ _widgets: aiscript: "Konsol AiScript" _cw: hide: "Sembunyikan" - show: "Selebihnya" + show: "Lihat konten" chars: "{count} karakter" files: "{count} berkas" _poll: @@ -1551,7 +1549,7 @@ _pages: fn: "Fungsi" _fn: slots: "Slot" - slots-info: "Pisahkan setiap slow dengan baris baru" + slots-info: "Pisahkan setiap slot dengan baris baru" arg1: "Keluaran" for: "Ulangi" _for: diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 69c8152d7f..0fdf78c799 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -305,8 +305,6 @@ monthX: "{month}" yearX: "{year}" pages: "Pagine" integration: "App collegate" -connectSerice: "Connetti" -disconnectSerice: "Disconnetti" enableLocalTimeline: "Abilita Timeline locale" enableGlobalTimeline: "Abilita Timeline federata" disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e i moderatori potranno sempre accederci." diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 6435b5835b..060c7b142d 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -308,8 +308,6 @@ monthX: "{month}月" yearX: "{year}年" pages: "ページ" integration: "連携" -connectSerice: "つなぐ" -disconnectSerice: "切ってまう" enableLocalTimeline: "ローカルタイムラインを使えるようにする" enableGlobalTimeline: "グローバルタイムラインを使えるようにする" disablingTimelinesInfo: "ここらへんのタイムラインを使えんようにしてしもても、管理者とモデレーターは使えるままになってるで、そうやなかったら不便やからな。" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 49069d258f..3d313b8c7c 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -279,6 +279,7 @@ emptyDrive: "드라이브가 비어 있습니다" emptyFolder: "폴더가 비어 있습니다" unableToDelete: "삭제할 수 없습니다" inputNewFileName: "바꿀 파일명을 입력해 주세요" +inputNewDescription: "새 캡션을 입력해 주세요" inputNewFolderName: "바꿀 폴더명을 입력해 주세요" circularReferenceFolder: "지정한 폴더가 이동할 폴더의 하위 폴더입니다." hasChildFilesOrFolders: "이 폴더는 비어있지 않기 때문에 삭제할 수 없습니다." @@ -310,8 +311,8 @@ monthX: "{month}월" yearX: "{year}년" pages: "페이지" integration: "연동" -connectSerice: "접속" -disconnectSerice: "연결 끊기" +connectService: "계정 연동" +disconnectService: "계정 연동 해제" enableLocalTimeline: "로컬 타임라인 활성화" enableGlobalTimeline: "글로벌 타임라인 활성화" disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다." @@ -546,6 +547,8 @@ disablePlayer: "플레이어 닫기" expandTweet: "트윗 확장하기" themeEditor: "테마 에디터" description: "설명" +describeFile: "캡션 추가" +enterFileDescription: "캡션 입력" author: "작성자" leaveConfirm: "저장하지 않은 변경사항이 있습니다. 취소하시겠습니까?" manage: "관리" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index 4b4c80f7f3..550bda78b0 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -135,6 +135,7 @@ settingGuide: "Proponowana konfiguracja" cacheRemoteFiles: "Przechowuj zdalne pliki w pamięci podręcznej" cacheRemoteFilesDescription: "Gdy ta opcja jest wyłączona, zdalne pliki są ładowane bezpośrednio ze zdalnych instancji. Wyłączenie the opcji zmniejszy użycie powierzchni dyskowej, ale zwiększy transfer, ponieważ miniaturki nie będą generowane." flagAsBot: "To konto jest botem" +flagAsBotDescription: "Jeżeli ten kanał jest kontrolowany przez jakiś program, ustaw tę opcję. Jeżeli włączona, będzie działać jako flaga informująca innych programistów, aby zapobiegać nieskończonej interakcji z różnymi botami i dostosowywać wewnętrzne systemy Misskey, traktując konto jako bota." flagAsCat: "To konto jest kotem" flagAsCatDescription: "Przełącz tę opcję, aby konto było oznaczone jako kot." autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od użytkowników, których obserwujesz" @@ -182,6 +183,7 @@ clearQueueConfirmTitle: "Czy na pewno chcesz wyczyścić kolejkę?" clearCachedFiles: "Wyczyść pamięć podręczną" clearCachedFilesConfirm: "Czy na pewno chcesz usunąć wszystkie zdalne pliki z pamięci podręcznej?" blockedInstances: "Zablokowane instancje" +blockedInstancesDescription: "Wypisz nazwy hostów instancji, które powinny zostać zablokowane. Wypisane instancje nie będą mogły dłużej komunikować się z tą instancją." muteAndBlock: "Wycisz / Zablokuj" mutedUsers: "Wyciszeni użytkownicy" blockedUsers: "Zablokowani użytkownicy" @@ -274,6 +276,7 @@ emptyDrive: "Dysk jest pusty" emptyFolder: "Ten katalog jest pusty" unableToDelete: "Nie można usunąć" inputNewFileName: "Wprowadź nową nazwę pliku" +inputNewDescription: "Proszę wpisać nowy napis" inputNewFolderName: "Wprowadź nową nazwę katalogu" circularReferenceFolder: "Katalog docelowy jest podkatalogiem katalogu, który chcesz przenieść." hasChildFilesOrFolders: "Ponieważ ten katalog nie jest pusty, nie może być usunięty." @@ -305,8 +308,6 @@ monthX: "{month}" yearX: "{year}" pages: "Strony" integration: "Integracja" -connectSerice: "Połącz" -disconnectSerice: "Rozłącz" enableLocalTimeline: "Włącz lokalną oś czasu" enableGlobalTimeline: "Włącz globalną oś czasu" disablingTimelinesInfo: "Administratorzy i moderatorzy będą zawsze mieć dostęp do wszystkich osi czasu, nawet gdy są one wyłączone." @@ -532,6 +533,8 @@ disablePlayer: "Zamknij odtwarzacz wideo" expandTweet: "Rozwiń tweet" themeEditor: "Edytor motywu" description: "Opis" +describeFile: "dodaj podpis" +enterFileDescription: "Wprowadź napis" author: "Autor" leaveConfirm: "Są niezapisane zmiany. Czy chcesz je odrzucić?" manage: "Zarządzanie" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 2610403a71..c9700f4c91 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -309,8 +309,6 @@ monthX: "{month} месяц" yearX: "{year} год" pages: "Страницы" integration: "Интеграция" -connectSerice: "Соединение" -disconnectSerice: "Отключение" enableLocalTimeline: "Включить локальную ленту" enableGlobalTimeline: "Включить глобальную ленту" disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам, даже если они отключены." diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index bb010ed5f6..3b8e6461c4 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -307,8 +307,6 @@ monthX: "{month}" yearX: "{year}" pages: "Сторінки" integration: "Інтеграція" -connectSerice: "Під’єднати" -disconnectSerice: "Відключитися" enableLocalTimeline: "Увімкнути локальну стрічку" enableGlobalTimeline: "Увімкнути глобальну стрічку" disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх стрічок, навіть якщо вони вимкнуті." diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 4754fec4a4..905c84575c 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -279,6 +279,7 @@ emptyDrive: "驱动器为空" emptyFolder: "空文件夹" unableToDelete: "无法删除" inputNewFileName: "请输入新文件名" +inputNewDescription: "请输入新标题" inputNewFolderName: "请输入新文件名" circularReferenceFolder: "目标文件夹是您要移动的文件夹的子文件夹。" hasChildFilesOrFolders: "此文件夹不为空,无法删除。" @@ -310,8 +311,8 @@ monthX: "{month}月" yearX: "{year}年" pages: "页面" integration: "关联" -connectSerice: "连接" -disconnectSerice: "断开连接" +connectService: "连接" +disconnectService: "断开连接" enableLocalTimeline: "启用本地时间线功能" enableGlobalTimeline: "启用全局时间线" disablingTimelinesInfo: "即使时间线功能被禁用,出于便利性的原因,管理员和数据图表也可以继续使用。" @@ -546,6 +547,8 @@ disablePlayer: "关闭播放器" expandTweet: "展开贴文" themeEditor: "主题编辑器" description: "描述" +describeFile: "添加标题" +enterFileDescription: "输入标题" author: "作者" leaveConfirm: "存在未保存的更改。要放弃更改吗?" manage: "管理" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index e8c7e55531..dd9552a871 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1,18 +1,19 @@ --- _lang_: "繁體中文" -headlineMisskey: "貼文連繫網絡" -introMisskey: "歡迎! Misskey是一個開源且去中心化的社群網絡。\n通過「貼文」分享周邊新鮮事,並告訴其他人您的想法!📡\n透過「情感」功能,對大家的貼文表達情感!👍\n一起來探索這個新的世界吧!🚀" +headlineMisskey: "貼文連繫網路" +introMisskey: "歡迎! Misskey是一個開放原始碼且去中心化的社群網路。\n透過「貼文」分享周邊新鮮事,並告訴其他人您的想法!📡\n透過「情感」功能,對大家的貼文表達情感!👍\n一起來探索這個新的世界吧!🚀" monthAndDay: "{month}月 {day}日" search: "搜尋" notifications: "通知" username: "使用者名稱" password: "密碼" +forgotPassword: "忘記密碼" fetchingAsApObject: "從聯邦宇宙取得中..." ok: "OK" gotIt: "知道了" cancel: "取消" enterUsername: "輸入使用者名稱" -renotedBy: "{user} 轉發了" +renotedBy: "{user} 轉傳了" noNotes: "貼文不可用。" noNotifications: "沒有通知" instance: "實例" @@ -92,9 +93,9 @@ followRequestPending: "追隨許可批准中" enterEmoji: "輸入表情符號" renote: "轉發" unrenote: "取消轉發" -renoted: "轉發成功" +renoted: "轉傳成功" cantRenote: "無法轉發此貼文。" -cantReRenote: "無法轉發之前已經轉發過的內容。" +cantReRenote: "無法轉傳之前已經轉傳過的內容。" quote: "引用" pinnedNote: "已置頂的貼文" pinned: "置頂" @@ -309,8 +310,6 @@ monthX: "{month}月" yearX: "{year}年" pages: "頁面" integration: "整合" -connectSerice: "連線" -disconnectSerice: "中斷連線" enableLocalTimeline: "開啟本地時間軸" enableGlobalTimeline: "啟用公開時間軸" disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。" @@ -733,6 +732,7 @@ noBotProtectionWarning: "尚未設定Bot防護。" configure: "設定" expiration: "期限" middle: "中" +emailNotConfiguredWarning: "沒有設定電子郵件地址" _ad: back: "返回" _gallery: From caf40e40fbd444c050d8d3cc60f4dd2898950900 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Mon, 31 May 2021 13:04:13 +0900 Subject: [PATCH 27/28] Supports Array ActivityStreams type (#7536) * Supports Array type * Fix * Fix Service to Note * Update type.ts Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> --- src/remote/activitypub/kernel/accept/index.ts | 14 ++--- src/remote/activitypub/kernel/create/index.ts | 6 +- src/remote/activitypub/kernel/reject/index.ts | 14 ++--- .../activitypub/kernel/undo/announce.ts | 5 +- src/remote/activitypub/kernel/undo/index.ts | 26 +++----- src/remote/activitypub/kernel/update/index.ts | 8 +-- src/remote/activitypub/models/note.ts | 6 +- src/remote/activitypub/models/person.ts | 8 +-- src/remote/activitypub/type.ts | 61 ++++++++++++------- src/server/api/endpoints/ap/show.ts | 10 +-- 10 files changed, 76 insertions(+), 82 deletions(-) diff --git a/src/remote/activitypub/kernel/accept/index.ts b/src/remote/activitypub/kernel/accept/index.ts index 083e312a6f..79cdbb2ef7 100644 --- a/src/remote/activitypub/kernel/accept/index.ts +++ b/src/remote/activitypub/kernel/accept/index.ts @@ -1,12 +1,12 @@ import Resolver from '../../resolver'; import { IRemoteUser } from '../../../../models/entities/user'; import acceptFollow from './follow'; -import { IAccept, IFollow } from '../../type'; +import { IAccept, isFollow, getApType } from '../../type'; import { apLogger } from '../../logger'; const logger = apLogger; -export default async (actor: IRemoteUser, activity: IAccept): Promise<void> => { +export default async (actor: IRemoteUser, activity: IAccept): Promise<string> => { const uri = activity.id || activity; logger.info(`Accept: ${uri}`); @@ -18,13 +18,7 @@ export default async (actor: IRemoteUser, activity: IAccept): Promise<void> => { throw e; }); - switch (object.type) { - case 'Follow': - acceptFollow(actor, object as IFollow); - break; + if (isFollow(object)) return await acceptFollow(actor, object); - default: - logger.warn(`Unknown accept type: ${object.type}`); - break; - } + return `skip: Unknown Accept type: ${getApType(object)}`; }; diff --git a/src/remote/activitypub/kernel/create/index.ts b/src/remote/activitypub/kernel/create/index.ts index 108cfedf41..f1a3ebff43 100644 --- a/src/remote/activitypub/kernel/create/index.ts +++ b/src/remote/activitypub/kernel/create/index.ts @@ -1,7 +1,7 @@ import Resolver from '../../resolver'; import { IRemoteUser } from '../../../../models/entities/user'; import createNote from './note'; -import { ICreate, getApId, validPost } from '../../type'; +import { ICreate, getApId, isPost, getApType } from '../../type'; import { apLogger } from '../../logger'; import { toArray, concat, unique } from '../../../../prelude/array'; @@ -35,9 +35,9 @@ export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => { throw e; }); - if (validPost.includes(object.type)) { + if (isPost(object)) { createNote(resolver, actor, object, false, activity); } else { - logger.warn(`Unknown type: ${object.type}`); + logger.warn(`Unknown type: ${getApType(object)}`); } }; diff --git a/src/remote/activitypub/kernel/reject/index.ts b/src/remote/activitypub/kernel/reject/index.ts index 96e9aadf5d..d7a80fce7b 100644 --- a/src/remote/activitypub/kernel/reject/index.ts +++ b/src/remote/activitypub/kernel/reject/index.ts @@ -1,12 +1,12 @@ import Resolver from '../../resolver'; import { IRemoteUser } from '../../../../models/entities/user'; import rejectFollow from './follow'; -import { IReject, IFollow } from '../../type'; +import { IReject, isFollow, getApType } from '../../type'; import { apLogger } from '../../logger'; const logger = apLogger; -export default async (actor: IRemoteUser, activity: IReject): Promise<void> => { +export default async (actor: IRemoteUser, activity: IReject): Promise<string> => { const uri = activity.id || activity; logger.info(`Reject: ${uri}`); @@ -18,13 +18,7 @@ export default async (actor: IRemoteUser, activity: IReject): Promise<void> => { throw e; }); - switch (object.type) { - case 'Follow': - rejectFollow(actor, object as IFollow); - break; + if (isFollow(object)) return await rejectFollow(actor, object); - default: - logger.warn(`Unknown reject type: ${object.type}`); - break; - } + return `skip: Unknown Reject type: ${getApType(object)}`; }; diff --git a/src/remote/activitypub/kernel/undo/announce.ts b/src/remote/activitypub/kernel/undo/announce.ts index 38ce5b6c59..e08fea188d 100644 --- a/src/remote/activitypub/kernel/undo/announce.ts +++ b/src/remote/activitypub/kernel/undo/announce.ts @@ -3,14 +3,15 @@ import { IRemoteUser } from '../../../../models/entities/user'; import { IAnnounce, getApId } from '../../type'; import deleteNote from '../../../../services/note/delete'; -export const undoAnnounce = async (actor: IRemoteUser, activity: IAnnounce): Promise<void> => { +export const undoAnnounce = async (actor: IRemoteUser, activity: IAnnounce): Promise<string> => { const uri = getApId(activity); const note = await Notes.findOne({ uri }); - if (!note) return; + if (!note) return 'skip: no such Announce'; await deleteNote(actor, note); + return 'ok: deleted'; }; diff --git a/src/remote/activitypub/kernel/undo/index.ts b/src/remote/activitypub/kernel/undo/index.ts index 93909352d9..0bab3c9666 100644 --- a/src/remote/activitypub/kernel/undo/index.ts +++ b/src/remote/activitypub/kernel/undo/index.ts @@ -1,5 +1,5 @@ import { IRemoteUser } from '../../../../models/entities/user'; -import { IUndo, IFollow, IBlock, ILike, IAnnounce } from '../../type'; +import { IUndo, isFollow, isBlock, isLike, isAnnounce, getApType } from '../../type'; import unfollow from './follow'; import unblock from './block'; import undoLike from './like'; @@ -9,7 +9,7 @@ import { apLogger } from '../../logger'; const logger = apLogger; -export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => { +export default async (actor: IRemoteUser, activity: IUndo): Promise<string> => { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -25,20 +25,10 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => { throw e; }); - switch (object.type) { - case 'Follow': - unfollow(actor, object as IFollow); - break; - case 'Block': - unblock(actor, object as IBlock); - break; - case 'Like': - case 'EmojiReaction': - case 'EmojiReact': - undoLike(actor, object as ILike); - break; - case 'Announce': - undoAnnounce(actor, object as IAnnounce); - break; - } + if (isFollow(object)) return await unfollow(actor, object); + if (isBlock(object)) return await unblock(actor, object); + if (isLike(object)) return await undoLike(actor, object); + if (isAnnounce(object)) return await undoAnnounce(actor, object); + + return `skip: unknown object type ${getApType(object)}`; }; diff --git a/src/remote/activitypub/kernel/update/index.ts b/src/remote/activitypub/kernel/update/index.ts index ea7e6a063e..6dd3e5f296 100644 --- a/src/remote/activitypub/kernel/update/index.ts +++ b/src/remote/activitypub/kernel/update/index.ts @@ -1,5 +1,5 @@ import { IRemoteUser } from '../../../../models/entities/user'; -import { IUpdate, validActor } from '../../type'; +import { getApType, IUpdate, isActor } from '../../type'; import { apLogger } from '../../logger'; import { updateQuestion } from '../../models/question'; import Resolver from '../../resolver'; @@ -22,13 +22,13 @@ export default async (actor: IRemoteUser, activity: IUpdate): Promise<string> => throw e; }); - if (validActor.includes(object.type)) { + if (isActor(object)) { await updatePerson(actor.uri!, resolver, object); return `ok: Person updated`; - } else if (object.type === 'Question') { + } else if (getApType(object) === 'Question') { await updateQuestion(object).catch(e => console.log(e)); return `ok: Question updated`; } else { - return `skip: Unknown type: ${object.type}`; + return `skip: Unknown type: ${getApType(object)}`; } }; diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts index 09e066708f..3b7452c3cb 100644 --- a/src/remote/activitypub/models/note.ts +++ b/src/remote/activitypub/models/note.ts @@ -17,7 +17,7 @@ import { deliverQuestionUpdate } from '../../../services/note/polls/update'; import { extractDbHost, toPuny } from '@/misc/convert-host'; import { Emojis, Polls, MessagingMessages } from '../../../models'; import { Note } from '../../../models/entities/note'; -import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji } from '../type'; +import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type'; import { Emoji } from '../../../models/entities/emoji'; import { genId } from '@/misc/gen-id'; import { fetchMeta } from '@/misc/fetch-meta'; @@ -36,8 +36,8 @@ export function validateNote(object: any, uri: string) { return new Error('invalid Note: object is null'); } - if (!validPost.includes(object.type)) { - return new Error(`invalid Note: invalid object type ${object.type}`); + if (!validPost.includes(getApType(object))) { + return new Error(`invalid Note: invalid object type ${getApType(object)}`); } if (object.id && extractDbHost(object.id) !== expectHost) { diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index 5b032d9d9c..1062fe2995 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -4,7 +4,7 @@ import * as promiseLimit from 'promise-limit'; import config from '@/config'; import Resolver from '../resolver'; import { resolveImage } from './image'; -import { isCollectionOrOrderedCollection, isCollection, IPerson, getApId, getOneApHrefNullable, IObject, isPropertyValue, IApPropertyValue } from '../type'; +import { isCollectionOrOrderedCollection, isCollection, IPerson, getApId, getOneApHrefNullable, IObject, isPropertyValue, IApPropertyValue, getApType } from '../type'; import { fromHtml } from '../../../mfm/from-html'; import { htmlToMfm } from '../misc/html-to-mfm'; import { resolveNote, extractEmojis } from './note'; @@ -137,7 +137,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32); - const isBot = object.type === 'Service'; + const isBot = getApType(object) === 'Service'; const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/); @@ -337,7 +337,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint emojis: emojiNames, name: person.name, tags, - isBot: object.type === 'Service', + isBot: getApType(object) === 'Service', isCat: (person as any).isCat === true, isLocked: !!person.manuallyApprovesFollowers, isExplorable: !!person.discoverable, @@ -476,7 +476,7 @@ export async function updateFeatured(userId: User['id']) { // Resolve and regist Notes const limit = promiseLimit<Note | null>(2); const featuredNotes = await Promise.all(items - .filter(item => item.type === 'Note') + .filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも .slice(0, 5) .map(item => limit(() => resolveNote(item, resolver)))); diff --git a/src/remote/activitypub/type.ts b/src/remote/activitypub/type.ts index db866ae67a..98025da908 100644 --- a/src/remote/activitypub/type.ts +++ b/src/remote/activitypub/type.ts @@ -3,7 +3,7 @@ export type ApObject = IObject | string | (IObject | string)[]; export interface IObject { '@context': string | obj | obj[]; - type: string; + type: string | unknown[]; id?: string; summary?: string; published?: string; @@ -51,6 +51,15 @@ export function getApId(value: string | IObject): string { throw new Error(`cannot detemine id`); } +/** + * Get ActivityStreams Object type + */ +export function getApType(value: IObject): string { + if (typeof value.type === 'string') return value.type; + if (Array.isArray(value.type) && typeof value.type[0] === 'string') return value.type[0]; + throw new Error(`cannot detect type`); +} + export function getOneApHrefNullable(value: ApObject | undefined): string | undefined { const firstOne = Array.isArray(value) ? value[0] : value; return getApHrefNullable(firstOne); @@ -92,6 +101,9 @@ export interface IOrderedCollection extends IObject { export const validPost = ['Note', 'Question', 'Article', 'Audio', 'Document', 'Image', 'Page', 'Video', 'Event']; +export const isPost = (object: IObject): object is IPost => + validPost.includes(getApType(object)); + export interface IPost extends IObject { type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event'; _misskey_content?: string; @@ -112,7 +124,7 @@ export interface IQuestion extends IObject { } export const isQuestion = (object: IObject): object is IQuestion => - object.type === 'Note' || object.type === 'Question'; + getApType(object) === 'Note' || getApType(object) === 'Question'; interface IQuestionChoice { name?: string; @@ -126,10 +138,13 @@ export interface ITombstone extends IObject { } export const isTombstone = (object: IObject): object is ITombstone => - object.type === 'Tombstone'; + getApType(object) === 'Tombstone'; export const validActor = ['Person', 'Service', 'Group', 'Organization', 'Application']; +export const isActor = (object: IObject): object is IPerson => + validActor.includes(getApType(object)); + export interface IPerson extends IObject { type: 'Person' | 'Service' | 'Organization' | 'Group' | 'Application'; name?: string; @@ -154,10 +169,10 @@ export interface IPerson extends IObject { } export const isCollection = (object: IObject): object is ICollection => - object.type === 'Collection'; + getApType(object) === 'Collection'; export const isOrderedCollection = (object: IObject): object is IOrderedCollection => - object.type === 'OrderedCollection'; + getApType(object) === 'OrderedCollection'; export const isCollectionOrOrderedCollection = (object: IObject): object is ICollection | IOrderedCollection => isCollection(object) || isOrderedCollection(object); @@ -171,7 +186,7 @@ export interface IApPropertyValue extends IObject { export const isPropertyValue = (object: IObject): object is IApPropertyValue => object && - object.type === 'PropertyValue' && + getApType(object) === 'PropertyValue' && typeof object.name === 'string' && typeof (object as any).value === 'string'; @@ -181,7 +196,7 @@ export interface IApMention extends IObject { } export const isMention = (object: IObject): object is IApMention=> - object.type === 'Mention' && + getApType(object) === 'Mention' && typeof object.href === 'string'; export interface IApHashtag extends IObject { @@ -190,7 +205,7 @@ export interface IApHashtag extends IObject { } export const isHashtag = (object: IObject): object is IApHashtag => - object.type === 'Hashtag' && + getApType(object) === 'Hashtag' && typeof object.name === 'string'; export interface IApEmoji extends IObject { @@ -199,7 +214,7 @@ export interface IApEmoji extends IObject { } export const isEmoji = (object: IObject): object is IApEmoji => - object.type === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null; + getApType(object) === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null; export interface ICreate extends IActivity { type: 'Create'; @@ -258,17 +273,17 @@ export interface IFlag extends IActivity { type: 'Flag'; } -export const isCreate = (object: IObject): object is ICreate => object.type === 'Create'; -export const isDelete = (object: IObject): object is IDelete => object.type === 'Delete'; -export const isUpdate = (object: IObject): object is IUpdate => object.type === 'Update'; -export const isRead = (object: IObject): object is IRead => object.type === 'Read'; -export const isUndo = (object: IObject): object is IUndo => object.type === 'Undo'; -export const isFollow = (object: IObject): object is IFollow => object.type === 'Follow'; -export const isAccept = (object: IObject): object is IAccept => object.type === 'Accept'; -export const isReject = (object: IObject): object is IReject => object.type === 'Reject'; -export const isAdd = (object: IObject): object is IAdd => object.type === 'Add'; -export const isRemove = (object: IObject): object is IRemove => object.type === 'Remove'; -export const isLike = (object: IObject): object is ILike => object.type === 'Like' || object.type === 'EmojiReaction' || object.type === 'EmojiReact'; -export const isAnnounce = (object: IObject): object is IAnnounce => object.type === 'Announce'; -export const isBlock = (object: IObject): object is IBlock => object.type === 'Block'; -export const isFlag = (object: IObject): object is IFlag => object.type === 'Flag'; +export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create'; +export const isDelete = (object: IObject): object is IDelete => getApType(object) === 'Delete'; +export const isUpdate = (object: IObject): object is IUpdate => getApType(object) === 'Update'; +export const isRead = (object: IObject): object is IRead => getApType(object) === 'Read'; +export const isUndo = (object: IObject): object is IUndo => getApType(object) === 'Undo'; +export const isFollow = (object: IObject): object is IFollow => getApType(object) === 'Follow'; +export const isAccept = (object: IObject): object is IAccept => getApType(object) === 'Accept'; +export const isReject = (object: IObject): object is IReject => getApType(object) === 'Reject'; +export const isAdd = (object: IObject): object is IAdd => getApType(object) === 'Add'; +export const isRemove = (object: IObject): object is IRemove => getApType(object) === 'Remove'; +export const isLike = (object: IObject): object is ILike => getApType(object) === 'Like' || getApType(object) === 'EmojiReaction' || getApType(object) === 'EmojiReact'; +export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce'; +export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block'; +export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag'; diff --git a/src/server/api/endpoints/ap/show.ts b/src/server/api/endpoints/ap/show.ts index 2ce11160e8..b4df1ad4d7 100644 --- a/src/server/api/endpoints/ap/show.ts +++ b/src/server/api/endpoints/ap/show.ts @@ -10,7 +10,7 @@ import { Users, Notes } from '../../../../models'; import { Note } from '../../../../models/entities/note'; import { User } from '../../../../models/entities/user'; import { fetchMeta } from '@/misc/fetch-meta'; -import { validActor, validPost } from '../../../../remote/activitypub/type'; +import { isActor, isPost, getApId } from '../../../../remote/activitypub/type'; export const meta = { tags: ['federation'], @@ -154,16 +154,16 @@ async function fetchAny(uri: string) { } // それでもみつからなければ新規であるため登録 - if (validActor.includes(object.type)) { - const user = await createPerson(object.id); + if (isActor(object)) { + const user = await createPerson(getApId(object)); return { type: 'User', object: await Users.pack(user, null, { detail: true }) }; } - if (validPost.includes(object.type)) { - const note = await createNote(object.id, undefined, true); + if (isPost(object)) { + const note = await createNote(getApId(object), undefined, true); return { type: 'Note', object: await Notes.pack(note!, null, { detail: true }) From 3e7a87f75afdfe8001ecd3354c5170d9e57f9f98 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 31 May 2021 13:06:25 +0900 Subject: [PATCH 28/28] 12.82.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ca33644052..68f9bd75bb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo <syuilotan@yahoo.co.jp>", - "version": "12.81.2", + "version": "12.82.0", "codename": "indigo", "repository": { "type": "git",