From fdc1786f9e165b0d339b745e6e78b1b3c32fe84e Mon Sep 17 00:00:00 2001 From: Etienne ANNE Date: Wed, 5 Jun 2024 08:57:12 +0200 Subject: [PATCH 1/4] start of rewrite --- .../app/builder/collaborate/_page.mdx | 13 + .../app/builder/collaborate/layout.js | 7 + .../app/builder/collaborate/page.js | 24 + docs/documentation/app/builder/ui/page.js | 4 +- docs/documentation/components/Sidebar.js | 2 +- docs/documentation/public/collaborate.png | Bin 0 -> 91672 bytes server/datastores/api.js | 6 +- server/datastores/s3.js | 512 +++++------------- server/routers/plugins.js | 9 +- server/services/compiler/compiler.js | 2 +- 10 files changed, 194 insertions(+), 385 deletions(-) create mode 100644 docs/documentation/app/builder/collaborate/_page.mdx create mode 100644 docs/documentation/app/builder/collaborate/layout.js create mode 100644 docs/documentation/app/builder/collaborate/page.js create mode 100644 docs/documentation/public/collaborate.png diff --git a/docs/documentation/app/builder/collaborate/_page.mdx b/docs/documentation/app/builder/collaborate/_page.mdx new file mode 100644 index 0000000..048658a --- /dev/null +++ b/docs/documentation/app/builder/collaborate/_page.mdx @@ -0,0 +1,13 @@ + +# Collaborate + +Wasmo 1.2.2 brings a new feature to collaborate inside the product. You can work together on a plugin by sharing it with collaborators. It's easy to share plugins with your entire team. + +1. In Wasmo, on the left, click Share plugin. +2. Email can be added to two lists : the first to allow users to edit, view and share plugin, and the second, more restrictive, where users can only +edit and view plugins. +3. Click Update authorized people + +
+ +
\ No newline at end of file diff --git a/docs/documentation/app/builder/collaborate/layout.js b/docs/documentation/app/builder/collaborate/layout.js new file mode 100644 index 0000000..9e489dd --- /dev/null +++ b/docs/documentation/app/builder/collaborate/layout.js @@ -0,0 +1,7 @@ +import Page from './page'; + +export const metadata = { + title: 'Builder - UI', +} + +export default Page; \ No newline at end of file diff --git a/docs/documentation/app/builder/collaborate/page.js b/docs/documentation/app/builder/collaborate/page.js new file mode 100644 index 0000000..6a9c69e --- /dev/null +++ b/docs/documentation/app/builder/collaborate/page.js @@ -0,0 +1,24 @@ +"use client" + +import Layout from '@/components/Layout'; +import Page from './_page.mdx'; + +export default function Home() { + + return + + + +} \ No newline at end of file diff --git a/docs/documentation/app/builder/ui/page.js b/docs/documentation/app/builder/ui/page.js index dcb8f0d..d76ccf6 100644 --- a/docs/documentation/app/builder/ui/page.js +++ b/docs/documentation/app/builder/ui/page.js @@ -11,8 +11,8 @@ export default function Home() { href: '/builder/ui' }} previous={{ - href: "/builder/plugin-structure", - title: "Plugin Structure" + href: "/builder/collaborate", + title: "Collaborate" }} next={{ href: "/cli/getting-started", diff --git a/docs/documentation/components/Sidebar.js b/docs/documentation/components/Sidebar.js index b943ef5..1011ca7 100644 --- a/docs/documentation/components/Sidebar.js +++ b/docs/documentation/components/Sidebar.js @@ -1,6 +1,6 @@ "use client" const LINKS = { - Builder: ["Getting started", "Your first plugin", "Environment Variables", "Plugin Structure", 'UI'], + Builder: ["Getting started", "Your first plugin", "Environment Variables", "Plugin Structure", "Collaborate", 'UI'], CLI: ["Getting started", "Core commands", "Configuration file"], Integrations: ["Otoroshi"], } diff --git a/docs/documentation/public/collaborate.png b/docs/documentation/public/collaborate.png new file mode 100644 index 0000000000000000000000000000000000000000..4ef75756a68f1db300ed24bb9909901aa3ae2efe GIT binary patch literal 91672 zcmeEtg6*%MHM7PMadQHtc^@93}Ij(A3r5LQBj`83mR_T24_%<@nmod^T1{YjKI-0&mc+q zD2O0|aLd8|l&qN{AwCpaUqbx!VGW8xJ`jQS9Q~f6^uJOs%Q$8(LmFy2ThO#Ft)9B7 zs2uh_f4!U5c#hyx)QkC1N$(BJSirj#1s!=e2N^~ZZV)*PJi-!){28}$=hJ7;Kp}(S z-nAFbP>W704H>h$n}>%VaW1zq%rFY9?Z292te&^`V0yRVeyoHM+818A9;g(Ho%_hD z8jcfk)GR5NvJovAnf`$@u^$=T5G;cLBfnzP`4I-X1xZO8lofM;kRoX!yTXCnDFWul z#>QNc$b^4_`h-7ZOL~gmI`e$=9uH42wR`oEJ#AqiZn+usIa6h*|Ka4-eQi#Tl+h%s zNJ<>lIqDm6!?o`RIoVHo)d^ZyO@a`b@^9(nVlLl&XXTE?=ry&_KIJn=B;01SBhSwi<6e5Q~+aIr}4n4eN20n(JM`idYd2)<#sqD zd-aMsbu9YAbe7Yz6s;+Vnv1IMhvy8s0~4>u)*UWY~s zM@Il+X`^*P?*=2a3mb&MD9|E?`eT8?RK9i@XbPArD5@gW z3h<|>4p=HwuEBPn&%OI%pF?%6d#ckZd2LiVGCTU=Wo9H_5h8W83`e*1VJ5tZ4Z*GnK9{<@p6qgzZL zi*0}nA<<5Te*?5ZvGLLf40EmB7?uJ0m&cO$8HmdUg&hJ087sA@g9=4jJx9SVdnMUb;CN{};(fG9w?{gvWJ6B!5UAne$WyB~2oU#z@YpQ+ z^(gg}b_wg(>jKenl15Z`rfiZTGcq&sJQ5A^ZqhGeBYH{K)a~$GIqH2qd~XA871JoJ z>4xJs`$|8R##l!m#(L)nDX^;O(le^|D$=UG`f4MpCl>w1B)eB>jkZLwNQp1sMa@>` zW2QkdPIlZ^l_Cd)?a(nOyWBKnTJv*mN@Q9O&I>c`DdO?5$>Sebru!2OU$`=5$InOPCYi?tviOTcM{UP3x8k-_ zez*#&WpHfgpQ5JM7@^kZvavPZWbt z@KE4V;7hy|^9nf*rH_aa9}d$d@njO`7snXK3k!<~FODM7Tx&MD$!zO74@1RUeD|Tr zpeu$!`bWZ7X8vXwJwoH@@>eqNRp#32SW?L4NQ@cNNOVX9`weZKZFG+LTnwA^zA?1K zJ@B=0WpL`zj&j**uGqx5E4hw^_)zEJG|8Pymq^}2vEnKO4Jo+jt6!s~<0UvLBCejQk|`{dQ+$g0MGP6$-L%!T>NHt`1hZg` zh-sir*V!i{2JT{ywAI*?6Z%v7gM!HS^K)nQcZHt|&8LLSSEd|_e&{5(IN78gHo)(G z+*O`@h&TSEmULkyIuUg4QGDxo1uuAcKcY^qL(W{+IBKXr7_~))An0UWF<)grUMr%# z{DYz}^Ei{GYi^}b@ZE#Tukp@}fWdj{XVi_gy0u8PbJIKD&V=%nOy#y)YY=8N`nL=N z4G(KrX1#LeN}7zG*ajN}S5cH@TIRM5G4Zyjyii>zIW96QnJijRxzoe9R$H`+nwRC$ z;CjQAet~p8S9n%zna@$cuoyO<+RLIz0U$jGb z(4RWOCM(E!R8FA1}v~!XkYrV<=g?KHjVkjrwGf^bqtUMG}#g3JAYe z{0UcGWI<>tYxb$8tLiN6RB(FqV9aYpZ9wNDeT-k#QDfbqk8tJlEeE!-{ZM2ob}G^9 zbQiBL`tN!=P)G5t9D^L3be`6^T*VCMplW?DsZ7vfNpD#3SYK1^{xNC(Y)YeKR!8tx z@|`tJ14)CX)vE=QUE3Yg!_-ChufxrUx3ZycY+|-x!*E8o3<= z&+d1hOrv*`mAD7Es$8o7vc19{q;YIXzF%N;gr*%_?6ll-?rnF#9<&Y!vOiq1Cq9JV z7TwklodkiCGn6|7I`~=enh8AU++GVP9p#Rt|0tEwCV3cGE#1u;&GIkpDy3P#X;F1= zd`O*dj4WR&r#T#7i1Mbr%{%|qO4FTNGOU=M9>wzDccb*{$Eo>Et&eroi?J6k#E999 z3vh+N&uFM%j*J9h)(Bu|`5_NwCJpIn>?YSlx}mM!E8A)A{lqXPB0+sMA&$>{C_{*f zMXNB=Ek3sM!&sOzoP1tzE|5>>WIXPw=rNBuutv>CcqrQ@y>D!Ict$_w~59d?!{ z)ZRC3`v7k1xV|M4&j1^Hi>IGFQOsLLvli(1Nl> zce1f^vj6w3|8?sBzE#!U&`#9a5_qSBz<=87_s#!(^7oB=ERR+HFH`(&=f92uh895K zWBH#(6F})j&H#cc7-CZ~c@^Ln=w*)|SP9?{?ccwT`z1YF<>4GKFhVerV!|peuzRUb znkK}ix})+AX)b4Ah-hDiD&spf6c2sf%5FgOLY~(O-wTciVAFN6! z4Dz2F0f?6BNSquN>(31-5hgIeN0E*4Uv7iK1cDaesf6JE++eZ4KrvY9ji~?AAly zH@UD0g91a4k?EAb#&MdC*{roOIdPmg{8`RSQ5Z_xp>#o(1QwmhA=n4!xDX`dD8A#6 zY`IjP0-ARJ=s#=TS`1lg4@5m4WTVXGnA>q?9ZvtFOOO|USO=H=(FH<62LsAbkmdQ$aL^dU&)zP#{oA7v zVF`5koDbf2+6Kr%T1^6EKC{1RYUlg2%)k+!wAOs(TnYhtu!RU58siQNh5n!RfRJl| zGWA^8EJQ@EPq)|8+z&qD5s?P}QBI)2Tg8`leiYhmd?(bjaN+KFL1i$QqS=k%Z{jVcJETVNZOM=pk5d|5uYoPt#YSsYsRQg|AvndMuG7PC4CQo(E?NG}YPWm-9d2Y08s0|r4T$QwVFWB!cROUMa(GCxk9|EH>tb)_ zMLubBy#>XkC~PuvVCXj!gV_dCProRsFVLmKP`1=RN7wp+4Gmto;T+Moxk+69r9zA>0f^BpRO|v ztFoAXi-OZjV{beN8EH_2KooBIUi|@bgglVfm=x5%-5gXj6@KO7OX~f~m1yyO8x39H z@(c0L#({7-D0N@}sBOsqPkRT13C$RDNX`k`9`{M)0v(&`ueV80(ymj;ixIwf*`} zNAL;@uw}@L56C!J6hZL_v41!#u_OT0L9ox6(nb7Ou)F^RRdZC2Gx>vvA|!J|?Rfogy>TF6-Tf>$ncuKKE{5U_itABZ+_C!3gi@7>o@6S$YLofttaVG@ zkHV>{^*QecPrJ=jXR~nvNw{TljKYh>zsFJP@oJQy)Yovr9J?HpTc&_jzbES5pBU8E z4qG06^?7c3AH>}pHV)IIVYXpjZX`<|4<%LG-4B$m_0gn5`*9iaoMl3nQ1q;R+pSI# z0COStfmu)t*o{qrhxd~uS}dA1RWW#0{ZalKAF?7poz<@==Nl|g=G3E;n=|KYYuAHQSkhy8nAx4py(kDN3%l-_4GbJB)}dt1@xCRP$YKw}~aslK_tJyE4oY$)=0 zxWi3{2jG=djm3MKhYRnA&Dd_Fe+nS;R{_j`0$`t1?@kW1wu!Dc9x?~#cM^5+U2jpc zmwD^ZX=K~9u6JulFfWC2=x35+m|rZ8=ye#*gum0Mah`WfNO|R@Zq;(ApwBz zOLYzDnR5FCPS4_cu6}dRtC+wT{+knR+qvHng>R=gaM@`oZD0T^YkG@r^yxM?`BUoc zusTskyY46=CTi)XykXt2i0a6cVz;?)HaM3f=J!=%nuSkDU4*phH8qkqa0tyWbP}xi z4XdmSh_T~nkuU89r?oiRatqOT;)xXg*c{wH!mE18% zfx&6t7q!ckMikoyiTZrbI*qA<6uWHGLiE z{A;+wX#W`A;xEI8DnUY?oQMWTK~mk&nxEEfhq~XE_hc(%zy*yK;?TuN+ns&7u>RqC zvm8vewp-c~fA{2B-$L`v+7m{pF9Zqs$urWJ@>QDWdB;sq>6scUT%4D?_m&#f&?Z-HBs1^a@59-{CRCno8&%5j zd7a8YMK(05cl(wpB}k-QUWy`akLCO0Fz>Zw9RoAmKr-b*%xnA2#z?C?rTUm~7yFP^ zwrC-68!kGwB?zk#>N9?~ottiWG{XIf=2P+UK(!9w^Bu;D&5irgSxO@ixhdZOrp@jR zwJW@;l}Q#*-e7s3-MMZ3w7lLn3sI>>o5WYQ`=1c)W2z8UK)}q7|1V+c7l|3NyLhl+ z$okz83FWI+As9aeqp&dXhLSDua7%&Nn*7)L_s<-?{1nNa55E3NwO*7x{wlOOa^la( zo>yVkHs72}=Q7W)kL5_uRK!wAQtVC@#W-w_nT=ygg4;0BusK0i2;eri8SlHZggM(D zl1bVU3146k(h9u|Zbc%N3Dp0m*K2+h;tT*CJr$EF3q$%DB?3na!nfkJTZ5>FKj}<@ zj^vqgOgFnn#fyLQa5Xq6BJ`8H2mw%?fKx8oyNb3YhM!)t6H=8gGg|2t4=id6w>_!O4JHTSQ z9KiQ|IuK@^`-QgyM9GC}jIn?jOvm9O=5oYByQPXo5M(~9*LF|CkOqQCxVm2hqh(1f z=+`n3pMq03-{Jk^9eq9l#sQsLIu`uu8NVUrgI=MHpsFo#pLK z*E982jvKRRSA=`~>T*pNtMljkwSejS>y6eI=$EYaS2RG+OzCA6NC8GhKGn9QM^jAvnx?gejf@AqbA z{w<>p#dL+y=+{7cs#eHUU>N>7fUp2}wJ3SK)`ym*6_q2$6eVySa<0A{+8OmwX}`JF zXx$QfSo)q#-2IDxGP@zlS%MO&$C0q>bv}(~8Z(v@KU>#Yto%@aZn_tJ%k6bxwoJ6r zyGs^wblOs)Hc(2B^P(q>j#eExKJ%PzScOuq9Cf~OPE0VV8>@-t^ETo|odtDFEUe@E zlb&8W5^-YNMIhR~*hy<W!vsWYlaDO2BcL+f4Tc=L%d3HR>G9 z)f0RAKcS?$1;BEJ(Y{;9%tgNPwSETNXLiFlakO3OtAzfbK`1^?*&%&x!lOI3oOSBW zv2OX1R!E4fm?8FSMX5jq8SuT{BdLduyWI5n%-G*on&LZxp2loe8gi|8-Cq@iC$lPU z3~7?Hem6$L?um*X%aPK^BAx=j8cgGme`zttc+|g)qf3Bh_)-CH()K}&$Kh~VGm@0| zz%jAXSnzD-CHMVF`U3>;`^m{^>m;>{%deVWOdhmYe+p4971o!ZFJ|~rsQO7Q`4y3D z^#<3J;zCmOuEEWDs-2jt<(2fi@6ITFF)u!4zCo4#JeHH)8P-K2f%$r$Op4E<@X_Hp zP4SdYurpq1Vwch`Tdn1l2cc?&Xt`j{!ix(pJ5TNX_zISvMNx&Aj20AoTu&RH(-Tq< z&p8KS9nJ3jtWS2DiXYTT2*vF;9iO8B(=(}8sq=GhFf1Qcykro2A>(d7+mK^0eBY!- z%6FJtJ<|BeOrf-7!Yk*in%Ay)M|UX-&)GRtKud#=+bE8Irjlff1C#eQZw{Nq-93j< zWfKY9MknOLtX5LgG+9+XS2~p7`+L&Q6eb@Nfg5U*lo?6Y$rb|de*kNWH&2MYhB&cY4qZ zkj=<+O9OzCk`+0x>2L=rviT*4(NH{c?AP8>vjC~G<&QLDy*^%^*+TaoZ3oX)yc`;& zKhc%W=c*2D9E;CRia*z`8;CE3>RCr(gnvWkY)!PdU{0mZYEbh>&Okw4XLuWpE_nTO zQ{nQs+42N2{ZfeH8HoTz=|q0%;7qPFdcIC=-NQeyp{(T`e{u|FEg>vPRi@Qwikp1JB8K{N&lbg(54)Ttpwf!pt?z8NZfdN`9j_z?VWwf>&1%t#D{3PcKg3Du#bMQ zV+kzj3qwLS?(m<1VGq=IxlA(e*sWTe?rY3{c)a7Mbd#`1k|P&sFP$ot#y0$pOR}TT zKJUE91fVKxnROISDseDu7KXOvq)5ckYdl)SQV(>&Q>cteJEOR0S0)sycGX16V-X;e z{(7Krq0K_Qt5Ek`U;DRPLkbeA0?g1SD-%)`;4M

AX%T1^nVDLY^F=}?m^?hxzHla%=_BN_b7}Sa@Btf3cRWik zR;|la11jicbWIt>$7rrriXrp)V>KpnuE49}fPiHqRZ;DBD+Sg}<=!VNk$>)Ayn&(5O^r5vR7r8Zw`t=%Bfhf zP?%XdTzWNooLK)r%{m3)?YVq^-RHr~x;#=%H!X@r*lHb-fK z(Wwy`dDy6zoXqp~&_mw=6pGJ4&(Z`P$lDcs=p@`G%YWS$97T+olB=0_W#>r9SJtA5 zM~T+m6UxwtMW9rSO0cX@WP;hzJFOqZ|H3BA^=@i6NU>C2fZv>eNi2ATZmDpgWM30x zf062!42ANl$)yibsJEHjd0x!Z*jyotIsm{~Mf@bxl>`oPV5Z9a5Z8FVRRt=AJAf)q z@TKw!dMsr7?%mbHP9bWA={TONrYwi>l^krUHNg(5ntF>~>@lfrn=&zJQ%6wLsfDJR z!c=#}bM>t7vq$RbMa1*#PD%>EiCrQgyI<@lT%#qM3}Z*v+igJ8_#04)erV2@`)!|- z@ET5RmCsz={k-sU)Y+dYf5V!*`8I-ivsEO2d|=T`<*&s<@vy6Cvg4|>FGBj z3ySuu-j~Zyrgrp5@1@q?AP!^ILK6c5gv1n2rjul4HC;sR4t^2)c&5q^rG4TupS^gw-4q?0oB&Y2@0;h7+vfvaNW|ftdjB9~ z2u4T{6eA==H%*hO3m~ChtivP#K=sRv%^(tfgl~{Tzu(KJ#q0f_#}l!M=n_J$NM4(3 z>2X>f`P^$1-u4D()R6Ev0R#`ay7P&KM9za~EU4&8R2ncuQc?h`@YbIuED>|z(+un35V%p*n^Jly+Fnz>KvsJ`9S zV;tURVE9>MHLLy7YG!YGxMA#@7Ln=nilezo)6{$b$bFerVKO3tOVL9%P5y?dly?mqR>mj)a0pE;D<7lMi`M*#ox=b35=PFzZQ`PDUaH>%T(5^H-6Gk{K64jTEJAj~>DcY*iXD(4bqM_1N={(Z& zN3eiHBjI&+wBXanX+P2vy=Ct%Z>`siTBx|)$DI+?N8$@=m!O~ufMN{(Wy|wTeT)I3{}u`V#GiX- z@d7%dgCt<8Z;kH3<}_^ONXV8mj!?(l)aEy>tD=Y6^YH9(4Gz<@{WVf#EkVY>bY}AN zOB(p)6_Kr>alT(hG_z%lZC1MRHf- z0#XM~2`bP>Mm!0sC7h+}Ligc?3bV5$^5I@?-Z`HPTb(>z*fxA7B(w~Ww+P&~+m=c@ z*H&hFHnKXE>Eq)r{LShg!k)&%DPLtpsm>XI0O_UYNUN99?)vi?POY_JL7h*xfuWaA z2gS8zf`3-pCtGn-4y8j!zIbVE&sR3C-k*ae)&o5ds_jF@`R?nAbQ(2u87N15o*92m zpeInK-Md-R{#k3BySTi1eg`)lSp&t`zhO}U!;ae?0HCY-9aSst>WQ>M*#w0SRZcyloXNd6~_u~7pH1u%$w?zta$=#6O{V)XG7lMr3 zh6HL`ix{p4D5x0iB7k=`Z6$MWfrTd_N;LW=lu#BDDNeVMD|lfMB;?F-e1(mL$CSvx zvNnXP^7#@jn8mgM26nAE>w0;yw?%)G*|!$ay*1_d(?9~^k7;p2)q)>@{3SmV#=)ac zmgY7*DhG^lpJyarO+d$vlYFdvoUaIrlezQ@T75Li^iJZ7$C?a@_N`Dihi!@dO6I|> zy(Xy0qu(AxaIxgl??+hkaytn2kfH%qO+=&0s*pj-NG=)cD1_J&wx(AoAfO9%6M4@M zMD(04A-d7|aP;79u^Dd1K*&qQH!l#udIOV|$8KShGs>Nn`m^z9L1;LZh{2GbU|OC0 zg2LtQ04%J@ZOvgBAIBDC#3;{vD~gD(XLf>)d9 zs6dizlYyX=XB$I93ijG;d?Y$0?YP>E+A={;pOe!mQK9ESxWUV|hP^hMPIn%bwe7!F z85Qd4-Zx}FjGeZ9)ED-gci@e0sVbRwM+qR(a&ew=>Tzwd5)lES8-w<`(FO{V)#rtB zKbU26QO3EP4u9^{rE9{8d$08?3$bqgB&AqqXH60j5t;J_z;YR8OsNp&jUj6dTfv7_ zp3SSJnbb_2ZFvZ-=GUrlGNPml90D^YHQ~c|dP>r z&_1NqnA2A{RKt!1@E3WX9L?V%dh(=u*uCwKE6kfO{SbKEZ$+v1L4kuHPw@6TN0#ub2XBrY8_Wum_grOi+q)x znicWE^>FDQLEr{ws6|=%)wlX0x>UASAv)adgMXI(IKP{%KBO{_tM+hjbzQz~C&{Fv zqUyZEFMz@@mqjnFbz@$j?!9fT5obgI0QTC))og>0_@X6tZ0nu~3r`$iOUT)Xwehk1 zp)k?IePIYZ^IIOlSgiD5wX|EmZKVb%h6r4^PdkB087zg{hQ;fuM#I6FH*dc+i~X;W zWrBq48-ld)`sZJ6^mA*%$AzDnf;g82^~S@E74E~@ukB9rR_7GG*U3pp@EqW7>){-* zu~t_73N@p&;Z}7U$}E1t^#OrCGu@tTGzQkvJJI(TNj%%P=L+3Agx+1F$JC$Tn^Z;+3&_x`6?~CxoDKvbNqw%^;q7I`Yg8jl4wpB% z5tn$^Xw_U35`ck)h`z(ZWv((c*`q^c5!Tur!ryreA$8ZPm;Fojv9ZSt34q!NMTisd zdS$|9Mm%>#(sXJAg&;pcCqZ`g&egXS^TQ?#zDzb1^Xs4^7)|(g_f{_D72RR8cl)Ji z@M)Cbz0GVF1De35TB-UAs}f|uS9>FlHa#<%O$6kCaDsfY`xe~;#=d0kD9flNZno@) z+;s1^qBo_49XI8cz$9$uQQT28ZUQ1^n!rT1Hy&nvZ;z;D493Sx7^3zd3!CJWwi-OwRUb+YA1|-C4wsQGq2>&GM{mjv* ziy|QcbUxS`Mcph40oYV62RFwI_u8ScY(-J6F^rQz<`PG~h9x5K zAcsKYk-&P_6Ou|rd0u8w?~HQ|O$}4jHgf_*{IJ=&|JH>%6CRn<%Sk$=n@5(-;sYku zUhduQ*=~jPqSth}eu7>chKPR)l*t_LUHP1=OI|XR)p+vM=c~llPMPlVhwb^=7G_Bi zOe|JDHvj@2f5Un^LH8-wufBd}cKb#TKx<6-5`w0|gStNZ;Yos|cmJUYlBegNQXCBd-rMQ|XIG zgiPtNH6xFwIAlzl%jVekoHt}AYeRf4HH*cW_UnHXYHD7eALPrTa@uut4~`W?Xs5^^ zfx$nGGtU6QOx@JxZ-J5g`6C$t1C~nBSG-c_d)zT8KJAE|%3xZIRxqtv02bbk~YHx5e8pcoS z&+Mj6HA&o!(6Z~)>alNG3ksG|R$5QhnrE=lS&vVH$s`fh5tZ<1skZW1BYzW2_tZk* zWgWax4M3G+8WSdc@_g%!D9idiuXVmwf@8Cdb#3}28=!2-OK^F zd!I|sbyj_X zEi%p0a{FO;r6FPznIl)8jZdJ?=qdZCEW}3w!R2j;{40;OyorKh=T9?Lwv^kGs>3Hn z_*iq|Pn(nqzdqetq!Ca@i$aqVX9Uz@R3_&ae^Kmp2uP1=>6@=m49^W%GR*t_<=Qzq zVnMr+E%n)PeOZXfj}5fxGTYeZq;97eD#^8+3d3x}6QdXbPaaEBcLS<9K}2))ryX<4 z&lT_|OSPy8$8x8ry9|f}L7qL_iD74tITJnoPB8DANcr8~KP$XC@FkG*T=IQluvsGu zXU({b0>BO5+ba;M)C%?-U%bQigs`@I^~ah^Kt*t3NN9>kYWiAioW8)H?WLtNRbsQA zTYH=_aGWM7j z(&)55dZ1b+v+EKS%0(CaTbA9IO=0XV z*1k6(gn^VPQrbt_;B&+s@8(=`J?B;cKZ1lOfxaKhdr;R=j5JlH*CEkAVd};Pmt|Y< zHos_Ia83k{Pk(Q*QQDo+JKBDe3F-xpXj=IiK-FRF2H)xHYR-KyRf6NpCjni3J`Lx{ zY4z)%!IafqV<3?CNbvdZyvHRxV7P&=@zX>^px>j2`CoA9@ z(Qe*}J$d#+m)~=T-?D=Xp!aJLZ*~W@Kgp3S9KGiD#@wHAj^Q$&GOYwU>g7tgO8V1tVoEzm^VA*w$ z`5Km}sK~89+u^PQip!2utYH{FB$9Z#+Vq~HROdm~?i*D8F;~&@t@m(TW|uVb0f&2U z%Inx7?PUubr{zrQu|j#l8>{Y`+3JY{-H+Kf5~xp0{!Ype3k1};Jl%61V4SlM-*b;F z4a8H{F73wg^Bwng_$k7fECo^Ons7c}Y=FKPT&>VA^lA_SI7WCxwCHL3jcqw$AShB# z8;(~^-oSgONSG1u1W79-9+0Sbx#H8^U-}n$8t0U_u@Opu!4)EK%-4@}xKwpQ^_1Y; zMVKHsV`Jcl;Zf`TOv7c^?M^!aol;TuOQNTc4^PS>Q^1hmv-*aqN941sR#GF$3JH8iam$}(l(rZ zEsI^VyA%GU8M*|4fL)wS8oR8f3i#{|rVgGXI6fd?gK4&RgGtj72$Fix2LO_`7*Muv zFTWGI<~Mn5ah`wt3S~8#0}&S9JLnDW;Kux_H+GctS_M>=qVUW8)4q#V_zUTal2p%? zRU%<;x1qU zo8t+({P(IXNO}#uL5_v%+qr3DxVr=w08CNJoREgAO_K8kFb&~vOapCg0gVYHORfNN zguDO`by-I|0la`wt~Ya-t=FsJfTuWEB3~8(zV}^|LB5mx(@L!>-4cVrn0LvfCToO- zb;V{8Nq(3Z4I|~Eyrv5>){KmR=>W!_@Zi{Cv+TnaR$n|<7_%f%aFBP{TT6q1$^97z zF>z%`tHRkBi|WYaqwrzY*+sEGfxZ9iA*{*c%AHQNCZ(oh`inLY!4F&&=4jSinJU<% z55~#fU+=P{TL6ipG4s5qB2f8_hc}&Lcfs_5JNKieoya`*?oL){J2N7QE?^yDUrjal zUVsUC_f19c`$8Hw)Dqwq`g@Z%5!y!1bj9VHy#dmwP*IwpXu!~; zTzt+1#Kk}2KaL^+dN#_AhOXK3uX(Q@VP(rFzc-=0GsTa(JNO7upP|iiBL1BOK*k|6 z|4!#>{VcQq7x)2s!Fv;NSEgUMWXiNlq>A_-@+rCXRt#LtPt~=! zasYwG57CYs);?H|sLj;D|D8BzAc(lqql^AAvmrV7LgU*^RXM;Ek1`A`BF?oGYt?Ee zGfn8t&9qxYQ%ki_0jY&nVYpfTPSG964Tvz{+@EtrU*w6IsR$b$xId5bWIp45$wb8Y1bk(t0 zBPU*>T4fCt-6#Q~>JrcILA!-KVg6I>e1DeZZ%Ol1neEJdfv=p1+cG-7cos#xcPKT7 z+l!>QG6RZ1ValzqWfWa+zaFADBR|s?Xi7npi{kXY|V_zMmO$+bmL>?zVlV`-}$|y56rqqKI_M@g$FlpfC>(ftw zEZm?v3OS*n`XZ31jS$5t2TK{VU^1l`ZxBefG(y%eML`CyNMtp*^i^m?p`243HXdsC zr7|S&J$Ss8{7jMan7r0KRgms||E}cr$lt^30Cl?2yCqB2+1wS#dxusRs`dM$f2|UC zFYZy2c-FBF2%TV7;(XM^QmuMB>UH;Tl*6Ol-Pvrp9jkO7%XWS( zcLg+pj}+S5Z;Qs?Q}*-IJ*nS>u65&&)2q@;CiReAc+B^V6mkH9eNR*ijdC1sGXRf& zd@C$R!!+xz>QkzNHZEn(zi#clhkr7VQh!oizifowF^Rf1oGV)^(G7I`1|SbYcX5Zy zoM`IF?m0|?w`}su7@pFml~+7SsYeI=ZoCT*i98j6!dZVZ#Jk@W+_e9KiZ2=L?{Qjw zf%KfX(XG0`Pwz@z;3~kDS;y<@6Pt)(U&Xpc6!K*Ma@s=OJxPnsk|+@J7Y%8-=Ouh? zgdHz>SCLnWPW^N>Y?$w)>eg-_-w3;08sYeY+(kHuOtZ`#&7w5yjE8@n*Kc zXQG#soquGv{aq`m%lC+62#3={D%1T`9dA%aQ!+( z&Tw&3VQVqWNnY1(qfMT)DlGLV<;L?KG>I~M+Dm%fZqt}ol&5i9lXiqGe}8B1F@_P1 zA<}N$a`yt4Ue*3i#Pls*Cd>xs9iYG-`(s7JAur#5DCT_;KHZj=s_ByprBMmqN^`$w zXt_Nu{GKB4CYgWoKyX)dN?WfsAFVeGlZK`6^;sg(A7WGF7vit`j8_}J8HlzcFhfE_(- zV^8v2G5{$VK4np0LyJFDlJXvD)Qd?UwR9kI8{94MpYujv#fhSm!&{ zl(VoFWDK&fzKY8 zU?ZBWCLo#;(EYFpuAWlqzPjqkl{cyEp1H#(1H;lFbrh5(EWLPPB7FHVpj?GO7R?#J z3RQ1Ei0n6@t#7dz`}_M&<^%cBX6mU6TZPTxK>caW4{j00O1*D`>1ve6$&YFEV*zd6 zx-_$#BoFFyqxbVDI&zr8aybc7_wc?3lvOgM3>i7n!}&e+f<$4I(WMzQomj_6w|Myz zkFn_w=UfX&ICY^^_52?#v|C=bNl^O01F4F0Xj^>kv7)b@Z#3+*PKp5W2m;!W&|{vU z!sb9f5p0vf`#KvosPa)CJbbJ){xG+={!>r;0R2_|i~x|zqn< zJ!M(LM9@-%h z*{um-W$+y(NnF4Q9Z^xARv_T}l3u}h7gASWvkqvdFBAS@6w+H)s0UOQi=v|0*g}xM zhWKDV=PXN~aH|jUMpEn*27|+)%Zf{bnAWDni@|mT1VX!$Ik9mp7fK3^J(*A+>`O)0 zjIDWRA9j}-aNbGV5eY#8Ct7<~0n{f}#Q4KloGH8Vw2aMh!?M$H8K&=UMCsSWQqp}^ z)oC*V4l6(1h9J$l9|54USDx3R9V-tR9TIaRbzk*Jy-P{8s3juzO0HI72|}Pg$E84W zSyoSEf~yqpB%!RrCtkq-)J30pZs!pQs$|}(TP1`M?Ds_s-)e`11Ib53=d?CxtGpC# zZWbXhiS*?_eo`@!s=0QN;N67sSe83JkQ`7(gS?=#gdqOEd5aWe85Qk#@%!NcrRoCJ z@ES)TW1r^`!=H{o70AJT)lo-Lfd3sbVv_O<=pxGEt9X+O&igZzjfY~+Ox&zE z7b@b{hfgUtSu#uBc(I4+0)K>mP`^pKUQgZ9q*7C4NBB;u2=UF`K9Q8$7ZJaAFL~PT zo;<~KD47?3&v$Y<-cM_Wo&P@HuSYlUojCb`4Kng8Jf8WJPneTH7)VIzn-Yo6@(h?E zVuz$c0!9@$0)TEc&}IE(SSMVm!y%WYr9pS%5SR{Q9Zbic2a$6KToi%Zm$(hkFfx;C zC!&Zc^C=~Z)-}ND@=?z^?;Rl_B@VHc>5%0UQc_}m$Igc8#)OPuMbHsgG~V7_1ljVt zxYyaeA)Hp$NmHis76O*NG53+xm5XJeM|{_${(|jv*^8;~a#q`4*g+=+K?NGQ)j1{~5`2y2x6-L-YWiSuMxtnJ44cUSuti)*&)(aXq&2G}IaPq)V<-h+D3^toGp{Z6k`U8M%SLX$a}4Jh_l|exr9Iqk3i{&VM|;%Cr_3p7pue)Z71Iw0 z;Tn3M&Ti@#=Te?n-$(s>j)V&NfOI)v57k}#8VTXWjcQGQP`t39_<5xWDZ^fV`FPb_ zCkWvCtyYxfa8!FtK;cp&foJAwgd&m55w^eQ79qHM}d3j#5QV@}4jpfQSxSp&91udSg-&M7u^^Fz^}lpj45_Q zseC#{1QDKB?~$ieti;*e+DKUuRxU^VV;D`9+_7r^v+#tx4WPFmtfzdekL3(V2ZH}2 z4le0{{aE~C+?YbO(j>*Sb$GMTnFcNf1Scy$lWXj4C&lk z3>vH%m_p@w0{MSdrP%BO>oRP1wcXQRBSVEK9k^a$ zi&Xltr7;av&b&%3Sdl)`VHHjleR>m?V3x~~$gBIXtxWSd`Tc)Zn+Sc!0E)+v;D-wa zitKy&SXl3%dnxtDY_OfNd7YP(Bn!q$RPtbF25VA$=J#>Hx&wqNvL-pkcTGYEEr;KH zjybUZX?GZ8u~?vjCSx1rkAsf9{gGTCIC=;r119TbIeKPh=DjB#TWwDauPWZe@Tdab zdl2rwOiv74AK*_h@R;A>O8~IdU^n~^cR{ja_2%5ZZJjoO9#_Y`OR#s;vPnH1s#F7y zW&V5fEqg4vQ#K>?kv_)yKkU6_P+VQNH5xP!oCLSv?m>bF4+IDhq;Z0~)3^r-?hYY@ zK%jAV3+~pqyIY`f?&f))bI$j@&s(?Z{>w}=D$hLIR7%u+1j1{Zuc3xmJE_f(N4Jx~#}6nkIhsk7f<`%$(sP3c3APAw$s!jHOF#B=LsK*$3;C==;hO&nmmhbD6 z`+s|7g5LeLS@~meA)Ox!!>7Vg1+Xwbnr7 zlu0&nPp!Fdt$PK?XC-s4(-B@j1^WT2(6E)hFNxWR4->O`F>gg86DWWT$+vASmVDU? zFIpnRWEu#UNOebMGh`Qd{si*Wn?OLVL&a7czXH7M(@r7YEKsc$avd84QT#x5JGx4J zCYO1WB_Mp#hEQQQf_XjS`>-CiF6WH;#{I$ta11GgpR;~^-3nX^yDRSjQkYyzfYjz( zwN+N#6m!!1t-(~Gd7V=_Y_t(SP~~?2oXdVQ)8%DFm)`sW(5bbq>c-KeTDxJV-lW8+ zM~Rxik%n?6mnGyP2Q68A4P?>ctU;BsM>;K$XU1Uqco}kc%?h&ty4{IRZ2e z62_Mq?%jRXOLgi%LVowR!{?;RtCQU5JRmSDJ!n(ANGDiho6tkyyAaaU<%!sZwu95%&SxlB?Nt+?TTzoPf|Ab zXv9XFR~E=Pc6daX6CmT*9y|usZORk8zuc~rYJ9=z*>oiAIS`jO(7eZIGVaEB`q`nB z-&QhOEbH#e6E{`(d6nq7Mr)<|9QfeieUDsj@I{^(tOVd8UL}jM2q8d@0RfflthY8L zjzmcCP%C@xDxR0?xBM@XQ$s=x0XkAZ6Te-lCSzvfC5jt6IwRRV)V>tcv`ujNH<4XU zh$lmV5^%RrD{MZPbs6~Sa`s16$-QQg@OQ0~9FUfOyMY(+hX1^ezWV{T49;G+d($$_ zwDBU?qQ_#wtbdeGhA?k+;2uB?KatC^RCiHTKky6S08=IJ?rK7*8})}$vf{(>;{&^) zBHf@&7r}Uqo%5fBudyheU^}I%+zugn;!fs=pdi4Hp^^HU7}NUjFzg7qhFbNDig&{t?oB=GLOd|&6tkFs0Cf^mA``KG z3}my8Gt_Q7K|CPQ*?d3S=Q1^cNp)#v_TkH@)@QHM#A-;8rp$c^BPE;#=EqY61{*~}3J`%pM9`$QcDcULqdv~6qhaThA zw5$(RFwmsXe6Ot13rt^qn?<W2CyPGLMVJR@rSahm4sru(}c%LmaVa1vN|Yy$ft)H0E6=puTc#E&BT9g#_bpj zlZA)}xL0!=KL8ocv)zcaT^;FYF(49Wd0tB#dvZ(R`xM-)PRxxtVNbFbLU>?ht>W1i zTw(!IR`00<)DJh0Rc$ z`Si6tfstNAd?v~ZR8-%6Rax09D{M=Qr&b(@ix*he>tCTFGSOTKa(xN%Mg6i)bYf?b zQw*AaYYMQzKj4mmv`XOs$C_hXS+-5i4;VV>XZ!6yNDb4;UY|v|TXPBEGdkKolDB(V z*ZQ%)pH9fTNMKa?y>B%RTh>>0%8Bp2hb(vI=c|xB<)Q#LdUa{`^2h#=iK(P17jVy^tFL02RT9Xp{SE(*(`qSwk`J7P5ZL zm_7O45{FqO_$a(mL2LNC; z@rVmIJgjc`itQ+wz4fMl(3ItVYM$8%g`_>Jc7oEt4j~r{Xa$M;PEH6-AXh~O?;&^A z0kBAyeQp&L060-!bYc&M=ivE~h||_f9+g$_^+tT<>i&f88K`xQW@b_ZYhO7Tj z&{eR#NW3zBmW_WEiStF&26vlKR^KClV8pqru|HlX?$EsSeZ1A@P9GV+iQ;>y04%JC z8k=QGGtv7G56UA~q;D%oqzwbzyE|QxkI>vK;CrepCgg(AcO#agNtABSS4$on7tu(bE zfb*~Lx_)sDB-h*J5%D)EkbVXrJ0MT*p6kc1HbXQSRvw&6eLgqkEWeFx~ADU&d(Rhm^rIk*@f>EoK)r>g(r$eE?)r=v33e2pg3W zE^;O|x4XB|6V?x2*5%zh^TUnpPheyn(z7`*|1hexYrhZzJ#*eSH&96DoUmUGLFPIF zzxg`;D;V4zL6pcmm>hu_VY>`)ru(*rv?G7yg>R$j%)kAD`jUR2aLOuT<89?nPl2~5 zuBX2&7v9xz39?~xk*kwEfeFtA8C0|EWQM7d;cRMUFA zAm@kd>e11wfZ5L`AYTA0(Cv-H3AYMpSE%u#zaVP8&Uouc=+N+Z(5m)^zx_{}zeGSo zPHrt2hS}+fK3wh)Zz|2HR-_&W(8JGP)dTrWqBm|SW`Z$zoD#kH47V?bq|L^O%}NNEZoZH0^3`U0 zouSUwTn9Hlulqs5?^cj%%mO}~`7ET`Of`jkL_)Y0lu2+$egM~sHaLAexr&i(W(2VD zHh_?drz<0XzWb2QHCAa;(uPOtLvPx~y?fH$EJ8JH&Vz?-D>vkKyLA#q*-ycBIJ+k~w%2V?)$z*e!)~9evax%mTdHUC#1kYHa+9s6_mR@C+ z%5}|FQ&@H02+* z_uZCh`?TD1n&0n(cxMV?JIUq^d#{d`yrrAjbWZRQhdNGLAbQ1xe-0Nr&lk8R%B&|h zcxIcob(LO4h~+BqftTPr=ZCQHHGZT+OHXl|J1*u=NOdYd`}$JK)6AvW4+(qyX2?Z` zf*XFyRctRdkNdV6Q2)9ov3Vj8HdqHNGojNR&D0N^`*cihelBbt&GFV=mmpjS7)h@k zo$)2j@GD?l3A{PN2B3b2=YHa;i=LOt`ad2VAs7O9s`Sy|gHP*UrXcQC zd2g~ESrAaK&o(*tzLDS=dS9=Y%#=Ivf$I09&2%)(OETUX19;cD(j_PXBtFJo?$??^ zIu8th%l5P-mlI9UJ-<&x!e}%d(k7CM`WNtcMfL89w-}}4E{tK00te_&?8IYzmMI}P zBgy2h0|hloai?HNHZcG|{qYFiBwKXnOx4oNd+Qk+nw73`fGrom&fM^f(_*czT&1q8 zGN&2X02++6*F|owjCN;!6@5G1eKk6>H{X~K&7*DU&N+{?H#oMPPBsS?hDnwO9JTdw z#h6hxBAei1RP-GHaE&?J)axiTWL(@UtW*tgRXN<$5DLhS?K`75`ts#``|E_pS8AE) z_W-uDHqWoPvX&%0;4@-4KDW$9=kp3ZJ63G{=Vl`*OpR)}?5|+a(B;T# zo&AQ{oto}1>(`d7BQ2QiXy1SG@YlOY(dPlF3@Bt=#btP|*2?HN5)%!n=tEZ1Qbl#L zws@&HD~Uy-n~+UE4SQfakj6i6STY}79S=UgMYqCU>KakklUF zHTg!GH^t$S7Lf`?>e6pn{?T6|LH6*0lm{jOi zyGw64-f*u;v_Xg_(3NL)?>B(u@pi<-XUR=Z+=~LBYoE>wl>SE4Z9An?AT&d{XmIY1 z6?^k^J&UWjX#c@oQ^0%_iJ7^X$zv5s<3(2MlPHnjOInCBC6e#K`RY^WdkQuE6--hg z>w+IY3p3wifOM`eRVZjl^r!Tg^k=WWM+bg8jT80N%Oyp3=qA_$NOB%6Ehb(^pj^`1 zQU3i-G7NYj6i%A*kL`CVKkN;sa&Bhb#MbuXt}kA|_ryt^d$q8E^veV&S0TiO&!@w7 z2B{4;kfP$>V{CVo2{N2f6e(k)Trt)795Nje@K6VCiE{MC3TB_SmHthrRAOW3SJ1aQ z3abrAG!0NoD7!U^Mxunf(5rN9cJQt=?X&iJ@4FU8KgqMq4uCXc?OJb#`SEm)O)MqR z&t@d}k0d-(FKynigui0n#fdt}dtNBevhjF1e;kZbW4y~%?{gna_}w!#L_$_95U#VJ zRaNK2goF~nUIpC#M!$+o(^H3h$2x*Bi|Qy-wZXdWsO+*9_V^r2Iq#fXa9Ic0g=e!z ze)A)9xn+rB>`n2sOlrY;1X_C6VWDHMPzd#Oe%MoX zZ6RIDqAJPCgrKc`hMNL`ezmw!vFJDS5^`BvUtMGA$G{F|tv0m2O9!QNSZQB0d45a` z_ftd=@GwQ0KA&Pys{Q@u`+_@~2de*0)wg&9tgFHqJ3lb}?>QnAN!hm0yoyaGk0XtJ zPL8y0QarQBkr>Kmh{`cVUA*K>m3uhu?>VhXH|h42Sb!&+2nfBTrf1O}SuD&${my%2 z$+*Q%0?hG-v7^qVy*V69R1){P?{`Xl009op;M#Q&E(r(<>Z%)^_Lm@BcKl@Oy)>gB z^;oJe8%R?`4GQ;y_p2dXzU@>a4DY=ZfaUoutg|wroBP>wU>>A7)`G^@AEqGBesUSS zp~;%q@|^h4>5Y|xp2^3p3EX5zOZcooS7?FAm+@Nc(-c0s&d>?;-_ZtNFkQk z7n*2G>Xk*YXtId$@k9da@S{3t6k4a{&<20W*nG+|ewVCQ@ z*Lj4#N6V7h`N&<>Ln9 z#30Q`g-mHnmh;B_#q@sG8z-ru(s0#IL*eU54i)acO6!>`H6?-Ja_eh3hbNs9`b8aO z+l@6Yvw_{dMz>uq;9m|z?aS!zf!wOcTKRXqV28c%5_;X{i01Jjj46vh8x}JgJlp~+ zdBiGK2c^L)mar#|Pwc~on~3fT`!U=(FH6a=*sO^vni|58Tx2q+I2>R3s*EC7;v(o<|OT1vDWg&cffYK&7(L* zKT;rz>;1Jo=dm58N6D0{$3(bA^`a578v zTgmf@FS8sII93yFZ5i4J;(LU)%GZxm``)oS3=MG|j4)WeTlHx9%a&5ZtebCod-6UR zxCverEpw1B8X`jZ#c;>8=S_oo#EMo!xcNU}R~tmv&>emg>h|9J;7>ij7_mg$r6Y;9 z8MlGB8CRCz7H3&iYbT(EaiEHPR&9G5oKFg2@b95tPUX&a+eGb=<{u`x&}=xc$-Y@@ zI(a~nxsaMQ$Y8W`wGp{Jm!8!)=?kd|CQ{52!UI$~#_FWxlWsOhxUB19gPm`W`xlli zh@#B6e0-svVhWw1ot2~Hlg?l2B0#n$ZB9cE8KGO;&!&5{%plB$<3;^)1{XphLrp6t z7*KFi>hpmYiA7xs*>9OKO$vUcJ0QIvIDflDLM8wT=YkF-6zwEp+4KN`ryfQeCd}g; z0I%2WUwWab`CQwotd(N$ZZ~^~RHJ@xz`>2L&_dF|ttPeg8n|sjF6^~xr`c7WR-yOM zoQvWV=UO7+@v5cW#2UzKGs-NNZACY%CZ&%={`R(S`F6(o(Ln!BZ!$zMTjE!=hu(dF z$-twx`jxRMBC2GH82`ugivvC+7T*)jWXp{cR+()-RUN?x91E z?A-+tJzJ*7+Z94=+5>hot5ov{hU$slbRN$m{tKRtG*UC~x?G&1eXmr(0?M(hhGd3| zIHE(4mVVi#bVK#}7{pz^^!)VIH}%m|uC8Yp+0CR@dW$zd`c#EoKAq=ySER5bm+^HW z<_X+xNaQ~2k63hEa9`}Y`}5d2{r1YL zF6G);{*L?v(ssZ6O@}i#0IpTMl|MEy?Mt_d#*JfN8eT<-;qP8*vrL z0^M176xxf>oe%<|fH~oX6z;Kzu#=!UaVY-sh{&p;zC_wFHH0IHg!f@SREE51yMwI5 zeF^*RkP~OJTk>p|5D#w8xexETp+)QCJt`_)IBZFD)k(+ss|3bh=LBMv)Fg*>&)!O| ze_uPBfYdpWfe$IiK0Mqff|J2+8G`eXC8FvYr@@L7<@SemxI-*eNzU_kG-b#r>sMrH zzHY;*nO;^R&W=M|gPU_MBoI*3oTDi@N_`!L3uQ(thVJzx|Nbp=oXrpba&g)kf^lf% zGB#!TUpl1=*aXwF&&)3?JuLO8Q^MKM=06+jXsWRcN>kM1 z6=V&eNRc<+4zWBdsly2(jt1f{_SX0Fu1Cvw*^2cUoO973CI*W)E(#d4Fu59$g++m% zTZ;Xo-|`hL>yn6USc=&-8=s99%dn65*pgwr3Mxh1a$nz9=V+hfujG3M2qoJkga=0x zN0EmNqebN|@OY5H_m}?SkVq*D(Zt3bt*hlTIJ~Ih9k;iEP?ExOs++D#*2A;hhZ0TP zO4o3;WKUq)UCES{N9HJ6fsRP~?^T^xi{H<=pg1$2<-W9!)(X6+Ekv)^iuZ!v^XPcF zUg;xBQz!cT3Q&F?VcJnZ=P4#J{p$H1pY>;@%UQOYHH>#?nTAE0e(aNUeEHoapp-P3 z102cn=;j>Lt@B!RNm-Qb^C^Ar*Cjd1HjZs)#bZXzuAS5GtpuQlWPAYIH6pdryT1+q zz?ZJ4kU%|qgi6v%4Cs0oB~0~9K*w-R6Oivc3BM+q&$Ohpi&DbY`6D&YNcn>j?KAm& z2#B~$F9ZmL0#8duks{VOW>W_MH7)0hsjhnA_+s$@Z|8_+7L&<|PIy=#kK{R#U-a{> zYL4W~%hYOH#eJx26bSS8bKNqL$5;XztcVU2-U1635tnRkaEk^f6cz|#uRTYbw(Bgy zWg~&!NL3UQu~D|)yhEedxI;Scq&S~C&e?L)+ z-Se#D=-q>OSHvM8ZgHM9ABA`;lt zRB9XPG0$I3mGK&pK<5RSIvq^QhP69|%r)PG(EuW?3e=c@5Cp;hiDQ^4XkHs@pmp2b z)C%Cr3H4|*q7WoLmVFNQbLxDHOgtGacZSFEd9bf`UX7up8v=Al8y5612c z>!L2#BC;<$MN5~QH-u=sxL=v9+Bsx*@ePo)G1A{u7+}&1eR>_VFY@LE+Jvth_PM5A z?Fc)1NF-uq8npiF<=X*Ma+X70+!fXiMuU>Zn=>Pt`N*zc%9P}tdNNqYuzh589tg(# za|Y|nK?}Vs7D(%q>M@1WJqBn*)r?_mSLXvPH03yA8RZ~(1_3?ymtr=~;z9M#@%nX) z{liPIE!XoTn*_5o5&fuAL+O$wDvKHThhq>w9NuWf#BPB*7L_W+|jLh?moR{ zzS60;!RObONxL;wb7^8Cw~g&yim4_u6FA2s(gDMvCBr8q{WrJAzLKb|nioNofyliB zJcUH_a@g?#7bNcE9KNL8TiC!ZXfo5@exnOsG;y=+)%X_5DN5#`PQtK<)@mq&aEj6y zf}y@d#k4$wGV!=7U^fF(hELZhUQx4{?B#1-8^mz?sFFK3s9&+8z%;|V>H$t zc@28jDk}OAuwH7E>TyG^(p{?u?*QN=pO8iBJQRkoHVPGRw9iiVe$LIF1YB-42b{HQ z|I0ZR;l=%M%UZ_ma@I^uF3734ZO%h#eF%B ze5J|xSzhCz;2W%9+{6Y;nt2Rz5XF(=Q3qtR<6d`U$vcw4`SoZlN8kmLmEDhY0PlAJ zUh-v9!Z)v@Y%7S=rXTJ#z8&#;*P-&~Ck7w79u=C9KwBske?WekHWQEK6sLf(IZ8Tm z*9tL4iH||7*JER&SA<-5Kn%9*b472mgbyGmLg_n$4=<((D0N^t%9?(U?Ulcu1IV=8 z0Q!Xeq4@r^Ym?3xdgAw@`uDRtb?H*G`W>jOG&6$FOofMNBMg7xAa?)Mm3`)$A&5?Q zxZF6f(B<%}WfFz>1O-qVpaM{3XB*WI>I$u!S(5W#TZ6+{8_R#SSPp!n?2K^HWDa3g z*ZrFox)8r{472u*0%434#*zn9^4<*}Bs~g$(N>4R{O`%jjh%Ec{Q?z4I>G?!@C6-_S?M_~X2-0QsuGth2ZJbl<^MjzQBchVe<{NU;}|q8A1u{7Z(HI; z9vsvA9vi93`b`G|fwG;iu-}`2WW{6wQl}dyxux0}i-FTdOt9mTIaiJD0DnCcOp6#Et(1LIj%&yMvSs3&fW=bM z=r`P?0e1=(_)7J0ClEGd-m~d*!Bcqj*dpEhH$N$U`X-?I1*&=3HwiqsvT!*6gq)vm zLWxz(jfJZobvvI6>!F+1t?i!*GAEPhYmN1Kz5`)|6!iene-ixd@|P_SE6mse4m*Rh z5-cdFQ1+n98&Iu2iyGAaJrX-1eG~<6TnBKKie9h5gn5!B+{zRoIn~}>CYMzs5Eb6a zoV!5@8k!C5w%W%9sM=X6&a}QDYT5>SenNEn-kWjQ?Oj?~VPjlz4;xN%`Ht>LRKD2g zk_3oKc{rxDs!8(m=nM@)&(Vz~;RWO_rU*X2(L2q1sV|fnKpe+rsaT*|VRbh%X2*Dp zVj})Ia7^M#Aljf+MhDCC{onq8qj`_of+(Ss$wk^T0)vx5e=}9vDSJtJ6gaBR#>U!P zudzS$G%Mj{yTu*lRBWi1n6HDdP+psIwBDtDW?fTgckPDEa=G?~gtuExDMH_W-7k=5g-xaMww zafyRUz^0dwR6K`te+#tJ;73=I!agMAQVZ+JS@@WYaej~2S3`$sXH%L!LYr^-w%!+r z3!b>um|jHUv?*@BCSUs2vHCDp8uM)-u2c1s>E^ZN#a9zswa>(;k`Yfca7>HhLqq91 ze|+$t8BX_lbzSP)PAp-XOhFg>MIN>ziYfc1#olv3c(fn~%-7d`D$W zKI2tqq@ACTem$6%_+|Fvn+XWJW>Jh^snSRAslBuq!k|9K$bIW$^Zc!~oJ^y(p;p4sbHHYAq=b z=c+0+hO9_3qam|SM42@*30BJJt&%9y-a9TFAs9UL>cP>ZCJeVJ_@Bh;pAUX(IRmJ) z)WHMIfMlcwYa+$gtdnr(@?qxhbT!#$xC^k98)MgRq#W+w6GQL=38;cG$r+yH4u61r zixe?5xScq};eR$5%=x8p+X)%1bl^q`=Ns4z8oBW;@H8Nu*G32dxn+)EOzmt`{|e%23yzZO>Sm^v|u?3*Lx9ZiR6@6F}H5|!Rv)lK%q4Z0*(g!dn!op{^6u$X|KPXetPONQ`@B?)vI0byX zzyv7p!B9+l_czF2S)8H*=eyINv}!iO9RFD2hG%1+W?N3`c%-rbx3uH*Lro3iU8Jl% zE-vmKk)IV@a;|-LlTWAg+lzLK(lH*{TI7hDwzp~Rf6ulDK<5h}->9A6nGT*k6fNv- zso1uw{*X2NpwQN+Gwa9|A8F$CB?!1FH!*Ei7<&6k6&8neYR?zsyg+5>xjT#gg^|A` zx_6@}=~KtqU8RZm*Daa900&6)qj%r28=^{T$<~=fvKA8LTKR#ICO&>09?IMwPWp|R z+2GbcTjL!{ITq^Q@<()X{iwDe? z$If{*Bt82@3&K<>!OaJ3TOT^3I5hA2wj4b>pwr^y&LO$2|85M3d!y z@)QT0v^vDHEZh{r*d5d*tS-F`42B^^iyvdt3fJCGfU0VeOzv%Vgkm%!$!8<8K?CIt zzRm+7-u9yDE*j@F#a(VXe(n4@i-ols-H$eH!YQDnv(lr)t{d$OxlwCPz;9gWTR@ub zbsi`rR&?m^4o}^v)2VQ-&yQ*+mZ-*0Qz9zj9^t{}H5D$829D39{TlMna;bgcSIgT) z+PV-6j-OT)ZMbYwI1p(|94su#hLw>SswOYZfss_f?2;6}b%?Z$vh9=RBdlPHlgn&q z^|a-?z&i*2tkjy%Q+xbVMCaDjJ_G7@)NODYzm>Y}Ghy{k*`8AbF0P))%&YD6kB^mL z@)JkADGCb2FMg=lC^#rM%5o^*UK2O`S1c@yd<5Pa&U6CcUv*?*L~(Tf zYoFiOTS^r2zD2LmWM4T4-k1Y2diO|~pjZXBdU36ItKg>(&#Y~rSybdz!J1H(ZPfGS zw)5DbOgYN;G%wjQ1bb(8yi6%T)wFwij?*iL{qyZ1HzqDw3k830TeC^r-Yi3-bRGMk zzTj<|VVTri8W(D^bL4HMp}}bnxn~m#QR~Rfm$K+(rHiE=d}~g-#ImanXi~6u!xiB@ zzQ5~B2DF5f&)MjxNVL6Z;TJm==IWXbc>%e5c9^+`Tg6P+`PeO2B`sTX|q)Jc{R}_!> z;hNy|aZ0`lYv#~AV@%>gj%3JpXt={n#x_cT7>9fRCcYu zigJwM_^fWt!)r)dRS)@G*uxXC$+HeT3vU4dh+ig+q9P!wYV+OA(JPDXA8%+rxgT%8 z`!Ge9&qd?>9iw;s^L)WHVHafZAl`&np#$7>SnGXi22?lmGhY2}LDOS)GEL9jaw;Zf zZ?;SbdW(Kso#{O^$o#FF>HLIw2+G-$`B9LUmnO)D>EcZ~zG@0S%*e%mM+KfS0?EXpH|C?tNou-lal0YeAy?l*&YenVM4BjU;k;U+b8~vf96vI{siSh@ zr+Y3^B+1gV)BzJ#vX-F8+d?adTADh3l>=QSXuO}w0jA}{;nm(995lEHab4b`9i^sj z$9iRZl-fF@i9@P>lSoU~NB;t}rL`;C=egvlt8H_h1%sz9o{w4{%O+rN$Jc`Z!?vJ3 z>(Ln@H0$kP-E?5~)q{|$@0L7DN8bZ<9Lp;hB4EL;az@-5#OYI>DVns!9A_~iJZ%)# zCL|%wUr{IDclJJ#(EB47i*LD1fO}I}wzu=A@F1S=HXx~2#hM*vJWB+RCDRV8S0|~1 zI`-={a=yN{#TR$NxjLH;m`bMC;+|Qcqfr_i)7T1z{em&dH{D*{F2BbiFe0`db>jrN z*%s)4QXPd|`3bqi@v9oMlJ?bA;g*n0J^8?z<&Em;hcT<`4(r0{4|8Ff1%eQ8c5xN= z%RpM+h_5|y3NLqKm+h}B>kvjNdXw0b8qt$geoy?+jXP_RpiWpsL*AQ7r_&}Ya4mHu z%^o7gtx${b$V;9U`DB<$E=N z5gc?vrl4h2((J%OpIEIaHYJRfjRx^{?oCy^*x0<%?1v%#+K%zDWYt-omlDIs9Xf1 zX;m4bGOy*{_HpI!IrfSI=pc3Errkt-qfPB{y9qKP#lmUHyxnejgi_s)xxymI!LTR{ zqW6vIaQuhRF`mb(XQYs`kNecly=wiA#L|f zhodFWW3Uw5F-nZYH_|N{Q!-PE0sllQO-?GM21;r8>(B1h^1d$R)Px^dW@RfIch+D@ z1Z2&I)iI9hD4Zh`!ui&aUb|MG83WW;M4F>?k)o&~34Z!+fal!PI6 znkzd0XzMQ+f*}ox&E-2M)~%aANl0<(Bq&eJcs=&E*+%%5?ZMkNFG)uXSr`LY7_S(F5$D1O}MmFPDw#>e@}U8CFfDdJ%|GW~c^9 zZ5SpOQ|cG;Y~#GR^L}|`N+K6;WM)&at0FIjd|XjA3D{p+Y?nC*)_jk{FM? z%Sz}XUb%K(?Sf^I%{6++pKs=6@3nM_b*{RYhllExj|QMM2m&fGCcy>v;6u+!PFy!> z5FC|iu$9H>7gyh6i0-{TzAQL;5xiesx+4P|eNqvKXEVt*&zOg>=cyCCFXyPWGfk}? zv%Zapz5bl!OB)>Up46KCV0(sB>d>K?5VeQQH%{<=u#$!7v3?Ujy`GhKdC;3BJiN>E z=8lE81Hh1vuM_%I5mWoWQ4uEjp{CRUBj~9qDt$i(m7#8FSK!mx)<`h)WH5wK)P%@S z%sD{#<1pB!mInUNLp8mtSqg5>d`uz*k6Y$X*)q+t+`S;*m`j@avx!@?_nM$?{n)&~fu9N@3+v!@jXUq`Tgv zJL)2nPH%h`MU}Pxp7J@x&gImVwC-W(5{2AM-Ae^wC*ZY#+$EL6bGAb)p>zAQXF9cut|5&U9YJYal16e`N}FvIk2UOm!45IGYAsy|ZPvRCL6c`%r{ zsOx%-2b>HKJo~Sre@PWb>32AP0__nh>J3xBg8W`&OETjEYUCFK9@Zi za4|X8y?1aAMs$VLU*52h6kqhjbZ>#$+VYag*JZ)&NTO@Cd@AbBVm8-3_EX5&A1{xi z1dkjWeN3<(<2|C!x|iiZiz7O@l{+x-waI%{OVPu2qWnzj{ArO+v+9C%%jd~1Iv)vZ z7c2!wo>BMriN0-TrgpVm+73mM=6@vzWJ;)Mi_%}Sus0kvSlw>B$4W`p51w~UcMkYE zj}{=TURaSfb$@e!)of)}!bMbvB^oWKsH`gEF_68;{lMNa>5}bnTZwNB++rKB6U%wr2M)_2^J1 zQc|A(bjZ&<%`*JjvMLN-IttWs!U#k8LGxNdJn&5?Ls_v0jlG4yYO1GB=~zyab-5zX zzS=e_wI<*bDk-#&2nGYW9GA<~{0rht6aASwC9eaVf`a*OY;!S zM~EABw}i746OZ(vaFh8V;Ir#*Fcw&U#-hh9`7yJQe2ea(HBxgw%yba@1vHDaQuM{UYl;ty(OfdGU7DHDM*> z(Z1DagF{K&XgjQjBAw`-w@(mqRPzZIX{L)0`w^1E+rod`TU6R4blx^UQyTR7R-2P% zG|D#9oolO^kA#Qjhj~0;?8=0O&9kq!lp#J7o*r8Due?8h+RDWCqzk?$6|DVhs;zh- zNBTHVatf57JyEyEpOlmb1RF|vZV!~$M$H?ujxT*tH_(+j4S9y}iA2LeUO0w%I;>2zqkyLPFt zt>Txqf21F%kmuOohj3#kbKW`r!a#L(mrkj4w`~@ntu#?Tnc^OS|A`rNl;f&*6?P_1 ze>R=03pFBhj9NvilBl7(OKz#FBoS~nchfrboT)qKGPZ=<&2)gy`e^x)$(otmT^*7J zW9W38(|EPx6XEhlN1;b@Ex{Z1pmc$M(I(s99yBNGSHvzCqpn_jI$o!Axv~en%2-|FQ#pM#|zjwXlxTZXHEhUME-fDwl^;FygooNO+IG zBz)3+BcGn^IN5yN(^3N7V$H#6xSqgRPjni_#SlT(SarKLELd+-vs96c_I8`AO&~3u}awA)_jAtaKEvIZi9oN3K?h5z#3wD-bdbx|MXcbYFlu5^@pQ=iN zxf;UVRtW2?OQLFfva>wa6w<}2io(KHzCIf1>i*@4yv#}J^V9sz9<8VNVsE3nlFB1% z=Z_165u@@4Q!amp>RNv^6h#g?oo}6njtIjzc=6=;zw~1=rE%eU5X_XjWoKxtxtPiq zp0ZutH-onnv&*aYyni;>Mfeju+)J>6av=(D6sdT)`f_+O}r`zZm*qwHJtr`y2BZ1zJg~-NCG~5w*6~nF3e#58e~myZf;0dh_wdMdKiMYPxYf3mGT-=Ti zv9Bgx+Q0NZte(Cj9aYZ7Kib6CIu?jvGgDq_&hv(XL}P}gb$TO7>;1r}Y=V5Y3x}H0 zyL}Utpujhuf1}jOCeaJXF(TDAZ0oy;keEEA^C@~c|Jg|r!dBTTZq56$CTPFAf_&wj&~;M zW149LT~IWt+)c{t$=C*JXpct37(n;>AlM(y{xM0q_Aqe$PqO}H7PT{W>Vstc8%uOA zK;p1Mad{FVWO}ZuvGz;z*KvBqRnLBDsUbeDhqt7B_Dg;lVk=(TdlU57yB`k!OG_Sdp6B~RBQS! zO4M9@^=se=zhn*nG#uMvN$IN7ynC5_GJ#aI#dG0MmHCohI935GZ2)J!AhE)R*N!nO zvdIcuQ5yCx7)aVXQ6etG24x#+dbk>FIny4!Y83*{vlIeBNn5tsgj6uQeweD zuCa$9yZAgu_b2KJCOFt|IN1Jh;(oESwNw2r>PhL#$h>NPyoXcuBf zB01Z`HIm3PGXgUW&`UTR2Fg*2+cXlpnmnwn2(w;?p1T<(?}+AulNJ6yWu5o0UfBA= z7G0+j0^&Tr=^_OBp`#+?lEyDc`C2c;FVx}U04;^X+0lZpk;8C`1tEQZX}hG0CPVbn z-$(kJ=p_%R>i~E`pblEl`@59QBwZ+L<)9{?Q8&8i;nx(}76m!iJ~f)-3MIoG`gWQ^QBv&b zN@N~qW)?P&IG8swQnSE5E^AE>RHQAv1HgMqld4c>Mzh12$6 z-01EFhP+ueE)F3JqOmL?3vKc3tG{~SX~h2fML!o&MbOC&*ocP~u{8vl=^%EKGX;kIk?~6$N)&BppO!76{(>fRXg!|NG|Mua~ zMCpy;(3I1HG5(|MH9RU%81w9V-2XKHf9z-%2fDFcWxPS@|FG`Aie<%sLPYGup#NY8 zIzBuX4SG@>)cfl{yFd&cD5UW#p!vTn_?JxvW!N#4hGhv=N2>p!3vhu#N;pYc%Kuhq;y8!s= z^@5Jm(8a?r+ZCOJ_{%RGE_+B=f@aU2v3b6ieD~=SpZColy5UL2fA8X_#YO0kLoHng zyn&m~RfQxv3Gf~2;^~!K0sWZ zk@)YT=SoKwx+P%(+r_`n5`QS_hjiW{C8r(aQbZzLrlf6v4R^HUb8ACy7xcBwIprU{ zF2)7FmB`UU>Qq;5)I%p z0y_Vp#n^v$4*si&5=AwBsqG`if4pmO1{`=hLU_eRv z%p@W1?f^!kACPgkOPYkmvXA2TW>nE8+W zF)-loW`h5JZ|2RPL16!x)wcseHQ?-<$$YH_*ZwN4{!m3*5`W+Ybg-uglr3%1khA%_a=Y&%rg~1)}A&5^4rE!nSOzEu3}r`JfUVKF_U`{H1EF`t-m_zU%m6T z&vw=LnqkD|+!L6+S-OXV+P3vwfbisy_emgy^LkG-nBuDKx#g{j_cahl*p9!s>JdG1 z1;PPIaSEyG@dUa1n{_WyGIzXt0r)%Sj1e-Vj5=cD{4 z@KVSoZO1-Zt5#L-_p_9Wsp^Hdssg(xLXUUnM+cQ7CA4(_iMRfGE4}nmJ2=Q$x}Nxx z_pThg<$gnjW&^LPUGqhfC*bmWJ#3`{G3V9dscMcy&L`LE@5;k#wfR9J{Z8|DdPKNs zX4@l8H+w~kKuA$X-sI`Pzdfuiy;r=TH3{K2(fW0ywEKhB9(Y%FL)c6DhRr1H;o_=s zIbqyn%6Gv*Z41q!_&|y$IwuYdz!JMzW*8r zccTB>@P)?XmVF?Q#s5Uko>WP7fr1u+P3s^Sok(RqX-&Q#)1kjgWg&fhM;kajdt*jx zc}ko#RJEtyUf!SfbJ6_iQBlnT9D|#M==(7dhR*m4Gt=W3f`(33YzSiqV zCBus;4R8cJHHlqQ`R$;Y$a-u3O>%`q23PSXSibX36mX0JkJGi;KS-Q}x+MAfHa4K6 z-WUH)&9{b$OqADC}}%`uJ~@9ams;u%?=!+Li;2H(QyqU*fP5uiT-W9)`I z`pPDezZ)w!tgHm#@bO`CW>&jKVp!t4K@-n6-~z8)&>c8K}lZ zM0jM+7(}61?Jlb8tpSSmxF68F`>?yXX!J$0?`}+ zm%)LforNw2PtS8~5oo|5)HaC*NkC`yWK}C^+$lT|BfCW_q?BMgyqEpIl<~K^;sbL-IwR2TQq%&`EA5~mC0OF!IhU= zlEOQ?YAuy-OQ_S`I;Y3*blp0H5v(YF&jU?yZ>zdP7{x)YAA?r`dSw^H`FG%zq)d5b zJt26*$l1q+Y?`#n{U6{y64(08bdO=V453B(*aeD9ExV4Vw~oZm&QK>p8cuGM>cCKq z;|)L!*Qv|*?*g*DyVJ6z9P6Ce)_e#a5tP0tse|qegt$KP9dit(ia*-t9MMW&%Mrgm z2KL%4dwWzTTM3}v!I2Y6vPw3=U1FFb5P~kWGhgG+$DT!blQCRShghN!hBF)ry2C{A zR~p^Z;aM*JgNla*T9fn;l5_kK-i-Fn>ZKBCdidq27{;Y(q<~e;lv=B`|MXgajGz`I z%$07W6GC6Mm(Yy{3pH3Pt{Of`(H9q#G_$1DiedJ-91xsynTPIGEm@_N?*d^W;C}Oo zw&PB;vQW;{)GJ&OoLxN=7jW|#t7BVE3%AcoGXx)^$Q#2AE@}94z*ESQ?dfWh7y!20YnHEzAJJq?b`fi+woG+jRnsq8VLQZPxe=IVPv28NJJ zVDR1DjF~fI6HcYM%+V&GRPb+MX0HKeKv(da8Bi#FOJ!f{8`0%?v+VF@Y{O)2Jp0YpBnwD-$ucykq*W->ac)TZcivicX z#n*qXq5v{CPJb1a;Sp;xM_Zaf9Xk-OWwQ+gBT2eVtJR8~Lx~p8`vMX91gpv2ym66V z2wFdSmMG9+_sCK!`1K(^YVEvLRk+XBo@>@tL&VNeWlH>h#ozrh?BhFLQ0HkKn@)e^ zyK%j%@q`=L)x#iI=4XBDg43xJo#*#wO}a3vQPyOSgNDWF*7KUV^9z(T%vfAL z9HcZw31)(+T(M!Z7ulgs4)|b`Q*_be1@LCtt?P9H8>39sUQERHkM}V*Cset3vM4?lU>jB z5mUst3D4w{gp=tahw-tq1+`lOExr$pUmBlc7?Wi(F|=N+w8ki;b;1yspA$;>N57XC zHd?Pt3-reYdP|!s=f0$CmSJwG=8jLoLpC1Tq8iB#SJ8EzQiWy*RqDqkRBjSbDG0Pp zzJw|2ue~xBil;AY9#5d1Cm8F)K}Nuo=Fd)ZyZgA<$rBtsjwm)^o|m?v>vg8u7Tg#P z3ONqv8@Y@WJpU+nX{-{bv?bwhdJGVPp5}dDQzknu%#1K9?)=#Mf_#D9*&QhU!7ee% zupHJkdQni+1P%o#1VR@~XGjl#05;;BDmK=bBsT>v=y_lO@mk3Q34VTTjJO2Q9!}yq zqY43C2fr$IR?kSLh|P{MDUu3oHp>hmiO!}u4)eCQMx#$>>Dmj72!T74A7w%f7=AD! z1#p;Mn<4b$^)j5u5lMOHr{D+aOM98PldhYB2$b5eO23%m?+aLS!o$^H!N2IsUUcHM z{%+$wVeat?3`7i@c5_$cUVf_O5#fd|%W`4I0OGYPLn39XUPn4)g7&{99%)IbzS`r~ zq~2@1ddQ-Dk;mSm7ki@X(`;&J-klg_`tEg$#CTAd-kGbOePIIGfMm;R@*)uA;H-5< zaHbX2{L(Cne_^x7McBE8M6xcBsbAJ|uGtnk25KfWtotbOWlL7`YqTE>6`6JdGGJLB z9ziC7%a^f+m-9v5goZ|ohLnceNfv{DubkAerVAZ+Gn3)_Z{UbX!!H0AurJxQg_P|y zF>CVQb}QR`K1Qsqn6nnp3F~9f&GJUJ#1v z)E&KFJ3pLRo~%E5(XZgW^77SA55PQ>%s=-NsoFUV7Te`Qfc;^e`HyRyfArz4EZCq? zI{mxIhj=9APi}#$SN&C?9hgQ>oD(av?h^I!vzo!_EN}K2Hq!-Tc_UWw)2SEQSS>uy ziu6hZ6=>wtwe15G&}|7N0*#i%ZvfjouGb}yx?%+J)^nLP*6rU+9O9ZEECFKc7Iciw z_`Y6r^oWL1fD{KhBp{3Qj3Libnk0nig&qy<0G}-Pp$E`$S~mZ4;HLk;Bjx8TzW)2N zsjnacA+8G>F>i@M6pl(Hs4q3baNOSdV{aha(ua{(9F?x51#}OP+yMc3i5&Z?_lh}v z^;6l*6=)P-`s?i66NO)2CF}@^i9JbZ=MnV2xzL3QXBzzYIZF0xHBmD07I(y(!ud^e zZEGlymk^;wJ|dNv+mCMN+-jTQ0C}gVup;6TV7c9|M335R%K$dX@IP#lXxSiV>Wha{ z>Q}@1x{@1wa);8mMxvtFN}2`y=kdMzR-cF}MQn!5SGeK) zJ_+v*v6=Z|=#PUSDv)flz2LSQegN>Xz=XGnH3yOA_0EG&T=pvZuCS>2;56>yqFDCf zIbtE*BtKc&@Fnc5O%YjNZLE)jB^BSG^R7iL;6Dp{oAr@A%x1OJrW1)7N`pjEBM$3e zsbs+pU!%3z2b3yA?@x%PxE!uCB)IpROhTwxhGv)9`#3a!;+BS_K0lkMCq%$_NC;8E z9|!`d>pQRYz#DGon7C9$KC)$o$uE8Lu=mB%?MNW#*{>){!jXDk#7d;6RVFZx5I(DE z9@~zxOmf8nQ=X$ft7|srr5$J_^*d_V{3vm%5lyY|lX;?sg^fb?CClLx@2}=lmM{=i zjTG%$If#G3tFSDJiZ>%3ei69S*Bjhzvt8xO3P10npFMjoq*Qv0Ey`ke4|rQAORSy- zEWg%pQj{H%YgSIw- zXjnkBxkM^~(%sc`8`RPt3(r-}O(fBFB;PR!^LN-IJt-?GsU$TGQo_ZrT5y1z&(VjF zqA{#6$)AKKcD)4-op(9A>NW;$zN=Y^=nP-cQMFU5S?7ht?z50+$Fn{nuXuyIfro+9 zOn5@6axegSX^bpR)-i=&79bJe?zR>!c{QQP6;y{Q-Gqv~%n4~t_$$1x;5c;6#v#0C zPXq8)MYQ!!>Swl?te-r4OH|Tp^caJT(z_Nm9$d%jwQ;pu22a_YI=)z#Y$Fh-hN3_P z+{gp49fneba7isEdRgYYcD0rY_oa1y)%c^d`i}^%_XxhE&8O^8d5r6UNb#f>F@~jt zttWLg_`S#@QA)1PtMHDtQ3@(1pEe@$WL?ai^2z?0wFYQ=cZg8g6|pJOwwf>pvT~OhVXiMJF7f2R?J-A%}&IRWb3~ObP=i89z)t zmoZP)`41+gbP2r-q|CVfE`vS+U9%*yJz|Ojm5LP4BkmhE+1QNZb zxQQaRH>_!SW;?+42#L+_`)ImSR%7KwxVhZrtJ?u!&)f0MZu~z>r0H zgCxWLtUL|=%KQU&z8`*R;#CxNShEpotw=htAJ!Ko&tXaANXb*!IPDyurn;{5UL^#1 z`YRtK10V$VbD{zqanu@}hj8dB8AzU-IVHHP6%8h~pCyOqjYc%Pd{cH4j*qm{f*b}>8;Q8`@0H)m1|N!bKd%uGcY=ooOKQOd%a_M+vYX$}p)6SR;j|MWWg4eMct#RM zd!(TeRr^3m7&LAB>k+k-mTCpKSTzzO(nvZad_i^pD2I(wKgdA(S^N|o_B8Uac^-M@ z)J;xt743wtmZ}&CzdHs-7~PymL$gH#iRFEvPg6}!V>5Z5h5vNWvCo*_mR2KHGYqTv zTt6Mt;7YZXy(VN`iuLucn2*657lqoqTu%L_@6xwlOYKyi2gl!_WL&$=iHwUvI}M9ZWK# zzOS&8^R;Dj4tLj7{Qxzk2=uq38%5{e8{GvLu0#W>rA(8$kZsETPh5fbu(AEgzg*L^ zy>ynp1$UAe`&ogovV63~K{5W0?ojVA#GiVq2 z$8L7M!@B+)Aq1fWv4}|I;6KixfUp_fuo{{dsXIZUM8ck(g#Nx%8ll3Y>X+EW?-3^l z6}nt}z{kda`YFAJ!2EZk8CT&T}&fpeJwzd)1D+$4(H0E*r>jQ9yQV! zp5+etJBMBn)VFRTLkMMxutg$;cVdx42nQswPs^n8x$!A@63m}vo=Bz{gecToyi?+< zP4hJ3;K+WJjsP>PwMF9?@%lyf&X75CW8n9uan@`wXOt>{l7v>IVX`{fAWVpA4OO9sseneQ5-u z4o6z6AmRzQ{O!U24k!WSW5=Z=2>TXUCKUmwv2q|{d=CXa!MJQWtC&(vS3)Pi0Uwg&fzi|)EyQ>HkeT*+waVAg z`{2{A{qbpEEfjmhS_YpAYiDNfm~onO(bbnsMEw3{9YR!;s{gT&nmBU&>xxnISyG62 zm~SEWFE3P0#50EEw+7kA4=xhZt~+X{M;YGh=<38d`$;9qO9w-`DbeR7DoyVa)dX{* zzn9_f{SK0ppn!n)eVtp6ygPaP^MVN&L*p@qWI{eyRF@z==~c#{)ih)~^sLQk94VJ8 zE4mAVz@|D@{5kexz(wM}HVsIh|Y#ty~np5DDKU z*#J*+*64#{-lJ!*0!QVjLMm5R&sk-Q0C<}l!N%jx4}oVZl--wRYutx5%JRHa(-%rq zX^I6VPTspEb)^o;!f&U%0uPgcY|vk&7?-&t^57kqu|L#wwxjjyq}o1n@y6P)yot(q zC?X&c@UKrQdWyd0~g567#eM`X92s~IO zFu2@$u)>&t|A+1otJaGMBYtBkq?aDK`sIUfrRAeB-{BY`AIxQj+D)PBxwW{Lj*8P0 z&d}%(p9v1LD;wdPOPuVy14Osrujm#Pl0QB`6v-JZV5R{AohS7%t0xg*B>c6tVXs-y z61_in1UWDedYLpL&1@O)+7X4s#>&5Yf}OK;0{vC~=0&E0$jMLLL^X_bu9~;y@v(xYMZrDMXI;%@^(!GZ zF>+iL7SpTjr#6PtV?f?{AC9BoXZ}t~7gMs`n#@JZF>2Evz9_V(?j0GM}fp z-7Kn4GmUyD%LOQ`^4)Nnj8?cRbvBee;Q{{><-CHpeC6~s3Fvr;Dvm(_J@<7$%@(7J`LdehpD;n}btmM4Lx&up5V zDhYJbcT1qMD<=uqe9H`Jf~DsY-i&KXyUt z0IV;cgURPzQeHX*P>yWQ7&!1zx#)xWfkNztjN>(WHzQAjm)hACZl1-;9$=J(0!#WK z-nnHSSWXC{A7&@)r)@6rsEuMTzDEIuly327mJ)XWfY{FS84EX))J|@WLE=`a9n7p> zToZ?HHi7hmKzb{{w+kr=^xLJPyBB= zQs(=AsR+`QH|-P_HM6y6A`*#d@dc#(#mJN59>GGw;wDpk3=?qf=JQH)U>w}cW&nc zXt@_+Pe>*K%6KTfsR3!{Ax%t}H%{4;{vw<|$pW%Vc_0Ou1Blue4-t(#n{~#fpWZmQ zn5a=;>*vZAEIcPxaeojYd8z@?XS5g+ol3ciTe6Bmv!$|L zXtEa++4s87(T^H?UxyzJS({%KuozhP5g8t+&l7ar%`r-kdBr7sc}h5^wb_MgrfpFb zCsEkh&iH=&5GkIxF4*@af3xgU%Any6lgsROMnJ$#XUW9+vdXIV8`+PQKK8;h%w*C! zMGcEoYu%#=o-`gM;w@$bPV^CYA*PW2C*%Ask<9C%1pGxFlch%`(BI+ezZBsWzmklV zXt5C-9m97duTdg~;SxI0WEOd@8H612HQ zK6yqSfDm7yTjZy*hbTxAQalL^v|*);^4U;eb?-IvE-q)yctM3Sz0gec=C3^t;~eWD z*fv&~#|7;?wla_Nnx1fc&07Zl0hp z{4O(}&<+^~DfM}LX(WgzIg2(&i6!gp7g$b~?dY07MTl5rpYFSUMDeV*AS+JbWPEV` z7sM-XQrelm|S-=}W+>8%xnpL;nR;m* z1e?!-1~=>o_bF$Q!&t2OR1@iCj-RwQGC;;rl@cINo+jqlk&8gm2j0rPF=_|#cH4>c zwJ1@jH62p@Q_(;$C>}YZ82g`CyxqbGWXeTT+?X7Z5Jbvfl1<7YSx%CrEzc2pvsR+| zv~sGsrmA=VS1_zK7Hc0lwJUl@?U44+6LoHqeoiY^V<*00c9SHd4vpfGHRpsm3&{W3 ze)H<8^`v=|>F@}9WA7%%6@QNxm-|rVDy^QI(#UR)xb*Q}!n*kNA9YRi4^W7?)4$*h z{s9VUe?+BdB8C6Gu6Q0B*pm<2^21+jNJzmeva*TP;^zNA2jsE;#oaxldBEYBb?nqW zyg&po`5~K?_i`=Q_IsrK-XAA&u@)71|4ot8-y5zNfg7%@$7omoLRPHb{H>vX{?{5h z-(SR={$~JpZt(e4vCUt6(s%Q}1BxjBFPbUY^uLV8cj{_fyoW1X3J;A8jeM#euM z1xW1t)oSjU+ZCA)ce{ULTE=+51FL-aJCy(Y&4T$4T*CIh;u8Mfpl5)mb3Vy!cb)rf zc$sg^aEu4M8bQ_JKVJ$Ip8kCM#eAkcVxv@HxVL#cwe6|WozwMqZvX4=XZXN05;Ba+ z(;qE<&_^?zSpJ_6Lhu)<+sXmlc!R0QK$mFM#{YSVUkLE7MYd5W)5ZS<;{{aa%j=tZ zR>rqIIfCTAK4hi;^&1rvfMIZaj7n4c-IUW#*qz(;cd`Fv#7JLk#$*`D_B#s;(Ef8j z{L3YP82)ZD{@(I`1(N}A4}E*rxs?;3@Y|5YZrb=#?PJFf+W-UFhVH)^TaXA0aa3|l zW(@e@r!^ThQzv~ECfWHpuJD*!h zAbNo5c>sVTo2%1pn+H7OWWLhNk#U3nImK^A`M#J>JHl87BNdYjK zkRzxkOpf~ji&))^O$VCzH@7q5u-GDEYg!cKJb*E$9Fj{Oj{G2;4$>Q(&zic1GS(*M z@^G}vfBo-RvCl{x@{cGwClrMD0rxwt^hn}2`olsJQ!AwJ>H6F{`@EVU298%A0HB(z zM`36VlmBK5T_PeC^8g5C_)byT)ix?Ir_~Hg-Nj1iX2xir766%gQV)L1VP7c!7y0}z zc*rk-NKRtf{w!T=l>Quq7xIJt>bl^bmg8O`Xb>ub;K2jX=Rx$!? z!#7uq{{svNh>D^V0OSL4%>6q6>>ErR03G`_Yw6!C$m=Ho03?~_T{g+T{U<9RS^xXW z|DD+X-9G;x6Ou13MA&y^j~?CMpDtkS{<9_BUfR=U5>9BKpI5z0X1zzn3*71PSFRyV z4_~;huCDN>A<92~uBg4THZW+=gF3OVtql)W@WN9)u79*a*}wIFO8>;xpR$(03gFCh zxRgKdE;hM7&csASeEsM#lE|Ycn4cg0^UpI(x6Ac}-G=798l%^gvBv(-|JwzapGES> zKSzz4e+vl${mZQnPf1pIM4owRE`s_$pPUx>2n5P|Pxt1Qot*Kk`+tn$^{6@0Yf7i&A8)e$6O-<0~nbWpQn?hV5dF z4AYR&(e+_l3j2uA^ks(WLBpU$QRxQ<>xMLgE+LjWsP|XcFP|Q74qV9NqE*ipbJe~Q zl!nu3Js(cf499fv(nivL-Fz=YrCkQ7)7s?qh`@E~m`{ToH}of0VxEnOljaWe-t(BM zYPmOs%0Aiiq<^SNLD1w+TP@yR2}`W{yFj8L>I`p_UQ!RMs%g@u8nrbxL)ayrUgDz5 zQqfv51kWq7dG`hQIE228OV0-uo@CdFJT%O2b4XTV>>~`cb#UC!*h~iMbbWgj*Gwjj z=6Tk$_1LVye87OU&Syhga)Ze^!WZF5q0B9V4E60dCd)$m^y@gXJYSOBcMftleNmEm zH&e4A{7z_xl4Ksu4L2c03ta+k_lqXA8m9Y%7_S6+MqQztMWwCr{A|7WMu~l-txJO} zU8up40?&kRPph_P^U7dN5D0xz)i4idL+Q8hucY4Hd*u_Q!@EFNc^j@dJ4LF5<-BK4 z&T>`J>KIP3TfRa<9oXk?E`N%_lUVzRt42MXob<7mhp#d)U18zVN$DtzGh<3-v)3d} zKMghliENpIm!58B7+sZ=d-k)sXnt*%NU^ffz1R$u-y^u%C@R6JNKN!STK<}Q->xd- z*}ari8ijWjK3#W5cE4q!>vmr7ZPU7kWC}$^I7-W-gmb8$nshp3j8YyDX*@ur6^%hq zhh1wEfQh5@f)0&VaJ4!{hA{D^-a@e824@!ku~FQ++cRz`hh<5Xx5HlM^8?nZVK$k{ zjK1&CURn^yw_yV4hMr}P)TyXe~T*-`-84`p;H?zj#B;;;^bC4TH6p^d|>M$Y6>ZE^Ap5MxrYvH$TM-}OT z_B{R*9aX7(4rZI0q4SMk>idKJ3=0VDkmHEJ82*fE;2iLgT$emXQk_oBZ7jO(uJ^4? zlE52u8Tf|}s*R2sST|n!O?F&tbl$DcF5!K(wOp{X>7_(KaOa1W`TOqBou{1-p^MtC zXD>~1lT3TRuTF!?@O6L=Y7I@DLLktk3NfppKY?xY zG2bmm{E5QcrI(W`c}JdXLvv8n@At?5p=|QmS|G^C26jHTX9=^w(exPIvM&f%DUKsT z6sP0Hbuhv)o%77>=L1HcqcA%xI+;KqobPx}GXDO)BG|Grm|Hs_5bsYUhyY(F!A*+2doQt+x&3XI?UE!KV}?zm`OpU$ zrs!m9&XWTGW6WiJp5eNfyJeD_=0ez(asz2>gE$|?_DzHx(zSVB&6+|}t{L$Tu}28_ z#p9UWP3(7mS-(&o@W|OcLz{vNGwp`pVmpzQB>sr=YAxUEt6ds5&rP2jV33Bl+!%ZVOf zYIr@HVX|Yc4UQ+yH_D!Clx5H30y-}%FqO%%z29Ftwos%^J0mIl5uq+ue-ZEqv5^Td z?sOBin&hG#UoEbOxV0_a&$aUxb!tA1P$lRZBw%~~R8?CO4L4W)!L|5Ftm?|5XhRKg z{a*O~GBVme+p?@}tl2tGa$H0-Uk(ewCmEp)4WyWdYl6*{y!T)f?2HS>=;0{T%WDJuw1 z2;2^`Z_L|D5|kz{gh(MgM`xM0ZBor;?O+)fU+ZFNuC^XZY1#TFvv}eKfa_Za4h!oC zS+A{3fZ(=9#-MbMki~pD-E(Jq12`z+l4sL=XQ93<3(GUwOb19-&>S1+nVNXa*M1hB zJtypNF1sW49KAD{HI8?=HaE`!=TFbVWd~BW2|f{9d21;hP8H?N zx(_gF8&M!L7>l75?UoqdYS}g|uG0r2JH*KI1=j#aG(9$f_FHid>-l2GO;=Oudi=Q~ z!uJZ<^#!cQfOc+}GCB>@vTkWnI*XV#nj&iD3dJL!OjkkB#U4y?-nAsu)uIeu@H|}T^D;Q zN>ruX&Kov|mL-Yz4~nQ3J&8>dy{~>Sq;#~dCOkW=kNniKUW7rt&&?6FF(Q1oj1Y;4?V6sE=*&(pEK zg5xBg*2}?kk3EZc4fldc*c~uoHxtCJEBz86(AMc~LQ8nd`OKmx_f1n}5!c$fc-+v{ zo!ScejE`Q`{x2=Hm8r|%0ZEeL8m1U(O59_?5VD>9%tL&FpQ`Dw^^ES6^*y-RVT+3^ z38SXVRbf%7b3(fFhPi}{lm~SavuP?gVqWkhNNi{&lyOI)VT$_ZTUv@uWAv<5OnCVC z)vx)(>cg$j11*kPxm5z|J8kov$0iABE@ss3R9`9{1MX@2AMH*w`Gzw8Lmo!q08S<@x&Oellk>_N4!blJHS!eIjgqSQi%cOjfS3AySpn{-6$~Xa z#9z6FvoluZ>*5_=F}23-%aI-kt+_6NQ-t@tVfa5dV+pB&{@-@oP^<8f1P(ivMpe{qcqng&g(so&qbq%UuC#dH3V16#XcldFSy?OX|HPG z3EU6Hm_TUnl^m?=9lMqK5{pVnRcGB#Gp}*3DO(Nkufpp_1a5JwcIfDvy6iRgo8Y7- zX=mKBh;0{|syn4ESJE+4p+BIVX)d#jFn2O2mzK5pCkA@!g5Dz?uj_F2!7H`V5c*qP zDI^c{x*1Pp$E}X%S6V#Ftkp*usXu3fx}wyP$ydDgow?Vv&5K1Uqnmtw6=DGdDs{8A z_XtB7_c&@R?QfIK-Z)-T;UxpkdT#DISr_~*`uw5g`b*{?S4QHoWkSW(OVtvp3r)La z*FZ1g!4L$s;Z@q0=J@ znQ^VdENK7edryt>={NFg=#fjVaFYFaiHM$927@=K)X>s8PXf;lafxuT2FtR>g=Pau z9b%u(_s7=XyyVmcHj5|bgWsIIR=UYIijaYPU(3=LqhO~DI;b8vaAv&EZ}b~q?M<;k z1}Q8}nuQ3uE=|#l{NN3iPX3nkmH~XfgUWlbMKL5@RJx4Ou*5U6vEEZHpojgjT)89) zGd}X{m+=k!4ZuP5rd6`Th!D9V#kR)UL`M+>GfvQ6Q#1ald8cMve&VaGoj10FzdL7} zuwc`4U=^=z$Xd0%p+%i0fAH=|^6-}PmtuW7$#Xy&UWWtmFkxJ(Q4JEZ$3aU*1FW=3 zCx3?o!()Atq98IlHTE~O*1q)zi9BEU{uhtC0K6gP+RkF>Vr`81#Mb?IqWN$d#j-g_ zQR%k(>AJ$34&dncPUje=>6xPN^#aQK!<0a3x>UCd&5PqHNeMDKG)FCq{vKC0y zOV02XCNI21AED_Op$WiOKdF378!0x1==@G9NqB*oV_5EFoXH@0vE3=hMzbaHL4KF5 zw&9{W`IQyy+9#eV`{FT8m%Y1TZt5!>JKF_$>Gry|E9Bg0L(!|>LofjM1cdr#>36MA7wx?pHr#@ z?sEhZ*++)l9T%%j+tjVHXd{*yB8(XLeBp}Q-1<2`P|-(v=0obByQ4K(QHr)Yx!L}R z$o>{Tk0C(n<;UOXQNG>f_nc>UuhsNSV zA&C8FwOOkQRBg}6+}}CqdP|1kC#AxOTn@BSNYGO*s=B0Z3)WmiOKRnYcL}U*#pcT> zBq%p;19c=yty`aZPG@();rz8@i5Ihm zyZblGAwK(2nyxJ*zA5fQSdyfx!y{aq%?GaA#@9JlRTR<-m%N)vZF1XKjpDDg#?!Qn zYdE{QZ1^L;lrYrqc7p**BnwyT1_TVDQ#*&2PQ|;5EYYR`tFn(N)~Qt9>Ux$seZr?7 zRfJHxWan$+Vkk*H_N(-g6S(FTtx}r$e215EQ*5V(BT1-Y{4?D)8@eI|_>L_%W9wjr zp4&_7%^XM*(sv%`+w&$KLh}KpM5fjgjSe+G|AZ5j z-P$8z@my*p+if=ehKru22G+QL&L4N7%uB-Om2l&DsmI?!bvka62d{=15oKG~8tmS9 z-Cg2YyFkSFh4V7NF4)5u!Z%wG5+97)-lcNKVWI^l)XRBixL}N3wZYSLmpg#Y^cGuq z-6e!ZZCIQi0 z^r%$pU*M}g`y4&rj!sUpUK-8ox?dW@ebUajXc}w}1Mp;q`$yFEyi~Dm8q(@Li}RSW zZJW{+mab-Ao(YaO{osHBsxgNbVEO*#;d2!}Ae{3>xXc~2wKknGQFc&psJ6FvT3HL; zJlww7A&OPY?VB#gc#IU5ju>LLx*5Pahk|Sn&_y>-)v(tcqagR<3`oNjuW8q3Ll!$j znU-&u8ZXAF&_w6`VpQI!u>Pb!T5?+*ss(Ro{S2rQ83~xT!jN}MsXbaPlD5)Z9njPF z`1GwFzS~wQoO%P~rMd#Y(pg}?8c!wE)kPhZWG<`BaPj&`TMf&c2OpE!RKN{=_g|WA z+SYJR+m961cbBUABl6erL}*r)1s+unz!k>nZ4zHaW5lGaTg{lWTVt-DZcI zA}kcoK7Kb%IT6_kDXga*EdQbt9`$|mr$a3_&2q7;uv6wmRj=mmI$OCi+c&#Y-Cib7 z^(p**$7M$~dde*?MU70jEsU}5y_fp5u4@#3nMBmPD26;Zw9S;&mOqCa44wM*v1v$t ztp7#semO;i^bMV^y*MANbL$-y2o$|;>2&SVWZ}OH?!32n7a)HqMzaDr1p6@~wN$Tj zLf4eS*)q`YbX{ni-s5{q`bNOBmwMv0Ybh}vh%~7-jWXYZ-!?;S=cBdnUL61wkz_Q- zd579H59<2HBykJK@6S$jeu%pi6@i&^jGE3DE&O~x6|~* z$Z0OPANswjjU+Fq4ATL+M*2gQw>c0oD$KeeqnT^tWtCSHXWXLhVX?R%&cqOPvNKz$u1LwyMQiXDN;*R4w2g>RTyED>;qfZ~@eYbW)I}vsa&V2JrgXzaJmzwvb{r%qzOgGnWr4l+=oVuZ33GRngNi%sYXE4X7 zCS=sJD#}YK0$GHwwIyi@->xsxSPm~N_ne;kH14k{%QGX>4dDdqJmU3dw4*Sr*Kn9#`5fSgNqpHyCMez zb#pTfe$XDT}$y zRu`zNVcRkFmeTLHIy0{;STL_(^UI2GH{5p@rSTqLJA3sR-duX*+yXrW3HJ$w(l&}a zs@K3%_zat!0V+r|?a__>FpdPYwXSY#SGnjEv zrY41SxPDk*tEs8EGxOqBUHX9PK9sq}3Y+;}?sR!e9i$mAgY!dENh-gNX#xub%4p4& zNR@Vj5S46BfR;Qnh5a>)1>`+u^tJ8zU zrih}@CJv6;kMHx@DxD^=zSdNUFjMHcd^E#;SMRCdz%nz+w+aPtU*{%eg08Mj%j4fL zgx$)Lj5m^6>L&FFq^o!8`0(!IjeaKuch&KFqBK_}9YF+1zY_oI5*GQZ1Z75K#DuDeK-X%YuP$5T;O_f(|*eAm* zup9q$s-xx?!6&Py!n{5|MV}`!*5RwBU;C|xG0ts28%`{N>+P2@+#OcJXeeql<1+Jf zE+sDKn)*n++X?8V*uH!BAP{Uc?S#mL*Bt6wWzU0kEQhA7TZKAi2!m0&xbnR)H)iul zt0dMNsjsK3CY4+LdtcR9i0N5wu@C4t57qRBY#&ssF@Jk3z{}6qF~fUrqeb6l_B7GK zW_$gLz%n7r!YK5vNtNlQ7{+#VGdy3`beJ4Q@3C41_4(y6#m7uh;({0g-|;SDt;N^H zZ#Cu9H76hl#IpIiZq(wOBtfJ#SvUJa>OfHOlA!DkS14hr)-aKGa{;z!1867YUS6l_6#hJ?Y=F(%RK@Y?w|QZ=X@Gp`z!r*AD~7D!zp?DaKC6q3ySalkz69wUXO|lp(Gq2xCWm$M>_L3Q4kM+xdg(2oQ_Quuaoy95K&s*u>rr5`v|nn};LqfKn)0t25m+=cMvs$2f$MZK(&v{`vI*R- zW#?Y2D90h{$V)|i-d8W8X(;bZt5+gl7JCxp>b{Q2Xxo033ZBh8JG?kdZf3~L7l781%t#`kNBgc^$eW4Uy)%9cOO(^3! z-O^^NbD)N~z7Nb-$K5hpuFi}bB4TMO<90+Qhm0oZ0e40#tj?*#jRf2umqlahsy>^h zHN%Ptfx#z&*Yy(flP=~;s+j8`kgpBg_e<{?19Ca%;+|8wF&ZR1PqmDg-LDK#ZlZ#} zP5Oj=Z8_cFdLwOwhSONIB4KFy68n?|0{`_fUF(-3TD}95)nuyO0;@@j^Rzq@^3v3s z07OPk%kZ{Yr8iwV1ld#!sstkr27cZ^9|PwBPLs$ha$i8Wk&*Xt9=0s zJUr*lI=y6<4a44yZr<&5j6*^ek1tzpfz?Lx?BE-HO`_JlIjPZHnV9<~6=A6{XlYdj z-UI5E_J?Jq54p(|{cm2u`m0*@3;c{V1z69ZJ=q^ryh+RNg>pyUgz_JY-PttW5oWLJ z)_CUCNUkz-DhS<`g8@!j><(G#%J}DUOpqCxEKlU61_ui4N`L=y*(vOA)EREt5@Gj3 z3%2c$jqFIFQcbC-pNtj!PMR*N13S6AjCI7W-d45=!BgYynnL69X|xS{JM_%#p^S5N zYl%x^Ke5AEkadcXkwdf|R2B+yNOszszKXgMU$c2YS~HvaU5#w%JIaRo8+wa(Pe8t1 z17rZ=0rd-TrOup$??%?Io$5y4jMezsL7klC0}_VmQy~8n*CGr1aO<^771#-$K2F`M z{%lOv`SsHbkm%88Y-TQsV`Rkl9&N$~AgFj&L ztmm1}jCzy@>hLrXO_TQCw%q8*D-cY&GJq@!rg)O`TiE|Nk zqbFLUT!)r9IN!zQl@v~KCq${`z#w%oyd{tI85AjTs3?Ff<*L#=>KR`%dpi1yi$5ociT_mNRo9n7Z7lI==d# zZmMajogz=xp-vuy9kFzAM-v`0w)8SP(+c8`xU{6^CoBY>X~q(aOdOdOKfWJQ(k`l+ zc=te2<246>Q$*J4iHt7Z8hlhBIfDUfwCCK6Z{~I{D7&+Vr-FW8+wIcsui=u<3I86W z7=<%#O-p$)+16O;5krepc44&-Tjq>|$%SAVljuLQs^d#5Erp z>dce>Va{n)*_Z&59xspRPR~nCafy0>3wt>}Dy9wDx1}c(d^mrVB3onPi&}h)lJSO0FRr|D zSM!HXd$w`h9xq|fX2U@4>uRTKA$0T(0WH_uCDq3L;+g7TM*pMyFiqLnwA}>xf`Ss|?IN?aMbdY zMG;!++++7OE6HtH@UELH6vFptSpeVNRgt~@uDG~T4ilp2k-BcIedm1;#4w1M^gz3$ z@_g}ht^zW3Ri#%cVfQ!&F0#VhfM`GM4UMo+zV8-uGz< z*|V7#T@BmR zK0Z+E$6%pCgp$#UpD_kA0B7(iu$tEUXG9hp8$Ca6ntyxZpkY&9Zj$emUVILc{oU0Y z7OQz2rtS-a(p+&Wd(qnGI8w_|jKwFq%O&CrW^Zi!l_t8Ky;(~}_Wi5B3wVcIxzE*_ zKIz2Rn6zkMMif?s%GNGId9@D?1!4@y_}$I)`tx2{DyE%|S>GY9Y#2A`?4GtiX;m`o zW~kXR-Yk-JcvZIl^o;g1{Y^u+Glp%Yn<{3-^~+T&S}#;@@6;}>^VGSpZ%kGUEGh-{ z1>qPM;F*zAX#p-(V%3_TX-F_jOV1A|k$RP-|C@N7+cnNDn(es{IY5{TXGpH?-N#$iICs97#ye8K*Gc=rP;S-Yt#Z7}0c`fDoy^%S)?SB7l zHs)@q-fFjM+-GiW^HBHC=JFr2o_aLb`BNlrO5uEagqLY%J|-MnoTfQNufHl5U8+a9 zV8ayf#{Z;7euheCmC;psqda1_g8o67ZXjmLy~lmC@hImP&f1R>k zurmyz&NNyXCbTLOUOJR&p)FD={?!s;#kTzRl~XNa+`*`7b+Com~s!Lr_n7FWSt;v31tN;=;ZB+atRKyudDGTCW4x z%XM^4vxa=tsR_1^_7lckE+Z%n`@X>&ehd|tX^OgKY7(2zKiSHa9uA0)A8$*)QIR*$ zjYA9zdzq~SxUC{Hwx4nRl$c}^B@jfuS4~?*u0wmy>r75SO8nxfF&uT$DI)6pOpCs5 z=);JSgR~weUzdC@m!5)>H0!v2&!cXj{x$cMo#C3JL)dFR^+K13vf$oWT5GKiZV?uU z)jbF2A+j4JDH6gM^b{LSRk{s$X1-~Kv4tY{50;d*$0_!;m@E9Li>(`WW?ZTxPtOt7yR|YNNPPmADD~i{tlkeb4%p>2tYFegjx()1SjPH=i z>ZHF#n{-Mruae><{o192#a@3jGTEaY7oPl}lVNsNvAI0|h0^ESH$myyr)id1v4$p3 z7$ihxRHS)M%f_ugZI!M0JY<!d68S?_7;q zSa|a7g*emv6#~&NuJf+JF2M&<1T(qQKceI%jfavCr9zwoqnIw9X94Qmk@`$sFLT$6 zI%d)=S4V)b;7kJ=LCcJUTU+gIi6-MfWT|(OOCdH zzds1zRQXB_n$49LpIDbfzj9Vs>GgmgrspLDFT@kfX?_oxG_h@!vU1xy&6 zyfCQ>s=ujy3w^d11HXi;P#4!^X@*eQCwc7o7IWwB>7-dqgyScfQkpSVHxCGV*=`op zEO*$SlKzM+*A_fY{gwwQw=RdMAMQ=QX}GyL(Bx39$+CGAB0tUrsMQ?a4bGHha!phA z+MSOPB&zSjlZnRGXqLYV+&lhUu&TVY1F$FCGn5{l`$E@nrI+^Pp}bM2h?0RHZ87nM zilTkT;-uEmg9F}b^yzZHLfQL1KB)~NYM1&%HS?`Wo-z=g(*1t0bd9uJ_m;?$zAb5s zprhj^Z9$><+<{z7*+F2R5avY!Pcxk=<@oE!aBWQ$8@DW*?CS)K`zNf9KpU7#D_n- z)}`Lqz~$VJr#T8Lr70<|u58^*tTDFDhg)_G#z#&535ZgI&YE?T+JQ`4<=zB$T(*5H#M^^k@1>#X3h!cM-h)M6FhTfK`i zc`1`O2AZQjJ0{#P*vP?3i@11*Nqc|axUr0(e}cUYkS=Onc)KJ||7Og>cWGYUoL6In zuG?D>HN-IDdrXYTKtV`Y&XnqK!Lt`;V|OUB$Ukr$e&MEQg~F0&_RGn?VAu~a=RIv0 zgA=jH1%@ZTXgFIpEgfX2p+J#wO|sPX%2gWcH%g_$XI-o#K2;un!r1_ma?}vVuHWCk z+hgtKSUa#q+7SZ|nk*d-AOT>X_DADNe_O(&MP2`<=`H$@u^bTZ_z2NS-6;^hh_VsG z4@ASw(9ODchh8M-B4X(bWYmX+&L$&#QxM=nf|?1u*JVtv;}9aq$S8nSQ56WehdoSe zw#fI{UzC@oNDu&I3x}OV;bKYlCElr3EJjBbaZ#0?&=-eKxFKsNV`OW_S#KIa7+f<3 z07Br#>Sf{IEeekc^L-aT%;+T$P2PL2(Gg15H*}+6F|FbGmEwXjyp71Bp4}MDg;#Cg zLZIe3z;M3ReV*v3>ruJLW*%>3;@L>#Iww@{a)}$!$Y#{%EZpel=aW?>=7Xx4)se&# z2cgHtz%V(<%`(h-;$SJxYSQpDODrORcFn6#p1RJ49tZC5n{$Vu4a`qllC=$ zsc(8*G|N+Phr>+b8=Tz36XKB-Z(r@UtQ-O@lHDnHh(9{kT!z*JYsNksFVrWK6z#Lq&6+7f6xQ{Gz|flHVv8%>p27)f-tWg} zG9|doe78SUNn<6SF^N*3a3bG%dKE92Z_~p)8_j$F`5+B>1&^bLu{7dhRK5PpMd=LD zk75$v+QX$3WjNgbtQb9vQWI1@(yY5tLnJm$(nSrtD9Vz?VwdPb|p{PCrUE zJ#5dg3+5Q)-k{e8fZT_~EbA18CTC$u1L+HA1%;(OYKA-9$Ck5wA*|!I<|O=cxj4>i zOUjiOkMF`Eifl>r>SYS0(E(jH&WyM3*SC#Om|Mvw)@r-$DB_PBdGnheszkuNn$3o9 zD)_mYiM+wO-sT)-W>f+zJ)obhKn_19>}bdU#5;oIBfyb{@F+x~+L#=++GyZlQMKk= z3Pc^rvp7uzz3MeZ9^2)8ny~cQN>{Oi!y{i60o-0N58&CT(dDxcD>eywoXp+%{Ot_X zh#fUoF{+~4@HWe@<@|&J%1CF%+Q%779&gLTF?A&Fl3krR^R8zo$MaksG9&z?jSY*e z#5US@uS`!Bce?Sl-E~}I8?5jUv4-)*1vqHs%_#_tG`}67fzkD`0);8^2|W|2c&~hE z1fa3CpJ=3wog0s5uP3{h4!V{BpCNjM`#3%mHG7^D)U}SyF}unerj`ooRnZGGfKpbH z#c$#Qvm8@!0~&~T0^OqOuICeJ&d7ENSomCllbpz*_?_kK^_pBh!O65k6JVdX>JI9e zv2E!$GRZTnhcpZT6YtI>KXy!E@#I*obJTfZbajJeR4YW&YG=kxjFa1BSt6yf-ztag zg0wTnc_DQJ{syYJchJk@u(U^4+75Qp~ckNgAHxEi~iBb{)xCwbJx|1T<2pDrqgx~A~FcW^_;QN@qj1HsZR z%}k=EF(vk_!X;+mjtv(ukZ?1C0!+5>gKfb%hl(%@N|?9;$!{ju>K`Zx#SEqUCZ-&E zUm+~k0J&3>W49-Y83LKOe&;<6CD4hwAvPZN1(~-qdIWA`7-8}C-2Pjb#a&%;vA^h6 z&_KR7d7Vb^Jdn7Z9Dj_>I!>wj=hbPq>%q|rpw48AI0Inl(!iJ9Tza*VjXiHkz)OXL zmooaesv0&$$Xl=4+i3OITY+sfp`791;(&2%IOd*}J@vla+pH%QHp0vcICq^!E$<=0 zIb?wG1@$ta#FCHBm~}gL!#|IddQ}n0bF1I)H@WRkTZ+Ee4JI<-;39fNbN<1t2 z61iFbV+hwNl5uvx#D>m#T-Tif{I;fLqy$r7tX?SlI#zY~2DsmsRS{C7fOb%(-Q@Yl zKfP!QzR~`-W`ODWg4q2e<&)ej4cP;1Tp9_1aY09L6o}seNq{>zDKK+KWUTPtnVX(x zXrdAXfKz@~f4geMyt*6J#|L9@Fig157Wj=x^^J(#+y60Bvy;An+U?7k>}8GFKd(Ok zd=sXI%O9U~pfKMr|M?AHoZU2Bpt8g@m?jd|*qBf83_K+Vi;$_Aes2t+%!o;=)P`yC zkB)gJgL#ASPlXx+2QqfhHD&lFbcmA{rxRK3zJ`=sY2<3ED?8s*FWFAM=DuyB7^5?$b4XUA61DDlAH9m@kztGG5gNK z{?6wxk+kn*1zSYS0OgJ+x*LC zF)zhHte*s|e;fP$W0nS8>AzpmN3$hI944$|U?Py#hRg8xe)eR5ePNUMH0?RH z{z~0967-5A$$#sWRZ>*friUaXy!=0}ayK*OeIG8v$ywm@_eQ@^!`{V`_wl({G{=+T zd3su$e@O>}ZEaTK{=Ma2jQGKGad7t|YZPCdIxes3(;6uTARr&;>vG-N35rT`S;=cnpew4Ri8D9vhkXJpPxtq)-kM{2!7u&Bfigh% zVx)B9)CMqRY>c(LUX!z+P)0~|j*`Q#*4UY+xm<{D^mE_Yz4$rJWXON@ebxj`yerqf zp8C0}kI%$-747v=p+_vJ{l$fD;El!l!ls)=xkP(NI0A2;w25Z@6fNr&ini;XZ`Eq* z`wvK22mHZ&_0RgmBq^_C89Y0L#c`AKOxyL1$3>A9LN0cj?lZW9l5wh81o^`yF9Hbw zOOpcq#1GI&-S_|A58E9;YG|+)NXxvY0={0DD&QJMUZ%1J~2)o0KOe>=+euUY`W701TqVED{(EEQ$fy)-+%9U9&@ zE*E?POxhF4UlXbQ^WP@YGN^3AO6lzU(x63XH0&|^0CM+L)uQ2-9LSwkXBm+9<5~!2)(|SOn5{Sb{>5_{3_qJR*PhSam#mgCc0`JCNO^}h zLfWK0G?}>)QY^y?hM?FU)TWpFb4?j8NlIH$$vn|h4^VZp3v{qKfQAztfZ9CO9brm$ zK02@GbOVHHbI*+&K=O{Qoe&yow0RD*Zlg>6j13n9bvHSldt3Yrj(kuuYqt)HQJOyi zb|EDAZSG#WgRqm@sX?qkG_`6K8Et&+0>(i*WfBx{Y^zSeG}H8!>&-rq1ZjnB?t3B5 zemX50M_1Gq8*ZaPY#tYT{-*IX1(#<>%u76nLg{t{4P{$p$66t~txsLLGT`+$tZ}(P zK_n?&Ua|3@7;XmL#xaLueWtc_6^>EW6 zTCPmH`OqU^{VkzYYW zJ$bpOHhZad;M2g^fV9kU$jy@zCc1k(V3V6DTVNjEA~4H1oL@W0ramu7mJsG5V%gzx zk2s8r_b48?SrLg4toz)D;WcV$_lJt)^>%<_oK-7ySO=8%=*7~6M}vBmBd%Z;#9j@B zYdK8YNwOoSDihg3!-L$9?a$yeU!Baq-om%VTc}i4!15PosA|^Cy&Vb>1x1bw1}ejv z6mD69+DzG%%GC8*dv)8@8yemeJS)e;t8x@t-_Z5&TOGa;zJVeOT|RKrZOC;sui2_7 zcqcbu(kvE71nRDbD4mF!J{CKaY0rp~jFSr?W#MX9wbW2v8S`(&Lt{Dn+>C=zjzmd{4qDZHS(xMriOZ=M`uz3&xw zg`<(DPH9v#Pp7k|G?9RNC6ek)<#L z)q}-hvb}yZ!hp$}Gp-lL{b6CxkA}n~J)4OnODx-&aTt-0QROT@RFf?h>YO<2LYd{a z^8Az8e8puFY16*W@@1~>8BnQv#V9ZF()WF&&%>iLQ3)p?{xmH^4r)DuWBr6_p(d_| zil^L?MqQ7t+er?zc73DpHcF?>CQb1-weoneMsk(ft_N?EBXpt zhy#w|t}s$rHN>&pPTEdREl_Qm3Lwq!b#{XPa2CO%tZV3?b@%adHXr^NHof(Ec6&gf zbvUgsLKOGikdd-Qmhja{ErpdFX@&!C9Ow=n$;CLmzdp6!aR8Dady?^DH-`eWVPl_4~|rJFBhAc4rx*ayFARN_EtG5%R;feG^X0u8Yc2YsqimgJ^{CCF?W$wNIM~7aQYsh~w!(R_T-T z{hu`=Vo^E7MEkON-QMUbhkOiwPD)j4(ipI#z2+`rMT1XF9AQH@{=p zx|2S}Csk*&o=vchESD;aonmv^VRlO@=d(;2WL~Fu{g#};fHJ`nyQC-;Q(jr6U#r4i zWI$?1);vPknak?@{wH-F~(K8jTa4BHtA}A{Y<8b%Qf>=z42UjiE|5{a#T8;X4FK%C&X28 zoh6PR&#LNZM7=lr>v{re2w;^DvZzEcR!muU4$TN3@tp@*sVx-A!eFv{w7^^uDvf%RX3Ipap#Vv>;ZfQReD zh=gNHb0<3cHk+<;kLxS*k4^;P1V-$dv)S@ZlAx#wUhIfYrSasR3nQ=2O=#_NjoT_t ze54>1(X5m6ve3^#W0_XM3QcG^kBfj+pF+w=A4G^>8JEGE!^hUtGc2MtYNzH@-z2~# zm#dK8shdjs1)>rl-T`Y?F%Su%SAa5JDh@a^&EkN9%BEboq9(V96m0F zZ0?by+;(;nXoAlH`)I37D1ksbpG8U+@1UT?2n#(lRB_gbc&HD(*?sKS+0{jBgm4s zI8>8Aw?32Oubd{kI3)jxg;;|0y>sf)0K_~57dI$KJHn}=t*G&~1=rBj*Lzo@i-W7U z8Am|6E_!^hP}tuu8!`8gZ4o}gL75eSMw?dzv-EFZI|7u8NO^(w6<*200Vpkdh=SZ{ z__8f=MuH&yQJK;Lxl(q2wgctP0>VMkOHFOZwvENYHe#M?k{NeuAam(q?8p*<&raP% zu8216emZ-L@?beRQ>St6J7Ue`%NKdp23ljH-6judB{Bjt)w1z58O+-@YEputwjI8E z-ibyd+D17S(Gd+FDuj$eC`c)B&MOx}Z65*`Jr$V#nI32bG(r4A%P<3*#JgZi$5~f%8g#lF2cG zWDs~2X`BVOKSO5V99XhPoi>>)6-|~$Z+^_<2N_0>fGXVzxsGT zi0KflTmZIO*e0K4$h#0iSo;mD7C$IRMzthJW$ro$we1`XtGo3x}zy_!*X8jJQJ%lnLy_h{9Ri+C=1c zf@TQW(K~T@VakEwGDAO?<3ii-<+zJA7>bK~m^U1Nu1X4{NFxYZoWPVa-aFnCHn}f2by~gBX>kI^2AL)zhs0J zbry>Ku%fcvq_IcsURN1OyYbav=;@1jE|P}sN4O9D^L`y9#jWlU)tVqhf$U~#WYRl2 zAIx|dDGN5nqC(lxaXK8_n|40Ca;$LIPN^dnsrg+16H?&u5i0DGm(mZX+GsZM`fwwCGR?H zEhhq1)k)^Kp9=of`;O`0xLJhgLV~chv0zNR;5&mD&5;n{z^rhm>%GFvrnb!Y@y=%k z1j?xfTCVdi_J6!bT1e6J89ytu5OVP^@NlAPKhXL;*(4=8DfdS)R|aY%D($U^3rw!>x0g;EN`@J zj+chU=#H5>uw$2HJ6l4&kv3VgW$%VT_|_$d`^*&s9*VH|9cs&CK%2*F0CgQMuV`7S zG1jSFfb!HIhgB3BPi?B^-OzLY$>xKHcqw1+jABS%6aB~6TfNyF8v{1{e0 zF=A;_RrUhVyRun+tq9!CqOUF|bN(ALxp#@k_mg!_oEW;)`ypbY$*wi>hazB#oiTvT&n^27u2&NCFZT?7*M$#D4VPO8C?uu_DVK0Z%Ri2KdQ z>w`^P>Zfy2HEQ5tKgZ;eKKolb6hd0Y?sx1&a ztiF`=vNMTSD=ZxQ-sK47JJWzt&cT5S?Gf?WVAR7|8)akPLMjq|Sa7DksA_(5ixX@V zDhFg~es}k0z+mqS$SOd5%NQ4wy{U<1kWmGBmm*J?>i22iBRT}7+h#O;t1Lr*I2%ro zGW_&HG~mUBSOrVDSZ9P7AKl8laM^`&hoXBE$?1B;s4aF?>fpq!xC2Vpodx5Ysxhas zgdD^{&vaA(iLRa=9GuORcB@)hO!(|^VU#KSr(!?UFMwW<6WppKmL%=#%(k$j7Mv-g zWpVO`gO0q4}MD{_R9BT zP%*12`&IN+wr?9;wf*p^)DL6+yh@ zx0oBT=&~~O9&tNDBLlCV)Cctl#U!yD-?-V%a1xORr-!CC9t;=y;8wkoUR6m0tjk}g z2s$f7*;k?&WvpWBLBqycigQB9S>p1L%HTjQQ*zdIhx|-VO6C%o!69QO^{i-cUwND% zHtSW?8)yrq1`8?eV-JnJXromj`Vh0eQZd0{3`!mMDBch`Be1V96@H#N?2S1lsy|*5 zCOEZ(mcN#d*B74)7r3|QPs<=+>xmyyo=}0|AL`$;LZ2ZS-A8`iai1wTyOw0GOFowlW2sp#fi21eK3pE9h+_RW3rZNJPunM-f>ti_u6( z@*bLQ*NtTy9p>PA0EM3_s&7uPJ46)Z4{;hu56mKT0zdW6PV^9Y5f}8y8e5b%=8YJS zRd?WEwg13k%eN-g$%&8jU^yGw-HSeA!4qf2ps~}LiAknNOH>^scLmaNz={r5Kla`o z?<5peP>GwcYDA~3v>i~$t@|-STO~xGR3)4iTrXDiKqRCMfZh|Qin{#jrE&eC1I_QF zRXhDNZ&7#`)2JJusgNBlmPCJ)sL^T#{%ny14O@9i01moP-TClhAf4Z1e`LV8A5|^G zGoJD}R9#Ret_&G%6<&G(vSIBeiP^yd?TRMG+D(H7A77hKzWpp2y)VCfd>eHT;<@eh zUHTOz^ABx(m9O;jY{TAmApBUJm}iaI*r8eKgPI?Z_*LKG)+F0eRmYROvPNZ} za7z%fsd*~EGE&B+8W)g!;%60f;M2nL=!l=6elFsC_~TXHV16HO;#qTGjk+4}Ua|3YW1}Ywg9}@!$D4 zgA^0uy0$tc@KXfbsya+w6hU#q=+a2+*0eY`x6vK|>rvQC`(W89T<*Lw3WAhk3^wkd zvv$>y%urdR-zQ6Z^PY2^;5O5;CoW!$f0EM3MrFTd$Jc zc^^+Bo(qNB%t7|{)-ly!atG4Glj$8{ew57qw6Uj#7&{#Or`et;$?U7Fq6IY zQgGO5#`nd~sxa7C(da`=`qCU>spv=?$v_H7!IOJ91!ax+Glf$!t9f`>u8f*yN8F*M zncuRrR2k@{p`h<*e)V0hf+)5FA)jI#Z9{fM0-G3LQ#;F|W&x0%ZfEi;F-~5)gBu(b zs#@0@x19_pUW6vORV_u^Ow#B<_xZ3i$7S2Bk7qp^oIux4t%+@wwAoPoX@vfTE-h@% zx)7WK9ww23ZEFc^TQLpRoxNHhZ(=xBq{GlQ7GO@G#7;5?bgG#@&$`##rMxn)^!`d5 za(g$`Teu#~_dy4N@|W8Z%D>>5gIB9Nxj!L9X0M5LA4YX zg9Y^S?0Du8Fxabg3dXwkj>m}h2gVn#kXhekLXagZ155$#hGtaB8!?dq$vB?*-_?F* z{yu_~rszno+c1|4;rzkvFZ&G2(#a6pTr*=xGpJIL0DVX9njH7*jxGZ7D zprzdoP5v$(3Aqo}=e(zX67t9kG!ZR{cRu*HqY|k5*oebul4q=WR<3;HV1HEoC)Yi8G;vaQ{^Q74unHc&WHVWARIp3+ZbU4mB%96oqd z2=McKP(#-HsBZMyfXI2z$CSHVCutYtE-avUmhw$m0$fe~&gasrA5<(ss-5Gdn7H3I z$Q*3ni^EOJp|Q^WXl$YqhepI&Yfz)N1Xs2nRoKr8SQ&C_EUB5nLC5tXM{9pA5zYS% zs{HdrEc*UtD{>cvdIPVoA}G8M^EP~{6qz1Xz+F+h0HedV1#$)Uoqx6?oF6Fa6b?-T zf!+4qM_3*|SZ^tyD=f`0=4bd-^|Xh_(xk2%50z-HQx8lj@{aQ6BR9sS#0?NFW6I^~ z$hjjy#taQshMOG~+cA+B#3iZ4W5Ob;kW z3MLfstNgOx8fhA*e8z)^g1((wL{vYGgx1{#)pO0|uUx3~mF zgqHTB2gF05b*+WJdL z6Ger~^Yn2m8s>~@V_CZ4laHLi2^2Eh!wpv(WvL+aY5iCoM7Wp=N;!1MpzMbdS-&TW zGma}FG)CITBUQ2a37F`wI;3%-!^zQq7C>W);@N0g{fVv~?ZpdWpm#Jt-hLmH>qT%C zzENV|Xo(^P-sFK`TgY4Fj@wNN&cbNur__D_mSV*in+BpD1uvf!GO=cUgJnp;rMqck zB4x09GL$G}xZ(9ot6V}J3RVh?o&6%jTRrJ45WCd2qExD=O%q$+yi!CfP3LPf*`3^x zXECOk;mPNwW0CWlE+@8h-LC~fs)1f3jhjpR=UDRN4FQ3>UX~u9qBm(Gk$bHEgJyBy z_fTdz;)6^b1_DCD-<$lcdS7Xx^E|$=~#5_L({}NkP(p08bou#LjcGNC6I6G^cQ&AW? zs#$ljZoDtmBpah){aX2S)K1lAz0TtjY(@m;wfR9?H}7+Or^(h?(5O4f6%`i@@9U$1 zdCeD|%CB?FTewgCIp|8mQUZmty$x&=qjd!pj}y!D-XpS<>n7^?vt6H6=;m=n#ZW?k zFNDWSECpgl0Y#wD#^GaU;)@OpcdaT99+m-pX~CTmpIlHpMsz0%GVflHbdf7t@KpaO z$fS%vTK?&9+fA0Mi&dgiMfTL&MM{6jGCJo=?K!We_-mZZm3XdGk4D`Ik^1l_NPK!F zrUaBRw32Ssjh0mKBwV=USPlh5Zv{b9L{|nI2llL`jw6dT10_?ElfPXk$V)D5uVq!~ z`)2r63o}w#_!(ziWE;O(g8j-=txXEIkKQPglUXUg}sx`+#^tjgiZ zcX@2)Arc$4eu;wG84(>1tnRtwZn$NZZBQ7|YUk7{>M1#O-Y=CtNV1n8eb-?k2|{)_ z7!$@<3wj-cSFv_(ajOt*`7L*xUezt`g9}=>7V*>Cc+GUM@@p%9ArxtU!(@hx{)Pki z4U?&+^5h~(dC$WXcIy{_sg2^NnW*}Rd+yl1yf{e9TTz}QB$}P@MENIti4UIw02GBD zM7RFLk$A=d5QA|kQ5y>{-yO{{q-lX1jiI53kcwCSM0WHU1CWC4L^|30uRosw&nzfB z{C?$s1LLfw0-!;)QRBYKFH{KgFI0%g&-?xh6=Fs46Io;{i^Al74uB@rS%U+0s%kg>yeqH~6ANmEr+)AjfH|rHSCWvkUY>uoxIx>&=H$0ax zEt0hI+@TaNh6EHO0QJNN0l1GDpeq#!UH$`(_6Mp7W%7^gi42plT+IoIm0KagEh(MB zqK-(ogGIxoA%GSTq5i#_e8=@+f6idyn!n) zp}K%)Y+CKGW1a~h9~gy={{=DVjsoJtu}H{eca*%8u_4gwKD(Jmh6+w&bC=-L-FVvH^@{h77RfBf#yPEFsL<8@Mqo!vYCHi z*Z7_#yu+jgW$6Zx-!bR#c9le;GD6vxMH`a6!vNmxkEt+;>^V6G&%ccha#bk^CF5<9 z`rrtQq8Td`(|7YztR zR%!dkweR?z15CFBH2&|C;^$(1_8e4kf1pGB&lv+UIuc;c#2URqR{8z=v)}Pa`~hTU z8hQSXLHv1LEU+<5{gx>I<$fOl3R1$y?LY4J2PzY=V*06b|4qB(C209awDW(C73eX2 zPa>Hz$vM8@XxIv zc@TGX8j(f#>d%(whYtbd_H6~}{9o1_8u!Jfx8YCo>Qg1*r%(uV?&n6pM{^@YK?O)E zb?d(WA;~?P=Z%KmP1@`B*VbZWjB=m*BD-p3=3I>s3A{f4N?LI;cGj-(d&aH9HPtmz6lbCUCME3J8q)j{{b8 zZe5vnlLV$HmEDZ^9`~0O!x|*jk}PN--6)?*}x+okgLcOSbfO z|Es4dP(UqWUIa^-oEJ9!&)XP@@FCdZ?K=XuLLC-?pAeefUH6w60R!W*S#BZT8rJr^ znS$Sg2c^tu(0qI{=U0~h8ul_=@I@stta+55mRJ=y0VQpMtuDvgPoC)fk01R|9QcAN zi9Ww5Hz7!6B@$)ej`Gi8{WY+0z!ew@4!!$pq;7)06wr{taG8Hmdy)--U&4~M0Q=ec zRX*?+UZb#RHul4rznYyL3%rKz)k^H2cjW-|IUo%VI4r2XY=8Bf3Gmotm^m1J4v^

Rt}O)ET-FCjP5?n~=PnfNI2D^cC)1 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 Promise.resolve() + deletePlugin = (pluginId) => Promise.resolve() /** * Update plugin content diff --git a/server/datastores/s3.js b/server/datastores/s3.js index 8c98ba2..b2be1de 100755 --- a/server/datastores/s3.js +++ b/server/datastores/s3.js @@ -101,90 +101,13 @@ module.exports = class S3Datastore extends Datastore { return this.#createBucketIfMissing(); } - getUser = (email) => { - const { instance, Bucket } = this.#state; - - return new Promise(resolve => { - instance.send(new GetObjectCommand({ - Bucket, - Key: `${format(email)}.json` - })) - .then(data => { - try { - if (data && data.Body) { - consumers.json(data.Body) - .then(resolve) - } - else - resolve({}) - } catch (_err) { - resolve({}) - } - }) - .catch(_err => { - resolve({}) - }) - }) - } - - getPlugin = async (owner, pluginId) => { - const user = await this.getUser(owner); - - return user.plugins.find(plugin => plugin.pluginId === pluginId); - } - - getUserPlugins = (email) => { - return this.getUser(email) - .then(data => data.plugins || []) - .then(plugins => Promise.all(plugins.map(plugin => { - if (plugin.owner) { - return this.getPlugin(plugin.owner, plugin.pluginId) - .then(res => ({ ...res, owner: plugin.owner })) - } else { - return Promise.resolve(plugin) - } - }))) - } - - #addUser = async (email) => { - const { instance, Bucket } = this.#state; - - const users = await this.getUsers(); - - await instance.send(new PutObjectCommand({ - Bucket, - Key: 'users.json', - Body: fromUtf8(JSON.stringify([ - ...users, - email - ])), - ContentType: 'application/json', - })) - } - - createUserIfNotExists = async (email) => { - const { instance, Bucket } = this.#state;; - - // console.log("attempt to create user : " + email) - try { - const res = await instance.send(new HeadObjectCommand({ - Bucket, - Key: `${format(email)}.json` - })) - // console.log('user file has been retrieved : ' + email); - } catch (err) { - // console.log('user file not found : ' + email); - await this.#addUser(format(email)) - } - } - - getUsers = async () => { + getPlugins = async () => { const { instance, Bucket } = this.#state; try { const rawData = await instance.send(new GetObjectCommand({ Bucket, - Key: 'users.json' + Key: 'plugins.json' })) return new fetch.Response(rawData.Body).json() } catch (err) { @@ -196,28 +119,10 @@ module.exports = class S3Datastore extends Datastore { } } - updateUser = (email, content) => { - const { instance, Bucket } = this.#state; - - const jsonProfile = format(email); + getPlugin = async pluginId => { + const plugins = await this.getPlugins(pluginId); - return new Promise(resolve => { - instance.send(new PutObjectCommand({ - Bucket, - Key: `${jsonProfile}.json`, - Body: fromUtf8(JSON.stringify(content)), - ContentType: 'application/json' - })) - .then(_ => resolve({ - status: 200 - })) - .catch(err => { - resolve({ - error: err.Code, - status: err.$metadata.httpStatusCode - }) - }) - }) + return plugins.find(plugin => plugin.pluginId === pluginId) } putWasmFileToS3 = (wasmFolder) => { @@ -271,51 +176,39 @@ module.exports = class S3Datastore extends Datastore { } } - putWasmInformationsToS3 = (email, pluginId, newHash, generateWasmName) => { - return this.getUser(email) - .then(data => { - const plugin = data.plugins.map(plugin => plugin.pluginId === pluginId) - - if (plugin.owner) - return this.putWasmInformationsToS3(plugin.owner, pluginId, newHash, generateWasmName) + putWasmInformationsToS3 = (pluginId, newHash, generateWasmName) => { + return this.getPlugin(pluginId) + .then(plugin => { + let versions = plugin.versions || []; - const newPlugins = data.plugins.map(plugin => { - if (plugin.pluginId !== pluginId) { - return plugin; - } - let versions = plugin.versions || []; + // convert legacy array + if (versions.length > 0 && isAString(versions[0])) { + versions = versions.map(name => ({ name })) + } - // convert legacy array - if (versions.length > 0 && isAString(versions[0])) { - versions = versions.map(name => ({ name })) + const index = versions.findIndex(item => item.name === generateWasmName); + if (index === -1) + versions.push({ + name: generateWasmName, + updated_at: Date.now(), + creator: email + }) + else { + versions[index] = { + ...versions[index], + updated_at: Date.now(), + creator: email } + } - const index = versions.findIndex(item => item.name === generateWasmName); - if (index === -1) - versions.push({ - name: generateWasmName, - updated_at: Date.now(), - creator: email - }) - else { - versions[index] = { - ...versions[index], - updated_at: Date.now(), - creator: email - } - } + const patchPlugin = { + ...plugin, + last_hash: newHash, + wasm: generateWasmName, + versions + } - return { - ...plugin, - last_hash: newHash, - wasm: generateWasmName, - versions - } - }); - return this.updateUser(email, { - ...data, - plugins: newPlugins - }) + return this.updatePlugin(pluginId, patchPlugin) }) } @@ -491,14 +384,9 @@ module.exports = class S3Datastore extends Datastore { }) } - getConfigurations = (email, pluginId) => { - return this.getUser(email) - .then(data => { - const plugin = data.plugins.find(f => f.pluginId === pluginId) - - if (plugin.owner) - return this.getConfigurations(plugin.owner, pluginId) - + getConfigurations = pluginId => { + return this.getPlugin(pluginId) + .then(plugin => { const files = [{ ext: 'json', filename: 'config', @@ -528,56 +416,24 @@ module.exports = class S3Datastore extends Datastore { .catch(err => { logger.error(err) }); } - #deleteRootPlugin = async (plugin, pluginId) => { - const { admins, users } = plugin; - - await Promise.all([ - ...(admins || []), - ...(users || []) - ].map(user => this.#removePluginFromUser(user, pluginId))) - - return this.deletePlugin(plugin.owner, pluginId) - } - - deletePlugin = (email, pluginId) => { - return new Promise(resolve => this.getUser(email) - .then(data => { - if (Object.keys(data).length > 0) { - const plugin = data.plugins.find(f => f.pluginId === pluginId) - - this.updateUser(email, { - ...data, - plugins: data.plugins.filter(f => f.pluginId !== pluginId) - }) - .then(() => { - if (plugin.owner || plugin.users || plugin.admins) { - this.#deleteRootPlugin(plugin, pluginId) - .then(resolve) - } else { - const pluginHash = (plugin || {}).last_hash; - Promise.all([ - this.deleteObject(`${pluginHash}.zip`), - this.deleteObject(`${pluginHash}-logs.zip`), - this.deleteObject(`${pluginId}.zip`), - this.deleteObject(`${pluginId}-logs.zip`), - this.removeBinaries((plugin.versions || []).map(r => r.name)) - ]) - .then(() => resolve({ status: 204, body: null })) - .catch(err => { - resolve({ - status: 400, - body: { - error: err.message - } - }) - }) + deletePlugin = (pluginId) => { + return new Promise(resolve => this.getPlugin(pluginId) + .then(plugin => { + Promise.all([ + this.deleteObject(`${pluginId}.zip`), + this.deleteObject(`${pluginId}-logs.zip`), + this.removeBinaries((plugin.versions || []).map(r => r.name)), + this.deleteObject(`${pluginId}.json`) + ]) + .then(() => resolve({ status: 204, body: null })) + .catch(err => { + resolve({ + status: 400, + body: { + error: err.message } }) - } else { - resolve({ - status: 204, body: null }) - } })) } @@ -586,7 +442,7 @@ module.exports = class S3Datastore extends Datastore { const params = { Bucket, - Key: `${id}.zip`, + Key: `${id}.json`, Body: body } @@ -610,225 +466,137 @@ module.exports = class S3Datastore extends Datastore { const { instance, Bucket } = this.#state; return new Promise(resolve => { - this.createUserIfNotExists(email) - .then(() => { - this.getUser(email) - .then(data => { - const pluginId = crypto.randomUUID() - const plugins = [ - ...(data.plugins || []), - isGithub ? { - filename: metadata.repo, - owner: metadata.owner, - ref: metadata.ref, - type: 'github', - pluginId: pluginId, - private: metadata.private - } : { - filename: metadata.name.replace(/ /g, '-'), - type: metadata.type, - pluginId: pluginId, - template: metadata.template - } - - ] - const params = { - Bucket, - Key: `${format(email)}.json`, - Body: fromUtf8(JSON.stringify({ - ...data, - plugins - })), - ContentType: 'application/json', - } + const pluginId = crypto.randomUUID() + const newPlugin = isGithub ? { + filename: metadata.repo, + owner: metadata.owner, + ref: metadata.ref, + type: 'github', + pluginId: pluginId, + private: metadata.private + } : { + filename: metadata.name.replace(/ /g, '-'), + type: metadata.type, + pluginId: pluginId, + template: metadata.template + }; + + const params = { + Bucket, + Key: `${pluginId}.json`, + Body: fromUtf8(JSON.stringify(newPlugin)), + ContentType: 'application/json', + } - instance.send(new PutObjectCommand(params)) - .then(() => { - resolve({ - status: 201, - body: { - plugins - } - }) - }) - .catch(err => { - resolve({ - status: err.$metadata.httpStatusCode, - body: { - error: err.Code, - status: err.$metadata.httpStatusCode - } - }) - }) - }) + instance.send(new PutObjectCommand(params)) + .then(() => { + this.getPluginUsers + resolve({ + status: 201, + body: { + plugins + } + }) }) .catch(err => { resolve({ - status: 400, + status: err.$metadata.httpStatusCode, body: { - error: err.message + error: err.Code, + status: err.$metadata.httpStatusCode } }) }) }) } - patchPlugin = (email, pluginId, field, value) => { - return this.getUser(email) - .then(data => { - const plugin = (data.plugins || []).find(plugin => plugin.pluginId === pluginId) - - if (plugin.owner) - return this.patchPlugin(plugin.owner, pluginId, field, value) - else - return this.updateUser(email, { - ...data, - plugins: (data.plugins || []).map(plugin => { - if (plugin.pluginId === pluginId) { - return { - ...plugin, - [field]: value - } - } else { - return plugin - } - }) - }) - }) + patchPlugin = async (luginId, field, value) => { + const plugin = await getPlugin(pluginId) + + return this.updatePlugin(pluginId, { + ...plugin, + [field]: value + }) } - patchPluginName = (email, pluginId, newName) => { - return this.patchPlugin(email, pluginId, 'filename', newName) + patchPluginName = (pluginId, newName) => { + return this.patchPlugin(pluginId, 'filename', newName) } - #removePluginFromUser = async (email, pluginId) => { - const user = await this.getUser(email) + patchPluginUsers = async (pluginId, newUsers, newAdmins) => { + const plugin = this.getPlugin(pluginId) - return this.updateUser(email, { - ...user, - plugins: (user.plugins || []).filter(plugin => plugin.pluginId !== pluginId) + return this.updatePlugin(pluginId, { + ...plugin, + users: newUsers, + admins: newAdmins }) } - patchPluginUsers = async (email, pluginId, newUsers, newAdmins) => { - let currentUser = email; - - const user = await this.getUser(currentUser); - - let plugin = user.plugins.find(plugin => plugin.pluginId === pluginId) + acceptInvitation = async (userEmail, ownerId, pluginId) => { + // try { + // const plugin = await this.getPlugin(pluginId) - // if plugin is not owned by the user, we need to edit the root plugin - if (plugin.owner) { - currentUser = plugin.owner; + // const newPlugin = { + // pluginId: ownerPlugin.pluginId, + // owner: ownerId + // } - const owner = await this.getUser(plugin.owner) - plugin = owner.plugins.find(plugin => plugin.pluginId === pluginId) - } + // return this.updatePlugin(pluginId, newPlugin) + // .then(() => true) - const removedUsers = (plugin.users || []).filter(user => !newUsers.includes(user) && !newAdmins.includes(user)) - const removedAdmins = (plugin.admins || []).filter(admin => !newAdmins.includes(admin) && !newUsers.includes(admin)) - - await Promise.all(removedUsers.map(user => this.#removePluginFromUser(user, pluginId))) - await Promise.all(removedAdmins.map(admin => this.#removePluginFromUser(admin, pluginId))) - - return this.getUser(currentUser) - .then(data => this.updateUser(currentUser, { - ...data, - plugins: (data.plugins || []).map(plugin => { - if (plugin.pluginId === pluginId) { - return { - ...plugin, - users: newUsers, - admins: newAdmins - } - } else { - return plugin - } - }) - })) + // } catch (err) { + // console.log(err) + // return false + // } } - acceptInvitation = async (userEmail, ownerId, pluginId) => { - try { - const ownerPlugins = await this.getUserPlugins(ownerId) - const user = await this.getUser(userEmail) - - const ownerPlugin = ownerPlugins.find(plugin => plugin.pluginId === pluginId) + getUserPlugins = async email => { + const plugins = await this.getPlugins(); - if (ownerPlugin && - ((ownerPlugin.users || []).includes(userEmail) || - (ownerPlugin.admins || []).includes(userEmail))) { - - const newPlugin = { - pluginId: ownerPlugin.pluginId, - owner: ownerId - } + return plugins.find(plugin => { + const users = plugin.users || []; + const admins = plugin.admins || []; - return this.updateUser(userEmail, { - ...user, - plugins: [ - ...(user.plugins || []).filter(plugin => plugin.pluginId !== pluginId), - newPlugin - ] - }) - .then(() => true) - } else { - return false - } - } catch (err) { - console.log(err) - return false - } + return users.includes(email) || admins.includes(email) + }) } getInvitation = async (userEmail, ownerId, pluginId) => { - try { - const ownerPlugins = await this.getUserPlugins(ownerId) + // try { + // const ownerPlugins = await this.getUserPlugins(ownerId) - const ownerPlugin = ownerPlugins.find(plugin => plugin.pluginId === pluginId) + // const ownerPlugin = ownerPlugins.find(plugin => plugin.pluginId === pluginId) - return ownerPlugin - } catch (err) { - console.log(err) - return false - } + // return ownerPlugin + // } catch (err) { + // console.log(err) + // return false + // } } canSharePlugin = async (email, pluginId) => { - const user = await this.getUser(email); + // const user = await this.getUser(email); - const plugin = user.plugins.find(plugin => plugin.pluginId === pluginId); + // const plugin = user.plugins.find(plugin => plugin.pluginId === pluginId); - if (plugin?.owner) { - const owner = await this.getUser(plugin.owner) + // if (plugin?.owner) { + // const owner = await this.getUser(plugin.owner) - const rootPlugin = owner.plugins.find(plugin => plugin.pluginId === pluginId); + // const rootPlugin = owner.plugins.find(plugin => plugin.pluginId === pluginId); - if (rootPlugin) { - return (rootPlugin.admins || []).includes(email) - } + // if (rootPlugin) { + // return (rootPlugin.admins || []).includes(email) + // } - return false - } + // return false + // } return true } - getPluginUsers = async (email, pluginId) => { - const user = await this.getUser(email); - - const plugin = user.plugins.find(plugin => plugin.pluginId === pluginId); - - if (plugin.owner) { - const owner = await this.getUser(plugin.owner) - - const rootPlugin = owner.plugins.find(plugin => plugin.pluginId === pluginId); - - return { - admins: rootPlugin.admins, - users: rootPlugin.users - } - } + getPluginUsers = async (pluginId) => { + const plugin = await this.getPlugin(pluginId) return { admins: plugin.admins, diff --git a/server/routers/plugins.js b/server/routers/plugins.js index b46c67d..8bba43e 100755 --- a/server/routers/plugins.js +++ b/server/routers/plugins.js @@ -157,8 +157,7 @@ router.get('/:id/share-links', async (req, res) => { }) router.put('/:id/users', (req, res) => { - Datastore.patchPluginUsers(req.user.email, - req.params.id, + Datastore.patchPluginUsers(req.params.id, req.body.users, [...new Set([...req.body.admins, req.user.email])]) .then(() => { @@ -169,7 +168,7 @@ router.put('/:id/users', (req, res) => { }) router.get('/:id/users', (req, res) => { - Datastore.getPluginUsers(req.user.email, req.params.id) + Datastore.getPluginUsers(req.params.id) .then(members => res.status(200).json(members)) }) @@ -187,7 +186,7 @@ router.get('/:id/rights', (req, res) => { }) router.delete('/:id', async (req, res) => { - Datastore.deletePlugin(req.user.email, req.params.id) + Datastore.deletePlugin(req.params.id) .then(out => { res.status(out.status).json(out.body) }) @@ -423,7 +422,7 @@ router.post('/:id/build', async (req, res) => { }) router.patch('/:id/filename', (req, res) => { - Datastore.patchPluginName(req.user.email, req.params.id, req.body.filename) + Datastore.patchPluginName(req.params.id, req.body.filename) .then(() => { res .status(204) diff --git a/server/services/compiler/compiler.js b/server/services/compiler/compiler.js index d98e23e..a226084 100755 --- a/server/services/compiler/compiler.js +++ b/server/services/compiler/compiler.js @@ -155,7 +155,7 @@ class Compiler { .then(() => this.#websocketEmitMessage(buildOptions, "WASM has been saved ...")), Datastore.putBuildLogsToS3(`${buildOptions.plugin.id}-logs.zip`, buildOptions.logsFolder) .then(() => this.#websocketEmitMessage(buildOptions, "Logs has been saved ...")), - Datastore.putWasmInformationsToS3(buildOptions.userEmail, buildOptions.plugin.id, buildOptions.plugin.hash, `${this.options.wasmName}.wasm`) + Datastore.putWasmInformationsToS3(buildOptions.plugin.id, buildOptions.plugin.hash, `${this.options.wasmName}.wasm`) .then(() => this.#websocketEmitMessage(buildOptions, "Informations has been updated")) ])) .then(() => { From 6c5cdc38e13f15a892e93da90e7bf99f9b430724 Mon Sep 17 00:00:00 2001 From: zwiterrion Date: Wed, 5 Jun 2024 14:40:14 +0200 Subject: [PATCH 2/4] rewrite s3 file --- server/datastores/api.js | 7 +- server/datastores/s3.js | 270 ++++++++++++++++++++------- server/routers/invitation.js | 59 +++--- server/routers/plugins.js | 33 ++-- server/routers/public.js | 25 +-- server/services/compiler/compiler.js | 2 +- ui/src/App.js | 2 +- ui/src/InvitationHandler.js | 35 ++-- ui/src/services/index.js | 2 +- 9 files changed, 270 insertions(+), 165 deletions(-) diff --git a/server/datastores/api.js b/server/datastores/api.js index 2568e41..727bd95 100755 --- a/server/datastores/api.js +++ b/server/datastores/api.js @@ -48,7 +48,7 @@ module.exports = class Datastore { * @param {string} newHash * @param {string} generateWasmName */ - putWasmInformationsToS3(pluginId, newHash, generateWasmName) { return Promise.resolve() } + putWasmInformationsToS3(email, pluginId, newHash, generateWasmName) { return Promise.resolve() } /** * Fetch wasm from datastore @@ -178,14 +178,13 @@ module.exports = class Datastore { * Check if received link is valid and add plugin to the current user * @param {string} pluginId */ - acceptInvitation = (userId, ownerId, pluginId) => Promise.resolve() + acceptInvitation = (userId, pluginId) => Promise.resolve() /** * Get invitation informations * @param {string} userId - * @param {string} ownerId * @param {string} pluginId * @returns */ - getInvitation = (userId, ownerId, pluginId) => Promise.resolve() + getInvitation = (userId, pluginId) => Promise.resolve() }; \ No newline at end of file diff --git a/server/datastores/s3.js b/server/datastores/s3.js index b2be1de..4d67458 100755 --- a/server/datastores/s3.js +++ b/server/datastores/s3.js @@ -119,10 +119,44 @@ module.exports = class S3Datastore extends Datastore { } } - getPlugin = async pluginId => { - const plugins = await this.getPlugins(pluginId); + hasRights = (email, pluginId) => { + return this.getPlugins() + .then(plugins => { - return plugins.find(plugin => plugin.pluginId === pluginId) + if (email === "*") + return + + const plugin = plugins.find(plugin => plugin.pluginId === pluginId) + + const users = plugin?.users || []; + const admins = plugin?.admins || []; + + if (users.includes(email) || admins.includes(email)) { + return plugin + } + + // TODO - better error handling + throw 'Not authorized' + }) + } + + getPlugin = async (email, pluginId) => { + const { instance, Bucket } = this.#state; + + await this.hasRights(email, pluginId) + + try { + const rawData = await instance.send(new GetObjectCommand({ + Bucket, + Key: `${pluginId}.json` + })) + const res = await new fetch.Response(rawData.Body) + .json() + + return res + } catch (err) { + return {} + } } putWasmFileToS3 = (wasmFolder) => { @@ -176,8 +210,8 @@ module.exports = class S3Datastore extends Datastore { } } - putWasmInformationsToS3 = (pluginId, newHash, generateWasmName) => { - return this.getPlugin(pluginId) + putWasmInformationsToS3 = (email, pluginId, newHash, generateWasmName) => { + return this.getPlugin(email, pluginId) .then(plugin => { let versions = plugin.versions || []; @@ -208,7 +242,7 @@ module.exports = class S3Datastore extends Datastore { versions } - return this.updatePlugin(pluginId, patchPlugin) + return this.updatePluginInformations(pluginId, patchPlugin) }) } @@ -384,8 +418,8 @@ module.exports = class S3Datastore extends Datastore { }) } - getConfigurations = pluginId => { - return this.getPlugin(pluginId) + getConfigurations = (email, pluginId) => { + return this.getPlugin(email, pluginId) .then(plugin => { const files = [{ ext: 'json', @@ -416,14 +450,15 @@ module.exports = class S3Datastore extends Datastore { .catch(err => { logger.error(err) }); } - deletePlugin = (pluginId) => { - return new Promise(resolve => this.getPlugin(pluginId) + deletePlugin = (email, pluginId) => { + return new Promise(resolve => this.getPlugin(email, pluginId) .then(plugin => { Promise.all([ this.deleteObject(`${pluginId}.zip`), this.deleteObject(`${pluginId}-logs.zip`), this.removeBinaries((plugin.versions || []).map(r => r.name)), - this.deleteObject(`${pluginId}.json`) + this.deleteObject(`${pluginId}.json`), + this.removePluginFromList(pluginId) ]) .then(() => resolve({ status: 204, body: null })) .catch(err => { @@ -435,14 +470,50 @@ module.exports = class S3Datastore extends Datastore { }) }) })) + .catch(_ => { + resolve({ + status: 400, + body: { + error: "something bad happened" + } + }) + }) } - updatePlugin = (id, body) => { + updatePluginInformations = (id, body) => { const { instance, Bucket } = this.#state; const params = { Bucket, Key: `${id}.json`, + ContentType: 'application/json', + Body: fromUtf8(JSON.stringify(body)) + } + + console.log("updatePluginInformations", id, body) + + return instance.send(new PutObjectCommand(params)) + .then(() => ({ + status: 204, + body: null + })) + .catch(err => { + return { + status: err.$metadata.httpStatusCode, + body: { + error: err.Code, + status: err.$metadata.httpStatusCode + } + } + }) + } + + updatePlugin = (id, body) => { + const { instance, Bucket } = this.#state; + + const params = { + Bucket, + Key: `${id}.zip`, Body: body } @@ -467,6 +538,7 @@ module.exports = class S3Datastore extends Datastore { return new Promise(resolve => { const pluginId = crypto.randomUUID() + const newPlugin = isGithub ? { filename: metadata.repo, owner: metadata.owner, @@ -481,6 +553,8 @@ module.exports = class S3Datastore extends Datastore { template: metadata.template }; + console.log('generate pluginID', pluginId, newPlugin.pluginId) + const params = { Bucket, Key: `${pluginId}.json`, @@ -489,8 +563,9 @@ module.exports = class S3Datastore extends Datastore { } instance.send(new PutObjectCommand(params)) - .then(() => { - this.getPluginUsers + .then(async () => { + await this.addPluginToList(email, newPlugin); + const plugins = await this.getUserPlugins(email); resolve({ status: 201, body: { @@ -499,6 +574,7 @@ module.exports = class S3Datastore extends Datastore { }) }) .catch(err => { + console.log(err) resolve({ status: err.$metadata.httpStatusCode, body: { @@ -510,97 +586,149 @@ module.exports = class S3Datastore extends Datastore { }) } - patchPlugin = async (luginId, field, value) => { - const plugin = await getPlugin(pluginId) + patchPlugin = async (email, pluginId, field, value) => { + const plugin = await this.getPlugin(email, pluginId) - return this.updatePlugin(pluginId, { + return this.updatePluginInformations(pluginId, { ...plugin, [field]: value }) } - patchPluginName = (pluginId, newName) => { - return this.patchPlugin(pluginId, 'filename', newName) + patchPluginName = (email, pluginId, newName) => { + return this.patchPlugin(email, pluginId, 'filename', newName) } - patchPluginUsers = async (pluginId, newUsers, newAdmins) => { - const plugin = this.getPlugin(pluginId) + patchPluginUsers = async (email, pluginId, newUsers, newAdmins) => { + const plugins = await this.getPlugins() - return this.updatePlugin(pluginId, { - ...plugin, - users: newUsers, - admins: newAdmins - }) - } - - acceptInvitation = async (userEmail, ownerId, pluginId) => { - // try { - // const plugin = await this.getPlugin(pluginId) + const plugin = plugins.find(p => p.pluginId === pluginId) - // const newPlugin = { - // pluginId: ownerPlugin.pluginId, - // owner: ownerId - // } - - // return this.updatePlugin(pluginId, newPlugin) - // .then(() => true) - - // } catch (err) { - // console.log(err) - // return false - // } + if (plugin) { + this.updatePluginList(pluginId, { + ...plugin, + users: newUsers, + admins: newAdmins + }) + return { + status: 204, + data: null + } + } else { + return { + status: 404, + data: { + error: 'something bad happened' + } + } + } } getUserPlugins = async email => { const plugins = await this.getPlugins(); + console.log(plugins) - return plugins.find(plugin => { + const userPlugins = plugins.filter(plugin => { const users = plugin.users || []; const admins = plugin.admins || []; return users.includes(email) || admins.includes(email) - }) + }) || [] + + console.log("users plugins") + console.log(userPlugins, email) + + return Promise.all(userPlugins.map(plugin => this.getPlugin(email, plugin.pluginId))) + .then(plugins => { + return plugins.filter(data => Object.keys(data).length > 0) + }) } - getInvitation = async (userEmail, ownerId, pluginId) => { - // try { - // const ownerPlugins = await this.getUserPlugins(ownerId) + addPluginToList = async (email, plugin) => { + const { instance, Bucket } = this.#state; - // const ownerPlugin = ownerPlugins.find(plugin => plugin.pluginId === pluginId) + const plugins = await this.getPlugins() - // return ownerPlugin - // } catch (err) { - // console.log(err) - // return false - // } + return instance.send(new PutObjectCommand({ + Bucket, + Key: 'plugins.json', + ContentType: 'application/json', + Body: fromUtf8(JSON.stringify([ + ...plugins, + { + pluginId: plugin.pluginId, + admins: [email], + users: [] + } + ])) + })) } - canSharePlugin = async (email, pluginId) => { - // const user = await this.getUser(email); + updatePluginList = async (pluginId, content) => { + const { instance, Bucket } = this.#state; - // const plugin = user.plugins.find(plugin => plugin.pluginId === pluginId); + const plugins = await this.getPlugins() - // if (plugin?.owner) { - // const owner = await this.getUser(plugin.owner) + return instance.send(new PutObjectCommand({ + Bucket, + Key: 'plugins.json', + ContentType: 'application/json', + Body: fromUtf8(JSON.stringify(plugins.map(plugin => { + if (plugin.pluginId === pluginId) { + return content + } + return plugin + }))) + })) + } - // const rootPlugin = owner.plugins.find(plugin => plugin.pluginId === pluginId); + removePluginFromList = async pluginId => { + const { instance, Bucket } = this.#state; - // if (rootPlugin) { - // return (rootPlugin.admins || []).includes(email) - // } + const plugins = await this.getPlugins() - // return false - // } + return instance.send(new PutObjectCommand({ + Bucket, + Key: 'plugins.json', + ContentType: 'application/json', + Body: fromUtf8(JSON.stringify(plugins.filter(plugin => plugin.pluginId !== pluginId))) + })) + } + + getInvitation = (email, pluginId) => this.getPlugin(email, pluginId) - return true + canSharePlugin = async (email, pluginId) => { + const plugins = await this.getPlugins() + + const plugin = plugins.find(p => p.pluginId === pluginId) + + if (plugin) { + return plugin.admins.includes(email) + } + + return false } - getPluginUsers = async (pluginId) => { - const plugin = await this.getPlugin(pluginId) + getPluginUsers = async (email, pluginId) => { + const plugins = await this.getPlugins() + + const plugin = plugins.find(p => p.pluginId === pluginId) - return { - admins: plugin.admins, - users: plugin.users + if (plugin) { + return { + status: 200, + data: { + admins: plugin.admins, + users: plugin.users + } + } + } else { + return { + status: 404, + data: { + error: 'something bad happened' + } + } } } }; diff --git a/server/routers/invitation.js b/server/routers/invitation.js index 1780a78..b0afc07 100755 --- a/server/routers/invitation.js +++ b/server/routers/invitation.js @@ -4,43 +4,38 @@ const Datastore = require('../datastores'); const router = express.Router() router.get('/:id', (req, res) => { - const [userId, pluginId] = Buffer + const pluginId = Buffer .from(req.params.id, 'base64') .toString('ascii') - .split(":") - if (req.user.email !== userId) { - Datastore.getInvitation(req.user.email, userId, pluginId) - .then(body => { - if (body) { - return res.json(body) - } else { - return res.status(400).json({ error: 'something wrong happened' }) - } - }) - } else { - res.status(400).json({ error: 'something wrong happened' }) - } + Datastore.getInvitation(req.user.email, pluginId) + .then(body => { + if (body) { + return res.json(body) + } else { + return res.status(400).json({ error: 'something wrong happened' }) + } + }) + .catch(err => { + if (err === "Not authorized") { + res.redirect('/') + } + }) }) -router.post('/:hash', (req, res) => { - const [userId, pluginId] = Buffer - .from(req.params.hash, 'base64') - .toString('ascii') - .split(":") +// router.post('/:hash', (req, res) => { +// const pluginId = Buffer +// .from(req.params.id, 'base64') +// .toString('ascii') - if (req.user.email !== userId) { - Datastore.acceptInvitation(req.user.email, userId, pluginId) - .then(accepted => { - if (accepted) { - res.redirect(`/?pluginId=${pluginId}`) - } else { - res.redirect('/') - } - }) - } else { - res.redirect('/') - } -}) +// Datastore.acceptInvitation(req.user.email, pluginId) +// .then(accepted => { +// if (accepted) { +// res.redirect(`/?pluginId=${pluginId}`) +// } else { +// res.redirect('/') +// } +// }) +// }) module.exports = router diff --git a/server/routers/plugins.js b/server/routers/plugins.js index 8bba43e..75cc34f 100755 --- a/server/routers/plugins.js +++ b/server/routers/plugins.js @@ -148,28 +148,25 @@ router.put('/:id', (req, res) => { }) router.get('/:id/share-links', async (req, res) => { - const plugins = await Datastore.getUserPlugins(req.user.email) - - const owner = plugins.find(plugin => plugin.pluginId === req.params.id)?.owner || req.user.email - - const hash = Buffer.from(`${owner}:${req.params.id}`).toString('base64') + const hash = Buffer.from(req.params.id).toString('base64') res.json(`${ENV.SECURE_DOMAIN ? 'https' : 'http'}://${ENV.DOMAIN}:${ENV.EXPOSED_PORT || ENV.PORT}/invitation/${hash}`) }) router.put('/:id/users', (req, res) => { - Datastore.patchPluginUsers(req.params.id, + Datastore.patchPluginUsers(req.user.email, + req.params.id, req.body.users, - [...new Set([...req.body.admins, req.user.email])]) - .then(() => { + req.body.admins) + .then(data => { res - .status(204) - .json(null) + .status(data.status) + .json(data.body) }) }) router.get('/:id/users', (req, res) => { - Datastore.getPluginUsers(req.params.id) - .then(members => res.status(200).json(members)) + Datastore.getPluginUsers(req.user.email, req.params.id) + .then(out => res.status(out.status).json(out.data)) }) router.get('/:id/rights', (req, res) => { @@ -186,7 +183,7 @@ router.get('/:id/rights', (req, res) => { }) router.delete('/:id', async (req, res) => { - Datastore.deletePlugin(req.params.id) + Datastore.deletePlugin(req.user.email, req.params.id) .then(out => { res.status(out.status).json(out.body) }) @@ -342,15 +339,9 @@ router.post('/:id/build', async (req, res) => { const pluginId = req.params.id; const release = req.query.release === 'true'; - let user = req.user ? req.user.email : 'admin@otoroshi.io' - - const data = await Datastore.getUser(user) - let plugin = (data.plugins || []).find(p => p.pluginId === pluginId); + let user = req.user ? req.user.email : 'admin@otoroshi.io'; - if (plugin?.owner) { - user = plugin.owner; - plugin = await Datastore.getPlugin(plugin.owner, pluginId); - } + const plugin = await Datastore.getPlugin(user, pluginId); if (plugin.type === 'github') { plugin.type = req.query.plugin_type; diff --git a/server/routers/public.js b/server/routers/public.js index e7b9e6c..20079e9 100755 --- a/server/routers/public.js +++ b/server/routers/public.js @@ -67,27 +67,10 @@ router.get('/wasm/:id', (req, res) => { router.get('/plugins', (req, res) => { const reg = req.headers['kind'] || '*'; - if (reg === '*') { - Datastore.getUsers() - .then(r => { - const users = [...new Set([...(r || []), "adminotoroshiio"])]; - - if (users.length > 0) { - Promise.all(users.map(Datastore.getUser)) - .then(pluginsByUser => { - res.json(pluginsByUser - .map(user => user.plugins) - .flat() - .filter(f => f)) - }) - } else { - res.json([]) - } - }) - } else { - Datastore.getUser(reg) - .then(data => res.json(data.plugins)) - } + Datastore + .getPlugins() + .then(plugins => Promise.all(plugins.map(plugin => Datastore.getPlugin(reg, plugin.pluginId)))) + .then(plugins => res.json(plugins.filter(data => Object.keys(data).length > 0))) }); module.exports = router; \ No newline at end of file diff --git a/server/services/compiler/compiler.js b/server/services/compiler/compiler.js index a226084..d98e23e 100755 --- a/server/services/compiler/compiler.js +++ b/server/services/compiler/compiler.js @@ -155,7 +155,7 @@ class Compiler { .then(() => this.#websocketEmitMessage(buildOptions, "WASM has been saved ...")), Datastore.putBuildLogsToS3(`${buildOptions.plugin.id}-logs.zip`, buildOptions.logsFolder) .then(() => this.#websocketEmitMessage(buildOptions, "Logs has been saved ...")), - Datastore.putWasmInformationsToS3(buildOptions.plugin.id, buildOptions.plugin.hash, `${this.options.wasmName}.wasm`) + Datastore.putWasmInformationsToS3(buildOptions.userEmail, buildOptions.plugin.id, buildOptions.plugin.hash, `${this.options.wasmName}.wasm`) .then(() => this.#websocketEmitMessage(buildOptions, "Informations has been updated")) ])) .then(() => { diff --git a/ui/src/App.js b/ui/src/App.js index 914d269..7a6b67f 100644 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -14,7 +14,7 @@ class App extends React.Component { plugins: [], selectedPlugin: undefined, configFiles: [], - version: 'unknown' + version: '' } componentDidMount() { diff --git a/ui/src/InvitationHandler.js b/ui/src/InvitationHandler.js index eed498c..d020973 100644 --- a/ui/src/InvitationHandler.js +++ b/ui/src/InvitationHandler.js @@ -18,13 +18,21 @@ function InvitationHandler() { const paths = window.location.pathname.split("/invitation"); if (paths.length === 2) { - const invitationId = paths[1]; + const invitationId = paths[1].slice(1); Service.getInvitationInformation(invitationId) - .then(invit => setInvitation({ - ...invit, - invitationId - })) + .then(response => { + if (response.redirected) { + window.location.href = response.url; + } else { + response + .json() + .then(invit => setInvitation({ + ...invit, + invitationId + })) + } + }) } }, []) @@ -38,7 +46,7 @@ function InvitationHandler() { }}>

diff --git a/ui/src/services/index.js b/ui/src/services/index.js index c1d8324..2701dfa 100644 --- a/ui/src/services/index.js +++ b/ui/src/services/index.js @@ -134,7 +134,7 @@ export const getWasmRelease = wasmId => rawFetch(`/wasm/${wasmId}`); export const getAppVersion = () => f('/version'); -export const getInvitationInformation = invitationId => jsonFetch(`/invitations/${invitationId}`) +export const getInvitationInformation = invitationId => f(`/invitations/${invitationId}`) export const acceptInvitation = invitationId => f(`/invitations/${invitationId}`, { method: 'POST', From 5199ba6dbd9d0b6bcd7ad9a66a2b675d26ebc6e0 Mon Sep 17 00:00:00 2001 From: zwiterrion Date: Wed, 5 Jun 2024 15:49:25 +0200 Subject: [PATCH 3/4] rewrite postgres api --- server/datastores/api.js | 111 +++++- server/datastores/postgres.js | 522 +++++++++++---------------- server/datastores/s3.js | 5 +- server/routers/plugins.js | 2 +- server/services/compiler/compiler.js | 2 +- 5 files changed, 303 insertions(+), 339 deletions(-) diff --git a/server/datastores/api.js b/server/datastores/api.js index 727bd95..1551b29 100755 --- a/server/datastores/api.js +++ b/server/datastores/api.js @@ -4,6 +4,16 @@ module.exports = class Datastore { * Initialize the datastore */ async initialize() { Promise.resolve() } + /** + * List of created plugins, whole database + */ + getPlugins() { Promise.resolve() } + /** + * Check if user is in the users or admins list of specific plugin + * @param {string} email + * @param {string} pluginId + */ + hasRights(email, pluginId) { Promise.resolve() } /** * Get current user */ @@ -13,6 +23,26 @@ module.exports = class Datastore { * @returns {Object[]} */ getUserPlugins(email) { return Promise.resolve() } + /** + * Add plugin to the list of plugins + * @param {string} email + * @param {any} plugin + * @returns + */ + addPluginToList = (email, plugin) => Promise.resolve() + /** + * Update plugin informations like users, admins list + * @param {string} pluginId + * @param {any} content + * @returns + */ + updatePluginList = (pluginId, content) => Promise.resolve() + /** + * Remove plugin from list of plugins + * @param {string} pluginId + * @returns + */ + removePluginFromList = (pluginId) => Promise.resolve() /** * register a new user if not present * @param {string} email @@ -32,8 +62,11 @@ module.exports = class Datastore { /** * Save wasm file * @param {string} wasmFolder + * @param {string} pluginId + * @param {string} newHash + * @param {string} generateWasmName */ - putWasmFileToS3(wasmFolder) { return Promise.resolve() } + putWasmFileToS3(email, pluginId, newHash, generateWasmName) { return Promise.resolve() } /** * Save logs file @@ -48,7 +81,7 @@ module.exports = class Datastore { * @param {string} newHash * @param {string} generateWasmName */ - putWasmInformationsToS3(email, pluginId, newHash, generateWasmName) { return Promise.resolve() } + pushNewPluginVersion(email, pluginId, newHash, generateWasmName) { return Promise.resolve() } /** * Fetch wasm from datastore @@ -62,24 +95,26 @@ module.exports = class Datastore { * @param {JSON} runOptions */ runWasm(wasmId, runOptions) { return Promise.resolve() } - /** * Check the presence of a specific wasm in database * @param {string} wasmId * @param {boolean} release */ - isWasmExists(wasmId, release) { - return Promise.resolve() - } - + isWasmExists(wasmId, release) { return Promise.resolve() } /** * Fetch plugin sources * @param {string} pluginId * @returns sources as buffer */ - getSources = pluginId => { - return Promise.resolve() - } + getSources = pluginId => { return Promise.resolve() } + + /** + * Get log files of specific plugin at add them to files + * @param {string} pluginId + * @param {any} files + * @returns + */ + getConfigurationsFile (pluginId, files) { return Promise.resolve() } /** * Fetch configuration file @@ -91,10 +126,35 @@ module.exports = class Datastore { } /** - * Delete specific plugin + * Remove all wasm version of specific plugins + * @param {string[]} versions + * @returns + */ + removeBinaries = versions => { + return Promise.resolve() + } + + /** + * Remove object from S3 + * @param {string} key + * @returns + */ + deleteObject = key => { return Promise.resolve() } + + /** + * Delete specific plugin and all assets + * @param {string} email * @param {string} pluginId */ - deletePlugin = (pluginId) => Promise.resolve() + deletePlugin = (email, pluginId) => Promise.resolve() + + /** + * Update plugin informations + * @param {string} id + * @param {any} body - plugin content + * @returns + */ + updatePluginInformations = (pluginId, body) => Promise.resolve() /** * Update plugin content @@ -109,6 +169,15 @@ module.exports = class Datastore { * @param {JSON} metadata */ createEmptyPlugin = (email, metadata, isGithub) => Promise.resolve() + /** + * Update plugin field + * @param {string} email + * @param {string} pluginId + * @param {string} field + * @param {any} value + * @returns + */ + patchPlugin = (email, pluginId, field, value) => Promise.resolve() /** * Edit the plugin name @@ -135,12 +204,12 @@ module.exports = class Datastore { getPluginUsers = (email, pluginId) => Promise.resolve() /** - * Get plugin of user - * @param {string} owner + * Get plugin (check if user can access it) + * @param {string} email * @param {string} pluginId * @returns */ - getPlugin = (owner, pluginId) => Promise.resolve() + getPlugin = (email, pluginId) => Promise.resolve() /** * Check if user can share plugin @@ -182,9 +251,15 @@ module.exports = class Datastore { /** * Get invitation informations - * @param {string} userId + * @param {string} email * @param {string} pluginId - * @returns */ - getInvitation = (userId, pluginId) => Promise.resolve() + getInvitation = (email, pluginId) => Promise.resolve() + + /** + * Check if user are allowed to share plugin to other users + * @param {string} email + * @param {string} pluginId + */ + canSharePlugin = (email, pluginId) => Promise.resolve() }; \ No newline at end of file diff --git a/server/datastores/postgres.js b/server/datastores/postgres.js index 5e5598d..8e5ee7c 100755 --- a/server/datastores/postgres.js +++ b/server/datastores/postgres.js @@ -51,35 +51,29 @@ module.exports = class PgDatastore extends Datastore { const client = await this.#pool.connect(); await Promise.all([ - client.query("CREATE TABLE IF NOT EXISTS users(id SERIAL, email VARCHAR, content JSONB)"), + client.query("CREATE TABLE IF NOT EXISTS plugins(id VARCHAR(200), content JSONB)"), client.query("CREATE TABLE IF NOT EXISTS jobs(id SERIAL, plugin_id VARCHAR UNIQUE, created_at TIMESTAMP default current_timestamp)"), ]); client.release() } - getUser = (email) => { + getPlugins = () => { return this.#pool.connect() .then(async client => { - const res = await client.query("SELECT content from users WHERE email = $1", [email]) + const res = await client.query("SELECT content FROM plugins", []) client.release(); - return res.rowCount > 0 ? res.rows[0].content : {}; + return res.rowCount > 0 ? res.rows.map(r => r.content) : [] }) } - getUserPlugins = (email) => { - return this.getUser(email) - .then(data => data.plugins || []) - .then(plugins => { - return Promise.all(plugins.map(plugin => { - if (plugin.owner) { - return this.#getPlugin(plugin.owner, plugin.pluginId) - .then(res => ({ ...res, owner: plugin.owner })) - } else { - return Promise.resolve(plugin) - } - }) - ) + getUserPlugins = async email => { + return this.#pool.connect() + .then(async client => { + const res = await client.query("SELECT content FROM plugins WHERE content->'admins' ? $1::text OR content->'users' ? $1::text", [email]) + client.release(); + + return res.rowCount > 0 ? res.rows.map(r => r.content) : [] }) } @@ -104,28 +98,6 @@ module.exports = class PgDatastore extends Datastore { }) } - updateUser = (email, content) => { - return this.#pool.connect() - .then(client => { - return client.query("UPDATE users SET content = $1::jsonb WHERE email = $2", [content, email]) - .then(async res => { - if (res.rowCount === 0) { - await client.query("INSERT INTO users (content, email) VALUES($1::jsonb, $2)", [content, email]) - } - - client.release() - }); - }) - .then(() => ({ status: 200 })) - .catch(err => { - console.log('update user error', err) - return { - error: 400, - status: 'something bad happened' - } - }); - } - putWasmFileToS3 = (wasmFolder) => { return this.#sourcesDatastore.putWasmFileToS3(wasmFolder); } @@ -134,51 +106,39 @@ module.exports = class PgDatastore extends Datastore { return this.#sourcesDatastore.putBuildLogsToS3(logId, logsFolder); } - putWasmInformationsToS3 = (email, pluginId, newHash, generateWasmName) => { - return this.getUser(email) - .then(data => { - const plugin = data.plugins.map(plugin => plugin.pluginId === pluginId) - - if (plugin.owner) - return this.putWasmInformationsToS3(plugin.owner, pluginId, newHash, generateWasmName) + pushNewPluginVersion = (email, pluginId, newHash, generateWasmName) => { + return this.getPlugin(email, pluginId) + .then(plugin => { + let versions = plugin.versions || []; - const newPlugins = data.plugins.map(plugin => { - if (plugin.pluginId !== pluginId) { - return plugin; - } - let versions = plugin.versions || []; + // convert legacy array + if (versions.length > 0 && isAString(versions[0])) { + versions = versions.map(name => ({ name })) + } - // convert legacy array - if (versions.length > 0 && isAString(versions[0])) { - versions = versions.map(name => ({ name })) + const index = versions.findIndex(item => item.name === generateWasmName); + if (index === -1) + versions.push({ + name: generateWasmName, + updated_at: Date.now(), + creator: email + }) + else { + versions[index] = { + ...versions[index], + updated_at: Date.now(), + creator: email } + } - const index = versions.findIndex(item => item.name === generateWasmName); - if (index === -1) - versions.push({ - name: generateWasmName, - updated_at: Date.now(), - creator: email - }) - else { - versions[index] = { - ...versions[index], - updated_at: Date.now(), - creator: email - } - } + const patchPlugin = { + ...plugin, + last_hash: newHash, + wasm: generateWasmName, + versions + } - return { - ...plugin, - last_hash: newHash, - wasm: generateWasmName, - versions - } - }); - return this.updateUser(email, { - ...data, - plugins: newPlugins - }) + return this.updatePluginList(pluginId, patchPlugin) }) } @@ -198,20 +158,46 @@ module.exports = class PgDatastore extends Datastore { return this.#sourcesDatastore.getSources(pluginId); } - #getPlugin = (email, pluginId) => { + hasRights = (email, pluginId) => { return this.#pool.connect() - .then(client => { - return client.query("SELECT content FROM users WHERE email = $1", [email]) - .then(res => { - client.release(); + .then(client => client.query("SELECT * FROM plugins WHERE id = $1::text", [pluginId]) + .then(res => { + client.release() + return res.rowCount === 1 ? res.rows[0].content : {} + })) + .then(plugin => { + if (email === "*") + return + + const users = plugin?.users || []; + const admins = plugin?.admins || []; + + if (users.includes(email) || admins.includes(email)) { + return plugin + } - return res.rowCount > 0 ? (res.rows[0].content.plugins || [])?.find(plugin => plugin.pluginId === pluginId) : {} - }) + // TODO - better error handling + throw 'Not authorized' }) } + getPlugin = async (email, pluginId) => { + try { + await this.hasRights(email, pluginId) + } catch (_err) { + return {} + } + + return this.#pool.connect() + .then(client => client.query("SELECT * FROM plugins WHERE id = $1::text", [pluginId]) + .then(res => { + client.release() + return res.rowCount === 1 ? res.rows[0].content : {} + })) + } + getConfigurations = async (email, pluginId) => { - const plugin = await this.#getPlugin(email, pluginId); + const plugin = await this.getPlugin(email, pluginId); if (plugin.owner) return this.getConfigurations(plugin.owner, pluginId) @@ -228,60 +214,34 @@ module.exports = class PgDatastore extends Datastore { return this.#sourcesDatastore.getConfigurationsFile(plugin.pluginId, files); } - #deleteRootPlugin = async (plugin, pluginId) => { - const { admins, users } = plugin; - - await Promise.all([ - ...(admins || []), - ...(users || []) - ].map(user => this.#removePluginFromUser(user, pluginId))) - - return this.deletePlugin(plugin.owner, pluginId) - } - deletePlugin = (email, pluginId) => { - return new Promise(resolve => this.getUser(email) - .then(data => { - if (Object.keys(data).length > 0) { - const plugin = data.plugins.find(f => f.pluginId === pluginId) - - this.updateUser(email, { - ...data, - plugins: data.plugins.filter(f => f.pluginId !== pluginId) - }) - .then(() => { - if (plugin.owner || plugin.users || plugin.admins) { - this.#deleteRootPlugin(plugin, pluginId) - .then(resolve) - } else { - const pluginHash = (plugin || {}).last_hash; - - Promise.all([ - this.#sourcesDatastore.deleteObject(`${pluginHash}.zip`), - this.#sourcesDatastore.deleteObject(`${pluginHash}-logs.zip`), - this.#sourcesDatastore.deleteObject(`${pluginId}.zip`), - this.#sourcesDatastore.deleteObject(`${pluginId}-logs.zip`), - this.#sourcesDatastore.deleteObject(`${pluginId}.zip`), - this.#sourcesDatastore.deleteObject(`${pluginId}-logs.zip`), - this.#sourcesDatastore.removeBinaries((plugin.versions || []).map(r => r.name)) - ]) - .then(() => resolve({ status: 204, body: null })) - .catch(err => { - resolve({ - status: 400, - body: { - error: err.message - } - }) - }) + return new Promise(resolve => this.getPlugin(email, pluginId) + .then(plugin => { + Promise.all([ + this.#sourcesDatastore.deleteObject(`${pluginId}.zip`), + this.#sourcesDatastore.deleteObject(`${pluginId}-logs.zip`), + this.#sourcesDatastore.removeBinaries((plugin.versions || []).map(r => r.name)), + this.#sourcesDatastore.deleteObject(`${pluginId}.json`), + this.removePluginFromList(pluginId) + ]) + .then(() => resolve({ status: 204, body: null })) + .catch(err => { + resolve({ + status: 400, + body: { + error: err.message } }) - } else { - resolve({ - status: 204, body: null }) - } })) + .catch(_ => { + resolve({ + status: 400, + body: { + error: "something bad happened" + } + }) + }) } updatePlugin = (id, body) => { @@ -290,135 +250,118 @@ module.exports = class PgDatastore extends Datastore { createEmptyPlugin = (email, metadata, isGithub) => { return new Promise(resolve => { - this.createUserIfNotExists(email) - .then(() => { - this.getUser(email) - .then(data => { - const pluginId = crypto.randomUUID() - const plugins = [ - ...(data.plugins || []), - isGithub ? { - filename: metadata.repo, - owner: metadata.owner, - ref: metadata.ref, - type: 'github', - pluginId: pluginId, - private: metadata.private - } : { - filename: metadata.name.replace(/ /g, '-'), - type: metadata.type, - pluginId: pluginId, - template: metadata.template - } - ] - - return this.#pool.connect() - .then(client => { - return client.query("UPDATE users SET content = $1::jsonb WHERE email = $2", [{ plugins }, email]) - .then(() => client.release()) - }) - .then(() => { - resolve({ - status: 201, - body: { - plugins - } - }) - }) - .catch(err => { - resolve({ - status: 400, - body: { - error: err.message - } - }) - }) + const pluginId = crypto.randomUUID() + const newPlugin = isGithub ? { + filename: metadata.repo, + owner: metadata.owner, + ref: metadata.ref, + type: 'github', + pluginId: pluginId, + private: metadata.private + } : { + filename: metadata.name.replace(/ /g, '-'), + type: metadata.type, + pluginId: pluginId, + template: metadata.template + } + + console.log('generate pluginID', pluginId, newPlugin.pluginId) + + return this.#pool.connect() + .then(client => { + return client.query("INSERT INTO plugins(id, content) VALUES($1, $2::jsonb)", [newPlugin.pluginId, JSON.stringify({ + ...newPlugin, + admins: [email], + users: [] + })]) + .then(() => { + client.release() + return this.getUserPlugins(email) }) }) + .then(plugins => { + resolve({ + status: 201, + body: { + plugins + } + }) + }) .catch(err => { + console.log(err) resolve({ - status: 400, + status: err.$metadata.httpStatusCode, body: { - error: err.message + error: err.Code, + status: err.$metadata.httpStatusCode } }) }) }) } - patchPlugin = (email, pluginId, field, value) => { - return this.getUser(email) - .then(data => { - const plugin = (data.plugins || []).find(plugin => plugin.pluginId === pluginId) - - if (plugin.owner) - return this.patchPlugin(plugin.owner, pluginId, field, value) - else - return this.updateUser(email, { - ...data, - plugins: (data.plugins || []).map(plugin => { - if (plugin.pluginId === pluginId) { - return { - ...plugin, - [field]: value - } - } else { - return plugin - } - }) - }) - }) - } + patchPlugin = async (email, pluginId, field, value) => { + const plugin = await this.getPlugin(email, pluginId) - patchPluginName = (email, pluginId, newName) => { - return this.patchPlugin(email, pluginId, 'filename', value) + return this.updatePluginList(pluginId, { + ...plugin, + [field]: value + }) } - #removePluginFromUser = async (email, pluginId) => { - const user = await this.getUser(email) - - return this.updateUser(email, { - ...user, - plugins: (user.plugins || []).filter(plugin => plugin.pluginId !== pluginId) - }) + patchPluginName = (email, pluginId, value) => { + return this.patchPlugin(email, pluginId, 'filename', value) } patchPluginUsers = async (email, pluginId, newUsers, newAdmins) => { - let currentUser = email; - - const user = await this.getUser(currentUser); + const plugin = await this.getPlugin(email, pluginId) - let plugin = user.plugins.find(plugin => plugin.pluginId === pluginId) + if (plugin) { + this.updatePluginList(pluginId, { + ...plugin, + users: newUsers, + admins: newAdmins + }) + return { + status: 204, + data: null + } + } else { + return { + status: 404, + data: { + error: 'something bad happened' + } + } + } + } - // if plugin is not owned by the user, we need to edit the root plugin - if (plugin.owner) { - currentUser = plugin.owner; + addPluginToList = async (email, plugin) => { + return this.#pool.connect() + .then(client => { + return client.query("INSERT INTO plugins(id, content) VALUES($1, $2::jsonb)", [plugin.pluginId, JSON.stringify({ + pluginId: plugin.pluginId, + admins: [email], + users: [] + })]) + .then(() => client.release()) + }) + } - const owner = await this.getUser(plugin.owner) - plugin = owner.plugins.find(plugin => plugin.pluginId === pluginId) - } + updatePluginList = async (pluginId, content) => { + return this.#pool.connect() + .then(client => { + return client.query("UPDATE plugins SET content = $1::jsonb WHERE id = $2", [JSON.stringify(content), pluginId]) + .then(() => client.release()) + }) + } - const removedUsers = (plugin.users || []).filter(user => !newUsers.includes(user) && !newAdmins.includes(user)) - const removedAdmins = (plugin.admins || []).filter(admin => !newAdmins.includes(admin) && !newUsers.includes(admin)) - - await Promise.all(removedUsers.map(user => this.#removePluginFromUser(user, pluginId))) - await Promise.all(removedAdmins.map(admin => this.#removePluginFromUser(admin, pluginId))) - - return this.getUser(currentUser) - .then(data => this.updateUser(currentUser, { - ...data, - plugins: (data.plugins || []).map(plugin => { - if (plugin.pluginId === pluginId) { - return { - ...plugin, - users: newUsers, - admins: newAdmins - } - } else { - return plugin - } - }) - })) + removePluginFromList = async (pluginId) => { + return this.#pool.connect() + .then(client => { + return client.query("DELETE FROM plugins WHERE id = $1", [pluginId]) + .then(() => client.release()) + }) } isJobRunning = pluginId => { @@ -473,79 +416,26 @@ module.exports = class PgDatastore extends Datastore { }); } - acceptInvitation = async (userEmail, ownerId, pluginId) => { - try { - const ownerPlugins = await this.getUserPlugins(ownerId) - const user = await this.getUser(userEmail) - - const ownerPlugin = ownerPlugins.find(plugin => plugin.pluginId === pluginId) - - if (ownerPlugin && - ((ownerPlugin.users || []).includes(userEmail) || - (ownerPlugin.admins || []).includes(userEmail))) { - - const newPlugin = { - pluginId: ownerPlugin.pluginId, - owner: ownerId - } - - return this.updateUser(userEmail, { - ...user, - plugins: [ - ...(user.plugins || []).filter(plugin => plugin.pluginId !== pluginId), - newPlugin - ] - }) - .then(() => true) - } else { - return false - } - } catch (err) { - console.log(err) - return false - } - } + getInvitation = (email, pluginId) => this.getPlugin(email, pluginId) canSharePlugin = async (email, pluginId) => { - const user = await this.getUser(email); - - const plugin = user.plugins.find(plugin => plugin.pluginId === pluginId); - - if (plugin?.owner) { - const owner = await this.getUser(plugin.owner) - - const rootPlugin = owner.plugins.find(plugin => plugin.pluginId === pluginId); - - if (rootPlugin) { - return (rootPlugin.admins || []).includes(email) - } - - return false - } - - return true - + return this.#pool.connect() + .then(client => client.query("SELECT * FROM plugins WHERE id = $1::text AND content->'admins' ? $2::text", [pluginId, email]) + .then(res => { + client.release() + return res.rowCount > 0 + })) } - getPluginUsers = async (email, pluginId) => { - const user = await this.getUser(email); - - const plugin = user.plugins.find(plugin => plugin.pluginId === pluginId); - - if (plugin.owner) { - const owner = await this.getUser(plugin.owner) - - const rootPlugin = owner.plugins.find(plugin => plugin.pluginId === pluginId); - - return { - admins: rootPlugin.admins, - users: rootPlugin.users - } - } - - return { - admins: plugin.admins, - users: plugin.users - } - } -}; \ No newline at end of file + getPluginUsers = (email, pluginId) => { + return this.#pool.connect() + .then(client => client.query(`SELECT content->'admins' as admins, content->'users' as users + FROM plugins + WHERE id = $1::text AND content->'admins' ? $2::text`, [pluginId, email]) + .then(res => { + client.release() + return res.rowCount > 0 ? res.rows[0] : { admins: [], users: [] } + })) + .then(data => ({ status: 200, data })) + } +} \ No newline at end of file diff --git a/server/datastores/s3.js b/server/datastores/s3.js index 4d67458..69aee8c 100755 --- a/server/datastores/s3.js +++ b/server/datastores/s3.js @@ -15,11 +15,10 @@ const dns = require('dns'); const url = require('url'); const fs = require('fs-extra'); -const { format, isAString } = require('../utils'); +const { isAString } = require('../utils'); const Datastore = require('./api'); const { ENV, STORAGE } = require("../configuration"); const logger = require("../logger"); -const consumers = require('node:stream/consumers'); const AdmZip = require("adm-zip"); const { Console } = require('console'); const CustomStream = require('./CustomStream'); @@ -210,7 +209,7 @@ module.exports = class S3Datastore extends Datastore { } } - putWasmInformationsToS3 = (email, pluginId, newHash, generateWasmName) => { + pushNewPluginVersion = (email, pluginId, newHash, generateWasmName) => { return this.getPlugin(email, pluginId) .then(plugin => { let versions = plugin.versions || []; diff --git a/server/routers/plugins.js b/server/routers/plugins.js index 75cc34f..9e91b6a 100755 --- a/server/routers/plugins.js +++ b/server/routers/plugins.js @@ -413,7 +413,7 @@ router.post('/:id/build', async (req, res) => { }) router.patch('/:id/filename', (req, res) => { - Datastore.patchPluginName(req.params.id, req.body.filename) + Datastore.patchPluginName(req.user.email, req.params.id, req.body.filename) .then(() => { res .status(204) diff --git a/server/services/compiler/compiler.js b/server/services/compiler/compiler.js index d98e23e..7693c64 100755 --- a/server/services/compiler/compiler.js +++ b/server/services/compiler/compiler.js @@ -155,7 +155,7 @@ class Compiler { .then(() => this.#websocketEmitMessage(buildOptions, "WASM has been saved ...")), Datastore.putBuildLogsToS3(`${buildOptions.plugin.id}-logs.zip`, buildOptions.logsFolder) .then(() => this.#websocketEmitMessage(buildOptions, "Logs has been saved ...")), - Datastore.putWasmInformationsToS3(buildOptions.userEmail, buildOptions.plugin.id, buildOptions.plugin.hash, `${this.options.wasmName}.wasm`) + Datastore.pushNewPluginVersion(buildOptions.userEmail, buildOptions.plugin.id, buildOptions.plugin.hash, `${this.options.wasmName}.wasm`) .then(() => this.#websocketEmitMessage(buildOptions, "Informations has been updated")) ])) .then(() => { From e3f1ede0e82056130769e5f967be3ace16633256 Mon Sep 17 00:00:00 2001 From: zwiterrion Date: Thu, 6 Jun 2024 14:54:24 +0200 Subject: [PATCH 4/4] rework documentation --- cli/README.md | 5 +- .../app/builder/collaborate/_page.mdx | 8 +- .../app/cli/getting-started/_page.mdx | 11 +- docs/documentation/app/faq/_page.mdx | 109 ++++++++++++++++++ docs/documentation/app/faq/layout.js | 7 ++ docs/documentation/app/faq/page.js | 20 ++++ docs/documentation/app/globals.css | 34 ++++++ docs/documentation/components/Badge.js | 7 ++ docs/documentation/components/Badges.js | 4 +- docs/documentation/components/FAQButton.js | 21 ++++ docs/documentation/components/Layout.js | 2 +- docs/documentation/components/Searchbar.js | 4 +- docs/documentation/components/Sidebar.js | 67 +++++++++-- docs/documentation/components/mdx/Heading.js | 36 +++++- docs/documentation/components/mdx/Pre.js | 9 +- docs/documentation/public/release.png | Bin 0 -> 86005 bytes server/datastores/postgres.js | 27 ++--- server/datastores/s3.js | 7 +- ui/src/TabsHeader.js | 25 +--- ui/src/TabsManager.js | 15 ++- ui/src/index.css | 1 + 21 files changed, 346 insertions(+), 73 deletions(-) create mode 100644 docs/documentation/app/faq/_page.mdx create mode 100644 docs/documentation/app/faq/layout.js create mode 100644 docs/documentation/app/faq/page.js create mode 100644 docs/documentation/components/Badge.js create mode 100644 docs/documentation/components/FAQButton.js create mode 100644 docs/documentation/public/release.png diff --git a/cli/README.md b/cli/README.md index 4c87587..64c200a 100644 --- a/cli/README.md +++ b/cli/README.md @@ -67,7 +67,10 @@ Then open the content of your `my-first-plugin` folder. You should find the gene You can now optionally start a new plugin from a template by appending `--template=[template-name]` to the creation command. -If you don't pass a template, Wasmo will list the available templates: `js`, `ts`, `opa`, `go` and `rust`. +If you don't pass a template, Wasmo will list the available templates. There are listed by product : + - empty template : `js`, `ts`, `opa`, `go` and `rust` + - Otoroshi template : `otoroshi_go`, `otoroshi_rust`, `otoroshi_opa`, `otoroshi_ts`, `otoroshi_js` + - Izanami template : `izanami_js`, `izanami_go`, `izanami_rust`, `izanami_opa`, `izanami_ts` ``` wasmo init --name=my-first-plugin --template=[template-name] --path=[output-directory] diff --git a/docs/documentation/app/builder/collaborate/_page.mdx b/docs/documentation/app/builder/collaborate/_page.mdx index 048658a..d994964 100644 --- a/docs/documentation/app/builder/collaborate/_page.mdx +++ b/docs/documentation/app/builder/collaborate/_page.mdx @@ -3,10 +3,10 @@ Wasmo 1.2.2 brings a new feature to collaborate inside the product. You can work together on a plugin by sharing it with collaborators. It's easy to share plugins with your entire team. -1. In Wasmo, on the left, click Share plugin. -2. Email can be added to two lists : the first to allow users to edit, view and share plugin, and the second, more restrictive, where users can only -edit and view plugins. -3. Click Update authorized people +To share plugins from the UI: + 1. Click on the desired plugin + 2. Click the 🔗 button + 3. Write administrators and users emails in their respective lists
diff --git a/docs/documentation/app/cli/getting-started/_page.mdx b/docs/documentation/app/cli/getting-started/_page.mdx index 643213a..6851926 100644 --- a/docs/documentation/app/cli/getting-started/_page.mdx +++ b/docs/documentation/app/cli/getting-started/_page.mdx @@ -43,7 +43,10 @@ Then open the content of your `my-first-plugin` folder. You should find the gene You can now optionally start a new plugin from a template by appending `--template=[template-name]` to the creation command. -If you don't pass a template, Wasmo will list the available templates: `js`, `ts`, `opa`, `go` and `rust`. +If you don't pass a template, Wasmo will list the available templates. There are listed by product : + - empty template : `js`, `ts`, `opa`, `go` and `rust` + - Otoroshi template : `otoroshi_go`, `otoroshi_rust`, `otoroshi_opa`, `otoroshi_ts`, `otoroshi_js` + - Izanami template : `izanami_js`, `izanami_go`, `izanami_rust`, `izanami_opa`, `izanami_ts` ``` wasmo init --name=my-first-plugin --template=[template-name] --path=[output-directory] @@ -61,7 +64,11 @@ You have two ways to build your plugin: [wasmoserver]: https://github.com/MAIF/wasmo -Assuming we want to build our `my-first-plugin` locally. Enter `wasmo build --host=OneShotDocker --path=my-first-plugin` to start the build. +Assuming we want to build our `my-first-plugin` locally. Enter the following command to start the build. + +``` +wasmo build --host=OneShotDocker --path=my-first-plugin +``` Let's explain these 3 parameters: - the `path` parameter is explicitly used to indicate the plugin to build diff --git a/docs/documentation/app/faq/_page.mdx b/docs/documentation/app/faq/_page.mdx new file mode 100644 index 0000000..5160f5c --- /dev/null +++ b/docs/documentation/app/faq/_page.mdx @@ -0,0 +1,109 @@ +import Badge from '../../components/Badge' +import Badges from '../../components/Badges' +import FAQButton from '../../components/FAQButton' + +# FAQ + + + + + + + + + + +### What is the fastest way to use Wasmo? + +``` +$ cargo install wasmo +or +$ brew tap maif/wasmo +$ brew install wasmo +``` + +### How to create an Otoroshi-compatible plugin using Docker? + + +Initialize the plugin with corresponding Otoroshi template and Javascript language +``` +wasmo init --template=otoroshi_js --name=foo +``` + +Build plugin from folder and Docker +``` +wasmo build --host=OneShotDocker --path=. +``` + +### How can I create a new development version of my plugin? + + +Rust plugin + +``` Cargo.toml +[package] +name = "foo" +version = "1.0.2" + +... +``` + +JS/TS/Open Policy Agent plugin + +``` package.json +{ + "name": "foo", + "version": "1.0.2", + ... +} +``` + +Go plugin + +``` go.mod +module foo/1.0.2 + +... +``` + +
+ + +
+ ### Can I download the generated Wasm from the UI? + + Once you have built a dev or release version of your plugin using the Hammer or Rocker buttons (available at the top right of the screen), + you can click on each version under the 'Releases' section on the left side of the screen. + + ### Can I determine who built each version and at what time? + + Each plugin has a **config** file under the **configuration** section with the following information : + - type: language used to develop the plugin. + - users: list of users allowed to edit and view the plugin. + - admins: list of admins allowed to edit, view and share the plugin. + - filename: name of the plugin. + - pluginId: unique ID of the plugin. + - template: original template, selected at plugin creation. + - **versions: list of built versions, with name, creator and date of generation**. + - last_hash: hash used by the backend to check if changes has been made between last version. +
+
+ +### How can I collaborate with my team? + +Since version 1.22, Wasmo allows users to share plugins with two levels of rights: +- `users`: Can edit and view plugin. +- `admins`: Can edit, view and share plugin. + +You can find more information about sharing by reading this [article](/wasmo/builder/collaborate) + +### Our team have a CI/CD process and wants to automate the building of our plugins. + +Since version 1.x, Wasmo includes a command line interface to create, edit and build plugins. + +You can find more [information](/wasmo/cli/getting-started) about the CLI and the Github [repository](https://github.com/MAIF/wasmo/tree/main/cli) \ No newline at end of file diff --git a/docs/documentation/app/faq/layout.js b/docs/documentation/app/faq/layout.js new file mode 100644 index 0000000..4962903 --- /dev/null +++ b/docs/documentation/app/faq/layout.js @@ -0,0 +1,7 @@ +import Page from './page'; + +export const metadata = { + title: 'FAQ', +} + +export default Page; \ No newline at end of file diff --git a/docs/documentation/app/faq/page.js b/docs/documentation/app/faq/page.js new file mode 100644 index 0000000..2ec87a6 --- /dev/null +++ b/docs/documentation/app/faq/page.js @@ -0,0 +1,20 @@ +"use client" + +import Layout from '@/components/Layout'; +import Page from './_page.mdx'; + +export default function Home() { + + return + + + +} \ No newline at end of file diff --git a/docs/documentation/app/globals.css b/docs/documentation/app/globals.css index a8aa6a2..6e2ff3e 100644 --- a/docs/documentation/app/globals.css +++ b/docs/documentation/app/globals.css @@ -16,4 +16,38 @@ overflow: auto; max-height: calc(500px - 71px); margin-bottom: 24px; +} + +.sidebar-group::before { + position: absolute; + bottom: -0.1rem; + top: -0.1rem; + left: -0.5rem; + width: 10px; + --tw-border-opacity: 1; + border-right-width: 2px; + border-color: rgb(226 232 240 / var(--tw-border-opacity)); + opacity: 1; + content: ""; +} + +.sidebar-group-selected::before { + border-color: rgb(126 34 206); +} + +h2, h3 { + position: relative; +} + +.anchor-link { + color: #666; + opacity: 0; + position: absolute; + transform: translate(-1em, -2px); + width: 1em; +} + +h3:hover .anchor-link, +h2:hover .anchor-link { + opacity: 1; } \ No newline at end of file diff --git a/docs/documentation/components/Badge.js b/docs/documentation/components/Badge.js new file mode 100644 index 0000000..c8eec99 --- /dev/null +++ b/docs/documentation/components/Badge.js @@ -0,0 +1,7 @@ +export default function Badge({ value, raw, ...props }) { + return
+ + {!raw ? `<${value}>` : value} + +
+} \ No newline at end of file diff --git a/docs/documentation/components/Badges.js b/docs/documentation/components/Badges.js index 88e92f5..23682fa 100644 --- a/docs/documentation/components/Badges.js +++ b/docs/documentation/components/Badges.js @@ -1,5 +1,5 @@ -export default function Badges({ values, raw }) { - return
+export default function Badges({ values, raw, ...props }) { + return
{values.map(value => {!raw ? `<${value}>` : value} )} diff --git a/docs/documentation/components/FAQButton.js b/docs/documentation/components/FAQButton.js new file mode 100644 index 0000000..eb64260 --- /dev/null +++ b/docs/documentation/components/FAQButton.js @@ -0,0 +1,21 @@ +export default function FAQButton({ title }) { + return +} \ No newline at end of file diff --git a/docs/documentation/components/Layout.js b/docs/documentation/components/Layout.js index 080cd37..1e9cd54 100644 --- a/docs/documentation/components/Layout.js +++ b/docs/documentation/components/Layout.js @@ -49,7 +49,7 @@ function Layout({ children, next, metadata, previous }) { table: Table, th: props => {props.children}, thead: props => {props.children}, - h3: props =>

{props.children}

, + h3: Heading.H3, h4: props =>

{props.children}

}}> {children} diff --git a/docs/documentation/components/Searchbar.js b/docs/documentation/components/Searchbar.js index 53f47b2..ab93494 100644 --- a/docs/documentation/components/Searchbar.js +++ b/docs/documentation/components/Searchbar.js @@ -70,7 +70,9 @@ export default function Searchbar({ handleOpen, open }) { paddingTop: open ? 20 : 6 }}> - {!open &&
+ {!open &&
  • Overview
  • +
  • + + FAQ +
    New
    +
    +
  • {Object.entries(LINKS).map(([group, children]) => { return
  • @@ -65,15 +95,38 @@ export function Sidebar({ metadata }) { -
      +
        {children.map(child => { const href = `/wasmo/${slugify(group)}/${slugify(child)}`; - return
      • + return
      • {child} + + {NEWS.includes(child) &&
        New
        }
      • })} diff --git a/docs/documentation/components/mdx/Heading.js b/docs/documentation/components/mdx/Heading.js index 26384bd..5d2741b 100644 --- a/docs/documentation/components/mdx/Heading.js +++ b/docs/documentation/components/mdx/Heading.js @@ -1,6 +1,38 @@ +function getAnchor(children) { + const text = Array.isArray(children) ? children[0] : children + return ("" + text) + .toLowerCase() + .replace(/[^a-z0-9 ]/g, '') + .replace(/[ ]/g, '-'); +} + export const Heading = { H1: ({ children }) =>

        {children}

        , - H2: props => { - return

        {props.children}

        + // H2: props => { + // return

        {props.children}

        + // }, + H2: ({ children }) => { + const anchor = getAnchor(children); + const link = `#${anchor}`; + return ( +

        + + § + + {children} +

        + ); }, + H3: ({ children }) => { + const anchor = getAnchor(children); + const link = `#${anchor}`; + return ( +

        + + § + + {children} +

        + ); + } }; \ No newline at end of file diff --git a/docs/documentation/components/mdx/Pre.js b/docs/documentation/components/mdx/Pre.js index 96f6cde..ec73f26 100644 --- a/docs/documentation/components/mdx/Pre.js +++ b/docs/documentation/components/mdx/Pre.js @@ -31,15 +31,16 @@ export const Pre = props => { {!['javascript', 'go', 'rust', 'js'].includes(language) && {language}}
  • } -
    { if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(codeString); setCopied(true) } - }} - style={{ - border: '1px solid #fff' }}> diff --git a/docs/documentation/public/release.png b/docs/documentation/public/release.png new file mode 100644 index 0000000000000000000000000000000000000000..36f2f70046c4f8f86ca5344c21edb927aaa88a19 GIT binary patch literal 86005 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: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≫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

    cy3!DNb)|appE8GB$mwCsq0#&1 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 { + if (email === "*") + return Promise.resolve() + return this.#pool.connect() - .then(client => client.query("SELECT * FROM plugins WHERE id = $1::text", [pluginId]) + .then(client => client.query("SELECT * FROM plugins WHERE id = $1::text AND (content->'admins' ? $2::text OR content->'users' ? $2::text)", [pluginId, email]) .then(res => { client.release() - return res.rowCount === 1 ? res.rows[0].content : {} + return res.rowCount === 1 })) - .then(plugin => { - if (email === "*") - return - - const users = plugin?.users || []; - const admins = plugin?.admins || []; - - if (users.includes(email) || admins.includes(email)) { - return plugin - } - - // TODO - better error handling - throw 'Not authorized' + .then(authorized => { + if (!authorized) + throw 'Not authorized' }) } diff --git a/server/datastores/s3.js b/server/datastores/s3.js index 69aee8c..29dfc09 100755 --- a/server/datastores/s3.js +++ b/server/datastores/s3.js @@ -5,8 +5,7 @@ const { GetObjectCommand, S3Client, HeadBucketCommand, CreateBucketCommand, - DeleteObjectCommand, - DeleteBucketCommand + DeleteObjectCommand } = require("@aws-sdk/client-s3"); const { fromUtf8 } = require("@aws-sdk/util-utf8-node"); @@ -23,10 +22,6 @@ const AdmZip = require("adm-zip"); const { Console } = require('console'); const CustomStream = require('./CustomStream'); -/** - * Class representing S3. - * @extends Datastore - */ module.exports = class S3Datastore extends Datastore { #state = { instance: undefined, diff --git a/ui/src/TabsHeader.js b/ui/src/TabsHeader.js index 98cce4a..a7abcf9 100644 --- a/ui/src/TabsHeader.js +++ b/ui/src/TabsHeader.js @@ -6,7 +6,7 @@ import { toast } from 'react-toastify' import Select from 'react-select/creatable'; export function TabsHeader({ - selectedPlugin, onSave, onBuild, onDownload, + selectedPlugin, onSave, onBuild, showPlaySettings, children }) { if (!selectedPlugin?.pluginId) @@ -16,20 +16,15 @@ export function TabsHeader({ selectedPluginType={selectedPlugin?.type} onSave={onSave} onBuild={onBuild} - onDownload={onDownload} showActions={!!selectedPlugin} showPlaySettings={showPlaySettings} pluginId={selectedPlugin?.pluginId} - // users={selectedPlugin?.users} - // admins={selectedPlugin?.admins} > {children} } -function Header({ - children, onSave, onBuild, showActions, onDownload, - showPlaySettings, pluginId }) { +function Header({ children, onSave, onBuild, showActions, showPlaySettings, pluginId }) { const [runtimeState, setRuntimeEnvironment] = useState(false); const [canShare, setCanSharePlugin] = useState(false); @@ -73,7 +68,6 @@ function Header({ - {runtimeState && } }

    @@ -213,9 +207,9 @@ function ShareButton(props) { @@ -248,16 +242,7 @@ function Release({ onBuild }) { tooltip="Release" className="navbar-item" onClick={() => onBuild(true)}> - - -} - -function Download({ onDownload }) { - return } diff --git a/ui/src/TabsManager.js b/ui/src/TabsManager.js index 615287d..a04285c 100644 --- a/ui/src/TabsManager.js +++ b/ui/src/TabsManager.js @@ -70,6 +70,7 @@ function TabsManager({ plugins, ...props }) { onNewPlugin={props.onNewPlugin} setFilename={props.onPluginNameChange} removePlugin={props.removePlugin} + onDownload={props.onDownload} enablePluginRenaming={props.enablePluginRenaming} /> {props.selectedPlugin && } - {props.selectedPlugin && } + {props.selectedPlugin &&
    + + +
    } } diff --git a/ui/src/index.css b/ui/src/index.css index a706e20..946b09d 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -73,6 +73,7 @@ h1 { [tooltip]::before, [tooltip]::after { text-transform: none; + right: -50%; font-size: 0.75em; line-height: 1; user-select: none;