From b18412ecb21214e487afb9b1b357aa48b72656b8 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 Dec 2025 16:23:03 +0900 Subject: [PATCH] Update 2025-12-19 16:23:03 --- README.md | 31 +- .../telegram_bot_service.cpython-314.pyc | Bin 7590 -> 7980 bytes app.py | 10 +- backend/instance/site.db | Bin 45056 -> 45056 bytes .../routes/__pycache__/admin.cpython-314.pyc | Bin 13263 -> 15201 bytes .../routes/__pycache__/auth.cpython-314.pyc | Bin 19065 -> 19437 bytes .../routes/__pycache__/main.cpython-314.pyc | Bin 14036 -> 15178 bytes .../__pycache__/scp_routes.cpython-314.pyc | Bin 7197 -> 8313 bytes .../__pycache__/utilities.cpython-314.pyc | Bin 15438 -> 17967 bytes backend/routes/admin.py | 52 + backend/routes/auth.py | 7 + backend/routes/main.py | 44 +- backend/routes/scp_routes.py | 46 +- backend/routes/utilities.py | 84 +- .../__pycache__/logger.cpython-314.pyc | Bin 3580 -> 4482 bytes backend/services/logger.py | 13 + backend/static/js/index.js | 104 + backend/templates/admin.html | 208 +- backend/templates/admin_logs.html | 245 ++ backend/templates/base.html | 43 +- backend/templates/edit_xml.html | 174 +- backend/templates/index.html | 292 +- backend/templates/manage_xml.html | 616 ++- backend/templates/scp_diff.html | 195 +- data/logs/2025-11-29.log | 756 ++++ data/logs/2025-11-30.log | 96 + data/logs/2025-12-18.log | 3824 +++++++++++++++++ data/logs/app.log | 904 +--- data/temp_ip/ip_0.txt | 2 +- telegram_bot_service.py | 26 +- 30 files changed, 6607 insertions(+), 1165 deletions(-) create mode 100644 backend/templates/admin_logs.html create mode 100644 data/logs/2025-11-29.log create mode 100644 data/logs/2025-11-30.log create mode 100644 data/logs/2025-12-18.log diff --git a/README.md b/README.md index d6944bd..77808cc 100644 --- a/README.md +++ b/README.md @@ -903,33 +903,4 @@ git reset --hard HEAD~1 ```python SESSION_COOKIE_SECURE = False # HTTP ν™˜κ²½ REMEMBER_COOKIE_SECURE = False - ``` - -## πŸ“„ λΌμ΄μ„ μŠ€ - -이 ν”„λ‘œμ νŠΈλŠ” MIT λΌμ΄μ„ μŠ€ ν•˜μ— λ°°ν¬λ©λ‹ˆλ‹€. - -## πŸ“§ μ—°λ½μ²˜ 및 지원 - -- **이슈 리포트**: GitHub Issuesλ₯Ό 톡해 버그 리포트 및 κΈ°λŠ₯ μš”μ²­ -- **문의**: ν”„λ‘œμ νŠΈ κ΄€λ ¨ λ¬Έμ˜μ‚¬ν•­μ€ 이슈λ₯Ό λ“±λ‘ν•΄μ£Όμ„Έμš” - -## πŸ™ κΈ°μ—¬ - -κΈ°μ—¬λ₯Ό ν™˜μ˜ν•©λ‹ˆλ‹€! Pull Requestλ₯Ό λ³΄λ‚΄μ£Όμ„Έμš”. - -1. Fork the Project -2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) -3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) -4. Push to the Branch (`git push origin feature/AmazingFeature`) -5. Open a Pull Request - -## πŸ“ λ³€κ²½ 둜그 - -### v1.0.0 (2025-11-29) -- 초기 릴리슀 -- iDRAC Redfish API 톡합 -- Telegram 봇 μ•Œλ¦Ό κΈ°λŠ₯ -- DRM μΉ΄νƒˆλ‘œκ·Έ 동기화 -- μ‚¬μš©μž 승인 μ›Œν¬ν”Œλ‘œμš° -- μ‹€μ‹œκ°„ 파일 λͺ¨λ‹ˆν„°λ§ \ No newline at end of file + ``` \ No newline at end of file diff --git a/__pycache__/telegram_bot_service.cpython-314.pyc b/__pycache__/telegram_bot_service.cpython-314.pyc index 4ffcbf9fa9699ec7a01a940120973b4e67266915..0997c23c4598590524ecadf1e2e70c95e7b10179 100644 GIT binary patch delta 1224 zcmZWp&2Jk;6rc6lyW@E4;_r<=;vtrkSaBj1f@18_#vq*LfH5I*sbu1O>?W+(c$c+Z zM5q)?aBx(rHX5W{IPf7MkXqCT331`VAzV1vX~1TcLy-6bf{0{^UYK=kio%oTy*KlF z@3-&0SDy3pNe9s;jY zMov#yVrc-(e|?7=p2$=4luD6hB8$$09F6yUe(dlnv^j6aA9%CDf}@|AH&6A57OBL? z00?9FjyK|9^5#VlH_GSpgH zbA!0)bDOS5aLVTo=Bd#hu+9Sz1pkQ(;62(Efc=`oV4jMALhxUnA-wDh`HcCTlRPy= z;SGx)7iq|-%ylL=Y*39_M@V3v$wkk_lWn121t^SR8y}(={2A||>G=Vi<|o3Rr|1xn zCgsg?wyHKZ!CES1(#S=4SytK#Hl`jx6&kyKgyf5IeswIsiXDsPEs zP14F0q?AN77A?onyOJ!U6%kd-qA!2tQNv&OQ(I6Y7YTGL zX{p%d)uG`U1Uh4QlAxQ3E$cMVTEt+vOjH>XHGlbt`ko&9*sb#|8$TZd%c)9dH-xmD|tX zd*`DUjyUJt>~^*hSZpi_4RP(4rS;a5++30mxs4MBTy)*LbZ5OzAG>|Kb31c&Yun}B zP3$D5!uW5{e#!vX zP|F!Ta7G_FM_bN`rgP#e{Bt0=l{kj377RCG_~AkWhJS_0&dU_xX<5 zdVktG>!N>hS%@F=>TIznD67Sy&f?p_2T^0hUxV%|)1A2vFK;M9RTi&kHwa|B7v&lJ MW@rYIH>n%;4-JDL(f|Me delta 798 zcmYk4&1(}u7{+HZNi#{e(d=f|Z9X>J#C{v08c`BVn_}#(Y(h*c1WVdfS+H%~v|13f z2SGs!mAQKGBozc(dhsH((9(;Cq%CeK9z6I5L@Y+^L1!D6_8oZV_v}3HJ2T6?PrQx0 zdR;9}K;!bS4)u|+>Uze9Bq|p`9faT@{BfLLU#T6>Z?X+dR(Ky|$Y(xGj`&3cbCA3g zhJ_rw11@-tZjC5eazJ57!Nx&1Nn1UcoJC>H#gmzS^_(b^qNqVZkb%ZH(Rso2DB`On z4~dHo^7|Yok6M&do12~}Y>*Nle;wYY5*k5@L{<@Jw|CH$93lnL!{o!JKYyNl5(5Y+ zoz WH9+DM%otRC;(I_gM621Yyw5feo(Sz&0F>weR~~QYt+(k1wnuwzhv#VW?=X~ zsZa%tZr#qc=dB3xp5Ym}Ffpq>oRr6BX3O&3nYrmAse3y~)OQUEWXjhS(n7T3fpxkt zHF;oBi<;Gt_VGFOesNOMVpNe0U%bhti3SG!!c`lE4w;6=QPPIY^q4}~_$v#b#sKWz zv5Q|_Vx@Ip*Ojc`BzfZR&-d*ym-ZR9-VB^=Rfk+V3(jy$eT(;ApndGtu~@~TjssO3 zs7R^x^7j13d?lUL(<9aN$Q~Z8bF}PG1e~O^?K^DyF59WIkt!Qmp7@}?Bew&wr-Set z=5!pa;^0cQf`eahtRA6NxibIv~^^Xs9oZP|5qZ%>{f66`d zI?KtH8wuM+e~jkM9ya9$n~CO>%dzEdzKPJ5-=9jLt%Qwow{Tz|8!Hk`=>Px# diff --git a/app.py b/app.py index 08d1917..f8c0f19 100644 --- a/app.py +++ b/app.py @@ -151,8 +151,12 @@ def start_telegram_bot_polling() -> None: # ───────────────────────────────────────────────────────────── # ν…”λ ˆκ·Έλž¨ 봇 폴링 μžλ™ μ‹œμž‘ # Flask 앱이 μ΄ˆκΈ°ν™”λ˜λ©΄ μžλ™μœΌλ‘œ 봇 폴링 μ‹œμž‘ +# 주의: Flask λ¦¬λ‘œλ”(Debug λͺ¨λ“œ) μ‚¬μš© μ‹œ 메인/μ›Œμ»€ ν”„λ‘œμ„ΈμŠ€ 쀑볡 μ‹€ν–‰ λ°©μ§€ # ───────────────────────────────────────────────────────────── -start_telegram_bot_polling() +# 1. λ¦¬λ‘œλ”μ˜ μ›Œμ»€ ν”„λ‘œμ„ΈμŠ€μΈ 경우 (WERKZEUG_RUN_MAIN = "true") +# 2. λ˜λŠ” 디버그 λͺ¨λ“œκ°€ κΊΌμ§„ 경우 (Production) +if os.environ.get("WERKZEUG_RUN_MAIN") == "true" or not app.config.get("DEBUG"): + start_telegram_bot_polling() # ───────────────────────────────────────────────────────────── @@ -162,5 +166,9 @@ if __name__ == "__main__": host = os.getenv("FLASK_HOST", "0.0.0.0") port = int(os.getenv("FLASK_PORT", 5000)) debug = os.getenv("FLASK_DEBUG", "true").lower() == "true" + + # python app.py둜 직접 μ‹€ν–‰ μ‹œ(use_reloader=False)μ—λŠ” μœ„ μ‘°κ±΄λ¬Έμ—μ„œ μ‹€ν–‰λ˜μ§€ μ•Šμ„ 수 μžˆμœΌλ―€λ‘œ + # μ—¬κΈ°μ„œ λͺ…μ‹œμ μœΌλ‘œ μ‹€ν–‰ (쀑볡 μ‹€ν–‰ λ°©μ§€ ν”Œλž˜κ·Έκ°€ μžˆμ–΄ μ•ˆμ „ν•¨) + start_telegram_bot_polling() socketio.run(app, host=host, port=port, debug=debug, allow_unsafe_werkzeug=True, use_reloader=False) \ No newline at end of file diff --git a/backend/instance/site.db b/backend/instance/site.db index ca337bfd52c6927669caa36a738c99c7fe423a11..923f48c22138c2c36eb16361b99940b933702a8e 100644 GIT binary patch delta 376 zcmZp8z|`=7X@WFk&_o$$)*uGGE|HBXEBKlCK5Q0LSi&pOsNbl|%*7zD-WbTi!IAv7 zwdeJeDQ`CHoy;b$Tc73UYUZUKlI*VSXJKw?SQV0+Q&JSDVqlza=3JH^?2?~rsP9tc zXHgR3Zd_54ACd3qnO^DR=#o+q6qFPaW>#X6o~oN+?o??I98~1&Z<$dX92r{a<5Xbm zot7RMnVRP25+0mVkr-)FnrG6;$q00ZX=5NKCnv+Joei%xOfHk_VnaB`iE`&mE|s6b zczv^=!%2Q#US@elgm<6lYcd`L2FXT#E@5UtW_8Ar)Z&uOC+(REOo{XZ3o`>FGl*bf IW@g|70J~v)U;qFB delta 362 zcmZp8z|`=7X@WGP|3n#QR(}S)vhIy3EBKjs?`{@USiEv^=nocHVRcWP$sfBLg z-o+(pk*WEH!G$S-Dt?s~CK2g*5rz4w0paciE+tv%c_xLX&Lz37CD|?oWtncS!By@7 zrm60ZlT+m8u)XP8^0sToH;QLb)zRh|JU-svWjOXX)UKHV(naFU;wmsy?> z?(WG?^fejx0|Q|rKbJ7GHY3E#n@`#^6_{p~<)@_NI0v`|2Bl>rg~wM$g$869=Tzk< dMp!ykRzww=6?!EX1+XwPFfxM(CT3;^P5>POfb9SP diff --git a/backend/routes/__pycache__/admin.cpython-314.pyc b/backend/routes/__pycache__/admin.cpython-314.pyc index aaed1bc09dfe4cd21ac5949a89a4fbe63fec6af2..94b58a7669d2251a29eccaf67bec3d6567eb6db5 100644 GIT binary patch delta 1865 zcmaJ>OKclO7@pm=*Y8)Hr(?%<(>AdiJCC}=NlVkFagru!8kTAg#KstV?QG=O+p*IG zt6(Dzv`DCSEs-K58V-m^aB$TF6(?S$+;DIqWdXH_9yq3?Qc)`;X5u)2gxK}`Gyk{W z{QvjQ!{5YyIj23K)hGdajK&c@w!fflCO)~=@eEbelT8i#8J(M8^!+3z01z+)+L(^w z5wf+z!I1611_CA;L}UP%ffX=hy2S!~#8iO?hbar-2}kGkcd5%=Co-z;P-KBmbjOpi zRJaqQQv^c@5|)@T79Z3Zi+}D_(1Nt)>Szy9ohZ;XtT?$( z>5{pDAZI1JDL?+7lnpp#QEyeeuuFdn(_4YRsiu&XFmgu0C>d41q>3FTg2W=ZNCkms zha@Oi(hgV!qpoQel&n$@Mk4?SDpoqvf-5j+Sk>-#HB0ZJf6r30vj0TYGp)sII+ICI zv+A1E7}^6fn0yuhRz73Fm1NbmHL-%pC;$r3hIRm3u6`Z0O9V=0TKGIDHa({aj z2Y}JDa>h_QI{2_00K1vchL{0QT7d;c!0(i$dSq>L{O zFZT}59&-f)=V$%P-X52Kd0;dfaLo3&h62u6o2whyW3E>M8Kr*~fdhvF2Np6m#O=Y{ z9xrOcsGUH~hk)Ku|8oCmKqO#Bx|oi-yrK+pLOc}a9Hc1c65(WoPsBu;gD?qG{BLMI zA%mo5ntd3tK`s>Gvx%4u>9a-oI5%j^G)?V{ZHZ*s7ELZDBDPQ(eb8-RuOPl&?z!|r z9F;4-hC;pBqA zXX6}FxXh>0DN&LXxP(Zh7GH$y_Dr;RTp|LW!w!{7LnuchhZYu42QOoT(}+qWBp6M` zBODav+hbIWs0t-ge2`0pLjotN<9wQfp?DC3q7F$)!(==dp38<{5OOe&;}B6%0Ygi{ zIG^B<8r+G>aGVPzwjmfqq!g~n_OAz`69h!XIi5pP(UQRTk-vqx9J=l~uq6jl?T-@8 z&l3B6gL%WyzHVsWFm$XNI@Z*ctl>~@;_g$&F2GAm7nd%bzj*%2#G2x^uJ6-GZhVsl z#)cJV!CAIURO%*klL)A9Tp273URkIZyK+;=Z)#bYFU*(iBb7#9?({vCCO2|drpphn zj1|UKrVG=n-i`XMZ|l1@$wtM%Plks4+$yyeyTNZ5#=kR+f1N%4gW0}nUp@2AbYAhR zd0&3yp1tb>>wDHxUFl54KA4|CIrg|W?7i#u-qN1Ze8qmeiuG>TJ?nN)iMe5_*hi{Z zSCv<>AIwiYqCxYXLgr4h`Ksz|)#}9e&D{^sZQTDz2I@`{WyAQU8yKD0waztDS??+9 z4qcGkSDDJKT_w+LmA5SMK01hrNavG>oj~965D-)<_@0%k} zHYxPsaq4Nb;-4RE^0~kl%Ht-VN&2Ou0o$W;1l=^Ld``v9eG)u&QrNmwqXz1x#;Y4u zQ(vj2$Z0I`RHN8#ZQawzpS)WFZ+@jB`ifHH6{6@l^8qOTF!6bdzo%x;Og( zseHZd)waB5O}e_2YpW35cL*K&Js@4gz!n8a{jTZxokrLnn=jKqZbW4vo>KeT2~-E6 Zdz`96u|Ff}F%7t-QH+_WTP*~#e*sJO)XD$= delta 129 zcmaD@c0Qd?n~#@^0SJ=ssAa~ZCWAGzEJG%vrrPFb%#(DO zG&MK(n5!^O-fAJqH3g`N5r~UzHs7#NW-|W7Cd#P)b8FynX0%(2hk7%y$>v$Qy3Uz7{V9=943F1 z6=rmv{7_2DNr1u4n}MO6iGiUgEQ}4vb6{X7V2^PH83O|0tU=Ny%n)_~M;M1XgvpR1 z4mOMihF`EEw1Oa&lZzl!@P6jy! zZ(lGyIZ;!7a=w))t1Q^152b`BuQyg$a@&1lK1r$_rdJq+FJ{8`n&5gzL~=&x4EF^_ z3sM(^Uy#yWQF%?o@-xuCDj%5{)UCeoFz`xjzHWJtgYnYjnNBW@rzhWYQfIvhwBCF( zm$Nks>un%2ce9uKMMh1K!-_ysMIgZoQl c=AW4vm`pz~0a+j1m?ppW3}j54Z0ltM0GURTi2wiq delta 258 zcmaDmo$==sMm}vmUM>b8$atrgx!r0bUym-U2^#}L!{h*E`N<8MoRbe&uh^Wg_ko2` zd-6eRb5`jvh5(1j52b`B2RMpMj@RdB(v;b}%%qx0rb;5n)!j2V#5KrDAuYcsS0S~i zD8H!KR<&4DadMW2)Z`fRt4x}#ll?8^REk-FRw{r%kt&dNi=!wtJ+rt3sN)on`x%If zqc`ucFk_lr>L$23(t0xoYYvW!nA@9>%~V8&?vnVEsf^aB%+^`U}sa=dpSWANmS-ZlWH;7^DE diff --git a/backend/routes/__pycache__/main.cpython-314.pyc b/backend/routes/__pycache__/main.cpython-314.pyc index 52687d9432bac8d143e88e95ec17fd36d79a93a7..1bab7a9bcdf3ba6f67c72db2a6551d2fa657be1c 100644 GIT binary patch delta 3120 zcmai0Yj6|S6}~I2)~okp^{}j$-$urf0fQYk5Qh+B7=tZjSU17Jqu{ksZKTaziC2d) zZKhMVlNORoz|&vQrUTQNv{1E8LYtw`ObB!+mT@X|NoQdCuYbxT{lTyF+?C`Z%yihX zzkANPXU{$7yXW53(*7Fsxo8y_8~KfC zcBLKsg{qfmU(VMq+V(IV%OxW;rX#Uf(A5FNR3F(PI?84;ZL_q?PSGwpL??qFdp4(w z(d>iJJZ!=_R_PKT26}<)u+`NkTq4`48`2GiE2X#~mFKFlMLTg?1kw`|Aa;v3W-L~{ zAlk^RrFW%!taI7Zy~o&5RbUi_wYst@dqjPFHSJnAWaO=66oQPrqBq_J6Hy;*nCOKh zB9$b3Fz1V}qdc_{EZ9x6-enuiMguC-euRxl=>uxbfRvVSaz@n;#CNIsZNuADcIT_{ zepUDF{v*~A2_KMf{r1d+G^42c?He~8;Q{KmrYAEhJFsi(D@TkJD~6H&o#QIEX?SS! z)&bSJDU+T|P33Sho6=>{zHJ6V;HDWeKY$C7Z-q8YqlEGPNLS?6=!-~pbX@BN`L>8SXCPLP`Vza6_IWtpT1B)~oS6&hCp$fk@ znU$t8_^|3MM;fmfQj(%F)2jEWG{?(cc$6mh5{+O=0bk|BRfCqHs)x+*F!iX9Q`|uz zL7kZi)%-?s?{rQEa=W82QJ}Vfz|$;=I?_gN~dfr2@0EM z?{la*cCPPi--WKFrk>lbu|iYNeDZSd$Gw+dINAOA>Ra8{yBButE)4EI*?nU5>F$%= z;Bahk!8P`!D=UBCcu!m2u@Kvwp(F(7_>$RCGzSXizz+ETK*iq=rU8k(18i`M2vYx7g)$)dGo(b{s}Q;hZ&qP-tA7o&ZP(Y}(e zReSk_^!A>TuYtYzf`{(T<5mjH497T_R@;>x#5DPrNsNb;2Vnk ziUPmly!Y??ss}Adcs=}8JMu^7XN!@pLZs_5U1Fwxya!zIiG+0kVme5P)w-y zH5eR7co_ySa&Azzz&2ia-k0cMuexm%+Z=$`oX*60_F9j7gki56eIp!uy~P1|81dwXyo-7R&e48ZQTMN=S7@6s*hdGQ${fOTASv|D zYdwbP2MTW?u|Om9bFw3_%`-_2@S#H(emx-Ur|>HIZD3U-MMd}-fUpZd<(0krq>0>& zgkPh!?-C~1$NYe73@XeV`F*g}a2CvO*!UM`Gj1JwR7d|AP-}Gvs(Bu`kqFs;UG?Br81Q^YSpTz z%j3UL_>=-&Tdg^m#OYLeD$3#OROgSR1~4EIQnbPfvb6x{bYYP_G;0#{3T% C&+n)J delta 2105 zcmb7FU2GIp6rS0gnf;rco&BBd58Z87+wPYBBrRPEDpJ!{1edZ2Q^ZPL+pV3n-A?ap zw?9f)^aT+i@oK~uO%$|B)YLVS01twQV!%LvgiMSv`eJ<00>1P~@7d{=22F^Q?03)o z?z!il^W8JE-{-E63k`zPPLU_`s6&~rTo6XA4uMR~6h+?|vK01P57H)?#})wctPbeHB&`%3E@mtt+cm z*@7Kn8C{95I=y(k$h&N^t$#TNISTktv%T*X*-ld;wUe1LHBnQh?n;VMnIzM{d72aX1S8}b+ti9B9^ zGo=P(pGb8NkPC6iE~9*0?H-6oA}oF)*ZP>R4(Pwd3!+Tgp30MBSndB1O$=x@~5BY@p~FmGDEpK3kkD zBWPKC*{9G!9QQXpiM9f{4WONXZdWqH6UHu`)v_s+)y?V55b6StZh#(u$HA8#(jCvG z21ln>k5-?YU6?)j!lB$(li%jf=C17MyV}!tD0g7;NbXRMIOKb-2)lk1q@$K= zLO9=Zv$`I;gKy&bV1ixbC|?jS1veJj%WU*M>87XRFNx>5MT%#+rQ2OJ<$8)<`bCT{ z-L_JmE_&&Yd~@MS`448R&KcK%->>>LvH{AckpzQDr)LlX zE#M8Q%_De|(IfbyP!0VOz7*Q#-w6aX31R@s01~({+(Pfdd%{6(I}k(o#qcINgU^ID z8sV1eTJ}{!4;G%T)@csHqglK$vX(tU%!7C=@-p#SjD*?O$^1aU|G+78?KF77VYsxEd5z~REd`hI%dYsA#8(7Kt__;Roi;fQvJ$)Ld}vc}j7 zbs>Nca7FB>6_O%e$MNY{75g!n9K%1wQ0+1Bx?hA(3b}^8rtNP*2!563#X@HYt8-JC zF(orG~%?zg1tTK^7lR&Ph7B!PhI-AzApA-5-L2Ek4(DS&xHO3j5k-Jo*&iGk`n+-7=j*871?4ltW*@+&ZVI18OQeqBF!A zE-I~CQCd?cGFlofV4$JYAu<{PTDS&+8R=k#8w-@Xd0y>K(rbtTg zsX)-n2wK(npumM9&=Nrz=mOr>vBrLbn9vZ~Wvq3)QQk&#cg&PY1c(9n7CFK#X6Qm> HLmmAOUPaCN diff --git a/backend/routes/__pycache__/scp_routes.cpython-314.pyc b/backend/routes/__pycache__/scp_routes.cpython-314.pyc index 730d1b1ae87716e1e8e8622304ab7ccb22e0f10b..707851f18888905d66330babffb902f3113709c7 100644 GIT binary patch delta 2345 zcmbtVT}%{L6u$Gfzq32b&cgDyFbV=&V1Y%8Mdi0zM8NHUTQO_t76#Y`cGtT@LosBv zXs8fmolrS}fIi_(`Sz1cnY z%$e`}+;h)4b7}WqP1bU&#RSm$?fuqJEc=2ro4EWxc+B}44t|QYx-U)VYJfwHoB~nGe?NB}@?h*C#lVl;u2C|YM;LE|5 zG7Wx`1zs}cvJm)5UmkwL8Aa2k2cHYea#+!_GwCx*|I3!uPi?V9{YP8OR$Fl{P?(C; zPp6_|foOY1&TPuPX-pvqY)fg18Ghg={Is7D?RCtCvZoWtN{CNWUl%D7al&1I|ol>D|&B=`F8g)fL)bKFVL{{n7 z)Lh$FyDYQu#ES=iDOwo*xpJW};pn|(IChsSn2E0xRpMWX2mRKKw5@s#(8lT~C|)*G z`Oz{I=~CS0o>K7> z73NK8ltj&LRPNBZN*8UkAY=JJG#B9!U`Flku!^KerDlxJf+xT}T4d4_fbvtXFNiu( zUq#fSNrU>znlg_1s#@zSI$%r5d_1-gH}ue3x(e5$wtCWCEE;RcG13J@QLbLCnP}Ug< z2m2$!{(+Djk3a--n_v#o*UA~@VXgoZJaZ=4WFuAfyc3lsa&XyKp&ku|_Xz#NK^T+d zeOm8MIFdjCw}+sWf4vY|M_1M5CMT>1(A5gzh%5|7#=--ki%9M^!#IZ>v6$c{fW?@g zi34C6Cb+-g3z(E(QjSRlB5p=Cq-sSq#)5-VA1)h}##N;1lSKt<5HyCXr$j&tdJ(5` zLz3KwCxHfvhh_K@661gD3q($VHI`-aRvndd!%L33DdQ@~Up%nv*s=`!UET3zht4(B9-7-|Ka`x$(58s}S zO`W`L$o+ebLAa+eoS=7`$>u6>opEDYWo~g(*XxU0im4j~1mbTLGcE3Um)S`(a2G<) zH*gIEQKU@T8nd=!(W24=K{==h*@e0m1ckel$F}X2TH5e+;H;(BM*2+Bcr*gF48k4+ z>6|~1G0rI4vJ1*LEFhU`4vlJ{(a3cx$j4qLGY{KbV%g>`N-!gj>F3Fud9oNc;8 z_(g{jckVNHVzvmAF6E}P-o7PjEA%O@?Cl+Cf6$MyuVNx%(v69B2;70`187Y06v+Tl z9l&0MnV3JFy+{xRN_TF%QPty*#7O8(<#O(L6LzjLFft}f5YK9@T(qNWnpeOhBm^5~ z4SUgv$3~ delta 1239 zcma)5%WoS+7@zSzyt{sqwc~&tKk6v%k|u&G!D$U?(kL`)p>dbgg~|{)_Bd>?cUR+$ zEKu}pa41+UO#Po71<6B!S!Eq0vynT6Hxc%Jp@b7Bcje_ zfi^&%z&|}APd*Qw>U2GSLB8ZE#>jR!!s~J#lB+^Ipy+KJ478w({+g1TQ1-%F<@p|y z1!+(oIrheY2x?0JB!DfINBb#7jc!iW!po8$ob~jJra;--kI-K!kM;FsiQEzAUmdWe z3sM&7N78@>C~(8TfSO7U#V4!6hEpG()}SjPvuf20bM3Y#ptw+RfyTK~9b3)1(Lk=~ zSVvN77$$NMZr%>Gs`g6FsMjYxb(#&0!x6eLj#6;_j9#f)rh`m}AZZ2;lDpFUs*F*k zw&I|+<9ac&8SatmyrcpOg?Wn%XuUm$R*)h5DLc5A=$FNLS zY*=e+2)hEp*upN?as(yO zlQ)$(oy(gCB8VjS)!eR{yBFJ0b9?H!eRXbEox4B!cw|pq+E;JxsyFx4@|M(9hyP#T zp$NjU{ZRJzP`2xjbfYPn*^R~yWg)C=@drULl=$Y>_O0(DWOH!K|1^}mvw3fQr_%f* zWbR1j^QXQDIp%DIO*?H^g6SP46ZTI8BCEc6S2Wwdq(r0_CBP(TIc7) zE`-D6i{xbi=O{z=lI;Rp#uc!&LEkZZ4M$tSEa3<6j3Vf5R(iW>)ixSv2G=RaesJqP e`8xGcbQI>FaljL1aOQxg=0OpXRJz0?8uT}ZcqaY; diff --git a/backend/routes/__pycache__/utilities.cpython-314.pyc b/backend/routes/__pycache__/utilities.cpython-314.pyc index 8e913b80d00dffbb909820b26753fe979d4a3517..fce965fd6a5c3612629c08a71dc1551802cbaf43 100644 GIT binary patch delta 3135 zcmaJDZA=^I`MvYk`5S)_3^w={62K}oU_SFvwu(R&LL=)!oup%UuI(@rurGH$NF!}_ zq@;6!NoIy&I%w#UE>2Zxv`MCD%A~Yir%tM-qh_*o&7}OOR4G0r(qu|h?Z@8p2O+5H zYu)qS^SsaZ`#jJ2U+4ZjgJ@pRs1*RN!{77-y4q(oCS>5l`n@PdDC`KK>_l%ILq{A) zW#TVww2V3p87bwBy7v93Q{6;+JwbQSAD#?RXS{-r(o(}05de}$K?psXC>RC=as-ft z9SLdjI7%Wz(vpZ1OI*+yo7;*wiMmjyvUK=tC(t&efRQ4BBhgS{`6(%OiRz0yJ~;pu z;1ZYc5xsy92_EGaAtoe*l#n%TDI%~|v=*3w3na=UMvY6fWkFIPMO@-7lJtbDYYqV4 z_iL5!)Hy=vI=0ayC0b+t3W^>oc33#P-Erkk6J{`|X+;Qi1sKCfnH`WAeB^@? zQgP}}UHhS{)j|RW<<9cN!!kpyqGZwv1{K3d<5pq-AQeY<12BjrepF_RI4et@Kih{L zgr>8Y{sV{`VNgco23uFA98@~Z&$vr(r`~8o3-6?gK!jf#tO{XS!AVsacva1ycEF&0 zFb8MJy9P+kE#HWgBlnWsebMfwM5wDnJ+= zqNc6OtOYn2f)`m>Qs65WOk4BC5-AXi)uMVn=?q#8YC!-T5LJT!f`b>pR<*%MEg*ne zK{Zq){07bzxEM%dP*NQLwsPh?)UM#KMfsKaTzm;X;>2?w&GAbY8;*xPR2cs?-Wv(} zZ2UqT&&@q9l{@^!+x**e_COpK z2P5QSOZvg;cIg{8p?!Sg!rzLX1OUtz(=G^1UFH8eS4;yGir~46vD^nY;g;a1ZWa0Q z2L9F(ObOoJ;&`ySWMk}t=+er@+bNg{m{6RbpW>IJLi~bTZ_eNw$$5TyS#Y)xyti1t zP!IrN`WOH}9(@6+2azZkDxK=!I4IQUj_~BCm z>nt`PDRoJmu)}t);cel$kXk^<^Sz=e@`<@k z*sB8roPzR)87kEPqr2_J z^L|(|^v6)*X%yU3m&JEY|15@PP55Q)qBd=^WlT+Lrlz#1W%bm$se4v@_fSld#T%|^ z-_@q^mJHsxhIgj%eYa1o<9&Q3?aHt2I#_*QOtvYO$W34CEerPF*;guVR%Kdx)>?YL=+6ucuMG^Z>)o-Q zY^CLL&0@aQb;eK8gPg+S4F@LtFiA8g@rIaSn3IkBY1%X5XS7gNv8nmR zi*S1E$C(O9gKf(%C-IHNCH~@v`8l<^^x&fpY+6npAU!@GycU|e1Q|KKHx%?T6y*?umpcn zs72NG5~P*|37b%M|AYXR-6IW`xo& zqXSu8`GRTIlx#@rYNP$%C@U_Xzo*@ml&`d>>eao z*c1WPi~rSVB&JOq=#A0-O)S4sw>8CnBKu@k0#Y^=aL{ z#mS_8VRwA@;$*a+)$N06%woEDTNX3MN1^sPL;SUbRUo;q9#2K8uDIDf?QHu1Yj|Z- zE-^|U0f|bs2_zEP)5k{1KGdAJ=#Fz63tOn<4!B9(|ux@);`WbBI4vNd@?lrB7oK{lkp(L1HOUiC z_TAaL3?;7v3jT!wg_^+*j)#2An7^C425GQR0L|RU?(aYjJCNhZBN0H$1kkZ57Vync m1fYuF)Lf{EYg2V8Q?w?HII@WLA=--|vMuh`Z^Z(hH}ik&d|O)p delta 671 zcmZ4A!+5TOPn(aIivb8Cj(^Blq&<;Og3)cGx+)Ws3FGDvrgS4l@yUBkiZ@%Dnlowh z>!;+G=jG%lro^XKB&X&igY>}w8<4@wz`*#KkCB0)VY0oL*koH4X^|iX9d4Ks28IHL zFopp3$%VF3lUO+?C-AW{#c)pcRS@TftBK*AoUb4|d9kK!y&^*pOBe$WgAvdz5F_Ln zVuWFa#fZXKVGJ<&Al4v;AU1QLGeAI@L6g0TH#adkJ~J;ZU#}vkxS~`ODE?;p#5b)C z3U7OQ-pt+eW^RkZn+^NGRL2a3H`CkRbZvdz-SN7+eu-X@15kO9BakSSLD#78x_il+ zSzBH&ob!6}94mzoO{QDCiN*1WB_)Z;8M&!>CAWCtp_P}Io2toO!~!yn7eqvZ2rUr7 z2_o1*1P6%F0}&=5g1a6>8UYDS#-cPJRSXIQg(6S@+~R{94UUMSGN7O;P`((Xuz}$V z8v~EP6t?REnimB$FLP_Hh`-8h)8KN0o4?") +@login_required +def get_scp_content(filename): + """ + XML 파일 λ‚΄μš©μ„ λ°˜ν™˜ν•˜λŠ” API (Monaco Editor용) + """ + try: + safe_name = sanitize_preserve_unicode(filename) + path = Path(Config.XML_FOLDER) / safe_name + + if not path.exists(): + return "File not found", 404 + + # ν…μŠ€νŠΈλ‘œ μ½μ–΄μ„œ λ°˜ν™˜ + content = path.read_text(encoding="utf-8", errors="replace").replace("\r\n", "\n") + return content, 200, {'Content-Type': 'text/plain; charset=utf-8'} + except Exception as e: + logger.error(f"Content read error: {e}") + return str(e), 500 + @scp_bp.route("/scp/export", methods=["POST"]) @login_required def export_scp(): diff --git a/backend/routes/utilities.py b/backend/routes/utilities.py index 34cf208..832c974 100644 --- a/backend/routes/utilities.py +++ b/backend/routes/utilities.py @@ -290,13 +290,79 @@ def update_gpu_list(): return redirect(url_for("main.index")) -@utils_bp.route("/download_excel") -@login_required -def download_excel(): - path = Path(Config.SERVER_LIST_FOLDER) / "mac_info.xlsx" - if not path.is_file(): - flash("μ—‘μ…€ νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", "danger") - return redirect(url_for("main.index")) - logging.info(f"μ—‘μ…€ 파일 λ‹€μš΄λ‘œλ“œ: {path}") - return send_file(str(path), as_attachment=True, download_name="mac_info.xlsx") \ No newline at end of file + return send_file(str(path), as_attachment=True, download_name="mac_info.xlsx") + + +@utils_bp.route("/scan_network", methods=["POST"]) +@login_required +def scan_network(): + """ + μ§€μ •λœ IP λ²”μœ„(Start ~ End)에 λŒ€ν•΄ Ping ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•˜κ³  + 응닡이 μžˆλŠ” IP λͺ©λ‘μ„ λ°˜ν™˜ν•©λ‹ˆλ‹€. + """ + import ipaddress + import platform + import concurrent.futures + + data = request.get_json() + start_ip_str = data.get('start_ip') + end_ip_str = data.get('end_ip') + + if not start_ip_str or not end_ip_str: + return jsonify({"success": False, "error": "μ‹œμž‘ IP와 μ’…λ£Œ IPλ₯Ό λͺ¨λ‘ μž…λ ₯ν•΄μ£Όμ„Έμš”."}), 400 + + try: + start_ip = ipaddress.IPv4Address(start_ip_str) + end_ip = ipaddress.IPv4Address(end_ip_str) + + if start_ip > end_ip: + return jsonify({"success": False, "error": "μ‹œμž‘ IPκ°€ μ’…λ£Œ IP보닀 ν½λ‹ˆλ‹€."}), 400 + + # IP 개수 μ œν•œ (λ„ˆλ¬΄ λ§Žμ€ μŠ€μΊ” λ°©μ§€, 예: C클래슀 2개 λΆ„λŸ‰ 512개) + if int(end_ip) - int(start_ip) > 512: + return jsonify({"success": False, "error": "μŠ€μΊ” λ²”μœ„κ°€ λ„ˆλ¬΄ λ„“μŠ΅λ‹ˆλ‹€. (μ΅œλŒ€ 512개)"}), 400 + + except ValueError: + return jsonify({"success": False, "error": "μœ νš¨ν•˜μ§€ μ•Šμ€ IP μ£Όμ†Œ ν˜•μ‹μž…λ‹ˆλ‹€."}), 400 + + # Ping ν•¨μˆ˜ μ •μ˜ + def ping_ip(ip_obj): + ip = str(ip_obj) + param = '-n' if platform.system().lower() == 'windows' else '-c' + timeout_param = '-w' if platform.system().lower() == 'windows' else '-W' + # Windows: -w 200 (ms), Linux: -W 1 (s) + timeout_val = '200' if platform.system().lower() == 'windows' else '1' + + command = ['ping', param, '1', timeout_param, timeout_val, ip] + + try: + # shell=False둜 λ³΄μ•ˆ κ°•ν™”, stdout/stderr λ¬΄μ‹œ + res = subprocess.run(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + return ip if res.returncode == 0 else None + except Exception: + return None + + active_ips = [] + + # IP 리슀트 생성 + # ipaddress λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜μ—¬ λ²”μœ„ λ‚΄ IP 생성 + target_ips = [] + temp_ip = start_ip + while temp_ip <= end_ip: + target_ips.append(temp_ip) + temp_ip += 1 + + # 병렬 처리 (μ΅œλŒ€ 50 μ“°λ ˆλ“œ) + with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor: + results = executor.map(ping_ip, target_ips) + + # κ²°κ³Ό μˆ˜μ§‘ (None μ œμ™Έ) + active_ips = [ip for ip in results if ip is not None] + + return jsonify({ + "success": True, + "active_ips": active_ips, + "count": len(active_ips), + "message": f"μŠ€μΊ” μ™„λ£Œ: {len(active_ips)}개의 ν™œμ„± IP 발견" + }) \ No newline at end of file diff --git a/backend/services/__pycache__/logger.cpython-314.pyc b/backend/services/__pycache__/logger.cpython-314.pyc index f1111950954cbf47427e7ff63fcdd36ea54c9970..f1a404b0f67daff5c34f75ea2811203ee69685e2 100644 GIT binary patch delta 660 zcmew(-K5N?&Bx2d00iHDyJX(y+Q@fasit-BkSaNCN(xih9E8z#>orVq$bC)axGJ~ezWX3JX~ z<*7y4RjH-vw>XOPle1GxGV^b7q~@h(=A{BztQjRG1r@hAKvZ&mQR*#@lGL2k^rFPv z$=zI9j3Se_ac$yY4|fdm^Yn9{oX(xj6#%qF1c-|tZ@$4T%vdk4(!t!%)yZ{*L*@$y z11ryU7ST&Aq7$+%vnXC?(YnZ@wZ?qC{Yv{QEY^2exXG0K!iF$7{W6Q(br$uDEb436 z*YmHm$leq+C83QM42>SBE K99bj?lmP%7R40M} diff --git a/backend/services/logger.py b/backend/services/logger.py index 9aef772..44e1156 100644 --- a/backend/services/logger.py +++ b/backend/services/logger.py @@ -60,6 +60,19 @@ def setup_logging(app: Optional[object] = None) -> logging.Logger: # Flask μ•± λ‘œκ±°μ—λ„ 동일 ν•Έλ“€λŸ¬ 바인딩 app.logger.handlers = root.handlers app.logger.setLevel(root.level) + # 루트 둜거둜 μ „νŒŒλ˜λ©΄ λ©”μ‹œμ§€κ°€ 두 번 좜λ ₯λ˜λ―€λ‘œ λ°©μ§€ + app.logger.propagate = False + + # 제3자 라이브러리 둜그 레벨 μ‘°μ • (λ„ˆλ¬΄ μ‹œλ„λŸ¬μš΄ 경우) + # werkzeug: 기본적인 HTTP μš”μ²­ 둜그(GET/POST λ“±)λ₯Ό μˆ¨κΉ€ (WARNING μ΄μƒλ§Œ ν‘œμ‹œ) + logging.getLogger("werkzeug").setLevel(logging.WARNING) + logging.getLogger("socketio").setLevel(logging.WARNING) + logging.getLogger("engineio").setLevel(logging.WARNING) + + # httpx, telegram 라이브러리의 HTTP μš”μ²­ 둜그 숨기기 + logging.getLogger("httpx").setLevel(logging.WARNING) + logging.getLogger("httpcore").setLevel(logging.WARNING) + logging.getLogger("telegram").setLevel(logging.WARNING) root.info("Logger initialized | level=%s | file=%s", _DEF_LEVEL, log_path) return root \ No newline at end of file diff --git a/backend/static/js/index.js b/backend/static/js/index.js index c33d5f6..38b1001 100644 --- a/backend/static/js/index.js +++ b/backend/static/js/index.js @@ -1,5 +1,7 @@ document.addEventListener('DOMContentLoaded', () => { + + // ───────────────────────────────────────────────────────────── // 슀크립트 선택 μ‹œ XML λ“œλ‘­λ‹€μš΄ ν† κΈ€ // ───────────────────────────────────────────────────────────── @@ -77,6 +79,37 @@ document.addEventListener('DOMContentLoaded', () => { const csrfToken = document.querySelector('input[name="csrf_token"]')?.value || ''; + // ───────────────────────────────────────────────────────────── + // IP μž…λ ₯ 데이터 보쑴 (Local Storage) + // ───────────────────────────────────────────────────────────── + const ipTextarea = document.getElementById('ips'); + const ipForm = document.getElementById('ipForm'); + const STORAGE_KEY_IP = 'ip_input_draft'; + + if (ipTextarea) { + // 1. νŽ˜μ΄μ§€ λ‘œλ“œ μ‹œ μ €μž₯된 κ°’ 볡원 + const savedIps = localStorage.getItem(STORAGE_KEY_IP); + if (savedIps) { + ipTextarea.value = savedIps; + // 라인 수 μ—…λ°μ΄νŠΈ 트리거 + if (window.updateIpCount) window.updateIpCount(); + } + + // 2. μž…λ ₯ μ‹œλ§ˆλ‹€ μ €μž₯ + ipTextarea.addEventListener('input', () => { + localStorage.setItem(STORAGE_KEY_IP, ipTextarea.value); + // script.js에 μžˆλŠ” updateIpCount 호좜 (μžˆλ‹€λ©΄) + if (window.updateIpCount) window.updateIpCount(); + }); + + // 3. 폼 제좜 성곡 μ‹œ μ΄ˆκΈ°ν™”? + // μ‚¬μš©μžμ˜ μ˜λ„μ— 따라 닀름: "변경이 λ˜μ§€ μ•ŠλŠ” 이상 계속 κ°€μ§€κ³  있게" + // -> 제좜 후에도 μœ μ§€ν•˜λŠ” 것이 μš”μ²­ 사항에 뢀합함. + // λ§Œμ•½ 'μ„±κ³΅μ μœΌλ‘œ μž‘μ—…μ΄ λλ‚˜λ©΄ μ§€μ›Œλ‹¬λΌ'λŠ” μš”μ²­μ΄ 있으면 μ—¬κΈ°λ₯Ό μˆ˜μ •. + // ν˜„μž¬ μš”μ²­: "νŽ˜μ΄μ§€κ°€ λ¦¬μ…‹μ΄λ˜λ„ 변경이 λ˜μ§€ μ•ŠλŠ”μ΄μƒ 계속 κ°€μ§€κ³ μžˆκ²Œ" -> μœ μ§€. + } + + // ───────────────────────────────────────────────────────────── // 곡톡 POST ν•¨μˆ˜ // ───────────────────────────────────────────────────────────── @@ -160,4 +193,75 @@ document.addEventListener('DOMContentLoaded', () => { }); }, 5000); + + // ───────────────────────────────────────────────────────────── + // IP μŠ€μΊ” 둜직 (Modal) + // ───────────────────────────────────────────────────────────── + const btnScan = document.getElementById('btnStartScan'); + if (btnScan) { + btnScan.addEventListener('click', async () => { + const startIp = '10.10.0.1'; + const endIp = '10.10.0.255'; + const ipsTextarea = document.getElementById('ips'); + const progressBar = document.getElementById('progressBar'); + + // UI μƒνƒœ λ³€κ²½ (λ‘œλ”© 쀑) + const originalIcon = btnScan.innerHTML; + btnScan.disabled = true; + btnScan.innerHTML = ''; + + // 메인 μ§„ν–‰λ°” ν™œμš© + if (progressBar) { + const progressContainer = progressBar.closest('.progress'); + if (progressContainer) { + progressContainer.parentElement.classList.remove('d-none'); + } + progressBar.style.width = '100%'; + progressBar.classList.add('progress-bar-striped', 'progress-bar-animated'); + progressBar.textContent = 'λ„€νŠΈμ›Œν¬ μŠ€μΊ” 쀑... (10.10.0.1 ~ 255)'; + } + + try { + const res = await fetch('/utils/scan_network', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': csrfToken + }, + body: JSON.stringify({ start_ip: startIp, end_ip: endIp }) + }); + + const data = await res.json(); + + if (data.success) { + if (data.active_ips && data.active_ips.length > 0) { + ipsTextarea.value = data.active_ips.join('\n'); + // 이벀트 트리거 + ipsTextarea.dispatchEvent(new Event('input')); + + alert(`μŠ€μΊ” μ™„λ£Œ: ${data.active_ips.length}개의 ν™œμ„± IPλ₯Ό μ°Ύμ•˜μŠ΅λ‹ˆλ‹€.`); + } else { + alert('ν™œμ„± IPλ₯Ό λ°œκ²¬ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.'); + } + } else { + throw new Error(data.error || 'Unknown error'); + } + } catch (err) { + console.error(err); + alert('였λ₯˜ λ°œμƒ: ' + (err.message || err)); + } finally { + // μƒνƒœ 볡ꡬ + btnScan.disabled = false; + btnScan.innerHTML = originalIcon; + + if (progressBar) { + // μ§„ν–‰λ°” μ΄ˆκΈ°ν™” + progressBar.style.width = '0%'; + progressBar.textContent = '0%'; + progressBar.classList.remove('progress-bar-striped', 'progress-bar-animated'); + } + } + }); + } + }); diff --git a/backend/templates/admin.html b/backend/templates/admin.html index bfca298..05fdda2 100644 --- a/backend/templates/admin.html +++ b/backend/templates/admin.html @@ -1,72 +1,151 @@ {# backend/templates/admin.html #} {% extends "base.html" %} +{% block title %}κ΄€λ¦¬μž νŒ¨λ„ - Dell Server Info{% endblock %} + {% block content %} -
-
-
- +
+ +
+
+

+ κ΄€λ¦¬μž νŒ¨λ„ +

+

μ‚¬μš©μž 관리 및 μ‹œμŠ€ν…œ 섀정을 μˆ˜ν–‰ν•©λ‹ˆλ‹€.

+
+ +
- {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} -
- {% for cat, msg in messages %} -