From 50799467ce8d6f07a0de6c5939ca70b25683fe01 Mon Sep 17 00:00:00 2001 From: huai zhu <138862916+riceshowerX@users.noreply.github.com> Date: Wed, 22 May 2024 08:44:14 +0800 Subject: [PATCH 1/4] 1.1.8 --- __pycache__/fastapi_server.cpython-312.pyc | Bin 2752 -> 2808 bytes app/__pycache__/main.cpython-312.pyc | Bin 865 -> 865 bytes app/core/config/config.py | 12 +- .../__pycache__/http_errors.cpython-312.pyc | Bin 1017 -> 1257 bytes app/core/errors/http_errors.py | 12 +- .../__pycache__/http_mock.cpython-312.pyc | Bin 2229 -> 3940 bytes app/core/schemas/request_schema.py | 2 +- app/core/schemas/response_schema.py | 27 ++- .../request_helper.cpython-312.pyc | Bin 3783 -> 4178 bytes app/core/utils/crypto.py | 28 --- app/core/utils/crypto_utils.py | 26 +++ app/core/utils/encoding_helper.py | 47 +++-- .../{request_helper.py => network_utils.py} | 15 +- app/core/utils/process_monitor.py | 20 -- app/core/utils/process_utils.py | 36 ++++ fastapi_server.py | 6 +- run.py | 57 ++++-- ui/components/param_input.py | 30 +++ ui/components/progress_bar.py | 11 +- ui/components/request_form.py | 21 -- ui/main_ui.py | 182 ++++-------------- ...1\347\233\256\346\236\266\346\236\204.txt" | 14 +- 22 files changed, 248 insertions(+), 298 deletions(-) delete mode 100644 app/core/utils/crypto.py create mode 100644 app/core/utils/crypto_utils.py rename app/core/utils/{request_helper.py => network_utils.py} (79%) delete mode 100644 app/core/utils/process_monitor.py create mode 100644 app/core/utils/process_utils.py create mode 100644 ui/components/param_input.py delete mode 100644 ui/components/request_form.py diff --git a/__pycache__/fastapi_server.cpython-312.pyc b/__pycache__/fastapi_server.cpython-312.pyc index 35fb66b4a47a2740aad651e48e9db9278d0fd059..8cea056da8cbf98bf0fdd82ec4ba4430b11bf603 100644 GIT binary patch delta 746 zcmZuvOK;Oa5Z-m1*m2@G59PsAN-u4S2%@Ec%B!fnTGF(DRH;4?OucF2I(9Tpk$OPA z6bW&QeBvK~(p$tGapeM1PeD0wK-^FzE(k8{B(10vR`SezGvCg9>)rS1UupHDsw#x9 zAFNb;&{B`lm#xuaM54qbrfAFcaO+8CyB^bH{fuO5y2dM#9oOT1pU@NB%XYG!(o>WO zgr(ZpkFP$`tg!()YMEw@AH{=sH{7O!h|f}(P!-I`j;LqM?dT5Cvn*$-8@w4T^C5~g zG2DO!ShQ!LsR2KhcK z`|l5INn27O>A#`ZMxyhN7RwKr*Ki$=6=*kLG#cC`*PG2og#ozG`9tr~3|>c9y16u0 zxqo{FP;Nm4)n5rb59T;XcfJHRgj^I^;#~R8VnOK2>xN_63_OVXk3-9J#D5oRQN_P2 zUCCyV7=cZvQZ>A$(Wt=`&z$VMmZm8^?eEE##u5jWA{?mmK*C)zS6HkYH3wu4;hcXW zoG*v5qG4s#1;&k2t*rryhBIi+VqI{(Zpd_-%y|Y8Ohldzap5MPD&ft&pRrSvbbfYEy!BPoUM%eAiFh1JeBWPc{+HkY delta 629 zcmZWm&ubGw6rP#g?9XgAq1i392#ts}B0-vp#UB+BrJ5Q%72L~q%}(lWvp<|{i-n2^ z6}*P|DUO36e@xIcBbS%Wy@vC;E9jRKPAK)!p z@Y1vjhxW59OI`e;h0`oZ;=vSJfws!jL;23*2X5ou6T(eYMUtn_g_pT$M#_UDVG}`} zx{}RsTUvt^`c>*dmAdkcX`8EcvK6~+ubX*kKo;4}?BKn89l}}qN4Zv1*zj891u>CX ziqvRBtuz&`GkKNM2OETYA(uE@=6RHKhmxOUINm1w$A|h;uN$=Pj*I7b9k)mp z=|{D>^bQ;`0tmPCPq`T-{#FMslLn*wzq+3o910M^-=O{-)DOVIFED=qmg$Oi8#d`% a&9S3zR~tXX)t{pI=FwpVh-b({Ci)M4l!7Y& diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc index c561157b715a2ccde582ecd5b01161e32b5c3ad0..20bf33a626ae534080afd69856efa3442b6c96e9 100644 GIT binary patch delta 19 ZcmaFJ_K=P1G%qg~0}!aCZR84P1^_f$1Ze;O delta 19 ZcmaFJ_K=P1G%qg~0}vRxZsZDQ1^_eo1XBP2 diff --git a/app/core/config/config.py b/app/core/config/config.py index 5352933..133fe3d 100644 --- a/app/core/config/config.py +++ b/app/core/config/config.py @@ -1,11 +1,15 @@ # config.py -from pydantic import BaseSettings +from pydantic import BaseSettings, AnyHttpUrl +from typing import List import os class Settings(BaseSettings): - SERVER_HOST: str = os.getenv("SERVER_HOST", "127.0.0.1") - SERVER_PORT: int = int(os.getenv("SERVER_PORT", 8015)) - CORS_ORIGINS: list = os.getenv("CORS_ORIGINS", "http://localhost:8501").split(',') + SERVER_HOST: str = "127.0.0.1" + SERVER_PORT: int = 8015 + CORS_ORIGINS: List[AnyHttpUrl] = ["http://localhost:8501"] API_KEY: str = os.getenv("API_KEY") + class Config: + env_file = '.env' + settings = Settings() \ No newline at end of file diff --git a/app/core/errors/__pycache__/http_errors.cpython-312.pyc b/app/core/errors/__pycache__/http_errors.cpython-312.pyc index 353d42e96f8cd1da4212dbf0e3a4e8f22ef60fad..b0511ea4aa4902836097a79ef516c10ebf861d1a 100644 GIT binary patch literal 1257 zcmaJ>O>7fK6rR~1+ez$}1_1$4WmPV$R46p4LJ<_A6ap0_2*aW7N~7)WSlMd5Yi4F4 zL9P@b^OHj*I3S>^)Es)?Cqi%w5{LH0feV#DYDXN9iUqwH#ZpmDeY3U`$f57>=Dm6Q z=KXx1{kTx*MIh(ON6Nc3g#K1eSImyF_yri(kdJ&Eq6#kIidNDv($E<4wL0>3itp&{ zZ%evwP@}B*X5A>6(-}Maj|$kDEF3y<;@E*Hk5)wxM>79)abo;9<<&Ui)N3cVwb5}F zKLz6=)TO9|eN@tX46kd{fO=-xxP;2sH|qIP<`SC1rEC$|rtC?JFc!099b~`0c)M}; zr{KuY4qX2=PLfb3(W( zYFXCI(u6ncoaB^o=kuMrcI|M!ioK*lBf*`qKpaZGbBbqzvKTpbc-v?kMbs0HTdg`? z%&0@v7dP9#f+AA4}E2a%9E7c4C*3!U*i8ip`GW(8Fe zgWAtN0L?O8rKabffO=a;e;I>K1DhjwZnTNid2j8%>G>wr?89!7)ajJxbufTSgw*C! z-bKkTO9O)bX0oCX)P5=M^ delta 625 zcmaFK`IDXRG%qg~0}wR1`J`Q7oXBU%&%Bx$B*4HB#gf93$~ZC1jah+#Vd6qbt`wFQ zmMFGLR!z2v=iI}XfJ#6rKLceMrZdzq)-c2aSrAae1mrQoc$EyAjDDI-MeIO;mTx3_xmw@D~sT(F9U|i#fF-IT_?t7+?i5z~1%%GNv<>z&%;Rn1!rP zAF3gR5h%d|(~Y1r8P_nahH3}~2FT123s@u*Z*jW9OoD_J$e1D#5CIRwUmP~M z`6;D2sdhy&KnalNixq&x2WCb_#^((BUzk`KWhZccVE|Ge_*fa)R>*?bKr4WX0JqS2 A&j0`b diff --git a/app/core/errors/http_errors.py b/app/core/errors/http_errors.py index 551db9d..e9aa8d6 100644 --- a/app/core/errors/http_errors.py +++ b/app/core/errors/http_errors.py @@ -1,15 +1,15 @@ # http_errors.py from fastapi import HTTPException, JSONResponse -class HTTPError(Exception): + +class HTTPError(HTTPException): """自定义 HTTP 异常""" def __init__(self, status_code: int, detail: str): - self.status_code = status_code - self.detail = detail + super().__init__(status_code=status_code, detail=detail) -def http_exception_handler(request, exc: HTTPException): +def http_error_handler(request, exc: Exception): """全局 HTTP 异常处理""" return JSONResponse( - status_code=exc.status_code, - content={"detail": exc.detail}, + status_code=500 if not isinstance(exc, HTTPException) else exc.status_code, + content={"detail": exc.detail if isinstance(exc, HTTPException) else "Internal Server Error"}, ) \ No newline at end of file diff --git a/app/core/routers/__pycache__/http_mock.cpython-312.pyc b/app/core/routers/__pycache__/http_mock.cpython-312.pyc index f2dd0d93126e6a2069afd1e8d32bf9cc0d535268..f1333a23baa7b91d2838ed56f87554b4a3513cd4 100644 GIT binary patch literal 3940 zcmcH+TWnKDcEwN`17QnEn&4te!t!VyP_Ue}9N$Ue#4qMvHz7C( zXw@baB8|FgkhYbw0%?OnMbM&c(0;J&R;6@*Y&nv%cR{;VL(Bf91ZfLhX?Nz@*G^DY zE3MR#?#!7pXU?3*oHOVC*<{iK(wOE$UH{er@C}_PjY|WIj|ngic)&wH2tX$csGO<* z<75J?lZ6zo@~ZdCx`5uPmtnQv5HLE8GOY2N0y)kc8P@vEfmO~` zfm~;rtr$q%2kRSZ4vs>)oaZn;m&GGV-gg z=T0#x9O1>Hf+?7qbwQbn1?l-b!b!vDzoVW?Z*~@)=Bx$(pff8g>FjSbw}v7j!jh?} zz5R{-16~vseW9SFZAGUdNDw8h1BFqL7pzb+9&9^$q!kI_P*6Z*o-2?AcaJEB-8e-} zk(5t*bDOsZ1v~_4Ng-1^GgzH`B*r0ZRY^t^^y1TD(an2Ak7VRgc6MY?y*&`}_6^D# zDh?BnY|w-;>hcXptmx?$ByCy(NrRCX!9j1@7P8tb>E9;bB2T)X14uj_1-Hjj{s@oU!BCJGL%t1`B|@Q~r_+yk?@~{Zs80EJkoYI%UoqqV-gImc znoQ$i6#fc)qEaFT0v(8|K7hDb%y>8odo`I289><)h36vEW@tgpOvk*vKdb3XGeuPs&ShYETLRMtN+t8+4EP90*yOk?f;Phn5brX z1`uh+mK%|Hy#i-PB}Nq;+enPUboBNASNIih@zzqh^+2prcvbi#PzTv&x%v0rFr-dsf>I#jOe*;k=V@&gMXyvXUKnbyWL;1u~NG8nIfTjA|8) zS30vF3@|4_Ka^iR?U6xCEAk@WTZpenr~KLqt5(tkkk}LAB_@LX+x{)&x zEhcI!d{2qy))GnU4|R7FE)wetc7-s#hM0O8ucN1`eIi;WhK`CM*B_WhC$69!^%kZD zq-5^Gp@7>XirCjlN+toDD3%q`fQV^)Nm?=F=6zlf(+rPkI>sAlw~;RDvbB|37E@vb z5nDO=L6^;dX+TM^Pl9hroT9g6QlL~}1d%{xt4KFY!&fo}Jbg&1!SK%rs(^gL8uEI` z@AW)uc<<2pL$SOK6a2*SSnk$YcH4q#qq<%)ter1fKjHXI)32KnMcalQ7n|nHd821W z&cw_c=NewS(|5Zs*>E)8a5T}-k}PYz$xnM`axb5{x^?o@mF@BJ>O@)V2kb8mbW?OB z8Z*B%*SPOa^ma7a*b;ASNi?=4%iC{?)2C)CC*alkX?UeEUa=!l-cB|d>88^or(@=- z2WvJ>=HDo}R+3n=ldS0I%Gr^#F|&2fzW-j?-LjdDu0|C6}w;eO~nNt%j*NxLHQ|9=FR}xj7l)7@BGmjcajIp9E zlZPi8V+D1yT>S%c>E*-8HP!Jo)kNWhxhBTdJX_oa%G=?e;oS_Ow-!sG{%OXKYh#?1bxFX##fqrGt;5!9>TI@4+TmKcQWqLu~ur)Og(agw7Wkp!x;;UPVCs H8WHI~da;!6 delta 1118 zcmZ8f&2JM&6rb6d_3qkUI|&KIn9!Jf5QAcfDg;Ub6vYUHDg;%fRx7|VnQ^e$^@iDv zS|mf-fYcti4H8Ig1sst%aN!T2iPS?csd7Q99;g8PDV^vEKYZ=S!rTD_5NHDjTF_A}#X%N=GN#zrQ7tv( z$c{OhrG*^Zx?@;I$W=S;B&dqvr|?|0TdFQ7&Guuys50uw9+(P%dPVd zktAq>Bp;bgv>qiXib$Fo6-+vA;^s!F)p=FRcRrV19`P(3;3wdD{;RMHXku7n^vWFd z`~Z`(U!LBW7`7S$Z-`%;DEtK)=LOu)Z^tq)&hN&)f?d278{Mn;Jz=eY+SZ6|+J@4E z>)tiQUu(HO^b`2yeGCFW6V*0QEz3_U+xZziK7J6~IyC_N9Z_-+u;FXi|0MDoxYi%> zO)H|i_>!J0;p_Mt=nqC2(fz6D-dIE|Iviy-Em0fzqt)I|BQZZ8RW@)F@@2gTi5dKy zF_{RoOSDWV^H>MJXIS$+fo8iiGu8S`fUEV2%QVr-x+H9o&@Dk~#ySK9M$L2UB`H1I zCWBJ*AYEauQ!4vDt6rS*smEe6s`+k-RHuC=kCb5vy%MB^fpPrXX*%mykGl0hrHr{O zK%UR?Doe|naF))fH|y3t8ki9aCkK#v^1S=S`~6ZXr|L4}U(Eoxuz7))g|yzwi*W1p+oC;H^ICxMa?zN7$Hf zQjL}9!OE8fHA4qudkq}d%7}J_6bwZ*KKJehfa!I)P;5oAlyNzf(yXVaJ zU(Wyi|2gx|_w_$D1+FTJ1jyJv)Iap5G#!Y*AE(!EpTGcfBmPnGfH*20kRTAtepto4 zCS!q2pT=vfCTsE#vx)?v6Z@Z_6N!#EN+0n^6C3c(c5lurh7+@=*v+g& zHV$$j5=m(xhI+5Y($hN{a?pgEjfRgr4L-ajxU|dnp)O$I8!@h>OuR07>m)a&;@Gt>zXl^%Nm-4UMlY1 ztLr)45%(TSYh&hcE*n*75<7I-DNfa+&L#RmEt@(xqG7#)8ie%7Fk#VKslaN7(hk%N z+GtJoe#w~FOc3pABTd@y2DtYYxU&mXgs!Pgi)z!^@RGXqE@r z{-IW1Q=1pnW@^|6Z3AsnFUA*8f>q%3gvJ<+1=dBsi>Sc6s33qq%@m%A&$2U})7eF3ZNcXn>{Ot7py(n2 zIXD%cY%9n`8F{i&lmj$`HaS>Oi*glEiT?~c)j63h1dFT}&KR6ux(6?)>i#Fzf;amZdC(El?#`MOa8sw{;uR*joFbrlvB3LR#9+4k_wD z6^(|dE#xLjQmZuj&;%c!oye?|x+>T#A33sM~u^LBY=oTY@cH1cqo6o3~|K?&Zp0 z3mcZS4JQL9o6|m(Bq23}Z^Z|KUdkZIxiM8GI8T?#{ zv?)dd`(qgQ{}AXUoW?T!4L6or#FH+fOX4(M55p4>0f6>RP;@{or;&f`77BbNAu z=tuG_5wD%iB*6N zq7DgcX94rl7ldM>fv*TqTmU0NKNTvmLZ^jf$*!l+CwF0}HOLK=QGb-)ArYf*Slv`k zXUHS#%5G_8$CTKPoY-@s54Jj0pp@h7p`IMJvpN8JM9Uto1B2C- zErTa@7d?Czt)@^x_Ui6@{=|}zxQ969ReChE zuTX#G(eFm_O{03rsAg;@{Vw!`zNmKqUCM|QT@CF#x2U&2@(G}|2kn8v3RCzydmfq? zEzh)IcyoF2OCGGtI1}T)ZcD4&^-7!;#p|(WO#m~^Iyh%Ka9S2;Iz_N&MGX9`jG3*P znQ}5)*_GIj$sCRXm@DZb&sUK7icTHSeEptGlq^IE*bC)6NG!y}%;VBR0%z*^E!QWe5G9k(W1rufO$iGih1tuFyh zZyUvjnOjAgIo>kX str: - """加密数据""" - return fernet.encrypt(data.encode()).decode() - - -def decrypt_data(data: str) -> str: - """解密数据""" - return fernet.decrypt(data.encode()).decode() \ No newline at end of file diff --git a/app/core/utils/crypto_utils.py b/app/core/utils/crypto_utils.py new file mode 100644 index 0000000..db74254 --- /dev/null +++ b/app/core/utils/crypto_utils.py @@ -0,0 +1,26 @@ +# crypto_utils.py +from cryptography.fernet import Fernet +import os +from dotenv import load_dotenv + +# 加载环境变量 +load_dotenv() + +# 从环境变量中获取加密密钥 +ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY") + +# 如果没有设置加密密钥,则抛出异常 +if ENCRYPTION_KEY is None: + raise ValueError("ENCRYPTION_KEY environment variable is not set.") + +# 创建 Fernet 对象 +fernet = Fernet(ENCRYPTION_KEY) + +def encrypt_data(data: bytes) -> bytes: + """加密数据""" + return fernet.encrypt(data) + + +def decrypt_data(data: bytes) -> bytes: + """解密数据""" + return fernet.decrypt(data) \ No newline at end of file diff --git a/app/core/utils/encoding_helper.py b/app/core/utils/encoding_helper.py index 0480598..82673d7 100644 --- a/app/core/utils/encoding_helper.py +++ b/app/core/utils/encoding_helper.py @@ -4,7 +4,7 @@ DEFAULT_ENCODING = "utf-8" # 设置默认编码 -def decode_content(content: bytes, encoding: str = None) -> str: +def decode_content(content: bytes, encoding: str = None, encoding_list: list = None) -> str: """ 解码字节内容,优先使用默认编码,如果解码失败,则尝试使用 chardet 检测编码。 @@ -12,22 +12,31 @@ def decode_content(content: bytes, encoding: str = None) -> str: :param encoding: 指定的编码格式,如果为空,则使用默认编码 :return: 解码后的字符串 """ - if encoding is None: - encoding = DEFAULT_ENCODING - - try: - logging.debug(f"Attempting to decode content with encoding: {encoding}") - return content.decode(encoding) - except UnicodeDecodeError as e: - logging.warning(f"UnicodeDecodeError: {e}. Trying to detect encoding with chardet.") - detected_encoding = chardet.detect(content)['encoding'] - if detected_encoding is not None: - logging.debug(f"Detected encoding: {detected_encoding}") + if encoding: + try: + logging.debug(f"Attempting to decode content with encoding: {encoding}") + return content.decode(encoding) + except UnicodeDecodeError as e: + logging.warning(f"UnicodeDecodeError: {e}. Using default encoding ({DEFAULT_ENCODING}) and ignoring errors.") + return content.decode(DEFAULT_ENCODING, errors="ignore") + + if encoding_list: + for enc in encoding_list: try: - return content.decode(detected_encoding) - except UnicodeDecodeError as e: - logging.error(f"Failed to decode content with detected encoding: {e}. Returning original bytes.") - return content.decode(DEFAULT_ENCODING, errors='replace') - else: - logging.warning(f"Failed to detect encoding. Using default encoding ({DEFAULT_ENCODING}) and ignoring errors.") - return content.decode(DEFAULT_ENCODING, errors="ignore") \ No newline at end of file + logging.debug(f"Attempting to decode content with encoding: {enc}") + return content.decode(enc) + except UnicodeDecodeError: + continue + + logging.warning(f"Trying to detect encoding with chardet.") + detected_encoding = chardet.detect(content)['encoding'] + if detected_encoding: + try: + logging.debug(f"Detected encoding: {detected_encoding}") + return content.decode(detected_encoding) + except UnicodeDecodeError: + logging.warning(f"Failed to decode content with detected encoding. Using default encoding ({DEFAULT_ENCODING}) and ignoring errors.") + return content.decode(DEFAULT_ENCODING, errors="ignore") + else: + logging.warning(f"Failed to detect encoding. Using default encoding ({DEFAULT_ENCODING}) and ignoring errors.") + return content.decode(DEFAULT_ENCODING, errors="ignore") \ No newline at end of file diff --git a/app/core/utils/request_helper.py b/app/core/utils/network_utils.py similarity index 79% rename from app/core/utils/request_helper.py rename to app/core/utils/network_utils.py index 77cd893..b49f8f0 100644 --- a/app/core/utils/request_helper.py +++ b/app/core/utils/network_utils.py @@ -1,8 +1,9 @@ -# request_helper.py +# network_utils.py import httpx import logging from typing import Dict, Any, Optional from app.core.errors.http_errors import HTTPError +from app.core.utils.crypto_utils import encrypt_data, decrypt_data async def send_http_request( method: str, @@ -12,6 +13,7 @@ async def send_http_request( data: Optional[Any] = None, json: Optional[Any] = None, timeout: Optional[float] = None, + encryption_enabled=False, **kwargs: Dict[str, Any] ) -> httpx.Response: """ @@ -36,7 +38,10 @@ async def send_http_request( async with httpx.AsyncClient(timeout=timeout) as client: try: - logging.info(f"Sending {method} request to {url} with params: {kwargs}") + logging.info(f"Sending {method} request to {url}") + if encryption_enabled: + data = encrypt_data(data.encode()) if isinstance(data, str) else data + json = encrypt_data(json.encode()) if isinstance(json, str) else json response = await client.request( method=method, url=url, @@ -57,12 +62,12 @@ async def send_http_request( except httpx.HTTPStatusError as exc: logging.error(f"HTTP error occurred while requesting {exc.request.url!r}: {exc}") - raise HTTPError(status_code=exc.response.status_code, detail=exc.response.text) + raise HTTPError(status_code=exc.response.status_code, detail=exc.response.text) from exc except httpx.RequestError as exc: logging.error(f"An error occurred while requesting {exc.request.url!r}: {exc}") - raise HTTPError(status_code=500, detail=str(exc)) + raise HTTPError(status_code=500, detail=str(exc)) from exc except Exception as e: logging.error(f"An unexpected error occurred: {e}") - raise HTTPError(status_code=500, detail=str(e)) \ No newline at end of file + raise HTTPError(status_code=500, detail=str(e)) from e \ No newline at end of file diff --git a/app/core/utils/process_monitor.py b/app/core/utils/process_monitor.py deleted file mode 100644 index 6ec969c..0000000 --- a/app/core/utils/process_monitor.py +++ /dev/null @@ -1,20 +0,0 @@ -# process_monitor.py -import psutil -import time -import logging - -class ProcessMonitor: - """监控进程状态""" - - def __init__(self, process_id: int, process_name: str): - self.process_id = process_id - self.process_name = process_name - - def monitor(self): - while True: - process = psutil.Process(self.process_id) - cpu_percent = process.cpu_percent() - memory_percent = process.memory_percent() - logging.info(f"{self.process_name} CPU Usage: {cpu_percent}%") - logging.info(f"{self.process_name} Memory Usage: {memory_percent}%") - time.sleep(1) \ No newline at end of file diff --git a/app/core/utils/process_utils.py b/app/core/utils/process_utils.py new file mode 100644 index 0000000..c84eb6b --- /dev/null +++ b/app/core/utils/process_utils.py @@ -0,0 +1,36 @@ +# process_utils.py +import psutil +import time +import logging +from multiprocessing import Process + +class ProcessMonitor: + """监控进程状态""" + + def __init__(self, process: psutil.Process, process_name: str, interval: int = 1): + self.process = process + self.process_name = process_name + self.interval = interval + + def monitor(self): + while True: + try: + cpu_percent = self.process.cpu_percent() + memory_percent = self.process.memory_percent() + logging.info(f"{self.process_name} - CPU Usage: {cpu_percent:.2f}%, Memory Usage: {memory_percent:.2f}%") + except psutil.NoSuchProcess: + logging.warning(f"{self.process_name} process not found, stopping monitor.") + break + except Exception as e: + logging.error(f"An error occurred while monitoring {self.process_name}: {e}") + time.sleep(self.interval) + + def start(self): + self.process = Process(target=self.monitor) + self.process.start() + logging.info(f"Started monitoring process {self.process_name} with PID: {self.process.pid}") + + def terminate(self): + if self.process: + self.process.terminate() + logging.info(f"Stopped monitoring process {self.process_name}") \ No newline at end of file diff --git a/fastapi_server.py b/fastapi_server.py index ca1f5bf..49ed6f1 100644 --- a/fastapi_server.py +++ b/fastapi_server.py @@ -1,9 +1,10 @@ # fastapi_server.py from fastapi import FastAPI, Request, HTTPException from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse from app.main import app as fastapi_app -from app.core.errors.http_errors import http_exception_handler +from app.core.errors.http_errors import http_error_handler from app.core.config import settings import logging import uvicorn @@ -62,7 +63,6 @@ def run_fastapi(): except Exception as e: logging.error(f"An error occurred while running the FastAPI server: {e}") - if __name__ == "__main__": # 添加 CORS 中间件 (全局配置) fastapi_app.add_middleware( @@ -77,6 +77,6 @@ def run_fastapi(): fastapi_app.middleware("http")(api_key_auth) # 添加全局异常处理 - fastapi_app.add_exception_handler(HTTPException, http_exception_handler) + fastapi_app.add_exception_handler(Exception, http_error_handler) run_fastapi() \ No newline at end of file diff --git a/run.py b/run.py index fe67e4c..235529e 100644 --- a/run.py +++ b/run.py @@ -1,36 +1,53 @@ # run.py -from multiprocessing import Process, Pool +import os + +def main(): + # 获取项目根目录 + project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + # 设置工作目录 + os.chdir(project_root) + # ... 其余代码 +from multiprocessing import Process from fastapi_server import run_fastapi from ui.main_ui import run_ui -from app.core.utils.process_monitor import ProcessMonitor +from app.core.utils.process_utils import ProcessMonitor import logging # 设置日志级别为 INFO logging.basicConfig(level=logging.INFO) -if __name__ == "__main__": +def main(): try: - # 创建进程池 - pool = Pool(processes=2) - - # 使用进程池运行 FastAPI 服务器和 Streamlit UI 应用 - fastapi_process = pool.apply(run_fastapi) - streamlit_process = pool.apply(run_ui) + # 使用 Process 直接创建和管理进程 + fastapi_process = Process(target=run_fastapi) + streamlit_process = Process(target=run_ui) - logging.info(f"Starting FastAPI process with ID: {fastapi_process}") - logging.info(f"Starting Streamlit process with ID: {streamlit_process}") + fastapi_process.start() + streamlit_process.start() - # 创建两个监控线程 - fastapi_monitor = Process(target=ProcessMonitor(fastapi_process, "FastAPI").monitor) - streamlit_monitor = Process(target=ProcessMonitor(streamlit_process, "Streamlit").monitor) + logging.info(f"Starting FastAPI process with ID: {fastapi_process.pid}") + logging.info(f"Starting Streamlit process with ID: {streamlit_process.pid}") - # 启动监控线程 + # 监控进程 + fastapi_monitor = ProcessMonitor(fastapi_process, "FastAPI") + streamlit_monitor = ProcessMonitor(streamlit_process, "Streamlit") fastapi_monitor.start() streamlit_monitor.start() - # 等待两个进程结束 - pool.close() - pool.join() - + # 等待进程结束 + fastapi_process.join() + streamlit_process.join() + + fastapi_monitor.terminate() + streamlit_monitor.terminate() + except KeyboardInterrupt: + logging.info("Received KeyboardInterrupt, terminating processes...") + fastapi_process.terminate() + streamlit_process.terminate() + fastapi_monitor.terminate() + streamlit_monitor.terminate() except Exception as e: - logging.error(f"An error occurred: {e}") \ No newline at end of file + logging.error(f"An error occurred: {e}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ui/components/param_input.py b/ui/components/param_input.py new file mode 100644 index 0000000..3ec3206 --- /dev/null +++ b/ui/components/param_input.py @@ -0,0 +1,30 @@ +# param_input.py +import streamlit as st + +def ParamInput(param_type, key_prefix, value_prefix): + params = {} + with st.expander(f"{param_type} 参数"): + param_count = st.number_input( + f"{param_type} 参数数量", min_value=0, step=1, key=f"{param_type}_params_count" + ) + for i in range(param_count): + col1, col2, col3 = st.columns(3) + with col1: + key = st.text_input( + f"{param_type} 参数 {i+1} 的 Key", key=f"{key_prefix}_{i}" + ) + with col2: + param_type = st.selectbox("选择参数类型", ["string", "integer", "float", "boolean"], key=f"param_type_{i}") + with col3: + if param_type == "string": + value = st.text_input(f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}") + elif param_type == "integer": + value = st.number_input(f"{param_type} 参数 {i+1} 的 Value", step=1, key=f"{value_prefix}_{i}") + elif param_type == "float": + value = st.number_input(f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}") + elif param_type == "boolean": + value = st.checkbox(f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}") + else: + value = st.text_input(f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}") + params[key] = value + return params \ No newline at end of file diff --git a/ui/components/progress_bar.py b/ui/components/progress_bar.py index e6528b2..1873d99 100644 --- a/ui/components/progress_bar.py +++ b/ui/components/progress_bar.py @@ -5,11 +5,6 @@ def show_progress_bar(): """显示进度条""" progress_bar = st.progress(0) - - # 模拟请求处理时间 - for i in range(10): - time.sleep(0.1) - progress_bar.progress(i * 10) - - # 设置进度条为 100% - progress_bar.progress(100) \ No newline at end of file + for i in range(101): + time.sleep(0.01) # 模拟耗时操作 + progress_bar.progress(i) \ No newline at end of file diff --git a/ui/components/request_form.py b/ui/components/request_form.py deleted file mode 100644 index 77afd8f..0000000 --- a/ui/components/request_form.py +++ /dev/null @@ -1,21 +0,0 @@ -# request_form.py -import streamlit as st - -def get_params(param_type, key_prefix, value_prefix): - params = {} - with st.expander(f"{param_type} 参数"): - param_count = st.number_input( - f"{param_type} 参数数量", min_value=0, step=1, key=f"{param_type}_params_count" - ) - for i in range(param_count): - col1, col2 = st.columns(2) - with col1: - key = st.text_input( - f"{param_type} 参数 {i+1} 的 Key", key=f"{key_prefix}_{i}" - ) - with col2: - value = st.text_input( - f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}" - ) - params[key] = value - return params \ No newline at end of file diff --git a/ui/main_ui.py b/ui/main_ui.py index 5538d2b..3cd75ae 100644 --- a/ui/main_ui.py +++ b/ui/main_ui.py @@ -1,113 +1,28 @@ -# app.py -import sys +# main_ui.py import os -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -import streamlit as st -import requests +import sys import json import logging import time import chardet -from typing import Dict, Optional, Union -from pydantic import BaseModel, AnyUrl, Field, field_validator -from cryptography.fernet import Fernet +# 获取项目根目录 +project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# 将项目根目录添加到 PYTHONPATH +sys.path.append(project_root) -from ui.components.request_form import get_params +import streamlit as st + +from app.core.utils.network_utils import send_http_request +from app.core.schemas.request_schema import HTTPRequestSchema +from app.core.schemas.response_schema import HTTPResponseSchema +from ui.components.param_input import ParamInput from ui.components.progress_bar import show_progress_bar +from app.core.utils.crypto_utils import encrypt_data, decrypt_data # 设置日志级别为 INFO logging.basicConfig(level=logging.INFO) -# 复制 COMMON_ENCODINGS 变量 -COMMON_ENCODINGS = [ - "ascii", - "utf-8", - "utf-16", - "utf-32", - "latin-1", - "gbk", - "gb18030", - "big5", - "shift-jis", - "euc-jp", - "euc-kr", -] - - -class HTTPRequestSchema(BaseModel): - """ - HTTP 请求模式定义 - """ - - method: str = Field(..., description="HTTP 方法", example="GET") - url: AnyUrl = Field(..., description="请求 URL", example="https://example.com") - params: Optional[Dict[str, str]] = Field( - {}, description="查询参数", example={"key1": "value1", "key2": "value2"} - ) - headers: Optional[Dict[str, str]] = Field( - {}, description="请求头", example={"User-Agent": "Mozilla/5.0"} - ) - data: Optional[Union[str, Dict]] = Field(None, description="请求体数据") - json_data: Optional[Dict] = Field(None, description="JSON 请求体数据") - encoding: Optional[str] = Field( - None, description="请求体的编码", example="utf-8" - ) - - @field_validator("method") - def method_must_be_valid(cls, value): - valid_methods = [ - "GET", - "POST", - "PUT", - "DELETE", - "PATCH", - "HEAD", - "OPTIONS", - "CONNECT", - "TRACE", - ] - if value.upper() not in valid_methods: - raise ValueError( - f"Invalid HTTP method: {value}. Valid methods are: {valid_methods}" - ) - return value.upper() # 统一转换为大写 - - -def send_request(method, url, params, headers, data, json_data, encoding, encryption_enabled): - try: - # 使用进度条组件 - show_progress_bar() - - # 添加加密头信息 - if encryption_enabled: - headers["Encryption"] = "True" - else: - headers["Encryption"] = "False" - - response = requests.request( - method, url, params=params, headers=headers, data=data, json=json_data - ) - - return response - except Exception as e: - logging.error(f"Request failed: {e}") - raise - - -def update_api_key(): - """更新 API Key 到 session_state""" - st.session_state.api_key = st.session_state.api_key_input - - -def generate_encryption_key(): - """生成新的加密密钥并保存到 session_state""" - key = Fernet.generate_key() - st.session_state.encryption_key = key.decode() - st.success("新的加密密钥已生成!") - - def run_ui(): st.title("HTTP 请求模拟工具") @@ -123,9 +38,10 @@ def run_ui(): # 参数输入区域 st.header("参数") - query_params = get_params("查询", "query_key", "query_value") - form_params = get_params("表单", "form_key", "form_value") + query_params = ParamInput("查询", "query_key", "query_value") + form_params = ParamInput("表单", "form_key", "form_value") + # JSON 参数 json_data = None with st.expander("JSON 参数"): json_str = st.text_area("输入 JSON 字符串", key="json_data") @@ -137,36 +53,23 @@ def run_ui(): # Header 输入区域 st.header("Header") - headers = get_params("Header", "header_key", "header_value") + headers = ParamInput("Header", "header_key", "header_value") - # 使用 secrets 存储 API Key - if "api_key" not in st.session_state: - st.session_state.api_key = "" - st.session_state.api_key_input = st.text_input( - "输入 API Key", key="api_key", value=st.session_state.api_key, on_change=update_api_key - ) - headers["x-api-key"] = st.session_state.api_key + # API Key 输入 + api_key = st.text_input("输入 API Key", key="api_key") + headers["x-api-key"] = api_key # Data 输入区域 st.header("Data") data = st.text_area("输入 Data", key="data_info") # 编码选择 - encoding = st.selectbox("选择编码", COMMON_ENCODINGS, key="encoding") + encoding = st.selectbox("选择编码", ["utf-8", "gbk", "latin-1"], key="encoding") # 加密选项区域 st.header("加密选项") encryption_enabled = st.checkbox("开启加密", key="encryption_enabled") - # 自动生成密钥按钮 - if st.button("生成新的加密密钥"): - generate_encryption_key() - - # 显示加密密钥 - if "encryption_key" in st.session_state: - st.write("**加密密钥:**", st.session_state.encryption_key) - st.info("请妥善保管此密钥,不要将其分享给他人!") - # 发送请求按钮 if st.button("发送请求"): try: @@ -182,36 +85,35 @@ def run_ui(): ) logging.info(f"Request data: {request_data}") - response = send_request( - method=request_data.method, - url=request_data.url, - params=request_data.params, - headers=request_data.headers, - data=request_data.data, - json_data=request_data.json_data, - encoding=request_data.encoding, - encryption_enabled=encryption_enabled, - ) - - # 自动检测响应编码 - detected_encoding = chardet.detect(response.content)["encoding"] - logging.info(f"Detected response encoding: {detected_encoding}") - response_text = response.content.decode( - detected_encoding, errors="replace" - ) + # 发送请求 + with st.spinner("发送请求中..."): + response = send_http_request( + method=request_data.method, + url=request_data.url, + params=request_data.params, + headers=request_data.headers, + data=request_data.data, + json_data=request_data.json_data, + encoding=request_data.encoding, + encryption_enabled=encryption_enabled + ) + + # 处理响应 + if encryption_enabled: + response.text = decrypt_data(response.content).decode(response.encoding) + response_data = HTTPResponseSchema.from_attributes(response) # 展示结果 st.header("请求结果") - st.write(f"状态码: {response.status_code}") - st.write(f"响应时间: {response.elapsed.total_seconds()} 秒") + st.write(f"状态码: {response_data.status_code}") + st.write(f"响应时间: {response_data.elapsed:.2f} 秒") st.write("响应 Header:") - st.json(dict(response.headers)) + st.json(response_data.headers) st.write("响应内容:") - st.text(response_text) + st.text(response_data.text) except Exception as e: st.error(f"请求失败: {str(e)}") - if __name__ == "__main__": run_ui() \ No newline at end of file diff --git "a/\351\241\271\347\233\256\346\236\266\346\236\204.txt" "b/\351\241\271\347\233\256\346\236\266\346\236\204.txt" index 5954e3f..dcda67a 100644 --- "a/\351\241\271\347\233\256\346\236\266\346\236\204.txt" +++ "b/\351\241\271\347\233\256\346\236\266\346\236\204.txt" @@ -11,10 +11,10 @@ SwiftAPI-Connect/ │ │ │ ├── http_mock.py # HTTP Mock 路由 │ │ ├── utils/ # 工具模块 │ │ │ ├── __init__.py # 初始化工具模块 -│ │ │ ├── request_helper.py # 请求辅助工具 -│ │ │ ├── encoding_helper.py # 编码辅助工具 -│ │ │ ├── process_monitor.py # 进程监控工具 -│ │ │ ├── crypto.py # 加密工具 +│ │ │ ├── network_utils.py # 网络工具 +│ │ │ ├── encoding_utils.py # 编码工具 +│ │ │ ├── process_utils.py # 进程工具 +│ │ │ ├── crypto_utils.py # 加密工具 │ │ ├── schemas/ # 模式定义模块 │ │ │ ├── __init__.py # 初始化模式模块 │ │ │ ├── request_schema.py # 请求模式定义 @@ -31,9 +31,9 @@ SwiftAPI-Connect/ │ │ ├── notification.py # 通知服务 ├── ui/ │ ├── __init__.py # 初始化UI模块 -│ ├── main_ui.py # UI 应用入口(原 app.py) +│ ├── main_ui.py # UI 应用入口 │ ├── components/ # UI组件 │ │ ├── __init__.py # 初始化组件模块 │ │ ├── progress_bar.py # 进度条组件 -│ │ ├── request_form.py # 请求表单组件 -└── requirements.txt # 依赖包列表 +│ │ ├── param_input.py # 参数输入组件 +└── requirements.txt # 依赖包列表 \ No newline at end of file From 470ec498420326fbe59cc1bec1d0e4b5dec1837f Mon Sep 17 00:00:00 2001 From: huai zhu <138862916+riceshowerX@users.noreply.github.com> Date: Wed, 29 May 2024 09:14:22 +0800 Subject: [PATCH 2/4] 1.1.8 --- .github/ISSUE_TEMPLATE/bug_report.md | 38 ++++ .github/ISSUE_TEMPLATE/custom.md | 10 + .github/ISSUE_TEMPLATE/feature_request.md | 20 ++ .github/workflows/apisec-scan.yml | 71 +++++++ .github/workflows/bandit.yml | 52 +++++ SECURITY.md | 36 ++++ __pycache__/fastapi_server.cpython-312.pyc | Bin 2808 -> 2752 bytes app/__pycache__/main.cpython-312.pyc | Bin 865 -> 865 bytes app/core/config/config.py | 12 +- .../__pycache__/http_errors.cpython-312.pyc | Bin 1257 -> 1017 bytes app/core/errors/http_errors.py | 12 +- .../__pycache__/http_mock.cpython-312.pyc | Bin 3940 -> 2229 bytes app/core/schemas/request_schema.py | 2 +- app/core/schemas/response_schema.py | 27 +-- .../request_helper.cpython-312.pyc | Bin 4178 -> 3783 bytes app/core/utils/crypto.py | 28 +++ app/core/utils/crypto_utils.py | 26 --- app/core/utils/encoding_helper.py | 47 ++--- app/core/utils/process_monitor.py | 20 ++ app/core/utils/process_utils.py | 36 ---- .../{network_utils.py => request_helper.py} | 15 +- fastapi_server.py | 6 +- run.py | 57 ++---- ui/components/param_input.py | 30 --- ui/components/progress_bar.py | 11 +- ui/components/request_form.py | 21 ++ ui/main_ui.py | 182 ++++++++++++++---- ...1\347\233\256\346\236\266\346\236\204.txt" | 14 +- 28 files changed, 525 insertions(+), 248 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/apisec-scan.yml create mode 100644 .github/workflows/bandit.yml create mode 100644 SECURITY.md create mode 100644 app/core/utils/crypto.py delete mode 100644 app/core/utils/crypto_utils.py create mode 100644 app/core/utils/process_monitor.py delete mode 100644 app/core/utils/process_utils.py rename app/core/utils/{network_utils.py => request_helper.py} (79%) delete mode 100644 ui/components/param_input.py create mode 100644 ui/components/request_form.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000..48d5f81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000..c4cd416 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,71 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# APIsec addresses the critical need to secure APIs before they reach production. +# APIsec provides the industry’s only automated and continuous API testing platform that uncovers security vulnerabilities and logic flaws in APIs. +# Clients rely on APIsec to evaluate every update and release, ensuring that no APIs go to production with vulnerabilities. + +# How to Get Started with APIsec.ai +# 1. Schedule a demo at https://www.apisec.ai/request-a-demo . +# +# 2. Register your account at https://cloud.apisec.ai/#/signup . +# +# 3. Register your API . See the video (https://www.youtube.com/watch?v=MK3Xo9Dbvac) to get up and running with APIsec quickly. +# +# 4. Get GitHub Actions scan attributes from APIsec Project -> Configurations -> Integrations -> CI-CD -> GitHub Actions +# +# apisec-run-scan +# +# This action triggers the on-demand scans for projects registered in APIsec. +# If your GitHub account allows code scanning alerts, you can then upload the sarif file generated by this action to show the scan findings. +# Else you can view the scan results from the project home page in APIsec Platform. +# The link to view the scan results is also displayed on the console on successful completion of action. + +# This is a starter workflow to help you get started with APIsec-Scan Actions + +name: APIsec + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "main" branch + # Customize trigger events based on your DevSecOps processes. + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '21 19 * * 1' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + + +permissions: + contents: read + +jobs: + + Trigger_APIsec_scan: + permissions: + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + + steps: + - name: APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + # The APIsec username with which the scans will be executed + apisec-username: ${{ secrets.apisec_username }} + # The Password of the APIsec user with which the scans will be executed + apisec-password: ${{ secrets.apisec_password}} + # The name of the project for security scan + apisec-project: "VAmPI" + # The name of the sarif format result file The file is written only if this property is provided. + sarif-result-file: "apisec-results.sarif" + - name: Import results + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: ./apisec-results.sarif diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml new file mode 100644 index 0000000..82e5a88 --- /dev/null +++ b/.github/workflows/bandit.yml @@ -0,0 +1,52 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# Bandit is a security linter designed to find common security issues in Python code. +# This action will run Bandit on your codebase. +# The results of the scan will be found under the Security tab of your repository. + +# https://github.com/marketplace/actions/bandit-scan is ISC licensed, by abirismyname +# https://pypi.org/project/bandit/ is Apache v2.0 licensed, by PyCQA + +name: Bandit +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + schedule: + - cron: '44 12 * * 3' + +jobs: + bandit: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Bandit Scan + uses: shundor/python-bandit-scan@9cc5aa4a006482b8a7f91134412df6772dbda22c + with: # optional arguments + # exit with 0, even with results found + exit_zero: true # optional, default is DEFAULT + # Github token of the repository (automatically created by Github) + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information. + # File or directory to run bandit on + # path: # optional, default is . + # Report only issues of a given severity level or higher. Can be LOW, MEDIUM or HIGH. Default is UNDEFINED (everything) + # level: # optional, default is UNDEFINED + # Report only issues of a given confidence level or higher. Can be LOW, MEDIUM or HIGH. Default is UNDEFINED (everything) + # confidence: # optional, default is UNDEFINED + # comma-separated list of paths (glob patterns supported) to exclude from scan (note that these are in addition to the excluded paths provided in the config file) (default: .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg) + # excluded_paths: # optional, default is DEFAULT + # comma-separated list of test IDs to skip + # skips: # optional, default is DEFAULT + # path to a .bandit file that supplies command line arguments + # ini_path: # optional, default is DEFAULT + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..23885ce --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,36 @@ +# 安全策略 + +## 支持的版本 + +使用此部分告诉用户项目当前支持的版本以及安全更新情况。 + +| 版本 | 支持状态 | +| ------ | ------------------ | +| 5.1.x | :white_check_mark: | +| 5.0.x | :x: | +| 4.0.x | :white_check_mark: | +| 小于 4.0| :x: | + +## 漏洞报告 + +使用此部分告诉用户如何报告漏洞。 + +### 报告流程 + +1. **报告位置**:漏洞应通过我们的专用电子邮件地址报告:[huaizhu@miksz.cc](mailto:huaizhu@miksz.cc)。请在主题中使用“漏洞报告:[SwiftAPI-Connect]”。 + +2. **报告所需信息**: + - 漏洞的详细描述。 + - 重现问题的步骤。 + - 可能的影响和严重性。 + - 如果已知,任何潜在的修复或缓解步骤。 + +3. **响应时间**: + - 初始确认:您可以在提交后的24小时内收到对您报告的初步确认。 + - 进一步更新:漏洞的处理状态将每5个工作日提供一次定期更新。 + +### 漏洞处理 + +1. **评估**:我们的安全团队将评估每个报告的漏洞,确定其有效性和严重性。 +2. **通知**: + - 如果接受了漏洞报告,我们将通知您计划的修复措施和预计的修复时间。 diff --git a/__pycache__/fastapi_server.cpython-312.pyc b/__pycache__/fastapi_server.cpython-312.pyc index 8cea056da8cbf98bf0fdd82ec4ba4430b11bf603..35fb66b4a47a2740aad651e48e9db9278d0fd059 100644 GIT binary patch delta 629 zcmZWm&ubGw6rP#g?9XgAq1i392#ts}B0-vp#UB+BrJ5Q%72L~q%}(lWvp<|{i-n2^ z6}*P|DUO36e@xIcBbS%Wy@vC;E9jRKPAK)!p z@Y1vjhxW59OI`e;h0`oZ;=vSJfws!jL;23*2X5ou6T(eYMUtn_g_pT$M#_UDVG}`} zx{}RsTUvt^`c>*dmAdkcX`8EcvK6~+ubX*kKo;4}?BKn89l}}qN4Zv1*zj891u>CX ziqvRBtuz&`GkKNM2OETYA(uE@=6RHKhmxOUINm1w$A|h;uN$=Pj*I7b9k)mp z=|{D>^bQ;`0tmPCPq`T-{#FMslLn*wzq+3o910M^-=O{-)DOVIFED=qmg$Oi8#d`% a&9S3zR~tXX)t{pI=FwpVh-b({Ci)M4l!7Y& delta 746 zcmZuvOK;Oa5Z-m1*m2@G59PsAN-u4S2%@Ec%B!fnTGF(DRH;4?OucF2I(9Tpk$OPA z6bW&QeBvK~(p$tGapeM1PeD0wK-^FzE(k8{B(10vR`SezGvCg9>)rS1UupHDsw#x9 zAFNb;&{B`lm#xuaM54qbrfAFcaO+8CyB^bH{fuO5y2dM#9oOT1pU@NB%XYG!(o>WO zgr(ZpkFP$`tg!()YMEw@AH{=sH{7O!h|f}(P!-I`j;LqM?dT5Cvn*$-8@w4T^C5~g zG2DO!ShQ!LsR2KhcK z`|l5INn27O>A#`ZMxyhN7RwKr*Ki$=6=*kLG#cC`*PG2og#ozG`9tr~3|>c9y16u0 zxqo{FP;Nm4)n5rb59T;XcfJHRgj^I^;#~R8VnOK2>xN_63_OVXk3-9J#D5oRQN_P2 zUCCyV7=cZvQZ>A$(Wt=`&z$VMmZm8^?eEE##u5jWA{?mmK*C)zS6HkYH3wu4;hcXW zoG*v5qG4s#1;&k2t*rryhBIi+VqI{(Zpd_-%y|Y8Ohldzap5MPD&ft&pRrSvbbfYEy!BPoUM%eAiFh1JeBWPc{+HkY diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc index 20bf33a626ae534080afd69856efa3442b6c96e9..c561157b715a2ccde582ecd5b01161e32b5c3ad0 100644 GIT binary patch delta 19 ZcmaFJ_K=P1G%qg~0}vRxZsZDQ1^_eo1XBP2 delta 19 ZcmaFJ_K=P1G%qg~0}!aCZR84P1^_f$1Ze;O diff --git a/app/core/config/config.py b/app/core/config/config.py index 133fe3d..5352933 100644 --- a/app/core/config/config.py +++ b/app/core/config/config.py @@ -1,15 +1,11 @@ # config.py -from pydantic import BaseSettings, AnyHttpUrl -from typing import List +from pydantic import BaseSettings import os class Settings(BaseSettings): - SERVER_HOST: str = "127.0.0.1" - SERVER_PORT: int = 8015 - CORS_ORIGINS: List[AnyHttpUrl] = ["http://localhost:8501"] + SERVER_HOST: str = os.getenv("SERVER_HOST", "127.0.0.1") + SERVER_PORT: int = int(os.getenv("SERVER_PORT", 8015)) + CORS_ORIGINS: list = os.getenv("CORS_ORIGINS", "http://localhost:8501").split(',') API_KEY: str = os.getenv("API_KEY") - class Config: - env_file = '.env' - settings = Settings() \ No newline at end of file diff --git a/app/core/errors/__pycache__/http_errors.cpython-312.pyc b/app/core/errors/__pycache__/http_errors.cpython-312.pyc index b0511ea4aa4902836097a79ef516c10ebf861d1a..353d42e96f8cd1da4212dbf0e3a4e8f22ef60fad 100644 GIT binary patch delta 625 zcmaFK`IDXRG%qg~0}wR1`J`Q7oXBU%&%Bx$B*4HB#gf93$~ZC1jah+#Vd6qbt`wFQ zmMFGLR!z2v=iI}XfJ#6rKLceMrZdzq)-c2aSrAae1mrQoc$EyAjDDI-MeIO;mTx3_xmw@D~sT(F9U|i#fF-IT_?t7+?i5z~1%%GNv<>z&%;Rn1!rP zAF3gR5h%d|(~Y1r8P_nahH3}~2FT123s@u*Z*jW9OoD_J$e1D#5CIRwUmP~M z`6;D2sdhy&KnalNixq&x2WCb_#^((BUzk`KWhZccVE|Ge_*fa)R>*?bKr4WX0JqS2 A&j0`b literal 1257 zcmaJ>O>7fK6rR~1+ez$}1_1$4WmPV$R46p4LJ<_A6ap0_2*aW7N~7)WSlMd5Yi4F4 zL9P@b^OHj*I3S>^)Es)?Cqi%w5{LH0feV#DYDXN9iUqwH#ZpmDeY3U`$f57>=Dm6Q z=KXx1{kTx*MIh(ON6Nc3g#K1eSImyF_yri(kdJ&Eq6#kIidNDv($E<4wL0>3itp&{ zZ%evwP@}B*X5A>6(-}Maj|$kDEF3y<;@E*Hk5)wxM>79)abo;9<<&Ui)N3cVwb5}F zKLz6=)TO9|eN@tX46kd{fO=-xxP;2sH|qIP<`SC1rEC$|rtC?JFc!099b~`0c)M}; zr{KuY4qX2=PLfb3(W( zYFXCI(u6ncoaB^o=kuMrcI|M!ioK*lBf*`qKpaZGbBbqzvKTpbc-v?kMbs0HTdg`? z%&0@v7dP9#f+AA4}E2a%9E7c4C*3!U*i8ip`GW(8Fe zgWAtN0L?O8rKabffO=a;e;I>K1DhjwZnTNid2j8%>G>wr?89!7)ajJxbufTSgw*C! z-bKkTO9O)bX0oCX)P5=M^ diff --git a/app/core/errors/http_errors.py b/app/core/errors/http_errors.py index e9aa8d6..551db9d 100644 --- a/app/core/errors/http_errors.py +++ b/app/core/errors/http_errors.py @@ -1,15 +1,15 @@ # http_errors.py from fastapi import HTTPException, JSONResponse - -class HTTPError(HTTPException): +class HTTPError(Exception): """自定义 HTTP 异常""" def __init__(self, status_code: int, detail: str): - super().__init__(status_code=status_code, detail=detail) + self.status_code = status_code + self.detail = detail -def http_error_handler(request, exc: Exception): +def http_exception_handler(request, exc: HTTPException): """全局 HTTP 异常处理""" return JSONResponse( - status_code=500 if not isinstance(exc, HTTPException) else exc.status_code, - content={"detail": exc.detail if isinstance(exc, HTTPException) else "Internal Server Error"}, + status_code=exc.status_code, + content={"detail": exc.detail}, ) \ No newline at end of file diff --git a/app/core/routers/__pycache__/http_mock.cpython-312.pyc b/app/core/routers/__pycache__/http_mock.cpython-312.pyc index f1333a23baa7b91d2838ed56f87554b4a3513cd4..f2dd0d93126e6a2069afd1e8d32bf9cc0d535268 100644 GIT binary patch delta 1118 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 H8WHI~da;!6 diff --git a/app/core/schemas/request_schema.py b/app/core/schemas/request_schema.py index a649f08..c33c0d5 100644 --- a/app/core/schemas/request_schema.py +++ b/app/core/schemas/request_schema.py @@ -17,7 +17,7 @@ class HTTPRequestSchema(BaseModel): method: str = Field(..., description="HTTP 方法", example="GET") url: AnyUrl = Field(..., description="请求 URL", example="https://example.com") params: Optional[Dict[str, str]] = Field({}, description="查询参数", example={"key1": "value1", "key2": "value2"}) - headers: Optional[Dict[str, str]] = Field({}, description="请求头", example={"User-Agent": "Mozilla/5.0"}) + headers: Optional[Dict[str, Union[str, List[str]]]] = Field({}, description="请求头", example={"User-Agent": "Mozilla/5.0"}) data: Optional[Union[str, Dict]] = Field(None, description="请求体数据") json_data: Optional[Dict] = Field(None, description="JSON 请求体数据") encoding: Optional[str] = Field("utf-8", description="请求体的编码", example="utf-8") diff --git a/app/core/schemas/response_schema.py b/app/core/schemas/response_schema.py index 2f377c2..057c459 100644 --- a/app/core/schemas/response_schema.py +++ b/app/core/schemas/response_schema.py @@ -17,7 +17,7 @@ class HTTPResponseSchema(BaseModel): """ status_code: int = Field(..., description="响应状态码", example=200) - text: str = Field(..., description="响应正文内容") + text: Optional[Union[str, bytes]] = Field(None, description="响应正文内容") headers: Dict[str, Union[str, List[str]]] = Field(..., description="响应头信息") elapsed: float = Field(..., description="响应时间(秒)", example=0.5) encoding: Optional[str] = Field("utf-8", description="响应正文的编码格式", example="utf-8") @@ -36,20 +36,25 @@ def status_code_must_be_valid(cls, value): return value def to_dict(self): - return self.dict() + return { + "status_code": self.status_code, + "text": self.text, + "headers": self.headers, + "elapsed": self.elapsed, + "encoding": self.encoding, + "content_type": self.content_type, + } @classmethod def from_attributes(cls, response: httpx.Response): """ 从 httpx.Response 对象创建 HTTPResponseSchema 对象 """ - return cls.parse_obj( - { - "status_code": response.status_code, - "text": response.text, - "headers": response.headers, - "elapsed": response.elapsed.total_seconds(), - "encoding": response.encoding, - "content_type": response.headers.get('content-type'), - } + return cls( + status_code=response.status_code, + text=response.text, + headers=response.headers, + elapsed=response.elapsed.total_seconds(), + encoding=response.encoding, + content_type=response.headers.get('content-type'), ) \ No newline at end of file diff --git a/app/core/utils/__pycache__/request_helper.cpython-312.pyc b/app/core/utils/__pycache__/request_helper.cpython-312.pyc index a6fef2901ad0a77d76a680c213f694e5b08302d9..0e9b3443bc1db9c125387e49e324f336459c1ef2 100644 GIT binary patch delta 1514 zcmaJ>T}&KR6ux(6?)>i#Fzf;amZdC(El?#`MOa8sw{;uR*joFbrlvB3LR#9+4k_wD z6^(|dE#xLjQmZuj&;%c!oye?|x+>T#A33sM~u^LBY=oTY@cH1cqo6o3~|K?&Zp0 z3mcZS4JQL9o6|m(Bq23}Z^Z|KUdkZIxiM8GI8T?#{ zv?)dd`(qgQ{}AXUoW?T!4L6or#FH+fOX4(M55p4>0f6>RP;@{or;&f`77BbNAu z=tuG_5wD%iB*6N zq7DgcX94rl7ldM>fv*TqTmU0NKNTvmLZ^jf$*!l+CwF0}HOLK=QGb-)ArYf*Slv`k zXUHS#%5G_8$CTKPoY-@s54Jj0pp@h7p`IMJvpN8JM9Uto1B2C- zErTa@7d?Czt)@^x_Ui6@{=|}zxQ969ReChE zuTX#G(eFm_O{03rsAg;@{Vw!`zNmKqUCM|QT@CF#x2U&2@(G}|2kn8v3RCzydmfq? zEzh)IcyoF2OCGGtI1}T)ZcD4&^-7!;#p|(WO#m~^Iyh%Ka9S2;Iz_N&MGX9`jG3*P znQ}5)*_GIj$sCRXm@DZb&sUK7icTHSeEptGlq^IE*bC)6NG!y}%;VBR0%z*^E!QWe5G9k(W1rufO$iGih1tuFyh zZyUvjnOjAgIo>kXQjL}9!OE8fHA4qudkq}d%7}J_6bwZ*KKJehfa!I)P;5oAlyNzf(yXVaJ zU(Wyi|2gx|_w_$D1+FTJ1jyJv)Iap5G#!Y*AE(!EpTGcfBmPnGfH*20kRTAtepto4 zCS!q2pT=vfCTsE#vx)?v6Z@Z_6N!#EN+0n^6C3c(c5lurh7+@=*v+g& zHV$$j5=m(xhI+5Y($hN{a?pgEjfRgr4L-ajxU|dnp)O$I8!@h>OuR07>m)a&;@Gt>zXl^%Nm-4UMlY1 ztLr)45%(TSYh&hcE*n*75<7I-DNfa+&L#RmEt@(xqG7#)8ie%7Fk#VKslaN7(hk%N z+GtJoe#w~FOc3pABTd@y2DtYYxU&mXgs!Pgi)z!^@RGXqE@r z{-IW1Q=1pnW@^|6Z3AsnFUA*8f>q%3gvJ<+1=dBsi>Sc6s33qq%@m%A&$2U})7eF3ZNcXn>{Ot7py(n2 zIXD%cY%9n`8F{i&lmj$`HaS>Oi*glEiT?~c)j63h1dF str: + """加密数据""" + return fernet.encrypt(data.encode()).decode() + + +def decrypt_data(data: str) -> str: + """解密数据""" + return fernet.decrypt(data.encode()).decode() \ No newline at end of file diff --git a/app/core/utils/crypto_utils.py b/app/core/utils/crypto_utils.py deleted file mode 100644 index db74254..0000000 --- a/app/core/utils/crypto_utils.py +++ /dev/null @@ -1,26 +0,0 @@ -# crypto_utils.py -from cryptography.fernet import Fernet -import os -from dotenv import load_dotenv - -# 加载环境变量 -load_dotenv() - -# 从环境变量中获取加密密钥 -ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY") - -# 如果没有设置加密密钥,则抛出异常 -if ENCRYPTION_KEY is None: - raise ValueError("ENCRYPTION_KEY environment variable is not set.") - -# 创建 Fernet 对象 -fernet = Fernet(ENCRYPTION_KEY) - -def encrypt_data(data: bytes) -> bytes: - """加密数据""" - return fernet.encrypt(data) - - -def decrypt_data(data: bytes) -> bytes: - """解密数据""" - return fernet.decrypt(data) \ No newline at end of file diff --git a/app/core/utils/encoding_helper.py b/app/core/utils/encoding_helper.py index 82673d7..0480598 100644 --- a/app/core/utils/encoding_helper.py +++ b/app/core/utils/encoding_helper.py @@ -4,7 +4,7 @@ DEFAULT_ENCODING = "utf-8" # 设置默认编码 -def decode_content(content: bytes, encoding: str = None, encoding_list: list = None) -> str: +def decode_content(content: bytes, encoding: str = None) -> str: """ 解码字节内容,优先使用默认编码,如果解码失败,则尝试使用 chardet 检测编码。 @@ -12,31 +12,22 @@ def decode_content(content: bytes, encoding: str = None, encoding_list: list = N :param encoding: 指定的编码格式,如果为空,则使用默认编码 :return: 解码后的字符串 """ - if encoding: - try: - logging.debug(f"Attempting to decode content with encoding: {encoding}") - return content.decode(encoding) - except UnicodeDecodeError as e: - logging.warning(f"UnicodeDecodeError: {e}. Using default encoding ({DEFAULT_ENCODING}) and ignoring errors.") - return content.decode(DEFAULT_ENCODING, errors="ignore") - - if encoding_list: - for enc in encoding_list: - try: - logging.debug(f"Attempting to decode content with encoding: {enc}") - return content.decode(enc) - except UnicodeDecodeError: - continue - - logging.warning(f"Trying to detect encoding with chardet.") - detected_encoding = chardet.detect(content)['encoding'] - if detected_encoding: - try: + if encoding is None: + encoding = DEFAULT_ENCODING + + try: + logging.debug(f"Attempting to decode content with encoding: {encoding}") + return content.decode(encoding) + except UnicodeDecodeError as e: + logging.warning(f"UnicodeDecodeError: {e}. Trying to detect encoding with chardet.") + detected_encoding = chardet.detect(content)['encoding'] + if detected_encoding is not None: logging.debug(f"Detected encoding: {detected_encoding}") - return content.decode(detected_encoding) - except UnicodeDecodeError: - logging.warning(f"Failed to decode content with detected encoding. Using default encoding ({DEFAULT_ENCODING}) and ignoring errors.") - return content.decode(DEFAULT_ENCODING, errors="ignore") - else: - logging.warning(f"Failed to detect encoding. Using default encoding ({DEFAULT_ENCODING}) and ignoring errors.") - return content.decode(DEFAULT_ENCODING, errors="ignore") \ No newline at end of file + try: + return content.decode(detected_encoding) + except UnicodeDecodeError as e: + logging.error(f"Failed to decode content with detected encoding: {e}. Returning original bytes.") + return content.decode(DEFAULT_ENCODING, errors='replace') + else: + logging.warning(f"Failed to detect encoding. Using default encoding ({DEFAULT_ENCODING}) and ignoring errors.") + return content.decode(DEFAULT_ENCODING, errors="ignore") \ No newline at end of file diff --git a/app/core/utils/process_monitor.py b/app/core/utils/process_monitor.py new file mode 100644 index 0000000..6ec969c --- /dev/null +++ b/app/core/utils/process_monitor.py @@ -0,0 +1,20 @@ +# process_monitor.py +import psutil +import time +import logging + +class ProcessMonitor: + """监控进程状态""" + + def __init__(self, process_id: int, process_name: str): + self.process_id = process_id + self.process_name = process_name + + def monitor(self): + while True: + process = psutil.Process(self.process_id) + cpu_percent = process.cpu_percent() + memory_percent = process.memory_percent() + logging.info(f"{self.process_name} CPU Usage: {cpu_percent}%") + logging.info(f"{self.process_name} Memory Usage: {memory_percent}%") + time.sleep(1) \ No newline at end of file diff --git a/app/core/utils/process_utils.py b/app/core/utils/process_utils.py deleted file mode 100644 index c84eb6b..0000000 --- a/app/core/utils/process_utils.py +++ /dev/null @@ -1,36 +0,0 @@ -# process_utils.py -import psutil -import time -import logging -from multiprocessing import Process - -class ProcessMonitor: - """监控进程状态""" - - def __init__(self, process: psutil.Process, process_name: str, interval: int = 1): - self.process = process - self.process_name = process_name - self.interval = interval - - def monitor(self): - while True: - try: - cpu_percent = self.process.cpu_percent() - memory_percent = self.process.memory_percent() - logging.info(f"{self.process_name} - CPU Usage: {cpu_percent:.2f}%, Memory Usage: {memory_percent:.2f}%") - except psutil.NoSuchProcess: - logging.warning(f"{self.process_name} process not found, stopping monitor.") - break - except Exception as e: - logging.error(f"An error occurred while monitoring {self.process_name}: {e}") - time.sleep(self.interval) - - def start(self): - self.process = Process(target=self.monitor) - self.process.start() - logging.info(f"Started monitoring process {self.process_name} with PID: {self.process.pid}") - - def terminate(self): - if self.process: - self.process.terminate() - logging.info(f"Stopped monitoring process {self.process_name}") \ No newline at end of file diff --git a/app/core/utils/network_utils.py b/app/core/utils/request_helper.py similarity index 79% rename from app/core/utils/network_utils.py rename to app/core/utils/request_helper.py index b49f8f0..77cd893 100644 --- a/app/core/utils/network_utils.py +++ b/app/core/utils/request_helper.py @@ -1,9 +1,8 @@ -# network_utils.py +# request_helper.py import httpx import logging from typing import Dict, Any, Optional from app.core.errors.http_errors import HTTPError -from app.core.utils.crypto_utils import encrypt_data, decrypt_data async def send_http_request( method: str, @@ -13,7 +12,6 @@ async def send_http_request( data: Optional[Any] = None, json: Optional[Any] = None, timeout: Optional[float] = None, - encryption_enabled=False, **kwargs: Dict[str, Any] ) -> httpx.Response: """ @@ -38,10 +36,7 @@ async def send_http_request( async with httpx.AsyncClient(timeout=timeout) as client: try: - logging.info(f"Sending {method} request to {url}") - if encryption_enabled: - data = encrypt_data(data.encode()) if isinstance(data, str) else data - json = encrypt_data(json.encode()) if isinstance(json, str) else json + logging.info(f"Sending {method} request to {url} with params: {kwargs}") response = await client.request( method=method, url=url, @@ -62,12 +57,12 @@ async def send_http_request( except httpx.HTTPStatusError as exc: logging.error(f"HTTP error occurred while requesting {exc.request.url!r}: {exc}") - raise HTTPError(status_code=exc.response.status_code, detail=exc.response.text) from exc + raise HTTPError(status_code=exc.response.status_code, detail=exc.response.text) except httpx.RequestError as exc: logging.error(f"An error occurred while requesting {exc.request.url!r}: {exc}") - raise HTTPError(status_code=500, detail=str(exc)) from exc + raise HTTPError(status_code=500, detail=str(exc)) except Exception as e: logging.error(f"An unexpected error occurred: {e}") - raise HTTPError(status_code=500, detail=str(e)) from e \ No newline at end of file + raise HTTPError(status_code=500, detail=str(e)) \ No newline at end of file diff --git a/fastapi_server.py b/fastapi_server.py index 49ed6f1..ca1f5bf 100644 --- a/fastapi_server.py +++ b/fastapi_server.py @@ -1,10 +1,9 @@ # fastapi_server.py from fastapi import FastAPI, Request, HTTPException from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import JSONResponse from app.main import app as fastapi_app -from app.core.errors.http_errors import http_error_handler +from app.core.errors.http_errors import http_exception_handler from app.core.config import settings import logging import uvicorn @@ -63,6 +62,7 @@ def run_fastapi(): except Exception as e: logging.error(f"An error occurred while running the FastAPI server: {e}") + if __name__ == "__main__": # 添加 CORS 中间件 (全局配置) fastapi_app.add_middleware( @@ -77,6 +77,6 @@ def run_fastapi(): fastapi_app.middleware("http")(api_key_auth) # 添加全局异常处理 - fastapi_app.add_exception_handler(Exception, http_error_handler) + fastapi_app.add_exception_handler(HTTPException, http_exception_handler) run_fastapi() \ No newline at end of file diff --git a/run.py b/run.py index 235529e..fe67e4c 100644 --- a/run.py +++ b/run.py @@ -1,53 +1,36 @@ # run.py -import os - -def main(): - # 获取项目根目录 - project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - # 设置工作目录 - os.chdir(project_root) - # ... 其余代码 -from multiprocessing import Process +from multiprocessing import Process, Pool from fastapi_server import run_fastapi from ui.main_ui import run_ui -from app.core.utils.process_utils import ProcessMonitor +from app.core.utils.process_monitor import ProcessMonitor import logging # 设置日志级别为 INFO logging.basicConfig(level=logging.INFO) -def main(): +if __name__ == "__main__": try: - # 使用 Process 直接创建和管理进程 - fastapi_process = Process(target=run_fastapi) - streamlit_process = Process(target=run_ui) + # 创建进程池 + pool = Pool(processes=2) + + # 使用进程池运行 FastAPI 服务器和 Streamlit UI 应用 + fastapi_process = pool.apply(run_fastapi) + streamlit_process = pool.apply(run_ui) - fastapi_process.start() - streamlit_process.start() + logging.info(f"Starting FastAPI process with ID: {fastapi_process}") + logging.info(f"Starting Streamlit process with ID: {streamlit_process}") - logging.info(f"Starting FastAPI process with ID: {fastapi_process.pid}") - logging.info(f"Starting Streamlit process with ID: {streamlit_process.pid}") + # 创建两个监控线程 + fastapi_monitor = Process(target=ProcessMonitor(fastapi_process, "FastAPI").monitor) + streamlit_monitor = Process(target=ProcessMonitor(streamlit_process, "Streamlit").monitor) - # 监控进程 - fastapi_monitor = ProcessMonitor(fastapi_process, "FastAPI") - streamlit_monitor = ProcessMonitor(streamlit_process, "Streamlit") + # 启动监控线程 fastapi_monitor.start() streamlit_monitor.start() - # 等待进程结束 - fastapi_process.join() - streamlit_process.join() - - fastapi_monitor.terminate() - streamlit_monitor.terminate() - except KeyboardInterrupt: - logging.info("Received KeyboardInterrupt, terminating processes...") - fastapi_process.terminate() - streamlit_process.terminate() - fastapi_monitor.terminate() - streamlit_monitor.terminate() - except Exception as e: - logging.error(f"An error occurred: {e}") + # 等待两个进程结束 + pool.close() + pool.join() -if __name__ == "__main__": - main() \ No newline at end of file + except Exception as e: + logging.error(f"An error occurred: {e}") \ No newline at end of file diff --git a/ui/components/param_input.py b/ui/components/param_input.py deleted file mode 100644 index 3ec3206..0000000 --- a/ui/components/param_input.py +++ /dev/null @@ -1,30 +0,0 @@ -# param_input.py -import streamlit as st - -def ParamInput(param_type, key_prefix, value_prefix): - params = {} - with st.expander(f"{param_type} 参数"): - param_count = st.number_input( - f"{param_type} 参数数量", min_value=0, step=1, key=f"{param_type}_params_count" - ) - for i in range(param_count): - col1, col2, col3 = st.columns(3) - with col1: - key = st.text_input( - f"{param_type} 参数 {i+1} 的 Key", key=f"{key_prefix}_{i}" - ) - with col2: - param_type = st.selectbox("选择参数类型", ["string", "integer", "float", "boolean"], key=f"param_type_{i}") - with col3: - if param_type == "string": - value = st.text_input(f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}") - elif param_type == "integer": - value = st.number_input(f"{param_type} 参数 {i+1} 的 Value", step=1, key=f"{value_prefix}_{i}") - elif param_type == "float": - value = st.number_input(f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}") - elif param_type == "boolean": - value = st.checkbox(f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}") - else: - value = st.text_input(f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}") - params[key] = value - return params \ No newline at end of file diff --git a/ui/components/progress_bar.py b/ui/components/progress_bar.py index 1873d99..e6528b2 100644 --- a/ui/components/progress_bar.py +++ b/ui/components/progress_bar.py @@ -5,6 +5,11 @@ def show_progress_bar(): """显示进度条""" progress_bar = st.progress(0) - for i in range(101): - time.sleep(0.01) # 模拟耗时操作 - progress_bar.progress(i) \ No newline at end of file + + # 模拟请求处理时间 + for i in range(10): + time.sleep(0.1) + progress_bar.progress(i * 10) + + # 设置进度条为 100% + progress_bar.progress(100) \ No newline at end of file diff --git a/ui/components/request_form.py b/ui/components/request_form.py new file mode 100644 index 0000000..77afd8f --- /dev/null +++ b/ui/components/request_form.py @@ -0,0 +1,21 @@ +# request_form.py +import streamlit as st + +def get_params(param_type, key_prefix, value_prefix): + params = {} + with st.expander(f"{param_type} 参数"): + param_count = st.number_input( + f"{param_type} 参数数量", min_value=0, step=1, key=f"{param_type}_params_count" + ) + for i in range(param_count): + col1, col2 = st.columns(2) + with col1: + key = st.text_input( + f"{param_type} 参数 {i+1} 的 Key", key=f"{key_prefix}_{i}" + ) + with col2: + value = st.text_input( + f"{param_type} 参数 {i+1} 的 Value", key=f"{value_prefix}_{i}" + ) + params[key] = value + return params \ No newline at end of file diff --git a/ui/main_ui.py b/ui/main_ui.py index 3cd75ae..5538d2b 100644 --- a/ui/main_ui.py +++ b/ui/main_ui.py @@ -1,28 +1,113 @@ -# main_ui.py -import os +# app.py import sys +import os +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +import streamlit as st +import requests import json import logging import time import chardet -# 获取项目根目录 -project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -# 将项目根目录添加到 PYTHONPATH -sys.path.append(project_root) +from typing import Dict, Optional, Union +from pydantic import BaseModel, AnyUrl, Field, field_validator +from cryptography.fernet import Fernet -import streamlit as st - -from app.core.utils.network_utils import send_http_request -from app.core.schemas.request_schema import HTTPRequestSchema -from app.core.schemas.response_schema import HTTPResponseSchema -from ui.components.param_input import ParamInput +from ui.components.request_form import get_params from ui.components.progress_bar import show_progress_bar -from app.core.utils.crypto_utils import encrypt_data, decrypt_data # 设置日志级别为 INFO logging.basicConfig(level=logging.INFO) +# 复制 COMMON_ENCODINGS 变量 +COMMON_ENCODINGS = [ + "ascii", + "utf-8", + "utf-16", + "utf-32", + "latin-1", + "gbk", + "gb18030", + "big5", + "shift-jis", + "euc-jp", + "euc-kr", +] + + +class HTTPRequestSchema(BaseModel): + """ + HTTP 请求模式定义 + """ + + method: str = Field(..., description="HTTP 方法", example="GET") + url: AnyUrl = Field(..., description="请求 URL", example="https://example.com") + params: Optional[Dict[str, str]] = Field( + {}, description="查询参数", example={"key1": "value1", "key2": "value2"} + ) + headers: Optional[Dict[str, str]] = Field( + {}, description="请求头", example={"User-Agent": "Mozilla/5.0"} + ) + data: Optional[Union[str, Dict]] = Field(None, description="请求体数据") + json_data: Optional[Dict] = Field(None, description="JSON 请求体数据") + encoding: Optional[str] = Field( + None, description="请求体的编码", example="utf-8" + ) + + @field_validator("method") + def method_must_be_valid(cls, value): + valid_methods = [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + ] + if value.upper() not in valid_methods: + raise ValueError( + f"Invalid HTTP method: {value}. Valid methods are: {valid_methods}" + ) + return value.upper() # 统一转换为大写 + + +def send_request(method, url, params, headers, data, json_data, encoding, encryption_enabled): + try: + # 使用进度条组件 + show_progress_bar() + + # 添加加密头信息 + if encryption_enabled: + headers["Encryption"] = "True" + else: + headers["Encryption"] = "False" + + response = requests.request( + method, url, params=params, headers=headers, data=data, json=json_data + ) + + return response + except Exception as e: + logging.error(f"Request failed: {e}") + raise + + +def update_api_key(): + """更新 API Key 到 session_state""" + st.session_state.api_key = st.session_state.api_key_input + + +def generate_encryption_key(): + """生成新的加密密钥并保存到 session_state""" + key = Fernet.generate_key() + st.session_state.encryption_key = key.decode() + st.success("新的加密密钥已生成!") + + def run_ui(): st.title("HTTP 请求模拟工具") @@ -38,10 +123,9 @@ def run_ui(): # 参数输入区域 st.header("参数") - query_params = ParamInput("查询", "query_key", "query_value") - form_params = ParamInput("表单", "form_key", "form_value") + query_params = get_params("查询", "query_key", "query_value") + form_params = get_params("表单", "form_key", "form_value") - # JSON 参数 json_data = None with st.expander("JSON 参数"): json_str = st.text_area("输入 JSON 字符串", key="json_data") @@ -53,23 +137,36 @@ def run_ui(): # Header 输入区域 st.header("Header") - headers = ParamInput("Header", "header_key", "header_value") + headers = get_params("Header", "header_key", "header_value") - # API Key 输入 - api_key = st.text_input("输入 API Key", key="api_key") - headers["x-api-key"] = api_key + # 使用 secrets 存储 API Key + if "api_key" not in st.session_state: + st.session_state.api_key = "" + st.session_state.api_key_input = st.text_input( + "输入 API Key", key="api_key", value=st.session_state.api_key, on_change=update_api_key + ) + headers["x-api-key"] = st.session_state.api_key # Data 输入区域 st.header("Data") data = st.text_area("输入 Data", key="data_info") # 编码选择 - encoding = st.selectbox("选择编码", ["utf-8", "gbk", "latin-1"], key="encoding") + encoding = st.selectbox("选择编码", COMMON_ENCODINGS, key="encoding") # 加密选项区域 st.header("加密选项") encryption_enabled = st.checkbox("开启加密", key="encryption_enabled") + # 自动生成密钥按钮 + if st.button("生成新的加密密钥"): + generate_encryption_key() + + # 显示加密密钥 + if "encryption_key" in st.session_state: + st.write("**加密密钥:**", st.session_state.encryption_key) + st.info("请妥善保管此密钥,不要将其分享给他人!") + # 发送请求按钮 if st.button("发送请求"): try: @@ -85,35 +182,36 @@ def run_ui(): ) logging.info(f"Request data: {request_data}") - # 发送请求 - with st.spinner("发送请求中..."): - response = send_http_request( - method=request_data.method, - url=request_data.url, - params=request_data.params, - headers=request_data.headers, - data=request_data.data, - json_data=request_data.json_data, - encoding=request_data.encoding, - encryption_enabled=encryption_enabled - ) - - # 处理响应 - if encryption_enabled: - response.text = decrypt_data(response.content).decode(response.encoding) - response_data = HTTPResponseSchema.from_attributes(response) + response = send_request( + method=request_data.method, + url=request_data.url, + params=request_data.params, + headers=request_data.headers, + data=request_data.data, + json_data=request_data.json_data, + encoding=request_data.encoding, + encryption_enabled=encryption_enabled, + ) + + # 自动检测响应编码 + detected_encoding = chardet.detect(response.content)["encoding"] + logging.info(f"Detected response encoding: {detected_encoding}") + response_text = response.content.decode( + detected_encoding, errors="replace" + ) # 展示结果 st.header("请求结果") - st.write(f"状态码: {response_data.status_code}") - st.write(f"响应时间: {response_data.elapsed:.2f} 秒") + st.write(f"状态码: {response.status_code}") + st.write(f"响应时间: {response.elapsed.total_seconds()} 秒") st.write("响应 Header:") - st.json(response_data.headers) + st.json(dict(response.headers)) st.write("响应内容:") - st.text(response_data.text) + st.text(response_text) except Exception as e: st.error(f"请求失败: {str(e)}") + if __name__ == "__main__": run_ui() \ No newline at end of file diff --git "a/\351\241\271\347\233\256\346\236\266\346\236\204.txt" "b/\351\241\271\347\233\256\346\236\266\346\236\204.txt" index dcda67a..5954e3f 100644 --- "a/\351\241\271\347\233\256\346\236\266\346\236\204.txt" +++ "b/\351\241\271\347\233\256\346\236\266\346\236\204.txt" @@ -11,10 +11,10 @@ SwiftAPI-Connect/ │ │ │ ├── http_mock.py # HTTP Mock 路由 │ │ ├── utils/ # 工具模块 │ │ │ ├── __init__.py # 初始化工具模块 -│ │ │ ├── network_utils.py # 网络工具 -│ │ │ ├── encoding_utils.py # 编码工具 -│ │ │ ├── process_utils.py # 进程工具 -│ │ │ ├── crypto_utils.py # 加密工具 +│ │ │ ├── request_helper.py # 请求辅助工具 +│ │ │ ├── encoding_helper.py # 编码辅助工具 +│ │ │ ├── process_monitor.py # 进程监控工具 +│ │ │ ├── crypto.py # 加密工具 │ │ ├── schemas/ # 模式定义模块 │ │ │ ├── __init__.py # 初始化模式模块 │ │ │ ├── request_schema.py # 请求模式定义 @@ -31,9 +31,9 @@ SwiftAPI-Connect/ │ │ ├── notification.py # 通知服务 ├── ui/ │ ├── __init__.py # 初始化UI模块 -│ ├── main_ui.py # UI 应用入口 +│ ├── main_ui.py # UI 应用入口(原 app.py) │ ├── components/ # UI组件 │ │ ├── __init__.py # 初始化组件模块 │ │ ├── progress_bar.py # 进度条组件 -│ │ ├── param_input.py # 参数输入组件 -└── requirements.txt # 依赖包列表 \ No newline at end of file +│ │ ├── request_form.py # 请求表单组件 +└── requirements.txt # 依赖包列表 From d8d8a3fa1c437a4e279159982a32d3b18946765b Mon Sep 17 00:00:00 2001 From: huai zhu <138862916+riceshowerX@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:03:40 +0800 Subject: [PATCH 3/4] 1.1.8 --- __pycache__/fastapi_server.cpython-312.pyc | Bin 2752 -> 2730 bytes app/__pycache__/__init__.cpython-312.pyc | Bin 161 -> 139 bytes app/__pycache__/main.cpython-312.pyc | Bin 865 -> 843 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 168 -> 151 bytes .../__pycache__/http_errors.cpython-312.pyc | Bin 1017 -> 995 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 165 -> 152 bytes .../__pycache__/http_mock.cpython-312.pyc | Bin 2229 -> 3918 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 163 -> 150 bytes .../request_helper.cpython-312.pyc | Bin 3783 -> 3761 bytes 9 files changed, 0 insertions(+), 0 deletions(-) diff --git a/__pycache__/fastapi_server.cpython-312.pyc b/__pycache__/fastapi_server.cpython-312.pyc index 35fb66b4a47a2740aad651e48e9db9278d0fd059..c2783c7d88ffcca0d425b069e6520fde5bdc7dc1 100644 GIT binary patch delta 36 qcmX>gx=NJyG%qg~0}v?ZB&M}&~2`x@7DvmKSG%+!Vami0E%}vcKDUNZ^ MEb-Xf!z{-R0L~{72mk;8 diff --git a/app/__pycache__/__init__.cpython-312.pyc b/app/__pycache__/__init__.cpython-312.pyc index 157ef2841901ad2a1967813aa1638fd39e26b1e2..7952436f487364b06f87b9d11c87ecb310e198d5 100644 GIT binary patch delta 31 lcmZ3;*v-g&nwOW00SJ_H5+`ySb7-Yl#iVDJWK4{41psdT2l4;_ delta 53 zcmeBXT*%0MnwOW00SK6dy(e-T%Q+-l#e^2878S=B8Jd_F#JJ=qm*%GCl@!OgXO?(O HbaMp&T2K!Q diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc index c561157b715a2ccde582ecd5b01161e32b5c3ad0..2c0a763d2806a5f9dad0176558385f8f2b41f260 100644 GIT binary patch delta 35 pcmaFJcAAa%G%qg~0}v?ZB&OMIiEyDbq3l%HR>k diff --git a/app/core/errors/__pycache__/__init__.cpython-312.pyc b/app/core/errors/__pycache__/__init__.cpython-312.pyc index 1759013856f77b26b62e2bbdd3353eefeef18788..09930d2a64e82afb9f15e90524e15bedf0b7218e 100644 GIT binary patch delta 39 tcmZ3%IGvIEG%qg~0}v?ZBu?Zu<}gdKib>Bb$(R@;!IGR`lsd846ac)n3d#Tg delta 76 zcmbQvxPp=UG%qg~0}$Mj@R`VMZ0eqD6%$&VT2vfkWN2by5aW`cT$-DjS5h3~o>}5i eniLaUo|#tS7~rYvoS&DMnp_f-SWqxA$rJ!yrx?=! diff --git a/app/core/errors/__pycache__/http_errors.cpython-312.pyc b/app/core/errors/__pycache__/http_errors.cpython-312.pyc index 353d42e96f8cd1da4212dbf0e3a4e8f22ef60fad..649015ee1859a73ed8a782dadc6c89f13c23eaad 100644 GIT binary patch delta 34 ocmey#{+ONnG%qg~0}v?ZByQvmW#q6-v5HC0EXmkh$5_n-0HKNreE)3?j}b`# diff --git a/app/core/routers/__pycache__/__init__.cpython-312.pyc b/app/core/routers/__pycache__/__init__.cpython-312.pyc index 91c71a319ebdaff2499452d58cdac76dbd8a4cb2..969639b12a803559ca42e5c889ff17ba51bbdff2 100644 GIT binary patch delta 59 zcmZ3=ID?VBb$tX>V2`bN}ZT*1_17|65{{> delta 72 zcmbQixRjCmG%qg~0}wC^dr#yxHgrk0iU}=FEh>&NGBhzUh;d0R&MwI>hzTywOe=8= a@YHqA&&x|qF44_R%*=~PEGU?mWCj4lwiZbM diff --git a/app/core/routers/__pycache__/http_mock.cpython-312.pyc b/app/core/routers/__pycache__/http_mock.cpython-312.pyc index f2dd0d93126e6a2069afd1e8d32bf9cc0d535268..c1935ecf2a413e96a0a360732468e3f67f60bfad 100644 GIT binary patch literal 3918 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 m(SG(jv04Tf$BK-%S4UF9Y delta 1161 zcmZ8f&2Jk;6rb6d{kEOhi3`DP6VoIl1-nUU732nJ6qi;IAtCXll}oTPn_#o+4YL~| zB!{E{sYT*8haUJ)dPIp39Qp@@^oE3t%Ar~siSQxdW=Idv3(Ty&E#gW0d%ySIypNfE z-yFT3Ui&(kGys?Kxw*w_G5|jbBNlE)a_|LLR{;S8I>3b%bR|o2k%ge}Ne*^pOO7~l z6j!y>h+{`{bxV)9>=>?TnGsiAw1h6(BJGq5AUXrpA>9O@iOnTqPTiv5p`W^!{4zT(|F1ratXuAL!QEY^XZk(o(Il6yjQA(W5FaMKc2W1oR6KFGhPg zgnHfg8uQ{8&0N2tyQMpBhKT#GVcb zx%>P=sq&J~C#syBJauZKQuY>BT)PqYmGiaW0{c-qSxUIoH|_cE2GCKSD(d(L9DW0T zt%IMjd3o;AT&r(nlWe}+N`G8@td|RDH<_O;qTOOb@KUinhHi|>oZl!Q zesVk$s*6=Ws4my2*znHX#RbE3*I%Tzzw9-9+o#c6v7O`~RM~!V1Zqr8jY6FjQ~9;% zwzXtt)Kj)4$Eq_pkr} diff --git a/app/core/utils/__pycache__/__init__.cpython-312.pyc b/app/core/utils/__pycache__/__init__.cpython-312.pyc index a3d1b4c9c431bfe9d9e39c9731848f9828430f6c..d9ff3dad334124697c48b47d0a6bbb6629571cff 100644 GIT binary patch delta 59 zcmZ3?IE|6}G%qg~0}v?ZBu?ZuRxwSnib>Bb$tX>V2`bN}ZT*0s!rL659X( delta 72 zcmbQnxR{aqG%qg~0}wC^dr#yxHgrn1iU}=FEh>&NGBhzUh;d0R&MwI>hzTywOe=8= a@YHqA&&x|qF44_R%*=~PEGU?mWC8%gFcv`o diff --git a/app/core/utils/__pycache__/request_helper.cpython-312.pyc b/app/core/utils/__pycache__/request_helper.cpython-312.pyc index 0e9b3443bc1db9c125387e49e324f336459c1ef2..8f39c2aaf85abf78179143ea00608ab714de0edb 100644 GIT binary patch delta 36 qcmX>uyHS?+G%qg~0}v?ZB&Kz3`d0O1u8c>n+a From 2d22c7fab66435cc4892a2b41d0158772f356e4c Mon Sep 17 00:00:00 2001 From: huai zhu <138862916+riceshowerX@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:11:09 +0800 Subject: [PATCH 4/4] 1.1.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复Pylance 无法解析 cryptography.fernet 的导入 --- __pycache__/fastapi_server.cpython-312.pyc | Bin 2730 -> 0 bytes app/__pycache__/__init__.cpython-312.pyc | Bin 139 -> 0 bytes app/__pycache__/main.cpython-312.pyc | Bin 843 -> 0 bytes .../errors/__pycache__/__init__.cpython-312.pyc | Bin 151 -> 0 bytes .../__pycache__/http_errors.cpython-312.pyc | Bin 995 -> 0 bytes .../routers/__pycache__/__init__.cpython-312.pyc | Bin 152 -> 0 bytes .../__pycache__/http_mock.cpython-312.pyc | Bin 3918 -> 0 bytes .../schemas/__pycache__/__init__.cpython-312.pyc | Bin 165 -> 0 bytes .../__pycache__/request_schema.cpython-312.pyc | Bin 2821 -> 0 bytes .../__pycache__/response_schema.cpython-312.pyc | Bin 3388 -> 0 bytes .../utils/__pycache__/__init__.cpython-312.pyc | Bin 150 -> 0 bytes .../utils/__pycache__/crypto.cpython-312.pyc | Bin 2171 -> 0 bytes .../__pycache__/request_helper.cpython-312.pyc | Bin 3761 -> 0 bytes requirements.txt | 3 ++- ui/__pycache__/__init__.cpython-312.pyc | Bin 160 -> 0 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 171 -> 0 bytes .../__pycache__/progress_bar.cpython-312.pyc | Bin 689 -> 0 bytes .../__pycache__/request_form.cpython-312.pyc | Bin 1428 -> 0 bytes 18 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 __pycache__/fastapi_server.cpython-312.pyc delete mode 100644 app/__pycache__/__init__.cpython-312.pyc delete mode 100644 app/__pycache__/main.cpython-312.pyc delete mode 100644 app/core/errors/__pycache__/__init__.cpython-312.pyc delete mode 100644 app/core/errors/__pycache__/http_errors.cpython-312.pyc delete mode 100644 app/core/routers/__pycache__/__init__.cpython-312.pyc delete mode 100644 app/core/routers/__pycache__/http_mock.cpython-312.pyc delete mode 100644 app/core/schemas/__pycache__/__init__.cpython-312.pyc delete mode 100644 app/core/schemas/__pycache__/request_schema.cpython-312.pyc delete mode 100644 app/core/schemas/__pycache__/response_schema.cpython-312.pyc delete mode 100644 app/core/utils/__pycache__/__init__.cpython-312.pyc delete mode 100644 app/core/utils/__pycache__/crypto.cpython-312.pyc delete mode 100644 app/core/utils/__pycache__/request_helper.cpython-312.pyc delete mode 100644 ui/__pycache__/__init__.cpython-312.pyc delete mode 100644 ui/components/__pycache__/__init__.cpython-312.pyc delete mode 100644 ui/components/__pycache__/progress_bar.cpython-312.pyc delete mode 100644 ui/components/__pycache__/request_form.cpython-312.pyc diff --git a/__pycache__/fastapi_server.cpython-312.pyc b/__pycache__/fastapi_server.cpython-312.pyc deleted file mode 100644 index c2783c7d88ffcca0d425b069e6520fde5bdc7dc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2730 zcmZt|?{6DPb@rEc{c9a3v6Ciusnga?+$QF(*Y;3#I+2#74Y^C&B%G)zT5Y|Pc-{4G zm|Z80Ng?zKq7u@<7g|IS`M?KI!@mG3pHAXSd@YI@TB!&jxNpt{(5Zak&DxvfdN=aU zy!YnKdvD&a@t@-H2!e6c(iiRk^mm2?Lv91RYaBxNkbw-&LKUvSRrms55efpwXuKs> zq=Mwbf)%RB1;vL&D_n^bB0elx(Mqfk^Wl&cFT~+o$WjZcza|O^SY@lDk}M=S#3P)1 z*jDXh@$#|2r2Q4g%g6Vz0NPX@f~{DW1rsuyNv=iW69 z!@?^%!9Wr8r6pL>CC^*ZaKNgSbQ^dG>?khwJkwrurvZstj&5j%<6(OlP|3o}*rGCN z=_|I)^o~3S_XsW)2Qp|Ue(o#j)7^&z@BbeIf=?8tQ(lP zR9M6wVFpnd4rx>4R|(6FoJeAf7%u9TrP=tVM`ci@fl#Ut>@GRBi(Piv42%~e#<^>Y zrdO(7zc#mGF0k;9UT|z17rko>5N>wV#bgj!eiasqbN7lDL#tH02hI}H&lRFba&7C zO(KlEb^#((KA0=*1N%|ukk>9G;QWGkdrF|g^IH_d%FM4XjRU*PmX`+05<@g3BV@=X zXL5#OgaL~f(FX#SGW~)Q@DyC8#||KeRG~pSPf>{6`pB_JfENcn0a)9$_8H0 ztCmMY3l6F1UhV8Rv%|Vu^vnv*x!=lUhy7Bub%th48weH0s9J!XV=#7IeGw2U7a;*1 z3)fPXv$zO4^K%}-dgW59AyDb744gn_+%o-Jqz%~@9y^S2}kNAL>>A8$BG3eERRNa444g z1aWP%;&C4;7mAZ1}e@%hyzHoct3b)5jjE$m4qv7+t!lSdj=evYI zc1ZwJMbj#}X=~a%-b=NFHCx_G|8OF!DwI6im5LTy} zMpfM~w0$#aj6L7l%}*7Ik1yroS%EXe~WfOD)O`Ih4NOb;@>7yKx1j={sAV4 z5y$-#y-`PRJVU4cfrg%;x1XV7e?_YQ3qL~xJ0Zly?!=q0bg#?XsH={;?hpNHlIY~;;_lhPbtkwwJTx;YGnlCVi4ma MGb1Bo5i^hl0HYWmF#rGn diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc deleted file mode 100644 index 2c0a763d2806a5f9dad0176558385f8f2b41f260..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 843 zcmZWn&ubGw6rRa$GHJ32ZBvS%;6mKeg(g%z8Kh9_k9w#SS`UUm+3w7y6K8+0J89{j z>PbB5&A&m~|HO-hN|*5>NDtm3)k{y#?xtFC4)ebE-uJ$FZ{L2b)ha-qCbOKS0s!Aq z=kk=s;QT~__W%P78E|12!W0)+!Bx6Sh83oE)dC>uAXB>($gPZO|3*>|tGI|uN9a(^ za}daSh|72aSB|QON*?VZf(BDLzIqd7Fn!g}@gdA@rlk&M{%2tAFK1sdYu{6f2?cs= zMSOYvse}_doCjO3Z@+vNuNd=IWOM2gGtyhSG0(^@VV?EJcPJyd+mT7?5zjYq#&k&( zS$$a-PZ=*-hTAf*Zmf2UwT`h7BP$5n%izJAfW4bO|W7M|NtQ6(LK)3z$El%2IRUk)xC@Y|0x*#!`&SCo@X@~xh zOQkT~ra*GEKK1&dx@|?&UhzGL_C@jGqX*BQ2u0S2I`wQe#AGY46Q!|Lspt3~zoK*uN;>cYQo$WGTER%hG=!8pvy;KnTyk z^)oQ{1=L3+0A~)+DQFynMpBu2+j`SVs_Th%GpWt~nkd4;k2*m0q;eyfTuQXYPo$#4 rfikKCsGfr9V=(>h-f5$G+-RPFTVH{8fL?2_v`@7=pTXU5RzCL|X9?U= diff --git a/app/core/errors/__pycache__/__init__.cpython-312.pyc b/app/core/errors/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 09930d2a64e82afb9f15e90524e15bedf0b7218e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151 zcmX@j%ge<81j;#yX(0MBh(HIQS%4zb87dhx8U0o=6fpsLpFwJVnWb38q-U07lqSUl zmuIGxI0ks?I_Kx*r6!leBo-9JBlIY~;;_lhPbtkw YwJTx;8p8<0#UREp>7KB1jRdhqTz9LSR^TCU#}Bo6b(KksOLd ztp}l=qF(gip;{5WRq#LX(u0S%R~yt@pgHyAo82VsJN(|qypNgp-u%erh7r&O&o1p@ zgnmnZc(@qvW80vdEDp&GJq-coo9RGtQ~)JO}NS30tUGL>nXhga!b@xCio ztM`h_58aZuv2^?5eBk@c5k510Z)TZ_#a{xw^w^B0Rx66!>Xqb?4K&zgVvxKZwZLfEdB7_DG zm{r#oks1olvk?9P@fD05s4q?OaDw^yzo=9R%#l>8@l+ko1pKw(dBx zr+t#U6!&)lgeDS$Zr1UG<9lVyP4u_t;*o_a)Cmb-ZbOKM`OuQ`fE#8ZJ&BDqe<|Q zA16Eyuz~h7FR3M=pa=$;?Cq@SyLq&HnHMz*^Brh z(a_*iB(0XI`KW}!3=Y%Rk^x@lQ*a=A7n%Sa6=VDdUFs@2p57YjB4C{pIySbarJR)g E4^=?v8~^|S diff --git a/app/core/routers/__pycache__/__init__.cpython-312.pyc b/app/core/routers/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 969639b12a803559ca42e5c889ff17ba51bbdff2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152 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 m(SG(jv04Tf$BK-%S4UF9Y diff --git a/app/core/schemas/__pycache__/__init__.cpython-312.pyc b/app/core/schemas/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 0a7c3dd983409ea6fc7c5ba41f6b993cd003dd45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmX@j%ge<81kA$TX(0MBh(HIQS%4zb87dhx8U0o=6fpsLpFwJVxg=Y~gche36~`DE znwS{GxTF?mm*f}31ea%~l{f}?>N@A=<)tQ<=;kJ7=EWoy6vPxKXQbvP7RSWLXXa&= m#K-FuRQ}?y$<0qG%}KQ@Vg(w?2*kx8#z$sGM#ds$APWFzTPNuN diff --git a/app/core/schemas/__pycache__/request_schema.cpython-312.pyc b/app/core/schemas/__pycache__/request_schema.cpython-312.pyc deleted file mode 100644 index 558657c8a6811d7519ca723c0df08fed6453c5f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2821 zcmaJ@YitzP6~6P@m)9(;AD9;75d`e5V)H1pmdQhopG0JB@NQLYS4_rx$9R){ac3s5 z8&bK5NNXjPiK@Vgw5l655tV7652-|TrT*`qvDQ-YRi(bLo&G7fZdHk2J$GhzGYHfx z?U^(8Jnp&QdCdNyxjBGf-Q-Wt{L_Qb-`PujNi}AB6qp+bBg|z`mP>G1KEY>&gut;q zJ|kwOgp~CpJXvqToAo7p9OBU!!opRAMNL`~96t$vQHpr}!CoQ)6^GL)gZd^4MmnEU zGgOM_fQg9I|B9+>ujR3pp`O89F-|}>n$|Ly`rlGBX{;J~l5#vc)LVPmp5s`U8cJ{& zCHOdmn-F-UiI~R%7H@wUh@^QmFZR3uhBdht`&@FL<_EdIE>9yIz)d)an}5i=v;ta_ z76eb88Ga9VVn49j;j#+hop@K8bJ;XM(5{eDDc%Pu z?rOx_R&0sLbk}$+HhEUNU~0NCm73MEs`(0d56G~XNdDr^Z#I5$W#iUQ)|VI7KfC$h z^Z9@+94M$n&B|HLn9bt>M|b1;=NrHJ@ZoZK{nIP**huVod2(VZ_HcRLZDEqkK+Wss z&8u?!>}j{u_Noua8$bT!i#s2$FI?I9@UB}vtEpHcpy0%{{?lK(rBH8bUG(60KLFc} z?|=4i`Tg~UI}d*Q$wuYQ`p<7{-v0E#yaF1%HA4-91`ZL9G{Y_x8B}H~u+Kt^ z`WtYXVYib8P9@1SW_tUnuMTFa)=Qv`5DYlPhS7Eb$PIKJCd9|wJN#wtjav1?c4O7M z;4=RfM|K&tAqE`+`X9k?yUc%tek3l{EU$3?-^&z=h|!3jFpR0lYM2U>@fh_CkDMNf zjZkTFFg7$!#qp8BVd|ZjjGdZ@PEqgBL^L`w6r)n??BLKysqIwGo`Ftp)ElML)+c|9 zjmBE*=(0+*0U6RInT3KzXaJ<9HbO|Az~mKD8J$XYYH73WPH{ZxI=duv>Emc>Aj$Hm zCyyOHe*8#sIG-}JkWoE3mNv%C>EzVK^o%h$d8&6PpUY_}BdHb&N!@OCJxQG2SDg30 zLXoyQ2Pj!nH`>dTLPgJxh-bEB!?vbLej!v*qUdO*)M@4aLB_HKg%^h7nV%i7iO5->N= zv-5|!dWQ8TcMa5cS>7SHLNvmcysWdJu}Z#ri

e<79SgSvOQ-$QRSbtXxx*^TcLZ zFG8*Ywua6$$a4@;geM2#K@I`YSryAN>466g+UTiUqY>a z40Wx9x-9vdtD$}dT<2&tJZ1&Q0H|`1d=1o6@N2AkS8VTutuMc1BkSPmVLf zK(xaqY*^cbnd)8u^If#X2PEHCJ8J2u2)8+HRA*0~w0aA_r8TNR^n7q`CHP!bEUoqQ-kQGmrWJeL zn$BBOh1H%5b7SRiU&EEJFAA0Z4{|Hb2dm-*<}GHuJh|F^W^TCLbL~W>rGhJaR$30M z1i;OiHTmEow)&&1^2FS!@}-J6|J|xMv9|BP;;{9NSFPB0R`(_6#>=zU^!aR6O#W5c zQNC1_I<|OG>f9EPXU`+XZ3U4hR5q(p+ZHcKZA{Sqh;jc7a8mcb5D*6=22V5+pdLs( zeDxf>ZR8nV)K@5CHD{z#giUQHId&Rtu6Y11Tjz>OgfOjECDN5kHWS$Ai4 zJCDR58sUnhIB65yR$-G?EvS`Z3+E+8ksKrSuciLk?IHoAR!UVb)V~5D{gYokcV>1o z(9MBo{z^ps5E9<%0gNh5<9OaS#{ilnhw^MaI4 z0V1O&ZQr)^Ixo9vhHjpq1cOn69f3n6JS-v{X0ZozH~Gqzc;Y2K?0p0bExix>Z`Lfc zwx5Kb)Q|uPlG<@L221n@=s*R{;-Cv1A_o9V@nI6ASlh~$U{~w13*iH{VR-~p#D!|WQ5UMgg=%zAQ5;?dF6h7qXVJ1fMTtf@Z4`Sex+a(IAa0&T zi6+vFVAkn_-ntsFlzmxAm1IHFCEd`(B*eMT!EU|!@#f5xt;H*DoK7z5 zWx(dUf8Koam!(%;F1>fljhP@4CaPA3mHzV4-M=nvUU}b*A!#YA5nRS>E_{4%;iJ!1 z=C6K&SGyBjD*3n zgJ*=T4{w+L_?l(ZV}yZRo1Myu@7;m5IBNzm(UNM)suI&jE=zJYP0XN+tRW*(F?aJ3 zXVe3rFgZ$?>k}0+OrJxO(fE~vy)0Y=1&mK0g-_db8LjZ{M6wzUQ@o*%cYnw96Nj{F z25t(b0(5`h%%-K3V%EBuOFCs}ZGrX>`rADUVgXGvnE7c7LTXTV(i;+-U3TWbV5XU2 z6!#3N)Wy_;XY9QTw4VsFq3OaH5hNj-(NcQq1tKVhJVw->8;lwFp_Der8CA`wH(1k? zOlu|&y~ZFK^&|*iKAr3v9nm1SMo%0+d9ru(bS7!Y5NmC;Kcx>CW23__rpEQho;ln7 zL`G3aQXiGF*-_1^2W?b!>O-^_da{#dvqP*%_a%-IvAnCPWB*@KJzoa~Dv(CS4Ei)! zH~-kHegBBG<=dYuM*8!${p;cAT^M%v!vLvGvrNT>N^z;+H|y z*Dc<;p?erSrk)4RraFhMx)x|%SGXS|`iq1p!tvf-K~k`A>fwib)F!Y|Y2S3XR@-21 z#$2gh;mB(LQ(aXE`dYL=9Ulh)422`}WHsE8)BYaruz2b(Mh5b=1KS={ci6pP5vLvk zt7Ik3;Kc_&D;a+U6G%pM37-20thy7}QNt>?ABm`k^4;Z9!YW+P`v2O% zDbEhw@Tzs7QtPRpok4A&B1#3VbM-I?xL`Srq{lUSD#0e&r)NYAck)zA6&`4| z?EBQwS3FFW+hsSsXzalHszu)n=^0&0iyDFciZv@4OwUE4$61v&4b_UP-QnzH2#>27 zS(J2LO^q2k(WW}8B*i{VFL=@}fS5r$0aVwp-Yl#&cNLnu*6U*n?bq7Zqit)^BZcUZ z%498iq7Xe%jGkO?Y+q|UT4+4F9=m@n7B9r&Yq9P^toyoBjD7cuy}1)_GyS5dNHP&c zGa!m`1{-NQuNB3g8B*HO_(TzBlA@^6KwG4ko|K}SVNsM61?ZsvJ2lW-g+e2%(*M4u zN7Gf4ADe_j+vP(OQl(L}3d!Wh(-}#3)e}t>wVjIlsdxZ{8BC@nO_PZ}k-;W!McMS1 zgQI*9 z7PX~#?k9z&u6(4c5b7>+7d9GOau4OZp13o-+W0I$M~d9D8;1@rp3Z-3IR9h3dWg&n z%uUQ|SLGr{HaZ@>&gJ`_&r9-Zhca_+zBBjGHSnVRi$5?oS>$6otcP#h^&oG{Hl0BZ zc*Ao)FY@gb#UV@adBD#Hwxh^fzs&~uz@qWaOG__(j$lfS>v!m)O%A1GC>}Jukl*nC zOSM8#36&ibN}1`;PGU*XQ%RLJOgrROTr6h4P1k4>x7hyD|A?J3-&1KPqa9EaKx}&$ fhS@?rMbz_8)V_s!3#j)C?i3Tq#r})nVR`r;S%#mn diff --git a/app/core/utils/__pycache__/__init__.cpython-312.pyc b/app/core/utils/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index d9ff3dad334124697c48b47d0a6bbb6629571cff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 150 zcmX@j%ge<81j;#yX(0MBh(HIQS%4zb87dhx8U0o=6fpsLpFwJVnWk99q-U07lqSUl zmuIGxI0ks?I_Kx*r6!leBo-9JBu*uC&Da}c> XD`Ewj!U)90AjU^#Mn=XWW*`dyavdYi diff --git a/app/core/utils/__pycache__/crypto.cpython-312.pyc b/app/core/utils/__pycache__/crypto.cpython-312.pyc deleted file mode 100644 index e9150cc9bd86d49daf570cc23d20ffb2a6557188..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2171 zcmcIl-D@0G6u>6Jfi385s-$TlrYYazLxKZO}T)$ue#L=2k!_?X;Q&*zGtgNzHh6{@U^|dIP3%= zdZ*!U$u0{<;0j8rMi55~1$Sj-BXmVhZZkS?7w{3_!@zH!6}8R>EVw4P{*#zkn2ui> z-L*$|GSr;HniJa}+qI{FFW`cuxB0ADz)f}pl%|C(TQj)j6m}dv2Y#C9`na3x&)Q^4 zr>;6SKKk0kxz|sh9Z$bHHgV~|lY9(Y7ffPXQ`n-h3pz3N$pVgC zR0*E-IBW}udI>}*@1=XA@g0wuLnU+`Zdzoe`u+Ot`}0@tef!>nPrlGZ!h*Ts9JVmg zDNetQXCWgJ5Ql}1Jw7oyl6u3z#7P}KG&FQDb;8aR*{3?GQzm`6IGH*>V`gdM-0A(J zwq@ZAP3hCqsUkHCPAWrYr>PyEo+Uwu#kVdvIK8ly(Ql!ydT5{;8mNT^%cCnDYWbCQ zP#{+}bgH|4f@zUhnf47q<%5Uc{%1HWAZ5e-Y5V>yD9i~ZAE znm{vxCh=nrM@vjYyyDci8ZZUjAd#hHm)hWj>He|as!^;^-t;! z9v*U)$>AZ8*#>s~2If>5yO_b#)U++vU$ApIOk5vk!8Mt%tGGheRlPXaNb`aS*kOYs z28*ys;D$W|t|n0svBElO*vhCO`+e$Kc>DEZweWNE!b-INcJyX+ap>0Iyt)?Ytw#o{ zk-$>;7r)icsgu;0nNX|hPFYIX9?Z3N+_>v5&_!+ zLklp_a>COISN9&T?M>98 zFU_khBps_o4+9q&s00W88*Jo604)cUL|j_#iY7E^S@XeJ>vp@F11m@L>9=O%S^igO zHWp7zfOQI_&J}IP?SSSoGcfbAX3mvQkDom2DmhHgG@B`yPP6|+I?a>CT9VIrR|1n} zmxkWd3q{Nunh!?TH#tkO<4O)Cyq5^e%~y!9$;12Yaq#GrZ4?Xm7}*apHW!@_VQWZ& zAlySQtfJ0Uq^=^R;YUJGIZ#L4Rn&cB_w9W*_f-zipHOi{>7VbbEB#fa|HhG;va{?5 z)9pPA{nz>zc3j(0i5;wMJ5*Nh272#?dMdpqYoSvW`P4&M5+aRIP*ATH8wki^_c|ku R9)+D72*^_JpN#O2{Tt>u>mdLD diff --git a/app/core/utils/__pycache__/request_helper.cpython-312.pyc b/app/core/utils/__pycache__/request_helper.cpython-312.pyc deleted file mode 100644 index 8f39c2aaf85abf78179143ea00608ab714de0edb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3761 zcmb_feM}qY8Gm1&@BDSP!6qbp<{}{A5X?}f1p$FDVgg-4A|Tn~Fp%Rrz|7d}d>$Wj zs+O!~^rKF{xczTS`L?vG}(5umHU@9F-b0f3KjN4e}N;>le^Tm~#)p&z)DEp$WF z*Z4L4gq!Hsy0!hJn}k?K_^E!KTL*zb{jnBS%jsDX-4sW11gGcp-I^?<72m9G1D+o7 zw6fHoQbt4>WS&XZp-t*wX%6&dqe|nAW`2M^zc0h#B)NJ4|sKJyf7j9HFQ7uhZcaZ0f-VQKtQXCSNjE&;$L&1K^IBneLI4L~3jbRVTqt1U}Jz!ku1BM?2wi0%q_n^N>apaBtm zpMDjsP^>KiA~2$f5D{&JWT_uh5#2lRb$A9%vTR?QvTs;pU0ynhhGfg?B8H9h_tEQd zxQ9H`ta`SIUj1|QTE3uO1Je6NeKR5*Y5#*C8_k*t>9B3417Ws&9&;nbf*@k>YFPPq zBek;5px)Z`13??u3&!EuoqNIAWpo=0fS{`E%T!5~CQN;XE(ZaQsz(Kra##LTSPQ79Di z3!BDl{eQ*GrU?a#<%m(q%7$JSDmdifp3|mHW+gph`j0)P=6Rh>({pT^UNjBDK`=y| z0E4gqQ~-H3p>ymKflq_IfCM2psDa=FI@cf=)+CX?BnF|w9{v+L#EcBO7SCUJ_|nS^ zK4O{WsoyNUH)@pm_2|s3k|~Tz9=la*VM~|ZU%GYi(cQBT{_xu3&qtZ!gRb`C-AwV( zBW=m{nA{w29dfn1id9YM;I2v;Vve;QQu(Q~T#+rlb6XW9Pj^*vest;LO;w1Gd`0x& z_isG9`|9$w$;ERQ5oYP!uO8hUQ<1nVtVh1kcBF;*JTO%`6zuYySC%f{LH~!BZ!;x- z|HY3Ss>)rvlEtNpS$yTrgWp|BCU7z7p^R#ki_V%%_6owlkh7Hw4Fm%rE|t;6H_k2o z_RYnq_m|(hmP~)E#~0$(3{y+sx;c3G=3C2CQ?elki^4h;WQVJm z5t|YQvx>2*ycEdZ%6U28cQ_V#qXa!tB(FCP*K{W6^@e#K$p?FVeopaOjzCkzI5d(e zBzS~ysLLBIP4NBPm5%# z4-E&r4SpXN5G0*H*wcgDkhH!)cTm!$k|~iacPiW?=S19wE{OJ@aZUVQW}rv zuB$u9cO@N{tT@^>If&$I3@JO2%ss9*l3vM;M5l6`1QH>Emkh2UFDJhaIq3gBY4Q@_ zan+S5uN(!x9d`(B$SXlEgeMknXXScU%~o~vpk)z0)zLQS>lW&dHkUO70|At(4$r_q zhd0P`9bv)e4|Sx%@9O3J103%h7?#pQXr*=G%yp%Nd@(YBmsKbWmAGZ_=}Tax2Bd9? zTbZbpncNz)Ry+mU$(jUy+&rGpqnfuCIiI~+E zquUqqN+*v0wDYab1kmSO7PjUsWNcm7wjC2&ve5sr8L<-6Kk_!V06-!cTz4&;eCP z>OUx$EuATyEuSeD>)XZl zN1NR-+ewi=`Pl<&M%=n1YTbd=)~Z_TJ|JdIGp1SVj8&{_67=2RK zdb?qoxYZQ1?Gx#JNXs@nUJh2yZ`E|gbb**%8>8zKG`!UqvsH_9^=AnZL7zRXR>FOZ z!~Iw2b+klAbDHSOr=aBdF>I)A-au#Ny&g|BDvMHCG%cW6guEg1#NzP}Ih@Q_OkcIht zs`*R0`9j!SOwSkTFuhxcG&#r?e4!17KUR4BswGjv@BrHXJbJ?iJSwb!$M5!14%!$_ zyfK{eE)F^69gw7XyqozQ7)EycgC0TBE9arSH+cNn_5|I4--#a2^fmoKHtgqW`7+eS z9~Plsqep^(5Pl5kzW~eMz|JVx`BzYpFak%}LUHLrc0T@ZFL-R-2Wu8+Yl1-SM+Q1U zqBcPROXfH`vF9o=QF$Q{H5Vmxn5_rqE#vJI1rwo(k_(+tx-enDY$Gt)$M=ktB}}+S uD?KyrS+FKhZgg+y4gJJE21W diff --git a/requirements.txt b/requirements.txt index 85f4805..26e0105 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,5 @@ streamlit requests chardet python-dotenv -psutil \ No newline at end of file +psutil +cryptography \ No newline at end of file diff --git a/ui/__pycache__/__init__.cpython-312.pyc b/ui/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 5ff3eafbe0b2e068a267bfa693d38955279a7bec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmX@j%ge<81jn3x(m?cM5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!vUj$M2`x@7DvmKS zG%+!Vami0E%}vcKDUNZ^Eb%B!iU}^yOe=8=@YHqA&&x|qE{Q44jERrW%*!l^kJl@x h{Ka9Do1apelWJGQ3N()qh>JmtkIamWj77{q761ksCb|Fs diff --git a/ui/components/__pycache__/__init__.cpython-312.pyc b/ui/components/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 9a8a061addb78fd0ae81b174db8c9016c825f11e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmX@j%ge<81XrAW(m?cM5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!@^ZF{2`x@7DvmKS zG%+!Vami0E%}vcKDUNZ^Eb%B!iU}^yOe=8=@YHqA&&x|qE{Q44j7iSVEy&LUtBQ}$ p%*!l^kJl@x{Ka9Do1apelWJGQ3N)G#h>JmtkIamWj77{q763I0D|G+> diff --git a/ui/components/__pycache__/progress_bar.cpython-312.pyc b/ui/components/__pycache__/progress_bar.cpython-312.pyc deleted file mode 100644 index 0487e7fbcb5b9cd26e78ce157bc8e4a499e22e0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 689 zcmX@j%ge<81ovHi(tH>h7#@Q-Fu(+5e3k+-rZc24q%h_%L@|PCrYNQqrWS@M=1N9Q z=9eIae#ubfV2Tw;G6V7FLqK9WLkZAq2*`q&3KIa*DU7S3^0f>lAe~^4!UPj_VTjdc zV5ns*VTZ|OallvzI)xdgo{^!3Aq!+X8mM7RVX6X}k5Jd6%Ys8~3JXG2C95V|DL>G# zXEXLaU$X1P{@G7=EqgY1VG+nLFF^sAK5Hfj?6iNG0_6E=vfg4UF1f`~P?Voulv-ST zi?t{*FFo}ZOG##K>MhpdoYd3;O{QBsF!}hT#G+e_nZ+RU6cm2>Ia|eq7N-^!#~2x! zm>9&kN@A=<)tQ<#FS>nBr8q>BTAL<7SUVbST<6Rl?yPPS|C`NGD)Cp^KZJMt3)6RTPW z;~fs3ey&cg3C2DAH#kJDbI4xgke!jZKxt;$RSwk;tUxs%xENRkud_&AWRaYqe3eD+ zGtf{5O_p1n#U(|liMctMB}HsNks?kI0kV1}LlG!Ei+F*=FAkgB{FKt1RJ$S}AQzMp biUokg2WCb_#>Wg|w;8xUvv4uWgLMD^llPlx diff --git a/ui/components/__pycache__/request_form.cpython-312.pyc b/ui/components/__pycache__/request_form.cpython-312.pyc deleted file mode 100644 index 8d74e4cf73bf00a1f093552c5526097bcbe25d83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1428 zcmbVLOKclO7@paO?Ol6`;xsQC8l#jFE3_hg2&ke8%b`31QB5dPs|CxoC)F13uGt5% z%UTJj5-zF~_kj9{tmc?1J@m-YB$r5BR;q-eQEnBW+!9hLQcldQy*PxP_$}>x|IGh= z^UXh=UnNNdBz}AEpz%!co!L-3{lz_XS01B`OuD}9w6I@{`Fv)Jwf~h>={SyK~ zG~eBZl%s$uYZ26n>%O8tK}H8G65R%~(ylrSAUMF_4wSZ`$6y*vfh}+z<{Ql8z)HKa z%2`35<8Z#}1o&bOfGU4LIpkM$#zN4qmxM$tsJcJy+z}+AjIgQZZ z97smd;TJ)JeH>nCpSv0&JtRLrhrkK`5B&(yXI`i;9M+!`9Cj`=PJG?|jst?X`@i;Q zr@=57116khAh=9=55eupGJ6@!!vD;_{^EIuXtY+6(v(e`pZI;@`#*kqVoM!N(ybpB z4BCh-ded0SmU2c;HM4o$)~qCp1y#|q-{gum!klTL5@x?bl`TrJ=~cBg6RqUW2R9Rw zsDfcO2@Sez(~C*a>CdS|GJqM=!XZ>H<+K7aa744!tH{VITFJIBZ{)OV2nX|e(N;AR zixw(dt+k|xMbBB*s+15GiHU51iM?80a7HIj$;a0VG3=9>_5z#U4d&+Z$d5>m# z1`~elPo>YvmrP`s@~N>iXWo=2^t`Pi%`)Zp73%~0syscb%vh<5AD&3-nuhY0Y%6kJ zS4+C)RT=1O8<|#iMmN-xr3x0WA*be*sZQ)KZ&+@rSYdv8B}FE4MzXP1KKVbocH6R*F(R^^UsH|7yLsG_pLloOyJ5 z`L*S;d*tnAtQlEPt>@OK-SZhL9Idl^Ea7a2K(w#L9*8}Z?n{T+H{2uVn&Zur?!}Dz z>9m{q%zgI~5g3ftd0!x&s9mU^{&8}rm-yQg$jP>kf=G1!lf53g1GTol7wW*?PQ|`S zAIM@VlpbY&89tuw39bqv!K*#T(?h}4ce)5(>*3Qcht~#z6c6#~k?`6v50CQcvqAdz zH7x_>)S_Y;VWQ^U!6UFsATVO&vmY8)^@3eQ=Zztf(l=nz_p!}F2%mwzU9KBOjiY4d EZ;?!53IG5A