From 9b4f24df3ed25ea5f77178659cea647ebd235a93 Mon Sep 17 00:00:00 2001 From: CLOVEOS Date: Fri, 19 Jun 2026 14:38:32 +0530 Subject: [PATCH] Fix EmailStr validation, add missing schemas, resolve circular import, add /me route --- app/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 154 bytes app/__pycache__/app.cpython-313.pyc | Bin 0 -> 3524 bytes app/__pycache__/deps.cpython-313.pyc | Bin 0 -> 2046 bytes app/__pycache__/schemas.cpython-313.pyc | Bin 0 -> 1894 bytes app/__pycache__/store.cpython-313.pyc | Bin 0 -> 167 bytes app/__pycache__/utils.cpython-313.pyc | Bin 0 -> 2639 bytes app/app.py | 13 +++++++++---- app/deps.py | 7 +++---- app/schemas.py | 17 +++++++++++++++-- app/store.py | 1 + app/utils.py | 4 ++-- 11 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 app/__pycache__/__init__.cpython-313.pyc create mode 100644 app/__pycache__/app.cpython-313.pyc create mode 100644 app/__pycache__/deps.cpython-313.pyc create mode 100644 app/__pycache__/schemas.cpython-313.pyc create mode 100644 app/__pycache__/store.cpython-313.pyc create mode 100644 app/__pycache__/utils.cpython-313.pyc create mode 100644 app/store.py diff --git a/app/__pycache__/__init__.cpython-313.pyc b/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b530c48ceed4dd236dde98f48aa95703349e927f GIT binary patch literal 154 zcmey&%ge<81Y-Y8vOx4>5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~iQvsFxJacWU< zOhIBvW=@P-VsVLMfTylkc!;iJX-P(EUP)#$P$oYwCb6I(CO$qhFS8^*Uaz3?7Kcr4 ceoARhs$CH)&?Jye#UREL{|bfLwj`-c<s0?u>Zsk%eP(5YL>#SJFGaZL7GM&zq4AYIo zwUV)FDhq`l>3J=+s^^lXS6gR>o72}b*S@E*w&b>{d5$~#tKMsZCXR}_@bY<={I)j8n{2w#+mW->YV(!?Hf6`HtphUzZCRt& zq^OL0Zk+q&J2?kKmOAZmi-IO$0}dM8JKYgFnZSEOYj;+`${Vi2io8Dmv7Rx?b6 z@XBP>WENbOURU*ZGR)W(O{E^ji4-`=bf$F!lpweqT-$UX2bP#g*TW;9%<1$LtWLYJ zISoxjdXeFyXcAL`7^>- zbYDcI9Vn7{c%rLS)f{=_wK$%T#fwQJlZ{)r9EyE^VF*zJVVg;Tq>zs%i$#2+#dTA$ zpnx;cKA5QuUiMpPz91XqZ%W{UtM6akl0Wu7^gf!{RU#V``!1#8>O5#Wa}YYc7rIyp zUEB{`sX3&8w@M_xx9TNr9S^!TyAFcq9?QS?Z~J$Hmk*j+KkWbYxnE@WgP{jAn=>_! z!{^+XsQO5c`qQa<3m@G2`K^i^I#Al~r?*Bn@9c+9ZLK^!wcn#Y>Z3)#X-CSQ(NsH04MovF(O%JFD=fXXHQ19|8Tj=Yio z6M1%YV+@~%N%2X49sCKJgqzBX{|$4CqK);hwVqtZdjqjHhU}9x5d5MW34Aqk zeB}!Un7fouze3^M^%yR9znM=JAakY+3w7WR8%Z1wrt`L*q>xi3)AwAe_CKGaz1*P* zW4)+Hyc|>X46cHxqsN${Dt@+LgAk&as%SGb0L*@*d!aFv6Saa#z)@9|o<)g+#}yp} z8r9i{(42&WXD>8R2@UKj-`tq^v(kLf)O`XAeSn2##DWL05a_&LuC(=TOdlv+Tcdm3 zLzV8K$3NLq#wyC#^W%ma~mI$ z>~Pv(=ok+n72(%q`n}YZF!Q9WIy9~ttR#)t79U$K$OuPVZfJ{MZ$V0`^1T-}C zqSf`t<#T@NBkkX~H&OMV8;s3? z{ZP-=txD+petUT9+~yCDT(ZYo^MXzdwX4JhHvN(S`HcBs&by&8;iK?n`4jh;P?j#_ z^m5k?-B7JNfC@pfqNmkN-YWIn@Dz)h={tE(czKPFgP!FJJRX0b)>(Di=3xKw3J6^L zc6>rv6OJpMI7_1-iO9TUykL2-?5iL=X;~*)7E0Mg;9o}>R)c!fx82V^C~Ov<`1Xb; zE5nn2jPK1PD)3X@+L7OS#$-^uJ1{8BMqV*p%Wxe`7Bf@=CNFSxSmIvIR!bPQuR@Gs zY?=xdBgQX$tiD_LBS0wLPpEKzu=ZVFXvv4iop~elfO%6d$>S@A#6>f5+ zto}N^20Zj4`v){tNf3n3$k;9!drG38k?~zJ{*+w)m*^10Ljuh|eMC6(l=T0dbo|BX ze=oBkeJ;p93%wh<7uh)#{lnTuXh*nF6DoyvMm`g_?*w`fWJzzX2pd>x2LR literal 0 HcmV?d00001 diff --git a/app/__pycache__/deps.cpython-313.pyc b/app/__pycache__/deps.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d17fdbe08d91a31d187c6e57ec5ec67da56605a GIT binary patch literal 2046 zcmZ`)%WoS+7@vLkwSLBqQ}+?aO{2E%BTdqzB~hqwVz=qKIFZ*TP+PUO_9k(#cdgkq zts@TQaVRGw1cC#Ha-tw4%0u*8(LaEsIn-JuDhMvUIV3y|9QbCP#i4?cJl{9pYi8&B z{l?pFH-}(6V#k($upsm^S(pRX#@PN2#0N+~0+mEOg(;rKbcC$wB*Qb9F?1%$@)m62 zt=P)ju+6ZsNjvYr4$v)vHOcW#?Bre8W%z7KH}An7L$@crybt>f-I46#{n*b3aKOmp zlHGg|2O}u&=tqK6a0%{l8u#Qa{V3lvMzxPPN(r9)KEaz0jIx6709t03DI8ktiu#|D zfGDk7vT|9GbtWb^qLgkgiJGL9t5THFZE>k4$t6{H&tx)liJL{Krjbyqs)<@%h3M|t zSY2BhnG;p@hJs5|l87bzlxz)!9kDByX4985GcW3#keE&Q|n0Ml>MExsR?Lo z_nu@X%M?TZv;Tk@-;sh%u#eLcl{;qIj5}k(&g|k#^Is@m{ z8Cki33D~ingaiqV3!_8Bg={L8&CCEfuO{NU9cW|_5ECrw{@tjvg^RQ4smt+rB89t% zgISa`ol)!8{|Ol_q-HY^mQBSmL1>i4K@w@JnG7~KSOAiXI&1KkP$&XJADf=Y6K1RV z8ax4&{6$gKVsn>I!(sMbGdvD?u~y5Mq?$TdYv?{WM4?#67;djXD&kX6k?gAe3e!_` z3$^Gj3%B79et6=&6HQmw`x9S`-#xj-&;jnYZO4jw`qzS|?p%WDHusA&@Ys3yk@N7K z6Q2%#GPv#>{&wd6m9MXSlUjF9ZTR~(!UK=P6Kmm#=DwrN{U`6fyc#;UWn%-L7Gm9= z7K2>V)Xo7E9sbhwx$EB5``5m@_8_xT89a73wAwua+mj-! zgKIGm^_NP>`zE|M)Z4MiX*>EJ#q6+nXg@c7fq8fVNJXWh`*o|a6Iwg)P{UHhOa!DW}V1}5*DZmC17)Hd`82z7Q@fgTv(wAUIXk#M68BPVoi z(0GqUwGC5(5Z$6`3j9YHfbwDqIw)SRNCia%W&yagc0X}1#BRe6;gclGK3P>t^@?-> zKL<V1Mn*3if%8vcW3DEc=JQNc|#@B|HQqJuyAk2c-GrY{H$ j2++5@TP_qGy60U#{=ybIM1}7hX%X?@>hDD0^HBJ2eazO| literal 0 HcmV?d00001 diff --git a/app/__pycache__/schemas.cpython-313.pyc b/app/__pycache__/schemas.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..43f726c4627b4d73206a4a511f87189352524b3d GIT binary patch literal 1894 zcma)6OK;mo5MGKDNxkewel$*7hYiq&ipD`7Nt~cakv11D=*&`f0-K1LGNSiK~52ni|)d1zu?CTzANMa*4Qw3&hnu?W&%~b3?HP zu*E61s8|zNYl<}$YXe)FVlBm%fvrrjcDvfBjqoQ8Ju7|X3$~dAEXs;^LKX#?{cYC| zBUkcPzK$GEi`a(oGvX1hxr7&7jqC5h(z)Rkp#;sOAG^&~XqhS1YDmx>T;PFkj)nQ%VksV(;p>R^c0%tejXmBTtd#QAS zl!tNK5n(${dk*-bq#edg!-NOFku6*kr5HzPR;IL@1ZjkJmC|2QKbm;vDcudZkWm=3I03gsX^^xiUD~62daVn+WF-RuE9*yvn!GdglaiMFOXyTwf0D%HZyB?%ZH=See^v z<`XdX^=0wkkhldU1X@ZQ*MP%B(`ljec`+*r+} zz|h%Jd<35VcgU0!=9CmxyVx+Zsw)1z-;WYM7~xvs_@q0T&K~t%udGZDV>z>|4z zz_bF7CX`jG4mJSxHu6%?J-d`!;CV$Ur@N}k%UgbWZ%x(mWw$RR>tf@N-iCzyA_6WY z?_azRCS_VPoA~AK&FbGZoQ{CG1vWfXINiL1CbaS1W?m}>ZZ%49?ezm6uMmzWJHNQp z?WArGbtY6tDE-tg&K{f0{vy8}Z}Cqcfd;R*3-GtDY1*IU$|1QjHVkcjzdt7M8P6Ho o{C<-1U}T0q70b`?;WdHyG literal 0 HcmV?d00001 diff --git a/app/__pycache__/store.cpython-313.pyc b/app/__pycache__/store.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26ee4310026eb1cb89fc5b67310cb1781733d41d GIT binary patch literal 167 zcmey&%ge<81k9|aS-e2{F^B^Lj8MjB4j^MHg8@SkLpp;dqn{?@EsoOS)S}|}l%$mm zpFyf_nK@gJaxUoLv$TWOEOaPN-~pyGWmHii3J5Q#U=Sg qsd@#Kw>WHa^HWN5QtgV^fX0AqECxB~12ZEd;|*@!MvfvDAQu1wrYQaZ literal 0 HcmV?d00001 diff --git a/app/__pycache__/utils.cpython-313.pyc b/app/__pycache__/utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f825546b0332543dd862c32a282850695c630d25 GIT binary patch literal 2639 zcmd^B%Wo4$7@vK_ueFm9=ivY$+we#nAhal<1S+H`a=>;8$QvspwzOTZC)mN+wPx3Z z^yEViZ7-FO5U4#g+#HnyM-Dyk2UL}|M(w=dT6t<2qu%*V$aezrUK%&v{uwLi<{Ecz>8a6HGv2vUCGON-Nf3>R}`A!XTu0t zZI`6e6KAaU#QdBr#imoSIXM|yuzD$Ts0TPz!hk8&)8X z%LJ=%pS7GkAX3Z#ELBAlb67N#C0tq)4O1sL zFTy_51)x-Nih;-ARh{m}vf2XM;^~f2hIGOG4Eh?;Ix~GPn>H{pvSr0Ama;R7VNP9* zp9JJiPE}w_%`5^Q5Q(f(E@vxXV~my8tnhVg%5*!Nm)&zn1h%yI?tw)Wz44&n!CwTi zA&BddO<}aokM4j1S;8Jim8BwQ@IWbk?}h3$X2S8cQC(}Hn$yjK4MhuFim8hwz~o*P z&<91pMOzVbx?aKnETIWt&{u2M#nl1^Ut(`yuIjtqffdD63nGL@(@nsd&<9(do0t@r z)_|oTko_EP!(^m<-rgdOydO9Xa^4yhVY*vn4yBbf$Nj=Su@+i1FGGhi=-DESR=~q5 z!O%-&MOCq3$fkY+Yh(;eXy6SQrYhR=9qoQs_~ayJ$FZMkKS9@9vI#pIJVQqhDWN&CRAQu zo$s~N=l`1~M5YnR*a3f_J{crell|I$M|N~Of|VB z^tyqLGPBWstAqBqQZYB?bbUfA$6|HEE{qle9&@b>4UW*#CG?+-#V_md6=;<~>+?!z z1(RcR`jAfa36=-iK74vfluACL6K!2B%xL$wc-lw_&2Kx=34Qzqvh%Q26TVMY^n9g+ zFOc(~q8}mSXPBBC!!S?L$gjx%42?WP15Z)+Q*`JB621sMKP1-Cfvv%49Ua^XhO7Rq zP{%h5UoX`AhBrea)xcIy-`$1V3-!T|H+#<3QP+#`U@cM$Ji6Hk&(u+8ljoS@>xY{N zNR4|#Ni)DR=kBRZ1mq!SlSlj;%DoN>Oy7FXx>1{6zjc4GiGY3FZ7pY9e4CM{QD{U;Klz_Iv*V*L_iE literal 0 HcmV?d00001 diff --git a/app/app.py b/app/app.py index ccc483b..aa50152 100644 --- a/app/app.py +++ b/app/app.py @@ -1,6 +1,6 @@ from fastapi import FastAPI, status, HTTPException, Depends from fastapi.responses import RedirectResponse -from app.schemas import UserOut, UserAuth, TokenSchema +from app.schemas import UserOut, UserAuth, TokenSchema, SystemUser from fastapi.security import OAuth2PasswordRequestForm from app.utils import ( get_hashed_password, @@ -8,15 +8,15 @@ create_refresh_token, verify_password ) +from app.store import users_db +from app.deps import get_current_user from uuid import uuid4 app = FastAPI() -users_db = {} @app.post('/signup', summary="Create new user", response_model=UserOut) async def create_user(data: UserAuth): - # querying database to check if user already exist user = users_db.get(data.email, None) if user is not None: raise HTTPException( @@ -29,7 +29,7 @@ async def create_user(data: UserAuth): 'id': str(uuid4()), "username": data.username } - users_db[data.email] = user # saving user to database + users_db[data.email] = user return UserOut(id=user["id"], email=user["email"], username=user["username"]) @@ -53,6 +53,7 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()): return { "access_token": create_access_token(user['email']), "refresh_token": create_refresh_token(user['email']), + "token_type": "bearer", } @app.get("/users", response_model=list[UserOut]) @@ -65,3 +66,7 @@ async def get_users(): } for user in users_db.values() ] + +@app.get("/me", response_model=UserOut, summary="Get current logged in user") +async def get_me(current_user: SystemUser = Depends(get_current_user)): + return UserOut(id=current_user.id, username=current_user.username, email=current_user.email) \ No newline at end of file diff --git a/app/deps.py b/app/deps.py index 69700eb..520ba45 100644 --- a/app/deps.py +++ b/app/deps.py @@ -4,13 +4,13 @@ from fastapi.security import OAuth2PasswordBearer from .utils import ( ALGORITHM, - JWT_SECRET_KEY + SECRET_KEY ) from jose import jwt from pydantic import ValidationError from app.schemas import TokenPayload, SystemUser -from .app import users_db # importing the user dictionary from app/app.py +from .store import users_db reuseable_oauth = OAuth2PasswordBearer( tokenUrl="/login", @@ -21,7 +21,7 @@ async def get_current_user(token: str = Depends(reuseable_oauth)) -> SystemUser: try: payload = jwt.decode( - token, JWT_SECRET_KEY, algorithms=[ALGORITHM] + token, SECRET_KEY, algorithms=[ALGORITHM] ) token_data = TokenPayload(**payload) @@ -40,7 +40,6 @@ async def get_current_user(token: str = Depends(reuseable_oauth)) -> SystemUser: user: Union[dict[str, Any], None] = users_db.get(token_data.sub, None) - if user is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, diff --git a/app/schemas.py b/app/schemas.py index cd47e02..ad17aee 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -3,7 +3,7 @@ class UserAuth(BaseModel): """Schema that is used for user input during signup or login.""" username: str = Field(min_length=3, max_length=30) - email: str = EmailStr + email: EmailStr password: str = Field(min_length=6, max_length=100) @@ -16,4 +16,17 @@ class UserOut(BaseModel): class TokenSchema(BaseModel): access_token: str - token_type: str = "bearer" \ No newline at end of file + refresh_token: str + token_type: str = "bearer" + + +class TokenPayload(BaseModel): + sub: str + exp: int + + +class SystemUser(BaseModel): + id: str + username: str + email: str + password: str \ No newline at end of file diff --git a/app/store.py b/app/store.py new file mode 100644 index 0000000..a114bf9 --- /dev/null +++ b/app/store.py @@ -0,0 +1 @@ +users_db = {} \ No newline at end of file diff --git a/app/utils.py b/app/utils.py index 9348b17..557c37e 100644 --- a/app/utils.py +++ b/app/utils.py @@ -26,7 +26,7 @@ def verify_password(password: str, hashed_pass: str) -> bool: match or not.""" return password_context.verify(password, hashed_pass) -def create_access_token(subject: Union[str, Any], expires_delta: int = None)->str: +def create_access_token(subject: Union[str, Any], expires_delta: timedelta = None)->str: if expires_delta is not None: expires_delta = datetime.utcnow() + expires_delta else: @@ -39,7 +39,7 @@ def create_access_token(subject: Union[str, Any], expires_delta: int = None)->st ALGORITHM) return encoded_jwt -def create_refresh_token(subject: Union[str, Any], expires_delta: int = None) -> str: +def create_refresh_token(subject: Union[str, Any], expires_delta: timedelta = None) -> str: if expires_delta is not None: expires_delta = datetime.utcnow() + expires_delta else: