From 774164e3d70ebabc4beed221ab40078930dbd3aa Mon Sep 17 00:00:00 2001 From: Alan Grainger Date: Tue, 29 Oct 2024 15:51:49 +0100 Subject: [PATCH] Update docs --- .env.example | 4 +-- README.md | 45 ++++++++++++++++++++++++++++++ package.json | 1 - public/images/server-settings.png | Bin 0 -> 13012 bytes 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 README.md create mode 100644 public/images/server-settings.png diff --git a/.env.example b/.env.example index 6d94329..72a1784 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ IMMICH_URL=http://localhost:2283 -API_KEY="Get this from your Account Settings page" -SERVER_URL=http://localhost:3000 +API_KEY="Get this from your Immich Account Settings page" +SERVER_URL=https://shared.example.com SERVER_PORT=3000 diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c4ff47 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# Immich Public Proxy + +Immich is a wonderful bit of software, but since it holds all your private photos it's best to keep it fully locked down. +This presents a problem when you want to share a photo or a gallery with someone. + +**Immich Public Proxy** provides a barrier of security between the public and Immich, and _only_ allows through requests +which you have publicly shared. When it receives a valid request, it talks to Immich locally via API and returns only +those shared images. + +It exposes no ports, allows no incoming data, and has no API to exploit. + +The ideal setup is to have Immich secured privately behind VPN or mTLS, and only allow public access to Immich Public Proxy. + +## How to install + +1. Clone the repo: + +```bash +git clone https://github.com/alangrainger/immich-public-proxy.git +``` + +2. Create a `.env` file to configure the app. + +``` +IMMICH_URL=http://localhost:2283 +API_KEY="Get this from your Immich Account Settings page" +SERVER_URL=https://shared.example.com +SERVER_PORT=3000 +``` + +- `IMMICH_URL` is the URL to access Immich in your local network. This is not your public URL. +- `SERVER_URL` will be the public URL for accessing this Immich Public Proxy app. + +3. Start the docker container: + +```bash +docker-compose up -d +``` + +4. Set the same `SERVER_URL` as your "External domain" in your Immich **Server Settings**: + + + +Now whenever you share an image or gallery through Immich, it will automatically create the +correct public path for you. diff --git a/package.json b/package.json index 3715649..000451a 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ "devDependencies": { "@types/express": "^4.17.21", "@types/node": "^16.18.111", - "@immich/sdk": "^1.118.2", "@typescript-eslint/eslint-plugin": "5.29.0", "@typescript-eslint/parser": "5.29.0", "eslint": "^8.49.0", diff --git a/public/images/server-settings.png b/public/images/server-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..3754b6e3acae55acd809765c3ae3fc225ba7bae8 GIT binary patch literal 13012 zcmb8WWmFtp&@MW-1(^vUxD119upkLR8W>zd&_Hl^mjnqggAeY30fGnjAW0xtaDsbq z2~Kbh@3+of_s3o9o^|?1uid?SSJl(iUAy+v)kqCB1!6*KLJ$ZELb$0%0m@sOZSu-`}rp9p2vF$!JvCy9J4hi`OU0q*XT%Mo*J2^S| z7@55KXMK9^proSi?OT(MAAOCj-<&){s_I%NruMgY_UfBEY8u*xMkl8i)?dAP^`)li z#fujQhlkhKH<`Jmh^RPeY3c6X!PC>T<;??q14BSg?%U5_PGd7Z)#LjROS~~cK7}+t?ijPecjkP8yVlLtY4U#Stux* z(Kc-aw5sBha~&L<((@*g((^t1BEJ1xSJAJ_{FIMEp{Ay$`iCcd{R1yAuh0KopPpU) zo?9IqpVrpV+1dR!yKvCew;3Im*z;>E2-&au=1ok>aA;`g;^LycS7-n5c3eWr@2R=c zsyQpy_V+;{ZXRCy2Ny9hG0Q7!{$V}t@4M>j>(eu{-Wfx4E*nO?4YwqY;`}*xqRaI4PZfdTU%QPM|V1Z^gFw_j!!T34vggFeGUkXYx~xNKt})Q85$g)9UB{)TiHH1JRTgL zKt{*s6_)z?`W6=#Z|$G_{{4GyZqCcczon!5VQX#ge$9Me1p@KQD+1CwKC^qXOin-e zDZ}5{FAr$wACiFAv3G^F=$nF_sEKyL-!UWI22dzP9F+0J3vZvZxenup$MXN(f{Oex z=*h^0#NUo=ddzO_(=LAs#WB7X>tt4d_}ykCJ{qv4e$|3Wd!9P7o#k*_0eU^xbREw< zg*hQCbO(!K!v1Y;quNjcN+$LUbgNbjx(YwH;F0o_RmLU4DRwH}J$Eq2D5cpEw85ty za+P*dxy^$@?pjiaB9|_&f8JRQd;_AH(V=Od+AuNo$V7fp$7-CNeBdP|U0% zS=0D44;R90`yhdMcA*&B7!BH&d$a%iE=~k9jgRA+JwIk^7n6?~f~5-)8!rOE(j|!Z zM~+hBK&^ANFrx|ZM@TnF3;fc|S?YcH$bpr+1_xTKp-F`CK7)pV>y-x4s37ehX(rjg z{UbZ6b_B0NQ%H~&0!kgv;1ZWfCT(e)$Hx?+gt#Jul0h$Oz3;grv0N#87y_36HepHF zo5*yMeRCpmoEL}^DT^WuDTxk?gt#yf$&@!h$Qw1H0#l!7e28dt0}mUcmWrs+f(92X z%)u})5{|HE1``~8f?jce378&QEa$}8VATFIj7mHvr{B%sH3Y?yDXtf$2mf=X$=R(Y3q`bKNwB&j>oE8 zb$0eOWs9eZ*OoI4>(;&ruI@m0bSOtWj26+|LFqrAE%l0`{RN(?u{MX1^+2yY-&1==5PWI9~9@!Lh8IAM6Y&#zpxqzg>oS`XfaZ5|T)H`Tq+4<7cpBY^{}e$dEl zicy-rM|UE6K83Cl8MTvVPwe*fhw3#)JYN!hs`weWcedc##}%jbj(I0JXWy@OirS3C zHgF`iV$-@dV3cI)S0az`%!lcZ0XlzPS8d7G`8D{M^tV}&?K)1NdJ04FgZJ_b0jKw!UoL%r!EJZaaQo9M>O1vFv#mlBY6mGm0zfKbd7e zgXMLeHP_-Otx>kqZsYiWiPw#T`W?vv4hAgg5?j9GX2q^D00oxig*-LZJAg~x2r=jB z1h6J@_z-3>xU%9&R+0#e?J9S15`MyTk6)qSz)kKUvxWX#>d5KUXO3v(nLPZFUOEp{~h`y zDpx~#B#}+pmeh;%{eac6Rf6g>VUM|%t{XLb0V;W)j==QPz6)LC4qPr7GQzFKNE`z* z2E9na0I4eKq!T&X0TqO%iMe>9me7o7P`;ETSGpQJ(KafQx9#T!_9~#wvXrJ46Yx1u z&9E;l%!gIH81?eR^mV)Iloq9@jXEC%`_#wq2VUiXYNvuG5f+4M31&jse^6Uz6!@^(ap?W zC|Tl`Wb`X;Cd!J57MDz!PdP~dD~DB3BNOyZSBFp!qCfDhez@=Vf9dTm#}dtBS*)5{ z<1F>SQ$qfNdOQ&Y4ebwKs0ujsY4A=g}Hj1jw!f?s2sIDRAh6s`&0#QY2!Y z&1Lo(IKNV9{>XPaW5KuiTy0!Byjii&Bl9@6Jecy;m{Nh?s~X^|jW93Dzt^v@K6>K`Qb~G1R*#Z~DLhjKnjt>_kK)f6(NSC^;rAW?S54cex}9@Cce0{Uum@flJ0b%be%nEfJ)hv z^c}9;&r5ZdzjQ1kbctQ-22!I@9go3E7hi9nRqu)a3X73ofU(VHHN7F!%4(kej{tz5DYUF%3e(jq;^v`bx z?fTI7hyGuA!K!o6o(+o>I9g(_T0<1$EuY^F)|(+-i;pRfVQ9K#P|TZe6%lV>%0g!# zg~U697i^s3d9~d#Fw`3#meIeTfxY*cj(cGcPs{vRc!pT|dOMMZ^`R~Pw;{t`F||Pq zHP*`j=bXo|F{ZQ*H#_nG_Uuyt*_0D=ZYryj<~;$d%iS;~&FI8j%pRX41yJ0;ygikn zuBhK<`!D>Iplz#L((d=Yk!}J5fh=eYY{xQUhE1|?bIVqftN_pXT}X`F^?MY|%8;^Y zDN4?{b_uTYKBTky#YK6~w`X~Y^^^{`lIN)4d^@}^E&KMm6&pNA_N~?>+Xy0!!N{$d z;)56WKJAuz;ms$zE2n$Xqf>}&-%+=`U5F+Y?Qz1F$6i`jj_+28WK!mzJ5I*yI1sYu zFdkwms>Ug}X`@x~RJ1M8Gwgf5YGO0%ixAWdrR(VdTS1eHkzpGq3%qXChvZo!Js-ED zW&-fbu1CuQSo&qb5S@G69W9|>7*v@fdKsV%wWCU2u5tG%YStHZ$dwnP+V~3rC)ek_ zl+b?UZ9{sgVPNNeovB+eSI4__mQ16rVQk!?!fqx|yZJ7$WT`#b?-8#f$Y#a++G_aN z^A**@`;sIKAT%OGMQN=r&|qsJspQyAU$X#*yW+xl1MvaLy)G(;@A`6-J|t+LGq;k3Ca zGp}A4?AHGIX!mwW?;^FfKO*5slRh9BAq9{@p}q0|BNQJDwqS;mfj=VfYrtRxz6^jp zgTM#6x`+OEI0Rp4S8)66uylo0rByPPFhcd(9GCLJ#?Zw!Wvkm`&i_e)Cs%8AcZwEJan3aRvbCgGeWLb-4tQb9_EUK-Ifr`Aiq_c zacuUSd0F@=#xmq}FfRxBQ)TQpl3#}=iW37*rV2ESct_*08Qktu0mANN*!K)kq6eM# zOmE`13@2<*K-+e%!6Nui;;vocy6Lho;xP`sz~Zv+d;iTA4zr1`)~-??MKWFw#L9=9 ziOxT&Suk{^|A&;x`5Za}RgA85ij#CKdz%1NijwwhWs^30`z6+&{BYEB ze5>i(6|pzoi>7xO?`wps^~?T##|$}mEmKaXg4jix zIaD!d_}52uYuxEQza>I%%3Ex@YU>Eq4e}ag=G-4fDwq`*0iT61DPG88g&uC2<4C3` zFqy>X?CxPQ>QR7Qmh#CeV}o8ZYdp!K80@S@(wg=?J^Ak7PSQ7kJl5@sk76k`DAs~M z&6Z5u!tE>&6AJccLgBPIRxc)EYY<6QJ4yjIT%BJ#8iSI(JX)X{3>!g8#l5}Q;$FLl6oUXg`P1j=5@}k%x_geBsCPVMI=`bKp|e*gG{=<7nw=X7m5UTIid ztZWGJFX2(VVM3lcs;1%V%-Tb%!a^;Rc-2BJMWq6P&Zzt2b3N#U0(BvMkfdV2Sf3&I z6>sX+&(0*{*1@FsE#@$CPM;0PnQsx4kH|*m13HLex5Y4ONipHE#kLd?JY2zkO*?!g zvUM;-xsLHCJ$Fd}%S|&@1Nn%c9;=CuJlNKXW&xbY+w(Rc6on$Ch=r|gu*xv<55LgTXkkw;Mc5QmG9qxwgp z)O9bC^|5MY7(p7rvzxTTcD@k;nk^E7zaGCx&^{e0aBHvAUbrvk~jVrtrYUnYp73f^tO!hClw_ba};ahRxq}q4q=`$ zjhB5Bn><4}#d%`8yk=NON-?uqU7?qn`_fn_y;c!{D6dhPi~ z$}4F!c_ufOxnd_o?~}&@QM6D#G51zbCKi8sSd?%OEvW#?5^|6UF5WT${i>i#K2E=n~xr~Kgt$OUmmjOWK%~P-ZEv4uC3lQnP>Z zFs9lX)qg>!4;84+Lol!?U0EG;N&NxK#Xw;YR|pUGVl%h;P$n!P`SCj4lzQaNl93#m z28-Vw5BXVY|LcQMAH#&~kKass`kP8^+J@~qnl5IiB3f)3N5rDfXpw+G_&4V|=-+z{ zsG?YpZ&?qT((MUy3+%(hq&XoT%YlMZ9Cn^-E8YyS5Ob+x5R3mjXJ4l3y`V9Rk5}r2 zxq*~bQeJIShD#f4WC63a${4B^`b-U?9h5OqQn}ZUk+AiwjS2+a6v_sBj~OXxP(0VD zQ%oHXhY>Z6YTBL&oN$9ONwe6PV~zve6~KQ8>5VGtsz$7CW8a5DysYyTVPmXPEb6e? z#%8wJ3h{J7$V(}PwfbL=E>wVe=&37G%g|n#X$e&M>{Eg|xZC)hxHvPaAS?0flcPW4 z5Zf76Imjqw&^wIJEDu1Wa)RS&QxOeMq#8jE?C3XmWPtAXTzTrCLaZw_sHV##tE&?O zD2+8}p9+~GZqiB_+1L=IRE>Ew6vhL3yN8Q0M1{0i2YkzpjA)x<0ilLC7PKv$1K$QR$bGrjx&@f3|;QHsf}BX#5}B@1+2!?0x8&5=wwN zP(4qMdz7AL)c%i57`jfkc>Le`)elKIp9XvNQ=Sw0Q#;A zawhmSC{!=O6{~Js8gfnOCjRUH$=p$GI%Cnc~x01ci)4uQ!>QbPAbq zE(;JGu#H@BCQ8_m5!xOR%R$({fY>^G)^KaAmzEd4|WRov#4TQOddDYpm&KI@e^U~mEZpK4&h3j z;iszVVC=E^r0^XFCQ~0ik@EmTd5wNv(V|?IOZI!GKx?1ooZOC+v#qL3rbDgupu*cp zTXxtfuxC*Z@Q%WIOEEgOIMF2JC%I70@7l+erjN&Tc*Ze*vU8#XNmrx{S(CiTP+tT* za^F|?o;g7In-T0UFqIP`6efJm3Kco9o}gb;LD8Be7NC7CTQvZUcdNdlUmoPBB#+Jg zp?)Ca15W;&)4nz6=DXIy?9my^b6(6_1i}q>d9Qwt8?jZ0@1Q*U<R=l-cd-e_=`q3{LaG1O)!SNv?NzO+4IbL}FZcG@PslpczEYT9g%JlX#BxQ^3qLO>-Dnqn2h> z#^m1`Bb+Wqd}luN<9$a@kJ3ET_s~rt<(vX%heRb|S0#Zf;3Xgt-;K@C2y@TN$)Vr; zH*N%78rbvcRq9oP5hMuL}265(Fis1fR;#vh%x-fJ zdjRAiHfM#C`qpu50kO3ZXXaTfrvPMNy%D1UfP7n0DQDl@Ik09K#*|m*Fu+X$4wP4V zq~@t5pJ+UQR`0WEJwiCKN8{17=)BrDAiDZEXxeG=%<>gG^ru-Yv)LZswFmqmEO+u3 zf7rEd$_yg;zlq8*#wWI_wD^Ax$=>IB5~B%NLyB`FGKxvMCZNeyBR3ZXVG!7Y8;?=+WquX3?H)n zVpSQvw8o%MTbS@&GI}kX&b>@&6(OryO`7|GFUn&4o%6`$A5HsOE(%g&YT3c4qOS|J z-fo|)nMBq{6vP7S^Rp(9rJO2iim$cvHmp@X5y&tmV!Uz_dmT-0HPuA&8M+wj7ltj2-G;X@VzQu41aowX~n^s@!j`fsh+ ztbb8TcWAXt{?D-Ov2`Qbc<${YpLkjgaw`{@k|w zZpvj+sa5Q(kd3Hjmb1ioG1nJ>Tulo?fn!GNM}HR+sE1a4GwjtRW7w(t`Xo3mph0PY z-%D6tw9_EP!;4gpkjNeHm+T;s=qf9+E3kAhRrZ!7wJ4-xxq^+>=DdSvdF*q|{Lo7E zJaVB?hU3EqU3OXiE@6*XQN^4~Nx_XOO9a-BWywD}KA+0|IPTHgXvIZN$qv9UUjwc_ zk5U(xX!=ILdCGoVR&Uh(+uS@j_<4M|+mV=8+n^Ba;5 z(!U;N631M$PvZDRytdc(C#Jr?iBAa^1Gu00xfS?se>lTShV^&XwNX8XqeVstBIMS5 zhG{xQQ`mP8(^=cJm?(nd-tsLd7pj)1OpoXmDHf55yp85Lh&Y(Xl5l#P1}Kph``b-3 z)|h=0_7R%PCCzcM^D;Yss&=jfzR zD+?Lre2}RM4p6<$AspOZCW6pI?zyO)aEm=9X$z_PtO>EDmiuxXbxrvZ%%h15NI`A& z96(TR)TaXZ0w5%KTIpv(LF>)((_aZVMuKS40BdYvz7chv%^XZk=Qy z^+_sKCA{d4KSZxEGCRFcH&bkcMDgQWuQ@vghM~zal4YGT3FkIc`b%n&k;6cj(wWOO zINH)_H)b+W8#*^XG`~_RfTTB$nySSJq99Up7c%z5)hp=Qo2}^kLU2mn?zL2TvaU6W zyJIF|V3n%Owby{BGx6I4G_CDNTJWS%a3MAYf$RJAFNW^-4x|w%cFK(6@Mizq6~h>r zfNw)?6^<(^<6R+Y$Om$yW*L1XRVeBEo=^!!Y){n5p_1d@0~w8!Y>~}7SOj!ar5X$Z z@dMV{NRqYFu+m1ZbAw^qynHu#Hu#K#2pWSTsgqThK=7kaZ>}a|9595~e>-*?3W}%E zBAq?Yf3ehKYvpD4-YP?hDQiN*x)S=|qcMDs^R^0^*-^rUd_2j|VKpxXJ_WO-1MYr) zgSJC^TMzW|tq38}kW=X>F|PmNr|(hdpvf0?yzyW;%Q4-X)rQ}e<-i?doD7<EIe-4D2*N^lgpS&LHEITy~_KSI=BNm%> zr#L3YIr2Hoo3f}NPw~Tf=2JG!RON9B(ok(v{m<4=XI+q05-18sA9QPult-sU41u&|FC}KTK~5f4kIkL zVU~gRo93jP;Oa#rsukFP2S00@Wnrwdj(kPjOFW%L&a1Foc*gQF*d3~vVA&;sGJW?x zIcH@WwPO_bK!~aO1X2|(t~iXbNdCS127@5$x8;?(C;$U`|_V+5lgkKUM0rC=e#5zbSUUfcU$)#e`d_pOw zoP>j01i39p5S2FeNzf^Tu#AKJ_UT=qnhetYLUA3*HeYu@UaBh3W3be{58{hu2zwMy z6Rp|aLPt*mkknF9crD2=<9VDIYQlauBq}**JxwxJbP~VF1K#17*3IR>uvA8|4)q0E~ zQ_J5Kvt_UyOKQ?WRZ7-OITc0=ZL6@**INS{!DR1Nxw4k6r~z|CmkT;#+8%=be3)+2 zW}nS;z;}TTaV8Hg$7(_~_qBN0(m@ zQ%zhdOq+<%5^29RM+Ww!5H<|B`75eTevQP?ubY3YD={eK1Geu%;JO-+=6-@<0b8MGu9mp2SRtWxeyP*cPDgml8)-{ZTvaiC+sKr#v!^)w*)rwA5T+t$H|Fm$;S8<9m)x(c#% zo<;n1)|`I9gm@&U*|J<)g^Ba2JmzRu1246%QRbt2bwJHR8P;Q>v;;j}Wo;wMr?S`? zNu~Vw?w9&1!pbI|6nxj|DWwCBPl&Lda1RJ~;AOgu5AO}Yv=n^$F} zIQ+i-a`q%oeD|o8{_{jB#Q?Tu?>|b)ogaFsWkr@Q&;gpX>qP=ET7u*EwP{X|bXe>a z#zQXDP*Nj=z~yXI3GKz4di6V_TD5$i-}#nK08>DoQ_xMZNFgWdq+9n$f{g8ihDapJ zBB1EvoqA8470C<%ssf$&o*gg^X%Sl@8RgbR;28XI2_(_{3aDd)0jg4p15wUD8AMpy z;S5P2bojBqK2!E_yMWAuM!Lb#zJ}kA50lsx=Rt^!fKw6%oh^2)RWDmIltP_K8{{`; zZP~Qd*Y9czWU(11liP?XD!yyQc@p!J3C0YL@hB1GgiWb*z`tH6L@y_zSCOvW~KNszKD@3V-$Vx?_qQ&zb3i1Lc31b`D=XzXnZC@s+zMSIO z(0bcEAYpLyqWyb*Gm_Tj{g;`|R*lSi!%A4pHz;wn|^}MU9+USF(*>7 zQ&F|)AHLxsI0eu51m1WoU^wr&kQ>`%x87pbY=&5J)Nh1S)O#|uBa7RbvN2XTDIHRt zioI+B>O(J@k?Y^9eXq!Fu?QKj3aD!;*UW!gI0WupSNd?gr_kf|k>N7n(SZyOU$?qL z(B7KeTaA4^1PpM7MJ#l~(A8yQ%J{S_1BECo0|F%&)f8xba{LZWd_9Mq=-wj>h~)fJ zNvL^QNzYcXYdj8U%bco_CHHNT2Qo4gWqo&PW5xpPB52t!1;2mLa;k+F+%;5%kBz}` zlYLQMEigdqzFs%#g&2<_qM+d6NDEI<2F7w$`QM&}DIwviPm=C2#v`m}pjMSQc9qD2 z3(PN+U&US`L!l69ng<>a0mpWHAn+KW;JW{qI|MNDAJ_N)blymee!b4Z@!nDvihE^L z$TPZ!!_exbfyMs_OIpKM7!*}-!%*nTHRz((-7qFVG3uXd#fi3$XSU%$A4rX80P@|)pMBd2zAbt?hoofEOCB}+~@1X!zsvZ?3YeJSZXF37Lpj#47m~-M#QQ}tg|FKCgxtESs(y53DCn|izvaa6;_uhOKMjU9GTTqS4hlL5mw9^s zmOvB>Htvawe?3j1_=7=ACc%8T0 zu<$9^+0i>5QZ&^iX#@26`#pT%rhc>qA5X=SOzft8DX%EyN?$UszG7=<=`h$wAYCe9cRFP6ofre-Ep`6?wV^*hnjqu-S)&7>ZDX%jz{ssMJCK|K~-n0EMz-k-!NPu;; zTxn85KhXgzuk{y(p0ZR#opYvVO*^KJH;vF{9!NX%bANKMj(kZusT52+nok8T?kT_d z!GMv7Ia6q$XT0F6GBWyrxBNX<9S-TG`!VOIVZ+|dgFZ*e16w33*Yen?<6-umVQ1>m zDMk~cSi}h9r|Kg4IjkzCPc5BZV)U>z?Blb{C!mi0?^u{Mc~XK0$6AFMK2qsr=fZWENxlbi}PwEThjT#HNU$ELk zO2SzN8Spi&N^NVfwm4)K-mAj>*~5|wYPde07Gk2<>0hBaKJ(;A>Vr5}#RM$&eKL~% zv7?;oaGOD~O0b@s-gLWan!Q7|DS9C>f*K;z@(KK!P5R+xbH*tNeYYs~liIGap2%_; z)KetPOnUYC`oYVL=g<(DQU<16WuEcBiv({Hd>Wl>#ImrUqtXE-ok{l;PTP7KR(<{L7ps1Z7Kzj6!R>sLaa<1$&&$z>Z?&*k7~%d38B|jy zz_;kVSzKXu0)K73t=3{b(BjD3EBT=D{g!e{f^5y~5OXXXteM9VHTlCZ!OPJFwwVi; zd&i~;&L9{>Td7$EC7=G`LjJ6?)H8nGk-?K5`q>eFlMM(Lf3=W%`kqlU^J@@1iPB_{ zs|XQ4CS+fel*XEjUjj_SU-?Xl^!Z^u`|M$af5gMQyNM`pHD_7hi|uvYx6)b<2Gj$j zv6|v?8It6$6wUo^rv)#)kXLJT0J`f!DVY!9qnR2#fDpa+R=DSZlDv}2<2#qzpd4#8BH_;M;}Xjw9ylFl$wdOevy zQNC4|{^mYpAs!0>;2jB5rSE*yual~LbgGR^fU|@^{yc{E3FqHc0BASb*NRdW3~&wnMH z7V~aOffVE4ku_t@FX-y(DCQd1LWI}>^RrnzNFLm9swxCM+)0wGs%-JPeIqFSTX_^n z`OR{yZKa05492pYzeMdQ4eZKJk;U;LZXo!I=FNAhFzjRhPVf=`%XmV% z*fR7S-Ziv+xVl(y0@{N`_M~c)u+&e@MKyf$6K#@U?MMl~dtTp=qm*x?+nJP zMq#M3Xl65w5w|}kVVcn*vTAug_h61Vtv=aTL)?#}c1ST<=0~9(BrGx=a?~PyEabyD zoR4@$b)q&UQQVXhjnXu-?HT+*)TxbWFy z#$+(Qs2if)*#G%WC~fc2}WB%E-uhYM$7j$+uvTS(R z<0_g9z~k|wz*t$4G;%`%xoP7N9u;veknWsr#4v`QexP8%Mm0wepWN&HOT)iN5jx!Y zMQJ8T_n!d{Jfo7?pJFx*+KB~XbRam3(#FdN+=mXX7=z0<4mkSRG`vI4DWLNVG43WE zdm=d)K(n**K7`Schwkrv5SuVm1<@mc0A8 z_0lGbr5kn8s;#Sc=gWP_pW1B1w9e=A3DPDHyLvg9rMyR#R=#{MtC)0raP$n;-_>}) zR`>GeLjmMAKj0BEl4F=B)kLt?$r85VKa?8Q-_e0*A}MttC@i{G18B=z{!$jGGtsY7 zWUoP2X%0fCs~C+?@o=z<+-jQNqSSeULdD8ig6WD#87J%HJgiTjO*|{)L(g3X-Y0cM z_9IMD|CO|X!FB(YutA|AB2Y}QH1LoLKUBCO5csqIVF81sX~4k&NWB$K`zX{%jhR(|H6dwQ2Zvl#OYQPs6(~$oKkCY?u literal 0 HcmV?d00001