From e34473ad30075b09a90f821c0215afcfc18b02a1 Mon Sep 17 00:00:00 2001 From: S0okJu Date: Sun, 27 Jul 2025 20:21:52 +0900 Subject: [PATCH] deprecated/kafka --- __pycache__/app.cpython-310.pyc | Bin 304 -> 0 bytes .../api/__pycache__/__init__.cpython-310.pyc | Bin 189 -> 0 bytes .../api/__pycache__/__init__.cpython-313.pyc | Bin 201 -> 0 bytes .../__pycache__/challenge_api.cpython-310.pyc | Bin 2421 -> 0 bytes .../__pycache__/challenge_api.cpython-313.pyc | Bin 3791 -> 0 bytes challenge_api/config.py | 20 --- .../db/__pycache__/__init__.cpython-310.pyc | Bin 271 -> 0 bytes .../db/__pycache__/__init__.cpython-312.pyc | Bin 287 -> 0 bytes .../db/__pycache__/__init__.cpython-313.pyc | Bin 287 -> 0 bytes .../db/__pycache__/config.cpython-310.pyc | Bin 2960 -> 0 bytes .../db/__pycache__/config.cpython-312.pyc | Bin 3633 -> 0 bytes .../db/__pycache__/config.cpython-313.pyc | Bin 3461 -> 0 bytes .../db/__pycache__/db_manager.cpython-312.pyc | Bin 249 -> 0 bytes .../db/__pycache__/models.cpython-310.pyc | Bin 4608 -> 0 bytes .../db/__pycache__/models.cpython-312.pyc | Bin 10676 -> 0 bytes .../db/__pycache__/models.cpython-313.pyc | Bin 10299 -> 0 bytes .../db/__pycache__/repository.cpython-310.pyc | Bin 5301 -> 0 bytes challenge_api/extensions/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 170 -> 0 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 174 -> 0 bytes challenge_api/extensions/kafka/__init__.py | 9 -- .../__pycache__/__init__.cpython-310.pyc | Bin 390 -> 0 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 412 -> 0 bytes .../kafka/__pycache__/config.cpython-310.pyc | Bin 1293 -> 0 bytes .../kafka/__pycache__/config.cpython-312.pyc | Bin 1524 -> 0 bytes .../__pycache__/consumer.cpython-310.pyc | Bin 3655 -> 0 bytes .../__pycache__/consumer.cpython-312.pyc | Bin 5001 -> 0 bytes .../kafka/__pycache__/handler.cpython-310.pyc | Bin 2550 -> 0 bytes challenge_api/extensions/kafka/config.py | 33 ----- challenge_api/extensions/kafka/consumer.py | 106 --------------- challenge_api/extensions/kafka/handler.py | 123 ------------------ challenge_api/extensions_manager.py | 87 ------------- challenge_api/factory.py | 14 -- challenge_api/requirements.txt | 1 - 34 files changed, 393 deletions(-) delete mode 100644 __pycache__/app.cpython-310.pyc delete mode 100644 challenge_api/api/__pycache__/__init__.cpython-310.pyc delete mode 100644 challenge_api/api/__pycache__/__init__.cpython-313.pyc delete mode 100644 challenge_api/api/__pycache__/challenge_api.cpython-310.pyc delete mode 100644 challenge_api/api/__pycache__/challenge_api.cpython-313.pyc delete mode 100644 challenge_api/db/__pycache__/__init__.cpython-310.pyc delete mode 100644 challenge_api/db/__pycache__/__init__.cpython-312.pyc delete mode 100644 challenge_api/db/__pycache__/__init__.cpython-313.pyc delete mode 100644 challenge_api/db/__pycache__/config.cpython-310.pyc delete mode 100644 challenge_api/db/__pycache__/config.cpython-312.pyc delete mode 100644 challenge_api/db/__pycache__/config.cpython-313.pyc delete mode 100644 challenge_api/db/__pycache__/db_manager.cpython-312.pyc delete mode 100644 challenge_api/db/__pycache__/models.cpython-310.pyc delete mode 100644 challenge_api/db/__pycache__/models.cpython-312.pyc delete mode 100644 challenge_api/db/__pycache__/models.cpython-313.pyc delete mode 100644 challenge_api/db/__pycache__/repository.cpython-310.pyc delete mode 100644 challenge_api/extensions/__init__.py delete mode 100644 challenge_api/extensions/__pycache__/__init__.cpython-310.pyc delete mode 100644 challenge_api/extensions/__pycache__/__init__.cpython-312.pyc delete mode 100644 challenge_api/extensions/kafka/__init__.py delete mode 100644 challenge_api/extensions/kafka/__pycache__/__init__.cpython-310.pyc delete mode 100644 challenge_api/extensions/kafka/__pycache__/__init__.cpython-312.pyc delete mode 100644 challenge_api/extensions/kafka/__pycache__/config.cpython-310.pyc delete mode 100644 challenge_api/extensions/kafka/__pycache__/config.cpython-312.pyc delete mode 100644 challenge_api/extensions/kafka/__pycache__/consumer.cpython-310.pyc delete mode 100644 challenge_api/extensions/kafka/__pycache__/consumer.cpython-312.pyc delete mode 100644 challenge_api/extensions/kafka/__pycache__/handler.cpython-310.pyc delete mode 100644 challenge_api/extensions/kafka/config.py delete mode 100644 challenge_api/extensions/kafka/consumer.py delete mode 100644 challenge_api/extensions/kafka/handler.py delete mode 100644 challenge_api/extensions_manager.py diff --git a/__pycache__/app.cpython-310.pyc b/__pycache__/app.cpython-310.pyc deleted file mode 100644 index a8240b7348c387e5fc3b0e66ddb25e8addd141e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304 zcmYk1Jx;_x429<>*^O3gwICWUkb(@NMu>u%258cZhS(%wlT0*|6iZ7*!40?oSDKcJ zE6^-Xgb>eqwk-L5lI?oEVx(+yQoYmsoR~A3=M4D^%LPghTj7pT zJVhWOf*2NWk~3J|&Q6mr%CVG(RpHPKs7>F8MC;bnoz~CGvuv)r`gKERGDMa2Lx=$% z#xg{`b;C^>=SM|lng+Xa%GcSWDF*9yP7pfCUt(uk)H+Bvb|1`5U(M}Xse6yktFmsv rlsmLtsjks)2ivPZ>?vUtfwF!Vl8e^D*x)56hMzzOY#|bP$fNxau31kv diff --git a/challenge_api/api/__pycache__/__init__.cpython-310.pyc b/challenge_api/api/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index ace4f9b7eee1f99a4cfc734db40805c9741e8633..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 189 zcmd1j<>g`k0uLdX3>hH(7{oyaj6jY95Ep}#r7)y3Mlqx?1~X_f-Qr2kNX*Ge%}Y;> zPb%=!WW2>59}g6Wk6+19#0*pnCVsi;hZd(673-&G=B6a3SElCYrR%$-7H5~_7wE$c s&`m7JMDXH4JP?kL&&^MASF{7f*JG~ix|=wHJNVlBxfY%SU){8HzhH>GBrOhUEd|OIJ+djKp(DGH?bfS z!HWm+KsY`=GcU6wK3=b&@)n0pZhlH>PO4oIJJ48=gNk{9#0O?ZM#cx+k{7t78rh3j Gfg%7k#y0B! diff --git a/challenge_api/api/__pycache__/challenge_api.cpython-310.pyc b/challenge_api/api/__pycache__/challenge_api.cpython-310.pyc deleted file mode 100644 index 202612106c7963b2f4340e6388d0a84d3011f6e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2421 zcmaJ@&u<$=6yDig@2(x&NmD0HniQ6{q8NcA1V|M^2$8l@sg$AygtS6hZ9Efq-Th%^ z#J#yf0*ejN|j}C(m;bnh9gB6SdB}5KFqOS&Os5zSKYk?kSoJ?pq zMrb;wl<9#LW}U3;XM$Wf;*5lOCm$A^f|MCSF&uS9!!c(}j?G|SIPQ$gz7^~bOHN7l zv%y3-=}aPpWN?lg=%~&VnW|6Y5uC3fT)3r@5-#G=j*7?dzH3Scb&zvVT8!_q*pEv+ ziy3J#vCCo-ALv=k;;BWYJpB|RSIQ_E{dn>0!U|PLP1? zrCW&kskj;8t~Y8jHMA-_M;bBnc}!Q5LqnQH?(yb;8eGhtInL$-pG3mn3F)oJiQ9|8 zU5fDvC<=X-Pe;pflC5q>kSK}%$=StE!Nv>;KObY3 z!^^&ahASIrQ)w$)#W`PTDfiGfsExSRQQB%tt?Mm>(Jj>0S{ggbGgxh*y77a8HTatF z)$e9-<`!B*+`&%>B0Gq4%A@*u3B#vrxCkVU1y0tq2-Z|W>HC{M?kb{&SAk}vI}`pcqHW8*Pm={uG^`c zuyy)?BqJF`P0`mFr)8bKB$O|U#uidUb}*-hg=C_;lq(q2$BT(+Ot_)y77~!<(70Me zIW&f>E9&303@QMQ$wDy%oFHHyxVkWVWdJy1FW}mm0IsFg4R}qguD=MOwek)?r~`y1 z7r^NNoGwxsXWIH@UBYHIUxN+*Gl3tkKl}9whbNfMTJ1DA8rf5X?wSxPoeaRO?Y>Iq=v8Dhg|t zJ)+7u$NI>Nq^^){-&qBQ?#@tB%Xw>0>tTln=E+h8e;*Wi8qUn{3V5j^PNRDl(Gg)L zkNL>38M(ws#NbF-<-&1R=~gpY5}m`tmQC&lUEeWW$JB#6j!{v6oTI~r@lu^sIjpsP zQOQavGcB=PSCmxO9lWWQ8cBx6O-^L(l6fExeE>gMRAp{i?}ssN2IK<-TeDZ8$t!tO YLV2|WiIkVWoLW>Z=oO2y9koF7FW4fq)&Kwi diff --git a/challenge_api/api/__pycache__/challenge_api.cpython-313.pyc b/challenge_api/api/__pycache__/challenge_api.cpython-313.pyc deleted file mode 100644 index 8a343316809eb53939cacdb1677b73fcf69484f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3791 zcmbtWO>7&-6`uX!@}HC_k)rjNjpD?j8Y&_x7Dm8$iv`5?V+g>F;=AG*eaBU!VZE2?QWe5fG*z#bcR{ z&|wBLL}nsv*aS^s4su~LG!q>gv4pMAN@P>S7Pdos*a013Cv*}W7jcE%&>e1pEo5$v zw1z#^lCDjNkyv~M(>O% zB0RN_%w|O?EuMm6Qq9N`@%ojuNii!Pwl*WH({f%)RidGFf0I`wD&}M*qsnku?`c{Q z)TEkkSVkdQBPWzo*^DURc)cXrq$(z={~<9er#~yXYXbbq58=mk$49LtPr1@Zb^%3 z0;3nUpee-vQRgb(g!l-d7i(B4xhO&oi2+t;ps47sT0n^mMg=!VaHLzrYBUOqcJrv| zkr2kA5=v(lQ_1wQC`;+kq^Mj{*Q3ub$fW_7%N>f`Gc!na?@k>bM?Xoxbn(-njhQ!}>vG{Xetr}snHKXn5!G1r=_Vpp;b!^T zm2%-q`S$y}Z{6n0#9(**R{1Au<=5Zay}q`4{hc7}Ms_Q{>Wj@+A3)5r#iR<4pv4*3 zgZhA-um<)bxsfoH`y>Q`_!1!oziuM@gBC0^iV6cPd>k+K;qD2nrNkOd;FDP4t$0dc zcGn=TKy=Ume^dDZ>Q=#?#cmqic6qm4y&t-IOD=wm**@}A@yNhN8|v0v2)c`ImwplB?s^aR<2TuuZVcU0-1Q1?KJxCL|+Z zDR@AR)NN?%V~jZn+%-?8cn#PB6*EQK z>z0Ouv_3GrljdH~|SJi5uq{Xk*UfJa=Ha zdGGe8zkeTPohzSRL!5YP^RqYKAUPsHL;NM3t6U`EMI-**U&TLu4)ji(L}r%>YU?j^ zf+P(dZGiZk^;WzKigT#kNL1YGOeVEdVGaH&9K_qKQ%iGV4p%=AXJ+^`>H_9Eb97G= zIgrPktUH@@I*XOW9UMUB3Ql!67ok?+nIFf}WK#_Zoh?Lxh?>Aj{x<26k;JoWB@-te;nUedg7L94{m}ZF~g0}Ai3P;iZyW!1Oemh!p zozyHRzuaSxvdYK__k;)B^VlYanS0%yi6CSR|PyzG;b4^R8dk8MwH;C6H2gBd{nBJzUG~IHIuExo-aw?w{ zpNCUOhbuzmPpCuJM^QU~-vJ#TgMlI#_y?Hyf(Fz%TuY$-W02SZV>@7S2R#2bxBpgG zVWH#>tvdcnIk%{`BGtA&QScQk8r7yzfj?8e^&b|NOVrRq+Ce?L&j9-CxI4bbqUl~Y qpj}m?(;Hl2i5Lww8ab)an$gf6i>7);aX~6?#4BZG;uV;< zWB^XO|G$6ueqG4(b4GJGBrOh zUEd|OIJ+djKp(DO7pNb>1DU3ulB6FWpP83g5+AQuPDLL{JgZx^p_y*nv6vtWBfE(ZgI!Qm!%dJXXfX{$KPU) zk5A0W0W!psGk}cLy!6!g#DYw{lq9`mux_xnl?(1+{S1?oreK&I)ZBJym f#0O?ZM#dX_q7B>+xTP*|NjI=RV=ye@0LlOW^khwj diff --git a/challenge_api/db/__pycache__/config.cpython-310.pyc b/challenge_api/db/__pycache__/config.cpython-310.pyc deleted file mode 100644 index 0bbe0ffc8967f1e8e418e5c00cd5b5c272f17c35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2960 zcmb7G&2JM&6yIHM9LIJ(p#@4+R=orZ8dm~Ul?81=VhU1-X`GbSmzCpLJ1(}@%&tq5 zQYjE4MWm=viA4l)WTdoJ5C=n}iUaB&vbU`u^*R%v`c9%8~2I1A7LZxsqo^UZ?5`4#8im0>^;T)&9p>;uv zvS_z$cI!UJk@i?S>+=Bah<7c=R{B@E(MNA6h z5II~FVHI6UH+e(pA$^2PisVglglT)pQCQnCqw`9frsE?QhSgj$oho$m9Ospw*lNkV zU$x4cwPMwLQ2}7xTbHbd)$JEm$ttc}m6BC1O52-r=17KztVheW(yBescJ&AI!8#6SRpzXIFidxXOdb-exsi=~eNn{kGJE7)s zNqFBK$4xOYSkGHMBgmE@%Z?c0ntGEJE7`ZQEn6 z?1!Xq7tS`a05c)_>F7`(NH7RFSNO70Fs#_}5rU=Cha9+LHBf zlf`bnY!#pHi5-r@f{0+8ElUeI3RG%34LK=U&p+mEwTKDQoS{@(DOrnUs|@q1XQ?xK zi$C-!St|>W4k0N!qut1yVzs@=Dh&mL7~c?vb`awq!te*l6h{1GJG$rXa67JNk7w~T zk*QO5yW8ImAKKvuclh9JhvOjNrtJ_W>cI+$iDJS~H&&P`z&fI)WRe>kJqF^=aBvc4 zr3F*zEQzOP6g8I$jwsr7UCjq!QkjgBODXTg^XaA}qeP~HFcmo91Nj-F*Pc~c&B?H_ z>0Ih<@U8Vh)z=~N;@{3Ixc{0x(LxXduYGH{>z)$=eBUUDyWwStm;`W(yTud!7}OQU zu}=h$MU+V!tKD{AJMmy2SalGujeNv!qfXLgqi)h;qh9PKefNEkx`zrrmrY<8;fp}l z*WqMWYRlgNLs_rpkizb+u>4^3C7H`5>+Z5yT!4Py1oPK9;JRk2IUHyGXW|ag)Sj%Z zZ;W)3q!W4|^nYjLWEHNYr64uJ$*bL24@n~zdMR}R?s#I|HjsLA1so`HcM zZ6H0{R(7|vT`gNLHXX+fs54IEz&dK&lz2Wb+xvW8`lh7|yZi0^=?_W$88s2l;92fJ zy{{dv9qi7WdH;j`4RC+aQFaDKJ-`WWJ=!399AMH23YsCm^ta6B+ZKsTHCNZNr2|Q- zX5}O%xNUK=xfKD&N37l600wY{>20_nn1st=0OuU@fnj52F)s*q2G9;TzA-=cHqwCl zP}L2pD?^{lk+E{&~HeD0n;`zW(@Ke!lStLJu$akMhiZEZ{PH&M&W`Q6zQNv)>Sg V=Ujc^AK_1khBug>6I_S+e*i*Nv3LLg diff --git a/challenge_api/db/__pycache__/config.cpython-312.pyc b/challenge_api/db/__pycache__/config.cpython-312.pyc deleted file mode 100644 index 045da0e1512542aad05bf73fd9a659ae590cc7d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3633 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 bcl0Wp#pL+6>1~FA=Oqw!GdZ492HozzMlCC& diff --git a/challenge_api/db/__pycache__/db_manager.cpython-312.pyc b/challenge_api/db/__pycache__/db_manager.cpython-312.pyc deleted file mode 100644 index 27118f95fd6b0e7ff68c495d8382be8b49a31618..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 249 zcmX@j%ge<81j(g_8R0UCi5+Uw4B7^?D*osoJ5FV5i?NTEvA&Dl?E8+y2%Lv59fIXa>yY+1BYC4iT*)qFUi+41AsKOW!Y4rf%)cvneP6&8;ZK!mVw`& ze)YlSzxEB|-_%(BS!g`O75@W*8O-#Jz;KMfbWD@l6~7WxovM7U`c_bLYVz6g>p{b5 z;JL=?Um1@5slgh|{>orBuU$32tU4_qP1XX^(nuRfn{|M6G|~amWj!E0jdX!*u|AN# zMtVTD*#O8uBU?ar*bvB2BYn1eW{gKSID;{+WHxgXo_GPznj{(b6L)N8)+b>a;9g-D zkgd~5aBm*|kuPU7Z}mr7Il&bVAQIz+`O-)#%w(14=AMB!RVO!1^k9|RWt56|Too0x zvwE7$!srW8MdNEjkm{v)b#naYn2Y#$?gh-9FL@Nsk3Z(|brLO(XP2(;^Kj1JcNbpS zno^5h90w8hA0I82S$~!a!NX)qhnil;NgeCQ^x}ajI=F3|g9bdr6+bZzgBy;?45xyt z%B`y!t~#9wSM|B&G!*v2X4XrE)}B|LW`WjM&{lz}v5n^yr@e;RShtD!om`WU6w+}=P28_3QEGE@l8M<>Zw-z~<`nQ6x(jqLG#jl5CJEKhrHW7NS0 za>&~7l}=WZwm&}|SF`p)c!4XH(`&xW>@f9x_rm8{*G-ei3ul4{ucox*WapPoC@>EqP(bx(IX zP3WbnDQfa1Cy}29Vb=IVnD99lS=D1tv-(*gyl|e?c;I?|W~Xprnw8lLH;%uE1k3Ep zDBufs&a=iWO2b4fGs|}|rxS^}8+y;&#EY=*@1w})Zs^!gxbPl%oMrV(k1-x*^$Rx) zIdfVIE&?x(X}FzmH<-|yy4vN>5;sZX%>Ed}K8N>9ch8zo7y$OWB&#LSH4mMpywM1! zlW$UvFQ}+U*~Y0w7lQEkJqX*d%wc6@j?5u0^*1op-$>qvkRwyhdOgwpi={S+_5wtc z^r#TOBma30G$g%upzTsdhny+Q7+GbIPrwIpx+-O}VQc=Bn8_ zu}Y+0yoGj~zdNT}4?9#dbGq&Dp+JxPM2+=m23 zRBz{2RT@pVeK?W6L5V4JJM!->|2^q{1i{8Vwc)vuABcLxoLCle2cl>q)Bg)ctE1C8QAJ2 z2z&M{Lc9r+SQ+dV%Q(%2?=H^}TG*{d`We=x4a!7KMjBM^fxtvnX&mZ*y>b5j`mA_& zaA~Wu7tZApAAI+}MlfbRQt?iYl*=9{iI2%#3h>*M|XIGsq9sOihDz%}U=vhi5+~#SoqdT8u1q_n603GXHyIwCZOVXOkrCY zXW!hH#t_uEq52{<3H07g^+@s-)M#sVf|pQjm7%~W6@Jy`9bjaFywo7m^rRV7*^?`M zhtyjfRF!s0^E%HEi8E^wg&ZXWRlx$Et}5v~8>~%^$r9f||CzOjL?ZQ_)ni#yepAWP zLhH7WHoC1w_${_Y3SLpZV#A>+@;-Ezze4%{1a>#&mwpe`=G9?~K4Q?*P>euNo_u5v zv843-7cE9fMPa#ddb+KX%-R^8XizKCEUGl~Bs1VU8X0N^wa?NEUAN~km}13>C1p5P zrJkztcE1x_#79_86%exCljR=;TNxKKE1@84FGTdjW1QW&ygR`P0j`+xg5u4{RJB7N z?2*+2jhT$M<+r{3rq3*ne<29^_ki& zE_tw21rSx8PVF=Ls+ZpqvVas+sK`($Nh_U4(Ccpt$eI12`nLOsGFeRA=o=lA{_M)Y Q8te`FgU;Y!&>pq^521D6m;e9( diff --git a/challenge_api/db/__pycache__/models.cpython-312.pyc b/challenge_api/db/__pycache__/models.cpython-312.pyc deleted file mode 100644 index 167d148e3b3ed5c56134abde093021e95433b434..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10676 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 diff --git a/challenge_api/db/__pycache__/repository.cpython-310.pyc b/challenge_api/db/__pycache__/repository.cpython-310.pyc deleted file mode 100644 index fdc8e49d54e4718e787636ae9b4cf0893dc4f0cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5301 zcmb_g-)|d99pBlX{+%>!(_XKTEr&`CT*7g1hzd|c8lg(9E9#V!{IYU96W8h5PG{F` zY;BIx1PSi0;vVimlomM>Dyr0no;VQC@W4O7(@H$?bi{e#u?UXuch(+z6Q|cox7Loo zGxMGA{P=$6GvC=}Hk(r5v7i36`te~!`6p&Z9}P0+;PnDXs6vUWl%ju9B9K>IwXT&k zf@PYk*Nu`PWZgCER>=~w;U?$y^nC_hkWf+n{Vn%q=N z6Oc>MH008do1~*ey!OkyC6>#e|T};tz2XE_9Ev^4n?}p)_wRt ze4*hp4r)clZ!$ipR_StH4O0u(%C5^ARpy1cs~+RSdrFxukTu;`R!N{ZFwvs&h!pZFH=!o9Jmz_@^00oo$J`a3 zhSEn0&%)o+i*wkaH&?CIX}Q{F%|>_3#iD zNEa8dMqg`kKQukR?6d+^cD!>yRISTy@gt>L4l?Crw&nAW~Em9}Mz-hSJ-?E6jc z{^IfO+kcLkk32KR=H^d!H}6YTDO?dA+%xT~qki{bGGf7Y-n&1XmNbwz_#yBzpTY#l zP~k@~ISNU~7W83PI4k?LM%9*xwrdTWE}yj@kub@Sft)e2BKEX4nd{F0MQuY=SCU#qtATfbq91}y?vmsF@R5JXT|H)V> zVv-z5sZ%6zTm5fmCcY*^VwjCA#pnTti?7%Np9cY{VO0$Xpr3RB>lpwn z*7`dr$t4|&S%C?=xBX=I-n&v$3ge+0+q)7I<90+GankJbp<};6X&_GoCJK37xHdFy zv>0!P$(5Q5$BeVw4vm!>_x!vTc@qIt%HbYyr2F>z?nIUO(9(%(ImleELURu>G?mf` z{yMK8=(mHd@RML6Of}doC)!RGbmrhe@JWKZ1DVcgU+IEtw+U6RYkbmISGBJ08&unb zGC^=!*9<`3(16agt`%sa94OIAh>|1{+?xv31^uPk=Yan7KK*Ikw4lEXH3D7Gp9%D* z1N~VW`dhl7zeG!o(cgZLz#c{duzSDy>CW9Jz%Zl4A>FkfM$!xRnfOY^*>J#J(h{=g zZ~-OuhP?bDe6-wby1+ssL$`sz|MiDILfPY;Ki!tShRLPH;*xXe^7%!GXn2b~eWSRz zWEU5U#S51U_R^L4LUDd!3FQmL%NOT`baC-Vix=%Hi}UC0h0B*NU07OLJipXA6>(Q< zjqc^H2oq;*ehi#n$Y(>f2}dEoi8XGDBR?WAi>Z6ud>kMY=txLn&3x% z0%`=e@t08&v}Qyb0U^j=!Il%E!N_*_99CiUFv4e9F;3RU>vy5jL*r^XMtp^Aggehx{gPt+WH$5$dw&}p!d%2c7OTvJ~7z4zy6^2U>G4u z{m%{s(&V$8JD^89Hagifw63%o4TzB7xWvE-t_~u$5hSo@r>AP3!z1lgP!&;rD%E-A zz||e<$M!O^622lv`927P7$s04N>N3Wq6M0l6H$t`s#EQ{%1;I$`LL%~%}qk}```*V zmY}SLw@tJ44=^OBHR6>1;q%W4K-L2{u)RecVrZ!V^qt(fVDaj7{|zq&tmd5QAleQpTCazA%2B~AvaG# zf`jqUOhQQ!RiFF>;&1W7QT566%=F~+@u{QJ8KGMNwb+9<&U5g3$cclm3k*&n_#&V( zgPCx*2gFi+OW!0ViyDZ|@bJRmNf zPXiZe0DgqM+WHG9NQjKUet=WBj1I3(FA&6!g5w$6z_w-QQW#4|d$Aqg7Z{~mFcXf* z4K)BxX!teXWdL!pO5>0@h4>nd6LIqnH208OG7x@rPLEmi+5L?QaH0DKcK%IF-hd>` zAmD42I`gkJ=?DP7g&G*+jK$VjsCXM*4>6KZRC1DVEXyAXEw@>%0?qO-K|VD7_8Ml; z)uo(w!!1Xj#AexgMMPz>La2`-xl{cwF{sAGH#Kh-KF~PBtO(@76GOeh7aONuZj`HV z;l-hqPVQHjrOUIh6PfEpju6*j+$>@S#D%z6^waIaT@b G6!||^(#p#K diff --git a/challenge_api/extensions/__init__.py b/challenge_api/extensions/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/challenge_api/extensions/__pycache__/__init__.cpython-310.pyc b/challenge_api/extensions/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 9a7355f2063c9f397d9562da3c6024f3b31381bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170 zcmd1j<>g`kf`=s<86f&Gh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vPKeRZts8~Ne zGdCqMy)rdFFJ0dywK%&Zzd%1ZBQYl@H7`9?H?bfS!HWm+QY%VQ^NKU`^NRK3<1_Oz cOXB183My}L*yQG?l;)(`fvhQJ0un3?0F}Hdg#Z8m diff --git a/challenge_api/extensions/__pycache__/__init__.cpython-312.pyc b/challenge_api/extensions/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index be81a5b40728d655b13390abf78bf61a9dafb5dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 174 zcmX@j%ge<81P@CzGC=fW5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!^3@M5PAw|dPtVLv zNldRy&Cg5McS$YIF3B&@PtHip$w|#iPt{E<$VBksLA=z8lGME7%>2A!{rLFIyv&mL lc)fzkTO2mI`6;D2sdh!IK+_q4xERFv$jr#dSi}ru0RT4)EolG% diff --git a/challenge_api/extensions/kafka/__init__.py b/challenge_api/extensions/kafka/__init__.py deleted file mode 100644 index 3b5dfc2..0000000 --- a/challenge_api/extensions/kafka/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -__version__ = '1.0.0' - -__all__ = [ - 'KafkaEventConsumer', - 'KafkaConfig', -] - -from challenge_api.extensions.kafka.config import KafkaConfig -from challenge_api.extensions.kafka.consumer import KafkaEventConsumer diff --git a/challenge_api/extensions/kafka/__pycache__/__init__.cpython-310.pyc b/challenge_api/extensions/kafka/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 86eb9b275dfac114fb6296fd1ca63860e09ba6ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 390 zcmah^Jx{|h5Vf6;BHBubksUz>?64z*1WZUQ6-$;=RJ(4J#E#;$qWmNN1iu4iW#TU| zAkH2r8zxQ9PPuxpo%j{KzyN#Z_peH)Vvbs{Ji4Q+|;65++Z;vH!U;$ zCCGS9#v*121+vXglkFCFe0*7IQE_H|UVQv5_W1b3oE#uSH8}&wNX<)6jZZAd)Jv@> zNzDVPDb~vd*`k*WHWF;sEp@_b!LBI+S-FzoGmvEX6|5gx47651Ju^2YF}*T1KQCS1 zCAB!aB)>o(Zk{gCZUhhH5`BbA^uaFCkB`sH%PfhH*DI*J#bJ}1pHiBWYFESqw1W|d tixq*y2WCb_#v6R14cs5t7+585un2x&W#(pTVE@X+z-V`m!L0}sDFA9}c&7jW diff --git a/challenge_api/extensions/kafka/__pycache__/config.cpython-310.pyc b/challenge_api/extensions/kafka/__pycache__/config.cpython-310.pyc deleted file mode 100644 index 6962c1f69baed969b1e57420f0958120f9a90a4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1293 zcmZux&1=*^6wf5tZMyxiEh>VS9F1sK@MIMc6hwptzb<r`@K8lWQU8%#y_xAhpdNg)+x@6@hIyIy@;-j=BVkihB@Nc(OnGs>plLr8 zGrA05&cG3KK+bDKBPfNlqE|JdU(p=H1Nn|Mus8x@s#OuusyabcV_a98j1hyFDXN;Z zKnkRoYAdK(?E12@iHmNn;htq-&95i>!M@AeGF_K1pW0h@Kjtqw^8R|oGe<%KY)x0X zaKvFCZ4{XV)lpl+T3e6Ci4Hf^Hlk@_z)f$PKmi26FC_bvL2FW|8k@R#yl6B%5^!@nfHbZh*l9WbG6(Q_!HsRYqLZlB-;^V&k^U>*B4(Ent6L zDqjPqqFncz#H}w;7S_w>sAxcd@(`38`9lsXj^2rBC?KFHH&oK)Uex($DYG#4LqEn? z#S<#Zq*X#{xAu+{XuhxJAyDZrbys~j3r8FP($;RMg9suzF;=1CH}nkz6*rTUpxo;# zG`d@~?FP%W)Xq0MThBYClD)ky(>URwSQrSDd{<@dLxC@1XTk3ImFa5!A+_bJZ}Q$l zJAeHSW@hXD=N&%!J+Kc!j|P?lMCVl-?F;92*tki|Fss!BjWMS{`^J5DW7*(a^mfP~7$Dv1Zc;Z;)63SYZ0{N6&5fbv@sFV9RIk z@=wbH+svXa4}2=(tB%@IZGx!o}wV`#!v#~j3q@aT?JkBkGG?SaAQ_GWu7 zXF({YftV8}(qWr_%!2v5b#Qbv{;_e4N~PkAsmTi6CdD`_VccXSfwXZpiSfmoeOrOll-B>u QGwW0j$Z4e^l%qNH7YW~TmjD0& diff --git a/challenge_api/extensions/kafka/__pycache__/config.cpython-312.pyc b/challenge_api/extensions/kafka/__pycache__/config.cpython-312.pyc deleted file mode 100644 index 3d148d5e7f671665f23156a3126a2ca00485391d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1524 zcmZWp&u<$=6rT029eWceaUhjxQM!quk`-zksfefn_t0}?0Rti5*9jO91;-kbN$ zoB2NbQ93On7(=7E<=Hqwe{m-oQg_kL!eRr(&=S&-hjigitdl9AT1*v-(SAO6GpIca z&ITf=An2$N(}jYVLj0r0b!%y!uZ*B(Hck{)lHD1n-9E5NCLogd?Rk$WFQGhnf!YaZL>x|VH#A6VTK~(5P z#couDXw*DfmB16DJvB*2IZHu`TLeH0}?N;-u=dM{=SK zrKs4W2poPJPDlG~FhDW73iGoAqCEopIbG0Wy0{I5UOg(B?uJ{!dq|bs*Wj>@)IfV7 zKV8iQJD$?q+dSObl;!X_!MsZxms)J07vPB#Bq}qe!5no#>EH(5cJpUX3BG?2e11b| z{%{v$e0cr-F^q_AWd`KXtL$~O#1*5wdFfb<<~laEOC?4eObOViI4b*lZ%t|5{yOmP z2S2VgZ#__gornBZ!L}Ftx~UukBbIi>Bsjz@+Lej{XmBwTdI6y~zuZ!S-*%gOUns%G z-R3WAy;?s>G-?`zIq$1dN0FfzGbg(9$4;K1iv1Fr1Y%TyreI=R0Se_mZGQyB7xL1T zfBno+;XRYg@Zwj%fc{RQsdG;z=O0hbH?l8&o8AJEz1)~O_cWClm0L0zdG5*Z^yA^_ z)*$y$A|=x_6slrApH~OC&@HV(F!p7PE4J>!>tKHf}Ccq*6kw3bci>atWvv5 zs8ge3;NC_dTzCh{1soc#Ql^6O(4UghN}w}m8^dFb%y>IB7@ujKMv394v5~mk7Nb=M rg#+Z_aY7*epWFxcldpH$Tcj^Qg1;Oa0n-u%LHJiXE6D#KFk!*}v=gQt diff --git a/challenge_api/extensions/kafka/__pycache__/consumer.cpython-310.pyc b/challenge_api/extensions/kafka/__pycache__/consumer.cpython-310.pyc deleted file mode 100644 index 9e9c201f7b7955f9122525e6ac3803c07dd05054..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3655 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 diff --git a/challenge_api/extensions/kafka/__pycache__/consumer.cpython-312.pyc b/challenge_api/extensions/kafka/__pycache__/consumer.cpython-312.pyc deleted file mode 100644 index 16deb195d76527f2f97d17de99174201f6696f4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5001 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 P(BE 'StatusMessage': - """ - JSON 데이터로부터 StatusMessage 객체를 생성하는 클래스 메서드 - - Args: - data: JSON 형식의 딕셔너리 데이터 - Returns: - StatusMessage 인스턴스 - """ - return cls( - userId=data['userId'], - problemId=data['problemId'], - newStatus=data['newStatus'], - timestamp=data['timestamp'], - endpoint=data.get('endpoint') # endpoint가 없을 수 있으므로 get 사용 - ) - - def __str__(self) -> str: - """객체를 문자열로 표현""" - return f"StatusMessage(userId={self.userId}, problemId={self.problemId}, newStatus={self.newStatus}, timestamp={self.timestamp}, endpoint={self.endpoint})" - -class KafkaEventConsumer: - """Kafka 이벤트 소비자 클래스""" - def __init__(self, config): - """ - Kafka 소비자 초기화 - - Args: - config (KafkaConfig): 설정 객체 - """ - self.config = config - self._consumer = None # 실제 Kafka 소비자 인스턴스 - - @property - def consumer(self): - """ - Kafka 소비자 인스턴스를 생성하고 반환하는 프로퍼티 - 지연 초기화(lazy initialization) 패턴 사용 - """ - if self._consumer is None: - try: - self._consumer = KafkaConsumer( - self.config.topic, - # 바이트 문자열을 JSON으로 자동 변환하는 deserializer 설정 - value_deserializer=lambda x: json.loads(x.decode('utf-8')), - **self.config.consumer_config - ) - except Exception as e: - # logger.error(f"Failed to create consumer: {e}") - raise QueueProcessingError(error_msg=f"Failed to create consumer: {e}") from e - return self._consumer - - def consume_events(self, callback): - """ - 이벤트를 소비하고 콜백 함수로 처리 - - Args: - callback: 각 메시지를 처리할 콜백 함수 - """ - try: - for message in self.consumer: - try: - # 메시지 파싱 - status_msg = StatusMessage.from_json(message.value) - callback(status_msg) - - except json.JSONDecodeError as e: - # JSON 디코딩 오류 처리 - # logger.error(f"Error decoding message: {e}") - continue - except KeyError as e: - # 필수 필드 누락 오류 처리 - # logger.error(f"Missing required field in message: {e}") - continue - except Exception as e: - # 기타 예외 처리 - # logger.error(f"Error processing message: {e}") - continue - - except Exception as e: - raise QueueProcessingError(error_msg=f"Kafka Error: {str(e)}") from e - - def close(self): - """Kafka 소비자 연결 종료""" - if self._consumer: - self._consumer.close() - self._consumer = None \ No newline at end of file diff --git a/challenge_api/extensions/kafka/handler.py b/challenge_api/extensions/kafka/handler.py deleted file mode 100644 index 170fb82..0000000 --- a/challenge_api/extensions/kafka/handler.py +++ /dev/null @@ -1,123 +0,0 @@ -import logging -from typing import Any, Dict -from challenge_api.exceptions.kafka_exceptions import QueueProcessingError -from challenge_api.db.repository import UserChallengesRepository, UserChallengeStatusRepository -from challenge_api.objects.challenge import ChallengeInfo -from sqlalchemy.exc import SQLAlchemyError - -logger = logging.getLogger(__name__) - -class MessageHandler: - VALID_STATUSES = {'Pending', 'Running', 'Deleted', 'Error'} - VALID_STATUS_TRANSITIONS = { - 'Pending': {'Running', 'Error', 'Deleted'}, - 'Running': {'Error', 'Deleted'}, - 'Error': {'Running', 'Deleted'}, - 'Deleted': set() # Deleted is a terminal state - } - - @staticmethod - def validate_message(message: Dict[str, Any]) -> tuple[str, str, str, str, str]: - """ - Kafka 메세지의 필수 필드를 검증하고 반환 - - Args: - message (Dict[str, Any]): Kafka 메세지 - - Returns: - tuple[str, str, str, str, str]: 사용자 이름, 챌린지 ID, 새로운 상태, 타임스탬프, 엔드포인트 - """ - try: - user_id = message.userId - problem_id = message.problemId - new_status = message.newStatus - endpoint = message.endpoint - timestamp = message.timestamp - except AttributeError: - user_id = message['userId'] - problem_id = message['problemId'] - new_status = message['newStatus'] - timestamp = message['timestamp'] - endpoint = message.get('endpoint') # endpoint가 없을 수 있으므로 get 사용 - - if not all([user_id, problem_id, new_status, timestamp]): - raise QueueProcessingError(error_msg=f"Kafka Error : Missing required fields in message: {message}") - - if new_status not in MessageHandler.VALID_STATUSES: - raise QueueProcessingError(error_msg=f"Kafka Error : Invalid status in message: {new_status}") - - return user_id, problem_id, new_status, timestamp, endpoint - - @staticmethod - def validate_status_transition(current_status: str, new_status: str) -> bool: - """ - 상태 전이가 유효한지 검증 - - Args: - current_status (str): 현재 상태 - new_status (str): 새로운 상태 - - Returns: - bool: 유효한 상태 전이인지 여부 - """ - if not current_status: - return True # 첫 상태는 항상 유효 - return new_status in MessageHandler.VALID_STATUS_TRANSITIONS.get(current_status, set()) - - @staticmethod - def handle_message(message: Dict[str, Any]): - """ - Consume한 Kafka message 내용을 DB에 반영 - - Args: - message: Kafka 메세지 - """ - try: - user_id, challenge_id, new_status, _, endpoint = MessageHandler.validate_message(message) - challenge_info = ChallengeInfo(challenge_id=int(challenge_id), user_id=int(user_id)) - challenge_name = challenge_info.name - - # 상태 정보 업데이트 - userchallenge_repo = UserChallengesRepository() - status_repo = UserChallengeStatusRepository() - - if not userchallenge_repo.is_exist(challenge_info): - logger.warning(f"Challenge {challenge_name} does not exist") - return - - userchallenge = userchallenge_repo.get_by_user_challenge_name(challenge_name) - if not userchallenge: - logger.warning(f"Challenge {challenge_name} exists but could not be retrieved") - return - - recent_status = status_repo.get_recent_status(userchallenge.idx) - if not recent_status: - logger.warning(f"No status found for challenge {challenge_name}, creating new status") - recent_status = status_repo.create(userchallenge_idx=userchallenge.idx, port=0) - - # 상태 전이 검증 - if not MessageHandler.validate_status_transition(recent_status.status, new_status): - logger.warning(f"Invalid status transition from {recent_status.status} to {new_status}") - return - - try: - port = int(endpoint) if endpoint else 0 - except (ValueError, TypeError): - logger.warning(f"Invalid endpoint value: {endpoint}, using 0 as default") - port = 0 - - try: - if new_status == 'Running' and endpoint: - # Running 상태이고 endpoint가 있으면 포트 업데이트 - status_repo.update_port(recent_status.idx, port) - - status_repo.update_status(recent_status.idx, new_status) - logger.info(f"Updated status for challenge {challenge_name} to {new_status} with endpoint {endpoint}") - except SQLAlchemyError as e: - logger.error(f"Database error while updating status: {str(e)}") - raise QueueProcessingError(error_msg=f"Database error: {str(e)}") - - except ValueError as e: - raise QueueProcessingError(error_msg=f"Invalid message format {str(e)}") from e - except Exception as e: - raise QueueProcessingError(error_msg=f"Kafka Error: {str(e)}") from e diff --git a/challenge_api/extensions_manager.py b/challenge_api/extensions_manager.py deleted file mode 100644 index 64babdb..0000000 --- a/challenge_api/extensions_manager.py +++ /dev/null @@ -1,87 +0,0 @@ -import logging -import sys -from threading import Lock, Thread, Event -from typing import Optional, Callable -from flask import Flask -from challenge_api.extensions.kafka import KafkaConfig, KafkaEventConsumer - -class FlaskKafkaConsumer: - """Flask 애플리케이션에서 Kafka 메시지 소비를 관리하는 클래스""" - def __init__(self): - self.consumer: Optional[KafkaEventConsumer] = None - self._consumer_thread: Optional[Thread] = None - self._running = Event() - self._lock = Lock() - self.app: Optional[Flask] = None - - def init_app(self, app: Flask) -> None: - """Flask 애플리케이션 초기화""" - with self._lock: - self.app = app - config = KafkaConfig( - bootstrap_servers=[app.config['KAFKA_BOOTSTRAP_SERVERS']], - topic=app.config['KAFKA_TOPIC'], - group_id=app.config['KAFKA_GROUP_ID'] - ) - self.consumer = KafkaEventConsumer(config) - - # teardown_appcontext 핸들러 등록 - app.teardown_appcontext(self.cleanup) - - def cleanup(self, exception=None): - """애플리케이션 컨텍스트 종료 시 정리""" - self.stop_consuming() - - def start_consuming(self, message_handler: Callable) -> None: - """Thread-safe하게 메시지 소비 시작""" - with self._lock: - if self._consumer_thread is not None: - print("Consumer thread already running", file=sys.stderr) - return - - self._running.set() - self._consumer_thread = Thread( - target=self._consume_messages, - args=(message_handler,), - daemon=True - ) - self._consumer_thread.start() - print("Kafka consumer thread started", file=sys.stderr) - - def stop_consuming(self) -> None: - """Thread-safe하게 메시지 소비 중지""" - with self._lock: - if not self._running.is_set(): - print("Consumer not running", file=sys.stderr) - return - - self._running.clear() - if self.consumer: - self.consumer.close() - - if self._consumer_thread: - self._consumer_thread.join(timeout=5.0) - if self._consumer_thread.is_alive(): - print("Consumer thread did not stop gracefully", file=sys.stderr) - self._consumer_thread = None - print("Kafka consumer stopped", file=sys.stderr) - - def _consume_messages(self, message_handler: Callable) -> None: - """Thread-safe한 메시지 소비 루프""" - with self.app.app_context(): - try: - while self._running.is_set(): - try: - self.consumer.consume_events(message_handler) - except Exception as e: - print(f"Error consuming messages: {e}", file=sys.stderr) - if self._running.is_set(): - print("Attempting to reconnect...", file=sys.stderr) - self._running.wait(timeout=5.0) - except Exception as e: - print(f"[ERROR] Fatal error in consumer thread: {e}", file=sys.stderr) - finally: - print("Consumer thread ending", file=sys.stderr) - -# 전역 인스턴스 생성 -kafka_consumer = FlaskKafkaConsumer() \ No newline at end of file diff --git a/challenge_api/factory.py b/challenge_api/factory.py index ebc3cf4..b4806ae 100644 --- a/challenge_api/factory.py +++ b/challenge_api/factory.py @@ -7,35 +7,21 @@ from challenge_api.exceptions.http import BaseHttpException from challenge_api.api.challenge_api import challenge_bp from challenge_api.config import Config -from challenge_api.extensions.kafka.handler import MessageHandler -from challenge_api.extensions_manager import kafka_consumer from challenge_api.db.db_manager import db from challenge_api.container import Container -def start_kafka_consumer(app): - """Start Kafka consumer in a separate thread""" - with app.app_context(): - kafka_consumer.start_consuming(MessageHandler.handle_message) - class FlaskApp: def __init__(self, config_class: Type[Config] = Config): self.app = Flask(__name__) self.app.config.from_object(config_class) - # 초기 설정 - self._init_kafka() self._init_db() self._register_error_handlers() self._setup_blueprints() self._inject() - def _init_kafka(self): - """Extensions 초기화""" - # Kafka 초기화 - kafka_consumer.init_app(self.app) - kafka_consumer.start_consuming(MessageHandler.handle_message) def _init_db(self): # DB 초기화 diff --git a/challenge_api/requirements.txt b/challenge_api/requirements.txt index 6588925..d75bb7d 100644 --- a/challenge_api/requirements.txt +++ b/challenge_api/requirements.txt @@ -4,7 +4,6 @@ SQLAlchemy==2.0.20 pymysql>=1.1.1 # mariadb>=1.0.11 kubernetes==26.1.0 -kafka-python==2.0.2 prometheus-client==0.19.0 python-logging-loki==0.3.1 flask-prometheus-metrics==1.0.0