From bea1c97e885cf59d2e7b417c5478496aea49ff12 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 4 Aug 2026 10:18:42 +0000 Subject: [PATCH 1/7] fix(seo): comprehensive SEO improvements per expert audit - Add public/robots.txt (allow all, disallow /admin and /login, reference sitemap) - Add public/sitemap.xml (5 public routes with change frequencies and priorities) - Fix heading hierarchy: h2 -> h1 on about, schedule, events, contact pages - Remove dead footer href="#" links: - Volunteer/Corporate Giving/Planned Giving -> routerLink to /contact with subject query param - Social media links -> conditional on config().social_* fields (hidden when empty) - Fix logo ARIA: add aria-hidden="true" to decorative logo in about page divider - Add social_facebook, social_twitter, social_instagram, social_youtube to: - StationConfig DB model + startup migration - StationConfigResponse and StationConfigUpdate schemas - Frontend StationConfig TypeScript interface - Fix events "Learn More" dead link -> routerLink to /contact --- .gitea/workflows/ci.yaml | 2 +- .../__init__.cpython-312 - Copy.pyc | Bin 143 -> 0 bytes .../app/__pycache__/__init__.cpython-312.pyc | Bin 143 -> 0 bytes backend/app/__pycache__/auth.cpython-312.pyc | Bin 5688 -> 0 bytes .../__pycache__/config.cpython-312 - Copy.pyc | Bin 1198 -> 0 bytes .../app/__pycache__/config.cpython-312.pyc | Bin 2367 -> 0 bytes .../database.cpython-312 - Copy.pyc | Bin 844 -> 0 bytes .../app/__pycache__/database.cpython-312.pyc | Bin 844 -> 0 bytes .../__pycache__/log_parser.cpython-312.pyc | Bin 12295 -> 0 bytes .../__pycache__/main.cpython-312 - Copy.pyc | Bin 13413 -> 0 bytes backend/app/__pycache__/main.cpython-312.pyc | Bin 14509 -> 0 bytes .../__pycache__/models.cpython-312 - Copy.pyc | Bin 10836 -> 0 bytes .../app/__pycache__/models.cpython-312.pyc | Bin 14910 -> 0 bytes .../app/__pycache__/schemas.cpython-312.pyc | Bin 15763 -> 0 bytes .../__pycache__/theme_export.cpython-312.pyc | Bin 13897 -> 0 bytes .../user_models.cpython-312 - Copy.pyc | Bin 1145 -> 0 bytes .../__pycache__/user_models.cpython-312.pyc | Bin 1145 -> 0 bytes backend/app/main.py | 5 ++++ backend/app/models.py | 6 ++++ backend/app/schemas.py | 10 +++++++ public/robots.txt | 7 +++++ public/sitemap.xml | 28 ++++++++++++++++++ src/app/about/about.component.html | 4 +-- src/app/contact/contact.component.html | 2 +- src/app/events/events.component.html | 4 +-- src/app/footer/footer.component.html | 22 +++++++++----- src/app/interfaces/station-config.ts | 5 ++++ src/app/schedule/schedule.component.html | 2 +- 28 files changed, 83 insertions(+), 14 deletions(-) delete mode 100644 backend/app/__pycache__/__init__.cpython-312 - Copy.pyc delete mode 100644 backend/app/__pycache__/__init__.cpython-312.pyc delete mode 100644 backend/app/__pycache__/auth.cpython-312.pyc delete mode 100644 backend/app/__pycache__/config.cpython-312 - Copy.pyc delete mode 100644 backend/app/__pycache__/config.cpython-312.pyc delete mode 100644 backend/app/__pycache__/database.cpython-312 - Copy.pyc delete mode 100644 backend/app/__pycache__/database.cpython-312.pyc delete mode 100644 backend/app/__pycache__/log_parser.cpython-312.pyc delete mode 100644 backend/app/__pycache__/main.cpython-312 - Copy.pyc delete mode 100644 backend/app/__pycache__/main.cpython-312.pyc delete mode 100644 backend/app/__pycache__/models.cpython-312 - Copy.pyc delete mode 100644 backend/app/__pycache__/models.cpython-312.pyc delete mode 100644 backend/app/__pycache__/schemas.cpython-312.pyc delete mode 100644 backend/app/__pycache__/theme_export.cpython-312.pyc delete mode 100644 backend/app/__pycache__/user_models.cpython-312 - Copy.pyc delete mode 100644 backend/app/__pycache__/user_models.cpython-312.pyc create mode 100644 public/robots.txt create mode 100644 public/sitemap.xml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index c81fe00..68c9a5c 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -2,7 +2,7 @@ name: CI Pipeline on: push: - branches: [main, develop, feature/tests-and-ci, feature/multi-admin-roles] + branches: [main, develop, feature/tests-and-ci, feature/multi-admin-roles, fix/seo-improvements] pull_request: branches: [main] diff --git a/backend/app/__pycache__/__init__.cpython-312 - Copy.pyc b/backend/app/__pycache__/__init__.cpython-312 - Copy.pyc deleted file mode 100644 index f2d8ded0f3e7974b892710f787d86571fe8b7182..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143 zcmX@j%ge<81lhkGvq1D?5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!($g=`FUl@1NK8&G z*3Zr@$xF-0FHbGfPfASAPR&cvPb?_VkB`sH%PfhH*DI*J#bJ}1pHiBWYFESx)XoUR Q#UREFJl}7iAY0Bqpa8 z>u2YdlIYq;;_lhPbtkwwJTx;YG(xE QVi4maGb1Bo5i^hl07d5@$^ZZW diff --git a/backend/app/__pycache__/auth.cpython-312.pyc b/backend/app/__pycache__/auth.cpython-312.pyc deleted file mode 100644 index 7c01d5e8813adeeec6bf54f55c01e7e40ec7da0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5688 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 diff --git a/backend/app/__pycache__/config.cpython-312.pyc b/backend/app/__pycache__/config.cpython-312.pyc deleted file mode 100644 index e68e6b6abbaaa34a84a79084b330c50e8b4cbfbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2367 zcmZ{m&rcgi6vx+iAr2T!0)!tn222tH0hVAAH7IIh?2U0^yq0%Kn!St`@5F3k?>f8d zBv@5Nq*jX53q2L}#IaTWlwK_PvKpyUrJiz2m7IEMXCCV$&~y)9&*#m&H*aR%c=xyd z{$7qgp?~f@j-27RKN)nMZolzvjW&MbbWRUcxmrLC1h_6P%jsR;ae5F3UI&lIsa;(h z?t1Mzslh#dzWWWM`2Y!TXbzUK>l$XoDf>g<$9Lx5{TyJEU``F_oZ7WbCsTu%$K86@ zEjmyS>U^b3?ZIa-KTet2yTRV0hG5hgppbeN=&Vn@K)pWo0rdd|)i6*Ps9)^|8UTu@ z12{rw7z8?}4np5~pds}f&@j-5dLHP4PeVYXKx67KL>UJPsUy%g;nM}6C{Rot1)2nk zt7AY@K-20t&0$Nt5fdrtC zIs>!Lbh18bV;bKabA4sJP8O%yjHZP)dJ|`9w zl*^DI-%=7YxssSxXj-}EXn9vND|o4fmke{MZd-bz>>8Fy2JSvkkSwN4qJr*;4~-*w zQOJ2_6u&M~e?({BRz$hkO_x-0NGLC5Wke%4qk>ki3l(e$S#0GE7q2d*4R>$3R?~NA zTB4NCDdH;1Y@lLF*(9NzhEdgbESHRINgFwu^2T;9pFy&`39BRH|1ozK`Q`$>B%g6B zN+so#TdI&LNx2N#lw{?UM^2K#+zfv!)T|w&iVy1JPX}f|je6D6bcdXD z+%D#&R0fio{vSupc8y0`*>z6uNlD4wNzp=Jrq`cS<`k%;=~#{D^7LdX7kcUbNVxZ=|%`fU87N`lK6=zY$r}eh_b8W(7W^DZau16 z&#}F1*%i|FK-`dNLBS=9K+5heuHog!4z1#dvMFwf=&mdkpxT-vLc_5IXIHbaU{(zC zg|O?^s$@`BQc5n3?$G*!Dkej|WxFUVB{8+d;*(x%K0|dIKQdmB?t5EG0Tt$FNeCfR zt6_vlFQP}LH)sVR5=Q80L#rP5*gdRLgN|#H2%?o^TCT>fnS+o`-$)$cs|M{nOPh8d zlQ5HhCId_&Oa`ftpkcc9Ifmz%3^N&FGRkC(343+e6HKB^VoWBP#FB*A2s z$wew8O!LQ8RJP1VM&(!T9kp?PFAC2s`-xN6%GiOzJl=m{M=L+UrVg8a?6F+mv)LOJ zgr^PXHhtrL&v|1H_wxs{ZT_J*b))r#)QT4=Up#ProNx0*FB02NzZz=uv9~>3baKCN zkfv>KBGzfe;w^sopxox;%$*-Sq+@S*6UpQLq!&-L_zQ=@HlLsYMq>M$ujtsQ7rW`Z z-K6HkVv8R?Ot$%}-X)>MPaZmLUhrmSee0}uY0kIKeQKS%-r}c@=zQ0`xm$komN&cB z;-iOhn_u&0(|$AUUCR0qvfgsJ#b5q~W>oeT@-04bly38Rn#I`l{d)&A+L{+#+?Nh$ zghelw^wUhf4RO2pI%EZ@ zMHGugL0jG;ww1g>KmtaYGMQ*z|L;xuLSB|4 zp4Tu>Vj3eDRhDTZ8Z%W!#wAm`CFkR{SUUbAJV?_ZkrOq?7-mA0TqoG2hpZqXYU^dj zgWU#ay2lF95p#+qhowmn6U_5UPEnjeA~R#cO-q4lSh^5}AjPIDCC&=2Q!rfHE_s2< znK#89BdRB|8aj$~Z{0_576^*qp-9nMZumnmOH8q6slx}R!N!+VOz-^a$rDf zC?mo$_x{dgt1d5#if%BGE!&$sQvMIqHQ@89*e@A%vEeu(~xBba-$J*SWdq)&ZF z5j;{4&?l`3o|26GOhxdLpjlOOlJ<%mO}y3+ywxx?lx_(+#Z^nUj0tAfGHlw*)Eg|h zJe(7b$^Z|iGHAidGluJomx^2s1Bp|)i@B4z;nRgn7tRH0GzC1I#d;slVGAMfwlFs~ zXuIZ=#g6k%q@hyc7@AE=2>A>nYcTQ+dcMTF7DqmG_pf#Lzv)ruJMjKLRAtra diff --git a/backend/app/__pycache__/database.cpython-312.pyc b/backend/app/__pycache__/database.cpython-312.pyc deleted file mode 100644 index 9a04cdf14b2fb28dfe987fb89cf4cbc2dd8a6d18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 844 zcmY*X&1(}u6o0d`A4!|EL1_=tQqe;RlthbI#iB&ii&D_~(dH7iyVEqye#DtAX;9f> zMHGugL0jG;ww1g>KmtaYGMQ*z|L;xuLSB|4 zp4Tu>Vj3eDRhDTZ8Z%W!#wAm`CFkR{SUUbAJV?_ZkrOq?7-mA0TqoG2hpZqXYU^dj zgWU#ay2lF95p#+qhowmn6U_5UPEnjeA~R#cO-q4lSh^5}AjPIDCC&=2Q!rfHE_s2< znK#89BdRB|8aj$~Z{0_576^*qp-9nMZumnmOH8q6slx}R!N!+VOz-^a$rDf zC?mo$_x{dgt1d5#if%BGE!&$sQvMIqHQ@89*e@A%vEeu(~xBba-$J*SWdq)&ZF z5j;{4&?l`3o|26GOhxdLpjlOOlJ<%mO}y3+ywxx?lx_(+#Z^nUj0tAfGHlw*)Eg|h zJe(7b$^Z|iGHAidGluJomx^2s1Bp|)i@B4z;nRgn7tRH0GzC1I#d;slVGAMfwlFs~ zXuIZ=#g6k%q@hyc7@AE=2>A>nYcTQ+dcMTF7DqmG_pf#Lzv)ruJMjKLRAtra diff --git a/backend/app/__pycache__/log_parser.cpython-312.pyc b/backend/app/__pycache__/log_parser.cpython-312.pyc deleted file mode 100644 index c66f89be58d349a8ad59df97196d840bebe1b4a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12295 zcmd5?Yj7Lab>7AMNq`_okd#QSNJ=CmJ|s)BB+fk?Oj5F;_n8ee#O&t@4T0$FTqE6Jb{s)KZ_>n*T z?%iF0lBw}@ra!tQ&OPVcbI(2ZeeT))x!dhv@Wg)KF)`G@Fux&${+Kmn?KK0#EHKY9 zJTuDhEN_UhqbyAgqXwE9M~z60G1H`Z)I4b#HL*m_6thfPN3D~#Q5&TVG5e?;d~?h( z=^S-Zo+aj*bdS0zZjBX9dPY5y-cj#l@n|u4hOket3HFI?x=Dq!0;u5UZHJKZ_Kju6 z47}s(rqR+U!#jD`>n!i)ivT^m7qFQ30haK7z*4>pFu<1sR`8X8oA}LuReUvI4bK5? z;cEfw_p&KK=yYN&XbzX}?WED;II(L|gR;$k#%eo~0bEgwP|(;qY_CO#|+ zij6=yIw^o->k7wW;d3!?2c}55a4g6wHc61>Xnb4(?hZ>c@kmHQmBz)&N&s8g{Qc# zs64}|9n>;glV@X`!so)0z{L|XHyev;iZLO`m z@Jh>M_|hbFw?qqRSo;_dAjIMsKi=c!P4XK1kF%yGq#kQCEYSU#^P6*50eETdmxcuH1FIAye6wI{C1OaRpXPWe=T9 zNqyD__U(!sV>sBs0v30$YerK^QI0XWiyk^u$u(5PD28W;gNE@j^8B{zD5;)dmY!c$ z485J;Q*!x)#%DhHkz(lX0slKU$1KScUj9mjsn{^3#Zhlw6l?7!PJ!$!dG9|fP< zBXw)`+!Mg>8USA%7oVo*^`T|(NxP_?yB*7FMfaZpe~|QpDqpMdmx=t~aqw09gPI=c zCFt+#*7e-{@JFh`nHRu6Q;=`f+I4`~d6ta3+HXyoo{}$te|jXZ->b>b5c!URJs4kV z*l$mOKTP^rCBdhLT|EYVA8D65j_tagq(A#fKT!Tr^rtGY1^n)lx}I&ApDMo&{9}DO z-!iE2=i9*V%+Hgfm=CJF5Bv@?-zd3^b)+`18T>BN4{Cqr*B1x=V19o74aQv+;Fhs| z$$U_o1HRhfdx;)0zG{1q>-DyRKSbI^t6Mv!_s@RtkCXYM&J)y24Vx$P_F2+zs(H|( zBJJS!lX<0*wYoi|e@>G2zUu$0-hZF{_K&oRI>GNH?NasE==S8W&vapY#d_4I+U_EA zb}-+^V@I?)ho1qzlgu4j=iguqRb^+7D!x$Hz_a~lhDJhNJxB)nx`!1*bSk2qth6oU zWTvP3PU=6~89AT(CKX_qIX1~o5ShoG!pS>lNE+r1QD&G)8a9&T;S0X0`3Q5yC&-|D zUdHqU8RI$`bJDnBT_*jMpR***8%mh-64ob3Sn?9Kr1h(Yq;0-g&U-c+=}cNKv62C| z0c*abJ!uzxw6ryDO4@lF@fPQ-NgL5eId;miJ)s?rLR&Ygk$0}EQ|sHLC287F_6v2v zZe0f~NV^{I+^VG_y>Ui5NyO$`_eS#u4Ts8+6a+P9+i-xap}!yxOsZ2#Iwn)4s+u!I%?m`v2(thZam7~X4P=m^Q6Dit!FUNjsR4%1 zGr6KoX5YQynoRMQlqFjnSg5>Fx!C!7H3(;M>PWVvG}V{&l`m|+v3+smmLWr2x2&i1 z+U%9t#cfNq8PC>~DO*yp&~~G3apG2OrldJ#%Lb|!UcK?^Qd=g_l5%B>%C5z(#1`8! zMcYzFojRB)YDgJ#7RKdC9bBwi+;J}uTn#j*1I@Q$AG92O`&=f_kve|Y6UbI^i=i8l z+l_DU`s*FnjgOH~P+mhT+jpxo6KGux>`n)E-|l|9>4UaY@787lr?Z%Y)N(PPMizM4DP2v89L>8k{IC%xaG+r7TlgO&&ICW(dV>3&{UJ*iS zqeIatF^=qS;3u5~zyfhGPVcqgmEekh>ynspY@hGCYjvcg>ydBH-uL*f&0LvTEcw<~ z7Q<-|x9ZuJ_H4^|8dq$MS(G|(<-qk9mpU`fh80tTNQO)?#iDUxOJ%ZC^VIFlF)=cT}c5a{dZGX27vvWSJ3WhRq)dhS^RUci{B9 zG;{J5|inE!lbpKt5Vx9XxlRQxLNB-^1 zUsQBhj6XKBfQoGl7iMxfeq_|_kfcle0hTs`C;0`QA1Sy`m`lgzt1EF(Mh%mNn< zi*+I&fYI|2_R0rSp!@WbWUvM0-HHvhn7>fM@`Vz4m+J{u*xuB7d(1p%gJZctYvA(O zbxV22ESi~wlSV>@SO4C`AmC>pf4T(AB-x~acYe=6R?99-x{GlAMi>@m>9yy~tz?k|N49#b3L2+3*Pj1?38^0a_WNNVE{ z3*-`H5fRq9Jy~87d}iS0a!(j6Kxtyc&> zLjV~eg~fS82(neHP7oZVgkmL{F9+qAPKIgP-@?E#)ikOC7e-?iue*3w%!>nEzjNdSuv6s@9IC!}; zhZ72`J|7O&tzsAhlQhaco!0A{DAhTIv>-K6R^aLwZ@JJ2S4!Ju^z>_tW|2{|$b`!o zIn*C}^fu5vyhA2~LLc5DZ_49V-p1SBBqs=-xAJ=x$`~f9RPK6M*!^kSYu4wfxL)hy zRzlu9!F^tS4*M|q)Azuy86NF#`f|(^E1t7{!B}toylyzM4qfk@O>WR7u%FlIcp**Z z_jA3c=kUUA&Viq^&|?!Ak;fms&2z4#y_>mic<}_zQ}-9xyW?>!I2+vgnv3jAvS_0D z^D^XRp5CSn^WBB~InO$~TRyKV>B-mUO?s2A3%JdR?A376d)acKlH%j=@OJAZ*V$M6 zf4Wxo>ow^1Y`9kPQWLQ4@h9p1KUpg!$&&dh`GBrH>3z$q+ca15gf&s3OTg1Pm@iq( z7n4P)=Os%*)$8(nByXZ!=M++XF2O5{p*YNsiZ^eKL_qchUIW45kocW+4>sYUTEe?^&0> zdCn&zte`%8QPPc$(6^7d`V0J) zzu4-lE2_H4fI{2;zz4C8E@dcdR10EQ$&8f*mfi>Q@tgPHN)losWbX zCYv}3FTCMr>&~_ARS(4BhU>*8juwP0Uc}4DNW}00912jMoZ!QVNlNGN2HSoadxz`c1&tbc|N|SWp!A<>^Tj z(!!ebYXNFoNVgR)mJtvU6BngWG>$i^xG$(759(#lR-%3qTNQ-}8a`8aAKk{m`Sv`f z3xO!|Hn$(Ib1jk(Lm9l*6~@8{9!4KvJJOwYBV1|48w*Ra`u-N*XyBqU>r%gOXx`p} zL7BB`6uk$`nkdi9aXmvr14DF3@K&9hHNX&7Bu-4hi~o@Liyso+hnUHTFsb=C;h2r? z6N>v4^+N{r0)s%!*CDC6AL*1}qx$CCc50w^IMm%YB$Dk%Bp#q*j!Nhuk;E_*hxFxG zI2OTNnGs0{N3jZ*gvd0;i(DNPGlImTAf^cJli&)6#Smf@p@bNUBUKHZBe@pLipi)f zULk~OJeD|zz(pb&N6#k6C@E$-l@uHKPJo1d6{{%F*ZGQ@1kDgQ#-yGM%Od$Mf!<0) z5_DC}Q(_c>G4lBn0=Gh3@n}~ltrsM;M;0a}(Mcp61tUe$nTlmvj>Hoeaq*fWgT{vt z|F!2UQ>^r(ktn|Xh)R)!n3%@bBto!Ue2tVvTzgy!p|2FH_Eib(Bk^UDi7*-TDb97% zLL{LT#fr!W`Y%ob-{T=V<>&~Be}G)^CIR#o_Z=ckKXD0R@>0L~} z|6)GD{m;*M%U3-dVsrDy^)C(%Upc&3jxP?fb@eN|PQH8m-L{q1vuV$BE4Jrw#q(BO zyL|QXQp2xoTkj)|efh@ayDs0i4`*CkR$UEgSHoNG)y4zq#shDUq#KWAT)k^XL%HK) z#^7_LEQm%I2Ufkov^SXX?ns&MJH4sH5Byt}qVLo%%kMt3GWblUbLftL7#B`g$+eSL zPA-~pvCR4^7Fuq!WPG)&uG$q>?ftS%t7T2;vZj>t<0{5mbM5k4Eo1gRDDqw#zcNmP z>HC*n$`tLH@4e?OyDl%x+?ZJkWCEyk`+VR1vcUX6)?Ye*8lkRhC$60M)~WfP2mY#* z?LkTD!oC~(R!i#AC3Q>3X{##|$lI?hip>KF=@Zc;<`xLs=(^?T)wx1x8-_g^<>y@3Vi4d-Id9dGS}`i5KPTd{QG z!A$+3RL>pP78I^)yyZ&Q?p>`tn65qeZ?%VS6kWG2mfovu%vRK9w=~`=exowGxo)ZB z=80?tw^V$yGFw%@^vuoCZ1vWq@Xd*AZNo#4jo!|EOmR7_tM`t#KI^SoDo%TAZ@6;- zrtV0tn!#L2+(_K@SAOp>eaX<8_P746dv#|=dS}P-NP1^~#y^0D1$<~&vF~FG zYZ!s5yXC>Gw_?>>m-g0Wyjzzp|AX=EzP~R*jTJ>0F&pEp_`p;DR$w)FC>=cX_Q<~l zd$VOV3!XppGWc*1n+rQiL6JLi9#82zl{--TJ-RdRyUMHm9Vc7lyrgzwm zb`bx_?i=tK{&Bwx_|MGFfm*}QyhQ}BW(OU%pVbhxKdbF94+hNdHn4+k+q;cjWgvcj zgdOzSe%?-V-ZQa-CARm>CS<&4Cw08%X9r6y@0C)#who+MSW5}4HWOIK4mMeSQST=B zZWfmP;%N#Gu!M8SGT^bkZ(s+^uJ?^rf;(*BzwfgT)R^DjL`-_Wnk6_j@BLc)pw;~T zj^aUs`TawrPgV?Oa8|4)g1cE*wc@3)gu+r%%Sym97__fcv4dREO0|{XTS+ZJwm--z z)=-E~L_#6)2iQ8rFB3pdS90LcBT=!)GgCP4MKj@$aF0l08j1s-f#HL<_*isYJ)T{J zp?_$moydeWiTHj@qGvEY`RL(BKWI}dL+V>jPbd_Q<9j>$5v2rU$RVJ<8a0uKCIX5$ zRm}1^J-S-9EscM7 zLxyR|S=?-3(Y{*Vm@aQzV?gBU_dadNnrx}&oDtZ4U`;tQFzi5PJ8&z(Z3N$uvlHAw z@aCM8;4Xr<l@J1C65`4QVvxDGGD&9=+7J|3r zS_!^WmET41r&RuKg6~n~_Y(YR72ikj{VINd;B6{?kl=?@{4l|f5WFeZPH=2Ol;~gy z)u~Zkl diff --git a/backend/app/__pycache__/main.cpython-312 - Copy.pyc b/backend/app/__pycache__/main.cpython-312 - Copy.pyc deleted file mode 100644 index a1fc2471fbc382e14e86f79f020e06b4c7beadcc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13413 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 diff --git a/backend/app/__pycache__/main.cpython-312.pyc b/backend/app/__pycache__/main.cpython-312.pyc deleted file mode 100644 index 8e895146cc909227d6f53ef180d41588a14f5fc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14509 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 X{Pd0k9y_P}P`f<#Uk-dSbL4*j#yvSU diff --git a/backend/app/__pycache__/models.cpython-312.pyc b/backend/app/__pycache__/models.cpython-312.pyc deleted file mode 100644 index 6fb50acbd43f476405a03d6a086f0ca5ef3dc78b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14910 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 Tw$tl{*0tWB!zyR2RO|R}2;4y) diff --git a/backend/app/__pycache__/schemas.cpython-312.pyc b/backend/app/__pycache__/schemas.cpython-312.pyc deleted file mode 100644 index 5a3da622b3d995a3b5ae13dce3abf1ef3f766870..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15763 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 diff --git a/backend/app/__pycache__/theme_export.cpython-312.pyc b/backend/app/__pycache__/theme_export.cpython-312.pyc deleted file mode 100644 index 06d0e0888c7d43a65b8a9083bb15cf57fd17bf57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13897 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 AO8@`> diff --git a/backend/app/__pycache__/user_models.cpython-312 - Copy.pyc b/backend/app/__pycache__/user_models.cpython-312 - Copy.pyc deleted file mode 100644 index ed7c844a473ee7d58489a01510d55439f0c0269f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1145 zcmY*Zy>HYo6nAo$kIUWl)b;=YLPC6rfanPU9iT#LDPQGdp)6S<_iT<(J4v0S<+ej~ zU<5JHs>_W?3xEJp2%x|Liu6^YP}Nas!70$9Q>2=sQQgr~tUwHEI%et@Nr_sH zr2rLN1xQ;2NXOP%AzM%xt=c~egQCW6a*X#eTf?Tco-oIC|oHa|!0ApAZ6P2;M>hu@yEX7Vo=g$5vfvGF=X@C+U{fg_(C6N3@s;(Q$B3a0a;wY0(i>n@Dx^y&5 zp+|THc~MAUJ59+gI1gjU+=P)-$9*?s!MuksV}?9rdXWne^?YW^jPjim!s4bMEO1LG z7Vi&oVf+$&6A1Wuy0H+j*(iif95rTX?B6570%naF*qp^aYQQjT$Rrv)(IR>`e44L(fK+4nM!pjW=+wGS<1=AKtrstUJ7N zazpD4k9KbM2lp)NOVLVs74-(sbf)_CeaoZWLo4Suw9SXT`nArjer?y&RyXO@PIayc z_L8}JZu9u-#;ZneaO%+%JFWYDIGdlIDF(+dCaiyP0+;?Fk>)A&mr#GO0uK zH4H~Gkxc#`GMR^^mzFsyN#?{_lE8 OxuHBc`vb@|&G83E*(nA9 diff --git a/backend/app/__pycache__/user_models.cpython-312.pyc b/backend/app/__pycache__/user_models.cpython-312.pyc deleted file mode 100644 index ed7c844a473ee7d58489a01510d55439f0c0269f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1145 zcmY*Zy>HYo6nAo$kIUWl)b;=YLPC6rfanPU9iT#LDPQGdp)6S<_iT<(J4v0S<+ej~ zU<5JHs>_W?3xEJp2%x|Liu6^YP}Nas!70$9Q>2=sQQgr~tUwHEI%et@Nr_sH zr2rLN1xQ;2NXOP%AzM%xt=c~egQCW6a*X#eTf?Tco-oIC|oHa|!0ApAZ6P2;M>hu@yEX7Vo=g$5vfvGF=X@C+U{fg_(C6N3@s;(Q$B3a0a;wY0(i>n@Dx^y&5 zp+|THc~MAUJ59+gI1gjU+=P)-$9*?s!MuksV}?9rdXWne^?YW^jPjim!s4bMEO1LG z7Vi&oVf+$&6A1Wuy0H+j*(iif95rTX?B6570%naF*qp^aYQQjT$Rrv)(IR>`e44L(fK+4nM!pjW=+wGS<1=AKtrstUJ7N zazpD4k9KbM2lp)NOVLVs74-(sbf)_CeaoZWLo4Suw9SXT`nArjer?y&RyXO@PIayc z_L8}JZu9u-#;ZneaO%+%JFWYDIGdlIDF(+dCaiyP0+;?Fk>)A&mr#GO0uK zH4H~Gkxc#`GMR^^mzFsyN#?{_lE8 OxuHBc`vb@|&G83E*(nA9 diff --git a/backend/app/main.py b/backend/app/main.py index 10ca14f..a551719 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -78,6 +78,11 @@ def _migrate_add_missing_columns(sync_conn): ("station_config", "color_black", sa.String(7), "#1a1816"), # Multi-admin support: password_hash (nullable — existing rows stay NULL) ("users", "password_hash", sa.String(255), None), + # Social media columns + ("station_config", "social_facebook", sa.String(500), ""), + ("station_config", "social_twitter", sa.String(500), ""), + ("station_config", "social_instagram", sa.String(500), ""), + ("station_config", "social_youtube", sa.String(500), ""), ] # Determine dialect diff --git a/backend/app/models.py b/backend/app/models.py index 8954944..8b26604 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -97,6 +97,12 @@ class StationConfig(Base): # Donation donation_url = Column(String(500), nullable=False, default="") + # Social media + social_facebook = Column(String(500), nullable=False, default="") + social_twitter = Column(String(500), nullable=False, default="") + social_instagram = Column(String(500), nullable=False, default="") + social_youtube = Column(String(500), nullable=False, default="") + # Stream stream_url = Column(String(500), nullable=False, default="") stream_metadata_url = Column(String(500), nullable=False, default="") diff --git a/backend/app/schemas.py b/backend/app/schemas.py index 3ac4a84..bccb55b 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -78,6 +78,11 @@ class StationConfigResponse(BaseModel): play_store_icon_url: str play_store_url: str app_store_embed_html: str + # Social media + social_facebook: str + social_twitter: str + social_instagram: str + social_youtube: str # Page content hero_subtitle: str about_intro: str @@ -129,6 +134,11 @@ class StationConfigUpdate(BaseModel): play_store_icon_url: Optional[str] = None play_store_url: Optional[str] = None app_store_embed_html: Optional[str] = None + # Social media + social_facebook: Optional[str] = None + social_twitter: Optional[str] = None + social_instagram: Optional[str] = None + social_youtube: Optional[str] = None # Page content hero_subtitle: Optional[str] = None about_intro: Optional[str] = None diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..27c712f --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,7 @@ +User-agent: * +Allow: / + +Disallow: /admin +Disallow: /login + +Sitemap: https://kmountainflower.org/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 0000000..d7169f9 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,28 @@ + + + + https://kmountainflower.org/ + daily + 1.0 + + + https://kmountainflower.org/about + weekly + 0.8 + + + https://kmountainflower.org/schedule + weekly + 0.8 + + + https://kmountainflower.org/events + daily + 0.8 + + + https://kmountainflower.org/contact + monthly + 0.5 + + diff --git a/src/app/about/about.component.html b/src/app/about/about.component.html index eb67bd5..931389e 100644 --- a/src/app/about/about.component.html +++ b/src/app/about/about.component.html @@ -3,7 +3,7 @@
-

About {{ config().name_primary }} {{ config().name_secondary }}

+

About {{ config().name_primary }} {{ config().name_secondary }}

{{ config().about_intro }}

@@ -73,7 +73,7 @@ @if (config().donation_url) {
diff --git a/src/app/interfaces/station-config.ts b/src/app/interfaces/station-config.ts index 9d7b60c..131d2ff 100644 --- a/src/app/interfaces/station-config.ts +++ b/src/app/interfaces/station-config.ts @@ -23,6 +23,11 @@ export interface StationConfig { play_store_icon_url: string; play_store_url: string; app_store_embed_html: string; + // Social media + social_facebook: string; + social_twitter: string; + social_instagram: string; + social_youtube: string; // Page content hero_subtitle: string; about_intro: string; diff --git a/src/app/schedule/schedule.component.html b/src/app/schedule/schedule.component.html index 7e2694b..9641c9f 100644 --- a/src/app/schedule/schedule.component.html +++ b/src/app/schedule/schedule.component.html @@ -2,7 +2,7 @@
-

Program Schedule

+

Program Schedule

{{ config().schedule_intro }}

-- 2.54.0 From e3a742b7087ee11d1e46f4c218478a87c52b878f Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 4 Aug 2026 10:32:47 +0000 Subject: [PATCH 2/7] fix(seo): add RouterLinkWithHref imports + default social media fields - Import RouterLinkWithHref in footer and events components (required for queryParams binding on ) - Add social media fields to DEFAULT_CONFIG in station-config.service.ts --- backend/.coverage | Bin 0 -> 122880 bytes backend/file::memory: | Bin 0 -> 135168 bytes src/app/events/events.component.ts | 3 ++- src/app/footer/footer.component.ts | 4 ++-- src/app/services/station-config.service.ts | 5 +++++ 5 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 backend/.coverage create mode 100644 backend/file::memory: diff --git a/backend/.coverage b/backend/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..5a3020a1b2a17eecfc07612d66c32300f6888ce8 GIT binary patch literal 122880 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 literal 0 HcmV?d00001 diff --git a/src/app/events/events.component.ts b/src/app/events/events.component.ts index d795f23..ec96c21 100644 --- a/src/app/events/events.component.ts +++ b/src/app/events/events.component.ts @@ -1,5 +1,6 @@ import { Component, inject } from '@angular/core'; import { AsyncPipe, DatePipe, NgFor } from '@angular/common'; +import { RouterLinkWithHref } from '@angular/router'; import { map, Observable, startWith } from 'rxjs'; import { EventService } from '../services/event.service'; @@ -14,7 +15,7 @@ interface EventsState { @Component({ selector: 'app-events', standalone: true, - imports: [AsyncPipe, DatePipe, NgFor], + imports: [AsyncPipe, DatePipe, NgFor, RouterLinkWithHref], templateUrl: './events.component.html', styleUrl: './events.component.scss', }) diff --git a/src/app/footer/footer.component.ts b/src/app/footer/footer.component.ts index f005716..676fbdd 100644 --- a/src/app/footer/footer.component.ts +++ b/src/app/footer/footer.component.ts @@ -1,6 +1,6 @@ import { Component, inject, computed } from '@angular/core'; import { SlicePipe } from '@angular/common'; -import { RouterLink } from '@angular/router'; +import { RouterLinkWithHref } from '@angular/router'; import { StationConfigService } from '../services/station-config.service'; import { resolveImageUrl } from '../services/app-config.service'; @@ -8,7 +8,7 @@ import { resolveImageUrl } from '../services/app-config.service'; @Component({ selector: 'app-footer', standalone: true, - imports: [RouterLink, SlicePipe], + imports: [RouterLinkWithHref, SlicePipe], templateUrl: './footer.component.html', styleUrl: './footer.component.scss', }) diff --git a/src/app/services/station-config.service.ts b/src/app/services/station-config.service.ts index 4eabf6c..983e1ff 100644 --- a/src/app/services/station-config.service.ts +++ b/src/app/services/station-config.service.ts @@ -32,6 +32,11 @@ const DEFAULT_CONFIG: StationConfig = { play_store_icon_url: '', play_store_url: '', app_store_embed_html: '', + // Social media + social_facebook: '', + social_twitter: '', + social_instagram: '', + social_youtube: '', // Page content hero_subtitle: 'Your community-powered, nonprofit radio station — bringing music, stories, and connection to the mountain towns and beyond since 1987.', about_intro: 'For nearly four decades, KMountain Flower Radio has been the voice of our community — a nonprofit, listener-supported radio station dedicated to music, local news, and the stories that bind mountain towns together.', -- 2.54.0 From a7d994a12235482f9fc56d0652d1f65e245024e8 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 4 Aug 2026 10:40:37 +0000 Subject: [PATCH 3/7] chore: remove pytest artifacts from commit --- backend/.coverage | Bin 122880 -> 0 bytes backend/file::memory: | Bin 135168 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 backend/.coverage delete mode 100644 backend/file::memory: diff --git a/backend/.coverage b/backend/.coverage deleted file mode 100644 index 5a3020a1b2a17eecfc07612d66c32300f6888ce8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122880 zcmeFa2bdhi*)7~vH8WjZlayCkD}fSJ@ZYT^5BSD)}{?h9kso(YE$+0+R9bc zwff)a#QBrQFPvPyaQuW>lgssO@*v>e})(>o--GuivtI{i@3C z>$j{e-@dNeJbFi6^=j%_xD8#49@w||d_ooLSL0dRtJmTsw$`rSTv@xPd}H;V-o*#_ zTiaf}d%L~?FH*gJ?G}1Oc@O{TJThdCQK?RVev}9h6Ufs#PZvU2MU#^qH6v2It#p?9W?gl ze{<;2{r~>Bp>N604-)zoZQ4{`v!?i!p=X!tor9yf@pq=5DMn>2ebLHm{0HKl{$D-X z=*{9ilvi!4srvb`Ry<v*|B96eO2n{GpwrFSzTMXwz{u>ua<@X+XoU3 zorD)Eo80N&9Xj?9s7P3-l5Ik6S-W;DR*j_w-i53q}>ODWbU;P3< zaZ`Ids%P=x_RdX2h0^crh-b}zms(_BdA7vf82zFz>FH&_Tfl>uZ6)07pRDn_jN);$o zpj3fU1^)9YAQ)#ZjsJQ4btnD_{we(_RiIRXQUyvCC{>_Tfl>uZ6)07pRDn_jN);$o zpj3hXkrjw=(IIYPA&DkTv}@yM0EYHW4DLHn;ZyPW`%e7*_?Q19KVa!imnu-IK&b+y z3Y02Psz9j%r3#cPP^v(w0;LL+Dp0C`tw2O{V8#aEO0@HQ7l7vf+k_5v;ups!#NLXf zVjZLRMynz}M$U_j2)`KK686-UYLfS=ccj-^{#wp$hNm z;L6tfYYVu(Ky4jup=^I`0c-R&S7Qt7T44)|_D7XA*HqDB)~X%rH?5}6QrQw)n)*v` zX;aPGimjEks27#1#r`T+-S$c>O5eI?ZF6j4!7u$G=%CtxCH=L#YO$ifxaX^yVMEjR z$A;>**Ay41qg+k*Te;TN?4ok5Zh|e$-XEWWmj7a9^7cL0N<|!7nYcf;vc0-;Gd55a z+h02aOQNwm=tHcEVjDAm=?_u-npafs-da<;9UEC6!A6$+(i@?}m%boaU|oR)_iNU# zrJWMqUpoaCyjX_~tWem%_+R?7=yl6Qz4Gw-zqnpet*UpT?a(GxNo?X5-{-W6ii-94 za#U1cBZs-z$jD#1PMa%>EpT}VFFy7cfAN~t)tjh5RYAP`FRp^rT`M-5ZBzuXjbB_n z=#-7-pT_^q_FuXD@qg3mKyvne`4o-E|4mi~ zk`wpKR{ZgQe826CpT_^O)q&*9U;IP-)A&ESA&^}1i*JMu-~TfHkL_Tfl>uZ6)07p zRDn_jN);$opj3hXzzPTv^=SUzY4RWV;YuH=RDn_jN);$opj3fU1xghtRiIRXQUyvC zC{>_Tfl>u*1!(@iH2$~cD1lN1N);$opj3fU1xghtRiIRXQUyvCC{>_Tfl>wjBP$TZ zr*h-Z(P!%z|1SPT{1f~V;G6Nk>YM%}KXU0ql`2rGK&b+y3Y02Psz9j%r3#cPP^v(w z0;LL+D)66Efp(|#!-r{eQd9g}c6>GdZCQU1{%xM8A%D{h-O?mAmg7G%emMShI>i4T ze?9(G{GRxY@r&Xo=o{&d(w|ZVN);$opj3fU1xghtRiIRXQUyvCC{>_Tfl>uZ75G1> zK>KKjd7SfB;!tsjdpusLt$#~P=V9m3&;z)obr_FfjJJq42|f$2txA32# z*a_@J@eFQh=08FmaU}l?JnnP1VkP?)Jm&Mz;w4O#*8ig-r9Y(#lqyiFK&b+y3Y02P zsz9j%r3#cPP^v(w0;LN4hgSgW|CtwXo%lELPvdXLUyMHyzc2o~_zm&P;%6fRkc{t& zZ;n^Rm&9kq$H#}nd&SG+E#oTI5c?wbLG0DoGqHzacgJptT^+k1c4{maJ0i9Sw)k-OEpZ5>%UGF9D3GY7d zci#2hCEgic!8_91>aF&cdb7N7-XO21*U@X{h2(eg6ZximPCg>Pjt?9Ys0*wO92S@xm>3uu=oRP`XdZCI_u^CWmUvz~D()4ximS!>;$)E)yTnFO zAr^?KVx;IN4iIG`%p3UU{5}3Mf0F-^-^Q=v=kVkBk$ekZ`5*q}`Oo=y#u8kxn0W@z zU%;fnbLTVH;5l|Cm_L(~-PI7)Uc-$k-4+f7N=X`JQn6b`x29F-&d~5Ki(atvpj~wNEZSaVZ&Q}Hx zAK`py@X+DT7X}ZR=zMPRQ0E^84@fwl8Ju{*`MbgW1~{J@+_#_eiNSsPIv*R{yN~lX zgM0OMJ~FsxFXuypdkk?tu(+r5zQJAJbKW!fz^=}_1|M*s^Nzt?4shN!xN{fhErZKD zJ8v4?soZ(P;EtV~*A4E_(Rt0__8pv84Q|`sdBxy1J)FN<+}3&7;8q_wFB#mjjq{?# zt(+GOZr0Ly-eA$pc}`(xV{eCq^DIHw;GqWRFXj;+wRD~__`NpH(+0oL!}+tpj|_31 zGWhO^&Xc|m9%`t;@$-abtDOs+$1Pjx{K|REvf0k9&ZCx%ckXo_v22j@nDek@-JKVm zhb(L1{LOjLGUF=pRi`m1OEF8jvw#bZ}I)keHP#A{L$cX_d9 zdkr2v%DKnlXPmnY#u0v(!6QaFzc+a32kV7%Fv~!BDAsgM0LH3I=!Y;p7eO*4@b&jD4FmxJy?j zV=&YxZ7|d*WiZqzX)x62*y2ZFOmygYjDhVt9Bp8`_D31mw%w5iwrP8WfvwvdZeUsK zJqETY+ihU;7P|~=(tM|Z@g_SAjK#Mb7>(5#7>U*z7>;Z+P=&V|=&Bk6L+%y>gQ3j^ z28L|1Fu2hxiXRh!4TSJN(Xig!{X<|KRfJ>8&q+3{Emns!ezcQZ<3EHSb#%3XyH8zh z;LhEv4BS>zW#HDDN&{<-T4~|73JbTcFxy!B6F&1Wi+@;nsCjDj+CvOn zUA@%6swTxzaN0-%r%WAT;N-o-4V*M(n1z#v8aQ#r5DO;_HgLkk zK?aVSGtj`%j}9{(TNIuwVb4 z2KMdO!@xcRx*OQLZ#M%E8r;>ut_K}xV3*epFtGC*T?{Pm+}XfRUCS*j?_^;6cRL!` zwo?ZK%RXvvV9U1c3~bV}t%30-Z48XXTN@aOl^Gb0v@%eITN>!876wYMxq+^1W?;x| zYG5GL#6S^<``>#mV*dA@H$=_dAC*N6e7Akrz&AQ617GXn8TedpY2c$nT?6+{2pM?G zyr6+sR0a$@b*GRa&y$Qba2J3{uYs=r_lbDb!OH*lvFiV+`286DUmw3Denz|yKQg{G zzB;}XWB+mSLGd2(_VFh1KM*SJC&QFGU}V-V?nkdKpIhx#;fbhUl{BoangdfarnIR#Ay@{@)_6MxKiNF>+hv zs>nH!<03~!wqS%mKQbvY1mpX*kw|!7`0wF2!+#0?Dg3+ewc!iGCxwp<*M?Vz7l)^X zM}&KaJBFKtx%vWQ`WMtA>MnJoxdwcpCFHs+vF;_NKTc*WiQ!5 z#wBwSH}4+d*0`(OCGJdjtUJ){=C*ZX7{PxP`Y`lr=;_d( zLU)F43|$sFD|B4w=umBFP3X|joX~{OkkCP)j-h6uQ1H9pC&4#^&jlX|-V?kfcvbMc z;7P$$aA$Bsa7A!_a7u7Quy3$SuvJh6_67bCcsKA;;EBK=1HTPi8@MQNS|ArVJg_BD z6<8dY5f~E~5a6`7Jdakix>Fed=p>6=kW=AP~(AaG}M@t@c+vdD0Xee-Q^X`jD+3g zFETR{b`M+5%t+Wh^B$ycq zyR#F_jD+1;31&vZ?#u);BVl(&f|-%9J3Yb7NZ6g0U}hxjPE9Z~5_YE~m>CJXlM~F0 zgxyIAW=6v9!~`=VVRu4;nUSzNKEcdL*d3Q(W+d#6O)xVOcE==`840_i63mQ*-H{1q zM#Aoh1T!OHcX)!Ck+3@~!OTe59g<*XB^ZZK1|-r59(&Th&K&lH#k1Mt7SCdjSv-?HYVi#A zh{e;{!xm3t4_Q2wJ!tV1_9u%cGc)dW6O))3_qvIR%#3^8!~|x>y>4PWGvi)2F^-vW zubUXl%(&N0jA3Tn>n27qGwyX0BbgcZx``3YjCKuFeG=?e-x*?aDfv+13F*ERWMUef<6q*ZmHMMluCoX4K6}!8~ z-3Dfcz3z*Bm>KrE7p!KN8*_qnfIV@UW$jr9cBy6USbKJfWo=nIcClq`SX*|HWvy8o zcA;futTns9vR15&oo`u7){32HS##EsooiV$)|{O~g~jM5#F{m*v+Z5+2FcE{EXLyO zOv|Dy#?G)T!lLYS%fc+ePP5EoVRouz62q)hEOVJ;CtDU`E<4GxAPcb*EfXxrPOywK z!H%~KV>EW0We#Jk-Y}_vM_cxH=QDPcWuG{IXGdE0vGWN#!m__P->}0Q zv5(mv%iee1WxFkV&v~Eivh3Z4?rf)JZ#(a?9hSZ2yv?>-_NMa|tF!D4=S@~?+3U_5 zY@21TIj^&=mc8n{#%e5k#d(!&vFv5%6}H*3mzC;oVlS}`mObnIg{`;j zFAam(I?JARo@Hw-`?K>jTVvT%&YxMeWluOyvDKD6?mWR(S@xLoBCBe|9%q%7J=idw zt+eb<&V#JNvIm?$u@#ox?>xYkTXvswKU-$mAD#QyVV3Q6{>Tos>|W>4)NvMU=_vq_d+;atfkT6Vc}1)E^mWzOYnyk(aQbk1T! zEIY$FlMS})H0KO9$g)$N3)sL$>@+sONV0R%-7I16I?*|a^|$N<=S0@evg4f-SYOKu z&he~|WqGH-dRvx5|LkR1*2%GhEXz1q*3+`IlVLq9OF3!Q-Lj;UV%;n|)=9FiX71=1 z=U8^2-zY;zAHxnX_^6{<7lV&High-4_mQmJ;9a{}CxdtHVjT_Mv6FQ$c>504-r#N9 zSv!NbZewi?uGz}k7`&;5wKjO;CRS$fx{a)r!E4vCmIkj`%UT#*y@oY6xT=~pGq|FP zH8pre1#4pP@)ay@@UrDBX7C})Sk&O9hp>pjOO~=Q^8ais&(r+>-{bGX0eCw85Uv1l zTl`vh0B0cqkd7Y?7hp|%S$uwcT6_$AfP;_$XcdpdIh=q`VsFP@iaizkQ|vCJ0Dcv_ zD0W8dxY)6=ov}@^s@T%l9OM8-#QMj&#oEIakkNh7FQOktUq=$)vFIP8zl+`&y&`&E z^c1)QM@DOr1y~VX7@Yx!U`Vugv`e&gG!_lOCHQ;f-N;`fPe&e#+ykfJ+Q=o5vl`_A z7Dr}9CPaos`bG|fW6&fL41WjD;Jxsxa19;_?+yPJnSjf}=fFA0gbxpI4X+6=3(tpp zFeW@Gd{DSN+$tOiGxe4FNWHF}g@fqff;vjosh<d*zDQ$xAX5uvT2YIqED zLz6@NM9ez*~VA0*}LKxIJ)v;L^ZZNDCYr*b&$es0b_!Oo!JnFwi5=AtKsZ%uv`?U$DmBU{kWG~uX~~5uHNqzlO_dt%6KJMN4f6>kQ>BLb z1d6FrLwo|kRH?x}fnKWAAfG@kRcfG5pq45%z>?FvRDuW#K`T|Nzkdg$Qlb<%LxZkg(+;m%z$ z)j`7@J7uc9hTC_@R67l~ZI`LG8gAVtQ*AV?*(y`5HQZDqQ)L=%+$2-2G+eh)rdn#a zcAZSM&~VLKnQE?K^%|LKreRgJOf}W8qDrQkXt<(6rs5hdUm;U54VNvKsi=mBER(5- zhD#5Tsj!AimdcdUaPbnE@-$qq80j4i=P!^cSHrpUWh$iMoVhX;)NuA3nF?q)Yqm^b ztVZ>nIZLLvhBIc$6owIW>(m)CnyM{xD%VeE~ zLng{(tq+IFdxmLrj@5$sE4G-)plhqm?aG*@C*09R~GPz2_&Rt}(O2hKb zGFho%r*fHGsbR-XGFhQvhmJD2Lc{hQWOBKNZQIM_G7a1GkjcY**j6SF)v(n^GI@xG zE!)WCQXjUG$t4;#Yble9H5AR<Cr5K0%m4C1?2rSq7Dy=@Ud5RC0z-kYrHF={`Y_K_#d8 z1UUwkoaz(A7*ukKPmp3z$;p~fh(RVNfiQ*)gG^5J;r%i>!H4(CTh1 z$ni8fN=e26N8=zQ5hwVJOp=5H(osqR4mbi2Ao&IyibIpc8*m5?LXvL4!Ke)hH{hVb zGD)%xIB<|ml4t`C7$}n@*?B?AlExNuvSjcqD}e?1JNw^ck=-j&o9Hf;b*Yn*rOE$s{Q=VB2;wNxBT! z1_u_YGGJ>Q0!2+`!7?1XT9Sihtz}Y6aAO}=fCbb|3)Ymep z1v%j1b+sS|LY_=&K@J2%GN}bQKyQWw*<%5`nW7$h0X#{|ao}rgK+7@G2r{YVSPc9@ zCbb-k1a_a6W6=luOv|w#4bgHeNJF$7iyqJrEytoec9fQ5(XG2oYB?6P&$JwiF4$*U zjs@)#)G?)G{ov z3$+T17G+Y4uxQ>wY7G`mnoBLgBHl!51s1Wm)B-G`F{$-eM50p5uLwt^R$qZ#qs3Ra zN^0#DAy;bY71$YCc|~A|)WRzQL6>wF@q&O`R9#OrNUgczhk!?t3-1jn1FtB#oI(at zOD-}7QY$XseX7)g%XjXUT5tKb8mZ-$Z>^D9ZF$X6Qj0CG*(SBt@|vw)QEE9yw!kY& zEy2gUqSO*R(<@3X;13I>mRerDR%)f?tE;6JT3&U9)H=&6S4)yvcyEfzI>U~8q1e$m0Du?vIVqw0iRCd!-gnK5ehm+R3L*ms&dc)M-*HC&%f87EV5SuhhE9Cry!B zHu@}h2)*zkXj%)zRy}8d8e*Y%Ol4Zo753|q5ZqEsE&-c?Ig82^0JSl z7DwK)t<>7co3xZ#8ack3S{XUMn_3uoBqp^ka(pATEOLA!wJLJV9BWbJ(vw;fxho|} zBHjVMrA0|(JP?vv5;?w-q=-1<6K+utJ>DR-8uE|Iq!vT|ZhNV2>GK!rPe{dcY@S1$Zwe^wF>epDy0@de(Fw)0Vx5{5MuZ*lMOWf@5T>t;-6#m z|6=^H_`UI4;#bDci60+7I=&6F|4ZUC;-lmJ;|E~O@5O$IeHwc+_AFBWzmMG*yA-4S zeC+Vp=Ge;Eg4h%!{(Bc^|AWzQq8~+Hjs7`$fAn^w{V&7_KONl}U61+yxzP#H!O(m!SU(`vWctg6%!HB*gKgVjN* z6Y>Ba(*2)%?|ZL!PkRq~cX>B^S9#}qr+8VU0&0-&Kg65kP4tF&eY`GSnHNE=zLFmy z8}KX={`bn;%13G{F~xXoNQ^h4+~Wc^lGs)(2Ju76hgRMk6QCJU4G-A=qoc&zRx z9<(c?8;Un{pv)9I3iEJ!m??G?K-^mFD9jrSSnMdw8w993iZ>9O&@IIqh^OhE;td=k zGrFmG0|(2D?kXP6Dl@vRcmoH>jP5Jm06bPV77yo|8QodDp50_dw-&EQPnprZ#p{7v zb#w8$1M2SLb;FD4_TqKJV|9P=aITrr4aUPMOh$JYuUl7{(JjX7iU;T(J; zK^HT+?RY^~W^~{20zsM4jmLvDW~lRE{3P5=vGaIdgPWnwBOzICrr3F;Z16JFc{p>O zuNqW_dJpHW^QH5Z%24|u@`dxI%1{R)@|p96%1{#`f@v_7p*}?9W9RQGL#>F&-<*$C zhPn}vk1$BeP(vc}q4SZ-P){QAf%BotP+KDMzVm^~P-i0Yp7XxSP;(;kuJfMCP=6xw zj`Oa{P>Uk+w)2k4P?sWtc`=otMnweEVk$$uipZ-Nvt_7V5y4cK%23B5@>k~-m7%6Z zi^@le1KYIvWu(+cMPMh~%BKybLus+HslX zGNW6ZyS)wuDsINKcAb-v8QtaFy6rNf+niflCo{Uwx!Y=GMmIWl^ER2$ozC5~S!Q&r zb2n^~8BVXp+_f8IhG|%h%{dybUMthb_^?{0kM`kenLf&g7s&LH8m_{vM`*ZmxlA9f zVZ|z$-s8iSGQC^FxPQ$5yYc-q#xJJXtfLg#2 ztdVIg;K#2lH{0`4Ye%d{47H!(}5wSc>c znKG>f+)d1oX)WMxV!BLg0e2JAWLgWjo0uxoTEN}J6q(in?j|P7v=(sWW@K6mIASw0 ztpyyZ8JX4sj?j!uYXL`QMy9oZBQhh?TELN*k!da92+YW|7I5TcWLgV2;xaO=1srJ^ znbrc1u#8M=0Y_FwrnP`0DkIZcz>$=ZX)WLg%E+`9aO7lUS_?R0GG4kU;G9x2GEEAO z?_QJcGF_B#hK!6%lZN9~jBnF@HN@$3np7OOqIIN6!~xMd(j?)4t|!wZ;D9)hPLq5C zV!WFs@dgw@nI`E5#L0A;gxdpj)1=zWa~ouuBpY|X*hi*GumLYvtrp|1Wa3%)^Z&}0H%#fYq<^~|I%8ewOoe~e<@RG zE!QEWUs|cOmg^A0FD+GC%Qez3thtvi$~Cel%^Fl%t96JW38T_ltwRXD#8q0WbqKka zm`ZE44l%@FR9dTb2&tEdN^7;I)dgXd)@mI>=EYNKt=1HI!Aonk4q>aVN^7;o872#< zv{vg7@-9J@E~+(BI*7lhG|4rbNu*u4N|Rs{LHI?bNwGmF`=ZjM*+dY1QE5_bB1pcd zH0d@G1YcB|l$!{0FDgyiO$4zQl_vEjg4Byj<4tMee5=x=;B*f%FDgYEP6Uw`l_C`< zg2anTk&Y9gPM;zrCxX0-N|BZmLEJ^9NX>~L?V?ho=bC)1Ql#iakabZh(sUx!+*73L zM38h*DbjT!2)d{gDLWDB=PA;5BGk`Qr0ztJa#1PLcOnS6s1zwY5oBCciZq@GA}%UL zDo=#keu{LS2m&rDMM_VEI(>??o(SSCDn)9q$xA9ldQSx57L_8!CxUE?N|ELhL9|7s zNcD*z*`iXU`$Q0IQ7KY>BFMF<6lp&Z#9CB})Sn1aEh<%M$>SQ&Mg7}I`QMVw1^omMRzaWC}ib_$( zAcE|QN>R@sg6N7$QP&`Xl;1i2NJqTWFSu@#k~?m+~p6_pzA6NFY& ziaH40gUkx$|GySr{r~0v$HoW7yTw~$<^K!TN8^q-1ukFJR>jn2f}{^00A(N59kQ8)5E*7?5` zc|P)JwbQ_&%)hzb<@n z`1Ei-d;~mzRpBM!nc=Zm;om*nE*uXF^)+VlUsKPh2i5PfzW;J{wmM!NqqeD4YLS|% zhO1txgNiGL(f>Q%^WMXlzrVq|*gMrrdpo?f828WgMtl7*bKl$x%5UYz@(qmn^}PMn z@_c!+Ov_zzqpXk%uqFV0m^?tjufeSS=k9y%%kGozegAe(ED`*Dz=Q zVCeT4>t7x^J9K>Lm{474ZRjwJ^(TgghI)lMg_?)l;P=5#gKq_&$9(<080}vjJU@7H zFdf_#+!(9~E(lJ=Y<<7r0T}Ox0}X-C1MdZ14m=sS4|Da`VZ48OARjm)P!m}7->eF_ zLtG~=#Jc{Zs1wy<3FhfXiar?SHx+_^i8cK%@<;jI{MVSJKON)yU3@)1jL+s{izEEf zKmTJZ;3t68;YaYl8@%UmZgTr-_a1I?`)b#2ZgTr-=Pqt?`)bEdZgTr-`wnh$`>Jj` z|Gkdr+{8_AUv1dPO>keW-@r|9 zU#(luO>keWUB^vuU#(foO>kdTui+-RuU1!c6Wmv;R&x{FS5>RH3GS=PDsF=NYGoxi z!F^S+lAGYZT2aAGa9=H7!A)>q9k!gC;J!NaFm8hT>X1XZ3GS<cvfNU-cZuO>STHJcwUyw$<$oewD#pyK$4;R|j_G zCb_Q;IFMg%9@FIjewo3YyYNd5F7M1QF}PDtezC>n{33(ff5pM zrx>il{A7c@R{SJ`1B#z$Fc0t({Iw5?^W*)s52}G5XCCokD_(E#8=ZK;;OBetyul9* z<2i#*s^unpuNoFZuT1z}HOzHx<|cfv8m2mTa1*{)4I`XCaTC5*4FjAfxC!5@hMvyz z+=TB{Lp$dkZo>Df0htSK!uP5n;(WnP_+IU6z=+C(@72DqagJuf_lgo3+=TDdzSmhf zH{pA=?>0;2}f#a*GG@Wd;u($PY7k&_I5u!2>7pLo6Q3ms&i8FR^$qUu^Lp zzR2PMe4)VuhVTUz59IR=?%9nWY;cdBe4fSK_*{d#cjI#m?lyqWwzwysWpNKa)8g)Y zhQ(d^bc4J0;L|Mb#-|$Gp(~$aaeF@5;PxH(B!inbU3n z-o-LZOz_T@VP=AtTZX9#-pMj}8N8!qFf(`u%P>2^+gk>og155_^Ao(SWtgDgZ7hRt z!CPB~DGFZJh`q#HS%ygp-qJG6Qt%d*VVZ(Bw+!{JO|km_>)3~}SCII> z599x9aMiz)W2xAV*oIgI9DwPuF|k4L06N85#5`pEKgTryUWq;(eK2}g^ycVQ(evR1 zWTS^iYoe>8hu}&86QjeTeUS4ni(<`Wk=G;7MjnA1a9iY>$VHLUBZbIO$OEj6 zEQ=f*nF2qcf21oi{_%ft)xS%_XN8Z)wE_NjuKM>=GGLY(uZH|L$$;~`6TM@-I&Tg9 zfmz;IZ-Cbo*9!>CeeyH;wtOC$fV<^Q@^X2WtVgQ9Mpog90n^|P^p%}u3+cMwxgWc) zyMJ*XbnkR;a4*3X19I*jcN3BT^WDkrFy#3=x=q~xoPiHRuOJ0*U+A}?YmntXIg|?R z2(5!JFefxVG$_<70v#fHg;9f?9XXq zSFsi_`*YgZRdD`WsGK%-6`cQamD9#9k>)C=ja|j!`@U8=ZS1O|^`Fzmt}0spIc@B! zqV=EC#;z(_|2b{!Dh8@@+SrBL1rt4|ja?#2<+QP@SZH5^%4uU)VP}L?P8++51z`Q> zw6Uu&4-il}ZS1PzJU~txy9)CFp2}%smk3umZR{%NhlYsCX=7J8-@)L|X=7Kx;QvnL zw6Uvj`Lu6UP8++*`5G>KP8++*`N}~Jw6Uv-^ZGe$>?-FA%s1q;u?vDp{+u>;mGc=U z1#;ThRYmJRr;T0Zd!t z<+QP@if(^S8@sAF$)D54t}0IQ=d`h_iq?Nl8@meD|4S;Tja}uu082hMw&?OI=Xt~o za$_ucLFGpK7<$C+%9{BUQUOu_oxku#=^2uG!-7449Cp1Hl>){icA;@+2 z$sL%Z$aS;iPL=EGlixPXRk;Iw@*7MFT*xPrAXar{ zsm}n`ZTGU&W$=Dr>dGuN8QhFSPL^5>V9i4^OAQ8abB)YWdjUiuCrix*5R1LC)LH;H zY?N7QEP(4b$m~iD*R7XXYAU!DOTeJWFi^aAl>;QWF8JSShpALIAN`EK3ap5K-|gwGTk#bh6Yu01rJ(W~p@m z9&)J6QsV%`%CRi94M40M%Tm(-T(m@Hsbv5zTqLv9FaQw+&r-VpL>4?t%>r=Ve3_+I z0XTP_%u=HO#CozUwFyA1C(BZk0K|H-EVT$gtS8G-g8)Q0JWK5XaQY0HrRD%QZMw`- zYXC$lJWGuM5V7#=I1Q1x%u-Xpt%y`+sU-j+1)imb0EifPmf8W}_z5yg%>Z!Rc$uYE z0Ek6BS!x7;SbCPFHUNmFXIW|jfLPR%r4|5)rDs`c0Dy>%XG#76ksHsF_yZ2Bm06O0 zg2QE&gdcF=^D;}a4~U>imP8*AD|xac`G81|XG!n@5g*Tz+yi1!PnN_U5X(lgB=vw; zHj*Ww2Rx{k%#zFl_8ca&B=Q6gl39{?z;18IED1ayf|gm5cR;NA$&$DOVij7Jq#Y2e z(6S`#fLK?OE!Pk^^el-wZtc`lW=YZsmdh*&I$-+`WtQX|5bH^@B<6rvP?9An2gHh! zED1RvmXu^k#sRUWBugp|*s7Dvl7m$&zLRdaY!Z6dN$0WR~sWna=xtk@a##vv3%#u#y z?ho-KQfWXW)w870fX|}}q|ks5q2{E|fG5?etgp^wxT>tL&g8eMtgp^wx2mkK&g8bL ztgp^wwyKP;&g8YKjIYjQwW^G-&g8VJjIYjQw5p7+&g8SIjIYjQv#N}*&g8PHjIYjQ zvZ{=)&g8MGjIYjQv8oKIGdc%3tSUqDOaun2%8)=4fxoIUB+*1*uc{1*G!eL~Dnl|& z1m>#BkWdqWx2iH*d;)7#Wk{^)9yqHiLvl?7#;VGYU=x9_sxlfiw z{VH%?;Do@@fvvd4+(L{UhXoEoay%Ar#6QH_;yLk9{2%!Bfah@SfID&Zz>DLj;<^Dl z;%noF#AjmlK)-nBc=LD=-oXd4mt&8|?v33XyF7L#)(-56ZHz6)uLX>c4UBb-mBBUm zKKgO=HLM)C55E&|b@bfm@zJB;7gRTL)kcNAAH7e#H@s)C(*Jhv8mtjG(L2W5 zhE@KHys6%Bub0=si(_@b=kgu+01wOG%Nyjy@>HY*c3@q=A#$c1jf_BN*<1$QuiX!j z5P00Z7wh^jchAIC1NUH6|8jRO*7Of_ySinr7y3T*F|q-Fh6C^$tmZ#AbbRQjP)(>Z zv>-GYxqzNn0}u^11V2M6;Mw4V!8?N21usMUW6@n_5G*_q<0M(jB8 zq-D8%N%4ebS(X!zTb5y2@t9?4mJyFymSSn~h-FEZ5)WH;EK7=qEIWqPiw7IAW5u5= zJ96Lg;sMK!U`LAkEjyeYA?~wm4?A4^(X!oakNAUSyV!2A*Rq{#m$=um9qbr!Pb0Qd z+-=#`eW!`LEURH##qTZK!gh!|8?hR3hh-c0ohN>0*#@>z+-})=wn6;XvUO~|_>E<2 z**bBXWoy_Lacd*CR@`FQs(lxWn=PwitHe!~RkAhW*Ns?}xY4o|`z{wZShk#1it8J( z72-O}mhQV*Tx;19wp3hW*-_HknNk=U6t0%@t=iVw1&LmW|){s5sNIv246J!?H1KtT^4W(QJ%3&9YH! zv^dqWk!+MW#j+7>q&V5K;cSFB$+BT=xH!?Wp=_8q!LlK2s5suT!EBN^t`Qp|>MiTP z?`cu6tRL$y@|N{wgGH_p>nF07!Ce;_%V4jIv}N$uMXC|&E|QkPVHd|XV%@|smce5e zM>k^K#Zi{QWfwEVm3UyjW%#YtwhYO9G0!qs@?vfy_OX~_8BBRG+cHdei&>Uo z!duL=3=`gBhGj73#q>t(T`|ovSo314W$@<36w5HhEhbxrDQ+>zGE8xciI!oCTTHMF z2E7<>860{s&N5i^Vr(P!f*4~NOnNceGPv|&lx48##YoHG(~A+7!KfF*ErU}phFJ!y zUJPx-o)kkYgIO;ITL!mY46+P%y%=a2{CY9KG8pzEVHq5I(Z3OUQ1r75p1tU68BBZ8 z$1=F~qPJzR?L{xk;M0d{Jf@ynNBhGMM?IrDbsQMGMO=aV{0jErXvgn$i4!SAOXKwDtc5WdFB> zEAi`oQ}HVReQ*`O=HU=V^M6C~|7rZP-|y6Q>SFv3K-RAPry2gCstOt@c)f#_tj(Gzb?2A&dvPbG_3v~h~N3^5NsB7 z;of|PUkP|Q@KoS|z@33#`@i&ebYNRxbzlj8C17+Qfz|&%UAgaF@v?YIJOCfz*SL1y zx#A>b0CtPbq6(`5W{L6mrGQ>=6Iu#|U;F!lf52bm&)^DvcjMOruI3l;Q{gEb!M7p{ zaHttW%X)3X#;^{*UYoEnti!L@CTtAr@awe+8^b#MdTqkSunxaoo3Jsg!>`vSYz*u0 z>$M3R!#ezWZNkQ|4!>TTuraK|uh%AQ4D0agwFw);I{bQV!p5);zh0ZLF|5O{*CuQX z>+tKf2^+&Y{CaJ|#=3Qv_1c7uVI6+GHeq8}hhMKv*cj~LdTqkSunxaoo3Jsg!>`vS zYz*u0>$M3R!#ezWZNkQ|4!>TTuraK|uh%AQ4D0agwFw);I{bQV!p5);zh0ZLF|5O{ z*CuQX>+tKzgk>y-b@=r~7na4auCiX+uraK|uh%wg432KSwqavfOj)mO*cjH~*J~R# zhIRP$WW(acunxbTOjtmy!>=a`77*+3>&bxi0Nr|WUzrzc@Crri6(NLRgDPm}HHvFG z2US5kuTiYp+N%oMd5t=MAflbuD3)-&tqR(CCGtmA(9SE7Kd6FsUZa?0+v^p`d1X90 z%qhqM8Lx~*VcZwUb_HzPQx?c{1uSbT3uL(hwk(qcGF$;$w3G$1TLE$Xr2?6)fY=8G zvRVOA%mNv$fH2JqWU~UoG%t|J3JBA@Ko%{t9^)DS80f&t5BsOj;=zb54+34N*{KUg$f@YC<`lm zh)pcl@IX9vnGf+|hiTXfk3H0fsKOx{wrnK}OEtt*>k3OWq$(`dFor5D(vUv-LJjGo zFVK)a`g{#>bQKQP5Jy*Go`yKO3Uf8Y(N&nEA&#!XYz=Xpu)-`r4`<76VJ6+^MQ|9+ z07BNyxzf49D@-TCKyb%2{|=hGE=={wiI}`DOtItyRhaA(m_uG+5)nMV{xMaU=-*Lr z>Q!NaPjWy0Srx`xf`^T>!M?rX~wowZoX2ENImZVr9_JN`vdzPTofBk1>vnkX zAX(759me$n3tG3s{a%y>t=r)~{bWJwb{Nxz1+Cj*OcxfkZig{lSkSs1#vEZm>vkB; zrJ!{?+#Q?Hx*eu(x7O|Ofv?Jf*6lE+3kzDe!(H$-)w&(V%wa+6cDU1T4+_4L; zB&7EUE<;z)x*cx!o-AnH4r7k6pmjUkrlTxq-40{Au%LB2+@g&vXx$DsYat7y+c*@^ zMhZpYW??jpJSjJB^_s{$2{#}X*XK#L0kOC~PofQo#r1iTY(VsbJP9@+`az!L8cWKB zn=`_q`cEi3! zDo;vHm(WntGfs65Fv-SMk^8&#eJo5;ag034HjyjXl`2odP2_TRh02q36S<6CuJWYbL@s5QsXPfdkxSU6Do+wlqO3E*Qq=SyC!F;JV`r|)AsFBc@lRbr?S&jp5&d# zDeP30CxIt&GCM`(N#co|#7_n9(p(ldb1(heMCjyUJnCaHLgUi@#zpq*Y#vKarg(uf<<6*wHGl#a}VF(JD{kPah6uw91qE zYf_`~B>zNUMXNmNKM^?5Do+i72#jczrw%{_KD5eH3m^g;TIHz+5P=J=^3()0S*!BY z1&F|dR(WazL|{RyJT(F$aG+JbuO(F~PtAbtf&Z-X)DDQiepY#E2%4-=`Gb4{^I7Go zCD1+ao>iWD0uflxDo;&;2%Kk?r?x-@#>rg5P|2c^5vGyQ+etUbPpV7m8UL21ctN9Q=cFLzggv}QxJjOtn$<=h`?=DdFmEK zU^c5fwF@HfnpK_}1`$}zDo-tg2%Kh>r>3FFWR<73K?FXt%2VSY0-IUosdW&6%dGO$ zJcz(#R(Wb4MBp*2JT(v^u$WbzS_lz1%qk!A2@GbHr#3?Oz+YB*Y9vHpFRMJY5+ZPy zRbKf7=CaCDJE409vmq)^4TT7-WtFFvLIlpT%2QJz0%Mux|GyERIzOAgzbJl6JQd%L ztNt&=)&EAt`{IiK&5-^7D)v6E_WxMyp4d&X%W&<#oSna)7#kYvh1`F0T;u=8=x5P) zqAx}tkNyF3_}Ad-f2Sh#zbCpGv-pdm)1#xKiD=hon`ji7|1TpSL|%#fIr0Ez@^6S- z8aXpkkHr7h$ZA{xa8_hoWKg7gq#g49BK&puqws6tXYf0Kci?*djo1G#4o?q{3ik_l z!Hm9C->Xm58(97SfVv%5@jqXk=&$`>j4Sw$P`y=0T)$u7w*WuJoc^;|{eL&E{CA~y zE@t(USpUD?TaK~+WX$XL!4&{ndY=3d3IBKGi^Y-uZ{)R@+dmyw?mq$<|5b7cX7|U+ zfwH@7him=`%3;`F|Hp%W!1emC!Ce2T z!EA6(aC5LSxF|RsS^q?^Yp_ia2TS0~zz2a>0)O_$`Mg*i}teQ9u(U6d9DJ z8zc&JlSD;DR4|PJ5lNzAR?Kw8))95gVjOi07)J-Qj)8F;$AmgK<2dMeOvlkNjN148 zYVB`X_dYi~_x^S6Irz`~-m2>Ao~q5e_IlU%t#wdheqtJkRy29yzvF-Z<9-6+%5%_= za=CXrNHw`H@Z+g&?(>!#+~+JO-QO}N8{B6tr`%_l<4N~v=4jk~irJ64PcnPH`vkN0 z+{c-fb{}I#c=af=E8NY@j_dv=?EMWK_mQyoH|TP?b38G--Q~{l#H`sacaA4!wY%Io zo|xJ0a_4xWeYSgl`0k1JS?+z7XS(-VZgaVFJkj3ea_4xWZI;WOvF=Tl$GA5# zk7;#pu-xo!WFDGxuV)@I)V`>wdXS9sy!#XYLTd&9bUDGE_Yp+?NgBE^5SbXNppGewHl?ly!cwp(p+AAt%hkX zFTPgOG?y1&t8tpki?7u@%{@7+Orb!{Jt_R_kf^z9nR^a(Ph{@i(>;MX-Q7K&xiRgo zVeZ!G9>iGcI?8#~RWucZA3A>Fm$%lSsM?nemD2$Ba*80W&_4eVOrz za7TCy*W}*(IIcJFq;xM5>vDO0w3@!TbHl5W z;y0IS(kMNu0~8qoiiIygEv1vdgQZQ24ot;bl*0oIBwsvBll> zC$YgDZv}p491A3JZtG8ChTHO!nCy@f*y0ZRNo;V3T7lm;gnxeCOm{Hz?(^In^W5Ft zLCkaJx&xVK&v6GZWAyCLJaaF1C;kP<>D+$&1ct!AmbbWln5VV5y_t8L<~A`;-p%dB zZ-lhY?a5EHjCFf3k8N?Y%%iuu-I+&@b~DT)M!IR{;UnBe=3&F#Zp=f6xed%1>r%{v z$GS<&L)?IQ;5IkGJYb+3XYN1PjaeSxMw$D*hr=>(*NcWn#CI+?e@FZDbu~ZU>jPIY z_w40L<{o`qVY#R4GB@^c9p;8cH^PiD4f&k;T^PISn7yRa#jHH%M`o#N#Ing;>w|cdL-=IiJ&ofYT=^*mFL!Pa)ZJKD7$bp7V)S$o8C%twOlx{F_xs_neQc zLcHhv-74gJ&WBbZ;B!8(3JIUH-6}+U&fly;#^=0m6+%AeuT~-DbKbKGF`x4ntB~_K zf3^xipYyI&Ncx;@Rw3$h{$v%hKIa{)5cWB5*QiUKx2!_k=WMkKd7ty9RS5i?H>^V9 z=e%weB0uLftC0CQuhRPe#K^fn_5VMQ)Bo-bZVj#uE)FgXPRAJlOM=D0-oY&70J~rh zz+OQTmHgk~9Dw%|TN5u~AApAvzd{;t8Fm6Z8|VHVi?aasOUzA7#ZCZ26Me!&;EVVN z@pn+w|7`q`_}%fFaOPhnejYM`mGL96E8reD6JQ)p{M#v>iQ_~dx)dmkeyc-?!^d)&L4(q>uu)&8i#WPwEp`k%40{61MP4vg57m9p51`c#IO*?0 zwN3p|J*PG!Gq^=vr7ptG046n9guZ|_wW}JX2B@rxs|a=n_!zmt>+(hUIJyIFlh?{i zWL~aAaD+u~*MjCfewC2qu70cGqScrwz1!^Qq$ zo|q=v-u-Q?clUgcioUVt40SGz~Ki;y6+VaI?`?f^H7 zz7N;=*7+0}!kf3Eqg^1IoDU=s#Vch`qkP5p9D73fKu(>H@{kl+VBb#trOx= z3f6(>$SY8+PKZY-R0pCtuRxJHAs(eb9k_6XE>N6Kh&Q7!oe=LpQ92=hjDmE+{dIw2 zbi(~~fkJd3I{gY1p%dbLDL@A{;{_C-6OPdZ3eSP4P$*Dz4(x+RDL4mq@1qM8n*(vm zQ-MNrAQoZ^dl$M(tjQKAEC*suwm?xi(2wZ?1?502#1<$f2Vx<% zKp{C03$X=?$bndhEl@xX#IjC-;&C9BbqW-Ydq7p7WW3&M?9v5_#p~<4p@*nIp*Rpr zcLj>X3GpZe;=q|eio=2JcmajsgtK&kqHw~Qxc3{Hq&K_NI03w{NPzzOkY z6o3P9)I)*dZy=6(C{XxKh({^<2I8oP0tMedl$aDK_6DNFq(GrJA%2V^Z^Bkx7#U)V zE>PS}kB-#^3cCsM{ZP~mM2Sg(f^I^*FU8zIl$aDK#@v zl$aDK+$O|pDcU9+q6-vk6Asn|inR#`=>mn?KokKKDAFdxn^B+*M2Sg(;%veuU7#=< zh!T?mMcF`t5@i}ao!Y8=gb@DO|8PC z#(7gKfyQ}LEARs2yeYVJ-Z*axE}b{dn_974=Z*8GR^V3{=S?lgYmM`!&k( zcuw9pZwj81H_n@C!)uN6reHLAmPQFzoiZ)zmaIB#kM-pn{} zY6MaA3ykxoP-Boc&YS9k3xk}uzCMM2+4-vV z)~A~AW5#<^O}HYB_ondgJ#V}>)znMpjrXQ{;RVKfQ)np88}Cge@dD$$sbrw@#(Ps3 zRr1DrQ!zYhyf=mMBX7JnMVFTG-V|M0#(PtAX&LWLp+X>Uycb6X>AdmYl;i5W@!k}+ z-_MixdiAM@%2%z|OW|rT&YPrf*f?(zLrLB^Z<4-j1#F4o20MRIB$}^ zR^z-$`dW?iCNY%cjq@fkl;n-`Cg~eC&YQ$gk~hwq#88qq&YQ$gk~hwq#88qq&YQ$g zk~hwq#88qq&YQ$gk~hwq#88qq&YMKFV%|7!5<^MeIBya|N!~bb5<^MeIBya|N!~bb z5<^MeIBya|N!~bb5<^MeIBya|N!~bb5<^MeIBya|N!~bbGB-r$jq@fkl;n-`CNY%c zjq@fkl;n-`CNY%cjq@fkl;n-`Ch3AB=dG_#(xp|k-ufhllDzTWB!-f_@!lkclDzTW zB!-f_@!lkclDzTWB!-f_@!lkclDzTWB!-d>oDFM;M(gj-WyP*tnuCe*ItM5-he7)jrRsrDQmnp zph{Wey#ZCq8t)CLQr38HK$Wt_d$CZZJB;^Yol18Y?+vI@)_89~m9oZr1FDoY-W$-h zXS_F{YtMLZK-Zq}-hi$>$)Sc%dd#(S|4 z=Jma0=ix z@r&aZVE4b3@x$W_<8$Lv;$z~ucu$-I7{T6uAH}xCUWq*$dpLGyY$Hzadm1(Tclg)& zm-y@b)BV-{k^TYxJe=P*)*s?G`AOf!8ve(q;D6P74yX6s?cInq{IYkBx7J(c9fEoJ zOmBiW3TO9a@SXmsztG$DTbPwUiR%5^aB|RIg6cc{@;)FQQ~nyy;aFznUWprrgxekR|SZ^|v0jo&A4#cq9_ zsNO$WE|-U4Eh`7k9d=!K59jv1;6933_|4dNq3oXHu638;)V_VN^TGto!TY-z*LQx5T^h?H z6;ia=bFp)QbE>lv9RLflo4^!jjFWSE zVjsW=_7nIB_5ZI#p2b}LPV6YKA<_{!BXS(7|IOKd|8dv9cl>4l-6s%6x0uF?o0$ja z#7)e(G2%wcL&Xi2hX`I&##~nLqB174;yQlKpdo@6l`)+ayr_)%tl&juOlSo!Dq}_~ zcu^TsTEUCTn9~YgRK}!M@S-wiwc;}VU8!#3Qp*iugXN^SggMzDcu^TsTX8Wz9#4vk zn4@vg$?Qi(h1v5(nHlG>@}e>(xT4693nh3_8B<)ri^`beijFX7@8*d0VbI>KOYo91 zrn%yR@Ld|_c8T+u=gbx7G56~&&Smb~Pn=`9w>X=*Pj9h~x%V7#mgRopOv`=68J7Eq z(=9iN)0mt3ic>B37N;<08^y`Y-Lv8(=1h07mN}gfCo(rSi4&MpX~B!g4fyoe@Z*W3 zIF31%5UZJ^F|mpn-^5C0d=tEg+<F7m>ldh}_U6co7-X zV6h~87tDjjQI=PTBbisO5JxbtSS=2>yh`vQGA6^~F#Gs&aj4}kaR~GBRpMaFE5t#} zbJ_(jB4bJ{co7+MV!?~Zm=ucx`1Lc}#s19g%f&*=bHsj@XNv`vX9-?J#?)BsV;^r5 zdo#Dq7V|B)i@lgfj}Ut@j~XrZuslM{V;(s|?9M!*P0Y1CTFkLLO3b!AQp~bET+C!1 zK1#G(9wFM8`!tCe%{r_Ss^neT_mYYRC=9XsBmw9Zn=)>IHCVE>QCz>p`ie8pmL{H0OMGwnkM3#9> ztLSdIS!9@p=0uu#$WYN3GG-R;??Oh3E?#7g#l;KE_(q>+#y9#LGrrN^GUFS4mKh%N z3^UyRX=Z$*PlayZ=!hpnw{PqcPlT^Ppe`O~#<%#G<#zEXGrq;m%=i|6V|kW%#PUq> zu;n)KYs+2YA!dAw4_a;)4>03fyq_80;(eBz#l6h<7B?~DTfB!E-{Ret$BDZvw~9M0 zw}@X^9xLv!JVxBkjBoKa%gy4K%=i{>WyZI7OUTGaL^o#Kqckw%9wlXYs7P8KA_B{U zMZ)qR5og9dO3ZRjM454q;#+PK9y9JyG&Al|6f^Eoq~(+l%(zE!Eq4=+YN%qR%@o*4x}|6)c#(09xz2>O;81wr31qe$&*W)!LYGrVdM zqBk8Ksq<e-(g0{<=f0CLwU<`&fUt4lFK)lQF8f)<)QBD zmWQ~nSsv`ZYI%_RM`o0_y<$1%{(%`KmoHmxa(~Z^lFOH9{eMJcMeX|k=3rBBOK=tT z@Vg*54fFpaf`#Y;m>ldJ3^EW@*j=cd!W1ib1n!ve$-}s;S@A+@| zTd>>T1K1znI)8(oM|Z#p{xSX`{=VoBnCy?io_|f~5Rl%#yw7ny;9K7By{Eljdv|&_ zc$cF`;2iHHZ@Kp~Zy`1DQ(s{xz(1*%aaQ01>X%scuV8z+7jtvokscGLDB8{?8)sMP9?60FNLOxE{L=Qk$M|JRx6Lz4{W9g-B&MP(rc4Ppi76x~ z<0dhg1f|?0CXt|=o5VyClysArK!UPv61&o0KxwxTQ~fgK-9}9D%anK?)V+%nd`pynlXwHCy_P5eC-E{i zH7!vFPU3m&v0kDSoWzsZBE3X8IEhVYpf6DpPU8ByO@4{8a1vKy-|iBn;UqR-+wBqs z;v|Z78@y6A6OUw$MaOK(gyNYaj@2a-if0xdp-Zdr^7{JByv4d?Lh;P3dAhXHJUVTb zE}2j~Gi91CnNU14VZ1JxP&|W8eoH14&x~)@B@>EgTF2^=3B@xlt-54F@yys=b;*R{ zndT|FWJ2-G=;69#Lh%gNEg`VZ746N+bg_17g6if7PhTQZ?|2HmwK6N+cB8E?si;+a%d zmrN+02~xUbLh%ghG)g8E&!A4DWJ2)_>NH9w6whEaw`4-`42}&c%{QMULgtbQ#WM(* zOM99}vCdX9p?C(JxFr*cXOK6SOemhgl3U4y;u$Qtl_(VV>N62ls%GL|rpqr;Bp!*R z>t3V_hC*=?FOZ;EoW%3khrdL@xDhXSC5px)^>_w9PuVy<@eEGjEm1m7;%OYzTcUiN z#8X{|`Xx%pN&LF*4!=YhISD#Vw?rv9iTiPyZi#Yo5}U9+eu00KOMumd5w=YqSPS4Qx_9aTvtAbueSvm>Y z>%KHB6j#tQLqqWk`WHCFiYxrm;84)U_oZAYE~S69gF?~Sb*f()7>Wvs0ih^i6Z=ws zD=L0zr%<5w+AsAB#RYYjdZoT3@RhFH=#~0_>mzB}PQKKexITTx$-2~J5PQg%dKttX z@}-^zv4?!Ahe7NiU&t?>~dYA5derSMqbFGJQO}2*yE{4?GY^h7UXdNOlk0 zA4p~oTnHqq2kw{CMKXHe0wCEua9<#qJa8W%Sv+uW;AsZu1Iga;=w3iFci^5tvUcDe zKr(jVJm6Y`y93G8@#tJ2SvqhIuxjY_>DfTz=jmBMcA)X|bQ{q4d3pxW z_<4Fd(D-?J8qoN8dN-i)^Ym1p@$>W)pz-tcWT5f$^dzA1^YlcZ@$>Wqpz-tcu0Z4G z>G440=jm}kB{5(AdX#70A3()v^dS{^V^Ymz-@$>X3 zpz-tcNTBia^a$WW^GOZ|8b41D0~$Y1W5u*+{5*}}rD*&-jb+lJ@$+=<5M4BWp3W`O zMdRn`+=04i{5+jIKo^alr*r%3qVe-|ZlNw3KTqfO(?#Rw>D&TcBtOR&o7-0xtA-BT zM;FP_>CwG)k^G!+zAloR6YiypLNKg;U2n3Mou_S7so> zvTJ&DsxFdQ6Hd`ZvTDM~x=2P%I7t`DrU@tNBAGPd1YIPHCfrpQ$)E|x>mu1R;W%9+ zb0%!nMY3kX7F{G`CLF7aWXpujx=5x>I7S!Ak_mUwMKWZ0)TWJ@DGlfN{f83=J4JEXB}(QJ+=}4Y&~(ZD_!$MqLaIIG)nQ z(13BnQVb0kH!Q`_fN{f83=LR$x)>TT?ox`O0pl*E7#c9{Qi`DgV^zFJ2JB%gPF1Y> zub1x9#j5?*r$6qli=q2&JjySU`(hOBI<>Co7s-D~EW=SSMRH&ght}Qd7s-Pok;b}< z>&kwST$ltM_);JrHlpko$cahN=`RKHVk7VjxiJYk3Z_7QYy_SmMzIc**tb1tnB)OV3X;cwx58e!33^oUM1=r&g!SjQYf@9D@ zutzWz{R0Dnbf6O7CO%30CGmRVMVth1Z{n83l{iK4+{8)f9XK?xFA{`_iP4DxI1L~g z@4^{^AH?5|{~r0lgYnzr*T#Pyzc79pP6Rk2zJGjoqzBFM!8j8jh&!>baem;tu|MKe zfJb6?#cqgQ7AwTgLeId_vBj~yVl$8&49Cd;-C`>GujpsdzeeA{*#M6rH@GEwWwa7K z7pDUpi`DUcv7_Kbqy__`-J?;z%l`+u1>VL90Z(B+0oqgW=h$-LG=CL(1@`xM_ow>J zNDO-U0rnL58aoQUi*o{g>pkM#g}mT0uYi*Rj>mq2i@m+P8QwUY70}n~<|+LzbP2qt zU&pC|kLpeOW@H6reGYmAmgz&Vm*7l20p|wxM^fOcAJHMOUA=`p1)fm%VJE@MQH^lA zTB#1hK7wtiMHqsdASQp5pUd~;tMXa-5V``cl4bM+tU*e!5LE~h2##yf4I((MMLUS#xEB2& zg5z2=gb0pn(Gemzu0>0T;J6k&A%f#tG=&I`Yta=VIIcxoh~T&ueIbJ5S~P|Tj%(2w zA~>!^Ylz^u7QG>Y<61O_2##yf9U?fcMSFfVbU%iw+gd%`IuFhw#!GY!n|E>wh9w>`5UV+bC-`; zg{ixI*ecB3<*#ehUGgETFngB|T7~Jme4s|%F7LMr6L@)Fjk-hLYZa#Oa+6hQs>qF2VNNfvw+fSbd7b^**KL&7hX3?vS}(5& z|J`CTeW%&XSOi}(V@)feoeC%>rVqSKvEHW=$CJW3*FO_-b zC1=SF%SX%g%ttMe7xD*$>At*xpIDTW=QAIeljkuXkdx;!@1K+BFfYu>vzhnH$#u*N za`G(ZeRJ|m=6!PV4CcLa@^t3;Ie8lMUO9Ox^PV|*3iBR0c{207oIHtn_nchIJU1s# zWS*0gCos>>$>W)4<>VUXnK^kJb9+v%W^T*LRm?MTawYThoLs>?Ehm>V@0OFtGEdFP zWz17@@)+jHIk}X1QcfPtJTWJiFi*(IqnLNi$s?J^=j0L0<8tzF=GL728FNcc9>zR2 zCl6(A&dEcV$K>R}%)8{|LCibn=c^`!f&E$%V|ra&kZB zp*gvLc}Py~%RD$I_hHT*BKNkuNY1x>pxn#y0dh~v`^!BnFO>5v?7KC%fn<-guN0Jfu?KAYH4FI7r7TL=G|%s%qvS>#aiQ zAnR&WS#(*2*g^bQqsrn3s}MYh?`u?9{L3mt58}HTb&>eCMs#iL>50 z!&!q91Q$7bI@6sNbnrJh0c!rg!Wn{ZBMW#Uavyg5zamnKtcx5USrR!A7f<+~-$~hQ zB$BPW2wP`&QaVeb6I*9@Qa)?M48L=P6_fo=%4n<4(9cjxOJY3<%4tcQUw5|ONl7gU zoX+NVQdVmOo}sjs#JTh`%4dy# zBXp;UakEDrr8`ZGo5hL9ohHW39&xkoG%;@WaJ-v|akDrlxf2%)a_Q_Li*=`oakDrE zxzohB*@N+@iE*%;Zj!W zd4^Y^^tQ^Ay$Xf5#PoW~ZozYiDY^x>5mRyto`lb@LcuNmobfHXLa{Ay+<0A~)RqvB zQf3Qm1)9V*+p}7dy!YF ziEVur&A}BD*k*^1@G8~7Hj*7qf0{{bvqSL~Cb7*99il5HvCX1suX2=mw*lL9(S29h#~?bj zDtjBmIzwf?L98=W_A-cdhRU7>vCdH0!ywifD)WFI4hK_}-RVIu+odaW&C{Ra*UmBc zKK`!R2C*-BWtPD=@m0(;xMh;Av>Uwna9wFLSUTRT%piZbvc0CSL6RJX{?EzJV9J}7@ z-sM+}U1ywYoh$u{vFi-_!>;u!#;!@=8DrNO=W6E~zhdl~1fDT=opG)rVeC4CO=s~2 z#;!@=XN+BwxZ1B6yC!j!Uomz~;tH=)wQC&qb|wBa>VuaI4%6bb^*kYAJNAVG#rV!eYsfyl9qz|W9n8+GH((GIS2a{vTq~s3>i3y#q=_=a1x6gyfvA)5%?Lh zaU%}$D`ezGEcPp84po5o@ozpW1(p!_ElbGqu@GIo#B-)*seuX^U2s}fs zPNI!oM!rsBhJ&{zXEy>rL*8ygyI&!9H=@n2ki8o*-7k~BlbG(b`(-kCBWCzza(EJf zcoGRG@XO@!M&KDTc@l9a;g`wfjleTx^CV(U+%J>Q8-ZuY=t)GKm|rHVHv-R) z*^|K1jbA3aC*e8xL&@-sz+XU?Zv=iRnZ6PDJ!Jbv;H}B{jfnbXvVJ3czf9(Dg!ap1 z|0J}7m(c)V1pWdV0w8d-(ssX0V*rT{BY*eHGzu7jXJ{NC@d3SzMgkJs9lSM-1yw;m zL!*HaANpk)4~+Q0FVl!%#NYffjR_?F7QxGCR4@W>P2&QIw<2%&Wf~bsY>m9-muYM; z0?*LsK;lh$8I2Dl-iY9>X@oEWKSN`L5nKH-jS@z@>6dAoFyeK;Od|z}*CTitjTJ`V zt!cC%@kHcNzf9u=iN_;P_+=U~jKDKAW{`M{UPhw^iAN)NYZ^CFCWFVhG@Vsiv9qcOw?yfuv?B<_oB^2;=ikhnK;pI@et#0We?V+n~(5xg6X zCPv_8G@cl7uV1DC#fW?SG7Tvt?upiu!l`G7U9G zZ1c-B*ckCAzf8l85%2hA8gNKFSdYJ&h8z+Pkf1?_#Qh{_*dcKr2^x4v+)IMS9uk}A z&k2VghzI?0IQ&37;FrVU2jYIe91cGa_;|zN2Lc~rIQ-OKTwnCd;qZe~G9bd?2d88} zgu@R`$$$ulADof_5e`2%B?F>5{NQgZ(jOX*KM)uD<#7CgxX3Su;}1lqUk=9~h>Bkh z#~+BYUk=9~h>~9p$DjH$s0KP5f9g*s5sp9gr;!N9ADn9e5sp7N*8+cAIQ~GK;g`el z2jX4Osq*PNi0h2nV6nvO$@`ne+>zV>i|2F<>tleLYJ^#**A0IyoEBABa6XPS}ed8&t+kb_c|E<{TZ!=cyuaEs49qC{A z@B6R&&-o8y2Y_q*PX8R#=r2M4|6G3(cINBnH=z6fpP0kH;FEivR{t*`{}61KMZT^ZMsztLC1d#E9{?R?*1zJ z{U1`dp%TBW)}h<~NVO2N_6cePdi|5wdGAZq;lC+gkiU_4%IoCC=<{DIm&!$$t4~1{ zet+30rT9jCgdYEw#N(Ky--rtQ^_ZhC7l(-X=j8H{ug|D&&M~%@4=plzlfJ2dq>(MyW&$k zF@8+^;P~G0_V}*&9Q(x^(LM2f?DN?Bu{UE|uxsLdv0GzT#X8YDaZ+p<_Dq}~n-Lj- z{Q-u?nsAyzB>GQuPHc<5g3}Zp#*T^CM>j+}qGzCQ;;86>(LJKmqAk%O(WXd0TpT<8 z`!DnfggGJIBp#&lHENOSunPB#YQ0ssX;c^1s2S=4t8m+>&bJEpjq1D_HB+5iquSLu zHL6XWU8AO}bynf#QJqzzW~ei*!tJ9v!z$cAs?%$fr+ByY>K&xw-O@>Uig!z|-b1RB z?a#&V+(}m9E>f+nQJy-{D%?k^6Rg6Gq&mJv1!_%=N~q&%R9x{<)pP?IQ>$v8i>j41 z%2z9Dlvc~F!VRT5wnlks87sP_RL59_drGymMr~I|TZOwywZtmiR;r_ZQuw(eYt)D8 zh#K{QI=n{xP5sO&+*_){YSeahs8zVTREJoF+e>wDje0{JWEE~O)ncn~hp86TsJGOC zHEOFmphmr^_ODT|tA$qKHdF0aqux*ptip|^+Se-FX{vo{)T3%|t8lNW_^9gY&8FJR zKKCeoZqFL^xZ0yeJ*MW>sLhJEVz1tDs=4;L&G-#-tinB~nr#(sI@PQiwMosi3b&oA zy++-q+G^CjYDSH^M@_d1x1MTRjoPGkvkEt#YHE$TN=>l}x1VaVRk;6DlUUIWsG4XM z?m*Q9t8fddcC`xkplZBTxCvF`ep2=Cs@58{O|{ghKdG@b>K)Z=74AgU7^`qAs&=so z_o9lAs;=IQs?qj2+>NSHR^fJ3jjT}*su4Bn0X4iv-LHn#sQc8=8g;K4Vij&m)nKb| zSE_PW;kHx_vI_U5YM@oPF;xSs!kww=ZxwD$)lN0)V%4ukU8MTfs7}?VMpabr8dX+J zHL9d~S%o`P)zd26qN*NN;T~0Gt-?*J>TVV8QdOo#ouSe->U7muqfS%ZYSgK!!76kQ zs+3h|A5=-J&_Aex8g-0H)TpH@ZWUSxRm>{%5UMB^!_NONG~EBk{J(0y5_EDZ-zv0n zD$gqPaw=^VnmLuS3f-Ja)~KmU)Tk-Stx=PeQ==xSNR67P>TA>lRc95NI%Stt=<1X| zvZA(5`GZyH>y+PHg~m?#FRRemDZjG{t)22)tI*pizp)CuQJ14{>Y3{hgXhK35|Wd7e}=jRQ}c~bc4!gss8_NHS_=G{J-C!YX6?Z4T%lt z`#%LW`v)iXN=!>MCx-m5=dJ(88S4|V2e8loHL;6h7sO6QxBua>g|WG@DX}rw<-cbv z@juL1@5CPemqzo^Gox$J>CZFPq0!#aR8;uiqR;;?{%ij8{$}j&e}jLi-+>(jSNVtg z`=Q5wA`*oDe#ZB`AH6TU?cP@Jcc=rn-}|L^wRe$szITeZ0{#69yxG{-f3!Ei>yA~a zAN1$?ulhCpTm7)U1G@)yqFVn1y##&zb5RE{O83(Z+QrU+AEHu!i+WVut!`3RAW1k! ztyRlV0k97~;2r<%_yl%*0y{o|9iPB|!cQO^gL_1M&E>58! z)*e`V=knGbsA%Bw)*jg4p(Py4dpLS*IF|S5(mbO@iPaB(&^)6hwMFxcmedB#Gumn~ zmgX64wHizFjF!|4%`;k3lQqw1p-_y%GumoJmgX64wIoaPjJ8^nrFlk6>TJz3+GJk2v&Qs-)((ULkx^Ng0%I^8eqf`IDMJfW>tXK9|$k~&-W z4)2DkK1(Khg);!QX!x$`073crKLfKSc3dDBFL4;<-?^@B519 zLfJlj70-pTz56Jh3uT-7E1nBwn|dpr3uSwJs(3Dx?cPK2Tqv9Au6Qn#O=lF(g|dxl z#dD!-w?@Ttp=?8w;<-?^p_}5lP&VG6crKKU#ud+nvVK(YTqx`LiswREQ3%wEbURy$M%yucB31vTRP%rS;{(h5so_Sk;^&In?!_{w@ zw@gycGT(f-dWN}lyn33yMihRjr@|*t`K6v@9-LE8Fz1G-$1M+5k69k19<|)1HZuRx8;sZGpEt9zJ* zQg<_>80@a_V@5UDo#Dsay-WRyvj(aMQ+HT}f-rS^jk;3ZW)(`p)Guq)mFiZjP!y(a zu?kgT>gF1Cjk>8uU9E1cQCF!OYSa~KBYjz@fTz+hb-jHGwPEVI8g-?*)+$toscWo4 zd6>GoMy*#@S%m^Ib){9P5K~vwsDk=MjmoRbYgC82tVUg^F0~3}VroN;TCXm#3WZ|o z=T@OoOkG@~7O9J@LamtUvPPX)F^V1pcvRE@kwGs zi3%TON8M$%i(*(n<7UDKmY;|fB*y_009U<00Izzz-bdmdj-Pp|4&=9pga(O00bZa0SG_< z0uX=z1Rwx`u?pb*|5!PZ83Z5z0SG_<0uX=z1Rwwb2teRe3Sj?#Dw_p`f&c^{009U< z00Izz00bZa0SJs$0Q>*3aw0PbKmY;|fB*y_009U<00Izzz^N3#{{K`q3kn4R2tWV= z5P$##AOHafKmY;|7^?vG|6}DuW)Oe?1Rwwb2tWV=5P$##AOL|=DKIxzPE1dIow)ed zxj&u#!`$anU!VD7^4E!<&HXl!IGR6~Qr>+xc}!eVwrIO)Q0KrgUFtCZ_t~wYmMdv$ zDR+BUQ~e?8VtUpz)SY}uyQ>w|e4(V~_jY%0sOi}zY0y&%f8oi<&^q%3eaBaO*GV9c=WP~t?6SJ!kcs(z9yZhe$1F0L#sWu)O;wvCQj z(jM^v?i=KW$yyfa=(fXWj>v3D#WnY-db?2A)pB`nV)(eYb3a#ntlrZetBa#9SawrR8Czm{C$s_o6fhCZ@2n|8D1*j3Zj z-A-$GsbIyZIjr=SkB*m{{%p41$#JST{3a-0)HGt7OHcc}Q7 z#4sGnSh!QR>b6fe8>kj-5OY|kfneCpZug9(VRpqNmR=9C>GIp|bK{v!-sfu$X*@S($8Njr@~|S|E4vOQjd)|$jnkkmG58uGKC+kz zyg7^N&u?d61s79nL(O0-|Qk?tg?|^%0?E@Y*y{)D`m+BDMw!EpzfbN zr3+QXUsUm8x^{JmmT5LBQG*(0dpHkDm)am3Ya5YuDqH-16q^GVFVs?nF_nm+IGT zHk`;p3x*nPI>Wo{Mqst!l}jg%8(DvG*W!!H8jTDd&*FOS>~>;dDw(+W&D=lF|1SNT z^p)8&vwuJLi&S;yUo$_MUYmLAtUaBcN}l=Kh5sZzyYQE^l2%UT_6UW7z_$@N`tVYB z2GPX~(q`V%E$`!1X$BFeVrCF|->|Z_Chy4lgVozA`ewqwbU^m`DmuKc5tCutt@9p^NAp74u`SBP$19SyI2EHUF|Gv^gEuR$>T%WZGUK(#ygZ}a zQbygMMTzuf@sDy?eWCj(W=hn8Z-w<1aTpbMaT+*$S*lqeFs6?24<#(Z-0npUJ^Gg3 zZh7awUCZ{@F1KEsQ65|$WfG{1z8pAl>2P)-rQCfld6*H49MgEF@0)!8%y&E9`iAK> zYV+qfZN6#hgaTgbx$We)wMS}fTD6c@L+RDUUe5dq2F?6*KBeS%QC^BEiU$_T=fxA0 zL$pA2A-tf>k$N$uy!T%6*cKfobdAu77o<)T3XbVEq9~#DU5{cgC3x!;Z(R6^e1Y9C z(;Yry^44@-0*f#8+_pj0JG+HkNu05pwZN%ODUV#h;G5JCT~l-`{SCx}-DOg&E~>cGQTCsENKDadQ5`pSzouPCBLBx|TeAz1u3!#KRVE zMOwD(G83P=X?Ray1}EowG`)iLhVO`!-pG|wtBYRFJkQM0)ND$*b}f0ViFT1;9kSbFKA>_@#Y}v?cKs{X`A)`-fh#F@B1Cx zp#Gtq3!lux4|u&E7rehpHS9eg{T_ELi9~Y!gsTli8weaNq*BV-TJm^HjHZg+Xz&@w z?dWy>+)zAVVNvJKNn@ffL zltkbUiK)7@Kn)Eox$eus1#H>uK8N-O8-H8jju?hxdAXMMddR|{o8SusAOHafKmY;| zfB*y_009U<00I*zfdBu00vi=of&c^{009U<00Izz00bZa0SE*H@cuslfurKLCP52tWV= z5P$##AOHafKmY;|fWQO_;P?L%*r=!y1Rwwb2tWV=5P$##AOHafKp-H1_x}M996|sB z5P$##AOHafKmY;|fB*z0Pyp}$C$LdbB?v$O0uX=z1Rwwb2tWV=5P(2H0Pp_;AUK2o z1Rwwb2tWV=5P$##AOHafOrQYX|4(3}qDl~e00bZa0SG_<0uX=z1Rwx`fB@eA2S9KL z0SG_<0uX=z1Rwwb2tWV=5STy#y#JrTMn#n%009U<00Izz00bZa0SG_<0s#TM{||uR z5CRZ@00bZa0SG_<0uX=z1RyYh0(k#FfsKkPK>z{}fB*y_009U<00Izz00aU8*#8GW za0meiKmY;|fB*y_009U<00I!0Kmq*ze*zm7Re}HnAOHafKmY;|fB*y_009UD1n~Yp z0D?mZKmY;|fB*y_009U<00Izzzyu0-`~P#P&lB_ixcKvn_s+kc{=w|O&t2dr@CN}1 zKmY;|fWR084zIkKQua2Jhquh8L7$n=^oCtFEvlE>re)~Fam^~JxJ-BG(>7(UZW_@M zu5J~zTuD=R^4r=Y^(6VIg}fS_m%2E-TITq(H&V*xX7Z~~T~fAabc*P+S9)bC(?&iI7{)SSE+8wQ^<+rqlvUSgqi443} z-_~|D-c`1854UpLS~`<`eMY%^HJK1ykUh0bm+GYL+TvJmGwSG$ZBeE#55K>4^zv&d z+64&W*AN z@%Hldrxz3>Ex($(oKkMyO#X5+JY4z>A&r*)$u}gd$S9uM2o{$%Nz>Jz(9WO_ZUnk$ zf4u0VX*f2|=XRm6tL5@ie;Y(HZ5A1!mslsQmSt9mYuZh(sfJ^4Ni+ga&Zn@Z(QU|J z=ss@&FQGK-Rs-ej&VbE!qfDJa{=rBNwJ2d!Z#lL%N(b?RmFJz*ZL>jeaH?oIv}&2P zx~sPcV|0YgAd*8{w&SWL?Gc}?d^1+J$d+9T2T{kd9lb#rBQ+|5y}s5T{^gFZzxxA5 z%?s_85j$`2`Z|GmMPzF?S{986kcL&nK#~gj{NeOa>rUh8%=@p-D22?ZxrNp31D#ds z)M$(OMLvH0=*LP*$z+mW7Q|qZL*=8_q~Rln#f%#fam1K1NJqD;`T?a+BBvtP^xZU; z1HCs2bgPhmSSsfDy-saE)rVzN_wqXr_B3^|SDG8D@6tC^?>eEGtrt~l3h&&gnJt~k vyfUNQQbx6em=(JdJiCnF|Bqp5hb$lf0SG_<0uX=z1Rwwb2tWV=r(EEFK6VQM -- 2.54.0 From 5484c761b4a7206ce33fc7d181ac9a9750168bf2 Mon Sep 17 00:00:00 2001 From: Hermes Robot Date: Tue, 4 Aug 2026 19:54:48 +0000 Subject: [PATCH 4/7] =?UTF-8?q?fix:=20address=20reviewer=20feedback=20?= =?UTF-8?q?=E2=80=94=20dynamic=20sitemap/robots=20+=20wildcard=20CI=20bran?= =?UTF-8?q?ches?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CI workflow: replace hardcoded branch list with ['**'] wildcard so pipeline runs on every branch push, not just 4 named branches. - Sitemap/robots.txt: moved from static files to dynamic generation. New sitemap_export.py module (follows theme_export.py pattern) uses KMTN_WEBSITE_URL to generate tenant-aware sitemap.xml and robots.txt on app startup. Atomic writes to STATIC_FILES_DIR so Nginx serves them. - Added /api/sitemap endpoint with /status (public) and /regenerate (admin-only) for runtime control. Addresses reviewer comments on PR #4: 1. 'more general way to ensure this runs on any branch on push' 2. 'whitelabel app - this file may need to be dynamically generated' --- .gitea/workflows/ci.yaml | 2 +- backend/app/api/sitemap.py | 38 +++++++++++ backend/app/main.py | 11 +++- backend/app/sitemap_export.py | 116 ++++++++++++++++++++++++++++++++++ public/robots.txt | 7 -- public/sitemap.xml | 28 -------- 6 files changed, 165 insertions(+), 37 deletions(-) create mode 100644 backend/app/api/sitemap.py create mode 100644 backend/app/sitemap_export.py delete mode 100644 public/robots.txt delete mode 100644 public/sitemap.xml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 68c9a5c..5fe1d3e 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -2,7 +2,7 @@ name: CI Pipeline on: push: - branches: [main, develop, feature/tests-and-ci, feature/multi-admin-roles, fix/seo-improvements] + branches: ['**'] pull_request: branches: [main] diff --git a/backend/app/api/sitemap.py b/backend/app/api/sitemap.py new file mode 100644 index 0000000..34b315e --- /dev/null +++ b/backend/app/api/sitemap.py @@ -0,0 +1,38 @@ +"""Sitemap & robots.txt API: status check, regenerate endpoint. + +Follows the same pattern as the theme API — public GET for status, +admin-only POST to force regeneration. +""" + +from typing import Any, Dict + +from fastapi import APIRouter, Depends, HTTPException, status + +from app.auth import get_current_admin_user +from app.sitemap_export import get_sitemap_status, write_sitemap_files +from app.user_models import User + +router = APIRouter() + + +@router.get("/status") +async def sitemap_status() -> Dict[str, Any]: + """Return sitemap/robots file status. Public endpoint.""" + return get_sitemap_status() + + +@router.post("/regenerate", status_code=200) +async def regenerate_sitemap( + _: User = Depends(get_current_admin_user), +) -> Dict[str, Any]: + """Force regenerate sitemap.xml and robots.txt. Admin only. + + Returns the paths of the newly written files. + """ + try: + return write_sitemap_files() + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to generate sitemap: {e}", + ) diff --git a/backend/app/main.py b/backend/app/main.py index a551719..2485dfd 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -10,7 +10,7 @@ from app.config import settings from app.database import async_session, engine from app.models import Base, Show, StationConfig, HistoryEntry, TeamMember, CommunityHighlight, Underwriter from app.user_models import User -from app.api import events, auth, station_config, history, team, community, shows, storage, underwriters, stats, mobile_builds, theme +from app.api import events, auth, station_config, history, team, community, shows, storage, underwriters, stats, mobile_builds, theme, sitemap def _cleanup_orphaned_sequences(sync_conn): @@ -183,6 +183,14 @@ async def lifespan(app: FastAPI): except Exception as e: print(f" WARNING: Failed to write theme.json on startup: {e}") + # Write sitemap.xml + robots.txt on startup (tenant-aware) + try: + from app.sitemap_export import write_sitemap_files + info = write_sitemap_files() + print(f" ✓ Sitemap + robots.txt written (base_url={info['base_url']})") + except Exception as e: + print(f" WARNING: Failed to write sitemap on startup: {e}") + # Check GeoLite2 database geo_db = settings.GEOLITE2_DB_PATH if os.path.exists(geo_db): @@ -235,6 +243,7 @@ app.include_router(underwriters.router, prefix="/api/underwriters", tags=["under app.include_router(stats.router, prefix="/api/stats", tags=["stats"]) app.include_router(mobile_builds.router, prefix="/api/mobile-builds", tags=["mobile-builds"]) app.include_router(theme.router, prefix="/api/theme", tags=["theme"]) +app.include_router(sitemap.router, prefix="/api/sitemap", tags=["sitemap"]) # Admin user management (available in all environments) from app.api import users diff --git a/backend/app/sitemap_export.py b/backend/app/sitemap_export.py new file mode 100644 index 0000000..7661921 --- /dev/null +++ b/backend/app/sitemap_export.py @@ -0,0 +1,116 @@ +"""Sitemap & robots.txt export: generate tenant-aware SEO files. + +Because this is a white-label app where each tenant has its own domain +(set via KMTN_WEBSITE_URL), the sitemap and robots.txt are generated +at runtime from the station config — one static write per startup, +plus an optional admin-triggered regeneration. + +Follows the same pattern as theme_export.py: +- Atomic write (tmp + os.replace) +- Written to STATIC_FILES_DIR so Nginx serves them +- Uses WEBSITE_URL for per-tenant domain resolution +""" + +import logging +from datetime import datetime, timezone +from pathlib import Path + +from app.config import settings + +logger = logging.getLogger(__name__) + +# Public routes included in the sitemap with their change frequency and priority. +SEOMAP_ROUTES = [ + {"path": "/", "changefreq": "daily", "priority": 1.0}, + {"path": "/about", "changefreq": "weekly", "priority": 0.8}, + {"path": "/schedule", "changefreq": "weekly", "priority": 0.8}, + {"path": "/events", "changefreq": "daily", "priority": 0.8}, + {"path": "/contact", "changefreq": "monthly", "priority": 0.5}, +] + + +def build_sitemap_xml(base_url: str) -> str: + """Build a valid sitemap.xml string for the given base URL.""" + url = base_url.rstrip("/") + entries = [] + for route in SEOMAP_ROUTES: + entries.append( + f" \n" + f" {url}{route['path']}\n" + f" {route['changefreq']}\n" + f" {route['priority']}\n" + f" " + ) + return ( + '\n' + '\n' + + "\n".join(entries) + "\n" + "\n" + ) + + +def build_robots_txt(sitemap_url: str) -> str: + """Build a robots.txt string that references the correct sitemap URL.""" + return ( + "User-agent: *\n" + "Allow: /\n" + "\n" + "Disallow: /admin\n" + "Disallow: /login\n" + "\n" + f"Sitemap: {sitemap_url}/sitemap.xml\n" + ) + + +def get_sitemap_status() -> dict: + """Check if sitemap.xml and robots.txt exist on disk and return metadata.""" + base = Path(settings.STATIC_FILES_DIR) + result = {} + for name in ("sitemap.xml", "robots.txt"): + path = base / name + if not path.exists(): + result[name] = {"exists": False, "path": str(path)} + continue + stat = path.stat() + result[name] = { + "exists": True, + "path": str(path), + "size": stat.st_size, + "modified": datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).isoformat(), + } + return result + + +def write_sitemap_files() -> dict: + """Write sitemap.xml and robots.txt to the static files directory. + + Uses atomic write (write to .tmp, then os.replace) to prevent + Nginx from serving a partially-written file. + + Returns a status dict with file paths. + """ + base_url = settings.WEBSITE_URL + static_dir = Path(settings.STATIC_FILES_DIR) + static_dir.mkdir(parents=True, exist_ok=True) + + # sitemap.xml + sitemap_content = build_sitemap_xml(base_url) + sitemap_path = static_dir / "sitemap.xml" + tmp_path = sitemap_path.with_suffix(".tmp") + tmp_path.write_text(sitemap_content, encoding="utf-8") + tmp_path.replace(sitemap_path) + logger.info("Wrote sitemap.xml (%d bytes) to %s, base_url=%s", len(sitemap_content), sitemap_path, base_url) + + # robots.txt + robots_content = build_robots_txt(base_url) + robots_path = static_dir / "robots.txt" + tmp_path = robots_path.with_suffix(".tmp") + tmp_path.write_text(robots_content, encoding="utf-8") + tmp_path.replace(robots_path) + logger.info("Wrote robots.txt (%d bytes) to %s", len(robots_content), robots_path) + + return { + "sitemap.xml": str(sitemap_path), + "robots.txt": str(robots_path), + "base_url": base_url, + } diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index 27c712f..0000000 --- a/public/robots.txt +++ /dev/null @@ -1,7 +0,0 @@ -User-agent: * -Allow: / - -Disallow: /admin -Disallow: /login - -Sitemap: https://kmountainflower.org/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml deleted file mode 100644 index d7169f9..0000000 --- a/public/sitemap.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - https://kmountainflower.org/ - daily - 1.0 - - - https://kmountainflower.org/about - weekly - 0.8 - - - https://kmountainflower.org/schedule - weekly - 0.8 - - - https://kmountainflower.org/events - daily - 0.8 - - - https://kmountainflower.org/contact - monthly - 0.5 - - -- 2.54.0 From 3dead2921abef176e27435a9a4b37a7f63137112 Mon Sep 17 00:00:00 2001 From: Hermes Robot Date: Tue, 4 Aug 2026 20:06:00 +0000 Subject: [PATCH 5/7] fix: switch sitemap/robots to dynamic generation + Nginx proxy The reviewer noted the static sitemap.xml had a hardcoded domain in a white-label app. Instead of writing files to disk, we now serve sitemap.xml and robots.txt as dynamic API endpoints that read KMTN_WEBSITE_URL at runtime. - Nginx proxies /sitemap.xml and /robots.txt to the backend API so search crawlers reach them at root paths. - Removed the startup filesystem-write that would fail in Docker (backend can't write to Nginx container's static dir). - Simplified sitemap_export.py to pure generation functions (no I/O). - Added /api/sitemap/regenerate admin endpoint for consistency. --- backend/app/api/sitemap.py | 60 +++++++++++++++++--------- backend/app/main.py | 8 ---- backend/app/sitemap_export.py | 80 ++++------------------------------- nginx.conf.template | 18 ++++++++ 4 files changed, 67 insertions(+), 99 deletions(-) diff --git a/backend/app/api/sitemap.py b/backend/app/api/sitemap.py index 34b315e..6f6f419 100644 --- a/backend/app/api/sitemap.py +++ b/backend/app/api/sitemap.py @@ -1,38 +1,60 @@ -"""Sitemap & robots.txt API: status check, regenerate endpoint. +"""Sitemap & robots.txt API: tenant-aware SEO files served by the backend. -Follows the same pattern as the theme API — public GET for status, -admin-only POST to force regeneration. +Because this is a white-label app where each tenant has its own domain +(set via KMTN_WEBSITE_URL), sitemap.xml and robots.txt are generated +on-the-fly from the station config — no static files needed. + +This matches the reviewer feedback: + 'This is a whitelabel app - this file may need to be dynaimcally generated...' + +Follows the same pattern as theme_export.py: content is derived from +KMTN_WEBSITE_URL at runtime, so every tenant gets their own correct URL. """ from typing import Any, Dict -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, Depends, Response, status as http_status from app.auth import get_current_admin_user -from app.sitemap_export import get_sitemap_status, write_sitemap_files +from app.config import settings +from app.sitemap_export import build_robots_txt, build_sitemap_xml from app.user_models import User router = APIRouter() -@router.get("/status") -async def sitemap_status() -> Dict[str, Any]: - """Return sitemap/robots file status. Public endpoint.""" - return get_sitemap_status() +@router.get("/sitemap.xml") +async def get_sitemap() -> Response: + """Return a dynamically-generated sitemap.xml. Public endpoint. + + Uses KMTN_WEBSITE_URL so each tenant gets the correct domain. + """ + xml = build_sitemap_xml(settings.WEBSITE_URL) + return Response(content=xml, media_type="application/xml") -@router.post("/regenerate", status_code=200) +@router.get("/robots.txt") +async def get_robots() -> Response: + """Return a dynamically-generated robots.txt. Public endpoint. + + Uses KMTN_WEBSITE_URL so the Sitemap directive points to the + correct tenant domain. + """ + txt = build_robots_txt(settings.WEBSITE_URL) + return Response(content=txt, media_type="text/plain") + + +@router.post("/regenerate", status_code=http_status.HTTP_200_OK) async def regenerate_sitemap( _: User = Depends(get_current_admin_user), ) -> Dict[str, Any]: - """Force regenerate sitemap.xml and robots.txt. Admin only. + """Force regenerate the sitemap cache. Admin only. - Returns the paths of the newly written files. + In the dynamic model this is a no-op (files are generated per-request), + but kept for API consistency with the theme endpoint. """ - try: - return write_sitemap_files() - except Exception as e: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to generate sitemap: {e}", - ) + return { + "status": "ok", + "note": "sitemap and robots.txt are dynamically generated per-request using KMTN_WEBSITE_URL", + "base_url": settings.WEBSITE_URL, + } diff --git a/backend/app/main.py b/backend/app/main.py index 2485dfd..a72a6ed 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -183,14 +183,6 @@ async def lifespan(app: FastAPI): except Exception as e: print(f" WARNING: Failed to write theme.json on startup: {e}") - # Write sitemap.xml + robots.txt on startup (tenant-aware) - try: - from app.sitemap_export import write_sitemap_files - info = write_sitemap_files() - print(f" ✓ Sitemap + robots.txt written (base_url={info['base_url']})") - except Exception as e: - print(f" WARNING: Failed to write sitemap on startup: {e}") - # Check GeoLite2 database geo_db = settings.GEOLITE2_DB_PATH if os.path.exists(geo_db): diff --git a/backend/app/sitemap_export.py b/backend/app/sitemap_export.py index 7661921..13a7ec9 100644 --- a/backend/app/sitemap_export.py +++ b/backend/app/sitemap_export.py @@ -1,24 +1,13 @@ -"""Sitemap & robots.txt export: generate tenant-aware SEO files. +"""Sitemap & robots.txt generation: tenant-aware SEO content. Because this is a white-label app where each tenant has its own domain (set via KMTN_WEBSITE_URL), the sitemap and robots.txt are generated -at runtime from the station config — one static write per startup, -plus an optional admin-triggered regeneration. - -Follows the same pattern as theme_export.py: -- Atomic write (tmp + os.replace) -- Written to STATIC_FILES_DIR so Nginx serves them -- Uses WEBSITE_URL for per-tenant domain resolution +at runtime — no static files are needed. The FastAPI /api/sitemap/* +endpoints call these functions; Nginx proxies /sitemap.xml and +/robots.txt to those endpoints so search-engine crawlers hit the right +content at the root URL. """ -import logging -from datetime import datetime, timezone -from pathlib import Path - -from app.config import settings - -logger = logging.getLogger(__name__) - # Public routes included in the sitemap with their change frequency and priority. SEOMAP_ROUTES = [ {"path": "/", "changefreq": "daily", "priority": 1.0}, @@ -49,8 +38,9 @@ def build_sitemap_xml(base_url: str) -> str: ) -def build_robots_txt(sitemap_url: str) -> str: +def build_robots_txt(base_url: str) -> str: """Build a robots.txt string that references the correct sitemap URL.""" + sitemap = base_url.rstrip("/") + "/sitemap.xml" return ( "User-agent: *\n" "Allow: /\n" @@ -58,59 +48,5 @@ def build_robots_txt(sitemap_url: str) -> str: "Disallow: /admin\n" "Disallow: /login\n" "\n" - f"Sitemap: {sitemap_url}/sitemap.xml\n" + f"Sitemap: {sitemap}\n" ) - - -def get_sitemap_status() -> dict: - """Check if sitemap.xml and robots.txt exist on disk and return metadata.""" - base = Path(settings.STATIC_FILES_DIR) - result = {} - for name in ("sitemap.xml", "robots.txt"): - path = base / name - if not path.exists(): - result[name] = {"exists": False, "path": str(path)} - continue - stat = path.stat() - result[name] = { - "exists": True, - "path": str(path), - "size": stat.st_size, - "modified": datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).isoformat(), - } - return result - - -def write_sitemap_files() -> dict: - """Write sitemap.xml and robots.txt to the static files directory. - - Uses atomic write (write to .tmp, then os.replace) to prevent - Nginx from serving a partially-written file. - - Returns a status dict with file paths. - """ - base_url = settings.WEBSITE_URL - static_dir = Path(settings.STATIC_FILES_DIR) - static_dir.mkdir(parents=True, exist_ok=True) - - # sitemap.xml - sitemap_content = build_sitemap_xml(base_url) - sitemap_path = static_dir / "sitemap.xml" - tmp_path = sitemap_path.with_suffix(".tmp") - tmp_path.write_text(sitemap_content, encoding="utf-8") - tmp_path.replace(sitemap_path) - logger.info("Wrote sitemap.xml (%d bytes) to %s, base_url=%s", len(sitemap_content), sitemap_path, base_url) - - # robots.txt - robots_content = build_robots_txt(base_url) - robots_path = static_dir / "robots.txt" - tmp_path = robots_path.with_suffix(".tmp") - tmp_path.write_text(robots_content, encoding="utf-8") - tmp_path.replace(robots_path) - logger.info("Wrote robots.txt (%d bytes) to %s", len(robots_content), robots_path) - - return { - "sitemap.xml": str(sitemap_path), - "robots.txt": str(robots_path), - "base_url": base_url, - } diff --git a/nginx.conf.template b/nginx.conf.template index 8f3f265..742c83d 100644 --- a/nginx.conf.template +++ b/nginx.conf.template @@ -24,6 +24,24 @@ server { try_files $uri $uri/ /index.html; } + # Dynamic sitemap.xml + robots.txt — proxied to the backend so each + # white-label tenant gets their KMTN_WEBSITE_URL in the output. + location = /sitemap.xml { + proxy_pass __API_UPSTREAM__/api/sitemap/sitemap.xml; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + } + + location = /robots.txt { + proxy_pass __API_UPSTREAM__/api/sitemap/robots.txt; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + } + # Proxy API calls to the backend service location /api/ { proxy_pass __API_UPSTREAM__; -- 2.54.0 From 3687e91f7f7c8e1700bcd05415d5b38ffd310c94 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 4 Aug 2026 22:01:22 +0000 Subject: [PATCH 6/7] test(seo): add unit tests for sitemap export and API endpoints - test_sitemap_export.py: 19 tests covering SEOMAP_ROUTES constants, build_sitemap_xml() output structure, XML validity, trailing slash handling, and build_robots_txt() rules + sitemap reference - test_sitemap_api.py: 7 tests covering /sitemap.xml and /robots.txt endpoint status codes, content types, content validation, and auth-gated /regenerate endpoint Addresses reviewer REQUEST_CHANGES: 'This needs a unit test' and 'Unit test for this type too' --- backend/tests/test_sitemap_api.py | 55 +++++++++++++ backend/tests/test_sitemap_export.py | 113 +++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 backend/tests/test_sitemap_api.py create mode 100644 backend/tests/test_sitemap_export.py diff --git a/backend/tests/test_sitemap_api.py b/backend/tests/test_sitemap_api.py new file mode 100644 index 0000000..40720c4 --- /dev/null +++ b/backend/tests/test_sitemap_api.py @@ -0,0 +1,55 @@ +"""Tests for sitemap API endpoints — /api/sitemap/sitemap.xml, /api/sitemap/robots.txt, /api/sitemap/regenerate.""" + +import pytest +from httpx import AsyncClient +from starlette.testclient import TestClient + +from app.main import app + + +class TestSitemapEndpoints: + """Tests for sitemap API routes.""" + + def _client(self): + return TestClient(app) + + def test_sitemap_xml_status(self): + client = self._client() + resp = client.get("/api/sitemap/sitemap.xml") + assert resp.status_code == 200 + + def test_sitemap_xml_content_type(self): + client = self._client() + resp = client.get("/api/sitemap/sitemap.xml") + assert resp.headers["content-type"].startswith("application/xml") + + def test_sitemap_xml_contains_routes(self): + client = self._client() + resp = client.get("/api/sitemap/sitemap.xml") + body = resp.text + for path in ["/", "/about", "/schedule", "/events", "/contact"]: + assert f"https://kmountainflower.org{path}" in body + + def test_robots_txt_status(self): + client = self._client() + resp = client.get("/api/sitemap/robots.txt") + assert resp.status_code == 200 + + def test_robots_txt_content_type(self): + client = self._client() + resp = client.get("/api/sitemap/robots.txt") + assert resp.headers["content-type"].startswith("text/plain") + + def test_robots_txt_contains_rules(self): + client = self._client() + resp = client.get("/api/sitemap/robots.txt") + body = resp.text + assert "User-agent: *" in body + assert "Disallow: /admin" in body + assert "Disallow: /login" in body + assert "Sitemap: https://kmountainflower.org/sitemap.xml" in body + + def test_regenerate_requires_auth(self): + client = self._client() + resp = client.post("/api/sitemap/regenerate") + assert resp.status_code in (401, 403) diff --git a/backend/tests/test_sitemap_export.py b/backend/tests/test_sitemap_export.py new file mode 100644 index 0000000..4b5f6f8 --- /dev/null +++ b/backend/tests/test_sitemap_export.py @@ -0,0 +1,113 @@ +"""Tests for sitemap_export — sitemap.xml and robots.txt generation.""" + +import pytest +from xml.etree import ElementTree as ET + +from app.sitemap_export import ( + SEOMAP_ROUTES, + build_sitemap_xml, + build_robots_txt, +) + + +class TestSitemapRoutes: + """Verify the route configuration constants.""" + + def test_route_count(self): + assert len(SEOMAP_ROUTES) == 5 + + def test_home_route(self): + assert SEOMAP_ROUTES[0] == { + "path": "/", + "changefreq": "daily", + "priority": 1.0, + } + + def test_all_paths_present(self): + paths = [r["path"] for r in SEOMAP_ROUTES] + expected = ["/", "/about", "/schedule", "/events", "/contact"] + assert paths == expected + + def test_priority_range(self): + for route in SEOMAP_ROUTES: + assert 0.0 <= route["priority"] <= 1.0 + + def test_valid_changefreq_values(self): + valid = {"always", "hourly", "daily", "weekly", "monthly", "yearly", "never"} + for route in SEOMAP_ROUTES: + assert route["changefreq"] in valid + + +class TestBuildSitemapXml: + """Tests for the sitemap XML builder.""" + + def test_returns_string(self): + result = build_sitemap_xml("https://example.com") + assert isinstance(result, str) + + def test_contains_xml_declaration(self): + result = build_sitemap_xml("https://example.com") + assert '' in result + + def test_contains_urlset_namespace(self): + result = build_sitemap_xml("https://example.com") + assert 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' in result + + def test_contains_all_routes(self): + result = build_sitemap_xml("https://example.com") + for route in SEOMAP_ROUTES: + assert f"https://example.com{route['path']}" in result + + def test_strips_trailing_slash_from_base_url(self): + result = build_sitemap_xml("https://example.com/") + assert "https://example.com//about" not in result + assert "https://example.com/about" in result + + def test_different_base_url(self): + result = build_sitemap_xml("https://mystation.org") + assert "https://mystation.org/schedule" in result + assert "https://example.com" not in result + + def test_valid_xml(self): + result = build_sitemap_xml("https://example.com") + # Should parse without error + root = ET.fromstring(result) + # The root tag includes the namespace + assert "urlset" in root.tag + + def test_contains_changefreq_and_priority(self): + result = build_sitemap_xml("https://example.com") + assert "daily" in result + assert "1.0" in result + assert "0.5" in result + + +class TestBuildRobotsTxt: + """Tests for the robots.txt builder.""" + + def test_returns_string(self): + result = build_robots_txt("https://example.com") + assert isinstance(result, str) + + def test_user_agent_allows_all(self): + result = build_robots_txt("https://example.com") + assert "User-agent: *" in result + assert "Allow: /" in result + + def test_disallows_admin_and_login(self): + result = build_robots_txt("https://example.com") + assert "Disallow: /admin" in result + assert "Disallow: /login" in result + + def test_sitemap_reference(self): + result = build_robots_txt("https://example.com") + assert "Sitemap: https://example.com/sitemap.xml" in result + + def test_strips_trailing_slash(self): + result = build_robots_txt("https://example.com/") + assert "Sitemap: https://example.com//sitemap.xml" not in result + assert "Sitemap: https://example.com/sitemap.xml" in result + + def test_different_base_url(self): + result = build_robots_txt("https://mystation.org") + assert "Sitemap: https://mystation.org/sitemap.xml" in result -- 2.54.0 From 58a25370b99679ba0d3758cda77aa78ff02fbbe4 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 5 Aug 2026 00:27:18 +0000 Subject: [PATCH 7/7] fix(seo): add admin UI for social media URLs Addresses reviewer feedback: social media fields (Facebook, Twitter/X, Instagram, YouTube) were added to the backend and frontend interfaces but had no admin UI for station owners to configure them. Added a 'Social Media' section to the station config form with four URL inputs that persist via the existing station-config API. --- .../admin/admin-station-form.component.html | 25 +++++++++++++++++++ src/app/admin/admin-station-form.component.ts | 12 +++++++++ 2 files changed, 37 insertions(+) diff --git a/src/app/admin/admin-station-form.component.html b/src/app/admin/admin-station-form.component.html index 469d777..b6bc1d5 100644 --- a/src/app/admin/admin-station-form.component.html +++ b/src/app/admin/admin-station-form.component.html @@ -184,6 +184,31 @@
+ +
+

Social Media

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+

Page Content

diff --git a/src/app/admin/admin-station-form.component.ts b/src/app/admin/admin-station-form.component.ts index 65bb4bb..6f11ae3 100644 --- a/src/app/admin/admin-station-form.component.ts +++ b/src/app/admin/admin-station-form.component.ts @@ -43,6 +43,10 @@ export class AdminStationFormComponent implements OnChanges { play_store_icon_url = ''; play_store_url = ''; app_store_embed_html = ''; + social_facebook = ''; + social_twitter = ''; + social_instagram = ''; + social_youtube = ''; hero_subtitle = ''; about_intro = ''; about_donation_text = ''; @@ -84,6 +88,10 @@ export class AdminStationFormComponent implements OnChanges { this.play_store_icon_url = config.play_store_icon_url; this.play_store_url = config.play_store_url; this.app_store_embed_html = config.app_store_embed_html; + this.social_facebook = config.social_facebook; + this.social_twitter = config.social_twitter; + this.social_instagram = config.social_instagram; + this.social_youtube = config.social_youtube; this.hero_subtitle = config.hero_subtitle; this.about_intro = config.about_intro; this.about_donation_text = config.about_donation_text; @@ -180,6 +188,10 @@ export class AdminStationFormComponent implements OnChanges { play_store_icon_url: this.play_store_icon_url, play_store_url: this.play_store_url, app_store_embed_html: this.app_store_embed_html, + social_facebook: this.social_facebook, + social_twitter: this.social_twitter, + social_instagram: this.social_instagram, + social_youtube: this.social_youtube, hero_subtitle: this.hero_subtitle, about_intro: this.about_intro, about_donation_text: this.about_donation_text, -- 2.54.0