From 75c4bf3f873412678dd22e731048b3fc3a5dfcd7 Mon Sep 17 00:00:00 2001 From: VPS 4 Builder Date: Tue, 26 May 2026 02:59:11 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20adicionar=20gerenciamento=20de=20cl?= =?UTF-8?q?=C3=ADnicas=20e=20tokens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dental-server-deploy.tar.gz | Bin 0 -> 120023 bytes dental-server/auth.js | 11 +- dental-server/database.js | 343 +++++----- dental-server/package-lock.json | 253 ++++---- dental-server/package.json | 6 +- dental-server/public/app.js | 982 +++++------------------------ dental-server/public/cadastro.html | 468 ++++++++++++++ dental-server/public/index.html | 244 ++----- dental-server/public/style.css | 466 +------------- dental-server/routes/auth.js | 74 ++- dental-server/routes/images.js | 190 ++---- dental-server/routes/system.js | 25 - dental-server/server.js | 138 +--- dental-server/yarn.lock | 164 +++-- 14 files changed, 1241 insertions(+), 2123 deletions(-) create mode 100644 dental-server-deploy.tar.gz create mode 100644 dental-server/public/cadastro.html diff --git a/dental-server-deploy.tar.gz b/dental-server-deploy.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..bb9c9e32a87bcec47cdec45dd852974fa75613fe GIT binary patch literal 120023 zcmV)8K*qlxiwFQh@DyqQ1MIzNb0bM|C|I9iHvb{IdR7Fh2oN_&mTG1|AW5Q#gT%q3 zdRk-xNRUYanP6suM@^SZBk4&dJ&kr&)9>x>Jk4r*^-Qvn+1UFqwzg~f%74;-VD%H$ zJ&w%CL;_^7(4}IlVBJL^Gfoc=4|fkgdYa*=gP5&ZmzuTw8Ij26 zq-Yv_{bX#ZxBq3Esa{%x zXKJnk&w8s`qMuY#opd+T%SM_?vstd!M1yLwlj^2>87QFaMO&cdQ9t}*jN22N(q6SQNq*^N3 z*NvWeWs`x~RR@ca_D-c@o`jhEfEpYB-ZiKPxps-yn2vGG&ip86?k&yMUg>O~l} za=%(5%j^Q+v>qK^z0m|nLWOHxvm=E{rM?gC)Eni^a;^FDS)|oyH(N*0!@=Q8#~N#q z;*-jg@)PAr?Mds&(UZcH9r$(hWE-9wK6&}XdeVOqd(wNNJu#j*Pt+#^_+`Vd?vpXR z)8N-R{Cfj`Q%@4`f9}Z-oobi0CsXN6G70oQ zozEsd(f<$eNhMObSRxxsrCQ0AwQPPZk$C;&)8{|T$G`szjoo&6uilJplxu}@GwxhF zbLeCC{!gZpi9GE8BuWXHbQ<};Og@+UwEsWE=iC4C%g6O!gb*UolrS_Ur8;RUjlFWI z-XJkjQfjS2g+5Ul}KPBj5i0eYBvhN)K+&YN~`b~7spREBj0}gr||#ZfBW^Hz<&hh`R9bail5me^z-ds{8Fqb z*=dw(#qv&}5^O^qJEv1y;BAZ@7>eHI!Wqa|!A5!@UUXDjo6-N0?P}yo>rB+QotuFc@7m!giS-rR(FdkIF)wvAx&xD6 zsh4UKKsIv1W@MRw==S7{W^WN}w_!RemFrdCR+$(f#d3RETXe7Ol(8LJ(6C8#g#fmW zOxM~Dwj4W&@3D)C`&>g@J()Y6a>%~A(}%H>)U&Y?oFP>->*HO9_X)*Rh1sxejv zq-zdG1I;l*>NncOy3&j_0EneRQ{FXLe{-y>#Jc@vr1yfAi(5)@yHakpVr4up=oDcj zd@6LaAutEJ?M%$M*@m4OR!!iBW{+W`YS&tK?C6)luCdob%;kys(JRJ|4OF04x)oLH zVhjz~yall`A^daz?66_n+K=>5|Bc6;?6wuMS1;cFH5_V6vD_?Bex*Tb9)tEMd9h#r zpa0+g`#%#>7b``nI=THVS+CS#&VUwVWLj|Oqn^~b{bw~2DO$QlhIC)xsi@JHs6?DR zKh`3MHpw`tnUrsUO<*nI@u89N(CxB&st#3gBtiL99FCb0lz~?!ba4BxJxVD2=Npzx z$4LQ|nNZXwm+HW@G#l&mO>2nb8pHq2c=R~Y@7I4q0SZK>TtrlR+?WPP&}RB)Sjko$ z$PKQUpe{@my?k|bh5uWo=`060QA7&LcK8rA3hyS-ZZQIgVT^|2po(PTgz50p9@oeo zkUyN@zarT<84Xhe#b-xicVjUF*g)bN*JH#il)pCGz?j!-g$kTmC8bHcqXf>d$0}4d zIfWt0^#}-oO`emc**({sc)3msAfE$gXG);#~@uJkqoJuTcOmv~GXV zY?X_T6Ng*C&<|vu3l${Z(}8ZBs_^WO)A4K~@f^>;7$68Nu+f93Nqou+v3DC5o}N=) z`d2?#B%+92ftzNHtVE_r8*3yTsowt9F?;Ne(T9y|WHrL2Ivcms8cC5TmF-|_p76Z_ z8+P$=;@sc<6)=PKS4yi+l8MCsB=kP@$B27PM|H-4T$BX5hXIwiJbl)Vb^H5keF*F7 zQlFj%-)@LulD(Z~1aEv{krSzGERl?*auE>rY>FkRcp{#7jLZDw)8}!1{QM(TO3l5G zd)t2{Q|UyG+W+L!*-!RgAL28){=me~xsN;U|8to{macy`nak(0dA$Emr?Q{!|31X0 z3w*mn&aNEt5_n!v>CriH~95mu{`HZ$J7Cc9I6%5hIkjz8+TQ6;f2(ya>Y>jBPbNdZJ{#QkPmr7Y(i;O`e95Fr@n$i0t`WdavrX)tF+l7(dKy^Q#)JO%I zM70d|zV4hx7naqLju<7^sUW*NyB6iB7Zwq_L;+klvcOI~@8)HJHBJA)QTke+tZh@C za6!z1zr3PIeX8z*r35Hx&h+o>5gjKSEkjAj`$auSlB>60p*OI4Kq$AaE=^c z>XoVs>K?~--12o;?G1H9ROCMyqO(HM#^v3)qg;~)jN4+a&}X+k?PmM-=Z&(^%~!q?#0Ta87NTtE zj;;>c0Ej39w`1LKBI6@+a6FCe@$EO3Zjv)NrL=2QR!V0HeV`jBZAKdy-u6226Ah?I zQ>iG$7I{WC8ucm#yG{1D6yOWQ;y{%jFD#KS7A!dRRBPeO#lUGIx9^cb?Ash@aiCVw zg@5~p|K-1vwiqQ{RB5bh3roI1x_IFdM22Y-6c5*h-VNYpBRxE-jZ;;cy4a3e_+Cui zM_3jb-E=OMO=cG4X+gtf0|ud6E^O&@7k1W|qdOtTI&i`YaPs`_Bb z9mo-OzT_hM5?}1zkqUf}I6(FamA29((H}1n_Fr;|)av`u#l>T>Q{2^9BK+G=*dL@s z3roI=SKxEoD3^e*g9in$aB9&Wxx zsxt(39viwInZPIj>R}fXoG`AYjE(r)e*obVA8E!169fVudW13Z_Qii>?Ui5tpKfgN zK7HI_|C>l9Qo{Z>mCK<1C!0&;Kgs_e;`2GxWxMN+Olm-}9f^GYIhq*#6Df#m=pvHDTG|K@FIQT1x^O zH{0{9^spWOP3y=4+OLn)L7lM zglkH6JaIq9t;cx58D?V?rp#t+$|_8!M0|#l)fC0gU;nd*pBH0|6!8261OgEm@COe# zfws#9XUs2^1q!qCal%%KM1`hCx1xCL#lb)$QhsxfT8-p{xHk$XUV1FnOK|KjH)nQU0_HI+zzY5N5EJaYDIt*DZjo9olNo(#MP;}Dh#T9fc?4w9Y?9s;>^^I z=+RM6=d7vd6~1{Jl#T&a1pqJs({M^A0k>|Gp|}e+OvG1QoIdb^L(WU$bJv0g_Y|;~ zfCjiHk8uGUExLG2#1kZi@Y`RY0CVv_2Y|Fr$HR^REVlpgIv{8+16$=%*RJqYL`E2& z5n9;nmdLYbY7aO(KyKpBNlfwCGh&XRggx#6^1;r72b96YVsXBCzy5O~ZcS~1+??2% zo;?)5JKX@ZSA~!6bQ`JT=|)m)q0oHJlM4No=dhTtXHO%tZIoDGG^SIiy(m<3j|I2N zJJ?UTEoS0GP$bJUq>2)hGP*tkNh34B=5LXxkHW zO?TkymfF!_>Z-L|0Ipo(L{k8{7coee4Y>WSY;VQhx`=3VV`gdmJJl3G zr$<%sA+x7wL9Ib4p7g|^o6U+l2E%;{=m3DyhE$HTfn44G4k#-_I9S-f{U2n5*BR)* z9>F}y$nsGZ0f$8KYtyr7!No57I6-Ps4G1>LBW_xHPvU9#)Ag|p?2F%Si@gC_?tnOY z${`Nj10k@zU%VKndz1Z@4SCgRAOL2j#%3W|xrOWmY#>X_k*FOog>xAl(Yq>8-&O~g zbmy_RGXKkHLkxEYf8V5qf!6-(z|lto*Z~56{E&97-gaC|JJx_iADHbNwISx%0q<*w z%@&%;>pd~#+U>s%HOmYZt)Ou-m-LtWbVIWjQ|$TZ0HJ?aqXDIFdt&IUS`|TJ5!2G~ z#%O?82W$X;sa!K!cJy8e_)!l1jN+ZjtPZ zr^g5luQf`Gp*06sG!pBRW!T(!fwgSli)E$+gx|*qnuf+Uo5ja!Y0MNmFkHq2*yDji zOU@F?;82alB9Uj$xS1PpY994wnAW-d#zt|`mE@6!Bv8dQdV@-?*_(r?VRP1v7Ne$b zP((6*d-Cb?@AwJm|AIaObLry_`=5L!m8JT>WFnEm7l~vhlS_Tl|9yzh<1;lSggN{$ zZB5;Eqh^k_k8V1VnNDRWDBB)O;yIY%ky#vDD1t{KJM{)BH=A{kkj(Dwzu=LNS}%SL zBRioPo*pv(Xx$B~w||9VKS{2UJzxY{nl+>xVzk-Z;)l)RBX#~NQZbcE{3<&&FJXo- zFJwDv>Lsi{ym0_YRJ^r>!xZ;Jw>QHnEP^}+^+q&(m_}=YY{xwck)46^r}p{ z6-Jqzm!SerW)Jyz79+@UOt>4aF?Wh!i< z9;-*xB=Crv1RkjaO$FXiS6SIWP&ji8EIzLhDCx#~12{{1lSm{Nr!tkcMv9I#cn&*C zK`CU3{XBT|TPDS}jH>Rah6XzZ^_?hb0b6Q4);&|8f%4oreft-GN91V*ch1^O2TI&4 z11gw+MDw%;k~y~Z6n6*8!fR}^WPPQSp6)zF)Om;flMmS>krj@8j~>m^49%Dz@JP|B zW{RMXvwg8i)EpDEV5Tm$sc=;S24pcjUBkt1%h-Nmpqme$uSb`|m%(Iv9S z0~{cAE-8CT6**qQya#$F<3y-0v7Z;gtuh7(4R3!3TnN=8FN&g)Z$gyv6NK^;R1f7A z@^c?E3YON7b_&hre!Wq`PK{H_%vW!kyA@i-Hv>e1A+qo=e0S(>&|VT9sXdGCY_IXH zdb9PC#;FAk$B}!b)zXQa^sL=f8ZVKaNY843jV~D*9WjlSc4&W^J{kRZdqU@QYuLG&@W$H#d-$_{1y#z`v%jv#CvTlO1Tu;N1SV( zx!sW)PN-Thl{d;o7Vr>nPM$pj#!R-fdiD%!pe@61{s#D;p^2&uPaiL=9K@|ReK)*; zx2a>%B5-Vym5$dSf<=s$-W0Hjq@{7Ec)1E5lU$qO}g_;syusH&rZ z-c@@lf5|L!ZEk^U2QO-|$n7r-m<6=WtBPCxXU~9}3|bfXrXhL`F0GhlLn*Wr(kiT1 z6jI(GwR#J9l5(@v6wqt8>PO`oltV5BH7tWM-c{q>EpRQHN&{mURtt?ovaKA78I@~? z@HU!U46DEk?G+lutwO_BqNN1i4mL}6r^BWe+|7z6@0l)=ZGKlE($*yY5vD(sn~ z@W6FYW4C#W0(Kc0b~ar$4}K(CtMAoC-(Cl_9Bs{Az=j5$Xhx6PPpi}p;8Fx3M!#~OQA=I+XX;Lc6< z+*!6PbKDtd_9^f#x^d!_FvY+?#CB~JeZy$@2-IZDTGueqZ7lGXH@gy_63}kiG9e<1 ztI^5cT$qH<7k9a!jnuwEL<=ngd2E*owcEcexWuugk61$`m0Dg!z7mlBilf3;k*{L> zBfh|+ux(7})8O9s6_6^Hl#N2W(jrNyChdIbF%+gd`zx{Oqp56m5pBcRV|ZTzGSn(p z6}MWk-Dp6sNB9shthz()RS3cQm4M0a7&gB4F2FeE-ad=RQ1W;Lv^@@-xOGDx@b~Iq zF!Mcnyi9%Xoa%NQsDM*0(IRMHF>o;UJ{&gXo?Hrq2m|9SxkI+~nE!m5i~!cEd-y#^n4M zXTC@@bQ6qN7|_vPNo-6|CLysAiD~31KcQgh3+siZLVU6bL1ZcvTY!a4kh{iCe`VOo z0BcgIU?u+hXl&?Rkfn~g`irNY?HxV-C++!W4and5ABG+uatCRj6&?!staX(1C#5x? znC6quBuY$G;mmt0TwIg7hXbz6?5hK2j?x?_3>{@|EEoi>g}V&npLt9YvuRdMKU)Fo z>zb$Vb1&=_$lLTt=kNG_6_y4ziNao70y%oHL||8m! zgKmz|;08_s*J2CEweXS>gRS!F$VAXU2f|#8E{0}Vlq=*>rqBURheMNtGdemQRc{K+ zS#IC7&O4gXJ$0F#l4f6ux$r4)=Bc3}iE{4-Io051gHpLbGN7TV+b+8$72{_26fl<7 zqc&)||0Me>6dsC|HWMBw{kmjfkXV=~!6AKyb~`O+~nS7SV|H)=@H2+IBk@{r+@j*U?6WB=HYyiV~^u|D0WV2NXCD{Jyt8dUOlSLnw z4GKsUuXERym)+BB4Rw?(0>k3k48==22(a;YfJ50fDP;2bKtS*`CO$o&X5E)7y$7q#o+XxsWIiEdw{2%}R zmlPp$>|^%+PiM3FgctuQh5J8~OnuV-eUK0HAL}&<1ZIKm&QK8NYhShRAS;X8vd{!vkv68f77@l@bXVi+G;i9PSC}lQrYL&VoQqc{6KQzJ8;uO}7&fo8owC{ARA= zUFI$&l*@;eUn4*K8Efn9v9u+aHbJOYnO7Uz=iad5=*18{rf4!<=`HZw4a^g&9YejC zs}@@d1ngldt#+-9`4F(>TDwvag&6YAD&faqS~6IqJ3z1;xt+OHL!OE7DHF=Ngxi|C zW$&S0^Wvi%2iN_c0OqQbhd`KCJvtdfolv*{!lI&e5(ogW(C5Ov=m=eoC*JxVPOhYZ zQCE7CN>EY2F-_<6$QTbh7(h>Im%TtoF8}IDqd%fvS6VN8vRO|> z@DU9TXyPuuqaeNh>dq?GGrNxIpPdCLZk_wV|6wug#4#U+UrgS$55EzVwmSwLX*B^U%xOaO!*3~wLQ>}dW?PCu_hI*_@aP-O_U$HKp- z<`~n!cuop_8$XXq5ovEeY8hK+ax0tyCe6v6L`^>(cleYQyGH8vLZg5mkYvf?AR zIngw`T8q<~tT0L_Qc)1?5_iNgt7*pRc<99!(+o7W#c88His2-?V50G7DKG^%qU`6g zrwCe%$Kwlb@VvWY9n0kcA@iglnw05Xa64I8BFRLcGQxxg&I^V2F)vHVLxeC7+m{56 zdV%y7-KHl*Gaf=ulsI;W>@kf&@|hgmH9Xi}Fj5^yBY(6yUq5cBIuUb^$IN{jDs{QHr=IG- z(;ToNuvmz)h{gu(#cG@&@j@13cP>0C9)755HK-TCdqf&J+tt1nT}T>R$D#MstsWrZD%HwLV`!&Ffd*W3ciKotb+#iVf!H%!aBYPZ*5y~b`t z)~@@aq7QX|dD3N*FUHzf<4+R|C^KVBPTniBt0sp3!SI{D_@?Z#c=Cu}Hph;b0fPI&a~gd9lPuU6oDoT(O2Wq=*L<JYDSw&tICbpnAn@6m+E)Ex)nW`|SEnbAE3TZi|B zx*G%`5Zn{+?9?)^*t4)$v>RIYG-NMFLcp&Xz3AdpLeSItNLSITp;-6m04+Is_f0P9 zuW?E>@6GSm9&Bscs4&pc<4|twTs-D+OH06Q--J>QfCUTI#7V_J%5=^fle@2}JreL> z-aMib)8?Z=%o!DrTVdKE@prs(+|mXbJ^I4dN0@zhYs%K3g;TesI`=!b%Hb(FIlpFQZ zfEak{{dh{Xbll^9@J@Dw4$d|UPlab2+jTBMbejomxVLi;=nZ5~sT7)oItP4*(YytE zNLuW7z~o3E_&1Mw+|zurkBg8VDsw8&x8S`s#6q_+!&cHe>0A}Du!79O88$@^EOf=9 zXfiRY>^-!`e!a1;*+5%?BH4)&u8cu~8#(R@~EzhZzEUrF_b9Zy<}o5>!hb`aV=$E}?A5jAYfN_gdDPhhcGu&U|Vy zSz{hni0c6T>*oWXq^%3?-iPQ_QGZZU4gk-txlDB=7AVTUEY2wF#$St005PfX8DhkZJSZEdtC1pw*cU%I^YKQYwKD1)+6hm8bq444H z>gc`{#M1(4;g?zdQ062$_Apgq4Se?eg4Hk&_VjdH^pmn3Bg^s3{5}R=DXE zWQrn+;=BxWE%Yr>FX;;@)JnV*B|4w^u8f!nX-6RYOLHO?oZQ1CiW9%K+%A}In*xF!{%l8CZ z*^FmmE1U2`v6TyY|4!H%6f-BVRH9EG1VPz=-iM^3Uxvt~#&vY}xd@+%&u!YqBS2?n zd~p}+BLFcIrRkg>h|$bpXn+xvSWJF7)0~Uo_8c*sbug6$ny%|~86VkfBD|ex%21D( zyBHSUz)W4K7)W;}(Wmo)#l>>CtVwR3|%yMq!PJR02S`fQ++@M-G$CXf}b*174)zoO-Pxo)ZH4wUN#a)vY;kAscyQL(X#!Vn(wT1S9=L9*-xqIPNtjf<+OZ% zMP2PAy2)NjOZPKswv+4Tdn?*%pHB6?f&O{GwVKDEnh%!oXh#6!u}WB|W*$pkBTGMXg*X3g*R}%ghT?QlU(vU#&*F**XFU92~wxOX0|z2`MMi zB2RI-IVXzP(ubNkc9@#h9BHoap9-|^cShGTQOgqrhBd-Y4r(k=Z!>C0X+%o}N_J|J z;q5n=VvbsJvJ?r?&JE^{hQlVCsIEtt~XrapNENTHCzj#4W$*8-8{ zU6SQRm#1luX<|)$jt+d@NkW7FqtP=u$w}u3)=JZmYT`&kg_-M@sg50Uh6K*a z2a)1h{0EV*K*Xo+eA=~&(Hq$l5zalfQO0uxF^avwl(+FSoBb$mDaGwdx!H=95%bVi z4FgKYhdus2n3|G3mLit12YwNjKMO@`_HHtvJ$pVQrnOgKG6*z#hw~`J+=-+21hA&w z`(U_s_EZjR&z`vPtHnV_l%X}(O)&o zd(l1By655r9jx27S8p`#4D_z|R2_|crF5gGT?cx!otuFc@7lh`ehQ6Qz$a+%R4Acb z{16X##3A|74?g9OeXmb|{vRlSTlKkSf4!6br!(*}p#K5M=M(+^5TChlq8x2dHl$g{ zQ1`P_RzO-}4C=q1&ZjslaohBySt$CCbedd?x?uDCSsGK{U}6f*pvg@noxoh=B~J}> zx9UgY(MdFyf#cg!yN-sLqfWJJf)>Ibx9&$$uV`Z~4n%z-4Ji<+;`nTF4r&tWwRnfJ zq$sgKOvd~mDvFXwJJuEPwLun|hlW6o*G+|PZeO7T%9tbibudNno+?ZSBR97&TN1r2n1IN_g29 z=h9w`6c`O1BCsSN(Rc2Cp!Q2VH=(yPu!uwmX>@nhrjMIGB}J%;;**j=`uK^`rqTcb ztGHFz1E~f;J0B)+HvgBTKRExFN+vP?TPC0TbpQ83K94UsBhbEo`-iW8NfiQwA}nDD zOKE{3@^HdmqbUFo8hmY?@iDrxKb8ooV2u z%R;73SL2T}+w^~B2u-xY zQiT~97lhB0-ZhWO-cEB3kNQmw?SDPAiI{NA+V%h>9ivBLC>W$uUO_)fO)0!bvqQl7 z0LQR8I5F|+Mjx(4)Rlg=pUma^nM^;kn#*Xp?&|7FPt#UXd7$H66d)4al@%?K>8-44 z$!@x{+F1b!0VDuzMNLLPOrRoymhPoM%vb@DBiGA<_>l&YBMG(hS`I{!42U18UQ%1> z=RqvVf+&*KpnN34gftX%D5j&RHqbpUa}n9c!tj4m;WqP_$ik2T7vz3KuhFE&2vbg;y9@uzNPv z$B4hwomgBwJw~+r_P>53bBRnLNTP_ED)Hx^$D#oqQWST2Mr0SEIaatZ>|o#knyATR zD&b#L+3B(C@^l)=PIZC&6kA16ioyKhLTTzs6OLo!Zw$0{bfqq+Gg z8Z5EzjP;MCFuEM9-|g>)9dlsXOT@VSSG?neZ$LOKkRj4>vN={Q)MB}kh&Cit^~x7G zl8KXAyH+e9R|=AaqYq9gpX%0#WaAuxJVC|7qQ{6C5s~AMX821+Xm&q2x8Eb2lVATc zc^N(r9m5^^pJXnP6!ssfTn6<&*-Yk>{^x^yxTDUgYK`P*=k&pR(tdRZATAOb=4eMC zmKWI38l#hWRCtZhq{sGnMDwKfHZXrG^~CsMVPu?O7ft;Q z#+FVd$TRpSmEnJ0pc8EpZ3MYD&O97Om?t`8e48v3lY-`bBr1E}(oj26n#9Gq^^R^R z)Psm`SQY|lecbQk-GNC1Zc#Uuw*tI01)Dz72D*VzqN^;|lEPILxS@K-0?N)}%A%VX zZ#52&Pfi^JdL^H|d`ZRzjl!$-Jog`|AMrI#J#17*i+aveUPEhSvhkp+(ZukWQ@QYO zmPaVwdTXjUP+==nAchooU{YL0DqA0&IWQcmc zYjfKzwfY{K=!-oQBaM2CWd>F z!lBD+ArLU?E)vR0`5->I@!Zvn^AU?ddN1hect8Eibnigk)axBBAD|nSbM=0KyWe^+ z#W?ph5Ng&7Q?54(c73Ffj9t5Ue~6 z@N(w}sjY`0!j#tbK1!reO}e~1WQImDM77=pN9NB5p@UGXTq&0dC1u{!{6uqFr{iJA zQ1yWym9O+3Vol!aj(fJbbxuo|o^#6ixU@+qaIZO%EIs>pM{EFUgF?D z6#!$!ywa>9jf1l&Q%E*rjCG(;fE}~8Oy(N|J)oa#&%|V|fkxjR&mzgv{6v7NA598? zGe<&84GC4IlM}qW(Qp`HO)oPMlKnwSy`f)gy=ZFjZFT+l4qA_!_(6;rLxu;kW}FmF zl17g%-F4`H7~&YDjqX{iQ%rmpQu$p|O2gz}eGq}9=vEaw@m_oTF4(^h1>o!E!JqBA zH4@l{9^mQ9N$=T8y06)aO7Hm+y9FrZ$mH&lB1oUAhM^5?sS)u=>PNK;gVCuf_rxEG zefS^>KF}<;#d|L>i(jJ9Kp{Z?(Mjg^|H3RUFk9mU*k+KsC!&cU4f3nOa9N)G`+ofT zUut4nXZ}p+Su~$M?(qM}WOE6j|IMV+)c!A(OMKG*eu&TKxWu$7L#xCi|Zgnwzdsf2FkQggeur>INRwTJ=bzMehLd`j0tW*m9H$8;OlN zBo>R&p8xG1e)UhTFCpPBb@=9U^6Xi$-cU$1 z<956F>{%p62Yc+AM<2`P!6RV*x*#kKNEEiQg?h~Aq-l1~H78!KFN(_C1r4hf#t!Ti zz01RnzS?hzLXr<67A*2STp|HIFEx3$f`vIPpx5=LoMnbjtU-Opqp7f&F@L#bd5k+{ zY(w=AHA?;dmROj@n-?p z#0pjM49)mhl1koxsa|YT7f;^*N?8A{dnmTGr_+milt^44&2VC+-BU{)dV`B+mRxJn zvlT0Bj^T*2Zoe799ulReJMPlWfL^2n-FCb&hx)K$*#@TYF}VR2OjT3~aUjOx)ODGn zJBN%rqM)lV+m%BR>XUkE@Unh)VG0yjwgKIW-2H>D2ZJO~!e~;nrqqN69_oQQg)2TI{!w;bV%^x2h1C2Y4bbDmFS5w6Egn{yr zBAR2Gk^>zKYXah~QypwvkbO)`v;8AUuK_JUc*0Io^I5v316C&Tz2fw z*&MatXoJVgq|r_1QrTo?5lu_5ynvaZ((J@4jgEyjB~so=%R7(JmIU9>@YahuqRT{fIQAzgOAQzJ;?u09r}Cpafkg!E|X6Q|DQxUPwhYQ$xrqlALN6y;Gg~< zQZ3Zl!0ne5|CuvY3;<^QKNC2}Z-3r^-3bKaiyiCsw=t@*uyx?&zl?r9@;_NbTJc5O zJ)a8v)Ps`j(2Di6n9JlKq4+}fUj0Xtiv&=<>w6wE%-H1!ssQ=;!VU6QLUILVDgDBx zDg9bRO7aCzYB+W%LB(+nQ!y=O;IG9<4qp3?YAA3i|CXpIlZ9^oD%MbdL1~s-o;*?m z8mR85hBjDpxddS<&(10ILPwfSnYLu^xnRd}Y+i|zf!X1<)S?v}r_URbJ#RKE%UiA1 zPE&qIq7}_qu!&+&Yjw8|m*kRx-r**o=2-X%(YIbJ>Y_9&+3y5>PNWm>b14Cp;C-11 zeGKQx9oW4xFjIQ?`Cd19_$c82G+Ja$KHEv`1RY=5Q7H+A|DDIALj)Gwq8KzFr-F7y zXb{0s2uDpL0|@rWaGhvK)Ij|gH(%F=0VXNHMgh!heW(wpowN&x;du`Zsn}>1qcoe1 zl2`%QG-9X7@-a>!jO7E-m^llnzDl0?5gTW~?l1D~*MI-**M9>45iSkFlRqVdeEap+ z{JAS068H1%U;Hv4C`#>cfy-@2Jokh=Upqc&Z=#Yb<@r13^4`Q)nUqK54;J@CzKA>? z!Maa06UxiWRVO5Rx2vgQy;nQPTS^1PKnQf%R0rfU{f%J5WbjI&XJDGXEwscF|4XoR z+0h9AaYU1qBP@9YEY70Tgrx8YHsLBicsK`NeF4}RY=(bpfBWnI9i2-8!nj`&g8l7^ z2R&Ilpj+gAVhcLBi#CwlaOm2Tazzsbntg}1IkM4N=7^cIAE_`)c5qPd7T$8uL4}@d{$?T*A%S@umxo3g@uEA>Rhb@S zq|IZ6e2B;*$QFob3R5GBz%s{rCY zqhk)kFhKWonH-6~A6dx(yv%5z1r;3vc&@Q{~);lN=Z^Q_v(OXOHia_;@c zt8M%hmcaZNTbM|-jirSF9DWaEBzGSP6Gq?8`1PMqsX`%`u=c=rZZa(l?rCJT-|@g? zmEA+&T_VyTEn(K%k|5`)!kC`Gz?!S8^U6g8ek#(LS}K&L3J95?ocIKgDKG# zDFh_h4N-Xy4f%&qe_!i>Ageul28_<%q4H>~l7Y}c;~Sg5Dh@ExEvw`gVWIhy)MD@@ zZ+J)5p>rCMGg#X@t66BW+u!$eI5%ljVLXv<|8?MCh~(q9$Lz$WVl76PqEMjNkZLMnv?j=11PlJTVc4p@exQ%&tb zPwf%xTbRiYY56hxHKlCP&8|fjc*%t+MYmu}LYwffT%fB0%YM|kp)7 zkpHd!3+CuwB4&*1=#Q1S_4FMG+~_=TXq|4#&jqw+?%$OIsXg10oDO@a&eG6m-} zR11XIz7I7;^5kf63KJTrES$CiaYr*|n`9~5-_UgTM38U&X>ChmC&NrN2bGvDRK^%^ z2%M;5LNL!~&nkFfj7s+7DO@YNYo>`JW5m_>!?8)V_f%JNJfYC%#o0XoITqIdFsu}I z>S`rscFY@*!=%uASb76(Mgj0rwoIl8dG`6P7=0+n((;efp+4%aO(M+UJ zL&enXuUksFIf0-I7lk&EpZWJV+GU)Zfn+GK!dwJ@Vvfi(Rz~7rCW@3a464A2^zgeD zzI${kIrDIfv@|}MEL$}V-9c*@R1nc}LwphxYJU0ONUKqAuUC}jR=wUT*EYpMqy5?% zh!m<}08iV8B_zz^?CIUtqP#B3@gR4@7Vq6aG0d&`G^o$TfEGheLB_U9d0qL+ZeW{z z64S|=kp8PG#U8E{i=6AIwOu8DxKl3u2;bMo81fH`&_;wJLTC_@!Es`VPKcO3Me*GA z>7T;IFc9DU62J=r(A{S#Wt&Wp!l}E$#KQy#-?-eXQH{ln4IdfLBaAtCm)N;Uvj@|% zaZG(GO&h2QcB zJeGxxg5BK0C|KXf2HS3Oexz5nZ!8#+v?p$|VE|Hdd^8kZsB%Mv*zsg6NGvFsc=_$c zeuLLDzd9&af%u@B1{DHH_l|ig9Yk@+B;}puo(MxJ>$>=jcTo2(Or-=B6gS;1%0#%L zBj#hIQCD@qWewy-O50nO06{>$ziQYL;UH1hJ#UFR^(?Y-CG*Ol9pVrT6C|q<$}I*x z19~PaR2Zozu;%n`35gJWxTSXVfeu@9xddk)|Lz0U|3R8<+=WUcLXR3)6p&(vDAS@8 zXJ0%$MwognQ~ZE-@50w(_tyUQ z*C-u%YIVlX!4d4ma3CZ?^$E6kgDf?DSQtB$A0Z89Kk(#x@d?C#a4$pV*+&8`d-GH6 ze|h}RR3ej1r4u>4|4--h$tNWHci4ZVa>=}~|48PtDVqN! z^~wI@gM1!e#2P}v!-9t!t31B>Mf`^DH3e^dsGZHD3`iAflhD&HnN-2VeYCsKhHH@- zms$}Psf}6vlW2&?z#`|Rg68b(>4b^7@3CuN;Csr<2-u^^yMFe3!a|faAl(nMC$6bB z`#I|doEDgFRSl5%H4VAwLk+wX$0o|9dV@rnowKYT5YX{d^HDW;+s~Oy`F20*m#26M znmr>LVB{AcXRtTC96`~56!>3mUGVtk5=jD9eEVnrNWzSvVBLZ@I{55!5^h5zdGJIc zDbcRbLN*A^rNJ7g^E<$uhEgmy3w&2|ZR2NH#zw8EeFG$5*mVX%;lnP;X%oU?1*$Te z6|U7;3$JZCymBNhM#lG31LnM=)JnI1T`QIgp|kdDVxWMmBZ*{0gGgx_Of^B|1P_Gy z`ML>WxbZq2sb>WNKO_qVOhH0Jl=BBXV=EL2M)7R78iAr%YSJKqJ@jl6*l*7)fxn+- znZRC$7$*>cJNGq8z*)b$T>{j)uPFi)oy=>J^{*1lv&9zsE}H_xhr7%QA~_GKXu=Ju znk4U#aYK})H<@{Ntip(?!S7)y8hMwE?CQ{O@rszQv)ERj6Mpr_v}@Nl?Cf)KVoe|( zQ|Zh)U><3hR{tHy zQ$Q=s>@BzK6SrtPmy&xdo~v+%o}tgHTx*8VD@A&d3iR|gK>fQ|+_%8i_I-L#Z z|1+7)r~JPk&Hq z#?V2?5@}wb*ihtN@GE6MdyGUd=~wS?9VXX0ON;zClT9(jq37D*kB7muIxK%#1XI%b z-!jpx-lkq=WawpD!gN)xk}{&OOlX1h)grfYy-D^8m3l*IqFa}zwY*zpc@KT%obog? zZ^CN3ga`m#e>TOP>O1$Lx~HOYa=epj%g2YT(=`9zgKa3$R&jFy+%eO42qP_eff4aE z54I@av#5qWH0y=~cI(svDn>%2 zLFE7sL3Z`@_$Cx-`L$jF>K)XQQKS%vXf5%ia>BYCv#(&}GJY=`kCgNi9oj($M2A+T z48uhIk)?HYTcz9s)IcqUuuNU1H$;yz388yyx1%(w1dviW!*Jk7C&pkz-w8!WzfrBSq7|L&m*Y=tk{f%o3q? z++N90G~R3gD~|VlKxfN)<%+Tii&L-ptv}<>NUKg3;7~n+)`4o73qM(KS%`%fk&Q~b zxh1vmBEm5J@vUz*c)cjhHXb9cmXw`(8C{o2Uwxxd7g-kn6CKk;7C=CY`}>BvmtK*n zejkqyPI$fXeYllM#>rC=IJqmLw5j|Ma&Ltgk0clx$@T1Bw?=sL`LxhQ_8S*o*LrTl z)J+S$u9@r`yl7$v>>8P*LFu@AnFd{|G|eS5a&p@i9%EFN_6V!@niEa5stS<32;Qf< zk!2an2B$j+;~=kNuvPhXie2bVLG1|uh3=oRJhwW%E|o zF#P-C13*0oS%B!I7hPRM&VQt4EI%g-`faq@4Z3*TZ1eF=Wioi;pGv{;V4{CM%aSBe zGQ_bUP-2#qOi0m5|E*xD37>_)d!P40;GJh!Gw~ucu6wZ9^b=+hj6Z)!aK?!?rUjzp z-W}89Q%-d0d5N%YzLvWYaWe@CjhgvLx9o3VqXNclA-4|X)paL$e3L=h^{%bSJa0tC z0yi%LbMF=wUT!wuKumwC1o3-!3+L{DomwhBb?eg#(}IUmukR*CiDc!fx4)(G;Tq5J z$y~*kc($q~FSFhf(VQ-;^T(+y2K@y&n#Cfylftl`SfU6w;R}w5#mkhS5r_cIp*h7^ za{?@`ZJ$ZS=~?8AR+m#zgpa3a4pFMI30~weB}3S{cpd9pAmm3B{f2HZHeU;2^e|s4D2f-!SV5(eE^xi zM&7&7y8T77RW3eGuDfMo1VhXnMHgZX&a!wmk$4_i2NDV^x#mNO4fGIZPm}SK_uNnW z=-Y(%)_+{#+r;Bu_({X=?}sV({SCOkFZSAOoW6sbGpOn#rAhxZ8J_9!%>(6%4SM(K z2HcxWzJLzpG}!UUSfA~nAfH%_ynN431lp*UHyec(a2cdYCB7!r zyrYmn!!X8TA^BR3bZq;78-|STRFrGIl~%b5>#A%N+Lab5wi^u?-w{3p z9-_JvStJTZlTcok4Z|$0dnI86u(~e;i6dTyF79`+kN`k5^ot5t<;CxOQ8{|wdsSI( z1>nef?vkal*~P$Gc^Mk$QsF~4J40U|XudbT2Y%FGIUz`^TswrJMU#u}QYG=doG3!V zphfQ;CXEQb2}>X$BdKq+B9{b$249GL5)@QoFbO&he|Cu}1w^ii$=C0M!dRhPD=7!C zYS%{sPEr14(f?XZhD0Z(6(~v9HR`Q#;58p2hRPCS4V3ULeN_HMclP4Pwe{+?Vx@ii zi_)CV6CQ|lp@oZJiv=b2nA+v_$AiJmXA7jzL^*ssa9%`$;g3Jr-aL-aH2$Ameaxqi z+5CShl}jgr{C^^y_{9Hzh|k>lf6n(y3a*LZ)|y`(H$^&-g@^YZq9i5wIC?L-JWB7wJB0>tL&-H+q99S=)v5arWsJ&=#ZNK@gHviPMA-O_ z!SzY}_)t6$TjTUvm>e=UB2u!<5pmRxHc*LbIQof7GwiUme<1C&@sg#!Mlg}lrRLf1 zQ$vBzC~B1&^&Jl_e8LL@21_x>JK=uLpX8qJx7@=P@hLoC!AzZ zc-p8pl=5Z`5iM+Jq@ipm0ED6foUj#xB=LWM+G9zM@UvOYot&r(|c6(MXt1K z1&q18S77G{CpNEce?I3^Bo>prf!s_g)QyWB`+<{3O#HYD6_ zM>NTr8pm-D4Vx?=!Yu4>0YVTq7*+tf`7tTfO2jup_K4MWN4>y@xUleoH^)a+-iXRW zc?r7{N*5}~jxY+!X=b5RqKjFr%_LL{3rh>4?VtX1VWCLJ;Z_kr7g={$043ugn(&=y z3kw1`7h}!3fssZQ0nR%O1rSRiwlm~Oeh|hlBFceMY`6IHcC%dDbk}l8UNw}s3zbT} zh^Kn50yEu?ccIA8a7bivU?Cg2}4uE^t@3&Ko0xuzhCulUD?G4n?V zJheZ5bih&05gCH4%1l(Cg}={;5XPi^%qS5y-nk&hUEJyvGzqQteMXoGj`3s0mWjd6 z1yw?YmMQo#S@!#k971aQm~ldkaW1G3E~ZYw1-J0`83h93`Nxb2fuYU?A!cePrr<@e z&i5WYgmUJiM-DO2`JqO*UTPL%xHZ0)Xz||47gza{6*}eHU(`xvR9^+}43z5p^gS%_ zhdpKqlDXoS>EHZ{Uv;FR21pyi`kjn1;EZc59>WyqG+XnKISE1$2X7J3+iHz=bNdVG ziYDoK!nOvr$)H#fnYK01YYTNxT{{xGFU0CcCbox!4fG}TNb;0Y6KYXZP)Yk5cZxSY zx>Il;`5+deTdmiG_E9KVef#fe8AlOT(Z5Tc%3I{#JK_$U6^kNxyN`NR#30{qSzKkd zw=R>a<2zQ!ZQ}zjku08loXaGS@PTXOyFc(2X!0Uq+5-6+_<*b9U0-~}TZoSDeRdDK ztMS%Fy0n?OV_3ZpxE$U^(Z{(Q-uNE49MY|rw;a;-*tF%4>V1IacyIp1Wr=cY7(={V z;-U99%C$mezNUws!##I%n`wXOGgu_(jNEm`mbOLLi9 zYu6(H3#*xlCs*RBkhd^zTg(%BPUhojxfJfoE3M<0=Nc=r3b63CLy35bzDvh*5}e?j zjnRYXt3*7V@=wZU{_48P$Crm@`P;WHNHqfltspcFvqypdqn9w}guh2)G-R(<=o`^s zL~54SKVk_k?d6{g)ltX1UdD$pfJp4*W+dZ^ga%1yGU-7W5YILTms$_si98*ab^`2_ zbrXky40X(2N%NqF^|BoE{fx2Zy}CKy zYHnUxT32hTrCj1LvD4mt`7#v2*PEqSI#wL0nBVgrnBD`%sq4fBdZ)`VHn2bxP%)ZP zIEFzD#I6Vw8>;78tfyNXuabdPM!5Agr+dmf$;I>j_oFH8!m}^t-F1ekzX|pfMiS?>W)K4 z8HTWHf**sZp*sr%&owI}9ik)e9uNsN!zP7QO zJW#gFyL)?=h0RwtTG{6X$}q=`^v#yVG1TA zsh$B(=Vk{jV`+OS(_5IxCdC4iSUv3#&<%{uEt^exu zeC0A#cZT(qHwXJ${hh-5fp?5L3_V6Ch1LEzAIEd<9#B?09ZS2MdV0x>$GgCnH4C6+ zUt+YSyJ2e>+Ujyy-(5a^Q$9V+jd~@cv+lgfm(F%t+ty(sv9tW8>>>L)H)0UbVruMe4$Cu4gy0lYV-n@C+9`GVX%8gD#HlS*xDR<533A2xe$3XQ8_!W!pF zr>(t0dOU1rPhWRaD|_kldd7B64y_xjHK>fnmnY@bOYCqiPPVJJs#m@t{v-|1Jm-ogSWsM)9_AQTy|f*eK-ybr}Iq72O_`_ ziRuvfq~v{;KDRM=Gw!r1uQ$`LQtEPTuUHsp%IVpgi>!HlvUQ^t+p9N+2bbN7eQEBg zTdyvb?d2jM!(&he$(H8Fp(#81I?!lndY;RUlm95og`@me;yRr{6wy7GYCuXivYYta3O0u%v zRaWeDJ#jVa>5Vrxjny~HuV25a4yyZ?>HI~#-dP>>UJa^kWqadZr0WhmU%3zc0gd{z%kDU2g+V~ce$U;lq)Oc>Z|O?Sl!azB$ca&@irhcUcteA zs==2V&6x_K#6v+8=m3Egcod_p-wn2+zI>t7hF4pLeQ2&;ZM? z$+OkH--2*{iGBRMo0#?eZ+J z-RtliXeX+}xSl^s1{DT)Nrsm^F@TJw4xf5@u6L#8$$K5MmZjdr`U4f@Xk-4Iyxcln zN9p%{`TCvWUSZe1+Si8TT)TH|mo_#ImY<*OX@?iv1+$kfrQ3VM)#TaM)s1m7u3r@{ z4|h(lSB%=A`uutAqS4*X6|b*HZMA|QEbZBR?RX&x5TeQUVh2N5z|Rw~uk{l@6k-u{U- zQaX+@GR`jFT&(Z(pAWCia%v_2$~TPDY&y;QRy6 zd1w=03jfAv8+U^-yPF#JFH-yc&GE)4J#H>9C+u8Dt-q>N&Ar}u=WtJ}4NHgD<7~ZM zNG^BR`a@*AuPkUM_!HOBRNm1A9Ajqh7T0&Y8Y7=v+5Suj`|&#P zI|=6+&W2HAw12g$?p&1z<)-rL^st@Xxq1+p>EL-sL!Hl54n7cCZrc=8?k}byycz`ed9)VQpHMkcT}i1v-)*^{p>8KU8iqawabH>!{u`&pIklLT`r%EmFBDU z?bKEFGIMpc+bC>p+S=Z$gX`7Z^~1Weu{pmDL1(0PKpu&8S)Abk)1yCVS4?Ydep>_D z@7q)5+3W*(DBiAE;l3TCZQZ@Pn}f5H;+ZxcZNGk1)121D^|rcOuqxZh#N}wXomw%i z#Pi10{+Tk!HqM_fUvH+b&PT)Z%jf%6cYTjb z^BxS9f489Qn~nSQNVLF6bxxm(tCqyXYl+*!+nve!V{5?Qru;WGebshieZ6mTelC6Q zdqr%Qvub{g>${+OXWZ|1K2owM53U`Ku4DtD@x?pW{X zwbf0vrPg1cG?f$ka;I~eEzAL}??#Nw{dgq_$HQTscpp-NEeojQ_gMOV0+@5t-%oXS zyX)CnZKbK|XV&S3k$8QYFBls&+vvV&bjSPk(pEw{EF7%cud*k5*M;`DX5}4wt69vI zU+44PLZYcVZ(7goO#a+-R;pLuJvlLLmsTY23w@V_gdf=X_kllaTdV;46{Bt5 za+j->;m|r=9bT|9zMefQPK&iA)Vt@E?IT*(w16R)o%)CO|<82H0YzZ9fg$N1@%o%2^t~ zd{#eKS66Xfj&}C0eSk24hS@mnS>3Q^^>OXR9~qZO2}1WL_WI%bV8oTrFQMs?|YxqmbT6Hd6KM@-V-h$Q7>p zO|72IrV~4g(ok~C`!~u-dSH)lt}>gze7)W>-(TJIM^x@1UwuV>^pa%btM}Q+!gw>x6X($S=e7RJF@2F&s_ny`m99?O&_fAxZN19cp${x_rE-A0A$S@z7&(T zwJ{ChZHLy?VMLd)f$q@Z0NTV;KFonl-`5S@KGk^N8NY$-1#LDLPo@KRWa?E6my=b> z`k{cckLflC090O)-;Rxu&Y+Wg7@0}LeeH4kZv@Q>I!3UA1}*B7_pv+bSZWspf$I_8 zuA4J2;2x+k@ppjx;Qwdu&6XU+mNns5DXXjQmPIj7W_<$)0UAJvN#bJv#yp4_1VR`4 z*;`UfipYpeQV~_>oZq(XApnt%^trQ^FJJDTzrFIf3pXZtR-^R9L~2ksF&9z@UhC7e zY`1;X?K18(i%zS+q6A{5nC;|X`Rl}5C(If!A&VOP$S?rIwHcbO-73kag8S8X z;r-6S2OFwq8+7gVd`J0cgdi{?o^N$%!9j#IFm-#{lx1K`{1xpTN+k56}NWMw{=0Umfi9vU72E?=BNV zocVwLq}JpMCjHT)Ud{Z24;`t(I0?jybxI~YsKKTi{PgC&sRy|y$ zUNF1-9b3`>NCBY@C+&!<$gr8qj!YW_h2b^n*6>75NSejGmh9Cv!YFj%Z!uCTgn`w` zHF@Iq(pGH~O^xy3B;zx%WIT80aNn}af0-zMZK&>f+411Sxp#K|%XD~%{PfbK^w00! zB>v<%m-2{@KN-7itd`K(`JIDtH{GkJ#I@(%kvrrP25h^1Gn>QBikSr!eqMMDkzxR_ z11$QP4@2&%%!GC=t8qduC<^G+px_Rg>|}@47xQz^=JV>6T}PBRGvcM==C!}W% z#hfZ30aKWOMobxXh-ihNQ!tA|zbjjGw=MCNh0&R%x;o&b?9gmo)i-PjFi;^6R-iO(MH?##lyF^!aB_*8NeeN3j=+xE_p+ zU^!xuwbL24;7hLv_e+m5nzEan_S7OMhk;L3ayK)ql+gG!BP_8d7v=ouH&Jhd>(*B& z2jvDYos@cvl#GxuvLQ-Nr?CW938A!?A&H1sAV~m@+JW=ST?lH!a+}KtjM>$F+)y9 zmT86L$^p+#*v+&I7J!bCXs`+q(h^vkOBc#_X!5zF{@<6W>-Wo0h>@|vHxK)4X^wtD zK^M_4+PR!u*W)ARE>9jN2z@XS8}22?62!yGDm6Du_`^~M4$KlbIIcCLaB4OVmzK>Em@l%R|Ij`%^=f7#Du075^BucedDzM_vu>8}cT zJii+6h#8`anPgu&tECJGOPm}gX@piFAVVu_mcW}Wz6BQ9%0?#Pe2SbVW-mh4X(|j& zR|&j|WNSP-B|$cA(xf{RBjL2N z^ls^Q`ySg=`$?;Q(S>O2;5Io3I}fbT-&I-sZd2y7^)L1%ZC!-lsOh=|a+NxwYF*x_6c z^#3%PzuJ*SZ`f~sAJo?^**E78SGgk$me;#*n-}dGVVWdfZsDPHl-w+MeJzTCph(t! z&*C_F9_~-P$ysMV4iu&w168@iuVQzd_&C?LsTiIL4BDx^r11S$y}2hNd}nblA3Zd2 znA;c1#^)CvziaDp%L86j`Euzx{NTUVrb16osV%r<>>8vv-t1nG>rJY$Qc1ZkP39g%q`N zr>pW)E}nZw=(ij^BSz$fC^*Y`bs&%pUjtF@)G~Eo{P-yGhfY79VttG=!TEo4 zO?RcDFLAeP$`DMpr+lL=rbz~H1nq@0#_;L2v69>c-w3O3;eCKdhBZ9@!l8Ag<2(P~ zC2spO-V^XnXn%Od73d$XUYSrvc;dE+V1u>783#a%d1~eEN`RMaVYTzEwmEVtM06Yy z6`Q!|b$D+I_IxqhtIRZ?ZYFL<%_o!+ZLjaWzCmtYlzuAD%X3UF4&XjAb}X+5YPbmx(POTkq@hW&(a$}PFZAN zSTN<(PA?RWZW^)f|GE5x*Yw9vl2h-7@+$X==Jn{|D(lyP!p)qYYxY(Y?G4nU@m!S( z35@d9j^Ild$jK>MsqDNzFUx1HWXj&R4R`H#ESw^XGTE_5L>fv(3GMl} zC4AiYgkQ{zd`s;5+0X#r3I2=hs{&rmu7kvgWj@m;FeA${i4=%A2pHv)t8m*KImK}8 zdauhRx2q2h10L!=mmqLZ5F9;!ac0mVD7Vte+*uij#Xu1dae-#1IAU7m`v!?omi{{>DX%4)zw~KVl$^5hLd%WeYpisp1<)?eMh+Mg#LSzX5P}2|2#V? zEXwDkDEOS!+T+zwM@v#TAc!}rSG zpNnOSOLJ==+kVLaaMzyjL82_Q+bjeL1r%#8a!yr5^imD`g_rM}!w%VFwC9yr8j_7s z81C;3e4~TxmXd1bEpj9Nw;y_lH#*^``fn**m+ zJV}Y6RYry??Y&v(J9wi494gmSQ6gHDWNE)wg{_OTWa$_2UmVCkJ z`FO1Vt$;ZtY+^LS=LcRiAb*x--JBsT&Nv<@M66dsylVKfgI%xYEAzAokGRQKhapfx z%Wx2=i}-Mx2iaPZw%IY4_Zz1E2lIn3p@Jg#Ik&>ar^N1r_D1>@-kbC5hQx??ZBcN% ziwkQQKW-~Pn(!dgpA$>Q72-W=>#;k=$8kXX&K>0!$ya=zjvub_evPfg?!`pi zNz;MP!42;B!nECnc>`Fr4@eqLhg}5$X5q|;`8J#d0jm1bx>&@lj0DoW_H}B##+e#nhdz z-p6ZWC)KQNBH!fpjanYxtkFLP8oAe-ewO@*@8j{cjm8LX+6piMUL0Ham}s3d@7Azo zE;*4s(s(k7=xlWo)1e&DqsxR)w=nV}Ntpx4JL+T~U~bPEv)RdGXA!(r*_os-Rord* ze-zf|o&2`Qn(trozLoz7@$Hqd3*Z?a3UEZ87E+v1X_640o$qIBS7gNn#g>?pOI3|MfMc?g)mW7Czp4*$8dzb6LZLmpocwA1gO-rhang73RaW2d&I?qU9+b5_f; zmuF;#$K9HBBE?tZogq1-(SU(MD#^A0=h~hV$`e^0JtY7`wM|=lUG^aB+N6U}i(|K- z++eYZ>Hb?nkUuif664(P3mJ#^(E3@K+O7YS2-2Oe{9E}K@ZVm!Mw3Ro`rBP_Sgg7P zl@TleN_o4Ko!k>Q-Jnc>2~J1yG@8f@Goe%prN~kcazI=)Ct(t{V8E~&b0r0P$Wlw} zh|qD6Y#ho*;1ZQr8B4A=3kug-Ulh(?C-ws9P!R=xV#Q;$erFZIsNfUSoF4_;a&B@AD<8# zp&tiDz&G41fBY-~_oML0cw(g6fbS*X^8VvDQ~2Xoe)Hp>=B~>p8>BXTIRP=;XUj99m~|ONSm$-e}?7q z)seTKw$VddJcjw77McK>gTjjd3f8{_}|d`Al8S4AGe=B=D=OP z6ZNhh@6xULp=s^*vC#g?*!`|!YkS>Cy%=c`JQ@5uA?JU7X#5w|ym|I7c&pktZ62=b z`*9}z<23{K-*;0vKp)a!eYn5<{2rdyr016)P0#NAc@lr~b>HrQzrFExq#Qq3-!zW? zWYI!?ZP0#xz|G+Obmieu_y{r(D`^^DVF~S|HB?=$rw6^>zz8o{)C8W0dm5OCb2ICd zIMr%QR@0ah_}OGiHLfF;9*YlrtBD|BM7B^Eego4IK>y>l3B>OWjXw^4XUHPo_MW!j z{|&wGr~LCV;#Y6!A@&|F_dGot`V0H}YAN2^#=V6WUn-dSWue80K&rI$b}r&AYm_LN zeW|lTyHI;kR<=tcCq0Wh482pZ@_Yvr4Gs1)b(yMmUn2{NthRG**y8xil_gCeIWaX3 z3vd`_cZSoRoTT5Y1$L&UJB7vi;W1+2 zV=NAyu%Qy^pwYC-)B#rtQOwI$59C`6a)cJ&DJhvWe1Q%C_z%``-8QB8EPi(1VEH2X zjOokqHGVdRt;(aCplHzHcq;q-bbpYDJ*-x0VkJS@JFTC zhEN(yED)c{%?emCUW=kqc0?h|toD=HeObrvoUG44y3guvg0J!(VeMf9hAgGJgL13zVNDUb&*~?b`D{FQT|NSNKKp#gv|p zAHZ?>!p>pCgF(r(8$s1rnO1!~n!*^_atCKtY?cmcNi;4USB4<@*NG-n7D0^4@lOx&FTIaJe_d7?ii_{C8m%rCi_7S7kX%1(~ZUvDw zH5({ z+S_Ss%1=n;AB3`bptjDh9-CNLFG;p@)fs)n4RVTyomr z2(JxiB(W+&nRo*4!hXrRA`q-+)_#et)%Ym4xFs#JNk0*S5@6i8zca9W{`%Q@JSy$x zXZxUcBlRr%5vb?0u_Y=7Fc0PBj8!Ka+;A(fN!f$#o17=HNO72^^OYnvwkyC!OE2RC zl^N)AT?;PePE&G^**!idlRh-fGS&)gvx_qE)P-N`YI=j#`<5A_t(Y8V!!O{y-LU?- zy3RAkoyE>Kx_VL6Robr=H9f{s>>X8ZF=+)JKgr@g?IgL+kf>v8N3Xs1v?EX*7Z%$5 z*oo6X)|Z%xtzka$kg2whqQ!d0SH7f+5mvb?bjJi=Y(G2LX&N_=6lmyOdK#yuziQu3 zf6|cZD0@!xn zu!n6mnH*-DCf7Ye?heq_&mI3D9;D6uxHFSban^@fq(Nv(ca0fT0iET2dI%u7UnM7m z5|UuKc4JWa+EDS*0{yXw0=blM{p*8##CHT*|AIbfOz;T_)CbIGk$v41bf3nXA9)%- z;39ax^Kgxhq!0cvZf_=}H!FrKfm|PZPhFngQe-~eQ9fKa@~@xc@4vW)&#&*N9?8AS)gPX4vEKLB#y0%&eFo!$mE`+f2Jo9*1%qmoH8$^N zA_?uqExe$%$`Q+AmRxv(&EoS6tQ69cd|r-;e#ek<;~f{xWCs_@|*LqDM?hICaPVWr;?3mh~?y{&QlIggFfO^Vkxx?n`J~h z6Pl!ka~on}nwhT)Fq4zyMDp{gzOn0!UGtS48zGhji9?ZJWkvWc4&x7#>wdp%>&Mil zC%XJM8)u&|U*FtvQNnZW-3Q;|U3J*IajO|Jr$}EP{{Dud&tJy0H&4wj?#qkrZw36e z7yVyD<$Md<_oj}#NPc=leydoq(bRxRiu0PBq~@Kt5dEO)2_0rx-)UR|-N!rJ?qj|P z7j2>PPMGXCfCnZi)y$BUIa^CECAx*oqVhCY2*GidmEafa2i_HUer)O=*fRL{<(oaK z{voJ~?4h*Xy&ZlYiNDuv|KJ%HjsAG`%5phkKs2a>C4HO2l-!u!FK*Z7t=Fo~Z1>wDknpCTo0HQ?awQx;wUEan6o5}Kh;es$NoKFBF z?ygk02)!V5{_{YLt4PRa8}PI&b$G|xn`2+F^KOaj9V@`|*)TK2uGf~07{CyH5Ykn% zr=cj)y!~XE?&htW?pmtDy{$8H6HM-SyYio`~J=IIcxt7q~%6gLhq^zg2v7GzlCZKjCA~73(l^?-ycB09- zm?<>dwCv8*%hWHYHgzoPzHhUTv-BAl0Wo$|X&lB*3?L!|*Vaw1sv0hDkG*Y+^ZN*g zUp&^&AcF6O=q&aTo%6vJm=PfoFq-R>%C5UhnFZrnvr1ZTO&j;Bh%h|?bVZRGChVY+ zHIL*=lw{nrwkaW7M24XY!`hStY)2fYJ^K#*tjY*>Ld-zf%M!eyQru207pJ-9HmdV5T*0hws#}4Vy)C?J`>*CXUw^=*>iY7J zK28k&{T?HK;P+_ncPeqwJ69I(D{;{=Yl{uv_h#NJ-QEc7Zeg@AJmZ)BFZqaU+5B*-#IjhJ-+HF&x*lf4H@5vhk z>id*1(xKSOs1j3l-Kl;qDQ50Nn!D*Eb_ognQiLIgtGqHHftvn$$AkJNr}jKLU8(N= z_`iSr{)_W2SNtbhdTCwy*?RT93F%4hMLmy4*H!D7$;Tb4XxeQd7RA%8_Y@O6IVu-hm zauAKq;0TF?O=MzM<>{b%j1xqrI{-6lg8--)2I(qXEaj$KPivl?(_ah7eI6O>Yr8BT zTfL8YyI)=C&sHLL4!}=>uXsNmUe_Qa#@;>9@%9!4cBze+r(wl*nj&v^z9@8+ylC@^ z@eU_VC{Lwgo#+Y~I28IONX2Z!GF?75U+G%9r8)?gCoUqpy%;MMKzT)0wQbJOXQ-=RsnI?^=lK^ zOziy`9H56tE8VIBv)jfyeJy_%Q^u(DfJ-F%Qi>V>Buy5%_f342eZlti?BOc&*XU-r zUWccdq3`UTXClbc8*_u5mv3nmyVSEV?q4P;=l!>IJ)8S8cOqX3~i( zgAH;F+9Gm-YI-EOJG&H~zkP5>(`(Ad!!;mR7V)R6W#sNS$|uix#P6d|t|>wz;wz^i z%dp8|(y)zc4pXX8y*`g_|zC__VX<`az&cWV1{hB3BQ5?s=@hcVM)iEOrF5 zqSMCo7pU%sIA`EnTnr8>n01XQXaVAEJCdwzCCt4d9>Tf)`D$z~NtlPEmpby8APF)`6{z7)O~fRz+HP zb`$*-=WcrN9%FU?Lf<<*&o?zrQKY_|KZ$x*pz?Cp6_h86S@eD2@a3-jkU38`eJ-j@ z?Kpqj=zkH1e|YCjQ)2Xeu<_}p7q1Teq2~KFNb;`OIqROjBXO;`|J`-q+p6s+zV~qi z3~wy)KVgnfVe0KqM@#hHL7=xU?|yGz`Ay+534H=KmnSeZC&+5mY2wslwz0=}c+SIn zD+w_GoBQj2Bqbfu-K zIc?Roa00BY*2JU=f`{fj;dfA*RAejb)~P3^r8+f@ZL*!#KJI%^50oBCVe+&TtexuZ zH(FzZ0{zv&`8k6a{p(wh{&7(MI5>W9R^~hTXAQi&a;2^u;jftpbHuX?jC`BUMOE&M zDUdow@Rul;WkTMUy2HWeT|FNyz8|ire zfLqv}E*A|F$xZ#h$fU0YBnAkJdR=V)wbpn0)rH^C0^2!rbPRqG6HPG?%T*<_Be zm>Q@Jayu6%NWhD2FsWM~ZIfMZFt(-BUxV$%3;LUk?NRF zvRzNCNxN5?z+Nr^F4xnoY3#*4Hq+NG+AyBxM_gse(qY)N@ERx9TY5i{wwcDbE=8Pr zs)c4s{lz!*tyB1!x#3>}?&cx4=-phtn%#&1XQ6ISwmFpSO4zSiMCusMUJC)ZXlN|! z*0sD26?57+A(<0JZNU_{0-1~jl5k_8ojb>;8XYBdqwYelF@f1+HItbyn%#@H{26-C z*U)>Ee+%4GISD#$3moj{$KWVHeJCS*?Htw1OQLigL!($C*E({bW7QziBQ)%!(vdR%2(K5or_KvW zQ|G$g^cs#qL*FB-duM`A4GO!PO5-B*ip<$`uyF($nM8*c#AkyPO$qu?@=i`S*fxm_-Ib^8|GNN9qNHym}ac^495YQ_^V$GOw z*=dw}6GW0$*ur75r4|ZCU@~rqo0EE}H)44zxy`!idVNi>?w(duxfr$eGQN$}PhtHl zi+@6Pb+64m%6&xiax}IKRO+aO=P|CLrb9fK2K#yD(gN zXxkTrYvl`hL3P=HCsPaBhhn)#C8qxEt!Ea1!o=Rgpv0o2$KJ&m?i zj7OcMh%_Ln<4Lwrq3Te{fx^V;uxUcm(+c4R{lHr*Ej3Um9g3RfVgd) zp4(%BwjQ{CSZwi#-|x1^6_q;)jxv)tHRd@BY;XoF1&l`x!~sjgSrStwLEC-A&}Hc; zdK%y}e4A2B;Q;-ET>Y7UUN~O4SwAyi@a|yDjqD4+uV>c{$PwUr)ui%Mg|hcT>B=b( z>c&Pp8lt?X=TN^ndq16nZW<#|$-*dR$&}a5YhkLN?=n5x=lTp2`qfen9Za6s$l@57 zcDeosIeR`LZ2fHa?9S-;`zKt1es^swH+*PI9jy|YMN$M%8@2`JtCk*)(hvis=Ohl@ z>*Y)-B?9ED+z8}&Tl%`bT5Oo?w6#@*oO;?81;DJq;Tm2{A`Z`#zmqKDcZcs6YTWPj z%bw(3vA-UT^~+*6r3GKAqzordvo^Ote*!JxK^ML5Bo+2yp`#{4BSt3x-Fg=LGolJM zX|G%QWJ*(O%2{tkl>!gxvCs=vfRV<4|3QAQ>@jBlF`D8A1RR5uoQbR_Bzp7Sa282km~&w`)9_ne1EVIxYl z*E)JI2yd5mtvNsT{ZW{L>q8tFQp^$BKsySin#U>2P?^5#>2BhzGwh^kv&pVx zYg(uB1*nH2yIX6Z(rS=onX?XUCKFBCyP;ugV2Dr94SW#MRiY%Pp%ED9bUc_#42({; zzm31>!@ru~v%USUp^sNg>^_{)z6|;P_b;v9h=0`2&4sbctP?nx;sl*d>$>+<(#l$t zOEAHkXZr!dCQI1I%`itM@d69XlY?tW5J|HNo~(Rr%0kU;D-qffm;>FU6_Tf9Ydd9uLlicw@y_#&;FQ`@fpaBb`H zjLx+bk+{32{TFJ9f6_4-zB9e`S@5$`;%mobcub-zJPh=PteCxo><*DXw{yKTSxiS9 z2P>NC&`S)tIZcgp+D`?iaOrvHS5*QX$cbu9iDYkT=+HF+Tbfm6R`k&UPwamWma<godL}q!iJNQvY?eU_MIcJd14v)u9*ITHJ*&s5=+|72aASZ`)Kct4) zgpsx^h}w1%7TFxJ;}jL9N7BroW4@XZDpt*uZwRiEBLO|7vGbF8`uF5Kcjg^29#ctB18Ysk+i5lG#Sc2yc0m6MYYH0?p2W z=(yaGWyH_HU9=Odf=J?&P`vP~GxQIfo;R`2uMTw@Sp>hFxou#_P{%;gbQBM+?R)bY?&Aw4YCOFFJZjcY3tEJQsy70B9O zPdz*^sd#omKtCXMO$ijSSvgU2@_EyQHJm^7_pRiffw@L6-ORgZJvXWf;0Yo(z0e|l zMpbv=xkkRAif8e!DtWqaWzZfI9$GBj4Y!KggSF{5bWp94&CHx=3v^!5rNOka7_>&1 z9B8Jb+wvH#d3)*xofApj6lBPF3Ke<{w8t%iNr`LkaH#?A%NEGKpF!a@&veurs+A=U zu5nSHPql+w0hmR)Ub8>V4@uBhfvoN8UhES>+=yzK@8`LOtHT^!v+JB+6|;n;JTJ0S zY+;DImToB`K7!btU<^%_!k>HDcbm;s?gf?W!Na4d5mXXP&D#f1+-8R=L?fHCHcTK$ zCWB=etilmJh-0$(UncRs&Szz&Me1rLoE*3!2FiQb``V4P5^O)y}L}zc?G( zee=*X>7}?Jh*SR)hQaectqzY#sPKCNz>kPLFSotx?)9d&`OklW*Cv8DpFiayTrwu# zHO6_}H2#2QE4MS>dnnWMIw2}j@0E`cdPjWfR}bf(aCm+?__mS!S&ui_=)% z4qh!VWlyb!APAM=a8$GmXV@t4Eva?orBG6$Yia}tFY$62j472`P? z=DRS3DHFC3>Ln#=;506)MU?TbZDqaOlCmseN9c=Dx!ZJ}OI-+r-oac0!GDb-2*~X+ zKAUHpdJoqIci%oGzkk40UvDpsovwM#sdsxPm=UW&vWp0gd7ea9c>tJJz<|8RbIpqE zPXu=?kH9iD1577dD(~itty&^gjfoBCEI`y{__Lt^i(YQb`=;yM4~sUo+v4*LSNvWR ze-V9w>gD9&DscpjId|brSTCE^Bmqty8(B$nX@wP{+gg~(;!kYvIq897~6Yy z(HxGf>@b@HK2lIke|{DLgTADcXYntpdA{&~ zjf>s;5y*!Dw=eU;oK_QKN6JIDi)P%3+2e`0b2bZ4Ob0Edx1up8*8^iO9I!5lV%ip- z=Xix+Mitrj^<-<2&?|2$?6-|Zj0ihc&_dh!5|^pDWlCeSm$b6nFR z^a*5EAllhsVr5 zTPzL2;Z}Xghr1=;feFp(4aeFx&>fCzP)xWYZ}CIst-|)JeK8!T_PeVq`J=Xehygtz zd5r*Fj_(Npjhed3EG@#H=F05$O zqw)h8xf3uvZI0>GfkX(L%mjvCZ5!N<6=CMFx}H`7MXmJJVHupvsWfea;}?UWm+t(% z|IkMM`C#zdrxolE{Y-w=tmOTY{;7k^M=eJD_Q6RDcmKLp9_JLl?{D(GRT*!jpVj*2 zd~9V#Jr6Y{-|M*mHM5=^G~7^iAQ$_ zV$`e>KBU~!&}atCm5$&4&!ujD+bHy72M4|P)p(VCQP}I*!&T;pVmKo`?L&l<=!o6J zC$A&KdG7&=K1EOP=D6w0z02>&vSAIE-C0tF_Pg9L>7}rmICXoNC;r-ucQb%Zv%|C| z*$A8LzZkak&u&^=KW+g%Mk{}J@)^lP`=;mj_oiCEihk6~)5+KbYXTvx9WqGyc0h=k zuvd=jQ;X^rtea=JS<75@GYbt4n0xTg0ES!e#IN%@-QiBZ3DKFS$Ue2x$<;oa_vf|y zYH-qVp67QV>wcahiQbLrwJh&ik9U>wYaCO_o`>QDRxo{|FnibK7J{b8YkTYI$K0~G zRMD&*xSHwo!R9I-;wIA2h29CU#X*X3i&~vz%a?d}96&%uJ+yvSrgrPU-1KH!@#7X#@A^so@zmDmIb{WU z-<9!)G>1#n1bchW2b&5XeCeA=?`84!zDLj@D?eV=)$YqTBi=0c;`6WfA42~&+2-c+ z)~+9dPrgfg$a6n^Veh~ErVq)}dg`bMmrk~!#9CwoYcw!z8Np773sC29k0+oU z2R(8Mi)bZvsc+Wxw#<^UB|6KWLCbt)eNnki`Z8d?8OG1IKNP{Oc7C7`3z`@1x99lW zPujfvI47MYd$w~PwQnAR+_gr=M;XnvvxDc=e}2BrP2A~$Jd3{i9?vJ&t!-k&!Y);s z)l$```dUNL#H)2cMu;lS;~;LQ>xtFZS!TI~x)_#=uCzc#@Fcr6_NFd-yQA3c;082InqEaf?vZM-i#PTnNo!&f zvkgU}0lX$pT^*S2bh$&$ed{s0P_Eph<1 zaz*GmEFJN@Njeq6E$Yw*3>39)3qE7ye%mzpc+Jh4Tu) z8Cw5mrR?{q`BwN@;kT#P2*C)=#~rbZBtr&KLOAU<$k{8zOWoC*wGs>33R3J8gQhO8 z9;_J?r;yDsCXe~TTJraV(}~NKy_*B%Mu&W|hv=0jWJ~2AjK-XQbLc-@V^E)a;`jDl zxfTA1@5|}f4lF_)HpfX(SH%$KSu}GNw!q;oOyTp1pwY^uoPT|;{wL{N>ued%QK3t`Z z2pXLDYA6FvN?5Vtqj0MgENL@vP3&rBJE0Xi5Z4W`O7|0{U2CUS?lcvIq>Tw6T!e%3 ziH(wJOI8hgifaW=3)cAR%<%kzzpK&w;}M6OPVPT;=6jR={H~AFWi-mm_%W~a?v~i2 zUL^O=?=EkwFNG7eJGP{8KpZ}-773y--f`(H_eGW#(Y|YFGW6(JS5lRk=S&pj6J~u3 zQOCt$kJMbogs|T3ra|Z*+0b>}edJo*&=1OP{VDJA|GLaV@Y5{xhrIdYtmn(0R}Q%U zJuX({pOt%T{ikl2;2R2u>kqv~T>kw-@0lOsKJVHs)^Yi8VGTcf{8{EdIsE^I|N5}A z;tzq6$EeLaLWQ3w|A&Ra$L=3~@5}yH{>3cbUU|66{d z<>4K>tqLBSX@VZJEUJo5jc?th#q2dGo}P;<7>0mAD1q0$fr$(^E3ALkhf~^*~JR zmUijtbF;6U7}ZN{+bW|QKQ8lU$Jm{4kXX20mMt8<3vKDBsPmd|ctbVzm`ZUKG@F#AvndF0vh(ff zao=N8kCfKZgrDM0lJ7FuIIIusHY*RiE@QXKfBMk^4{vz%Ss=Q}Ayh<8DGwd~sRV`AQ7bafs>*|JD z^}ax(+??DUg`Awu>}lL|k&)`Mk1{i;T}}y}PQX;GOe39mYX_*gV+}H`Ghk_)25`(y)|x^oSFk$P1@?maw;89~x#qR;^|g5~x=s`ChQ`b{?MbcZff z263r_65&fS0v2sE+i+{Mbhc1hr^&p4NPO<1dFIAI!|N7hgElyaPhn8OQUBFl&!@)E z@YxfiZoQLFdy0Gh8INLkUL?HTBfX?(Kc${rykGF`qR4MtyYwHTHhaC{Z8W^K!>3Qk zcR}#8a-v72VZXQ1dgd8}-aF}8^E_H1Z$o*vxp(gp?JidGHuiA&uI~y|f9#t6+aBF{ z_4e0T>wdc}bE|^S;!gLC&M%VBhW~Orw)vR0o^LC1Ud?RdJf*W{w(jyZEDi-~sVf3d z7Ot8dR?DgE1f_rlF|pD4%mZp|8cga#d!7s8X{>O*p-CZ~%{h;jk6{1LMVx*F`adn4 z-oW!(PW|yvds|@rdn4_W=nllv!9&Qt!7- zj4=R6DB+fD8_&h#v?iF@3SY1A+VSWuw4bTdop15WxD+jGF(_+L#x9fI&yc9Q%lg)C z1X(z(fV!}3%Fvmomhed(=FRXjlW zvQvTuGIT&-2n3Tr2+*gwR%talRA-TTAQ@n{J10rGB@Xmv;gd+4bj{kO+Jy6Vz4g z1)s~`hetsp3~5+uW*@p6>bz9&D4}P2X$7amWDa_|(&xYs5Gu&!TI+zmp`x3k44#)9 z-ErNXG<1fjy9tXf@SyTKU@F5zGY2~04K9)0h>uw(>Xn`O&&T4vk8rzb;4xyPaf`Hg z?rZrj?e3!#z_-mG-`DA0i22{`1b>vQc8w_gyesAHXFulHz4^B{Id;fj?^<(?&9VE{ z$_l+u*xX5p`2oMh8PYB;M+LO5_W1kN(fHq19gUAGznJ)TFQ;NS;3PN$_snUnOS5QY z@$DSiWz`B#(Ft1zsJIuiWLL{TR;~NZDnc+ggpq@2D~p-fx(bep#A0R7k3vWrcRFKl zx6Z$}O66AgW!$(q9b2hlZ7Isf+fDBm`nK9k45n_<$UA=~Mc7JH`r?4Kxjqe@c36QP z6J`-NZB2d+9GyjuM^&)i2!}+M`V$rmK<_wFUVha9X>Qh#|N3Fy=7&PMYdrtGn{kUp z`nt>WzGU+Dw#+VH?t(tOtM~2w?Va#FKFohbA=^igd~Y_y4^Mb+l<6bC>L>?k#x8|+ zGX=uDWMfv~;`vhUy==Cg^d~l(a(kB=2&vf}{TX^7n6}9w>(Gb&oIi@0mRhDuWF6{FS5-4i6-)B4mZ-TSlyhDofcg6|FQR`O^zhlf!KV`UlEhj#>^}v z68Dw8HPbi(_k9Dcp2`3a00Ia^1meKeTVq@;C7D*_isZ~{&A2P$j#fr4X*MRwMl#7L zeNz3C`2*rlkU#gh0c4@Fa(dZhV`@5q2>0ji&!4}`pFhv4$-3H{^NUS1Q7HN6?e4JQ zsZHsGz}7jRtQJ?>NN15}yZf#z((7Ws9#FCewba=M)*a7oa|mfZWjZL9nM61?iV4YT z@Q01=|GXRBb}RgJa+1%aIRx4wIN*yHUaUKkH!&&Fo8|qBRih{ehMCN?l&Q7YUT@~9 zb@xc~NF;rc%^+7_7o*aSWAf#FT-d4EMJgE+D~vzDj*^MlbRN(u>Fm0u^>>@(57hSi zr?v~kD@2T~a57N)T`tB2kaj8QJaKUBKQbAnVS-*Fl5kCB%N-8<`K_Gb3(x8~!CSPi zKk#D6AE$pIsRer3dZSgT%E6(KsOMV4QX-iX!m4kdi1GBwQ|Y(WPG&o;`W zc$Ue;U&>?*$be_#yBEW|AjQit#&yy=X&&zln)&>Ak)p>5P1uH*i5f|-T2)?>7QRR$ zybLxA@|=$kBl|(HFNYV2!@`e@cEZ-O&kF zFv}!09n4-SE(X(ugVeT!l+ZegecBQ?apibzv~+s)hhAs^V5n$P@Lw;6bHQ$x92CaB ze5@%~`omN?VgJH4tb{lQ^;Q%(n)BQ8*)Q?r>Q`lqcZ?{5VJfswYW0vFo%e4bpb z$Vh>bJPU9A`B~gArq{F2JU6>C0;~fsa5qF>mS|EJ>~QRscO)OiZ$Bg5RI4(X?Q;7W zyID>0i(q)9OlN9(HSToAjmYpY-t)O^Ua5wb;e0Bbo-O%AwA2+e_A@n-uPg-sG)hrm z1=Y*0IMonUA@|Ouljj9qBA@)9D4%r9|G*Ic$uq=Ll<)&TJcX(sR?9#6)$+qKIgcaT z#XYBS^3vpoCS*_)jPKsyW$6#s=>&HOX8CF?)8Ip%?P4p0;{IT>yyJQ0|sR*l4znQ!BYu+7oEPH2}q)YPz=-RH}@@9;plEu{TiRO1WX z6gpr)s{R#6GhX1NXl|>?#LyS3%O!tQ9+V4*Y=vjC9zGW0w$edaHw)R>s8Sxa$K_0P zTw}y|u+8+?X}#arq=M60I-Fc+gBC4JTCu8c)lQF=lkYHz0XYM^JB!lbOY?x}fH6w( zH!lJ9ag;6v4;v1%N_Q#*vy89iPuH_!b{C9I7lm@Pvg|Ufzp{$1J#=*HpZ7MASbUJC zo1={BJcgju0 zYr`k%aKotVPW8G^8imDWCt6wa(Nt~W z(KN}oEwRbZzm>~&Dc3I|mwK-VW8HESb^eUe7y1l*6W}Jc$xm$TUBJq>rG2B>kQPP1 zQjnrST5b&|Ib~5P_7p~nh|MZD&(F1JE>`T;e2GON747WVP1&RNwc1)1X7Pn@yeqYP z`(9_eX|-jJ8yw)N#yhp(c_%{I1K?@v3+f9 zJ$$+~cp(S08af@-`Et2pfYzs({P0FElP!Z#yjySO$LnQQPD@##k=SUh{%Wmhn~2!2 zm82Zojxsq}4vC@MIFaPLOthGqc3QQxFxusE!>q^W7sW!XB-gc0>!5Zfoycb{DBOQ* zjw8S4X>q5acx}0V)P^}xj;EJu1L0XJE-?PslpS}w%UU7YRKjem9j(^dOD;mM%CUB5 zqP3OiNDI*up|u|iYPT4c+4yocOnSDvL_uQ6?Cwsxk9*KvV&@KmN< z^KInRTm6!BVkMr)x@jsLjsT?3zjHcpAyJ%9n;LrY{V49VsiDhmL@5uGevzry#=&V9 zwCUY6G~0TTOL`KDD~DQ!m(Ao5(bOPDwejKV6D~oxQUTWzqy9h@Qopmu>j7jSO-=6O$iBZkp zPqkO$T4CNg42#{~R4FeNzenI%-y#x<50bq^^K+A@h8cYkMxA?cZms_~pOS82;l&-f zY-*gQ!nt#4FVjEmz+rl9Kk=Eg3-r&BPfQ#_eShCSZEMn28El&VkW}<#7CmjfmZ#g) ztlcbRX!Z*`h6NJmu2bDWhcwd zJg(-DsAK!mRMNJK5-ad6apw!xe5p1+?5H&BDdu8IWH6@>QGdM7C-cqZtLPM$IBirp z9KAk&9{Yt9jDP|<(I0#Y`vQushI$o6TX&;Tz8&(X<$iTUFLvP^z2(JiXcLv?@G3pt zL?v06Zs#q3Kwi%F*~y|_O$`u(kz-}4-wk-^)!ZML=7n`IH<`)X7r6&r=i^SJCi>E} z)i&UOBHK@Vv+MuBK+Rq=}PsIlOCkXv1L&yRtrL{ z9`7tN8G1WeMl0K>e={kUWjRvKmxl97veuHD!Rk_X;1KLBl0N#874nfN4?NJtZ@@B(8u-pjfj^88X17mq#0lFro;tdMWuRv74DWJ zlT5#K@CP~?J&DINQ%~uz7Z`EgDkr9?xjzvt^^&_*Y1qgVf?7)MuGLMm5|+1eL2fon z%A{PalzqIwFx^&@``n~~uDrk`k-_gRxhF*NjyaPEPR&dVaz?z2+u5PR^w@rS3D^rx zf*w4~u-96|%XIW0?lPX8yqJf((`6wSA28AKX0RNt)lMT5>dPd9nQ--Wk0%na7(ejk(u0W7Vjmn0<}F2=ZhL%b zTOWJ)(6}1&Y_%d^ifRY{U{a)Od?PdBqYKeD;Oks?HHtH{&a#`1r?Rnbys918eZ9JF zE|p|xeo#x_;oinkZp+s_YvlwF*mXR(^n4Xv&qUS%t-sIiic(rG3+Zt{Kng}^R%4=> zVM-h)3tS=CW?JcFteY+dI>k(dnWYbfWxuL)2GdMn!jycza+_|%cU)m4=hg8G$q!%B zzhdPX`|6so4YrOd{nw>>uv8hgH-%+8F!8jrxlN|F7ALhazwJE#?Px$$+yL3^-o%{xLoFn^`11;ZB^bsI+BU!Ok; z|1v5Bpa`Mx2OoXEfZ|fs+^AQcPW)`A&Wz^Eey6+gFXEcEY0~@lJR)wIjBm0Rx4zUm zIT{xCX-{O@@e~e?+F_L}9R}+`Gg)Yf&H1DuhuFfH~AL5g27tn67m=r>Xc(NIl7o~V>6&K}Z+CTS=#=I}HF2~eneUcl73xV`v zlJ(F`rMw#KQu%#fE0yEn1gnkf+4&|V?kdWZpVmh4<&I$+&s=g~q?dkXu{$ngKZU)Q zUp{Vu2CSJsF1?6cVdzLq(4^fmx(Ve6p@?U3NNopsmJ!AN{-L0W}oMUckRpSM}Mh* z{?94=pAh`#5~q)Om+9-O1OzN#8-KY}1>nOi$(Ly=tSBe8nOvJ7V{MvweWVQj9rhn{(OHe{E@eRG}d#HEceN+Qwk3$`rea!4ZYJ9Tx?czhvPZx6L$q@ zJl7|k_nJEO#=RS~HYa)t_Mg6uoX)TE@wh)nW0%GJJ=c}jiW;;ws?MEaeVL@u;UfCd zzv1(%7s!~J!~Hr^Px<5Fewl7X+ET66Ooq0Lev&WrDsycWTsFrWUpUYbg6yi{ zpX|qtURWFU%j{xX+RUP~vKdYIZrPI^&|@{0{OFRqg8#q2`q4ka4>-0N-ixzA%0B(x z(FfQ@f4w&Ret$F?rQk36xc~YCp>T`}L<0T*`VorxDSsdwj0S#0`G52sd^AO6WYm?< zh2tLF_v8Z$`&wW~o@1RDxD}!Soh0zeIGu$O(mCS<3f#+7@47v zVa<+MnPMqP7RRijFfxUk5EKfx$SD-!3KT4OdCH7g3Mpr#g%yJnfM1ioK%#H_m^5oK zY)G3dn?&#*k%PXDhT`$@FR+J|I?`xw1Ai}6_owU^-5CrL?v5tLT?__2V zVDTyLYerM&Bwt_?m1A^No)BWEHK%L)0bdY6F;l&P3x(mFxBem!x84}J9`QM1E3@PE zYOk#MpswRue!J_5sItO_C}mOGad7kC>(A{6U#~}Fc`uR3g@`SJ;2IbPnl)8ivUtLN zp0H_S2$s0y?1``OO_fpCXb{CI29-gOWsJn#lR0wBc)NVJA8@}$l7>8v8&dxtE9-uh zkBk2kl4d8q*K7mv!&oeKmjB})fk@CF4o3q)Bnyy4K;?f#MSk=heEx~^f7kqLBgCJ4 zuiVDv^B;}{k)nX+KNt)BF#q4h$2tE^I+>}^-pZiWC_kt>^>y0#i2fIeMk8+hFYFKf zp#OarpWpqPKmC6F11bNzfB#=m=~}f*H(MY6WsO3_%Bd{%yZ`#nsmdPxK<_H`GTlyI zeg8&x_4j}Ium4-BUCY#36#AEJHadka`jV|RlNEi??E<{5C!0w!-XC9m4+jc!Sfd&p z8V#*LRXUkuruKcDufPLkx0*3|{*Ql5HQAL2*3ct`^ln6*A%kv{u-7dL47?&r`PEhM zftoNX1DR+L5Tj&W6nSJIA=^k`cOU)_Rw1)(ylx?TiP^C_$uklX+{}bA-exu7!(Zb{ z#Ko<06GpkZ3O!IOZYDEevw2Alr3cgp>b6?z*6xhT6{Cm_9I^oLtZD*J%N(-dMvO2P zkyVE}&hh{}{6Nvwt``w$wvYqyVU+JOGWX%v8bl#0in?-O=Q>UcL1r`N!RF>x0$RLGRYQn z&tC9*NP2(&mw)kx!0KvLrdxUwzi-YoNlq`xW<(1%B z8clMWrZUNPGL>x6#C^cO!}hrHC6s_nm3TqXeLCG~C*hvfTr=4&)T$?;IzdqX?Bw#m z>xPhZr-?v5Ad?wG%cS1rYpwQMUK}(0TvSy3VZGLDzYT@_QT=xZ5$;=P(RiP1wR*K? z=B=VJ?-W*;Gy02avO>RgbO|pjeI+z;qsYRg^cw>3;we-M>4N@FS_LWW46*zil-CRX z9m=8UeP1O;wo^@`sZ5s98qUxys!b!rpym91qn8mqR!e4=8D-M@P320!ix`o z0&!9-`O(l=nL`A7J7d**3X#G+C2PVRewWl#(aQ;AnFXGDbyAk#r$bgo3|y8)aKuqO zcEEifDdVbz&woF<6+=@GOo6C+)REj}Wd))4k;1Ob@8fu1QU5<>{~;Orqu2aAZ~p}X zfpExW|Am6#$Pf14ck%fN_4j}ISN|uK!DK)cK;%#@ObpZ}2&<#TDJr{S3>^@2r5P)O zvzh_+f**X;olmXcoQl98by3ev%59j3tE-eB@sVJ`)Q^6*EQgEdV3pum(@rtMdV&Wpg?Fxgpb;ul{ zfnn54TV@2s1{*_+P@<$a1rott1aYx9Z;;&gQZ<>IaRS5RA5Br$q$zL0|Ns84u4f+e zS(fJ{Dlf|1L4@hIo^`=sKq`%n2C{2IW}9#dFCV~w#_#TCY^w&LKGXwD{i`{D0a zAm@x))NQ~U3Ets%Wdx79JX zIyVuG&Z@G=gPujfb{WhUDu25IMEuPP;SF;0QVb>PGj(U-!K~W`O2Odr%!osFBP06| zK^fl>Je?468G1M7HBNRkCujm9aeFIJGRyIZZe&dABv%TQo$GLs73Q0mt)O3|5)CDd z1qp;~UpF@LHFx_^fi^DCWc5)rVe5}0)9WG%geF1UFp6VDRS`~Ejdkh=(h(_5(LVfI z<_MOtY9q7wN>>2FSZwCxfECEDWM2JDtfe6c(&|kY%a0~nJS z(JD|`425(a{Y0H-CU6WGkpeOqHiT^S4{!yf4NAc-pN^_1>K@sCW5v-hnpBP#m7`uVlH{JjpHM|+!;lN;?DIhEHbMhV zAmceZUK&@wVG9&|K~`1EFhznHj98-~%Ugq(8FtECSx}$xpf3SXij%Dyfz$05- zZ(DXz6zU(M^7++xLY;?lWjBZQ5&eV$d)**5nR)_>e1f;=?-eo5k&=? zY>1EN%!WngI5M1&hH-4$I}Ce=Re#c?e}{bySEsG|PQ6@9X5M8A&9~>r<|F7;uIkNN znr^k|k5(`@a4l4TS8m6SH6%YQQZaG+3H}g5b{1f8Jz&WxQ}s&Fgm|$F0vALWw;)Cx zvsOiXdUvJ!OL6eXZjWRSNIOv{E^}@kVLqQIY~b2;23*XPP)OlKnhmBFS#xV|Aimzb zd1k#JZG)_qU;Qz#Ace|spv-;v4LCN*01Bood&<#m$_KXs5==gUw!r~jYy!_+$oI%i zpdi*$^@6K2<$RVR2(yD5K>8G_rXDMiPjy7e=OIAd7S_$&>Lsq*Ij$<9M&WEKXu?oC zse4joOGPu4urvLi=qeUeH`Of==;o+1G&0`lQwT~*%#6_$pYxqm#o3H!y{y2Dcox?? zsa27wGl>MCjd?OUa_oB#RSWtNaF`hK8jlQL{lki@#uYqaoCDD?k1RsqQ%^_NbqQCAPiMh3*#n&ybX zo^_Cnz^P~rn6EiO(p2NEr9V>XRx@iL>R(YQ1LYY&IU&Y2cKcCJI`04u39Q#kA)EHL zP;JvWx=GcWg-WtHpi1-r@PNy{n`uy_P50aIOkbw&F!wc|8n(f^DMFg_)$tR3^;pej z=xnl6Zc_nxt0Ji}e34tlCx8E!fBt`CPcY(d$PmL3hzKrqg;p5S zftoAGiOv)v46H1#p{JfaVbKeZDvNBmUn|FcQZ8UwNY{GKKuLl?u7|vwnD(Wmu{0`W(`0(2q>bK45UMufvF=; zhOLLN$KXY}CyTpfZ9#m^I!gV6cRc08nR$oAf^#>Fc_iBxq`dnbTxfst4CkC zusJlsOZ(PXg7XA!jyO{KOjp56 zgS+c+cp;jU*zt$oz|3JVh0MA7utP-#?AWZrjGcoX;SIHWJ5#!++*~OQIfYTPuH|>W z%Wl^jVW8Oe7CGg8Ya(d@d*9@yL1J@Bg!dS&Jnx?)B!138ml7xYN9yTY(>lJ#W^bPS z=~C~XY<(zXfYJra6J_?`BVIm2<KE|4cLp~Fp9Nx^?|3wh2T7PgH}Wil^ely95L1(| z2BMhC#z|Ks!HL@!iC6c`zSWEIzY;gBp`uW%zESfG@}1sG+|+6Jr8T)|T19<-*Umfs z?H_@25l2>{)ks04b<5NWMP3uoib+}#xsSN5C(~_u+Vk^dcl|t>J{-gpY;Qqi9=|)~ z1hSQikWxpUpeKj_=E0qr-@?!%=u>{&ie%lbyaJ^3Fhu>#@;W z^SOPH&cfJ7XFjt0P)`oE?mtn`w`Mk(rl}sCGCEjh_Q;09y?XP85G=S`+8=(SYP?8! zbmh#0I3JJ?>`~zU;_{&6LFqIjPZYboWGQ}Rv%4Zr7+%4EUF-}&&KOI-ZY}*cZ`vXf z-cZ9ObWtl)mYs}{U15yD=^EC@LR`22Ab_+JAWBdxrcE2P-TaXKW4_T_#TFQB(_5{kkrB0|1k)*xPmn;HCzTJc6B$UxVSUkA%f=2(UIYcq z?R)G0@6-26AZ=rE!nFA}^ zc52Dt5;TQ2175edOk^QbCb`fQ(15Cz?FEwV>dl)cb7SubVcKi*4*3!Mrwe3)k^|aT za51(@klVzTKlK)o8fsByZ*Lxa4sXEC-IrHj&uR*KXXAx8_jUlbLi(xU2jF+y`1Xd_ z#5Z@S2=?NlexC3e;10L65mFRXz`LkXD^?lvSdS+j16&pF7~{g^u*29Dx}f7JWNoe@ z+!$M+_AP1s_=gp%W*>Oc{D2GR>fGV#Hu`!69~j`L8I5&TfDBlUBQs21}Ii z36-oAo`W42SgUaLvENU&tt0af55JvoGtmf36u~yYX#9Hj4dTEPh!9fr zP$7Jp0JloiPAKB^3XQGcMtFHPG>5!CZSuey)9e8L%aTwhee+A#W}I#iBdF5Rd*U~- z`6$R^-3j9MeUO)S*M@Dp_96xQ-aBz;nAkYlBr(N>iY;8j3GlnWfZbBkQ|cSUei#Xi z9c$5;J20*i_P8l`8M4e_*&7eskqG25tQBGxq9r!bT)bHiKu?!DE$lo+V#kRF7{>uD zcDQ4o*FOAaLY$t8hI!PFfy1_sLTPONFFmeC#NrO`ciF;q5hbHF%B&1_PGJKdn`1Is zqDE!SE+HPEmv#BWZ+F}Z)OpkkSP>4XL5K*k%V&LL=2np&j*p-aeBdbj8s0Fx~JQ^niQ`~VEsHf*Lw zfdW;?)S9o-f-%7otW|vt-&%+xs^VL`m>-DiiKvgasD?G;u#AlreM>6c8`l z#O3v9WOW18`5{>@*Lrm3U9DNjAyp9)yI#N1Q**6YbuLa&^{=mUbo=_Ax(2WJ^*2V2 zFYIu^*I%O|H;AlB4{q+M8%gAmX*RpTiPg}oh+Dc)3z3AD@JK>0+uVvpv;Rm%fdLy& zv4yo^_(v)j(I2BRB7Isl%ZH5@ngmu9YrY5>fL6>dj}Vr^Svp(XfqkSRY>Yzl74cV6 zdJvVSa}1e5O1gJP&EUocWD&my;xX70Zke5=aR4dM?$_U#Zy~lEJ+Y$`KY|Ros3XSY zSdrtd!nPNv3b4(Gg~$oF@7_%~8N3Jxk$S|UwK5!$; zWiO0R3{#c&I+rJG0y`n!;(`xrjfIT9@C5OM8>Bm-5v@STSmm)sLIkW{o;*Pn;L)7y zw0(WE^P;YKm+b!Lo1=FmeGaL2!H(ctpoO* znLAl#RZSK!1re&b4f_Lk1~n<#7&`DsO&MN6>;uv=$cV1ea3~3%ys8%{PS||-H8}Tf zFm(Xb_g7{IevG&1pKZ_o(e`l62@blNiZYT`+(bNT3=w4D!AX;ZulKFo}aPn4v__I4i^k2iXZyfZByRQ_43|5^{^MCLM@aKUv z@ly&(Ut{FK(<5j2nYJ=chLCt@vY8DjH^#6=v7ZkFC0V`@VFitIh}z+9CkmW&AAT#z zNc%Eq_wJ~RX+woT{dB7n4+?^NJBBr^lfW1+Q?&NV;+B=u2<5kTz&WWCkI{(rTDL#8 zubr{Al7P0IUAz0zB0n1rQ8ib0Pm-mZ6p7$uHVntZ9-l><7#4Z&rp1fkkOu}B3{{8o z9ucBCo}=Ca{omP`p2)Kg_~GphjsCz<RwXq;qzM)-FU}OqFd(oSd;>#EqdVhNZgpg|1ddL8w zU|$6&;|8~GF9sZSBu@6^{46lTsz{0>p1DEPJ2|d%b4ucpV&~?|A?}C+*_r&zI-up0 zYs5-;PG;LZ6rgh_WtrJOOl5I(%ZU&AbMr4U7BrS&D7@T?3&^l>miI!Wu!tdPcUS9^_(LkLrk1qNi4OBj} zG`=;A{fXffd4LYBlrLSY2faM8Vqh&gChSVrN65oF=e#@gk+8`d4gG^GbE1KOaV4m* zvw35(MGRdhQg#LUpv1FSm(WLibFa6jGgJ(d=f<3|1GloaFrA3LIZbQ2qVE(g&$e(3 z8VzN`F-UJClwzX$S$a@mtI=YD5ESa*fyc8CHN0bUMSOyS|03>aY5UHFXk(OQVc846 zkZZhvZ&0SLVCpF6Rz9xoe@02spS$V6e0Ybq%mW=TJMkI=#XNk6I7|L zyZeEa98Cs@Rb6qvJ2Capb-n)Wt$yE(quiz3iZtP-7IVM`wdf2CnH!?SZ1CPu3`D$H#FF9!_Z9~J&fh+v(EOwdk zGLL4N$6E-uu&wkwX2GU}QV$0GASO$`eiF{dNWja+Wa=|~_vML|_8?BDw>K^!=taE- zL1^~>xrzjVBzEaDSZKn zz2r<|J2u;UG7m4#A6lrGEa&dUk+Az%By1Qq~%%B^N2YzUwL)kgUiXF?*?05hKu!T z;>j?`;%FQC);3N&Hd5gk-;~=hq0FRW6Bi-`mYpbiV@{efA_8c;N6dJsp_Ov-#b+Yc z2$Xi)E_(Mr0jb|olM&){BNC!{PqOftrYktBoXeO5#OjBZ@Ix%73;5%mf!^A)Ua|xomaGRuW(0T(@i$ESoBBiis5+MZ%L0HdHqh;;jG1YZd2{e z<6c(6Ltv#J8H=-@hN<<>sQBx5IZ5HmxY{L=>qeSXvO;al(wMS5 zq`ETXkUjd$wM=#BW_M*xxYbbu(cd&8+CsHm!(u|AZVg6-#5VA!d$YZF(8j$j&(fW_ zYP-pDhi*}~Kf9;MziO>_duMncs9KfEbn3AE-=-nE71gOjh9Z zw@Nu4-#r<{zjm@)U6wq!#sawe2G=+@UmXYf_4s>GW}NU(PMx`2OhIm&KJ!_f#6_Nt z!Lh2|IOcDvJZxo7W$}u0fVw}c&^_Bn{U%W3b{B)jmNREGojr}njl)-{19)~cwnm;A zjt$a1^x#2%fO!)>MR0Rk+on#c+s0GNqHyRhXU=Qr^5kXuFXK2wbKxf9Y2BB4Mn}U4 zIn|=0^OpKX?hZ1tXIkma9BiHFYEwm5Z!j*E z{qT2InvLUjpHAkQ{o-4@Sh0ov!aOM&AF9WV{M)tBRFFtZKX2hHKS1r2L zD%7f{VLD%8xT1!1HYa2RCFzXUbb(18M|0l1LABb=POJT{-mLWpZ}FYtS5LOh8J8Nx zi-?fX5^CqSNYoYsBl6srS>!KxpAiw}Y+ zcCtjzS68|TVe2XfTx-HAw>MUJ=#4oaZC^hN|9GMiIQ?j`U0yFBV7Ne8H+hzSi!+(Uo+&~3e<<(GS0K|=z)G-SkKXy ztkcz`esw9NP=BRJZkH~4uL*@A@5K3Yt7On5%8nIXmi} z)fda)I5Nk9e|&;DZq%PDEudA`AxNW){s${rq~rq$T7|E-j<)aYTd2og)QCTH>_xTQ zrA8_Rf$WoE+)Ts=Dv3|>P{wTPX|Byo2oE^Kob=!7 z6k?DJQRb5PMqHU%nTwxdBo=aA;XzsOfuO9TIp=xD-K!sRL4W8RB>ALnn{=Z?H|YwM z?6m1>+hC!uchm(h8(YdJFQz~iSq&~hd@c*m(HYje3lsL(ThFy?mp6?MeO}VAUeG#2 z>S)zU9CB&H`ti|Anl_8t&!oO0rL=1465WO@FJ3r0O7}nf8@fdyI%y+`Rf8-oiO_D1l#X)Y_|~O5alcTai_OujyoCQa#+x}_Y%m;8wJRiv=MmIx5VX%=;N24EAPsi1WH+WK^a`@1i7!mco~?P?)HzShyP+ZC& zjw}b7K6Mtv7AbjU58)jVycEUlHURwyxl`DMfDD=x6UT=~kgbIyV9u5sUc1c`O z??B06B9v7n=lQ|cDWPdN=C~_AAYM)0V9?Y zuyZ=J)D|)9oQgmA1L}PAuyZ&#vl(SB1;n`~hLUa%^49yLJQ$=)3O=)P;A0Vi@{!{T zdWDOQSvYB-j+pY4*@*JJ2@|apFPxKvrpY-+J&fFS%ht^)&Hf&;O#99GDjXk;F`Q~w zj-W+1?E9m5RoANhv;Ud;+EwWriY6V^FYAKlPGR;KQu;(>KGcLRAhiZ$2WS&5(BlSm z+I(Y1bif-3T(~*g1PLrM!-`I@EC7krG7avA58k;?Li`vf=Z0t^5;3axiTrXNBzEmx zf&@B7l@aKqOC7}+u1S3kV#1(+glwdv=aoG^J7WR8v00t=;B{LNkNw16m7H2Phjikg zy6xyVE_U|RxUL@Q+Jy!?evn6&!gskDjt9nm3Swao!nenZPBd(qs_f~LFT&)qGrZOq z&z|Hxb9NWMbrXcytJA#c>GZUlyx&wn9;q5R9((=8w^kS5Uz5xP_-F#cnWm5w@vtq4 z))m~Oh|htG4^JPXm}9Q<^wAc<;|o^_<7i;BSu1@45<3uxYQzXXP_!}@`8fjz@5ns~ zkf&F{{d0RDsOQca=wmM`5uF#*U9fHtGcs@vSEOLQz?fK^ z++L?SnSvavDpW%e~qR8<{t!jel)FRx68%J2b1^q0=IflngcBdv7 z@2SwkU0Qd+CWs&f9I|)xSYPbUi_2XN|!at!XBJ&N&DdUiX zAx1S}DTrIaJnm*ckWsar$I#=0^y>C#p#Y8+GRhHGj$VxXIN6RtG>_K$yK>gb990)~anKzO}DJeUi*14kk$FT%Vh1 zM;&X*wo^ar#WoVWiBnpFwD$D!jx@sJleh!VtQvWSjmumi}z?{T8{DJR$Kc{y#;6pz`-Utx3p~a)dG|Bpe1OqFp_0tem7~ou z_jwK@%wMs|DVK3$YsD_P)H!7Ou{pXd3xp7;?byZxWdj0^QE?=5Glqm>J=`3LS9fR3 z*|BttR->W>6y%a zU+a0Aki@nw6FyGSbbJNzTeKLG)r@)H@LMWCl?#5nOfj9^bEZ4B#jn+zM*4 z-8knO!-@D$1J*tD{MhwN=3J?}fC<_~wWFI3>*N_%%|{gLlU^ws?4Q|w5FzVM>0s~S z&6g)AQ-#PSLBHbyyG3Qr;mg(WjxVh%!*(dlt)6xPDD!k84T%9xd@E=x{90ea@DphU z6MP~Yay#7kZ)__{-$r`E#K3}l$Z9-4U>TXDi$FDRjW-ohQ0KRIln0p&?stTF+`Xrq zkMp92Gq=8fbb;ao*nB_nrUfpO34UYJ;d>Vr^%)|km6EEF;J~f`nduB{&mN??sKSlu z(j1@6$2qu;k$h`0!}F{1D0!lzYviUyyWq8*%yOO0GEyzNr3k)y?A{&_WWT1&&c$un z6Hf^QQ*JweoqVVZbUT!ZI95g3{zPVxwY79~z&tl*KD;|VIOhIm9qdB7g1yBfu5|By zYoAn8(KW6(JFyCnvE`P8AD5M_rKQJidx8=HVN!;_y7c%s4J*yF!2+s<5_F`Ii2c^MrG<#j_#hy zv!oAJHv&&>Y1$|8@x*j~x&n2n`t<&!m;9ru)2As;XVs?n=jQfh?g#sH8}|pw>rX3;;if8^GM(#kARWL_{TpsaPh3a0FBD# zIzYC?c@}W0iisXl@$BM%!K$}{uyJc2{uVCEw;XWCOyG5B@l?WP$0{?@Easw8H{h5fKo!{#G74@|l z#D~o4BPTWXM!}=^Plyqq)(wy9PbojRnr}gW!G^^hb~)dgx1QTP%{Xt1tFEh;oTHbn zexl#ljr#a>6p#X7zxID)#iZ!j0YR<`&Qc44+{fHWGKkRUQEitt~ zf5{Rep%^YwfE8uWqxE#}By-nROdeU|>Ilhm$_oi`A+XxqaNckm#wlv>lsi%Aj13d< z`)@DF7iOdzu)3nB6g-{t_UgG@bRQ4v`s=@?zIIi5VOSS}1S#fpQ%8^FBFPMGduU@L z>zA3+(wUGCM6KJaM(P-;|Lk5jG&v?HTO-e9*K@>ZTG{n%X&CPvy3EO4b~7oV{^b+W z!5wdO9w+7-u`4gwQ4`L{PWs-BL(55zjEdtKZ0c$FoECLViP z#H}C+WBPC0-8`44-9F-~{FTLKarB93tEWI#rk{ZWQAYsulH9Jb4=Ebei6=AcUs*_zAu_FyA!PY!d!M1b3SiJo;fSwf-ab`RDdTq z(@gS?;!p?sA#r5;SclfnMk$&zBbyuPLM-36JA)9K-jFA9<%T^zKJ~JKYaQvL5%lvA3<5q6=}*Cf!9528Czv=3 z;Fte`>X4iw6u5vLX`#+m`XBxuyfW*?#h`jsXRfEGe-vcn&~sm&9O9ERKAdEQFV5=V z=6{!K8g_3U=VFv@_nj$Sc1R%(SZc^C>5}bcvW)~5?0;uyif%Sv>&954-`Jldj2Ue< zSLxlDl?)ezQ`bI+AtIR=NyT!>jvTB}J6oA!r^uwqE-X(6u@~`?VaO%_T!VVLdE2QR zyS@4P8vH9=Uaf1OUtay_htGHOF|!$ftTWO_`ChjTzuzB=#VGiTKJLHdM<5dPhr`i8 zFdU=&flxRS`wVI z4EoXhhl2tCk0}3-zJt#{@%hJjl7IBW=b!4wx&HTzEFhI~ynNL*o?ZWeDB|@I!v6!o zP%shz{vQp8epvtC#pfqfJ6~u~=wGr5xyNcbx=JHOn$A$!LYaP`GBv7NYg2Tl&?ZIU zF@+2`I^8Z09gRCGG7z|a zyfV*XQrG&kE887l*I=tE$XJ!x8C*58e-NbAio?$6vAFV`0`BuVetHSm2X%G;&|r+u zWKP{v%AAP=g5JTLXXNN6(QC{)k}uE3Qj^)Cc8K%qN+YgxJd0&|9qaAlmHjv}$QH{Z znvL-@jq`P5xxhLV$Q1X>d@HwwEyNa+iTVfrdZ&R@Sc;mk5-Xr(HRf355!*czRz*s@ zm*+-f>iXxH<6h$zc$|>bd$;#ORyGyAj##f(%#!sYyAeGNJp_!Wc$iZbEHeNWeTW&) zC0BifH3*L*&jRF`%&lZ*iXr+Rg4en)YXy#u>z~ISLJtx04BWNXKaZkjVRF!fnu#NQ zaQsLVAPoTQY^bdK)h|f*9P_<~`X?FNPYq0_5G^U?l-^W<`Q zX*nvhF{vwdXk|G&L&L_Hf%>kg>o?aH8sO1aZ?5khWxo39+khXFq7yOUhs39ng$2}D ziQS4hd>n8-xV_&$?WE< zfQ#7$9zKoN5in@r=G7-xrBA0*Xzn+dLh-O^6mv@)SgJ?+cwJspoyO9skG ztsP%2S50lR^;DDT7Q^=GhNqezce63MR^_AXF|+H2IvTs$GTWsm=4lnn+cOiq;#RCV zy+MThP*`WL>`YWS1|o5<{fG~TVAg?)dtp!HtEdAhQG?xHjuaCV;Klsm#gNXTra7!` zt7ASJ^Hf^C_$V`r`I*%$6rAkYVQ#qNGFoFb+?C2nzOj%)i`>4v^Mp&wWhCCI#WKOh zWR@*7(&9#IX+ydtu+e5N+CMMYMjDbYVHmiea9(^L!dFDxI7V*@pvy;Z!h_M9KfD;y z+0n1HVx!NuTb*1vSk5O}oxT|FZk0h;5gB1V6Qkv7YZVXEfpTh0$CXgczm+Cj^RRCw z4!+^ASm7(ZO(?dm)kXWAzZyT$WsBmC^E3CBGUmvvxMQ%XAAV zUKN*opY9eKzG)y_D8~zxVnh-WdG-*Xw|IRcy=jZ+e9odTdsh9549_(>|EG@51r^5W zdgR4W&yG0F`POuGwap94KuqjPg>k1?jg;9;X;O?f1H-@~(e)K0+xZYOn^>0&i6OiEmy8?9%U zI<|^eQFu^QaRzi)2B*gQi5$)a6^842ghzay+f|1pcAZF0_l*tBR{5&`&>AF}p1ev; zkpvGF=yGK^ZA!&%ZzfChNTnrVu{o?$^{HpI6AQt3tSHW#8Ike$1ZBEjbHi0Oibp&O zj@>bRFzXb6QpkX)T}6uS7>otyr#z-HBEo zblK`EGuUa7S|=IsjZ@Q&9BdX^u?-gumM7JPPm`B{`E<32M`MN1T8(PM7-n4~hG&E^ zJMpeyJBM&E|B(&$X;bHf2?KOIz~pUJWEyqrOrK@0oQcrY^WV zKiV}j!2%_P(JCxxBrS9fR;OHHY_Z7}t1fiJY^;(XAF)VE};h^ydO zW#y|k_mur7VrUW2TwKp$M@%mMiA&Z75F^6;MHKk6SP*xuQ7px|ghQ%tYw%5B7(@4tYQoz5F zR;6HEl>MIOwzr`9NOS4&?Q)^*Qd%uHo}N5TtwQTSYMU?d!n}WI#%rP2a=cld)j(|6 z#?~;J;cM#!zL9~Lx_;YZlZ}J*Y%j0LJboH9EaNGosNN|z6|q1F!DE745g|8pgj^5{ z!B0o7Xd+isycot= z@7~a}vR>U!d%&kX9vlCN-0KW-CeSy zRppqf?oOEHvq`SI7Xpn^Uuxz2srWhjmGo#w=pJjYSH@cSiYx^e7D6v!j|iH9 z0`%33p`0E6Cb;MQZGjoKC$zxw%aYn3<^*JGb))NKJH-oCty7Wnd~}m2uU7IrvDzk@ zgMDFD@5FrJCNtMot!<+fSOྃn*$D>8@SvqhsiYt0Nux5MuLWdr3ldpb(HSc@MeDE3?zX8k~DmVFv zhR^5NP*{x8`9)$MDyd4M86FN4WYKt{)mSCXRQN(R8OjEl!CJ((ik1A))$do*xlkAA9NPGG^P@E337Sxr2dOXX|1 z$hs~h*2`5X_;5@R7q((wvw9SU*(X&gNB;kRvMhpdj6OiG_>Dq$dkp8DE zyf8tJIN2liAS}2|P}sK+N3Ou}S}lN}dxc%)0XlvFo4N%4jD#i(W|f zcY2X=pb^Wpw>&SY{q1}+SkZ^<{7{(=6Zu%S(NjIzkgYF<^+l@g>jj4Nei%@xGcXkI&A$!;*7)LeR{N85O;eU}|)=39m9QyVe-qi{l^}pb~Dy>KlH? z+kKygy|eHEk!N3$9l*0FJv5~bm)TByyJa+)S=TGcb}qgdC%SccIbO!v{#dQB^Us9E zs+bh=Lc80qHgciObknP9+_13RYKf9unj`~Uu{2et?e30_R*8JYUHXbJ9hcOJy=d&# zI5C8?W1{opeW9Legj1~?zgeUL(PgAGOBRE%bV11ve8FOBm2Ng!F&y6~uF3BIDA+hmrl!(z-7Cp%gbsu5p*vsALh zpi)U?r|T$ELH6@T*B2c5O4}?O42&z%DzDaSEp8DnB@(`EYbw%OyEa;Bm2)(MCe7gY zm}YdYVjMuiz1JxRENwC(Q4=QTa2I|7?f{;NF8F~l6h4a~&t6F+Vx2@3Dbb~!FPV~3 z>GeFBptW6*Y4kFUSuB`{qOF5IR0}UupAA ziD|KmbIkY5BggTF-MbST6 zADBJADym(fIydZzO4Q90L8~F$2xG$Kig?2@HUJ$aYd8yJ7=wf3r)K;Zf zsh`WvJNtN|E-S?z&n#12ve@)7y@qY#9@F!7$*3T%)J!%~%1IM(wk%H)T}9et56eVp zD(&|Hb1Z;# z44=;hRSc8mmGw~fPT$+geMO3WEWMvDV6vJPi=vB@A&n>BL%SyE{v zEgB%nFJRD#BbR*QMI=BVnqwqCy%^frQ7Ut}%WNB2aUl#h(Oo6%XM|liy%aNb&vdA4 zeM45JD@xBN?GH+GMDvnT_GMDtUThtrce|~oOsf4ti0@0Y9WIxX_sh8_&88+=q`<~%#VseK zXN`e>G>+xc&D}n->*n{KmB_4@QrWjyH`e}&+NiOEvAm`aVP-UrO#LOb6YGVO&(6Ni zmvkFEaBX`<2o{q@f(SL!
    e#3=nQV3Y7YhK0EU4h8?!6T5jtPk9T~M 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 A#{d8T literal 0 HcmV?d00001 diff --git a/dental-server/auth.js b/dental-server/auth.js index cee2822..d015d0d 100644 --- a/dental-server/auth.js +++ b/dental-server/auth.js @@ -60,14 +60,15 @@ async function verifyPassword(password, hash) { // GERAR TOKEN JWT // ================================================================ -function generateToken(user) { +function generateToken(user, expiresIn = JWT_EXPIRES_IN) { return jwt.sign( { id: user.id, - username: user.username + username: user.username, + is_admin: user.is_admin }, JWT_SECRET, - { expiresIn: JWT_EXPIRES_IN } + { expiresIn: expiresIn } ); } @@ -90,8 +91,8 @@ async function createInitialUser() { // Inserir usuário await db.run( - `INSERT INTO users (username, email, password_hash, created_at) - VALUES (?, ?, ?, NOW())`, + `INSERT INTO users (username, email, password_hash, is_admin, created_at) + VALUES (?, ?, ?, 1, NOW())`, ['rcesar', 'rcesar@rcesar.com', passwordHash] ); diff --git a/dental-server/database.js b/dental-server/database.js index 9a4235c..8da67c8 100644 --- a/dental-server/database.js +++ b/dental-server/database.js @@ -1,4 +1,4 @@ -const { Pool } = require('pg'); +const mysql = require('mysql2/promise'); const fs = require('fs').promises; const path = require('path'); @@ -14,15 +14,18 @@ function rewriteQuery(sql) { // Replace NOW() with datetime('now') query = query.replace(/\bNOW\(\)/gi, "datetime('now')"); + // Rewrite ON DUPLICATE KEY UPDATE for users + if (query.includes('ON DUPLICATE KEY UPDATE')) { + query = `INSERT INTO users (username, email, password_hash, created_at) + VALUES (?, ?, ?, datetime('now')) + ON CONFLICT(username) DO UPDATE SET + email = excluded.email, + password_hash = excluded.password_hash`; + } + return query; } -// Convert MySQL ? to Postgres $1, $2 -function convertSqlForPg(sql) { - let i = 1; - return sql.replace(/\?/g, () => '$' + (i++)); -} - // ================================================================ // INICIALIZAR BANCO DE DADOS // ================================================================ @@ -41,18 +44,13 @@ async function initSQLite() { username TEXT UNIQUE NOT NULL, email TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, + clinic_name TEXT, + pc_name TEXT, + is_admin INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); `); - sqliteDb.exec(` - CREATE TABLE IF NOT EXISTS settings ( - key TEXT PRIMARY KEY, - value TEXT NOT NULL - ); - `); - console.log('✅ Tabela settings SQLite criada/verificada'); - sqliteDb.exec(` CREATE TABLE IF NOT EXISTS images ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -71,7 +69,6 @@ async function initSQLite() { flip_vertical INTEGER DEFAULT 0, doctor TEXT, remark TEXT, - thumb_filename TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); `); @@ -100,16 +97,24 @@ async function initSQLite() { // Migrações para adicionar colunas se o banco já existia try { sqliteDb.exec("ALTER TABLE images ADD COLUMN doctor TEXT"); - } catch (e) {} + } catch (e) { + // Coluna já existe + } + try { sqliteDb.exec("ALTER TABLE images ADD COLUMN remark TEXT"); - } catch (e) {} - try { - sqliteDb.exec("ALTER TABLE images ADD COLUMN thumb_filename TEXT"); - } catch (e) {} + } catch (e) { + // Coluna já existe + } console.log('✅ Tabelas SQLite users e images criadas/verificadas'); + // Migrações para adicionar colunas em users se o banco já existia + try { sqliteDb.exec("ALTER TABLE users ADD COLUMN clinic_name TEXT"); } catch (e) {} + try { sqliteDb.exec("ALTER TABLE users ADD COLUMN pc_name TEXT"); } catch (e) {} + try { sqliteDb.exec("ALTER TABLE users ADD COLUMN is_admin INTEGER DEFAULT 0"); } catch (e) {} + + // Criar administrador padrão se não houver usuários try { const stmtCount = sqliteDb.prepare('SELECT COUNT(*) as count FROM users'); const userCount = stmtCount.get().count; @@ -118,10 +123,10 @@ async function initSQLite() { const bcrypt = require('bcrypt'); const passwordHash = bcrypt.hashSync('admin1234', 10); const stmtInsert = sqliteDb.prepare(` - INSERT INTO users (username, email, password_hash) - VALUES (?, ?, ?) + INSERT INTO users (username, email, password_hash, clinic_name, pc_name, is_admin) + VALUES (?, ?, ?, ?, ?, 1) `); - stmtInsert.run('admin', 'admin@dental.local', passwordHash); + stmtInsert.run('admin', 'admin@dental.local', passwordHash, 'Admin Clinic', 'Admin PC'); console.log('✅ Usuário admin padrão criado com sucesso!'); } } catch (error) { @@ -138,185 +143,250 @@ async function initDatabase() { try { const dbConfig = { host: process.env.DB_HOST || 'localhost', - port: process.env.DB_PORT || 5432, - user: process.env.DB_USER || 'postgres', + port: process.env.DB_PORT || 3306, + user: process.env.DB_USER || 'root', password: process.env.DB_PASSWORD || '', }; const dbName = process.env.DB_NAME || 'dental_images'; - const tempPool = new Pool({ + // Criar pool temporário SEM database para poder criar o banco + const tempPool = mysql.createPool({ ...dbConfig, - database: 'postgres', + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0 }); + console.log('✅ Pool de conexões MySQL criado'); + + // Verificar se banco existe, se não criar try { - const res = await tempPool.query(`SELECT 1 FROM pg_database WHERE datname = $1`, [dbName]); - if (res.rowCount === 0) { + const [databases] = await tempPool.query( + `SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?`, + [dbName] + ); + + if (databases.length === 0) { console.log(`⚠️ Banco ${dbName} não encontrado. Criando...`); - await tempPool.query(`CREATE DATABASE "${dbName}"`); + await tempPool.query(`CREATE DATABASE ${dbName} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`); console.log(`✅ Banco ${dbName} criado!`); } else { console.log(`✅ Banco ${dbName} já existe`); } } catch (error) { - console.error('❌ Erro ao verificar/criar banco no PostgreSQL:', error.message); - } finally { - await tempPool.end(); + console.error('❌ Erro ao verificar/criar banco:', error); + throw error; } - pool = new Pool({ + // Fechar pool temporário + await tempPool.end(); + + // Criar pool definitivo COM database + pool = mysql.createPool({ ...dbConfig, database: dbName, + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0, + enableKeepAlive: true, + keepAliveInitialDelay: 0 }); - const client = await pool.connect(); - console.log('✅ Conectado ao PostgreSQL'); - client.release(); + // Testar conexão + const connection = await pool.getConnection(); + console.log('✅ Conectado ao MySQL'); + connection.release(); + // Criar tabelas await createTables(); } catch (error) { - console.error('❌ Erro ao conectar ao PostgreSQL:', error.message); + console.error('❌ Erro ao conectar ao MySQL:', error.message); + console.error('Verifique:'); + console.error('1. MySQL está rodando? (sudo systemctl status mysql)'); + console.error('2. Credenciais corretas no .env?'); + console.error('3. Usuário tem permissão para criar databases?'); throw error; } } // ================================================================ -// CRIAR TABELAS POSTGRESQL +// CRIAR TABELAS MYSQL // ================================================================ async function createTables() { - const client = await pool.connect(); + const connection = await pool.getConnection(); try { // Tabela de usuários - await client.query(` + await connection.query(` CREATE TABLE IF NOT EXISTS users ( - id SERIAL PRIMARY KEY, + id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, password_hash TEXT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) + clinic_name VARCHAR(100) NULL, + pc_name VARCHAR(50) NULL, + is_admin TINYINT(1) DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `); - - // Tabela de configurações - await client.query(` - CREATE TABLE IF NOT EXISTS settings ( - key VARCHAR(100) PRIMARY KEY, - value TEXT NOT NULL - ) - `); - console.log('✅ Tabela settings PostgreSQL criada/verificada'); console.log('✅ Tabela users criada/verificada'); // Tabela de imagens - await client.query(` + await connection.query(` CREATE TABLE IF NOT EXISTS images ( - id SERIAL PRIMARY KEY, + id INT AUTO_INCREMENT PRIMARY KEY, image_guid VARCHAR(255) NOT NULL, patient_name TEXT, client_name VARCHAR(100), original_filename TEXT, filename TEXT NOT NULL, - enabled SMALLINT DEFAULT 1, + enabled TINYINT(1) DEFAULT 1, tooth_number VARCHAR(10), tooth_side VARCHAR(10), patient_name_resumed VARCHAR(50), original_image_id INT, rotation INT DEFAULT 0, - flip_horizontal SMALLINT DEFAULT 0, - flip_vertical SMALLINT DEFAULT 0, + flip_horizontal INT DEFAULT 0, + flip_vertical INT DEFAULT 0, doctor VARCHAR(255) NULL, remark TEXT NULL, - thumb_filename VARCHAR(500) NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + INDEX idx_images_enabled (enabled), + INDEX idx_images_original (original_image_id), + INDEX idx_patient_name (patient_name(100)), + INDEX idx_image_guid (image_guid), + INDEX idx_client_name (client_name), + INDEX idx_created_at (created_at) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `); - - await client.query(`CREATE INDEX IF NOT EXISTS idx_images_enabled ON images (enabled)`); - await client.query(`CREATE INDEX IF NOT EXISTS idx_images_original ON images (original_image_id)`); - await client.query(`CREATE INDEX IF NOT EXISTS idx_patient_name ON images (patient_name)`); - await client.query(`CREATE INDEX IF NOT EXISTS idx_image_guid ON images (image_guid)`); - await client.query(`CREATE INDEX IF NOT EXISTS idx_client_name ON images (client_name)`); - await client.query(`CREATE INDEX IF NOT EXISTS idx_created_at ON images (created_at)`); - console.log('✅ Tabela images criada/verificada'); // Tabela GTOs - await client.query(` + await connection.query(` CREATE TABLE IF NOT EXISTS gtos ( - id SERIAL PRIMARY KEY, + id INT AUTO_INCREMENT PRIMARY KEY, gto_number VARCHAR(100) NOT NULL, patient_name VARCHAR(255) NOT NULL, description TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + INDEX idx_gtos_patient (patient_name) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `); - await client.query(`CREATE INDEX IF NOT EXISTS idx_gtos_patient ON gtos (patient_name)`); console.log('✅ Tabela gtos criada/verificada'); // Tabela de vínculo GTO-Images - await client.query(` + await connection.query(` CREATE TABLE IF NOT EXISTS gto_images ( - id SERIAL PRIMARY KEY, + id INT AUTO_INCREMENT PRIMARY KEY, gto_id INT NOT NULL, image_id INT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (gto_id) REFERENCES gtos(id) ON DELETE CASCADE, - FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE - ) + FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, + UNIQUE INDEX idx_gto_image (gto_id, image_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `); - await client.query(`CREATE UNIQUE INDEX IF NOT EXISTS idx_gto_image ON gto_images (gto_id, image_id)`); console.log('✅ Tabela gto_images criada/verificada'); + // Verificar e adicionar colunas se não existirem (migração) try { - await client.query(`ALTER TABLE images ADD COLUMN client_name VARCHAR(100)`); - await client.query(`CREATE INDEX idx_client_name ON images (client_name)`); - console.log('✅ Coluna client_name adicionada'); - } catch (e) { /* já existe */ } - - try { - await client.query(`ALTER TABLE images ADD COLUMN doctor VARCHAR(255)`); - } catch (e) { /* já existe */ } - - try { - await client.query(`ALTER TABLE images ADD COLUMN remark TEXT`); - } catch (e) { /* já existe */ } - - try { - await client.query(`ALTER TABLE images ADD COLUMN thumb_filename VARCHAR(500)`); - console.log('✅ Coluna thumb_filename adicionada'); - } catch (e) { /* já existe */ } - - // Inserir usuários padrões se a tabela estiver vazia - try { - const userCountRes = await client.query('SELECT COUNT(*) as count FROM users'); - const userCount = parseInt(userCountRes.rows[0].count, 10); - if (userCount === 0) { - console.log('⚠️ Nenhum usuário encontrado no PostgreSQL. Criando usuários padrões...'); - const bcrypt = require('bcrypt'); - const hash1 = await bcrypt.hash('Rc362514', 10); - const hash2 = await bcrypt.hash('admin1234', 10); - - await client.query( - `INSERT INTO users (username, email, password_hash) VALUES ($1, $2, $3)`, - ['rcesar', 'rcesar@rcesar.com', hash1] - ); - await client.query( - `INSERT INTO users (username, email, password_hash) VALUES ($1, $2, $3)`, - ['admin', 'admin@dental.local', hash2] - ); - console.log('✅ Usuários padrões (rcesar e admin) criados com sucesso no PostgreSQL!'); + const columns = await connection.query(` + SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'images' + AND COLUMN_NAME = 'client_name' + `); + + if (columns[0].length === 0) { + console.log('⚠️ Coluna client_name não encontrada. Adicionando...'); + await connection.query(` + ALTER TABLE images + ADD COLUMN client_name VARCHAR(100) NULL + AFTER patient_name + `); + await connection.query(` + ALTER TABLE images + ADD INDEX idx_client_name (client_name) + `); + console.log('✅ Coluna client_name adicionada'); } - } catch (e) { - console.error('⚠️ Erro ao inicializar usuários padrões no PostgreSQL:', e.message); + } catch (error) { + console.warn('⚠️ Erro ao verificar/adicionar coluna client_name:', error.message); + } + + // Migração para coluna doctor + try { + const columnsDoctor = await connection.query(` + SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'images' + AND COLUMN_NAME = 'doctor' + `); + if (columnsDoctor[0].length === 0) { + console.log('⚠️ Coluna doctor não encontrada. Adicionando...'); + await connection.query(` + ALTER TABLE images + ADD COLUMN doctor VARCHAR(255) NULL + AFTER client_name + `); + } + } catch (error) { + console.warn('⚠️ Erro ao verificar/adicionar coluna doctor:', error.message); } + // Migração para coluna remark + try { + const columnsRemark = await connection.query(` + SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'images' + AND COLUMN_NAME = 'remark' + `); + if (columnsRemark[0].length === 0) { + console.log('⚠️ Coluna remark não encontrada. Adicionando...'); + await connection.query(` + ALTER TABLE images + ADD COLUMN remark TEXT NULL + AFTER doctor + `); + } + } catch (error) { + console.warn('⚠️ Erro ao verificar/adicionar coluna remark:', error.message); + } + + // Migração para colunas da tabela users (clinic_name, pc_name, is_admin) + try { + const columnsUsers = await connection.query(` + SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'users' + AND COLUMN_NAME = 'clinic_name' + `); + if (columnsUsers[0].length === 0) { + console.log('⚠️ Colunas de clínica em users não encontradas. Adicionando...'); + await connection.query(` + ALTER TABLE users + ADD COLUMN clinic_name VARCHAR(100) NULL AFTER password_hash, + ADD COLUMN pc_name VARCHAR(50) NULL AFTER clinic_name, + ADD COLUMN is_admin TINYINT(1) DEFAULT 0 AFTER pc_name + `); + // O primeiro usuário (admin padrão) deve virar admin se já existe + await connection.query(`UPDATE users SET is_admin = 1 ORDER BY id ASC LIMIT 1`); + } + } catch (error) { + console.warn('⚠️ Erro ao verificar/adicionar colunas de clínica em users:', error.message); + } + } catch (error) { console.error('Erro ao criar tabelas:', error); } finally { - client.release(); + connection.release(); } } @@ -339,9 +409,8 @@ function get(query, params = []) { return new Promise(async (resolve, reject) => { try { - const pgQuery = convertSqlForPg(query); - const res = await pool.query(pgQuery, params); - resolve(res.rows[0] || null); + const [rows] = await pool.query(query, params); + resolve(rows[0] || null); } catch (error) { reject(error); } @@ -363,9 +432,8 @@ function all(query, params = []) { return new Promise(async (resolve, reject) => { try { - const pgQuery = convertSqlForPg(query); - const res = await pool.query(pgQuery, params); - resolve(res.rows); + const [rows] = await pool.query(query, params); + resolve(rows); } catch (error) { reject(error); } @@ -390,25 +458,10 @@ function run(query, params = []) { return new Promise(async (resolve, reject) => { try { - let pgQuery = convertSqlForPg(query); - - // Auto-append RETURNING id for INSERT if not present (except for settings table) - if (pgQuery.trim().toUpperCase().startsWith('INSERT') && - !pgQuery.toUpperCase().includes('RETURNING') && - !pgQuery.toUpperCase().includes('SETTINGS')) { - pgQuery += ' RETURNING id'; - } - - const res = await pool.query(pgQuery, params); - - let lastID = null; - if (res.rows && res.rows.length > 0 && res.rows[0].id) { - lastID = res.rows[0].id; - } - + const [result] = await pool.query(query, params); resolve({ - lastID: lastID, - changes: res.rowCount + lastID: result.insertId, + changes: result.affectedRows }); } catch (error) { reject(error); diff --git a/dental-server/package-lock.json b/dental-server/package-lock.json index 8c0bc80..fef42e3 100644 --- a/dental-server/package-lock.json +++ b/dental-server/package-lock.json @@ -16,7 +16,7 @@ "express": "^4.18.2", "express-session": "^1.17.3", "jsonwebtoken": "^9.0.2", - "pg": "^8.11.3", + "mysql2": "^3.6.0", "sharp": "^0.32.6", "socket.io": "^4.7.2" }, @@ -157,6 +157,15 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/b4a": { "version": "1.7.3", "resolved": "https://registry.npmmirror.com/b4a/-/b4a-1.7.3.tgz", @@ -596,6 +605,15 @@ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "license": "MIT" }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", @@ -991,6 +1009,15 @@ "node": ">=10" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -1226,6 +1253,12 @@ "node": ">=8" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -1317,6 +1350,36 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz", @@ -1501,6 +1564,54 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/mysql2": { + "version": "3.15.3", + "resolved": "https://registry.npmmirror.com/mysql2/-/mysql2-3.15.3.tgz", + "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/napi-build-utils": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz", @@ -1656,122 +1767,6 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, - "node_modules/pg": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", - "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", - "dependencies": { - "pg-connection-string": "^2.13.0", - "pg-pool": "^3.14.0", - "pg-protocol": "^1.14.0", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.4.0" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", - "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", - "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", - "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", - "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", - "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -2024,6 +2019,11 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmmirror.com/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz", @@ -2322,12 +2322,13 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", "engines": { - "node": ">= 10.x" + "node": ">= 0.6" } }, "node_modules/statuses": { @@ -2588,14 +2589,6 @@ } } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", diff --git a/dental-server/package.json b/dental-server/package.json index 921def9..149b0e0 100644 --- a/dental-server/package.json +++ b/dental-server/package.json @@ -19,15 +19,13 @@ "socket.io": "^4.7.2", "express": "^4.18.2", "sharp": "^0.32.6", - "pg": "^8.11.3", + "mysql2": "^3.6.0", "dotenv": "^16.3.1", "cors": "^2.8.5", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", "cookie-parser": "^1.4.6", - "express-session": "^1.17.3", - "@aws-sdk/client-s3": "^3.500.0", - "@aws-sdk/lib-storage": "^3.500.0" + "express-session": "^1.17.3" }, "engines": { "node": ">=16.0.0", diff --git a/dental-server/public/app.js b/dental-server/public/app.js index f706ea7..ef08e9c 100644 --- a/dental-server/public/app.js +++ b/dental-server/public/app.js @@ -39,6 +39,7 @@ function getAuthToken() { } let authVerificationInProgress = false; +let currentUser = null; async function verifyAuth() { if (authVerificationInProgress) return false; @@ -52,7 +53,14 @@ async function verifyAuth() { if (!response.ok) { localStorage.removeItem('auth_token'); window.location.href = '/login'; return false; } const data = await response.json(); authVerificationInProgress = false; - if (data.valid) return true; + if (data.valid) { + currentUser = data.user; + if (currentUser && currentUser.is_admin) { + const navAdmin = document.getElementById('navAdminClinics'); + if (navAdmin) navAdmin.style.display = 'flex'; + } + return true; + } localStorage.removeItem('auth_token'); window.location.href = '/login'; return false; } catch { localStorage.removeItem('auth_token'); @@ -98,14 +106,13 @@ let clientsList = []; let selectedClient = ''; let showDisabled = false; let currentTransformations = []; -let fineTuneAngle = 0; // ================================================================ // DOM REFS // ================================================================ const searchInput = document.getElementById('searchInput'); -const clientFilter = document.getElementById('clientFilter'); // Pode ser null se removido do header +const clientFilter = document.getElementById('clientFilter'); const toggleDisabledBtn = document.getElementById('toggleDisabledBtn'); const toggleText = document.getElementById('toggleText'); const imagesGrid = document.getElementById('imagesGrid'); @@ -150,7 +157,6 @@ document.addEventListener('DOMContentLoaded', async () => { await loadClientsList(); loadPatients(); setupEventListeners(); - initDragAndDrop(); setInterval(loadClientsList, 5000); } catch (error) { _log.error('INIT', 'Erro fatal na inicialização:', error.message, error); @@ -170,12 +176,10 @@ function setupEventListeners() { else loadPatientImages(selectedPatient.patient_name); }, 400)); - if (clientFilter) { - clientFilter.addEventListener('change', e => { - selectedClient = e.target.value; - if (view === 'patients') loadPatients(); - }); - } + clientFilter.addEventListener('change', e => { + selectedClient = e.target.value; + if (view === 'patients') loadPatients(); + }); toggleDisabledBtn.addEventListener('click', () => { showDisabled = !showDisabled; @@ -188,14 +192,10 @@ function setupEventListeners() { // Transform modal close on backdrop transformModal.addEventListener('click', e => { - if (e.target === transformModal) { - transformModal.classList.remove('active'); - if (typeof clearTransformSelection === 'function') clearTransformSelection(); - } + if (e.target === transformModal) transformModal.classList.remove('active'); }); document.getElementById('closeTransformModal').addEventListener('click', () => { transformModal.classList.remove('active'); - if (typeof clearTransformSelection === 'function') clearTransformSelection(); }); } @@ -227,85 +227,22 @@ async function loadClientsList() { } function updateClientsDropdown() { - // Se por acaso o elemento clientFilter ainda existir no DOM em alguma outra tela, atualiza ele - if (clientFilter) { - const current = clientFilter.value; - clientFilter.innerHTML = ''; - const sorted = [...clientsList].sort((a, b) => { - const o = { identified: 1, connected: 2, historic: 3 }; - return (o[a.status] || 99) - (o[b.status] || 99); - }); - sorted.forEach(c => { - const opt = document.createElement('option'); - opt.value = c.name; - let lbl = c.name; - if (c.name === 'Upload Web') { - lbl = 'Upload Web 💻 (Imagens do Painel)'; - } else { - if (c.status === 'identified') lbl += ' ✅'; - else if (c.status === 'historic') lbl += ' 📚'; - } - opt.textContent = lbl; - clientFilter.appendChild(opt); - }); - clientFilter.value = current || selectedClient; - } - - // Renderizar o submenu dinâmico na sidebar - const submenu = document.getElementById('clientsSubmenu'); - if (!submenu) return; - + const current = clientFilter.value; + clientFilter.innerHTML = ''; const sorted = [...clientsList].sort((a, b) => { const o = { identified: 1, connected: 2, historic: 3 }; return (o[a.status] || 99) - (o[b.status] || 99); }); - - submenu.innerHTML = ''; - - // Adiciona o item "Todos os Clientes" - const allItem = document.createElement('div'); - allItem.className = `nav-item${selectedClient === '' ? ' active' : ''}`; - allItem.innerHTML = `📋 Todos os Clientes`; - allItem.onclick = () => selectClientFilter(''); - submenu.appendChild(allItem); - sorted.forEach(c => { - const item = document.createElement('div'); - item.className = `nav-item${selectedClient === c.name ? ' active' : ''}`; + const opt = document.createElement('option'); + opt.value = c.name; let lbl = c.name; - if (c.name === 'Upload Web') { - lbl = 'Upload Web 💻'; - } else { - if (c.status === 'identified') lbl += ' ✅'; - else if (c.status === 'historic') lbl += ' 📚'; - } - item.innerHTML = `🔹 ${escapeHtml(lbl)}`; - item.onclick = () => selectClientFilter(c.name); - submenu.appendChild(item); + if (c.status === 'identified') lbl += ' ✅'; + else if (c.status === 'historic') lbl += ' 📚'; + opt.textContent = lbl; + clientFilter.appendChild(opt); }); -} - -window.toggleClientsSubmenu = function() { - const submenu = document.getElementById('clientsSubmenu'); - const chevron = document.getElementById('clientsChevron'); - if (!submenu) return; - if (submenu.style.display === 'none') { - submenu.style.display = 'flex'; - if (chevron) chevron.style.transform = 'rotate(180deg)'; - } else { - submenu.style.display = 'none'; - if (chevron) chevron.style.transform = 'rotate(0deg)'; - } -} - -function selectClientFilter(clientName) { - selectedClient = clientName; - if (view === 'patients') { - loadPatients(); - } else { - showPatientsView(); - } - updateClientsDropdown(); + clientFilter.value = current || selectedClient; } // ================================================================ @@ -317,11 +254,6 @@ function showPatientsView() { selectedPatient = null; backBtn.style.display = 'none'; patientHeader.style.display = 'none'; - - // Restaurar título do header - const mainTitle = document.getElementById('mainTitle'); - if (mainTitle) mainTitle.textContent = 'Galeria de Imagens'; - loadPatients(); } @@ -417,7 +349,6 @@ function renderPatientCards() { return `
    -
    ${count} imagem${count !== 1 ? 'ns' : ''}
    @@ -442,7 +373,7 @@ function renderPatientCards() { cardW: cs.width, cardH: cs.height, previewH: pcs ? pcs.height : 'n/a', previewBg: pcs ? pcs.backgroundImage.substring(0, 60) : 'n/a' - }); + }); } else { _log.error('RENDER_CARDS', '❌ Nenhum card encontrado no DOM após render!'); } @@ -451,38 +382,6 @@ function renderPatientCards() { }); } -// Exclui todas as imagens de um determinado paciente (S3 e Local) -async function deletePatientImages(patientName) { - if (!patientName) return; - - // TODO(security): Usando confirm() para manter coerência com o restante das confirmações do painel do projeto - const ok = confirm(`⚠️ ATENÇÃO: Tem certeza que deseja excluir permanentemente todas as imagens do paciente "${patientName}"?\n\nEsta ação apagará os arquivos locais, do Wasabi S3 e do banco de dados, e NÃO poderá ser desfeita.`); - if (!ok) return; - - showToast('Excluindo imagens do paciente...', 'info'); - - try { - const res = await fetchWithAuth(`/api/images/by-patient?name=${encodeURIComponent(patientName)}`, { - method: 'DELETE' - }); - - if (!res.ok) { - const err = await res.json(); - throw new Error(err.error || 'Erro ao excluir imagens do paciente.'); - } - - showToast('✅ Todas as imagens do paciente foram excluídas!', 'success'); - - // Atualizar listagens - await loadPatients(); - await loadClientsList(); - - } catch (e) { - console.error('Erro na exclusão do paciente:', e); - showToast(`Falha ao excluir imagens: ${e.message}`, 'error'); - } -} - // ================================================================ // VIEW: PATIENT IMAGES // ================================================================ @@ -491,18 +390,9 @@ function openPatient(patient) { selectedPatient = patient; view = 'patient-images'; backBtn.style.display = 'inline-flex'; - - // Ocultar o patientHeader para manter o header na mesma altura de 70px - patientHeader.style.display = 'none'; + patientHeader.style.display = 'block'; - // Mudar o título principal para o nome do paciente - dentista const fullName = patient.patient_name || 'Sem nome'; - const doctorName = patient.doctor ? ` - ${patient.doctor}` : ''; - const mainTitle = document.getElementById('mainTitle'); - if (mainTitle) { - mainTitle.textContent = `${fullName}${doctorName}`; - } - patientTitle.textContent = fullName; patientMeta.textContent = `${patient.image_count || 0} imagem(ns) · ${patient.doctor || '—'}`; @@ -553,11 +443,9 @@ async function loadPatientImages(patientName) { } function renderPatientImages() { - imagesGrid.innerHTML = patientImages.map(image => { - const thumbUrl = image.thumb_filename ? `/uploads/${image.thumb_filename}` : `/uploads/${image.filename}`; - return ` + imagesGrid.innerHTML = patientImages.map(image => `
    -
    +
    📅 ${formatDate(image.created_at)}
    ${escapeHtml(image.image_guid || image.filename)}
    @@ -571,8 +459,8 @@ function renderPatientImages() {
    ${!image.enabled ? '
    Desabilitada
    ' : ''} -
    `; - }).join(''); +
    + `).join(''); } // ================================================================ @@ -626,32 +514,19 @@ async function showTransformModal(imageId) { `; try { - const image = patientImages.find(img => img.id === imageId); - if (!image) { - throw new Error('Imagem não encontrada localmente'); + const res = await fetchWithAuth(`/api/images/${imageId}/transformations`); + if (!res.ok) { + if (res.status === 401 || res.status === 403) { localStorage.removeItem('auth_token'); window.location.href = '/login'; return; } + throw new Error('Erro ao gerar transformações'); } - - if (typeof clearTransformSelection === 'function') clearTransformSelection(); - - // Se existir thumb_filename, usar ele; caso contrário, fallback para a imagem cheia - const imageUrl = image.thumb_filename ? `/uploads/${image.thumb_filename}` : `/uploads/${image.filename}`; - - const transformations = [ - { name: 'Original', rotation: 0, flipH: false, flipV: false, css: 'transform: none;' }, - { name: 'Flip Horizontal', rotation: 0, flipH: true, flipV: false, css: 'transform: scaleX(-1);' }, - { name: 'Flip Vertical', rotation: 0, flipH: false, flipV: true, css: 'transform: scaleY(-1);' }, - { name: 'Rotação +90°', rotation: 90, flipH: false, flipV: false, css: 'transform: rotate(90deg) scale(0.65);' }, - { name: 'Rotação -90°', rotation: -90, flipH: false, flipV: false, css: 'transform: rotate(-90deg) scale(0.65);' } - ]; + const transformations = await res.json(); currentTransformations = transformations; const container = document.getElementById('transformOptions'); container.innerHTML = transformations.map((t, i) => `
    -
    - ${escapeHtml(t.name)} -
    + ${escapeHtml(t.name)}
    ${escapeHtml(t.name)}
    `).join(''); @@ -659,21 +534,23 @@ async function showTransformModal(imageId) { // --------------------------------------------------------------- // Detectar orientação da imagem original → aplicar layout correto // --------------------------------------------------------------- - const probe = new Image(); - probe.onload = () => { - const orientation = probe.naturalWidth >= probe.naturalHeight ? 'landscape' : 'portrait'; - container.classList.remove('grid-landscape', 'grid-portrait'); - container.classList.add(`grid-${orientation}`); - _log.info('TRANSFORM', `Orientação detectada: ${orientation} (${probe.naturalWidth}×${probe.naturalHeight})`); - }; - probe.onerror = () => { - _log.warn('TRANSFORM', 'Não foi possível detectar orientação — usando fallback'); - }; - probe.src = imageUrl; + if (transformations.length > 0 && transformations[0].base64) { + const probe = new Image(); + probe.onload = () => { + const orientation = probe.naturalWidth >= probe.naturalHeight ? 'landscape' : 'portrait'; + container.classList.remove('grid-landscape', 'grid-portrait'); + container.classList.add(`grid-${orientation}`); + _log.info('TRANSFORM', `Orientação detectada: ${orientation} (${probe.naturalWidth}×${probe.naturalHeight})`); + }; + probe.onerror = () => { + _log.warn('TRANSFORM', 'Não foi possível detectar orientação — usando fallback'); + }; + probe.src = transformations[0].base64; + } } catch (e) { _log.error('TRANSFORM', '❌ Exceção:', e.message, e); - showToast('Erro ao carregar variações', 'error'); + showToast('Erro ao gerar variações', 'error'); transformModal.classList.remove('active'); } } @@ -702,24 +579,6 @@ function selectTransform(index) { el.classList.add('selected'); - // Reset fine tune angle when selecting a new transform - fineTuneAngle = 0; - updateFineTuneDisplay(); - - // Mover a legenda do card selecionado para o placeholder de ajuste fino - const nameEl = el.querySelector('.transform-name'); - const placeholder = document.getElementById('fineTuneLabelPlaceholder'); - if (nameEl && placeholder) { - placeholder.appendChild(nameEl); - } - - // Mover o contêiner de ajuste fino para dentro do card selecionado - const ftContainer = document.getElementById('fineTuneContainer'); - if (ftContainer) { - el.appendChild(ftContainer); - ftContainer.style.display = 'flex'; - } - // Show action area document.getElementById('transformActionArea').style.display = 'block'; @@ -734,48 +593,11 @@ function clearTransformSelection() { const container = document.getElementById('transformOptions'); container.classList.remove('selection-mode'); - // Devolver a legenda ativa para o card selecionado - const ftPlaceholder = document.getElementById('fineTuneLabelPlaceholder'); - if (ftPlaceholder) { - const activeLabel = ftPlaceholder.querySelector('.transform-name'); - const selectedEl = document.querySelector('.transform-option.selected'); - if (activeLabel && selectedEl) { - selectedEl.appendChild(activeLabel); - } - } - - // Mover o contêiner de ajuste fino de volta para o Wrapper e esconder - const ftContainer = document.getElementById('fineTuneContainer'); - if (ftContainer) { - const viewWrapper = document.getElementById('transformViewWrapper'); - if (viewWrapper) { - viewWrapper.appendChild(ftContainer); - } else { - document.body.appendChild(ftContainer); - } - ftContainer.style.display = 'none'; - } - - // Restore original transform styles on all images document.querySelectorAll('.transform-option').forEach(option => { option.classList.remove('selected'); option.style.display = 'block'; - - const optIndex = parseInt(option.getAttribute('data-index')); - const transform = currentTransformations[optIndex]; - const imgEl = option.querySelector('img'); - if (imgEl && transform) { - imgEl.style.transform = transform.css; - } - const nameEl = option.querySelector('.transform-name'); - if (nameEl && transform) { - nameEl.innerText = transform.name; - } }); - fineTuneAngle = 0; - updateFineTuneDisplay(); - document.getElementById('transformActionArea').style.display = 'none'; document.getElementById('newImageRemark').value = ''; } @@ -798,7 +620,7 @@ async function saveSelectedTransform() { try { const payload = { - rotation: transform.rotation + fineTuneAngle, + rotation: transform.rotation, flipH: transform.flipH, flipV: transform.flipV, remark: remark @@ -807,12 +629,11 @@ async function saveSelectedTransform() { const res = await fetchWithAuth(`/api/images/${selectedImageId}/transform`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(payload) + body: JSON.stringify({ rotation: transform.rotation, flipH: transform.flipH, flipV: transform.flipV }) }); if (!res.ok) throw new Error('Erro ao salvar'); transformModal.classList.remove('active'); - clearTransformSelection(); showToast('Orientação salva!', 'success'); if (selectedPatient) loadPatientImages(selectedPatient.patient_name); } catch (e) { @@ -821,53 +642,6 @@ async function saveSelectedTransform() { } } -function adjustFineTune(offset) { - const selectedEl = document.querySelector('.transform-option.selected'); - if (!selectedEl) return; - - const index = parseInt(selectedEl.getAttribute('data-index')); - const transform = currentTransformations[index]; - - fineTuneAngle += offset; - updateFineTuneDisplay(); - - // Update the image style using the combined transform - const imgEl = selectedEl.querySelector('img'); - if (imgEl) { - let totalRotation = transform.rotation + fineTuneAngle; - let transformStr = `rotate(${totalRotation}deg)`; - if (transform.flipH) transformStr += ` scaleX(-1)`; - if (transform.flipV) transformStr += ` scaleY(-1)`; - imgEl.style.transform = transformStr; - } - - // Update display name inside the card - const nameEl = selectedEl.querySelector('.transform-name'); - if (nameEl && transform) { - if (fineTuneAngle === 0) { - nameEl.innerText = transform.name; - } else { - nameEl.innerText = `${transform.name} (${fineTuneAngle > 0 ? '+' : ''}${fineTuneAngle}°)`; - } - } -} - -function resetFineTune() { - adjustFineTune(-fineTuneAngle); -} - -function updateFineTuneDisplay() { - const displayEl = document.getElementById('fineTuneValDisplay'); - if (displayEl) { - displayEl.textContent = `${fineTuneAngle > 0 ? '+' : ''}${fineTuneAngle}°`; - } - - const resetEl = document.getElementById('btnResetFineTune'); - if (resetEl) { - resetEl.style.display = fineTuneAngle !== 0 ? 'inline-block' : 'none'; - } -} - async function toggleImageEnabled(imageId) { try { const res = await fetchWithAuth(`/api/images/${imageId}/toggle-enabled`, { method: 'PUT' }); @@ -1280,598 +1054,166 @@ window.closeSettingsModal = closeSettingsModal; window.updateCredentials = updateCredentials; // ================================================================ -// CONFIGURAÇÕES DE PLUGINS (Wasabi Storage) +// GERENCIAMENTO DE CLÍNICAS (ADMIN) // ================================================================ -function openPluginsModal() { - const modal = document.getElementById('pluginsModal'); - if (modal) modal.style.display = 'block'; +function showAdminClinics() { + view = 'admin-clinics'; + document.querySelector('.content-scroll').scrollTop = 0; - // Limpar campos - document.getElementById('wasabiAccessKey').value = ''; - document.getElementById('wasabiSecretKey').value = ''; - document.getElementById('wasabiBucket').value = ''; - document.getElementById('wasabiRegion').value = ''; - document.getElementById('wasabiEndpoint').value = ''; + // Esconder outras visões + imagesGrid.style.display = 'none'; + emptyState.style.display = 'none'; + patientHeader.style.display = 'none'; + backBtn.style.display = 'none'; - const alertBox = document.getElementById('pluginsStatusAlert'); - alertBox.style.display = 'none'; - - // Carregar configurações atuais - loadPluginsConfig(); + // Mostrar painel admin + document.getElementById('adminClinicsState').style.display = 'block'; + + // Atualizar menu + document.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active')); + document.getElementById('navAdminClinics').classList.add('active'); + + loadAdminClinics(); } -function closePluginsModal() { - const modal = document.getElementById('pluginsModal'); - if (modal) modal.style.display = 'none'; -} +// Override para voltar aos pacientes e esconder o admin state +const originalShowPatientsView = showPatientsView; +showPatientsView = function() { + document.getElementById('adminClinicsState').style.display = 'none'; + document.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active')); + document.querySelector('a[href="/"]').classList.add('active'); + originalShowPatientsView(); +}; +window.showPatientsView = showPatientsView; -async function loadPluginsConfig() { +async function loadAdminClinics() { + const tbody = document.getElementById('clinicsTableBody'); + tbody.innerHTML = 'Carregando...'; + try { - const res = await fetchWithAuth('/api/system/storage-config'); - if (!res.ok) throw new Error('Falha ao carregar configurações'); + const res = await fetchWithAuth(`${API_URL}/auth/users`); + if (!res.ok) throw new Error('Erro ao carregar clínicas'); + const data = await res.json(); - const config = await res.json(); - - document.getElementById('wasabiAccessKey').value = config.wasabiAccessKey || ''; - document.getElementById('wasabiSecretKey').value = config.wasabiSecretKey || ''; - document.getElementById('wasabiBucket').value = config.wasabiBucket || ''; - document.getElementById('wasabiRegion').value = config.wasabiRegion || ''; - document.getElementById('wasabiEndpoint').value = config.wasabiEndpoint || ''; - - const alertBox = document.getElementById('pluginsStatusAlert'); - if (config.enabled) { - alertBox.className = 'alert-success'; - alertBox.style.background = '#eafaf1'; - alertBox.style.color = '#27ae60'; - alertBox.style.border = '1px solid #2ecc7133'; - alertBox.innerHTML = '🟢 Status: Wasabi Storage está ATIVO e conectado.'; - } else { - alertBox.className = 'alert-warning'; - alertBox.style.background = '#fffbeb'; - alertBox.style.color = '#d97706'; - alertBox.style.border = '1px solid #f59e0b33'; - alertBox.innerHTML = '🟡 Status: Wasabi Storage está DESATIVADO (utilizando armazenamento local).'; + if (data.users.length === 0) { + tbody.innerHTML = 'Nenhuma clínica cadastrada.'; + return; } - alertBox.style.display = 'block'; + + tbody.innerHTML = data.users.map(u => ` + + #${u.id} + ${escapeHtml(u.username)}${u.is_admin ? ' Admin' : ''} + ${escapeHtml(u.clinic_name || '—')} + ${escapeHtml(u.email)} + ${escapeHtml(u.pc_name || '—')} + + + ${u.id !== currentUser.id ? `` : ''} + + + `).join(''); } catch (e) { - console.error(e); - showToast('Erro ao carregar configurações do Wasabi.', 'error'); + tbody.innerHTML = 'Erro ao carregar dados.'; } } -async function updatePluginsConfig(event) { +function openCreateClinicModal() { + document.getElementById('createClinicModal').style.display = 'block'; +} + +function closeCreateClinicModal() { + document.getElementById('createClinicModal').style.display = 'none'; + document.getElementById('createClinicForm').reset(); +} + +async function submitCreateClinic(event) { event.preventDefault(); - const wasabiAccessKey = document.getElementById('wasabiAccessKey').value.trim(); - const wasabiSecretKey = document.getElementById('wasabiSecretKey').value; - const wasabiBucket = document.getElementById('wasabiBucket').value.trim(); - const wasabiRegion = document.getElementById('wasabiRegion').value.trim(); - const wasabiEndpoint = document.getElementById('wasabiEndpoint').value.trim(); - - const btn = document.getElementById('btnSavePlugins'); + const btn = event.target.querySelector('button[type="submit"]'); const originalText = btn.innerHTML; - btn.innerHTML = ' Salvando...'; + btn.innerHTML = 'Salvando...'; btn.disabled = true; + const payload = { + username: document.getElementById('clinicUsername').value.trim(), + email: document.getElementById('clinicEmail').value.trim(), + password: document.getElementById('clinicPassword').value, + clinic_name: document.getElementById('clinicName').value.trim(), + pc_name: document.getElementById('clinicPcName').value.trim() + }; + try { - const res = await fetchWithAuth('/api/system/storage-config', { + const res = await fetchWithAuth(`${API_URL}/auth/create-user`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - wasabiAccessKey, - wasabiSecretKey, - wasabiBucket, - wasabiRegion, - wasabiEndpoint - }) + body: JSON.stringify(payload) }); - if (!res.ok) { - const err = await res.json(); - throw new Error(err.error || 'Erro ao salvar configurações.'); - } - - showToast('Configurações do Wasabi salvas com sucesso!', 'success'); - - // Recarregar o status - await loadPluginsConfig(); - - // Fechar após 1 segundo - setTimeout(closePluginsModal, 1200); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'Erro ao criar'); + showToast('Clínica cadastrada com sucesso!', 'success'); + closeCreateClinicModal(); + loadAdminClinics(); } catch (e) { - console.error(e); - showToast(e.message, 'error'); + alert(e.message); } finally { btn.innerHTML = originalText; btn.disabled = false; } } -async function triggerSync() { - showToast('🔄 Enviando sinal de sincronização para dispositivos...', 'info'); +async function deleteClinic(id, username) { + if (!confirm(`Tem certeza que deseja excluir permanentemente o acesso da clínica "${username}"?`)) return; + try { - const res = await fetchWithAuth('/api/system/trigger-sync', { - method: 'POST', - headers: { 'Content-Type': 'application/json' } - }); - - if (!res.ok) { - const err = await res.json(); - throw new Error(err.error || 'Erro ao acionar sincronização.'); - } - - const data = await res.json(); - if (data.devicesTriggered > 0) { - showToast(`✅ Sinal enviado para ${data.devicesTriggered} dispositivo(s) conectado(s)!`, 'success'); - } else { - showToast('⚠️ Nenhum dispositivo Windows conectado no momento.', 'warning'); - } + const res = await fetchWithAuth(`${API_URL}/auth/users/${id}`, { method: 'DELETE' }); + if (!res.ok) throw new Error('Erro ao excluir'); + showToast('Clínica excluída', 'success'); + loadAdminClinics(); } catch (e) { - console.error(e); showToast(e.message, 'error'); } } -// Global scope -window.openPluginsModal = openPluginsModal; -window.closePluginsModal = closePluginsModal; -window.updatePluginsConfig = updatePluginsConfig; -window.triggerSync = triggerSync; - -// ================================================================ -// ÁREA DE SINCRONIZAÇÃO E UPLOAD MANUAL -// ================================================================ - -let selectedUploadFiles = []; - -function openSyncModal() { - const modal = document.getElementById('syncModal'); - if (!modal) return; - - modal.classList.add('active'); - modal.style.display = 'flex'; - - // Resetar abas - switchSyncTab('devices'); - - // Limpar formulário de upload - resetManualUploadForm(); - - // Carregar dados - loadSyncDevices(); - refreshUploadPatients(); - - // Escuta de clique fora para fechar modal - modal.onclick = (e) => { - if (e.target === modal) closeSyncModal(); - }; -} - -function closeSyncModal() { - const modal = document.getElementById('syncModal'); - if (!modal) return; - - modal.classList.remove('active'); - modal.style.display = 'none'; -} - -function switchSyncTab(tabName) { - const btnDevices = document.getElementById('tabBtnDevices'); - const btnUpload = document.getElementById('tabBtnUpload'); - const tabDevices = document.getElementById('tab-devices'); - const tabUpload = document.getElementById('tab-upload'); - - if (!btnDevices || !btnUpload || !tabDevices || !tabUpload) return; - - if (tabName === 'devices') { - btnDevices.classList.add('active'); - btnUpload.classList.remove('active'); - tabDevices.classList.add('active'); - tabUpload.classList.remove('active'); - loadSyncDevices(); - } else { - btnDevices.classList.remove('active'); - btnUpload.classList.add('active'); - tabDevices.classList.remove('active'); - tabUpload.classList.add('active'); - } -} - -async function loadSyncDevices() { - const loader = document.getElementById('syncDevicesLoading'); - const empty = document.getElementById('syncDevicesEmpty'); - const list = document.getElementById('syncDeviceList'); - - if (!loader || !empty || !list) return; - - loader.style.display = 'block'; - empty.style.display = 'none'; - list.style.display = 'none'; - +async function generateClinicToken(id) { try { - const res = await fetchWithAuth('/api/socket/status'); - if (!res.ok) throw new Error('Falha ao obter status dos dispositivos.'); + const res = await fetchWithAuth(`${API_URL}/auth/users/${id}/token`, { method: 'POST' }); + if (!res.ok) throw new Error('Erro ao gerar token'); const data = await res.json(); - list.replaceChildren(); - - const windowsClients = (data.identified || []).filter(c => c.type === 'client' || c.type === 'windows'); - - if (windowsClients.length === 0) { - loader.style.display = 'none'; - empty.style.display = 'block'; - list.style.display = 'none'; - return; - } - - windowsClients.forEach(client => { - const card = document.createElement('div'); - card.className = 'sync-device-card'; - - const left = document.createElement('div'); - left.className = 'device-card-left'; - - const title = document.createElement('div'); - title.className = 'device-card-title'; - title.textContent = `💻 ${client.name || 'Dispositivo Sem Nome'}`; - - const subtitle = document.createElement('div'); - subtitle.className = 'device-card-subtitle'; - subtitle.textContent = `SO: ${client.system || 'Windows'} | Versão: ${client.version || 'N/A'} | ID: ${client.socketId}`; - - left.appendChild(title); - left.appendChild(subtitle); - - const badge = document.createElement('span'); - badge.className = 'device-card-badge badge-connected'; - badge.textContent = 'ONLINE'; - - card.appendChild(left); - card.appendChild(badge); - - list.appendChild(card); - }); - - loader.style.display = 'none'; - empty.style.display = 'none'; - list.style.display = 'flex'; - + document.getElementById('clinicTokenInput').value = data.token; + document.getElementById('tokenCopySuccess').style.display = 'none'; + document.getElementById('showTokenModal').style.display = 'block'; } catch (e) { - console.error(e); - loader.style.display = 'none'; - empty.style.display = 'block'; - const p = empty.querySelector('p'); - if (p) p.textContent = 'Erro ao carregar dispositivos: ' + e.message; - } -} - -async function triggerSyncFromModal() { - const btn = document.getElementById('btnTriggerSyncModal'); - if (!btn) return; - - const originalText = btn.innerHTML; - btn.disabled = true; - btn.innerHTML = '🔄 Enviando sinal...'; - - try { - const res = await fetchWithAuth('/api/system/trigger-sync', { - method: 'POST', - headers: { 'Content-Type': 'application/json' } - }); - - if (!res.ok) { - const err = await res.json(); - throw new Error(err.error || 'Erro ao acionar sincronização.'); - } - - const data = await res.json(); - if (data.devicesTriggered > 0) { - showToast(`✅ Sinal enviado para ${data.devicesTriggered} dispositivo(s) conectado(s)!`, 'success'); - } else { - showToast('⚠️ Nenhum dispositivo Windows conectado para sincronizar.', 'warning'); - } - } catch (e) { - console.error(e); showToast(e.message, 'error'); - } finally { - btn.disabled = false; - btn.innerHTML = originalText; } } -async function refreshUploadPatients() { - const select = document.getElementById('uploadPatientSelect'); - if (!select) return; - - // Manter as primeiras duas opções - while (select.options.length > 2) { - select.remove(2); - } - - try { - const res = await fetchWithAuth('/api/images/patients?disabled=false&search='); - if (!res.ok) throw new Error('Erro ao listar pacientes.'); - - const patients = await res.json(); - patients.forEach(p => { - if (!p || !p.patient_name) return; - const opt = document.createElement('option'); - opt.value = p.patient_name; - opt.textContent = p.patient_name; - select.appendChild(opt); - }); - } catch (e) { - console.error(e); - showToast('Erro ao carregar lista de pacientes.', 'error'); - } +function closeShowTokenModal() { + document.getElementById('showTokenModal').style.display = 'none'; } -function toggleNewPatientFields() { - const select = document.getElementById('uploadPatientSelect'); - const fields = document.getElementById('newPatientFields'); - - if (!select || !fields) return; - - if (select.value === 'NEW_PATIENT') { - fields.style.display = 'block'; - document.getElementById('uploadNewPatientFirstName').required = true; - document.getElementById('uploadNewPatientLastName').required = true; - } else { - fields.style.display = 'none'; - document.getElementById('uploadNewPatientFirstName').required = false; - document.getElementById('uploadNewPatientLastName').required = false; - } -} - -function resetManualUploadForm() { - selectedUploadFiles = []; - - const form = document.getElementById('manualUploadForm'); - if (form) form.reset(); - - const previews = document.getElementById('uploadPreviewsContainer'); - if (previews) previews.replaceChildren(); - - const progressContainer = document.getElementById('uploadProgressContainer'); - if (progressContainer) progressContainer.style.display = 'none'; - - const fields = document.getElementById('newPatientFields'); - if (fields) fields.style.display = 'none'; - - const btnSubmit = document.getElementById('btnSubmitManualUpload'); - if (btnSubmit) btnSubmit.disabled = true; -} - -// Inicializar Drag and Drop -function initDragAndDrop() { - const zone = document.getElementById('uploadDragDropZone'); - const fileInput = document.getElementById('uploadFileInput'); - - if (!zone || !fileInput) return; - - // Evento de clique na zona abre o seletor de arquivos - zone.addEventListener('click', () => fileInput.click()); - - ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { - zone.addEventListener(eventName, e => { - e.preventDefault(); - e.stopPropagation(); - }, false); - }); - - ['dragenter', 'dragover'].forEach(eventName => { - zone.addEventListener(eventName, () => { - zone.classList.add('drag-active'); - }, false); - }); - - ['dragleave', 'drop'].forEach(eventName => { - zone.addEventListener(eventName, () => { - zone.classList.remove('drag-active'); - }, false); - }); - - zone.addEventListener('drop', e => { - const dt = e.dataTransfer; - if (dt) handleUploadFiles(dt.files); - }); - - fileInput.addEventListener('change', e => { - if (e.target && e.target.files) handleUploadFiles(e.target.files); +function copyTokenToClipboard() { + const input = document.getElementById('clinicTokenInput'); + input.select(); + input.setSelectionRange(0, 99999); + navigator.clipboard.writeText(input.value).then(() => { + document.getElementById('tokenCopySuccess').style.display = 'block'; }); } -function handleUploadFiles(files) { - if (!files || files.length === 0) return; - - for (let i = 0; i < files.length; i++) { - const file = files[i]; - - if (!file.type.match('image.*')) { - showToast(`O arquivo "${file.name}" não é uma imagem válida!`, 'warning'); - continue; - } - - if (file.size > 10 * 1024 * 1024) { - showToast(`A imagem "${file.name}" excede o limite de tamanho de 10MB!`, 'warning'); - continue; - } - - if (selectedUploadFiles.some(f => f.name === file.name && f.size === file.size)) { - continue; - } - - selectedUploadFiles.push(file); - } - - renderUploadPreviews(); -} +// Admin Exports +window.showAdminClinics = showAdminClinics; +window.openCreateClinicModal = openCreateClinicModal; +window.closeCreateClinicModal = closeCreateClinicModal; +window.submitCreateClinic = submitCreateClinic; +window.deleteClinic = deleteClinic; +window.generateClinicToken = generateClinicToken; +window.closeShowTokenModal = closeShowTokenModal; +window.copyTokenToClipboard = copyTokenToClipboard; -function renderUploadPreviews() { - const container = document.getElementById('uploadPreviewsContainer'); - const btnSubmit = document.getElementById('btnSubmitManualUpload'); - if (!container) return; - - container.replaceChildren(); - - selectedUploadFiles.forEach((file, index) => { - const item = document.createElement('div'); - item.className = 'preview-item'; - - const img = document.createElement('img'); - img.alt = file.name; - - const reader = new FileReader(); - reader.onload = e => { - if (e.target && e.target.result) img.src = e.target.result; - }; - reader.readAsDataURL(file); - - const btnRemove = document.createElement('button'); - btnRemove.type = 'button'; - btnRemove.className = 'preview-remove'; - btnRemove.innerHTML = '×'; - btnRemove.title = 'Remover Imagem'; - btnRemove.onclick = (e) => { - e.stopPropagation(); - removeUploadPreview(index); - }; - - item.appendChild(img); - item.appendChild(btnRemove); - container.appendChild(item); - }); - - if (btnSubmit) { - btnSubmit.disabled = selectedUploadFiles.length === 0; - } -} - -function removeUploadPreview(index) { - selectedUploadFiles.splice(index, 1); - renderUploadPreviews(); -} - -function readFileAsBase64(file) { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result); - reader.onerror = error => reject(error); - reader.readAsDataURL(file); - }); -} - -async function handleManualUploadSubmit(event) { - event.preventDefault(); - - if (selectedUploadFiles.length === 0) { - showToast('Selecione pelo menos uma imagem para enviar.', 'warning'); - return; - } - - const select = document.getElementById('uploadPatientSelect'); - const remarkInput = document.getElementById('uploadRemark'); - const btnSubmit = document.getElementById('btnSubmitManualUpload'); - - if (!select || !btnSubmit) return; - - let patientName = ''; - let doctor = ''; - - if (select.value === 'NEW_PATIENT') { - const firstName = document.getElementById('uploadNewPatientFirstName').value.trim(); - const lastName = document.getElementById('uploadNewPatientLastName').value.trim(); - doctor = document.getElementById('uploadNewPatientDoctor').value.trim(); - - if (!firstName || !lastName) { - showToast('Nome e sobrenome são obrigatórios para novo paciente.', 'warning'); - return; - } - - patientName = `${firstName} ${lastName}`; - } else { - patientName = select.value; - } - - const remark = remarkInput ? remarkInput.value.trim() : ''; - - const progressContainer = document.getElementById('uploadProgressContainer'); - const progressText = document.getElementById('uploadProgressText'); - const progressBarFill = document.getElementById('uploadProgressBarFill'); - const progressPercent = document.getElementById('uploadProgressPercent'); - - if (progressContainer) progressContainer.style.display = 'block'; - btnSubmit.disabled = true; - - const totalFiles = selectedUploadFiles.length; - let successCount = 0; - - for (let i = 0; i < totalFiles; i++) { - const file = selectedUploadFiles[i]; - - if (progressText) progressText.textContent = `Enviando imagem ${i + 1} de ${totalFiles}...`; - if (progressBarFill) { - const pct = Math.round((i / totalFiles) * 100); - progressBarFill.style.width = `${pct}%`; - if (progressPercent) progressPercent.textContent = `${pct}%`; - } - - try { - const base64 = await readFileAsBase64(file); - - const response = await fetchWithAuth('/api/images/upload', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - imageBase64: base64, - filename: file.name, - patientName: patientName, - doctor: doctor || null, - remark: remark || null - }) - }); - - if (!response.ok) { - const err = await response.json(); - throw new Error(err.error || `Erro HTTP ${response.status}`); - } - - successCount++; - } catch (e) { - console.error(`Erro ao enviar arquivo ${file.name}:`, e); - showToast(`Falha ao enviar a imagem ${file.name}: ${e.message}`, 'error'); - } - } - - if (progressBarFill) { - progressBarFill.style.width = '100%'; - if (progressPercent) progressPercent.textContent = '100%'; - } - if (progressText) progressText.textContent = `Finalizado! ${successCount} de ${totalFiles} imagens enviadas.`; - - setTimeout(async () => { - closeSyncModal(); - showToast(`✅ ${successCount} imagem(ns) enviada(s) com sucesso!`, 'success'); - - await loadPatients(); - if (window.loadClientsList) { - await window.loadClientsList(); - } - }, 1000); -} - -// Vinculando ao window -window.openSyncModal = openSyncModal; -window.closeSyncModal = closeSyncModal; -window.switchSyncTab = switchSyncTab; -window.triggerSyncFromModal = triggerSyncFromModal; -window.refreshUploadPatients = refreshUploadPatients; -window.toggleNewPatientFields = toggleNewPatientFields; -window.handleManualUploadSubmit = handleManualUploadSubmit; -window.initDragAndDrop = initDragAndDrop; -window.deletePatientImages = deletePatientImages; -window.adjustFineTune = adjustFineTune; -window.resetFineTune = resetFineTune; // A aplicação é inicializada no evento DOMContentLoaded acima + diff --git a/dental-server/public/cadastro.html b/dental-server/public/cadastro.html new file mode 100644 index 0000000..0051713 --- /dev/null +++ b/dental-server/public/cadastro.html @@ -0,0 +1,468 @@ + + + + + + ScoreOdonto - Cadastro de Clínica + + + + +
    Texto copiado!
    + +
    + +
    +
    + 🦷 +

    Cadastrar Clínica

    +

    Crie o acesso para obter o token de conexão do notebook

    +
    + +
    Erro ao cadastrar. Tente novamente.
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    +
    + + + +
    + + + + diff --git a/dental-server/public/index.html b/dental-server/public/index.html index b1214a4..f35dd2e 100644 --- a/dental-server/public/index.html +++ b/dental-server/public/index.html @@ -6,7 +6,7 @@ Gerenciador de Imagens Dentais - +
    + As imagens aparecerão aqui quando forem enviadas pelo cliente Windows

    + + +
    @@ -137,30 +149,14 @@
    - @@ -301,171 +297,61 @@
    - -