From 756a752eeec97efbd02d9417650c85000de8a990 Mon Sep 17 00:00:00 2001 From: Tyler W <7053824+tylrw@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:19:18 -0300 Subject: [PATCH] feat: add Kimai (#1390) * feat: add Kimai * fix: address code review comments * fix: adjust hardcoded app port --- README.md | 1 + apps/kimai/config.json | 45 +++++++++++++++ apps/kimai/docker-compose.yml | 86 +++++++++++++++++++++++++++++ apps/kimai/metadata/description.md | 8 +++ apps/kimai/metadata/logo.jpg | Bin 0 -> 42403 bytes 5 files changed, 140 insertions(+) create mode 100644 apps/kimai/config.json create mode 100644 apps/kimai/docker-compose.yml create mode 100644 apps/kimai/metadata/description.md create mode 100644 apps/kimai/metadata/logo.jpg diff --git a/README.md b/README.md index 37854fbb..e4608f20 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ This is the official repository for the Tipi App Store. It contains all the apps - [Kapowarr](https://github.com/Casvt/Kapowarr) - Kapowarr is a software to build and manage a comic book library, fitting in the \*arr suite of software. - [Kasm Workspaces](https://github.com/kasmtech) - Kasm Workspaces is a docker container streaming platform for delivering browser-based access to desktops, applications, and web services - [Kavita](https://github.com/Kareadita/Kavita) - Kavita is a fast, feature rich, cross platform reading server +- [Kimai](https://github.com/kimai/kimai) - Open source time-tracker - [Libreddit](https://github.com/spikecodes/libreddit) - Private front-end for Reddit - [LibrePhotos](https://github.com/LibrePhotos/librephotos) - Complete photo management service - [LibreTranslate](https://github.com/LibreTranslate/LibreTranslate) - Free and Open Source Machine Translation API. 100% self-hosted, offline capable and easy to setup diff --git a/apps/kimai/config.json b/apps/kimai/config.json new file mode 100644 index 00000000..a091ed33 --- /dev/null +++ b/apps/kimai/config.json @@ -0,0 +1,45 @@ +{ + "name": "Kimai", + "available": true, + "port": 8003, + "exposable": true, + "id": "kimai", + "description": "Kimai is a professional grade time-tracking application, free and open-source. It handles use-cases of freelancers as well as companies with dozens or hundreds of users.", + "tipi_version": 1, + "version": "fpm-2.1.0-prod", + "categories": ["utilities"], + "short_desc": "Open source time-tracker", + "author": "Kevin Papst", + "source": "https://github.com/kimai/kimai", + "website": "https://www.kimai.org", + "form_fields": [ + { + "type": "random", + "label": "KIMAI_DATABASE_ROOT_PASSWORD", + "min": 32, + "env_variable": "KIMAI_DATABASE_ROOT_PASSWORD" + }, + { + "type": "random", + "label": "KIMAI_DATABASE_PASSWORD", + "min": 32, + "env_variable": "KIMAI_DATABASE_PASSWORD" + }, + { + "type": "text", + "label": "Admin email", + "max": 254, + "min": 3, + "required": true, + "env_variable": "KIMAI_ADMINMAIL" + }, + { + "type": "password", + "label": "Admin password", + "max": 128, + "min": 10, + "required": true, + "env_variable": "KIMAI_ADMINPASS" + } + ] +} \ No newline at end of file diff --git a/apps/kimai/docker-compose.yml b/apps/kimai/docker-compose.yml new file mode 100644 index 00000000..3272c5d4 --- /dev/null +++ b/apps/kimai/docker-compose.yml @@ -0,0 +1,86 @@ +# Adapted from https://github.com/tobybatch/kimai2/blob/799a534b8d93d18a2d10573457936a31052ce8e0/compose/docker-compose.fpm.prod.yml + +version: "3.5" + +services: + nginx: + container_name: kimai-proxy + image: tobybatch/nginx-fpm-reverse-proxy:latest + ports: + - ${APP_PORT}:80 + volumes: + - ${APP_DATA_DIR}/data/public:/opt/kimai/public:ro + restart: unless-stopped + depends_on: + - kimai + healthcheck: + test: wget --spider http://kimai-proxy/health || exit 1 + interval: 20s + start_period: 10s + timeout: 10s + retries: 3 + networks: + - tipi_main_network + labels: + # Main + traefik.enable: true + traefik.http.middlewares.kimai-proxy-web-redirect.redirectscheme.scheme: https + traefik.http.services.kimai-proxy.loadbalancer.server.port: 80 + # Web + traefik.http.routers.kimai-proxy-insecure.rule: Host(`${APP_DOMAIN}`) + traefik.http.routers.kimai-proxy-insecure.entrypoints: web + traefik.http.routers.kimai-proxy-insecure.service: kimai-proxy + traefik.http.routers.kimai-proxy-insecure.middlewares: kimai-proxy-web-redirect + # Websecure + traefik.http.routers.kimai-proxy.rule: Host(`${APP_DOMAIN}`) + traefik.http.routers.kimai-proxy.entrypoints: websecure + traefik.http.routers.kimai-proxy.service: kimai-proxy + traefik.http.routers.kimai-proxy.tls.certresolver: myresolver + # Local domain + traefik.http.routers.kimai-proxy-local-insecure.rule: Host(`kimai.${LOCAL_DOMAIN}`) + traefik.http.routers.kimai-proxy-local-insecure.entrypoints: web + traefik.http.routers.kimai-proxy-local-insecure.service: kimai-proxy + traefik.http.routers.kimai-proxy-local-insecure.middlewares: kimai-proxy-web-redirect + # Local domain secure + traefik.http.routers.kimai-proxy-local.rule: Host(`kimai-proxy.${LOCAL_DOMAIN}`) + traefik.http.routers.kimai-proxy-local.entrypoints: websecure + traefik.http.routers.kimai-proxy-local.service: kimai-proxy + traefik.http.routers.kimai-proxy-local.tls: true + + kimai: + container_name: kimai + image: kimai/kimai2:fpm-2.1.0-prod + environment: + - ADMINMAIL=${KIMAI_ADMINMAIL} + - ADMINPASS=${KIMAI_ADMINPASS} + - "DATABASE_URL=mysql://kimai:${KIMAI_DATABASE_PASSWORD}@kimai-sqldb/kimai?charset=utf8&serverVersion=5.7" + - TRUSTED_HOSTS=kimai-proxy,localhost,127.0.0.1 + volumes: + - ${APP_DATA_DIR}/data/public:/opt/kimai/public + - ${APP_DATA_DIR}/data/var:/opt/kimai/var + # - ./ldap.conf:/etc/openldap/ldap.conf:z + # - ./ROOT-CA.pem:/etc/ssl/certs/ROOT-CA.pem:z + restart: unless-stopped + networks: + - tipi_main_network + + sqldb: + container_name: kimai-sqldb + image: mysql:5.7 + environment: + - MYSQL_DATABASE=kimai + - MYSQL_USER=kimai + - MYSQL_PASSWORD=${KIMAI_DATABASE_PASSWORD} + - MYSQL_ROOT_PASSWORD=${KIMAI_DATABASE_ROOT_PASSWORD} + volumes: + - ${APP_DATA_DIR}/data/mysql:/var/lib/mysql + command: --default-storage-engine innodb + restart: unless-stopped + healthcheck: + test: mysqladmin -p$$MYSQL_ROOT_PASSWORD ping -h localhost + interval: 20s + start_period: 10s + timeout: 10s + retries: 3 + networks: + - tipi_main_network diff --git a/apps/kimai/metadata/description.md b/apps/kimai/metadata/description.md new file mode 100644 index 00000000..f5a9cf7c --- /dev/null +++ b/apps/kimai/metadata/description.md @@ -0,0 +1,8 @@ +
+
+
j9r#r9r`C-C&Eh&!F#@#J0?g$w)D}XZtP21$n#+7H(bg zhpw;CYR0B;0PVKJ4H+*|GNJ~Pb3H!=ue_`&SW$$-1`?=U3# _fa--8+W7_b$M_E}Q~ d?I|Ep`}>TGg56dX>8+ zap!Y(QwEVTM~&V3Y+Gx<#D!Ax&!i=$I1~4ev|8>{Gbx@7bB}5QVcVjJIF<3gZ@%FR zc=JZYe+HxMt-sz^dcV)|bL6>7EBZnV9B1?l|DBR?u^6t~lW~XEn@l35%LTZ`F!y}r z4N_r_?Lho;{fStpObhV&IMs@ASd_QB-_iVux1nn*w!QeD!yz8xom T~dUb5caM1Fb>vNBargZ^nfBDD2`> zIFGb^HS }X0$nA2R8SpenwbVkXfdWaoGrSYJ;;YUMxK4f z5TyK#t9J8s?48H-+n>YD$lbG6oKL( z(BDxCeI}nIo>4}6?CU=jGZ9 CMvrKoZdBR%2!V<}-&uQ@i^7u@Ce{ z+PZg63jM8)-pD8y{)Svd(ZvJp=t=HGA*BM2C+UO7WG=obKeml;^B9wuZEy8SFfEO8 z8E^I?rTK7j0nLiq4D?eYu2TY(u>iJd$p}sk4K#9aC>3O#YTWrodk2h@NbZRBj8>1B z>9gpwqwEw5KjlG$q(yW1dvNb6h1Yz;b+W|ZE81ge66&(yYtQ>{%FEp<;i>~y)Pv%G z-bc(UJ`3%z>k-{11l*lIkQzg?43Z!5XD797qUy5`i}5CN2cZsnmIDEBwV?w=gICgv zTyQJu%0R{xKqDTW?|U!f*lB6v4|hnc)6pD4GJKw`-g3CnhIY9E=wd%GaX58!1u;BC z(KTph@=ovRovR7|nAbXAY0rf0LaJJ)PyA})Po4eX{sdTEY@a|3-S>PmDZ_sIX24~C z;=J}(^IeA9gEEb vz8{8w3o6fds^$%U3EE$ni*iUot7WuLfvv)Kk` Re*TwjQYIE#V%L=jr*1T#4-Qg;?-_<;WQ8D-jXPdf%Ou204|HamY zR#S%nXL|>16huDq;=7fr{#QSv@K0gpj1+)~y eq^$oG`F( fGC{uWI2&rd1_luiy5P40?+yPn-#`G 7olD3O6Bvyg@0uf07cplv1gF4Z;I8O(&a;U78$ zGU*IrS(5@uYc2*AsYKyJP?758d^>!*uZGz@hD?2(3L2xl&gf$3ChR-qt5mqSKvCdW zKGd>5HrGkeav(C-uT+j%OVmgKu6C*-`>3KvidoA|TVdGr1U$ s_Gl9lTeMGyQh;zdZP5QRSuQ8*IXAPMtPDil )$)Nrxafy( zRzBSiwI 3H z!Kc;Pz~-C~;6A3wxZwT2zZ+3wvSQ!tFLf%`R$h11v$-M6z74n9D`r1?(h2_;-UuAU zeZkD6kE;yB+%YFwt9Yjtt%7&4Y<7o)hTCsRxPBa&zc)M$K`lyvPbBu2r`1I4N>uUy zY4k`%g30f;zS#ZbMc;)li!J>iRHGHOCMpoz&sqAv6dIUgq)mVxw0)t~39+8H!h`G$ zr*I7s & ~%{c z@1mS#%g5=GNMU|Ijdy|rxo$0ftq+>qEH79O@T9(m=QzJG-%59bODBGIjqh<`c4>P& zlz42}Qp=wML~@-bwl9QKRITTt_Smn8U-uT@QAI98(I QH zZ|D;09%89k)Hi3*C;vwC|3A3N`^L*~zAu%!MdeRT$~={G>iHG*i-!Nih^Wi28kxNK z!0_o~Ok#`QZvrg1fonO>gy=}HiVBR|i=!rL3Ld~^R{JychKjFU{)|(5m2Z5J@LSd) zX0eWwfLmvqVF~rDe?N%#82ondP^^O=O6pakp@)cqt+?^&)+vee%iK)N*~{zO$@ElS zm5t_&SVzk0T*~6J9I39>lp6j$nFH84a@uLzFf&g4Z#RP@AD@He`Ce>~T&-li;(7dj z`t&ubKX(riNqRvJCc@#tX4OaH?=#|m9IsTwR7}Q>Y~wOM+QD-@ElMlUs@S%X)*&CF z^pk`YP`WolSuN{z`bmk7PvPi_3~5hn^ >nzk_kgSGZSt#UnU-!TEP*;C9 znUcFj?TSNVmVd*xPyM2xOd3zq&{(u{*JDYmmY+Y~4twXAjTy+i7ba9;MFaxU-4yH* zQ>BYCb&Nute}86C7`xw+KEJwzl>)s_iKQ2}D6jo^?R{gR&V7KKJS%3 M%M374dWUd&iaaT#Tp9g-UCCG_&OxkY>S1|}ia8frK_+*^5>|Ruvy~1{{ z|2ci=>&zO=usbx@XC~V}J^YsNcna`h^1{ymMjG26x>*(41d>TvyzJodQUm{&c&`B} zxAqAz-=Dt)CM#C+-f<`5UUxNLjR`@Jo7(xBdAnK1yHrj*<;9O2CmknGu&r|*T?&*W z|14#=HlnEHcnzEqd2i=f? 5e`KbaC7`H%S866n8mgPebKlidD|$5+=Y z&tN|G(3RsL4oon+M9Eusx}JV% TtR1{4WhLQ-|+)5yY5}luu zo;@B%N!3@k(Z@;d@U>SCL$ 6qIWM~ zDT|uz93>pX&xz)J?>GiSMil$54@Yjoa|EN{ce&m4Pknaf)zf0(f){Yh6`5Dd6fzKM z=A(LC^jz9Ju^l19Z=7mIQ_2{MPyb+cXi|6OX_GY93Vv{59M?1o&T6YDIF{RuotM3W z$s0UHuE@_S*Od=0cD~0Oj1hDMPdJr4aEFH&1Y=vXm>nLR6(uE;?#TmqGvi-)laR$h z&8NoeqmP3r$A8EA<^R_qhYmQ_r(*l>zTwaj`p3|9kB*RY2J+ >tPpq0$I+HUp()|X@qa0NJz##OcsSDtw%16IkZmrXlJd` z7PpIhL2_ ga)c*x2S*_kPan z4VZ!5np}~z@Uodj8S<60wQ6h+%8T$jPpg=>o^7?}U=en+#r~Rq|3KDKgKEwbzk<&* zjO^ba8k#r=U$0gSqTuXUK0m6U{Wv5`{dLEYy3^PDW)W$ES3;e2hH?zMHr$k*3ibVp z#!@S>!tN%CAoK&wl4UZaK|0`#3Qx_Heo$Pq;j4O8t`Y>tXly3fm|2kQ8Y+QS9O` zN=33{F@ydoQE`(58&*}}?~f%djD3pPqaE@7OW2#jdKOaYZaPlu7Wz-zvQFn1fpij% zt(PoXj)e^T69o)82HBP9Ak=Wm32>Jqj&Azh{-*v^_hFvOZ+n)#VRPWs0h759PA=?!I5_AJGwuP!VVgU7toN}IvPBUD50Bt?^q1mdL `H}mN_OAIr@i=m2#UH)}*LX zJf6lGc17{%k;zUpfLM; atpj%}|uzLblYF-(59!EV(O( zV8 LHM2R`X^M*^72?Wj$`wmXGwa~6 o43bF2QDI(6_4 zQ%Sk7dzpJ^$8jp#7vsHO8-lyp|L$vtuQc=t3VI8FmcV_xeG!psu S z>+`k{HcubDUc)1(yqjsu_oh&|RN*k&p)HcKe{0sz@lLZ;BeQYK&qg`-wujjKHR21C zAgF7{Z;8AswZ{g=pIrQO+kYtMIKMk%@6#S_Q8RgtaxK?$I(uo-Ln-NHHia%(9oOU_ zX`*KNR*-kU1uC@45pRgt?l(i7sbvl_bw=8dT;4K%d6`E}S477exC6z@hl|*3iDx%O z4EBu9N+}ss &+A%dGK<2l);>%-o^F?Bjx?@ zvxo@ATik?6^n^PsMsnxa27b2Hcd&b908ud#SH4vimKS%=P5#G8tG6UjW-)dNb@aY3 zyrqf|ka^ETsJ)Mk&@kC~@aZ0E;%_na!seVhv;?!I85@e6fSwOjC5F*pzg&_E?H{%0 zWq5VlFLTV%P9m$>Mxdy=c+4iZa^enRO?2yh!)bKqxLD|#Ej;hD@Zjg_W{+CBPq2-) zi- {fvKd=-zD` z{a?XR_2t-^ptCoE3cm~Cva3{^VWSf>)2%Gbl!s)!SzILT+-u#BerbA@gT*QO7+)`r zYN*jWsHbfr^7|%*lljP6qRn7*z=--zQPpSB4R*#O@T<4|&aLFYd#~dF&-Xz9a~A%i z$id;4`GknIf#jPejLVAhH}$TkKI~wIG$o4oLp@p$vc{^SDk445llq#oh8C1O^a%8n zz`q>j@NdHGx;*l=U-%zCbUsLcZcwKSMtZgUa7)rO#@;PCzX_lTAZ|8qZF~iMXQhok zz<1s<_{(>;2buuAalrTA23Cjmf_f6ZKA?5!_J})(c^G_HDPU;zs2(=sj%xabn4CUc zNB@x=&`dh4Z&BI&{94N 24%%tF@@h>DVE2vM^Y%H(Hw*(rIQO%#w7AI#GPKj3t7fbr*T zzKTvwxPQ>I-Y8`u7hBGkx>mxM7LP8Q>fg;E#p)}GF5)f(S`c9o-B#Ju7;t2C3<& zP2|(e8u@&2Y!_0NyS2H@JcF)!q(z83JzJE2#^9v9Saf%^=&fz}xgFaReT!+5sd%Ob zr4LZ`oN(szJ=H(ppIi14n1<%vz09@GKh$_4vSxs7k7z~0H!W_eY781P))kw_X^GyI z7ZnI`Yrl${m>xUvd*)Q+9;mT3WYAoqzPN$dkc{%_X3$tqmyzOfCnX1EUi?lh4KG;K zXd^9U?~!m-K-qpJym4-2q@_ngByD;aHWat3DETTkyyZu(tEC960H!7WsJN(jYGYeH zOsH+qm&feRp5P1Qxj<`bd7$j g78PVn$MJr z$jd)W0{eX(i98o)B`)t4a1!-d+OLRmp0|-2xc9yweM6Y(?5;V}se6O!D`JE!*OO+? z-&87?Azn*BF6bCAnWYf*zbQ=*=W9_6x6L#jNx7+oy_WVZINv#H8s81%lKMkg>RIyK z`C6lw^RE;{;VfI3dFycFpE^b%(zj3{;PEARVnImscwXGsAqFHDe3|WhPR}Ufmo22G zvUaOAWcckiC4}qgw? 2y8E=i#hwj?ju)W&d?U$M8k&;fVN|}SI%R##DxwRWrhQD`n zG4pzXyo>GtwU)rY%AJ63V(bAI w(a5^ya-;1-Vk`j@Mq{7|0{8$7s&u|AquB=@ z`G_Xj$4*XYSjXnK(bxOyVWh#n4zKq0$M{otd!^f?CtK?y$wd_Xde&dOTvE$VuQ|NU za8Bqp@QkxG4!`+ED_`8a5(DZ^PGu2t>|910%LR%f(K}2}x0U1laNc7BxquX=nE>&} zU=kB_5z&xXu4w<~f7uwpY+*C6V3i1pWb!(77Rl;Xg>>HPjpU@UPPz%6{xT)?D)-TJ z?OQ(Q(nb6r*EtPM9q_{WeWP3A-WS!qlxq{E-i6IXz$$-XDu1u>&Ei>CtL|bh`RQrA zzzNHHDHXi9*n$~wscgX1f9 wH5q1^p$ZmU{nS_&7R$5MHaB6>I%RDVh`r0$;--#+wUxrg6x^rKh!Uw zZP4qz83Znt%FOi#^-DC(omuwZ*p*$DbF0c%YK|U7JSJ`XPW+7Z{5f(p9Q!k(a9cC` zC;7yB$*SxhItazLaGV;ma*!l-Bk;?)xIL~Bz6(k7`$ck~(rg-vw$MRd5HR-evK45D z iuiyDY7l{Ek48Ena_bieP?Enfeh(S__ Ki>nvR+p9o zLJ4aDMZ&xsl$rL}pI8<1rC(;%Q>8BOTZtw8pEa*JXzaOKT@e+C>ZzVDL`@||a=kNa zi7#_uc1WnOu(%C3#zW*DoK#gaA40TG=BpK*5*2pH52^mN!1_^O@8Taid4IS|()&eE zoo_DBnlFE34@c!LGP)+k7>cEs52}ZnxuPy5RLHO ( zTfEHaPPv1BShbAL!NQ9DT6)?Nc`32#EbaIux%nvPg+IaKejDv<43 z-Lw1+b{??Y-9o4F58R8ldB`hdx)AyFkddsc&3@K!<_K>3*?ggJ$l^`S!|wc{y(%|z z-J=@ZBZvB$+c_zbCHsoQH^T*r?+vB@{-@?ajQt?(6or47ra-aiL$kRs2KMMbF72Ra zy;Ilmd(7|xawJNDJ7UIzaXEvfPV(H#X5FfoDWQ?9936<}au3=W( +%|5J+G3?){n-jz5v6-hnOcwpaz|I1RIS6YEWo|w`isZq4$#T&ztAYUU9B9=1 ~SsN>+QMgwUmKQ$WA zl-wjv`u*5hqcM>a7hGy1p0*g;B>kaNt5ymdV@Ya1`Wz&>m6UNYUX{0p>7w`5e4teR zC?a@LsJb*$9G(gr+X^rqA@owtHPaXw=+_5;8N_gdc%4O^?~Hn+<_K%-rhuun)uesD zOZ#_BLou>^Xh#36L}Gf2Lr#K A=}XI71X!oH;3fHMjx?4{xWP9Cb0lYgd; z)#OpAIn4&|jvQ+@qNXa}v?Z$>rD@a)tm?dt;h60G)w`b!6Q;2EA3O!|O0}VlV9F{} zj?5^KjJs8)2CS|YH3I^Q5AMrHWo+o2V##b=M|M5U&t0)RmG7D5c0;CO@?G?qpVi|X zOOH)sABX}oh|Vb{)(wftZVw5G ?s5Ig=>ho436 zqW)$%3D{0;k^X9#v;3nY+NdU7^R9W*RWVJW=(wx-$IlMMmkxG|>Ah@ApVO@yehgB? z;5N3Ex|r6G|12x*w^7$6_5S91js9b%-z!*?@_CZdEsM6zoRU5`3+5WXx^Oe4;QLg0 zTcDY9e6gDA*=Hhe;!SMzhb+<{t`}byjV^xbqP E}Pu3Y~y2{kkQe|>)6w<5H*d| z!fNzF^byCgaTEW1(%CD2AjOXC|I{@~_~^80dd+`as2tm0Vf1Fk)8ucNQz`0~ndpS- zwQoZ`<5XnK7CnPdGjfGEJ{sC%S@Q)xg2?i!wlwJJQmLh3t49?U;cAtt`zo~E@C9UV zMPOg 9FWJ#uEQIAY(5!kB=n<|BO z&F{WzvIbK-!jDf2)(2@a&u? lEoM zwJ#Q |aRnhKG!h_l5OwII`p%%;zIXYof zwG~Zhp2J_co~j&z#XMuIB@kBs?)w33xS*thb5Br0Dcb5b86ET#F6N_Wxb^5G)RZ5z z5$s<2>$W1RF%b3e?eDM5wB*N 2V^q$yP)@zW~neu G-V%^O|4aW@x4W{dduc#>cMGhhnx$4Wm{Vi*z4z@K748S@ z)NmjtU*LX``Ej~4&xT H2dxf9W zxaPL^w95PIS8bjIjiETF@){G)?5O-Rz;N=0=BYYLuXtW`b1g*K)*!Pzrco85-k;Ik zK@;b7hht^^kLE}R)EEhvvjivfvY*1GgvP_~AroJIuO)EbV_paDV7;F0D9~j`9Q`$^ zg4`2xH7qC^+1rPvJa{~iY1IFzLZeR4(o@DxV^k)aiG)+oT5uVS{^DtAq;1|YR#@`) zjJM 5)cG=UJ;Puy`@F^+)WCDucBQj^IuiM9_Y(c&w8=~- z68o{|*N6(?{i)Svg~4&X5lWf725p-=tj59tvKS!C*7f67(OR3%Uix~1L0p;HmFJg> z&!2de>)#t!`dCDpzu?g*q|V5}xKggDGowE*aaBR1m=G3sq00Y9;#ad2u%hJ;9rXoR zXcaj 0}lsCY2=iL71)e5xoc1#gHtoH=(r=VWn}STA|*At?|t=^i~fvX3v9;K zPgsh_12g;5SABe(rYHY(Ykqvv4zXTiYXhv>q8Tx;Tj;bGjjBxQ#?L{pEnNv?!3G-4 zg77E184_c$8*^X!cMG41Z)`Z-why~(TK1K@(bNgweVfMEdK9>d*)qhBZ(5K`I6I-C z<=>anIm^+C#meU&F9ay|q(E2b?U4OY3Rfc-XUij*UHE7Wn_KpVK#w%oCmx9OJnjW4 zX;ycP``Ovt1*@sRyc!|0*?wk>PQI;3F|%q=yJ)2D`_2xGa_RrKHO;?ER93-4gniIL zF$H?$P&BtrQfh50GAGpo9DR ucp#p3~Z`RH#PqR4mrjK&oNhMmuUmg)7}y>P vlwu&Fsfz5LUbj2C?)Q~sO%6FcmBqcPR(Ji-k)~{6Pn&ysUP1% $(*rBl+1QpyPenGSxAT|(dODVB1Srt5V-k19XzFL0lE3C^0a~x- z0&u{7n5fBSpeIlX%x}mC0MRaiq4qPz5hQ2g@3r>1F-1|rr|+Z-@rOZ&;=DDlFoMvQ z`i8f-45N}BwEU~)A5oJcaplRXJE`Tl`ugvNQg3Bpbn5cBu&psVeK9(OH}{zRatiw@ zHxjYY*nNp$<#Wh$tMnEP*5jkE? -I|0Wpjq#=x|^MJ_cSSd(2)8bqA$X_6mafkl&N8DSf*R 3V9q0 z|FH~b &A_a2P~xgpw58|6>c?2w8;84) z$gS9WRrrzl`gg}kYQM6nA7{{JyFXS)rE9eio^_GHxarVl4Wq@lS&qCW&W@{6uCjNX zkuyFM8JRfF+L!?O@!{n&Pc)vE#=SfZ`1*+6X?E`kKXEZ1zc6n1HrkOVwy0S=?{t8U zztsn#oJ*VHPq?B>r`{AQKPg*kB)sd{LRNFVTK_S#dku-lJ594r0>4wmhQwn}P?tQ% zi+OUC(u-VE
`7oo;Bz`;cX)1sND7$ z(F3JdbR=12&$^MQzuLV|kRGj)Nw^&Lvf+kiCJVEVA7dXytIO?hn-QIxkia-5WBitO z+SeNvo qT)7Y0sO+?HTtg(Mf^ z=DO_MC`EYI{QIRF5Vs=|a?(nj3KZpx`-g}}oGc1u(IQ55=@FcLxgzm~FTXnB)-sN+ zldDMC @(G-Y#p8i6Dws}7A-K@JLr*GbO8LY2>yke#;<$K1=;6?o3` z`k&27Uu#cNu87_mnaz6vbM6f%Wt?f?oOoqd#U8KvJ-!3I8_&Hq{b9z_GAI6t8%ZD9 zk(t~-Vj1guD8aYuo+lD>J{`|FO?eG6@Gkvl$lr miC5gpL`*A;3pIUS5rYxWD_%`Do|>Ax=c`zDZz3gj?x*+h7p Ty#&N4Yag&?3+&MFAsY};E4s0v+#)qb{^5W9xXDJo=V+TMQZWTm z^0t^0pFX|2nBCPC^3JA@)}wH$KFhCAwTvhc_L+bm*qqgkbbEVQ(YW>MnW88;5slNl z6|3aGq)hNXYD7(wJchjg(2Y)$@&+UQ=ndbhXQK9*GuB+^6;*j)+bin;3hoy8L+9`g z6L4$)(*DzBAJi(pv5 irm zcT|QSIoXAu^w`j%H=|VJmPgN@)x4Y^s0WVSQ-P*ixxL`35laZ(AjDr!5b9ZOsp+ACsYwaKVGc`(11M^-d5~mw zG3rxqymm&eStI2x@eTUiPfPPxM}Ses&$PL^3z;#b{Y3NX8eOb>e6zZZc85~fT`_$T zMH9j+#FR+9B{#ij`m}qz!j~X@`iUig+2h6#5YB)QCxW(vRF|$aSA0MM+&-z4bz7vV zLG*X3VrBH}=K6b|)gAd85+6EVS(Rnwh!8WkIO! Mv%qW5-?-+sd$4Z>F6VZM*_S*14QT}Q4F zm7)aR#5o7~g|{tBegs)#M1y9wHWBUT+eT_j1CeKAbztTM-Pq>7MqAGQJs@$hnWrG# zQNv*u_eP6Yqw7dT@$Q*-VncHa7Oq4}h#{V#N?xbFswy^q)ydwuY*-fhDOG>SY`&Q9 zoY=L&%I)G7=8~Ub# STB>aDoO?wh zr{mY-;C^}3dU$pWr+NHEG4|7^pV8BM`+SAWq4}1m_t&2K{a*WeBowGZ{S;_@bMhE- zdZf!LiK;!a? Z?6!k+iUXlAw*L(jC(Jb9HePols*`6?+kr2I--BR( za5fk_wgrft>|3~|q-ho!hk0wBD@nKHJkN^_uc5d5_C0Faf&ySuK{YxF$yAS0nqI1l z{kdKAG5x&kR1iR@L?!KOY6Rke`?P_zsG#jgxdD?)GWr_v+8~?*ox$|W`j>u_TozWc zCgttSNkb_EJ4-gUbC!4Z5&B>!Q |S zPv(7pFya&Nw4KyKOt*`QJ@C1!0MZQ{I(b~7FKb1o<43Og$)|zIAQuCH)yo1|2{KOa3$7b=M zQvP>G4Ceo1l_0GeKNFg 9M7#V{7kQS9T`u(|`02VO!L~QYWnn9a z&}2?b+_ 2gowBy?@h!_&j}Q8GNHv{fWb^Dbj)T;qwyDpy2MG as zEF 6Y6vZ9rec2ixoh&9RcMrpX$>A(=c5`zt7vG#eOrP zrI{NdepiDVFRvrxVHG&a1@Q?JtBeR_nX!=5_p+swrVNuu1_p HJpV<<+^=~RpeS0W-WIg{dxvU~(K7@4??PnQ4PU}T@f z{w6#7Y^wop+Qka09_=Tl6xkR%o~Gxz*8wY~+>LnUBYGcfK{_52FIX}U2!9#V gqM zuf^;{Fc29}0;tJ JGT;+PWpaP$QgGR&Zi|@b8~AaKSgKd-LFysm$;f~sU<)}%tj=OW!skaq1D`ys zwF#&Ccr4h?)w?w|+^z*aSAbZ)H}r?@bzyruHC+|#3SiEVPjL;?DP9!QTpa0sk}mI1 zapMcbEcesB3Z1LgQggX*S5SavUe~j{qrd&MV16HL6IC#CPAXQ*J^wUetKf;Qkz%5G zRk2H!;iCJHuP4^6LxC}8+tc#X^JV_r0q-6Y+riAfa$Q%@gPLnzypiwwROz2j^CJV$ zvfi~|Vge~{khM H;eHPHyelquU)*-DCp(gT&Y-NtY~nbbYzoGqaJJPhh*J zmv2qyB)@XbJ0d7tIcH>GzD_!#-4ec#H%W`c%?DUK(zSxP2Hx1D0lU~xj6()lz^37C z<@9-stEq8!#Qi?$ZCqwGACBt9<$s7+4Vub7^E5wasZXpTM}(D?&Jg1FcOTMIhz@88 zYX==Bfy@%lca?F4w1&COFXkL_=1+{l@-{@))03*6o+ooye7sjL z8VNf; 04k&T)ZEI!uhw# z0YO-A2^P;CW~`MQajsnWyLfK+u;DA~-^FvmTuO|Vhr6yp$-%`OQlHkMR!j{M&Y74- zP#4YAJ|x;zAV)_?GD8-MQc=SVyhKRh{go4shljtz7Q&r9X=NV{(EHo6>X7yRTn^8L zhz0#ekJbPS?dNkK`K0@wU&sHF- pczdG9RM>TcHtF5nvFK0qN<* zHeC3n9bPiou&VRxS5at$i15MohTtD*2ka+AO=c1%yHFYc@5Ub|Ns4ta@}WP;vl$ zgTgW*R;Mz%ZYJC8 0QCMDgN$n8!BGmn+mBEyiLt(Yul%7r`{W5tmarcr}(xeCU-85T=e)e1)0ay0m` zz2;trs@znL363Q&`r&vRE}8CGaVK)D`1`>0!P{R@IB9IU;K$cr1uI{d5oNgPaeksb zz*S)_j^A*lD?lp!bdiWq)t!=q ?;N*gW{KSv9O zQ35_T=9~YxRcF%1uL>A`d0+w&N#Iv@dOz@Nz)|G z#1G}Vk8H}e#geau11Wuk&*qENl%J2%#7OcxGMUQHspLO_G6r`hn1uvpk}r>Ddp z!8giOUj(=-9zB3f8+`pk_fIR@E9LQqEPb)1Z>&!N5QOiP5Xs-OFNBfj@_ec~X(_ zAdZej!3%2(iPRtLq*_&w{DY&VOv (Vf&Uq$z=Z`B*MIHiseQu+O77A 2X0eVoQ9FE@^->C<5 ypM$XQ#pla)&w{PaGTwL z|ALeNW63Yy9 YMBsIZ^rCUs7^C~N%s?1 zA>0?8alR$vfAa$I4_(YO_y61+>?08c 2Ky}$9N1lEQ)AF$Xr7GNcS&LYwgBehu&Q_8JH zZjbCh`bzIyyz4=nsrb4ldC>DftUkPsKX-7*f5vQ;yW-X-p$qmlC#E!vyX#?2B_!}R zO=i @Y%f-5hS~^1iTZ!uzuf*}{e~>8)Btn0DU#bFNW^A^ap!6?qD{R5AFFakPz1 zJM@F^nqT|XUKCjWXjhw9e(>>A!uu2NBeZ}VoCkmk^bq@+%UKC2&oYPAjj;EFi;}aF z^z%J6Nn7304`T}xTF&1|t9_u+t*N-i-bGS&w !iR&P>sj=U~R#OgH2p*83R-qpEseH2KplUv@_#ZGrX$WjKC z7AmNIZ<)udDJ5V=tjH(GtMZcCt8x}^Z Xv{NPRA!Sjl@c3v5&lOs)S=A{(YnPdj|7UYbPEf9>@O5(H<;Wr&y_j)*BUeyp3 zS@T(M=cDP$pSWeDV!AtOp8zG|$KN {YOUbFNL>Cg#C!8cSx zE3N0$bCp7%ANCrQuD>iB$FGAK`jMlZ1*?TWt)rf wh1 zryvqEOS$S _M*`J?-8WtlM$sdhYv>UnI`1?u0+gt%0F;)=qaJeyO zsu~4%2I%>LJ1>CD`lDb^*+CBy8 mfI~f05 zkZo+%!D_7OrGNS2V+OCm7t1Uacov~VN^b*k53o5Ov|poCmlaYnnGBED49w%@- 7@%Y)52Vsu69KIrEkgV7_MU+$hojG~o*E}VDQ2o(GcofF1 zxT5Eg6|=&3NFWx4Ba1?pY;+B*01fYxVuDl6V0QZtm3R#_m!vLm7F|UARtWId%8kc- zvzAwk0vj3a@Oa|HODU=|cJXro$`gC938ciNiuq(Iw5(I31i(7wt?ihUY|n$6O%cPF zEjCa`Bl48`F8H^_uvk4&QuT!A>>lJSwMRMevrM(eObt+?uCMN4p6K^$Gl^U$%6s); z{S^&V0kZRke0^YQ28~91{lsJ7WHiyV>3Co9-4B5Fh;Jn5h}1SW9T`}|>~<}_+j73# z?%m fu}87zdtv+iF>?)clgZ1{Wc8Ze0TXKQhaF!tw_m8RbujM91<&eCnQB;t#(#hfOa^ zp4$n0df`|9aQ&C|-`{*XI6ME#zw2s&JeLlI^`|$M0{*asJVRzs1m`fbWn#?=N&+tI zGVuIZMwIwS2&=RFEGIQ0CAQJ$oE7$l%iwB>VNub!)STkv)Sn`g(ECs|M)Dm~q#~sm zJo}Bbi?>5SNx{fyuY#~)yDmt~UODGj*0v3!uc*u;d(^eDl9QPumMY+UU|ycTfy#sj znWzFRCZlb3%sqm+WUtl&bsFFq{E(E$(eD!wzl_Z7-WTP?_Df+`AIg@i4>_y#9t2_! zG1%pM(oC?cj#CEv`1&j)R&=Y6N>&fEIHkj7H1-ZP?CYWa2$mKZr#$DeU@oZRW15I* zjhZQKx~n9Fhsa&W+h5VF0y;05f1L8itQzDn6(=2ZRjjy2Es*1rZZ7)+J%QY~j9V$L z2QE8L-}|MHOrMj#Yx;*ycKVJG65$C|n6UU&)#E~?Op sO5_@O%0spsO)NPydQSg((U}P6xv>o|@ z=Bp$9>Ebjy>DT_t`<|z2Y8!iB+IFzfKYhu7G4rmqpl~w!QQC z;AnQcVc(WY#Lnzk^3E}gG595_r$I`{AG&37|1kcimf7D-{x8%2w{ZdVhlDgxQ;DKb zi)?~e^5UGEKHWaOHsYZV;fy58TK<&?=W)ly;RU>({uGjEftne)705R2#&lu8MS4=q z8PB7UI~zM H};!6cZimox9sSTze+sy%d+U+QIg2 z*mUuqWt<0qBq_vK(8{vod3ruD1-jBysx7F{VeH0-Q{w~FxN5DYD#2ARd*$cGve@aL z0`!8x-p2Yp7m}2Z?ChHOEaR<#m5hJ}jEvV5fh+q)yDIHwO@&%(m3{nyvWEu@H^Q_t zr|?N{$O$AHkUm#~Zed-HAh{v<61I{op(NCTMR`o{zeCncJSlMhuB}Y<5XzpQjyW@6 zL6q7|dg&~rE!ibJoB`itHPmn<7DA7{0xj^Od|+;Qto~3|<$a!wUb^7@q!I#PjPb_u z0*xZ^=Hv$3@dds8 ?2j@qmpHf? zZU~6Mi$)#|b}z$UH56MvSnNucq*71?;B{A0;sMjCKxP1BfbIJ%Q@hv#$cH~)OR#I^ zSr3QL5+C~F$HbZT)s%U31?b Ok d(Hb;%u-M(x)!5DY^UAcDaegAk*+mi4*J=KQV{?gm57x7b5__0yL=D83YK zsPMn~Sdu#OeLlWdZdiY>;WZ$NhKZLWr3q;wu`~rAw56Dsrn|1*=DwqvSBR;9*7&U^ z`3^@!jyf+!7+)+@T8rAMK`E5=^@Q?fjHe+z%NiChsvrEK^sYxR%-)q7xb;mtCMzs@ z?6XW~XGtpR1{Xe>1w84hgJ?5d>AN^