From 37bd06e14271aa3ff69389939f27d7814479234d Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sat, 22 Jun 2024 20:41:43 +0200 Subject: [PATCH 01/14] Add local image proxy using sharp/jimp pkgs --- package-lock.json | Bin 300713 -> 326795 bytes package.json | 1 + src/api/Server.ts | 4 +- src/api/middlewares/ImageProxy.ts | 143 ++++++++++++++++++++++++++++++ src/api/middlewares/index.ts | 1 + 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/api/middlewares/ImageProxy.ts diff --git a/package-lock.json b/package-lock.json index 875aba1b5a898e35da28cc0a36d5a6278844ed5b..32582d06dfdf1235d7dd44174db10c86f3f94aad 100644 GIT binary patch delta 14842 zcmc(Gd8{kvbstDxNwzFmlC?a^vgP+A%dX4ZH!Ej`c4~{mZ8+RHoQ+Lw4`(N5<18Ew zTU$}mC5c_f%5^+{G^nGZEo#I~V>`u5iWab&7C{iei3&RbTC}!XplE`&h|r>oQM;WX z@4e)eL%{~e$DpAY|9|K#jrC&5?0d-BI84=zmG zd-{XdpZ+L#6NYa>@K=B9t(_O|&L)Amoz~igYeg77d*Dm(!#QC#=K-hr|I?g2E&NvD zX;xA@7)muj$MaoJDc2@A*00O!^+b}&V|j^LZhgprNPW57w5dS_@DT)CP5TvU#41#` z-x#*HtECfhqZ(0OcA*eNwh)}w{!XcK`rPhIqtLX1V;-OWv3uW|{XL!YvA9@)&c2>J zb@t$=?!C5ummCG5Yv*tK@Sg*3A~!2%&@Vl8k_(a6e*EO|lU#sYut#^_A-lesNtQnj zqHz{DnSjc0lx+K5ZNc(Tq|UdI+G;k!`fyRz2AG#jMV=q_0ggdQ*%ZbQ$N6+Xctd2F z$Zc`9apXzYsJn?iANYjYoQ-R#^z9=0zB|Y!Imzi<3ep$#lZT(5N1CSkI~7!J!Z*PS z1*jWSJM|<#I9~*BLfIBxtZJsEo`#91?%oJtH!C;Mi#0QFZP)hoR@&7Ypw%Rc_*GpFPBa#_OGc5&@Vd?fPV*x+sxU{V~#) z*ex$0UCtsUl*Rjr0K$~QEx;~bM|g=u77^aM_44V-`wHowc!hVRPyI^a11Fg#kbKME zl5cr#4C$|x3XffG`xE z1u^QZS}O!;y1p(_96+rSo6>}&+FqD-8XtDbxW#WUM{EtZ8P}+J zI}M(nzMSEEa)xzNOQG(`wZhTi%jPmpv;RkICK%b8(z>7rUjMH0nuEZCwGc>ruAXEo-Ljm26(fYzBD;`|6@n`&XOeH+rExcz0~Ko-)Ti~0V1yzF4QQ1dl$G(ik_<#u8Gt5kicwh;n(idf zB%0XFwhOm5poqW;aGx8Y?Ix}Ohs^=+Ao`A*OUHxWMDHZw+w(2;tVlPV)464yN#8rj z`OHo+hs7`x_h@t^^(L^k@XT$^hNGTHG-s$%hpRS(5oFldR4WavOMug@&YFb2n&LeX zs|V78AA+sgrV&$H%m7u_rutga7Qi43JkpL-s~Q4Tx;AImlGKRIwU_Ww$8L57Zd)0# zvlt%Cy@;U}aS+U>-f%{=AjG5vG)h1I?PBFmE7k)?dnTK{oI=>i6Z_-%{^Kpn*Q`8t zoNwBGd@LHz2^%OeqeW#g3;X6`#cFGANR#o<)LIT$g$Or7wa}$>RhKIg#q$+qHWx>W zb-k+fy!94$8OsX?7R+pXXdAoQV!At`e_GM*J&2aR0G_<_^2|MZMX=nzAIX&u4`Okf zj%`ev71Cpfja0U418ji?{RuCzI@{>EQkybp!>gBloauI(zSx+6{Fp z+XmP|x)Ri58L2P$4nFJbVeRq}gQxNN9k2VCb>vJh9PnO!M^5BUG`<>`+Z{|ecgwH) zNr5a`d!oA!i0at2$?p2>%m4ZiV_#)XYxRmbWy6Cyxh)t2(1rrmz|xs5z3E_6ZW0o% zIZ`l&W2#-*s#sESd$3|q5F2TONFwy!Sg_Uvz*n#`>d?^KHy8y*5Q?PhE(npr-kffG zOx}zt)`vLj>(a^sD^UCjp|mUJu>n{w*HNNR?>s_4D#%4X`++LB4`M{=ac=T0#L z&yv!w|Ao@QO3KFvZ%-f}`O}M;gN)qDoAe2KV#*t>i%Fx=synM;<@T10*^U{Slr)RM zu}@>Ds4Gah7EEQF6#O!RFX0V7mXrG0?g}DmNUYCv0y5H32C2URTe%H}IgS>J85&p4 z!IgNCuO1(S{rDgqyAbuZ!YEAFf>^dGIRa55Dr>YnX6^E*Nvx?>Y)Q7%T@uxxJQBt# zu5wL89jMGqKuxE)XAhfcCVco)Dc*Fd4=!yAIeMws!uzCjnM4|5356) z?=ZlE_C}H_Wqd5G&05|r^iC&z<&TQkZO{2__U_n)X_omz&t0~5bnf!eK`PdUE6~LC z@b-YLR<*}3vE@Lv96ecd=m6jZYU;MfShKxWhs=~2O9I>p%&Jzk>W$teESnD5 znd6Qn^?Dm;O$o#s3hqN29Y>*2m!cprr7N-V$`L&$I0}vzGe_zO zNNv7#hc=D1W;HDdHh?wH7;EE40zLN~$!Ml8f2;7sCG5F!K20YJOh+JmKRyy|P{#atqmX5M zSf5}c(OgJ1z%;jl;40o`*j`V8z*YcjqYA+cP;HO0Tdfuiv?M_m4X8S`K~!lsveBRg zD1(Y)W97{o-09m`@!O8%OTT-wbf|D#2_Ci-EDymWA00&F&>BC}%MI5KJ+=l`tBVOo zbec0(bD+4l#>$Rrqk{=u$9n()uQ%IXT^G93&3dpV<$+BvrqzUjdbOHu(rtzB3T+Gl z20xl(J&qoZ>)Vo*e(fv8YnOX>dK^9W1R0b$NO(DQBqqqXps#Mm=Gx(P$B<+Jm9=Fb zhG9fhnj43nP7_Q4F@C<(X3L6XQS653_mQNTf$#bNZ2CjoO;$~Q;xw1-dKO`zR`{cd zdF+EcKeNxLPYx0C3XaNFog?$y4G*%hGCp+-%G%fj-k?>+iG4sA_7*e?#D;5jMG^O5 zh?z%p#fhjgF5yIzheCBIEh>7`hhwp+;Nx)FTD2`ZOB6*wl&$1oo#-8)x#ddA>D<;b ze4Qhuk3u)mo6s(U{NS5#Qdy3?d;3w5;Qk}?_SN+3A1NFR{L1;=W7&fN?8iqMLI%ln zWR2!ir`>II<`Suo5o+GVVqsphTH1!80AK=v@FTgVpc&%isMgY^Rsgr! zT9@SXIPS4gca>z2dfsTao565Jh4aXm2L{*P3}C)LlNzXQR5}fQJt?cUZI4ELTbIOv z7()6RFzq)3qhGbVubXSUS*U&2Wr_D#nOVkil(t_vM!RI@pvc|uphs5Gji3sz`dHtf z79izw@TwMfNByNW4i^K@74;ffMUz1E0wJ)B$jn*{Ddtb8baO{FoA$i0pzDRK?@g#F;pArZ-|UMKP*4LM(peMdK|U>ZVCBZu`*c%od(T}7EuR=M1@mVvyGUk{ot zce}WCyU&uJ&r4xqtvP)Cp;Q+{mk98VlshbSM=D=^{-RoT zTWdOGTDK*;tg79fYUiELd28-&YG2m?F5S}Wi4mIi?m{#}W!O0?yG=rI*Um?tz4VU{ zKy-fz>^b?BO!oiiu=jlrQ+$M-I%TQXCBg*A3K-4awmb}#Ewo0Zny}?6s|vz48n8bn zD+{;F(Lmi05eOv9>SR0EblmlvZ9!emXM+rf6Vwh=ePC~Auep}E5I^JN{3<5>I#Md1 z7Xe;V8_m7`PNFa^yyXa9?XQ_aQ_Jtx^2d=-O{f?44Rn&wfm^R>wIw;>7%`bOreSYY z^{PGAG{q6P9)&`(5m3?;NmewCX^$bT`dF_xEm%c|-dM+(k>68j42$r^PN)YmaWr_h zNBO<#9wFamFZTD=&;5Mi$@A;@YdiOwU+(74clSIn&c$(8+)k9H)r9(_(3&*Y_0X*Z zShZ?yThxGC*Lv(4ueOE3JQ#8-E4GFki>HZ3lnmP)!R?Jung?tI6q9HgYAmhyC;dGN zJomlX)%)vN*nY1rZvS4_iRPDQKm4uPZvODae0%nn(XaaCH)q&;Wxo7i@lXNSt8-^# znL2mRgF4xSuE;#QblQ$-k*g{`6NX9F(m)vjHB4&Bf{I0r+O+3P(x1A9KJH)vmo$m) zNYC!w2#VqXr{5Kp#MEcvS`&x1Ar4`Ooa*eZPd-^fZtiZY@68Y0o{#hg|Ez=;ko1MW zTYysSpO;QE8tClC^Viv>2-Rks4^O0WSoTa#qEUS#wWNtU<0urYbq6la)&-0&E6td6 z0oa|<+m#j#pjB@LxjnSvRxo)aO@(GJ_NNBX@^n3Y;R^-etp7{*ef6=wapOBFyevI_ z_T1-A(ruJ17#_#mQ|#&UT{5a-jq9p zodhVmh{#0vD!bO}Ge!6t?)~|h` z@K4{C{?xSiNcum1yNJAd--~got|H0GIoY*l?1!gd`pTyY&u|l+;joUwjjO}PNa^u( zg{`f~6-^Fx1P2FOY&H%jTQAIReikyZ2AQymM>IMT>)2o$t~!kD*91xH_7QtF>L)s* zLi#=ldn$eI?I%yB-(D1Mq+j^e!acX_Unw+ZXTSVAK>D4#icg*W)o)*C=Wzn|+kB-m zZ=(>m+H4YU7-R+I_No^PqYj|Ca;#6qdaFNISd*-6dV00q+O|O=*uv6kyL4hxz^SDL z&d1HcLMO}~nWTkp7M@MPZRy>&{^8xl@85mbX)IaGRB9Bio&Dwez(*C=-pynhxpV8X5!m(NPmiQ2Gm@Z~9ijb9dS8U)7Dm}h|v(tqMw^nh5 zgT>y0^r)uGRs{=CS$^O()Y;lihHLnIbN<2o#r21ZrgnDyzdV^fxH>sGd+=VPrmnD@86k-Z&9L!ZzRP!19T3MTCYC$rOg+;ne)c1G?b;)DwdCyhuD()$9zE}v z$XGgmwP)u`Sq~MAW8fTGdo(q$d)pZh&8OkOX>KZf%^LtCk*C+fT4%~(4^s8%G&H27 zC3eQ+fe9g4tJiE~%^RJhDZ)*;?R7fS2BwPa4lBRkv?X0VU0ueS%jTi0Z5n6a{hRN( z^_8C~{*}_s*3W+CXOR^AO!4G_ot5jBJD04}tkvOc9Nb{zHPEZMlrm`|q&jVHD@dy* z*+$zM^$aTrx@aW|>~?EvbWBuOIi$Vljv*{6PX_Z5&aX{=Jbx%9n;YY3BYjL7 z@JRZvh=gxBGwhHZzJ`VuyKhk69&DSg%h9^x}xhs{JP6>ZF1Z#L#F2Ad-wU9Unzd=?I&8~ z$muKpzVLYZ`*#;l(*N|0BJhM6oaWP^>E=cJqfvU}tHozoE5;kF2THsk>g76FAC&3t zQf`9dEZ`(Fhgmx_(u(yBMUIVCSn*~9Eq8L$o22aJ z;SRX9{(A9aZ@Kr-e3m{Bm)?2y%Qui`4s-j4v>jttd(=MvtEFeT)tU`s8d+egIA|e@ z-ex(&;Z1rlcZ3*HHyxnG&ZKF<2 zS2|FM_t54n{N;z=ec8nn8&VkSSGwcSd~Zt`@;bUAf_5F08MiqgJHiZ;tl2{n8;-Wz zbm;_A;-0qvaCx09Hl}QCSfqtc{SH8*3)p8s$6R4zgYCN`r(y&|yWtJX!&%$Ky9rpq z>ywaIgT&Bw*m1T8SqFrByTsw*q`XeXumg=(!PeRT{=|(P5|JKw*Ihe=`Tqli-1^q< z6yZWC!-;w1FJ?jhPqK+e@)oc~j&{%6uQFw$XR9yXVENfrZ48^?bVzp5z7j}2*jY8% zO3?R_HN(M^)w0&vvP}*qDTmd%?Iei#)~FSCD^0cGR*^2!at(%>_iBp;7(o11DJ%Ok zbocKTAHR0bc}aU$lu!TUKNe2X|NN~Y@Z?dgJLBqTbPAn8zwiu4u*ujJZDrC(2#EBz znAEGWhBnJES7pnAJtUN-W7NpH7j0~AMzP6fT}z9RLDHK!JEm*I$X~8$0B<@gJ6^&; zA3_6nZ>s6n{-Ai@^ZDjw9h-;FJKMC+n?19J*UMhdU4!U(QT|j4WYgU3I+VQ=xy+*S zCejOuLa!FKqU83xU!8#|&iu{)(hPkhJz&EQ0zBb0b)R48V=9B67f1&i`*^h~j z-Fnx*E&lrTyY~)1eSt4tD`oEfOxi-%kK;h5-Q(eD9um^;{)f`jQ$B%6#6~m>n~z2| zTQQUx+L)jt5*}&|7AUtFB&m^jXY6j9NVpOtZVhLFT(=~=-)u9Jb+uCN^z31@HhRlp z7mMkzegNhVUO<_hoH=$FKUR9rt#1@dj&Rqm$uWKC7YmP{-S`Cf=suceobF$o=GukM zYW?e6uTM|IwJrlrqzem>lYKwHh`s|iARpXn7y@#Ga20wTsMQ?eYI#^~jZl3RRBN(B zN=#FAsm{oTi3nTiqY3CHO|Q+ILyZsp`O@beEiT<1AL&z%6v4N7($dY?$RB6Vf8l)` zYV=5Al%U(0tX2XvQL4h$^;R*r29ox`;2_c>22;Dff^ZC>)Gl0Z#@uvWw$!%TgOG+} zz_B)8xiL2q>YX9DMMFv4eLZmN8(wK$xbMPja??s5`fJ5U_o3!r{-YxBf!EsJj<+LY z+Zp63lz#cwi_fquI`O%HZPhKcGbDpfTO9Vg3#U4g)Rjaofh!?-@d) z5qa^{#eIRR=~A!Lb~&msW2zod5$4QF#z$H?#8f6cPwJr zG||yqZ(=MolOlkMJ9vf4m3}KTke|O+G|aP?|Le8%rH_}O2eR(?U6Vuh$XrhK?8VRB z;Og6!UjN zIlRv7r>;xkbt4SD;DZ1V$MH?avlqcl*|mZ5_gvRp|9aNpuUUE5InW1_}o&mgPpOpWbKYMcH7X9hc zZxo7qe$wy!T!y%ve^&(V-*IxmQF`MKO3$*>$#xsHCKJ2qGCbgAWzVc_VyKT~7w-6$ zWR&OaR$U!vw6NKT?U;#iF|G}3vVsdR(FN?vd_nTvAZr2dCT?G&=7}dI>5aFae6X1P zeCEMV7jLAW{cP#2x99I%7zS(>vYngzcv+JLL9zRc_a(?#dRGx@tE@Kz2WTmZY zfa3ElH&Gf89rQCcH39-bXJ(b#({duoS@ztNRi&a;CyOPzE%RZEosv;?WDL#f0$6A^ zIf_8L-xSffqtsf&4z=ux7w<~H{7UJ@ttb9gDLc|Pf-E^wnbk@39>yUhX zQ(egu!=+?hra^tp;>-ZDL>8V25u;fkI%n#Yp|@^Ot%R;GlmX@1BVn@`TXmAfaFNH* z(N@F(uG6IO7M%Y4qHrzkeXI1*TMz$X=?~>%hTgwiDm+?(&VQP6e_lMyRFOY2*Pj1E zh3hP4a<$uW!$>hV#Jn$&d{)KiM=J~^eVT6tP#NVwONjaQcotLTUe=_CD{f6RvIZGh z8aAU~GO7YPw%{6LdqxPuCFJZw_PvMZ<+GO_cr5)%;sk!|^4BywJ}!P*lRo!@;96*V}0;U|iZr9iU?J-91OXKd`AS;D_7u(4%# z>5$bH<&Dz=&u`Cx$|cmy^7*IVee0jBPk!^>^e5rc6L%rmO$D63_`(VH z&fV{nGR}?@v+QtZ>$%;s&6Q!cSWJ+RmW;%^q4ii+4RsD=k+uV+oLdOKP1gDRx0AzE)vDmKUK zP=97y2D#2|Ll<%|R3=Me;&-Bj8}bP)*;?XzWxgZtasFUmc**ti$*lNt2&zX(`$2h^ zApAIT?v``lodx7e;Qf}1*qs``UAmF}{)bPH(~BwfIGpEVzFwG5YrmCM3Yxy$jp=Et zf<~D)8}x-0EM#d{Cn1O}z19YB!&cn!I>EDk6uKOaCM=dHd^GG1`iW)_7fM2or`h#{ ziGcwqf=to@+u%;;Cpl`!oqGC#VG(&KAMITF{zX<=gq|us!zuo#yq zjPAp*?L{qW)m7#UfmF=e*k|iF(t}$SemQqL69q#g6M#h>>p+XGsf}7)3LZ?mWm(g$ z<^DGa*t45lad~3_cF=nE?eoaAGTgG;5eQ15dyC~X_x$&qucaVx@_6RezWd)x@Dm66 zy&lisoVgijbMjtJYgj=CShkuQT<@*tsw{(mYxLR`P>b}jNB9Y~?N1}Vx@IJ(O@o5S xcO4^OH@Iu6NV`gu{R)=lkR6spmqv)f6NFwLTH!Y7r|JPQT9@$61TU8dMFJj|)K>x^R#jLwb4PV; zc6M=7VOVEmOGj06XIfWrV^w)cSXwW3T2yjuQdBQ#Z(?anXh%#idQ3EAGjDT9YBP0l zY;0FbODkzBb7^=)Q!g_^PgX@wMq+hvMtOIa!JPyolMwwKx7O#*W}w{407 zjz5>$w*(iLJ^ceSx0}lXIs>;Z&;p1iw;A~Yx(>JJ2m@RQx1SXQG6A>Q7Xud;xBf5# zvLLtpQUh#Qw{WZjlNGlF!vl2zw`9o!ix;H{7Wl}H2)m*w^Z z6qj%X0R)$j$N?|4Dg@Fomog>-F_$ee1QM5zCITW=F=9hgMN?KbPibT4jVb9Y%|D|AaXSu$dDX;n;WWHc*gZgDX+P*^K& zaZyA=H&14CH&<(SW0QetAPH=8buD9IV`ycQQP38X+}sV9%`yaD1Z{R@a+mM*0r!_Y yfr=F3%8c?1oa7rL%@On diff --git a/package.json b/package.json index ac42c767..7f466e44 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,7 @@ }, "optionalDependencies": { "erlpack": "^0.1.4", + "jimp": "^0.22.12", "mysql": "^2.18.1", "nodemailer-mailgun-transport": "^2.1.5", "nodemailer-mailjet-transport": "github:n0script22/nodemailer-mailjet-transport", diff --git a/src/api/Server.ts b/src/api/Server.ts index 472ab1d6..0f5df490 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -34,7 +34,7 @@ import "missing-native-js-functions"; import morgan from "morgan"; import path from "path"; import { red } from "picocolors"; -import { Authentication, CORS } from "./middlewares/"; +import { Authentication, CORS, ImageProxy } from "./middlewares/"; import { BodyParser } from "./middlewares/BodyParser"; import { ErrorHandler } from "./middlewares/ErrorHandler"; import { initRateLimits } from "./middlewares/RateLimit"; @@ -137,6 +137,8 @@ export class SpacebarServer extends Server { app.use("/api/v9", api); app.use("/api", api); // allow unversioned requests + app.use("/imageproxy/:hash/:size/:url", ImageProxy); + app.get("/", (req, res) => res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "index.html")), ); diff --git a/src/api/middlewares/ImageProxy.ts b/src/api/middlewares/ImageProxy.ts new file mode 100644 index 00000000..2fa97660 --- /dev/null +++ b/src/api/middlewares/ImageProxy.ts @@ -0,0 +1,143 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +import { Config } from "@spacebar/util"; +import { Request, Response } from "express"; +import { yellow } from "picocolors"; +import crypto from "crypto"; +import fetch from "node-fetch"; + +let sharp: undefined | false | { default: typeof import("sharp") } = undefined; +let Jimp: undefined | false | typeof import("jimp") = undefined; + +const sharpSupported = new Set([ + "image/jpeg", + "image/png", + "image/bmp", + "image/tiff", + "image/gif", + "image/webp", + "image/avif", + "image/svg+xml", +]); +const jimpSupported = new Set([ + "image/jpeg", + "image/png", + "image/bmp", + "image/tiff", + "image/gif", +]); +const resizeSupported = new Set([...sharpSupported, ...jimpSupported]); + +export async function ImageProxy(req: Request, res: Response) { + const path = req.originalUrl.split("/").slice(2); + + const secret = Config.get().security.requestSignature; + + // src/api/util/utility/EmbedHandlers.ts getProxyUrl + const hash = crypto + .createHmac("sha1", secret) + .update(path.slice(1).join("/")) + .digest("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_"); + + try { + if (!crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(path[0]))) throw new Error("Invalid signature"); + } catch { + console.log("Invalid signature, expected " + hash + " got " + path[0]); + res.status(403).send("Invalid signature"); + return; + } + + const abort = new AbortController(); + setTimeout(() => abort.abort(), 5000); + + const request = await fetch(path.slice(2).join("/"), { + headers: { + "User-Agent": "SpacebarImageProxy/1.0.0 (https://spacebar.chat)", + }, + signal: abort.signal, + }).catch((e) => { + if (e.name === "AbortError") res.status(504).send("Request timed out"); + else res.status(500).send("Unable to proxy origin: " + e.message); + }); + if (!request) return; + + if (request.status !== 200) { + res.status(request.status).send("Origin failed to respond: " + request.status + " " + request.statusText); + return; + } + + if (!request.headers.get("Content-Type") || !request.headers.get("Content-Length")) { + res.status(500).send("Origin did not provide a Content-Type or Content-Length header"); + return; + } + + // @ts-expect-error TS doesn't believe that the header cannot be null (it's checked for falsiness above) + if (parseInt(request.headers.get("Content-Length")) > 1024 * 1024 * 10) { + res.status(500).send("Origin provided a Content-Length header that is too large"); + return; + } + + // @ts-expect-error TS doesn't believe that the header cannot be null (it's checked for falsiness above) + let contentType: string = request.headers.get("Content-Type"); + + const arrayBuffer = await request.arrayBuffer(); + let resultBuffer = Buffer.from(arrayBuffer); + + if (/^\d+x\d+$/.test(path[1]) && resizeSupported.has(contentType)) { + if (sharp !== false) { + try { + sharp = await import("sharp"); + } catch (e) { + sharp = false; + } + } + if (sharp === false && Jimp !== false) { + try { + // @ts-expect-error Typings don't fit + Jimp = await import("jimp"); + } catch { + Jimp = false; + console.log(`[ImageProxy] ${yellow("Neither \"sharp\" or \"jimp\" NPM packages are installed, image resizing will be disabled")}`); + } + } + + const [width, height] = path[1].split("x").map((x) => parseInt(x)); + + const buffer = Buffer.from(arrayBuffer); + if (sharp && sharpSupported.has(contentType)) { + resultBuffer = await sharp.default(buffer) + // Sharp doesn't support "scaleToFit" + .resize(width) + .toBuffer(); + } else if (Jimp && jimpSupported.has(contentType)) { + resultBuffer = await Jimp.read(buffer).then((image) => { + contentType = image.getMIME(); + // @ts-expect-error Jimp is defined at this point + return image.scaleToFit(width, height).getBufferAsync(Jimp.AUTO); + }); + } + } + + res.header("Content-Type", contentType); + res.setHeader("Cache-Control", "public, max-age=" + (1000 * 60 * 60 * 24)); + + res.send(resultBuffer); +} diff --git a/src/api/middlewares/index.ts b/src/api/middlewares/index.ts index 6384e1aa..9fd617f6 100644 --- a/src/api/middlewares/index.ts +++ b/src/api/middlewares/index.ts @@ -21,3 +21,4 @@ export * from "./BodyParser"; export * from "./CORS"; export * from "./ErrorHandler"; export * from "./RateLimit"; +export * from "./ImageProxy"; From e90f8e88c0ae44e3183632cce300b07c5cc992f6 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sat, 22 Jun 2024 20:43:54 +0200 Subject: [PATCH 02/14] Run Prettier (tabs -> spaces???) --- src/api/middlewares/ImageProxy.ts | 62 ++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/api/middlewares/ImageProxy.ts b/src/api/middlewares/ImageProxy.ts index 2fa97660..64d5ddc1 100644 --- a/src/api/middlewares/ImageProxy.ts +++ b/src/api/middlewares/ImageProxy.ts @@ -1,19 +1,19 @@ /* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ import { Config } from "@spacebar/util"; @@ -58,7 +58,8 @@ export async function ImageProxy(req: Request, res: Response) { .replace(/\//g, "_"); try { - if (!crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(path[0]))) throw new Error("Invalid signature"); + if (!crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(path[0]))) + throw new Error("Invalid signature"); } catch { console.log("Invalid signature, expected " + hash + " got " + path[0]); res.status(403).send("Invalid signature"); @@ -80,18 +81,30 @@ export async function ImageProxy(req: Request, res: Response) { if (!request) return; if (request.status !== 200) { - res.status(request.status).send("Origin failed to respond: " + request.status + " " + request.statusText); + res.status(request.status).send( + "Origin failed to respond: " + + request.status + + " " + + request.statusText, + ); return; } - if (!request.headers.get("Content-Type") || !request.headers.get("Content-Length")) { - res.status(500).send("Origin did not provide a Content-Type or Content-Length header"); + if ( + !request.headers.get("Content-Type") || + !request.headers.get("Content-Length") + ) { + res.status(500).send( + "Origin did not provide a Content-Type or Content-Length header", + ); return; } // @ts-expect-error TS doesn't believe that the header cannot be null (it's checked for falsiness above) if (parseInt(request.headers.get("Content-Length")) > 1024 * 1024 * 10) { - res.status(500).send("Origin provided a Content-Length header that is too large"); + res.status(500).send( + "Origin provided a Content-Length header that is too large", + ); return; } @@ -115,7 +128,11 @@ export async function ImageProxy(req: Request, res: Response) { Jimp = await import("jimp"); } catch { Jimp = false; - console.log(`[ImageProxy] ${yellow("Neither \"sharp\" or \"jimp\" NPM packages are installed, image resizing will be disabled")}`); + console.log( + `[ImageProxy] ${yellow( + 'Neither "sharp" or "jimp" NPM packages are installed, image resizing will be disabled', + )}`, + ); } } @@ -123,7 +140,8 @@ export async function ImageProxy(req: Request, res: Response) { const buffer = Buffer.from(arrayBuffer); if (sharp && sharpSupported.has(contentType)) { - resultBuffer = await sharp.default(buffer) + resultBuffer = await sharp + .default(buffer) // Sharp doesn't support "scaleToFit" .resize(width) .toBuffer(); @@ -131,13 +149,15 @@ export async function ImageProxy(req: Request, res: Response) { resultBuffer = await Jimp.read(buffer).then((image) => { contentType = image.getMIME(); // @ts-expect-error Jimp is defined at this point - return image.scaleToFit(width, height).getBufferAsync(Jimp.AUTO); + return image + .scaleToFit(width, height) + .getBufferAsync(Jimp.AUTO); }); } } res.header("Content-Type", contentType); - res.setHeader("Cache-Control", "public, max-age=" + (1000 * 60 * 60 * 24)); + res.setHeader("Cache-Control", "public, max-age=" + 1000 * 60 * 60 * 24); res.send(resultBuffer); } From 93bb891d7915639a7e405e1553b3b0ad52d53175 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sat, 22 Jun 2024 20:50:11 +0200 Subject: [PATCH 03/14] Fix @ts-expect-error comment after Prettier --- src/api/middlewares/ImageProxy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/middlewares/ImageProxy.ts b/src/api/middlewares/ImageProxy.ts index 64d5ddc1..80a3adcb 100644 --- a/src/api/middlewares/ImageProxy.ts +++ b/src/api/middlewares/ImageProxy.ts @@ -148,9 +148,9 @@ export async function ImageProxy(req: Request, res: Response) { } else if (Jimp && jimpSupported.has(contentType)) { resultBuffer = await Jimp.read(buffer).then((image) => { contentType = image.getMIME(); - // @ts-expect-error Jimp is defined at this point return image .scaleToFit(width, height) + // @ts-expect-error Jimp is defined at this point .getBufferAsync(Jimp.AUTO); }); } From af6e15b9e5467293b8d95d8968429226a98d19f5 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sat, 22 Jun 2024 21:06:08 +0200 Subject: [PATCH 04/14] Prettier stuff -.- --- src/api/middlewares/ImageProxy.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/api/middlewares/ImageProxy.ts b/src/api/middlewares/ImageProxy.ts index 80a3adcb..f642ff27 100644 --- a/src/api/middlewares/ImageProxy.ts +++ b/src/api/middlewares/ImageProxy.ts @@ -148,10 +148,12 @@ export async function ImageProxy(req: Request, res: Response) { } else if (Jimp && jimpSupported.has(contentType)) { resultBuffer = await Jimp.read(buffer).then((image) => { contentType = image.getMIME(); - return image - .scaleToFit(width, height) - // @ts-expect-error Jimp is defined at this point - .getBufferAsync(Jimp.AUTO); + return ( + image + .scaleToFit(width, height) + // @ts-expect-error Jimp is defined at this point + .getBufferAsync(Jimp.AUTO) + ); }); } } From 16f8a1c7ac4eb1dafd571b5b3082f0df129fd39f Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:17:24 +0200 Subject: [PATCH 05/14] Add config value for cache duration --- package-lock.json | Bin 326795 -> 326819 bytes src/api/middlewares/ImageProxy.ts | 9 +++++---- src/util/config/types/CdnConfiguration.ts | 8 +++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 32582d06dfdf1235d7dd44174db10c86f3f94aad..a350da90ed542fefb4b45804b9d3ff14e07e5ea6 100644 GIT binary patch delta 36 pcmeDFDZKcn@P~k9m&KX`nA-!G8G)Dyh?%zsFtb?y1OWZu3@rcv delta 30 icmZ4dQ@H!5@P Date: Fri, 28 Jun 2024 12:13:17 +0200 Subject: [PATCH 07/14] Add meta section to nix package, fix nix update script writing to wrong variable in hashes.json --- flake.lock | Bin 1497 -> 1497 bytes flake.nix | 12 +++++++++++- hashes.json | 2 +- nix-update.sh | 4 ++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index ae5e8b23070de1f0618864a223c9a3c17f752ca5..77bf2b8620a5f7d1a37dc5bb4b8c1c26c3c997b4 100644 GIT binary patch delta 254 zcmcb~eUp1a2Zy17p^2G+vBAU%vf9B>raoq=j^-X-`gyt8RqpQQ`kukbIfbca`B5qU zQ6YZ$sl}O|dD#&Gfs^O6h);ZPp^;>mVwsp^ZeV6+WNBiSnr2{OYLH@LVUdz(m~3fa zXqjf5XlZV0lsNeSqw-`PX3dH34JV&rjNve}G%_`@FgFER>*}1BsqL9v9+6dA>`|O+ z8Jt>?Qc`G9p6XbrUm03iVi*$cnVK6>V(gs{v{rJmIkUjz^^9zj^O%M8i&D#!tQ3@t yEG&)FOwCeK4J-_d43pE+QY@3qlFU-llFbc(?BwJWQ^V9GGXsz_HuEr_WdZ;vC{N-5 delta 254 zcmcb~eUp1a2Zw>Fv4N$LvB|^k2LW`W7;8QCW1F$?P#rIsmKDJYp* ym>DMCnp&vTNr?xv6+YYEE52THBEy6 diff --git a/flake.nix b/flake.nix index 00a18f64..cc624004 100644 --- a/flake.nix +++ b/flake.nix @@ -13,11 +13,21 @@ inherit system; }; hashesFile = builtins.fromJSON (builtins.readFile ./hashes.json); + lib = pkgs.lib; in rec { packages.default = pkgs.buildNpmPackage { pname = "spacebar-server-ts"; - src = ./.; name = "spacebar-server-ts"; + + meta = with lib; { + description = "Spacebar server, a FOSS reimplementation of the Discord backend."; + homepage = "https://github.com/spacebarchat/server"; + license = licenses.agpl3Plus; + platforms = platforms.all; + mainProgram = "start-bundle"; + }; + + src = ./.; nativeBuildInputs = with pkgs; [ python3 ]; npmDepsHash = hashesFile.npmDepsHash; makeCacheWritable = true; diff --git a/hashes.json b/hashes.json index dd55b81d..bc319094 100644 --- a/hashes.json +++ b/hashes.json @@ -1,3 +1,3 @@ { - "npmDepsHash": "sha256-fZNDN2/fNy6Nu7tbr0RhQ8j4BP7X1Yhrh/fSTH7hbJc=" + "npmDepsHash": "sha256-RxGkjCU9qqqDMjhJ5aEq1w7c7lS4nAp0/3F0zASJQms=" } diff --git a/nix-update.sh b/nix-update.sh index 4413e6e0..a676e294 100755 --- a/nix-update.sh +++ b/nix-update.sh @@ -3,8 +3,8 @@ nix flake update DEPS_HASH=`prefetch-npm-deps package-lock.json` TMPFILE=$(mktemp) -jq '.npm_deps_hash = "'$DEPS_HASH'"' hashes.json > $TMPFILE +jq '.npmDepsHash = "'$DEPS_HASH'"' hashes.json > $TMPFILE mv -- "$TMPFILE" hashes.json nom build .# || exit $? -git add hashes.json flake.lock flake.nix \ No newline at end of file +git add hashes.json flake.lock flake.nix From e069db134f8f75c197fd6df72ee639d1d165f8fc Mon Sep 17 00:00:00 2001 From: "Emma [it/its]@Rory&" Date: Fri, 28 Jun 2024 12:17:35 +0200 Subject: [PATCH 08/14] Add hashes.json to .prettierignore as this is a generated file --- .prettierignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index 51116757..9531c159 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,5 @@ assets dist node_modules .github -.vscode \ No newline at end of file +.vscode +hashes.json From a987671e4a1249ae23914165c1b3edd0f29d9ffd Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Fri, 28 Jun 2024 12:43:53 +0200 Subject: [PATCH 09/14] "Fix" jimp import typings --- src/api/middlewares/ImageProxy.ts | 21 +++++++++++++++------ src/util/imports/Jimp.ts | 23 +++++++++++++++++++++++ src/util/imports/index.ts | 1 + 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 src/util/imports/Jimp.ts diff --git a/src/api/middlewares/ImageProxy.ts b/src/api/middlewares/ImageProxy.ts index 4c324afd..27c69ae2 100644 --- a/src/api/middlewares/ImageProxy.ts +++ b/src/api/middlewares/ImageProxy.ts @@ -16,14 +16,22 @@ along with this program. If not, see . */ -import { Config } from "@spacebar/util"; +import { Config, JimpType } from "@spacebar/util"; import { Request, Response } from "express"; import { yellow } from "picocolors"; import crypto from "crypto"; import fetch from "node-fetch"; let sharp: undefined | false | { default: typeof import("sharp") } = undefined; -let Jimp: undefined | false | typeof import("jimp") = undefined; + +let Jimp: JimpType | undefined = undefined; +try { + Jimp = require("jimp") as JimpType; +} catch { + // empty +} + +let sentImageProxyWarning = false; const sharpSupported = new Set([ "image/jpeg", @@ -112,20 +120,21 @@ export async function ImageProxy(req: Request, res: Response) { const arrayBuffer = await request.arrayBuffer(); let resultBuffer = Buffer.from(arrayBuffer); - if (/^\d+x\d+$/.test(path[1]) && resizeSupported.has(contentType)) { + if (!sentImageProxyWarning && resizeSupported.has(contentType) && /^\d+x\d+$/.test(path[1])) { if (sharp !== false) { try { sharp = await import("sharp"); - } catch (e) { + } catch { sharp = false; } } - if (sharp === false && Jimp !== false) { + + if (sharp === false && !Jimp) { try { // @ts-expect-error Typings don't fit Jimp = await import("jimp"); } catch { - Jimp = false; + sentImageProxyWarning = true; console.log( `[ImageProxy] ${yellow( 'Neither "sharp" or "jimp" NPM packages are installed, image resizing will be disabled', diff --git a/src/util/imports/Jimp.ts b/src/util/imports/Jimp.ts new file mode 100644 index 00000000..c1389e03 --- /dev/null +++ b/src/util/imports/Jimp.ts @@ -0,0 +1,23 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export type JimpType = { + read: (data: Buffer) => Promise; +}; diff --git a/src/util/imports/index.ts b/src/util/imports/index.ts index 08b870bc..4bc5a6c5 100644 --- a/src/util/imports/index.ts +++ b/src/util/imports/index.ts @@ -18,3 +18,4 @@ export * from "./OrmUtils"; export * from "./Erlpack"; +export * from "./Jimp"; From c135de9c866fd23b862155faf97c5704e9e0d8e6 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Fri, 28 Jun 2024 12:59:13 +0200 Subject: [PATCH 10/14] Fix style + nix? --- package-lock.json | Bin 326819 -> 326812 bytes src/api/middlewares/ImageProxy.ts | 8 ++++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a350da90ed542fefb4b45804b9d3ff14e07e5ea6..83d1852bdb4aa74638b2aeb672c241f9e5aa8fa1 100644 GIT binary patch delta 32 lcmZ4dQ+Uo#;SCYYj9SeJ% Date: Fri, 28 Jun 2024 13:05:03 +0200 Subject: [PATCH 11/14] Fix build by using ts-ignore --- package-lock.json | Bin 326812 -> 326819 bytes src/api/middlewares/ImageProxy.ts | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 83d1852bdb4aa74638b2aeb672c241f9e5aa8fa1..a350da90ed542fefb4b45804b9d3ff14e07e5ea6 100644 GIT binary patch delta 34 ncmbR9Q+V-D;SCYYlhZE?G$%5*Co(evF%u9oZ%<@qvHl4F^Jok~ delta 32 lcmZ4dQ+Uo#;SCYYj9SeJ% Date: Sun, 18 Aug 2024 18:59:09 +0200 Subject: [PATCH 12/14] update nix cache/deps/flake --- flake.lock | Bin 1497 -> 1497 bytes hashes.json | 2 +- nix-update.sh | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 29505fc365815eccadfee3b290d344f4e9d9dcd9..42952374c13a35769b490038fb91fbf81b588142 100644 GIT binary patch delta 134 zcmV;10D1q}3)u^>Dgp*JGdDOjG?9=UD{W4l4H)1$BGdN-~ oH(_OBWHMxBWn*SHVr4KkGd3_VFl06|VKQPllfVHZvkU{x0{+t|>i_@% delta 134 zcmcb~eUp2G789GXnUST5@x%$T+9_^PL1Dq^xn9|oz6MFz0R@SXK_%|`*+ITRA^An2 zo-SDyP8Iq7C59m;ljpNYPBv#2n7p2mZE_y7uzpc$nUa-)QlhCvQnE#2in+O^nW<^2 lxut=jp{bFHiK)4vxv52}MM|mxP}n$S@&QKW%{ $TMPFILE mv -- "$TMPFILE" hashes.json -nom build .# || exit $? +nom build .# --extra-experimental-features 'nix-command flakes' || exit $? git add hashes.json flake.lock flake.nix From 0cb0b9d2fe0c22d0fd575d715e1897807779679b Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sun, 18 Aug 2024 19:16:16 +0200 Subject: [PATCH 13/14] update deps so nix hashes.json is correct? --- hashes.json | 2 +- package-lock.json | Bin 361091 -> 361171 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hashes.json b/hashes.json index 6883eafd..ffba8089 100644 --- a/hashes.json +++ b/hashes.json @@ -1,3 +1,3 @@ { - "npmDepsHash": "sha256-q1Q7rpSzfiRvrkoDPER9wjBOzZ5Bn5B+d41MFssM7nU=" + "npmDepsHash": "sha256-9Nf/61L6pX6vrWWYPV7hy5/Cj3bWgTd6Ed8qNCQfuLs=" } diff --git a/package-lock.json b/package-lock.json index 9f77c3859ce342b6dc38a85bba1e410feab7abad..055d2a1a70a54c1f8dd2503630f9e17f9c4c50e3 100644 GIT binary patch delta 3012 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 delta 2954 zcmaKtNsQap8OK3sR!_#ucs!0X-ex9FJVRR|MNuR(iEXWvL`u{`Qj}5$mNcX`Y9XmT zE^H@A+8{{a^c{#^>iCc%IkXKhEer%rqHj)A+- zW9G_D+a54wgY9ky4k+8a9e1E0W5cW-1FzYPDa_$oJhNxgIu_02XY7X`S$yi`;&Yh8 z<3JYAn0MXw*@@>+hjm(a*Y4SuK;3=_a)B4^U)c!<0xpkaCN0y>1bK1fAoyZr!J;@Y z`uM#2zM*ZLZ8X>9t*KGOK{;IDjVI^ku?O}+OK$4*Ej!|{kPvWf^c+Ni+oQ8~(pu(E zqt9B4Vq=d(IFQCp0dagkcx~)}&4q$D$DG!~kH?m*MgL~K-~mU*k6WLTQ*#5C;!@XMM3oCnzW}Giy16FGzARuA6si^<;(b;-nuY zitb@PY(!!%)jv!c0^)W_y|A+?H?)Ld$Y_X z;Kmt;^%Oe01U8;wz>o0>kbPxpojW@+w%egSz{F{4ZFxJecZHwT7dkC)bvtaMaBRl$jBjLs~e;zSE{K$R+T(~v~e*e@2 z_^0o&-MAA#@Xu~%NUrI0NddhcZ#B{>HOaE1#QCHBPMi=TI?X8VN_(vu*4n{(z?IO9 zb|Wi$A~88AazVD0BP+FbwQEFlQHQm@8j;YXS}*y*f%+-1u_}UR19KMe&E?&x@r~$6 zsQJi4((B?Z_-kr$iF4`L7NBL%}$yNAB-%e)od|T zYv{2gnG!@93B(dXgsMv|(eg71E7TUViGsfdP(gpwCI zIt6QbFU3`KCmC;II8BraB8!JQS+|GiV*@0}=AE7%QgtHX7>vu>koNjAL@UiYxiA+m zXBaixD-m>UYfo8XO&QJ2YPGY6_RWIu<-O}yF1|hk#+g;nQm4R1a|~o#kAft##D`K6 zTWlmUlCP;bd8*!^8+uDC>uDyQuDe{i7DZShfx4Msx?Z3=y>iG&1ler1FGX6VMx<46 z`Xho^j|g}$NUBB#6?xf2|FBD|zbv2JDLlu(oh#FzR(N#%+uE-lXmilo^LxPc&av&? zQE!D(p?rc;s#3W}#w#g40js_QlVW0NW~lfFyp)vmq(9wsD_$W#B-w<&NQ!IPpc$|C z-G0``rxdji46}Vap+p*9Bw*-Z;o|cB>IXFPAh<|A32yffgWHw-x~KPwZA+W42h^q} z0-caNYmIt`r30ZB;WucF_77D#*o+T!O0Jc-VcOR&vUr1$U^uRK`W&I4$zjx6C}z7u zq_oC#hZIf<91>&23T_{o zFb^+4drkezkwu}>z6WtJ^{l!hB-|It|V{r_4JG(9_@sv1$dU39inK zfb05E^Ts*o(ydpPpd0gM@ydwP{QPO_XMdd-HJj(5Bjzu=BPRtSm(S#)Tvh1j@p7r& zi5aayo9sG=qESX@DccDVT-RS;D;C7OvtMX1$!69Mcd|^U;nh+!U+Y)7L5K7AD7_vM zLY$`g5qTSHAB%!Z79Yc-ATepM2sn_b))> zW>0{o&Cdnsp!ua_TM~NhIvH&ngJQc}3lGq&*FCI8%C2@fT8bp8E?sM;JQx-5^>LL| z*|>mo-LyB=O6F?1D^ry^GU0CIh**VBXB1IqYl8rps)3uc4>UUq{_8xknb5cPXP{T7 zwq&vbHw|9*?ltcfpp|WvkV=m)F-+YXQQ&Tf6-lL9aiP(ItJO!*TALC31ha0c8!&H?yk2e{;0 zFn|6fv}9f?K=a^zYTSHh4Vv2xHDgZ8(8+Bmnd&6-dX!NlZ>z?~VtOaC)(^Mza#5;u z!>FQW{W2L%1^8jD#`eiIFDBwqlFq4Vo*F2DN-9%}wsKKbkmxv-DpKAx8RND>XZ}Wp ler<&e23G~o1EOj3N(q|$4qSq)X0w{zY9qJc(&*ds{{t9Q#Jm6i From abf91d974f3915ff54b1b12d51f012cdcaf407d0 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sun, 18 Aug 2024 19:43:35 +0200 Subject: [PATCH 14/14] "fix" nix dep hashes.json --- hashes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hashes.json b/hashes.json index ffba8089..e5996816 100644 --- a/hashes.json +++ b/hashes.json @@ -1,3 +1,3 @@ { - "npmDepsHash": "sha256-9Nf/61L6pX6vrWWYPV7hy5/Cj3bWgTd6Ed8qNCQfuLs=" + "npmDepsHash": "sha256-qcHlktC4qrhOJ6AwKbccPkr0cVrAtPhGK+xD/eV+scU=" }