From 7b52ad9baca63a7e4d47b71d945777f77e4df038 Mon Sep 17 00:00:00 2001 From: abubakar Date: Tue, 7 Oct 2025 16:18:33 +0500 Subject: [PATCH 1/4] implemented auth functionality --- auth.py | 56 ++++++++++ database_model.py | 10 +- main.py | 103 +++--------------- model.py | 33 +++++- routes/__init__.py | 0 routes/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 162 bytes .../products_routes.cpython-313.pyc | Bin 0 -> 4980 bytes routes/__pycache__/user_route.cpython-313.pyc | Bin 0 -> 3602 bytes routes/products_routes.py | 94 ++++++++++++++++ routes/user_route.py | 60 ++++++++++ 10 files changed, 260 insertions(+), 96 deletions(-) create mode 100644 auth.py create mode 100644 routes/__init__.py create mode 100644 routes/__pycache__/__init__.cpython-313.pyc create mode 100644 routes/__pycache__/products_routes.cpython-313.pyc create mode 100644 routes/__pycache__/user_route.cpython-313.pyc create mode 100644 routes/products_routes.py create mode 100644 routes/user_route.py diff --git a/auth.py b/auth.py new file mode 100644 index 0000000..45a21e2 --- /dev/null +++ b/auth.py @@ -0,0 +1,56 @@ +from datetime import datetime, timedelta +from typing import Annotated +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.orm import Session +from database import SessionLocal +import database_model +from passlib.context import CryptContext +from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm +from jose import JWTError, jwt + + +SECRET_KEY = "af3287c8391bb9f4f7a72feb3b85f72e1d5bd07cbf4fa4ad9497c78412923312" +ALGORITHM = "HS256" +ACCESS_TOKEN_EXPIRE_MINUTES = 30 + +bcrypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto") +oauth2_bearer = OAuth2PasswordBearer(tokenUrl="/api/v1/users/token") + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +# create token here + + +def create_access_token(username: str, user_id: int, expires_delta: timedelta = None): + encode = {"sub": username, "id": user_id} + expire = datetime.utcnow() + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)) + encode.update({"exp": expire}) + return jwt.encode(encode, SECRET_KEY, algorithm=ALGORITHM) + +# verify token here + + +def get_current_user(token: str = Depends(oauth2_bearer), db: Session = Depends(get_db)): + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + username: str = payload.get("sub") + user_id: int = payload.get("id") + if username is None or user_id is None: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalidate credentials") + user = db.query(database_model.User).filter( + database_model.User.id == user_id).first() + if user is None: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="user not found" + ) + return user + except JWTError: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid Token") diff --git a/database_model.py b/database_model.py index ef72229..30102fe 100644 --- a/database_model.py +++ b/database_model.py @@ -11,4 +11,12 @@ class Product(Base): name = Column(String) description= Column(String) price=Column(Float) - quantity=Column(Integer) \ No newline at end of file + quantity=Column(Integer) + + +class User(Base): + __tablename__ = "users" + + id= Column(Integer,primary_key=True,index=True) + username=Column(String,unique=True,index=True) + password=Column(String) \ No newline at end of file diff --git a/main.py b/main.py index 412cd1c..98e85c3 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,13 @@ -from fastapi import Depends, FastAPI +from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from model import Product -from database import SessionLocal, engine import database_model -from sqlalchemy.orm import Session +from database import engine +from routes import products_routes, user_route -app = FastAPI() +version = "v1" +app = FastAPI(title="Fastapi ", + description="this is learning project.", + version=version,) app.add_middleware( CORSMiddleware, @@ -15,92 +17,13 @@ database_model.Base.metadata.create_all(bind=engine) - -products = [ - Product(id=1, name="Laptop", description="A high-performance laptop", - price=999.99, quantity=10), - Product(id=2, name="Smartphone", - description="A latest model smartphone", price=699.99, quantity=25), - Product(id=3, name="Headphones", - description="Noise-cancelling headphones", price=199.99, quantity=15), - Product(id=4, name="Monitor", description="4K UHD Monitor", - price=299.99, quantity=8), - Product(id=5, name="Keyboard", description="Mechanical keyboard", - price=89.99, quantity=30), -] - - -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() - - -def init_db(): - db = SessionLocal() - count = db.query(database_model.Product).count() - - if count == 0: - for product in products: - db.add(database_model.Product(**product.model_dump())) - - db.commit() - - -init_db() +# Register routes +app.include_router(products_routes.router, + prefix=f"/api/{version}/products", tags=['Products']) +app.include_router(user_route.router, + prefix=f"/api/{version}/users", tags=['Users']) @app.get("/") def greet(): - return "Hello, World!" - - -@app.get("/products") -def get_all_products(db: Session = Depends(get_db)): - db_products = db.query(database_model.Product).all() - return db_products - - -@app.get("/products/{id}") -def get_product_by_id(id: int, db: Session = Depends(get_db)): - db_product = db.query(database_model.Product).filter( - database_model.Product.id == id).first() - if db_product: - return db_product - return "product not found" - - -@app.post("/products") -def add_product(product: Product, db: Session = Depends(get_db)): - db.add(database_model.Product(**product.model_dump())) - db.commit() - return product - - -@app.put("/products/{id}") -def update_product(id: int, product: Product, db: Session = Depends(get_db)): - db_product = db.query(database_model.Product).filter( - database_model.Product.id == id).first() - if db_product: - db_product.name = product.name - db_product.description = product.description - db_product.price = product.price - db_product.quantity = product.quantity - db.commit() - return "product updated successfully" - else: - return "product not found" - - -@app.delete("/products/{id}") -def delete_product(id: int, db: Session = Depends(get_db)): - db_product = db.query(database_model.Product).filter( - database_model.Product.id == id).first() - if db_product: - db.delete(db_product) - db.commit() - return "product deleted successfully" - else: - return "product not found" + return {"message": "Hello, World!"} diff --git a/model.py b/model.py index 23192ce..9c4bd8b 100644 --- a/model.py +++ b/model.py @@ -1,8 +1,31 @@ from pydantic import BaseModel + class Product(BaseModel): - id:int - name:str - description:str - price:float - quantity:int \ No newline at end of file + id: int + name: str + description: str + price: float + quantity: int + + class Config: + from_attributes = True + + +class CreateUser(BaseModel): + username: str + password: str + + +class User(BaseModel): + id: int + username: str + password: str + + class Config: + from_attributes = True + + +class Token(BaseModel): + access_token: str + token_type: str diff --git a/routes/__init__.py b/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/routes/__pycache__/__init__.cpython-313.pyc b/routes/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..598dd488b4e7562b7aeb22bb539df23f35435785 GIT binary patch literal 162 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 Date: Wed, 8 Oct 2025 17:56:22 +0500 Subject: [PATCH 2/4] Add File Upload with User Authentication and Database Storage --- .gitignore | 1 + __pycache__/database.cpython-313.pyc | Bin 471 -> 437 bytes __pycache__/database_model.cpython-313.pyc | Bin 867 -> 1607 bytes __pycache__/main.cpython-313.pyc | Bin 5286 -> 1458 bytes __pycache__/model.cpython-313.pyc | Bin 613 -> 1668 bytes auth.py | 2 + database_model.py | 27 ++++++--- main.py | 4 +- model.py | 5 ++ requirement.txt | 10 +++ .../products_routes.cpython-313.pyc | Bin 4980 -> 4980 bytes routes/__pycache__/user_route.cpython-313.pyc | Bin 3602 -> 3602 bytes routes/file_routes.py | 57 ++++++++++++++++++ routes/user_route.py | 6 +- uploads/85b140b589bf4c79b5c61f4c7d7fb794.docx | Bin 0 -> 24037 bytes uploads/88000450138746dcae8b06ee5fd6e40a.txt | 57 ++++++++++++++++++ uploads/FASTAPI_NOTES.docx | Bin 0 -> 24037 bytes uploads/main.py | 56 +++++++++++++++++ 18 files changed, 213 insertions(+), 12 deletions(-) create mode 100644 .gitignore create mode 100644 routes/file_routes.py create mode 100644 uploads/85b140b589bf4c79b5c61f4c7d7fb794.docx create mode 100644 uploads/88000450138746dcae8b06ee5fd6e40a.txt create mode 100644 uploads/FASTAPI_NOTES.docx create mode 100644 uploads/main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba0430d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ \ No newline at end of file diff --git a/__pycache__/database.cpython-313.pyc b/__pycache__/database.cpython-313.pyc index 714e5d917a25321defea4f9a3a6d91d8e170b3e1..6727ab55df928bd940c53b7f9698d2da9304f068 100644 GIT binary patch delta 72 zcmcc4yp@^zGcPX}0}xy~^LQh-7NfqEi&acoVsS}gL8fj>YHq%6K~a86X>v(1cG1ag GjDY}qHyPak delta 72 zcmdnWe4Uy5GcPX}0}$vc-P_2m#V8u%Y!wq)oLW>IQ=XX@dE3=We!FU; zQc^)67<>_gKzi%3x%FQurclkL4W$L&3RdrZZ&pg3#(@+%LT~4N%*>nL@4Y!G6tWD* z?N9&MyPIb0H&W)qm`zT;#>qqGFh|>9O%1eW3R2B9q?CeL}I9k}4e{uhGpxRGenHuGW5CMUZ%dB|D} zG>1XT(ID-lKyMi0)of|s zjs&dvfjG9&C$j@o`7Y|w_T!Yy^}+ADFyx<$p)~x^6L-F{DI}`<-ea~^D@vR5(CrG& zWtQ`9&l?0Z&vU*%aD$nr#BocA;=m6@*h6=L^Vr=9MB)eH0dzV)<$FLpOPg{aw=DF) z@A#n`sB^#~ucgxTJ-Ntv6eFN*?#77x&L9>M=Wq^#PVmp3X1OXJigW^8>xx75uoMcL4>$~kA%0FgSM>i*>#qsXp z-O>7_TzRzmU<>`l^7xZ)Kl{FNRK0RkY~1@0F3z_)oA^C+|4+o{=9EJ|DTJqb!7-rN zF>98zK2D%GF5$V-o&&QD1xk}3+^Ut}JSht#ULjE?u}I=6X|P19Vog_!`EJ+jr2NtUKVN9at5~Cb1jBVlv7QlQxhH2 zour|rZVFbr??%y=J@C#%?-HFrg3^fIL_uYG@j3LU zOjXgAR>A}iBL3ME_uAh3`K)ogUP3)tj zeH67S!)G~{_PfcGaUI^m-RUuluA%rdt!dgX?8Z;*#_#OyC)TS+)@vhuY8u+=K|E#n anHFvBjRSAW@H1U9wCcf?ztCP)%>NA`MtJ7{ delta 430 zcmX@k^O%kAGcPX}0}w>(+{-9ooX95;=>z0VWe8>nX7pz8Vk~0xVk%){h@;6ealOLC z+gg+VF;!@>0R?Vx6cpvBlqQ!HhiEe0;w~u4%uOt+jL%N3yv3TCmy%kslEF`tfAS{A zh{*?-H6|M{NksDjwS5K|b}Px*DkiizwWv6zJTouGCAB!aB)=dgEwQ*Hu^>}7B{er+ z7iPL{d1gt5ZfPcd*~yJe!d#p{ql-WU+hleYb#+1U8Eli~8oY0E2}}^})@^XT$tyg; zy*s|yqeu#-(od6favQ6{_Z z+lzZd7vz+Qt;M#&Lkf36`P*mO9T3%b$)|J+Tz;AT4D>S*WBdkPeuF;#9c2Ohfeyy# z;3S?tQh!rY!|F7@J6*kUDhq1yNStX1OB0kCqs&ywO!ci%=EJGJI^3M82-)57}8(#y416kIA3jeFwr;1`erou+9-eXICmU7pL_SMx%(4+W2|pnFcDsOf@IAA literal 5286 zcmdT{Yit|G5#GJyx7Nj4B9ejpAeWZ;b0D9Y@Kv6C-B=QT@!BR7D{}5$WS*teOC3jOek2`iELo)XOSl6jcZjx6{Xc#^ozvCc}*VJWECwBN_;}C0SLKWa61*Bplamaw4WAOR)^GQ`3Io#LRr_)Z5t_a8D$tyESrhu4g4i{u?J{eU(iDgzvL^lyJy6u^8N=v4e z8%~JxN^HJ6C9}CCTL>p2vKTj(V!=E9m;d(Ujj>G(@qkt5l&Ee=gcoGp6_wQpQ&Jk7 zRJW#>5|MS`@?tolDcaH|L_5))(n6SNsrh6=#y#R;O;$B=AsLn9qFP+a?{WX-vCRe$ z+T;z%aXB0{1l8rHsidOH-4Q%iJgy{S;(T#2HvZXi^{@UtKekzkvKExJpGzhbEyu-#)<`_5B8Ir=tcr&a_2*BGgr-%Qsi7-MB6M0-UjRRc<^X@; zl+qoQ7n0qnJl@pqD~dMXy{LS}>Oxpa^rV(_TTIqwqO+_6_Gy7fJqWK&vO+#~)cnl9 zax%y9R$tb$=cmF0!FfZtCcNtW$10a6{-ss}^G{sE^yii~@{6ir`mXl~{~LaoK4x17 zFrlrsMYzn)cHP;U7tt$53))Po)SLM#i2BQ#>4yZJrV<@_o{;dj2)Sr{*O1jm#)k+= zaIkMhnJ5f*-xt0WTpyX{f(X&FX6}2M4pH-bA~`oqh>IAQsVWnIp(%ryiID(5#W2`z zlYpZDhs%pHThcwzuoj*TtMZHya11R1P)RN(G+i*Auk+z(RCgL2GttF`lx~Y87ZwyP zz_a}zz`9V%ZnC3SL{Vf1j9CT3452UpqX0bWC3qEqkac@*j9(jn{X3bOL+P4B8}6=* zdm!x|*l-W7JoUitzDZwPzF)U*^(-=;xpwCD(Tp$fkuQ+;op|qTrfW3aHM-F?2BSZ@ zPj2y~Ud-8u!*#=P&GD)$=OBW28+Ic)?R^Y82!8`R2!&fDMg!c`a>Y0#j%1hkT$073 zEey|^e;r23xQsQ%WLab2WO5W96|=(%dEjbf$g}L|HBDfcC4$?S4YxD4{4Nl;Fo+*r zEpS~#9~muJ1gSX#E-gp|F`mns>28E(J7I0G9I&uX=ol6QdB5_I^dTLf%OEk&&X}iF zaa+@2doXl?vAD`cVD$-jR1}2kdaK_!_}am@M>9?R>8Ab-??A>ol=cp7c!yUev##28 zOKsj9o}xK=f1pHv#1N&C;tgSdcQkDBM;ed)%&jjH}k)o{9M_+z0q zBly#TKO?lHg_gBoTIks3sW)&x&~v-y{kj}atd)PYS7j?3)`fL{c-vKx*cPj!HdUD7e8z;>viri^e6SXmDotpt14i^D#60rv4ehK(P? zybJQ1X;yX$SkRU##xxkzVteJx)ic*eGp?48TrFwWv9+_A_Mvq9&_??(jP6=SvcU56 z)u&$q^m|vP48ujxMrJGG{1QYk*o^xI;hdl5i#VSKoYSEBvu557`e{C1{FDNR=aHoX z2^__&1sr>Jwr2ZUxs4ID)?t zd1o96yW>c=A!(GC3`5yS!MaDi91q7M^YX${5BxSV3}GlJ zQz_kpS+}HEx15AJR2SeUG>po0XN1WRvsjFobUwPU@f~5O11zvlegG*SYRJl=ao#+@G@(>f0g~%H`aIR%VDVO?(;B zoF>f~(vc<|?_F5OesD`5R`=@Vo8MpW9?yE~*9NwDYVG*U;)1y!X6p~%8Tt@PS!z8` zi*rZrT=;Mn=YoZ~rS+aslaAKr zg_j&qA9L1M>aNzU`d)0_;wWv}<_YJ?A(a!%wWbYPbFZ!Qy`guX+#(#Ud$aB#cF0v7&3p>!X{6g`u&;TKD$L+wC$#v!*1<4qx+!a wA&v@r0hMb;$;PXV>+a?ay7yjZ{|5)&@7*FoUJ|-B`$HU|h<}R%%1DI&0lUiQu>b%7 diff --git a/__pycache__/model.cpython-313.pyc b/__pycache__/model.cpython-313.pyc index 2dfe74a8f02eb6fda4dc1b4cafbe587b8c1aa2e7..486da57b21c4a80b285f1fc5b36c38847652e8ef 100644 GIT binary patch literal 1668 zcmb7EOKTKC5bk-;?&h6MOyVOUzF08<55WiO!3Y{PF);DA(2TpYo59_gwR(c&9Ebrg zUh*UIdvaMCJPE?$Mc5UuR`qN`Caj`;_`2q+>guZMs_707=NYa?o8Pv-Ym9xRkCSEf zh3+;Ahs|2T9P^B_wqm4qpexoy);hx)inWn-&akFpU1W1-SgW2d z6*}auq{-Y9Uj*xMHE2}Q3sN-uAKfwvhpfy%^BCx!2F7((Hp`Z$-$YH>M#(spOeHzU zSxO>1nWmF9$h_L^_)gS9dB&he{#!uqw^SZw9hZ4MYXPRuaaMv|12W z0_ncr@uMV6b~DjpP1Wko;FgWn)4}u%uF`fp=L!9G`%WC?ZHBj1q}C_)!#7kZ~k12SX$vty&}YlhnmD9UwocTPi8g%P1g0 z+(7uq+HPU*SvxVQlYJdt`iX{g)t|5_PsLQ zMg60?%QL~QTP0iOS0V6|;Ef2NGlqwyyMv-?7q{gL@gat4T7)BKP!ZrV9)e2*gYsM; z1p)aMRASpWG`}Sp zA75OOSdy6>pIA~-l$lgol3E-eU&I46?=#5ETanIIF`>n&Ma41YnRziTsm0kP`2~~t zn8Y}A%QH(dbW1ZqoO&21CKu!+y@JXjR-kS$!3<;+O9P2^h8yf$&2Be1d73?L2naX( z-{9hJ_P)U@(B@Ml22<^)$$X2WpfV*f59p{OkY0#MAQwSYi33@;IBb9xmFA?{6-ffQ hjEX>_SOiFXU}j`wyvrc>g@u(-up{#;1CRo10{~;=SULaz diff --git a/auth.py b/auth.py index 45a21e2..6206006 100644 --- a/auth.py +++ b/auth.py @@ -54,3 +54,5 @@ def get_current_user(token: str = Depends(oauth2_bearer), db: Session = Depends( except JWTError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid Token") + + diff --git a/database_model.py b/database_model.py index 30102fe..a5c2536 100644 --- a/database_model.py +++ b/database_model.py @@ -1,22 +1,31 @@ -from sqlalchemy import Column, Integer, String, Float +from sqlalchemy import Column, Integer, String, Float, ForeignKey + from sqlalchemy.ext.declarative import declarative_base -Base=declarative_base() +Base = declarative_base() + + +class UserFile(Base): + __tablename__ = "user_files" + id = Column(Integer, primary_key=True, index=True) + filename = Column(String) # safe unique name like "a1b2c3.jpg" + original_name = Column(String) # "my_photo.jpg" + user_id = Column(Integer, ForeignKey("users.id")) class Product(Base): - + __tablename__ = "products" id = Column(Integer, primary_key=True, index=True) name = Column(String) - description= Column(String) - price=Column(Float) - quantity=Column(Integer) + description = Column(String) + price = Column(Float) + quantity = Column(Integer) class User(Base): __tablename__ = "users" - id= Column(Integer,primary_key=True,index=True) - username=Column(String,unique=True,index=True) - password=Column(String) \ No newline at end of file + id = Column(Integer, primary_key=True, index=True) + username = Column(String, unique=True, index=True) + password = Column(String) diff --git a/main.py b/main.py index 98e85c3..c49d746 100644 --- a/main.py +++ b/main.py @@ -2,7 +2,7 @@ from fastapi.middleware.cors import CORSMiddleware import database_model from database import engine -from routes import products_routes, user_route +from routes import file_routes, products_routes, user_route version = "v1" app = FastAPI(title="Fastapi ", @@ -20,6 +20,8 @@ # Register routes app.include_router(products_routes.router, prefix=f"/api/{version}/products", tags=['Products']) +app.include_router(file_routes.router, + prefix=f"/api/{version}/files", tags=['Files']) app.include_router(user_route.router, prefix=f"/api/{version}/users", tags=['Users']) diff --git a/model.py b/model.py index 9c4bd8b..2f11cce 100644 --- a/model.py +++ b/model.py @@ -1,6 +1,7 @@ from pydantic import BaseModel + class Product(BaseModel): id: int name: str @@ -21,6 +22,7 @@ class User(BaseModel): id: int username: str password: str + class Config: from_attributes = True @@ -29,3 +31,6 @@ class Config: class Token(BaseModel): access_token: str token_type: str + + + \ No newline at end of file diff --git a/requirement.txt b/requirement.txt index 96c59e6..f081a46 100644 --- a/requirement.txt +++ b/requirement.txt @@ -1,9 +1,13 @@ annotated-types==0.7.0 anyio==4.11.0 +bcrypt==4.0.1 certifi==2025.10.5 +cffi==2.0.0 click==8.3.0 colorama==0.4.6 +cryptography==46.0.2 dnspython==2.8.0 +ecdsa==0.19.1 email-validator==2.3.0 fastapi==0.118.0 fastapi-cli==0.0.13 @@ -18,19 +22,25 @@ Jinja2==3.1.6 markdown-it-py==4.0.0 MarkupSafe==3.0.3 mdurl==0.1.2 +passlib==1.7.4 psycopg2==2.9.10 psycopg2-binary==2.9.10 +pyasn1==0.6.1 +pycparser==2.23 pydantic==2.11.10 pydantic_core==2.33.2 Pygments==2.19.2 python-dotenv==1.1.1 +python-jose==3.5.0 python-multipart==0.0.20 PyYAML==6.0.3 rich==14.1.0 rich-toolkit==0.15.1 rignore==0.7.0 +rsa==4.9.1 sentry-sdk==2.40.0 shellingham==1.5.4 +six==1.17.0 sniffio==1.3.1 SQLAlchemy==2.0.43 sqlmodel==0.0.25 diff --git a/routes/__pycache__/products_routes.cpython-313.pyc b/routes/__pycache__/products_routes.cpython-313.pyc index bc9fc154e037312c5f6196f4c63f8c75becdb930..ed383cb3fcdd60d096cdc8a4417e3f8200dcd3e1 100644 GIT binary patch delta 20 acmeyO_C<~RGcPX}0}!kTc(#!{Qy2hAC<^h3H8%HiaWgWSZJy2jn2}Lsay@U6 iA{zq}N4s>RbVuZ6M)8kKjEs7pnHiWQz^o!|peX?6{uT59 delta 83 zcmbOvGf9T`GcPX}0}$B1d6MyRBkz26MzhUp*&i}7s&DS+;$~zt-8`H7F(aeW5jyXw$e_ zS>ojb1Ciwd0R4^sf5-n|1R4`3%m?V;gkOTb1Lm4nCECe~2FLPaj4(~#0mGSOgq{Qn zI={OQ*yxLh=$Xcb@n$wXX29tK=GN0KA>f-_h%ZpNll>BO%vdScRt~9s4ADvU66v=2 z$5?zVnL4}BrC0#z2%84trxYP&GYc{UkkwuRqA8MMRERi+(P0I=BhYU@bZ-14ZxC7& zOV$Hmz-Z;dg2dM@(!E)U;Gyd;+srB(BuE74G{S|Px*gADCAuZa#M-X<+J3u=!bIXq zl~RuUa&CuhPcKC_G=N{|s9hXV<)NtV)KXJGDY5!(`(8eepy73elAq!$=3*^9r&2j< zJ>!!&Kz)fsKu!xgui+w~yN=d7fymUobr*{OQ4Lt@a4n?P_xNCr)HBJ+PcfbqTIM%l z(72oh7q$$*wDY}&rJ11^x5ysNgIAUG8dLY;QJ`|dWb2#yc&55S692UJz~2>*RQhYp zv%@fi_wSqa^YaTp?*EsbIB^&)H-B4X{$?N4-}L-rZ)E8}OY@KZe`)zYI2ZrZ&@1En z{-z}~-%Y?*z+9L7dLLSW46VWJ2HF}JxTd%i;>N1^%J&!7>MD@-nV#sx{6fO4yF;dk z({7^nEk?33On4W>@|(_p=0~S1KvGa=5xwiO?LKtI@w4&QIH5TCctC_Ya`+4eXxwXP z>Qpb{5rxq6Q3;e80r||L0Z~Iiww?0IOR6_xZW{g4vbmV12Ut0`aCb0v`!u>Yq%m(@ z3L|xFO#gPh_Mp;vLMua*Ir$S6h9w;>ab}!;B_zge=iIq_K~Kq0SP&IN9z+=Jho`X? zg2wkr_%S*!%MHGI)3-qgb-S8$F+5qS?@$-6&0b<(ZjL{8;Gg%+Akq}TLwsCkff^ixg#Gfy5-kF4mhlk0V1HY6k7$DxLhSrpvx4T`g>n9-G9NuBG7p+~_ zXm5%6)e&`(_lC~Xz6qJ}<4;=4DksI8I4)=yi4*S~Nrz9PdC(qb!uvMw>M0f?%UQTQ zxm}YCgcWNE_7M3gCL*gvIP@hc$$Mk!DSVl=N@BfcVyA}W!L{VZDUEdblD)X;<1>(r$u0j&{& zb(&2GJ-q0}bHZ<*tOqB0r>H{@M>%(hY=#%fjwLwl zo{iS?`tCgVFPO4w+#9j)7J@Bj8XMdnMN5t6`TWV(cbP++@kr@ zX9r&)lJTWp)T0dfm|W-YL@LYZQ%oFrlz;V0{R)pk4-j4oR%y1omaW8Ghd`SuH!q2W zY($hon_ztJJ=QgcaSX9w#t&U$+lA%Qv7o#--uiZ*QfA-?G3na8@K{?l4M*{ajLtQe zFK<#KQ$zbO@By{je@Z2Z!Nh_wXOOyXW?Zyca7~rcfNHk5GT*Pq1>~xlQ!%@BB2Bbn zP2K2Dfs^IArLDfsXM5tnM~A2XBSZx!57FxrMlgW;4)2qCndWdEHd8vx(g#d4> zp(Pzr^2rvtaLyz8or1kULBUVVxUWKk(u*b17GN|rI zPK8Ux6~x&gjyPL(kxSezcmd4Q{;dOVDnuu=cvbxZxSh^vX%8lth9*mRs*-(q;uK%( z09qO`+$RG# zZhsvt=1#}C$_m8|v$t;a_Kmay%Z*`PM=MUAjnLy10Jkp+v!nmua_dPUUA1Ro>`6Uv z$exu<{P29wmD6Af9Z4}hyZj3|C{s?*f>uV&xqrcGF(orry2f{dO9+ZkZZH0IoTyOI z;zgF(wP(EYKplvt1+c}Cla{=EWy$@XtbWSHdD9#rxSzY}?PRXF`U2*Sx{5-397aaLzxJC@wpJ2hu6EXl(p?VQ6q0g2z-)b{e|`KHFto zKBb4qp|Xo|k~@Wg0eFxmCdr^h+a8?jH@>LS$@p_UsBAu{vL;CnP0CDcb4wZP6lBqasVHa%kS%5xo%qu8 z1FCUQ$xJteh~;ojCFLqz35r-oknZ^9*#z`AmROM%!!f6fy2hZEaeX~|N!TnZ( zPeR$H*;hEDvn`A=Km}pKJnn-n@DWlW@XRXy%>aM$*og5sA4F{TOnkSI0099gGxd)E z!z2jLBKgsk)|!-cfa+ERV~ixLD`Y}&bV%UM^miuMHNIPi=a#L*sCc-6y)#9?jmc;` ziS%OON+h|)+Jnp>?ApgOqKRe2M-|+Jv&BmB^6ctJn$I|uERTw^(EB&@EcLKeV+~b? z`6ctuXnq+qRB6Nhzz(42)(TskZI4K*%0BJaY94(6%!w@GyM1@Lgf#R6IvoHF`EA6h zZ${^fC!dDz>Yhtfjf0lhE}-d4esN2*FjBT%ZOR}Jm8R%_LW%g;rTl;vrJCx8^LLuO zp`y(8BBoTgXAS^hFohwSpE*eDF1VETj!Qy;H0xV8?(cr`R{+i2wQu-BabhxIFFD54 z>y=T|Tt!GfxH}?m+e!oQ$}Sd=ImFZ4A1^$WmUa?rX#Fajypo3}9!Z^~LpdIt@GlYs zFhUnu>lcX9GT3(2FP_I@q^_=d1UcGewS2xKc&z^f6&(yZxMsFnGY;?acwh9deBQvj zf#sBx%$jKJyyJV2v~c6v=neqFmPNfd>G-{7?=)syPQ6xMo`zW3DRETn)6I9p61sv( z4yX8q&J5gatml3ar|r&&W?I$$n5`C&&nC)K*O18u36v>Pn&2L{Bl_!7m`t)3 z_YtVaS0;8Z4i>%Iz9rJg4>^{_UP7nj9kL8W|&K^@PrJv*Grj4EIV4HM>i{>B4-_vpAi$|xX>)XIRxvtWvA?en;uK9B?ShVal@`W_IPz4=^wOn#}XlijR?Q0g-y9-Wt_gKJ{q* zydzyy#K`1^Vd_&@0?f64X<%TEiIO7^Vi>Dj7z+!@)@oWS5pLSMTbpi{PC30DD0W0Y z6$eeVGSf;cqYD`(OULZ_*?WQX*HyGg^Ngn<)G{6|(U_PEQ}x2J*M3!l{(Fjd%$~|K z+98p+Uqxdh&fW-xU0Hc2HWmV~mJtQCdxb>&7&}nJUCNzxbhylpdTu^v<3o5wMXx!v zKm*${&-2ZJUia+%%ChS5cMcRDvKkqf5r%QCz%L(sd%!}uTVGzWAoTj zaL6rfh9M&HMCjQb^rrOhw}gr%g}f?cO_URXF*@i9--X2I;`UB8FIJrMNc$x1s4r#q zHL%0V9a6;A1E;tZwseFV1x9{2WQ?$jPYW$BEN0EiF35pZkuH?7MXZhu@f-uxcWJQv zg%cRi+UoQM*RCM+O=rSX6FrCYoy0VYjz@MDIp{?9HxfgD&PZ>J?i| zb<7&gWb>8r$5}fA_wv}8efA17MB0=#DS!<}#s;}+nZ@_fdNPy)U__Tw_!1r|giK>o z1;(tHag569DJIAH`ViqrB`5wPq7>rW@lO5eKbFj1A@`M?LY!J?V5}J*sj{J^W0`rS zRMpxkL?z8mRU)I0Sts~H^H$>I4BH1JLW|1UC&VaA9JXO%!8EoC26oF@0$9S0Z(~Im z_zN!CA#lnrFb&`;BhsN#QbP8p*7tw=$XSXnZ61&c#aj)*W23xytOnX9ldoou)@Pp9 zLhLyy+bdnXb|vdmB%9Mww`fGfGBk2w6chH6nFoR=8=ePqb&a;b!$CE+rM9!n z+gqJCuhOdgwj|A>(w=ZsuMXu6$*G|rZ!6cew>CJ~(P~PYxN4$UcM@G|navI|ECjP1BOa`f4K@ zx^@vg=C2(+-Fxg1tcLv_4|xjk$I1y?D|RV<}!gAf_p~;)rD~3 z=kZA~D${!&ygO_fKl8Ea5#_NyM&KpnBkEa+P zs!k9{d#+qi>FRl(om@(LGnnJDdh&ArZ;Dh^JFBU{2vs(IC7nyip?G2FOgXkWIw^Mw z*N((WVV00U%TKnUD<-A?3yYB?F;tRGtnfa`xb1JSKBN%1H@iis>*9CroIg+T)MTE3 z<`$|XR=EV!WL%YD%sTLFg0X+D1~n>XLUIm$EvzqEsFVKQD`aUTQQB_VLUoGA<6)63 zEADpOg?ISFC3b(C&#>cmHViG5AqUp(sla^yIn~}g9n8IaSEV$Xho+9m02g=wS8rgK z7j3`-<4)es+k&WbwM0D`2F8MV|Hz~=s0>-VRcih{LB)?1zsq!M4A=#e#?H?6u60OR zg|me%=C>xrf5=O=*t+t0>d}CXO?k`1+l3O6A8Ul5-NyS7X!xA|`#|}~M7Vxv{1?H( zqe{*nf1Usj>x#R7hCrP(BYK2s2qcBIyBTm6TsA#@g4!#*L?<10+r4wc&Fgy| z{4N*qwP^zyIuU6^S;{3~

M;7dl%WTINAJL1xd6jB<k5qU@xzsloA_aHB6l2(gE{cwjXbF6mcyop2Wz#zkI*mRV#*#e9M8<3 zyy<=O{6X*hA=XoO!6=ATS;jMtQy``5%6e^&%Q7;-Fna-H!Zl1rw~HYrcRd+TqQ(Z% z_yP>pTLx0Der4HBE!G`&4=%?6#|F2b@*>3M-g-bP{+@6wNT%=%Sn`)VKJQ zz@oh=MyrwPsB6Orhb4(p*qpg?{xFxqE0oe=x$UW4p}kCI-x^%m>gf@E4N-%huMe?Y z;ox4?!uc80awfQY&^$i(7t4R7O_gE?&mk2AewqPk*u{@`Y2!`n63?N$8hyILt75M? zd`=*AfC7N5lUl8QyL+c|>Ess;fG3R|eSyqI_2}ApMCPE52Dn;`G?3`!HryXWjLze7 zusI~RJEQKPGHUQi^p`u=9zWE^4cq^K*oDu?PQ<++=F7^qPd+<3K6TKgm}UZo=H$UH z{Twp+jv;0>18;dsCxP6wK7V~Uf=UX|22HK^qO*eR+K$%P6#w~=?-{nvA@k@eQ1d?} zm67C;WYORG<{jJ?>}ekHeJK9?W_q56-#&YX6E7visyy&dY;jy1YTCZoECt!{TpU(*nAMog`zuTR> zYvVi~4qiMC#)!?nLShOrK{b<@1X*E9=uxS}i}gaU6_9tPHNC*=Khq=kXrAJ|`4yS4 zkb0o|Lu%i#S-V}9urqAMZQcMPl21}p(BS@rc7Gr665g7y{;f-o8NzaE7$NvFb7yn| z=E7La;+Y_BnbD`J8x!2F8EjA~+R}$gLB!oQ{YK;Y!${R)`mvNO z%i4FDgrS{)b!TOsw;DR;J;kPQ0~z)b#d=@V=?rq_nYccl8Y6ZP2d3#4+impH2mSzL zWWfa;Fx37l!e}I3Ut`|CcDJ>Lt(%;C#^u}6+-OR#v&_9MqZ10YV}$oj^Js z>oN!sz*b%VaLka0SER)n)xr^=cMeJ;D(GmG7VYTOU<$c-(z}BaW&h#$xp)t-dm31R z-9d@P)LP(h%~*kvVWm!HWG{xGU~aav(|$hzw=arY)KI@?lbilI`T60U=(@AioEjge z?v9{;7phO4%S8;~lU3JCa{!~Do|+h1MTxw<*!k&k0&C4J1BrIG9EWblyGyrJld5@G z3!$OoG_+gCZy+1bXDYxIdLg^cLsCo3qiY?Z+-L20b~<45dK6hWs(}Fw^CydmXnMe& z=^+h!L64vlFLXkR7(&O*qNBy0xgp%8<@j=Z03_Dg_KEO^4mXB>cGwgSG{8BBA;jJ8 zoa(2QNl;E>RnvWjzrToJ6%+9EtM(z{d&;afEvn;|u?@WEp-F}CVCtj^#B*;~9PVe; zaUjxG!l<+V@!Z&q;|zE`aWC?G9!|uht!A|(K&oMNPM0Q3qMRHd7B#$mWadwIXpk1#dT>Se|Hn(k)Hu~4d1+i4v6&+ zP#mQoGxUxSW8z2WZ$q7wx+yF=O+Cbx;|Nd>_nt>uPU)>s5$iDYV|0$a)v(AO>u9R|Sb%_P#m%I2%)&1*cH z7SPmAU1O{14^=>6jUvGF(k-`;>p~xS#3HixJcyusby;nf#Mgbf>Li|0D&{WI`7m2V z_xC$3JBDl9PRTIzu}GeGxDN?@+HPeZfXgrczJ7QpRNPOld7%5J9 z7O9C&p3rzbat98{c!gSJ5*ObRe3R=wN9FNjJmjTCXa`t6(*gZjQ*x{kNzw@4LU%R3 z_+2sKC83Wrtto^ly*m<0Bw2Gm!?xyN;?vdQ3O0Y&Q-0Ak^hI9@TVOLDfvB%YzIWHt$l3Lt0fRpR55r-Af%4D zcgu00*;16<>1}&Nm&}4O- zw8o-WlI8o5dPJEZ;6c*)fZhpK^o{)wNAaa}BAfQ$kLANuoDER;E0V__4EZ`xAC1+K zO*(#;j6RR$S4wc!R!S_et;y$j;@pVWQ?yN$lVGBuMTR*@uc?5*t@DG4IGOm>HTM*Z zS2&{|4$C9^;JXsS&d zhupq?uGgtZTw$c|OMvC@TMvd*kQGdWgfGVm%y+>$$<5@G=bPO{lL67{;Dl&FD>5gt zYfIgljxIKOW;Zk5gEp)EE>cJ#<=OVTbr zAB_PE&_jB4oLe!7MQw<`Q0vj9NS}7_Aj7jt79p=MWQ!lqp^_+g9FQnLQYV#9wm&Z_ zlAn;NmR|?9h*BhXm9E9UyKO-=&lp4`qj&e_c@#(t3=}`DlVvjI^2v_Ff^wNVbILq& z3G}#`eIfKW+QCOio7t{!C6ixd>Wjl9=AMp4lCH`ca!TCPOv^ML1VE)F2ssu~T~xMG zcVI*e-k_4AaDYojX;DiBrg*eg$O!g#0*xgeVn4&~?&U_g+JhEuvOE|&S!8hup+#!k z^d}q*7d|ulRdiq;uVt#4~xBjT*PsOJe-i|(gdZ4Hd-?dw*gE6%v{rLt!rci@GkB1FxLx&{T zFT%A=r(ZHrQe1>q_PU?SnlN==5AYSc*F|r3yQMUH%IeFY_c^PpI@A4B80$BOqTRAhUjIe(afLAv~?W|*FgUO z=x5P3XYgh2Y(R#(zc5_87m^%C+zAd%jej}roA=SC>gcvS=@L!p5nAtft! zzCDWALjV2=-omwJn}nbc)rs%5ql@8$QN-;eib1?3hY2u=(g-(1o9D@2HeQRZoQNl@ zfJ*7<&0iKA4`vgl6Bf5}*{-z0Jf#p&2Y9Fn6*H@CX`n(mGCF$ef02jQB-KMUbs|vx zDvSbZUuo9JmeC*HFyAq13WCa#!504Xs-wcmO1}HKdQBy3y+ESmkRhEtr-l%~XF@H= z=|p-t!0&4=K_0hSWK(4bZMRm#6=jVe8KQzc>$u&j%|=9lPEH$a z0#5@`g3>Xm6VNk^XlXo-iwQRt_ZPU*W8l(BHhO<MZ zc_2R`PP&aVVb96sH``-m#rKJxIW!VS!LHt8Bu-%^9`mhI*kH`8x137@afD?exP$I% zZ{b`dtbB?Gcv3*{!8v%$PQJ%qBrd2%J@SjmJqP<>a1B+2?`k1??h_MIuZwh2J*~>@ zpa|R|2ER=_nj0fhzQbF9K%t=Bka$3oT}7}#rd?hO2(HAZUFBCA1wC9&oL&>yqpNp@ zbFp(>#{L}-^UCRSTVhioO={bH%P+=t6L^$^;Og9;BgPmbM1+@}&u)`CBCL7X95is7 zEC(u8!dpmF_-XVa`BpA`FUTkp7BGIw8viSq2PROM3)>$Yh%})u#9y{?fZ-(V5p7?m z`>Y|0ZRVh_Shl1Qt9frgXc}0;r)BG)XXDZ?_JQdGs?6&KnV`5|ji@z#C&D^Lz5Nsn zdF6PAHdqB$iu4n1XM&A^;d)^gnhlI6?4eNLQ`qw$ompGD-3BnHD z_jAWSrna_Pq+aFqPBA|!&vsl-VU+!8xK6cDW;>Owy;HKz)fg6Z%pCIaRz*>Hv(QQ{ zEkrNw#SCg!RLJie(B3nSV2EEe*)I`4Dm%$(Ujcx`5fWmEYCtvWSNa4J0JyWxYZ`>q z_2SmWj9pRIC3c1pLnpL9P*j)}~&6OLo>PBQs6#jb8rKZ(d8%CyGJ z%8u^`Fj0!}O#bUCl_l!NnP3*toy4}8`L&aS*C@->iO$|`+#)ugda1Cq82P<5_R}78 zIZw-#()92utvP@^nfiMYuC0Yb3vRh~qA0^q!By=Q$LcZj{lLmA(%ELL(~N4Vwse3J zwp8!etq1jV>1fxJgFFq5gKS)4Dl3yPDUHqR&wH|Qye!8VAYr<&QAfEM(uHE8+m#gd z$wd)V9?s6iQVzj+_K-FYom*(ZrBsFw^cp3ixX zGoVk{@5UdYon1#-Q=)a%AK+wyMBSw3%eX{_>ZHJrbL}4C3hkJD(QCkHVLD~NFJ#O0 zH?f2>!&Zt(G!a%pS2{1r;;a2Mq}X_;b`$_HP*WlbI+YMd9=gOSAWx~`y+`hn@pzeTG4{A;;L5Mg9az8E27eGUg zFTp2+LTQa2FJTFU5YUByXhwaL?w}?2hbm;w+=nh^pn4*Z;Np5G8x26*q31o2R8lkZ zvPynHLig3f7nciGrTG5XoAx$n^k73xefhofr)Z}6-_xvh3^}^j^ z3&bu9(4#dBYt<;b&QFB3`dGKTYt3fwkbkV2R!mPtKwiXSP*)$#CCQ{*$jPHPK6MKwVMsBeZ^6G*i)ypA9*3tu9 z!y)d1jy`e{GE+(6yq`AiasNL1|4J=tel$s)2L%8a!vg?7{u}Y{YvgEZWMxGAucrT( zUZgG=fhh{t4R((k+&1Apb*fj%JTG={vBI+56cE#;U{PF^Z8o>H-b5Fyn_~|FoEa+! z-F5^g08Kx57>{dEE1roPca9^@Nc}Vzqa6Uz;jhfwUH;u(-rmmSu($t}$|0Hx)gK?N z$*pRKof)~BN~AZ^#4K_v=24Er!FI=;+e3 zjY@3^e)f8YKfk0fUi+@^=I%c)4pV{B=9MEP76!-j|{aIU*t{$Ib9>hY3#9aSMD3 zC_@K(!LOD?O~}X+X6liFfu`J(H=TlxjTg!euG)Hiv<8Cj6NAfn&N3=>f7X1 zEHWz%$Cr#R_od#G$CZ{_?LS`7ouEb1Hc-4+plL7;xs7KwdtW*oE38iza_g3sdl^aA zI~EwOFyUn4#hRz#JEdS1Th;i8BD9)&Ry&w17Lu;P6eszVNwS9Wl|!dE2Z8{mCj{aM zqBO>?{vy6maKs^*Y#l_Ac(=x6dO*RHP2;r+AwTB_XUWfG95M1T9M~9PRro_A@PsnP z^#?j2Ro1`n&xayXVzPTYA3r8WV*c>FJGDJGZyF^Ay0x+LdOgf6&}Q}ceB4bX*j%~B zjr4pURjl}YzWq5b;=kxHsmbB>c=}rL`T86Ly0Ldm4Z)2?gF4~hakj2QV=+|+z`cJ- zU<-vClXOgw%LOJ!hjPaw{N?54M^?Y^i|kT9JgZ-NN4hsqMTZztI5`I?Pe#OnOxbyTQ-jc>6S zL2)25WmYSHbsTMoDP~icp+0W&&uyHZEq>nue(@xFWE~t!8m6b-Y!|56@bxoyd#E%~ zilMpSHNo)xC{G3`e7K3qG*Q_7Dy2Roet2evl!iFnLtrV-DRSe>>^<=k9Gh=}QpzSJB&hQZ}#xBfswn%+a?;}UrN zxRGVUl(OM)`8lZ?UsYi^W`sc)oq#iLNTu)0wm#ta(l1?SRjX{V?D0HrVLyOS0eK_jEHm-=Z2GRO&Ba zvu-W9PD;l$!rjZ55V}%M4b!h!GZcP@woE@L_Iaxi#y60- zIqyK@S}ElkxbUKLIW|U;T-*5pl`V7s=>u*S8PSTRB?oCZJ_gK?ehcQ`M(I+bm{XU4 z+ZtWhLGSz*;lA-+xCm`SQV32^VaMjT)I^WdGC{%{{rN?UsP(gdBx`z)4)LDEHeVD( z;H=^14US5bypOgNUmWA;r>s_}HY_NsOAgEeglIOU5ZdS>ckfrRc#QtEFs5KUCXzRs zF=^rstWbnm9)hN)a-X10Yi)rs6Ea=2BEF6RPq~7=2aYR+28)kij}_(d5;aV=#(ELu zBNdm-e~k#~U&|o~R-SC}B}8gAEh~9^3XL^U=42l|s&0?DGV~Fu)5SaK(J1R@apS8w ztzwDYh#nZ6#7@6a0|(z{C>!_%J z=WFgCq6$Q7G^g@Q(>Kdah)A24sKFg3YlaxlVcV^@^cgRfL+LB4o^LNlSUJ1G67;|> zoo7nqmG#%4MEfsMjxiyg&M7;$(PpueV!!YY-MI5l!Ac}0^OCta)hL+1ZMm0Sd_0}^ z)~LL9tqanHNA8#BRC5R7emK^7+SH7+Y;TQJD12;&4jC0+?AD_!-&|KT?Yq4E2AtqB zT@1c*?^q#gy4cbiyLizka-Ka+dXLIo!+rkMvxLLrm_-Ql-2T*S0j*Ia!VE5h! zvR+iITH(}X+`@d@4x7>(oK@4Udc4%>{QY+W$bT(@?Au=ybbng}-~j%EU+ZAx=xAnb z;_y!aY*e+f*yFFn~E@jm;)i@o}QDnN1D_J$s$hRVhCyQ0WYFYK!i9@g= z-3xCOhQ}+uY4dRHkAbBKADmFpGNg zT9JWNX;GVJt}5k{iaQ8A@C(+C;oKhlHB?v(V&ZRA$Pl(>EC|Kmi3atZ?&Xn?iL5P+ zQRb3quiyt;3D3C>IiT`GhUv>Gcz@d^$hDX>x6v_88Qo#h$)(Xb%xyGluL6gU$M0l> zRXAm4@B=bucSE`A!IobP*q9p z>u?;Idr6bemwhMbMfSi*BR_8Mf}S-tr?jRY=Awp0S#PqbltTY*66xiqT-nNdLX-kR z&4z$;uK0UMvV}@{OAz-g-C+AAGOzH0pnHua^1un?n$IjImqT=&^9eV;lIJm=%e*c8 zs&-Y$95BxZiu#Ju;K>xXB@orNCJ&cm+O-^@Rob#4UJb~AN<%@pogW3cZ?~6c+g?l1 zf)T+=F&^fc!YyQla!fBUwjYw;j zzyVMs^DC6`5c}p8J8xNAh?_^+^%lfKT)AM%^DTb$?Gh;ol^)(k&d{eBBZZZZ$4*bZ zb+RCJPh%+@8#n zI5KUjDzW3jz0Gi{E;ptI{BGRC?bs@lQL6i`$#9}-oAf0{qNjjKjDTJk#T|1*q3Q$; z12_&c+bU8RZl5?cSqx$_;pK#HpJtQp0D=lIhn+uxDm93%wFDMHMBOK(WiaB}D`}d9 z>F#@$dQTiM^2aolqg)!TVK8G{t?J7UhH;pn%b$m%k|@jW6Y_9@Dm(XN=|w8A_iY)g z4Ou7Po6YzUvlCmR;e|3R4TS8FzbNZ7IpuYuo)}<~+om@gLvlG6>(Wbqj*W}5Q{}fb z4O?{_!bMxmkFzPFn0sW+3~rhI`us8pE3B=0#|puOqAJWGrltWn*qpQ!=8g>uR+gxb zHxO@f`>GUNZ+NlV7C-r3b26Q*Vy@Y(KS~BlTfK9lIf|n`#ViO>x7>{?G0>c*bF0aD zxoxpa6kVU|0#zz=c4O399IGVL$5NDWFZ1^)m#W98tv9+aYFe(JTJtxu9F+@_Z>s`x zjNd2RBS4|epr2B|{h^!~W^qtR(HZI)xo1W@6DC<1eu$tVso2H@2%(ZJ!EU%h@-A?2 z@EVlvVJaw^NssxW~^0UjMa zFd8X6b+bJ4E9i6W@hHI(KXQw0}3^9cKUlApD;-hP9KG zzLEXk3HILwhW1+Y1`CoG?W_+S^dmhX&AO!_LSzepFeZ{%IFE&)JC#rb{#e6f)s^fc z>^iL}Z$*z>yv8tNJ+l0AWYU>e7#D(@5YB!4$)?)!MN5kxZ>zE0zVO}~Tu$DeZ;*B4 zV}HfNo-8hE&E+1Rc$V$itNiG6hzk{Zx7WjyYK_hC9!sksJuTZVnjS;nU{Ia=&(tk z;k$lWYK=jQByd1&3PLpvIO;Un6c7nVp3UJswwj;vO4@Xu>GPA2deDcAt>hi+!n%z` z+4+3y&A^VzcPjf9Kq$qrbQH#%2ehEE^^L>|!C923KU%1GD^9>#VBYfA#sQeY>v=_1 z$uI-lx(UZ82B@#<6BqFG=5*;m`IU2p5U@AujYPYQuiwK}rEB=8tXiIs01AL~+RL^L zWOSEUf(|zYn!Bzr1LeJABEq|_FN6ER7P|+-mjF~P5^#YiS8!nRUSO2!#S={!TJ$36 zr5A*C|NV{oc4sQy6mJN%O*skm)N?j&?+7-k|6uBexhd&lRkd|xcl%^4m{H*AeA zFOttua+`D_y%?ih>@rji9-pg z>^Va4eh4J{%?B1CvXnw6#WGiCa)_$_RXOfE4Hm~fog20Jkz5xM4eDh~tFK00*imo< zY#sZ5BA{@1kzV4h$RSUPT9dVbkH#jdZWGR#MYtI}9kVydBH9kyvTwVvUf&=M zbCHm#oi&4fOy;XWHfT*kYnaQ_L5rw7!5dB=A-&n46;2SdOZchdQam~sf?`n?(5Ven zEZPT&;83Q}>6}y~J{*GL{l|->^pAI7Mlp{JRUES|)QN%Kq`lJzvu!j@fP|#5uobJF z=1{*uJ=31x-atNhMedmjs21mPC!Fjf8(fuDTV>&;LC~m0)H@ zBp~u@hp2QAM!gUs7PHzv-}bIeHy^&j1$k z_{je;qohOB&=aLYtfgtF46=C#>UJC}Z)gl`-UOqbQl(7{>5Dvi0*Oibhg~8{BIHRG z^WxCmtZn3R5!QT6E*b8CF{wNjgL0H6JV2oK;|03diQgzzXSBqUBKjy z7RY1dAu!HZNd1;0k!VP6d6eF!dPhn*j_ZsSHF=>avMvE9FL~c7fLBS1oBd2-0Pp3$ z1xr5+!dsSh>G>?yHAX15F_nU4q@dD{Pw7s7DKr=)i@84h_rEY3U0Ji?|E}E5iva*& z|Ig=zgQJ_J(Z6fAOYIH&jZt_nS>kGVrj9OSC%M7=zWsS73aTdk8ZKd;SV{xw{*`&3<*Pd;qdAY3pF zWHL9j9)GU4yE!gEgvaJXiPgtxJ!CsizYa?N{wo_9iOBbqaD@l=ZERHwOzCn{r=g9G z4!4~~t$fATyjQx+G7>- zA0JE;YfSKB{gl7<)ck;wf1tb%ai8LR6gQ4)Q(+r~yx|I-6Jo6sgKFL)tK#cp|2%AL zdNS`U=Cr75p!6MpQ1-KRBYq)A(df^8cT_Zndg#NN2)ii{Ryv<(d6#Y>q7*f~aYXNL z$64O_os_M9C58F%&Siw}tY8K&C1b9YRarqu-c(p|c|95FyG*Y$AxmpNt9_f>7eRjv zL=bhG98sX!NqH-Rb8H^XMX`B*S83G=eZP)EM}Awttk}O~oFByp)tw_{BxwmXeV?j5 ziOK+XX>h`Y}( z4BhsGdQ%wIu46TdUyz;0i|f%D${0?1^P#Bi&a0aCo=)R>1OCzmU-Cj43~MB-ET|gM zy29#sM=sCn%frCHDbHIFWM<+}5888Mo=fMiWm}C20 zmuH8iA#lJ}=s#>n1{if<>&O&65c9WzRd_d1{q$a7Vh!xKjm_|uPxS4;mXE|g3iPkS zz5TL%8FAs={My@ub@6$8{_Co|YjrP2?nGsfoS3G_Fj7x5CWbvMY+=NZ1W6trHM>0& zk2xU%69*_D%wUIi5rIm3@7@ti6#JSvDU!kputb^M_ei^Uyk?W_xhK-R_Z5!oB?O*q z6C1}O+&@@g&$-Ji!7p*NPV)qf4#-0X^%+)hNxB*?+>fxnQKk$KhMS_OT8}h^FPZ*|9cYVH-x?fht8BA z$6zY-%Xl_)So(u>sr9eJj?q-;z<9PjW+6eM>ri%z`ZIgFfs=Q90=< z($KuPk9uOwYdZOXPo?QX@5*4f*&$0AcI%WQSEKo-ngbV`)c|>UJ zAQPjELA*n0{|Bm1x-nIZ>8!Ow^^GVwJN$kib+Y?Be=apVDz5M+eku%kr%wY{vJ*aq z{8pT=y3v3>^H)^Jmf%(i6H=3#TJh?RCqUn(o%k(wwf4 zA^=q6_>7qBkC3Vx6&(eyqOOv6ac}9Tl%M?D+OAWt2_rp3G3St4tP+n2|9qC|d^~Nk z`t&_1!6++4j|nh{LX>lc3F8})RPuR7D+HcV6bF8$r3D*Byw8zheYH5%xHr6#kb1l! zy1bQ@L%)3T#!K}TqoS<8<8{APzW~oneCSEEf&Iq+*UnW(MYX+cK)O2x96}K3mhKvw zkp`ukp-U8{bEG>2qy_=$7D?$4>2~OnOE=$KKkuh^I!kJBPM!a_7UQTye!($mt7X)VQ;TX>fZafv2$MFS9A}#9;p=(sQkq5^&iQ_Z8dfy}{WgC23pRnuWwt(NAaHV34+zW~W$U{y z+GKeqT|yt3c4?IXdz22IU6!?jOK>44RI zD(sH(kBOtot1g|zS3$byZ|U0ahhU^an~&y*?(G&Ux)pby^|6e)UzA{}?$B=faNJk5 zX${a%m%PBzG0^A|g_N{MQRTSLI!jmKN;rmU$8_63Fclr+$IE+gMuZgV0mCDhnFLNF znA5NvlB$z1ZTwCqefMOPI)J_!+AO|xwiJ4mx^O@7EIxBKk}F5H6mgY009n5fABhbl zh}e!nG7SsF+<6{|so@L%4oe07fc*8Fq59_0SzsYP3L6Oh6)8)g1lHpLGGX-p4~G7V zkcIPvkVQrki$v;85GG()YV0-TIo5xGdrXG)ADl1sa?~)#ZO(Gxla6SH=XPz2n}yKP z)Qk9;V9KfxsLx85?G0<2d!i_EH69Hv=)(vuL*{+we5EgtzD@qp21X?JrGvG!RVH8i zLkNcv_mEVfT|R>EZuqPF4P6 z-GhGJuIC}%T2(duOZWnv)_wR8CoS_D>?H(&PP4u}-so2EiSw!z!Km?*B&5Lmo>ll= zj)C`U0}UT9kz?kv**FH)Y}XS^e_of9_+ulmm1rwiif21`KbNu>8(7_(wGh>_xB_`PXxQhC1a*b$I4@^?}`nY|p!kEdEV*-da(6;LrtFX<_AzDOlw_v6k->;@HzuJlzFLs7$taso)_O2KY6>Dj4BIh<#Cvtzpd<1wM8I7p52B+Yg6+l*||sbI;m;dIk}e9 z;VlY87!;A_E8iU(a^aPL75Q?R4tQ+fj~zo((jN64%|{h$w{XFAHhnNz;|`7Li&HOO z9x+j_B(H(57I^6#;$g>Q{AaRz?N~<+m8Dal4_eBRA0;^KEfXIJ^FRj<4bsGXRmRyU zHkGNOBKpf0C`nV|gcp(>TyDmNTn!S_+*g-Zw~mBdvq9ctWpT9y?XG#QM`k35?9Y@p zp43N9vx1cfBqqRoZI*fso2He`tQqr`Y~*V&5hm3&EMNVYLndj`C~+M9)6x6=Eruu1;MpbPi}ZE;QWw*FTZ4zDPzP!w zi*+{=S82ScV(ce%#pzR-nw{S351=SMYGFq=0u7!ef}2ASvd z@CkSzIqcPb+PD{N`!S2>-reLI9;O>hZK?K~DXbMgT9^`YNCt7_N(=vGOMWtartq1- zyWu^*Yo?VJ%CA-#NYOkr;jNb0-b9VLPL|pr{T4!nl7gx&EQm$YU6EAVNimwaD|U;s zn(QHH(~RyPqa;rYa;uN4TvDx}ogpVhZdLLdj9Zy<3(3KR7htbgU-R>csn1uVfkST% zJLa#;eGu0MuZxU@lQ^#LFVUpE?0#n(Q)Iyp0+#7C8 zlp5n%V&XTUIT4=`3G#xoYKWqav2QdeS_ZU}Yyq)sMw71Pd<>?2U+A9T8ibTi0`x{Y z`N$p?$h=GTSv<)FxX`%0>g#L|4Mo$6yU;2O_~7>Ht&C2JVSHp5GgnYK8B3ISIqmo>LurfO3S@Mi7{;d)X_(4g=Vfx^f^if?M@DHlT8SW3z*Qa92*ZyK0v$9ofz{BA0loGuI>8 z5my!oW_P5QlAcSaf4q3tKWJ5IV`c?-dGSF}0ypi{QR6FJOR+?4M)$xOR+gX`kHt~% zmtxdy?%Y~UYfS(j#(sygrN~g}@s8alSxZntUcXVjEtz$tiQ^-Y#GC2&av7-LU}z$w zl%-tIz4E;d9U67LPf|jXB0m;*8gP2aAZtc61V}?=^&z1qoUzUNO-K) z9F(F&3OQjpn9ZgOSDCJuirrJL>Kt@=_yqoL;P1KLCfvIQUT|fcFfsxH@n3HbJS|MM zeo6nPkM{hj8$%d=mVNWoS8e&NA~OIhK2~!b)o3?d1R~226%#MCJK?#^>fX(`+nE|8 zf>#oErd_;F^)Ajj-?=QUgMj>15pS{0|4X^S2l*PBGxLmiWMKRo1Ed! zk7lvcb=H-WjtZW9Am>??!A)&sCw*$oDP3G&@TD|))uZ`=5R!Xqv!N2>^UiZc?(mby zW|>yjIHX+-dtUM<6FgYqGQ8|bLBiH6jQU~xY^<10kn!BH!BN9h`cbNMNqx)+T%Luz z&TY6b95fHyR45jII*GfUg-DM$z`sFw=y5#%N=(2n-eqN*tb zoJAb`v9`W$-f9Xd<2*e&>j9KmZN5hppT8?jxQN0WtGH?f1jM1f{Mo?)26r8U~$fUBCEP@wVI=qMC6OSBW!ayx@m}CqScTN%O*lJ_pKs~xmO>fTxHv3AWd_Q! z_mcTyoeWll4rSHKta~I-G@OwLN{EFd<#`wm|eA$iS zM0qN5T0gv>iKgi^jZDWqcVDLKy`|^&g_?{U{>Jfi0UzB}QmA>YYH6i#Odn&~o>v6V zwECu7{T32F*=_?)`p8`wzPu{~vkohN!t8*ScA|do|u?V*v zG0Up)v?A4&nfx7>7Ol><%FtwHCdGQhMA$d0M8bjnI+fHQ)Vy1hgwNAyo zd_VAtxsYh2mrIm4ymYEx{}l4=HJ*6X*j(8deA0P!wYe)FqaQW(02D8l&ip*l#~|fs zew%g5F+q2&y>Kh*?6Ildh|_Ef{+-WP`6!9XkGM*QqR3cER%0gOFVQ$M{5G>irkwSF zZh1ldzJ7+>y`swrXD#ahbsxqUF4#g1Ny-({18pP3oZ5!i>uEYL(p&3p*gqFrtV3a{ z@q~AH8}MR_1aRRnTrI5b0(NrcFax`Me~REG7yqjkhK~f1u|{fboW#N3R4(y=-{gfu zk(0RSC`%c*oaJCfHhAiKjrnZ-SI-)waLDTSc72=HHa2GNM}Sk%m^ihGZ6P@Xl8DUO zPT3x=hlwR^6qxUF!$+IcIps-os}#Us!hADraxbnRR2cH)$GG^Yl+4^_SD~)S3Q+}H zdE;zfR|9gz47CAVyIx#3G%w|DVxUvJmb^0#w!4~)6@!(iCauamf?#`e0P+eeM2_Ht zH8r_S87^spLmv~p#9Wup3Rl+z*Sg{@{5m*6&}P$@p93o=F_NTH z7lG^a&t`oQT^;JxeEYH0b&?NFTxW;x@I}3OIPO9Fh6Ss=gqGUexT}?$zUtuhq(2%; z*h(WdJr26s9fhEkQ8 z+RPQBC*Mp>ysjZr(|eYx_WJeVD3M~=%)~gUd}xnXt>ub&FQ(Jb8Uzt{iJqvK@y>c* zefF0YOH&04Hqq5lZWeQ5R!BO#cifPaH1A&K6>JhKsD)!rKPB zf3@g`;fx2R+R;uKTq4ksA6_?$>F1!_jU*8V`Ip+*d4NWkL+qKU{Ap&QIeUBGFz#^e zniwaV@g!M1kyxyFkLQCI$T4@s^fn}TH~3pJn`>orNUNG{SNUhy2VP(Kv=}C*M6@=f!S8iVb-m?E2vLYg}!Nb#ku4nz-0e;#2Lv?F);O_*# zSKR#ti;x9>(0-}EdmDJW6zNZB3w#y9-wKo7hX1~5cVU+m?{9o3v+-ADHaN#Ex z<->nj#&DbBw(RpK1ugdvieE*bx8b*CO+Vo;;O+MPR{SP%x{be`xBQ8pgOA+b@jtVe zx52lkEkD6j>Oa7@CNZ}eZimx989Ft7F#Hx=-^TwQNBl%1AYea6K=@l!aU1@7$Nnq4 cLFX6vk8WNahyr&nI5m7jgnQ_q{+~zx1JLD<-~a#s literal 0 HcmV?d00001 diff --git a/uploads/88000450138746dcae8b06ee5fd6e40a.txt b/uploads/88000450138746dcae8b06ee5fd6e40a.txt new file mode 100644 index 0000000..693a603 --- /dev/null +++ b/uploads/88000450138746dcae8b06ee5fd6e40a.txt @@ -0,0 +1,57 @@ +aiomysql==0.2.0 +alembic==1.16.5 +annotated-types==0.7.0 +anyio==4.11.0 +asyncpg==0.30.0 +bcrypt==5.0.0 +certifi==2025.8.3 +cffi==2.0.0 +click==8.3.0 +colorama==0.4.6 +cryptography==46.0.2 +dnspython==2.8.0 +ecdsa==0.19.1 +email-validator==2.3.0 +fastapi==0.118.0 +fastapi-cli==0.0.13 +fastapi-cloud-cli==0.2.1 +greenlet==3.2.4 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==4.0.0 +MarkupSafe==3.0.3 +mdurl==0.1.2 +passlib==1.7.4 +pyasn1==0.6.1 +pycparser==2.23 +pydantic==2.11.9 +pydantic_core==2.33.2 +Pygments==2.19.2 +PyMySQL==1.1.2 +python-decouple==3.8 +python-dotenv==1.1.1 +python-jose==3.5.0 +python-multipart==0.0.20 +PyYAML==6.0.3 +rich==14.1.0 +rich-toolkit==0.15.1 +rignore==0.6.4 +rsa==4.9.1 +sentry-sdk==2.39.0 +shellingham==1.5.4 +six==1.17.0 +sniffio==1.3.1 +SQLAlchemy==2.0.43 +starlette==0.48.0 +typer==0.19.2 +typing-inspection==0.4.2 +typing_extensions==4.15.0 +urllib3==2.5.0 +uvicorn==0.37.0 +watchfiles==1.1.0 +websockets==15.0.1 diff --git a/uploads/FASTAPI_NOTES.docx b/uploads/FASTAPI_NOTES.docx new file mode 100644 index 0000000000000000000000000000000000000000..5e8f7201b3ae97b78788d360fcbdd4dff400dc60 GIT binary patch literal 24037 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 literal 0 HcmV?d00001 diff --git a/uploads/main.py b/uploads/main.py new file mode 100644 index 0000000..1146afd --- /dev/null +++ b/uploads/main.py @@ -0,0 +1,56 @@ +from fastapi import FastAPI, Response +from typing import Optional +from fastapi.params import Body +from pydantic import BaseModel +from random import randrange + +app = FastAPI() + + +class Post(BaseModel): + title: str + content: str + publish: bool = True + rating: Optional[int] = None + + +my_posts = [ + {"title": "title of post 1", "content": "content of post 1", "id": 1}, + {"title": "Fav anime", "content": "onpiece or naruto", "id": 2} +] + + +def find_post(id): + for p in my_posts: + if p['id'] == id: + return p + + +@app.get("/") +async def root(): + return {"message": "Hello World"} + + +@app.get('/posts') +def get_posts(): + return { + "data": my_posts + } + + +@app.post("/posts") +async def create_post(post: Post): + post_dict = post.dict() + post_dict['id'] = randrange(0, 1000000) + my_posts.append(post_dict) + return { + "data": post_dict + } + + +@app.get('/posts/{id}') +def get_post(id: int, response: Response): + post = find_post(id) + if not post: + response.status_code = 404 + return {"post_detail": post} From 212e3e29521987eaa8664ec48d37278d0ec50b45 Mon Sep 17 00:00:00 2001 From: abubakar Date: Thu, 9 Oct 2025 16:51:09 +0500 Subject: [PATCH 3/4] Add CRUD operations for Post entity with authentication support --- __pycache__/database_model.cpython-313.pyc | Bin 1607 -> 1607 bytes __pycache__/main.cpython-313.pyc | Bin 1458 -> 1482 bytes __pycache__/model.cpython-313.pyc | Bin 1668 -> 1668 bytes auth.py | 6 +- db/__init__.py | 0 database.py => db/database.py | 0 database_model.py => db/database_model.py | 9 +++ main.py | 21 +++--- .../products_routes.cpython-313.pyc | Bin 4980 -> 5014 bytes routes/__pycache__/user_route.cpython-313.pyc | Bin 3602 -> 3636 bytes routes/file_routes.py | 13 ++-- routes/post_routes.py | 63 ++++++++++++++++++ routes/products_routes.py | 6 +- routes/user_route.py | 12 ++-- schemas/__init__.py | 0 model.py => schemas/model.py | 9 ++- 16 files changed, 105 insertions(+), 34 deletions(-) create mode 100644 db/__init__.py rename database.py => db/database.py (100%) rename database_model.py => db/database_model.py (83%) create mode 100644 routes/post_routes.py create mode 100644 schemas/__init__.py rename model.py => schemas/model.py (85%) diff --git a/__pycache__/database_model.cpython-313.pyc b/__pycache__/database_model.cpython-313.pyc index 4a7a5b452fb960ef4ad8b11aaf11326f086aeb8f..2367e17d40cdd73b61a6642b2947e8562a19a5fd 100644 GIT binary patch delta 52 zcmX@kbDW3wGcPX}0}!w!KFes>$eYQ`C@{I4xgSWfvutCOp1hBx4M@7OJ_CyAut@^| Dda@1! delta 52 zcmX@kbDW3wGcPX}0}zBpKFg@v$eYQ`$UnK9xgSWfvutCOn!Jyt4M@7OK4avYtivV^ E0EFufLI3~& diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc index c8493d2055f3edf3fd0d12cb94d5800b9fcade71..95b745160d60e11f132c0d7eb4741462941f7076 100644 GIT binary patch delta 709 zcmdnQeTtj+GcPX}0}#|SKF`=Ykyn!O%0zYLdN0-@Rt1J&hF}(NHZS%fb}*aOo5PE< zh|`O!h|7z+h}(;&hzBUnR>T{_sKgM=9?TKL8_a3RT*Mc{8YBlY6BPt0pz=YyAZ1Lk z81`UKE=xuQh8VseRV>onL28(y!Q3&td~OI`i#MRmdp_S6F-P%<5tcOR4#z5 z9Mj+!9z50w0`&_a>z`b}C>n{|K4GAG5hV4RnDzyWnleH}gTjvm2wtgzq~_e>L*sJ;l}!G@tjfm; z)Kc&-L+M-nnNvR1H4~eA*K`8N1*rB(Du$VXC;P6r5$*W?|t*W?>(6LqF$)_Z(UbFoEP@b z_SvakKo_T_wLT*hDA`Xn(=5FL0;l#fO_|BfEXy_(rU)+0R2w}6nxV3-(rg_w&DN}9 z;&uDjm?~97RS{YpuYgUgd$&81%6c%x^~cy+og&B3PE);(gpFpc-Eo_>*8gT{&Q^0k zXSOh%6>H30uW`3tPV_hPCr)DnLm1@$W1#$Ffq9`fr}U-CfC-=r+Xxn~OS-rW+v5v1 ze}KzN@1@NQj@36E-{ZTI-*{)Xqo~)YR{h|$@R#`cYVsiWMTELY$M8clPx=G3G$19mESR#%KjFu7IoFAt zS5D})x`FHYvCIv!QP4y~jGbOD&N;5z>ULb$_l_Lq@jcSmO`KUFNr@3l{zoi775cZn z_l#9UiA02k$Rmspx`N^rJoyHu0N-Kf5_b4!va~J(B45F{1Y<~yVZJgj9u4ygCmW*- Xp!_c&NE&H8CLg4c%+FIrk_`I;oIrkG diff --git a/__pycache__/model.cpython-313.pyc b/__pycache__/model.cpython-313.pyc index 486da57b21c4a80b285f1fc5b36c38847652e8ef..3790d7062aa5e08d77ee7111af16fad14d747408 100644 GIT binary patch delta 134 zcmZqSZQ7WE7cf#}dLQIe9XR29Vstk^mHuWt9S|v|vqOl$|`2)e%VEW-Vo8n;gg{A99W76Sm{m>@a; delta 134 zcmZqSZQ7WE7oj#}dLQJ$W*V29Vstk^mHuWtC!Nn{2_Fz$ia?CaWWmyvh)BErNd(&71)0Z0{z11$ys0ev8d diff --git a/auth.py b/auth.py index 6206006..bf4fd9b 100644 --- a/auth.py +++ b/auth.py @@ -2,8 +2,8 @@ from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session -from database import SessionLocal -import database_model +from db.database import SessionLocal +import db.database_model as database_model from passlib.context import CryptContext from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import JWTError, jwt @@ -54,5 +54,3 @@ def get_current_user(token: str = Depends(oauth2_bearer), db: Session = Depends( except JWTError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid Token") - - diff --git a/db/__init__.py b/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/database.py b/db/database.py similarity index 100% rename from database.py rename to db/database.py diff --git a/database_model.py b/db/database_model.py similarity index 83% rename from database_model.py rename to db/database_model.py index a5c2536..71baa17 100644 --- a/database_model.py +++ b/db/database_model.py @@ -12,6 +12,7 @@ class UserFile(Base): original_name = Column(String) # "my_photo.jpg" user_id = Column(Integer, ForeignKey("users.id")) + class Product(Base): __tablename__ = "products" @@ -29,3 +30,11 @@ class User(Base): id = Column(Integer, primary_key=True, index=True) username = Column(String, unique=True, index=True) password = Column(String) + + +class Post(Base): + __tablename__ = "posts" + + post_id = Column(Integer, primary_key=True, index=True) + title = Column(String) + description = Column(String) diff --git a/main.py b/main.py index c49d746..81d259a 100644 --- a/main.py +++ b/main.py @@ -1,31 +1,32 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -import database_model -from database import engine -from routes import file_routes, products_routes, user_route +import db.database_model as database_model +from db.database import engine +from routes import file_routes, post_routes, products_routes, user_route version = "v1" app = FastAPI(title="Fastapi ", description="this is learning project.", version=version,) -app.add_middleware( - CORSMiddleware, - allow_origins=["http://localhost:3000"], - allow_methods=["*"], -) database_model.Base.metadata.create_all(bind=engine) -# Register routes + app.include_router(products_routes.router, prefix=f"/api/{version}/products", tags=['Products']) app.include_router(file_routes.router, prefix=f"/api/{version}/files", tags=['Files']) app.include_router(user_route.router, prefix=f"/api/{version}/users", tags=['Users']) - +app.include_router(post_routes.router, + prefix=f"/api/{version}/posts", tags=["Posts"]) @app.get("/") def greet(): return {"message": "Hello, World!"} + + + + + \ No newline at end of file diff --git a/routes/__pycache__/products_routes.cpython-313.pyc b/routes/__pycache__/products_routes.cpython-313.pyc index ed383cb3fcdd60d096cdc8a4417e3f8200dcd3e1..2ea4cded6fee1c9702313020fa7436b4c9c32089 100644 GIT binary patch delta 558 zcmeyOHcg%PGcPX}0}y!mKhM}Zkyny&-b8h09WU-8ZUu&5hF~^t9xvV^UND>8o6n2C zh+lyrhDnLR6r>3ViUcN3ij@}(R+3^U5(-uZlEN{<2x*{VfgsJve2kLT{HBZ$E&7bX zs+LSKj7khKLNGBC#$dG=UK6HZbt#4{mMliN@uFb;c}$Y_g!F61h=I*u3D!b0LmX&^ zA;=8`&CrfvgPNg(W`-ot4AaR#cQ_N{4wyM?!MbSXNCVBW1errHJP`iTLo-8m^C6~J zyo_>_*9sWf@)jp&q~<0T>*eOBq~;W{0mJwfcS@38N@7W3Qetsx5j!ZBI6%UJNW$@8 u<&*yjbSee{wWd>cr9U}j`wyw9L^m%->RgW`P#ox2QLn^y>)VFUp3J7VAf delta 601 zcmbQH{zZ-VGcPX}0}!kTc$P7DBCjOltcmK*8bw^e48d&P++I9IJPHgzHoG^k7he&d z0z(Xw5`!s70}vGPPn;GjFA%IG#ZV*|tPCWDVuTRVK*jt)>XY{|O4jq4GD5WIGX|?# zGQ}_|F~kVM#7r22)na%|n1a=%7_wNh7~#f?fb>K3A(O!xVEu&5)Qk}Yo68ccC4y$K z7|>vSq8z3j!v-}(2h9u#pc%%<&VsN>a2(L=da&Va!MbRMO92fxr^s+nh_VFhp_wa_ z&Z?>Z5@fI@(=B$F)Xi6!Zu2tAPTnYBB+r_gpOTtW#0HeS#gUR&l9-fOoLa;V%#4hT_ZiggG8o-uki5^Jd7D9W^C`hIi~tvnTeAQF diff --git a/routes/__pycache__/user_route.cpython-313.pyc b/routes/__pycache__/user_route.cpython-313.pyc index 8613bf5670c697888b7b32f80a513a961b95ef31..61dd484270f7594535d94f6d1300d7053ba5290a 100644 GIT binary patch delta 306 zcmbOvvqgsQGcPX}0}y!mKhNOgn8+u=xM8Atw2_xUk$?h2FhelAx1g6$k&u^gk+7FY zk%$6A45Jc*DM$wp6p03NNHG+N#fS#!Puw8SC^qqeDysyT_e+~mY_b}oDyt-rYdSfA zQJ!DKlo6uMgfUnsMqqLsqdKcJNb%%VjGBxxo3Ak5U}ZGhyq5hT6Qla(elBiCM$^f& zxfe3ZPPXOoa^fvc&PdHoEY{1-Pf5)w;sn_ba(WRrkhsO2lBAcCSdy5OSe#nK0}>HL z5{U=vnY@}Ofze|!53f9z;Y9|+y9_FK8MG#w@_I5VO|IuHQsiJ@;%Jv{lqA|Wr~B4GuF z7)B)qQ;=#PC=v6nO&EiPV)!Qqa*9lz#wg4x1vlX!qZp&~<{OMRSQ#xg zZ)AVS#Hg`(G8Z=^quJ#7+zT0HCOh(Ysj%ker=;c-ae`b2@>CHwkhsN>l30?Mlvtcv u!~^6`KFgE9=ssDGSDs7rB7^2#2F2S9qLagUJsDLdFXSy^WScC)rvL! Date: Fri, 10 Oct 2025 18:58:08 +0500 Subject: [PATCH 4/4] Company & Product API --- .gitignore | 8 +- app/auth.py | 59 +++++++++++ app/controllers/auth_controller.py | 31 ++++++ app/controllers/company_controller.py | 46 +++++++++ app/controllers/product_controller.py | 63 ++++++++++++ .../controllers/user_controller.py | 0 {db => app}/database.py | 13 +++ app/main.py | 52 ++++++++++ app/models/company.py | 15 +++ {schemas => app/models}/model.py | 7 +- app/models/product.py | 15 +++ app/models/user.py | 13 +++ app/schemas/company.py | 18 ++++ app/schemas/model.py | 28 ++++++ app/schemas/product.py | 18 ++++ app/schemas/user.py | 15 +++ app/services/company_service.py | 60 +++++++++++ app/services/product_service.py | 47 +++++++++ app/services/user_service.py | 33 ++++++ auth.py | 56 ----------- db/database_model.py | 40 -------- main.py | 32 ------ routes/__init__.py | 0 routes/__pycache__/__init__.cpython-313.pyc | Bin 162 -> 0 bytes .../products_routes.cpython-313.pyc | Bin 5014 -> 0 bytes routes/__pycache__/user_route.cpython-313.pyc | Bin 3636 -> 0 bytes routes/file_routes.py | 60 ----------- routes/post_routes.py | 63 ------------ routes/products_routes.py | 94 ------------------ routes/user_route.py | 60 ----------- schemas/__init__.py | 0 uploads/85b140b589bf4c79b5c61f4c7d7fb794.docx | Bin 24037 -> 0 bytes uploads/88000450138746dcae8b06ee5fd6e40a.txt | 57 ----------- uploads/FASTAPI_NOTES.docx | Bin 24037 -> 0 bytes uploads/main.py | 56 ----------- 35 files changed, 536 insertions(+), 523 deletions(-) create mode 100644 app/auth.py create mode 100644 app/controllers/auth_controller.py create mode 100644 app/controllers/company_controller.py create mode 100644 app/controllers/product_controller.py rename db/__init__.py => app/controllers/user_controller.py (100%) rename {db => app}/database.py (57%) create mode 100644 app/main.py create mode 100644 app/models/company.py rename {schemas => app/models}/model.py (87%) create mode 100644 app/models/product.py create mode 100644 app/models/user.py create mode 100644 app/schemas/company.py create mode 100644 app/schemas/model.py create mode 100644 app/schemas/product.py create mode 100644 app/schemas/user.py create mode 100644 app/services/company_service.py create mode 100644 app/services/product_service.py create mode 100644 app/services/user_service.py delete mode 100644 auth.py delete mode 100644 db/database_model.py delete mode 100644 main.py delete mode 100644 routes/__init__.py delete mode 100644 routes/__pycache__/__init__.cpython-313.pyc delete mode 100644 routes/__pycache__/products_routes.cpython-313.pyc delete mode 100644 routes/__pycache__/user_route.cpython-313.pyc delete mode 100644 routes/file_routes.py delete mode 100644 routes/post_routes.py delete mode 100644 routes/products_routes.py delete mode 100644 routes/user_route.py delete mode 100644 schemas/__init__.py delete mode 100644 uploads/85b140b589bf4c79b5c61f4c7d7fb794.docx delete mode 100644 uploads/88000450138746dcae8b06ee5fd6e40a.txt delete mode 100644 uploads/FASTAPI_NOTES.docx delete mode 100644 uploads/main.py diff --git a/.gitignore b/.gitignore index ba0430d..3ccd43f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ -__pycache__/ \ No newline at end of file +# Ignore Python cache files +__pycache__/ +*.pyc +*.pyo + +# Ignore init files if you don’t want them tracked +__init__.py diff --git a/app/auth.py b/app/auth.py new file mode 100644 index 0000000..0449346 --- /dev/null +++ b/app/auth.py @@ -0,0 +1,59 @@ +from datetime import datetime, timedelta +from fastapi import Depends, HTTPException, status +from fastapi.security import HTTPBearer +from jose import JWTError, jwt +from passlib.context import CryptContext +from sqlalchemy.orm import Session +from app.models.user import User +from app.database import get_db +from fastapi.security import HTTPAuthorizationCredentials + + +SECRET_KEY = "af3287c8391bb9f4f7a72feb3b85f72e1d5bd07cbf4fa4ad9497c78412923312" +ALGORITHM = "HS256" +ACCESS_TOKEN_EXPIRE_MINUTES = 30 + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") +bearer_scheme = HTTPBearer() + + +# ---------------- PASSWORD UTILS ---------------- # +def verify_password(plain_password, hashed_password): + return pwd_context.verify(plain_password, hashed_password) + + +def get_password_hash(password): + return pwd_context.hash(password) + + +# ---------------- TOKEN CREATION ---------------- # +def create_access_token(user_id: int): + expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + to_encode = {"sub": str(user_id), "exp": expire} + return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + + +# ---------------- VERIFY CURRENT USER ---------------- # +def get_current_user( + token: HTTPAuthorizationCredentials = Depends(bearer_scheme), + db: Session = Depends(get_db) +): + credential_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + + try: + payload = jwt.decode(token.credentials, SECRET_KEY, + algorithms=[ALGORITHM]) + user_id: str = payload.get("sub") + if user_id is None: + raise credential_exception + except JWTError: + raise credential_exception + + user = db.query(User).filter(User.id == int(user_id)).first() + if user is None: + raise credential_exception + return user diff --git a/app/controllers/auth_controller.py b/app/controllers/auth_controller.py new file mode 100644 index 0000000..d214a06 --- /dev/null +++ b/app/controllers/auth_controller.py @@ -0,0 +1,31 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.orm import Session +from app.schemas.user import UserCreate, UserResponse +from app.services.user_service import UserService +from app.database import get_db +from app.auth import create_access_token, verify_password +from app.models.user import User +router = APIRouter() + + +@router.post("/register", response_model=UserResponse) +def register(user: UserCreate, db: Session = Depends(get_db)): + service = UserService(db) + try: + return service.create_user(user) + except ValueError as e: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +@router.post("/token") +def login(user: UserCreate, db: Session = Depends(get_db)): + # ✅ Query using database model + db_user = db.query(User).filter(User.username == user.username).first() + + if not db_user or not verify_password(user.password, db_user.password): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials") + + token = create_access_token(db_user.id) + return {"access_token": token, "token_type": "bearer"} diff --git a/app/controllers/company_controller.py b/app/controllers/company_controller.py new file mode 100644 index 0000000..76d647f --- /dev/null +++ b/app/controllers/company_controller.py @@ -0,0 +1,46 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session +from app.schemas.company import CompanyCreate, CompanyResponse +from app.services.company_service import CompanyService +from app.database import get_db +from app.auth import get_current_user + +router = APIRouter() + + +@router.post("/", response_model=CompanyResponse) +def create_company( + company: CompanyCreate, + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + service = CompanyService(db) + return service.create_company(current_user.id, company) + + +@router.get("/me", response_model=CompanyResponse) +def get_my_company( + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + service = CompanyService(db) + return service.get_my_company(current_user.id) + + +@router.put("/me", response_model=CompanyResponse) +def edit_my_company( + company: CompanyCreate, + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + service = CompanyService(db) + return service.edit_company(current_user.id, company) + + +@router.delete("/me", dependencies=[Depends(get_current_user)]) +def delete_my_company( + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + service = CompanyService(db) + return service.delete_company(current_user.id) diff --git a/app/controllers/product_controller.py b/app/controllers/product_controller.py new file mode 100644 index 0000000..3d06d85 --- /dev/null +++ b/app/controllers/product_controller.py @@ -0,0 +1,63 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.orm import Session +from app.schemas.product import ProductCreate, ProductResponse +from app.services.product_service import ProductService +from app.services.company_service import CompanyService +from app.database import get_db +from app.auth import get_current_user + + +router = APIRouter() + + +@router.post("/", response_model=ProductResponse) +def create_product( + product: ProductCreate, + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + company_service = CompanyService(db) + product_service = ProductService(db) + company = company_service.get_my_company(current_user.id) + if not company: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="no company found") + + return product_service.create_product(company.id, product) + + +@router.get("/", response_model=list[ProductResponse]) +def list_product( + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + product_service = ProductService(db) + return product_service.list_products() + + +@router.get("/{product_id}", response_model=ProductResponse) +def get_product_by_id( + product_id: int, + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + product_service = ProductService(db) + return product_service.get_product(product_id) + + +@router.put("/{product_id}", response_model=ProductResponse) +def update_product_by_id( + product_id: int, + product: ProductCreate, + db: Session = Depends(get_db), + current_user=Depends(get_current_user)): + product_service = ProductService(db) + return product_service.update_product(product_id,product) + +@router.delete("/{product_id}", response_model=ProductResponse) +def delete_product_by_id( + product_id: int, + db: Session = Depends(get_db), + current_user=Depends(get_current_user)): + product_service = ProductService(db) + return product_service.delete_product(product_id) \ No newline at end of file diff --git a/db/__init__.py b/app/controllers/user_controller.py similarity index 100% rename from db/__init__.py rename to app/controllers/user_controller.py diff --git a/db/database.py b/app/database.py similarity index 57% rename from db/database.py rename to app/database.py index dad789c..fbb7968 100644 --- a/db/database.py +++ b/app/database.py @@ -1,7 +1,20 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker +from sqlalchemy.ext.declarative import declarative_base + + db_url = "postgresql://postgres:123@localhost:5432/inventory" engine = create_engine(db_url) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + + +Base = declarative_base() + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..f59d8a9 --- /dev/null +++ b/app/main.py @@ -0,0 +1,52 @@ +from fastapi import FastAPI +from app.database import Base, engine +from app.controllers import auth_controller, company_controller, product_controller + + +Base.metadata.create_all(bind=engine) +version = "v1" +app = FastAPI(title="Company & Product API", + description="in which user create their company", + version=version + ) + +app.include_router(auth_controller.router, + prefix=f"/api/{version}/auth", tags=["Auth"]) +app.include_router(company_controller.router, + prefix=f"/api/{version}/company", tags=["Company"]) +app.include_router(product_controller.router, + prefix=f"/api/{version}/product", tags=["Product"]) + + +@app.get("/") +def root(): + return {"message": "Welcome to Company API!"} + + +# from fastapi import FastAPI +# from fastapi.middleware.cors import CORSMiddleware +# import db.database_model as database_model +# from app.database import engine +# from routes import file_routes, post_routes, products_routes, user_route + +# version = "v1" +# app = FastAPI(title="Fastapi ", +# description="this is learning project.", +# version=version,) + + +# database_model.Base.metadata.create_all(bind=engine) + + +# app.include_router(products_routes.router, +# prefix=f"/api/{version}/products", tags=['Products']) +# app.include_router(file_routes.router, +# prefix=f"/api/{version}/files", tags=['Files']) +# app.include_router(user_route.router, +# prefix=f"/api/{version}/users", tags=['Users']) +# app.include_router(post_routes.router, +# prefix=f"/api/{version}/posts", tags=["Posts"]) + +# @app.get("/") +# def greet(): +# return {"message": "Hello, World!"} diff --git a/app/models/company.py b/app/models/company.py new file mode 100644 index 0000000..555bc5e --- /dev/null +++ b/app/models/company.py @@ -0,0 +1,15 @@ +from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy.orm import relationship +from app.database import Base + + +class Company(Base): + __tablename__ = "companies" + + id =Column(Integer, primary_key=True, index=True) + name= Column(String) + location = Column(String) + user_id = Column(Integer, ForeignKey("users.id")) + + user= relationship("User", back_populates="company") + products= relationship("Product", back_populates="company") \ No newline at end of file diff --git a/schemas/model.py b/app/models/model.py similarity index 87% rename from schemas/model.py rename to app/models/model.py index 8908cad..1643234 100644 --- a/schemas/model.py +++ b/app/models/model.py @@ -1,5 +1,5 @@ from pydantic import BaseModel - +from typing import Optional class Product(BaseModel): id: int @@ -22,14 +22,13 @@ class User(BaseModel): username: str password: str - class Config: - from_attributes = True class Post(BaseModel): - post_id: int + post_id: Optional [int] title: str description: str + class Token(BaseModel): access_token: str token_type: str diff --git a/app/models/product.py b/app/models/product.py new file mode 100644 index 0000000..29bce74 --- /dev/null +++ b/app/models/product.py @@ -0,0 +1,15 @@ +from sqlalchemy import Column, Integer, String, Float, ForeignKey +from sqlalchemy.orm import relationship +from app.database import Base + + +class Product(Base): + __tablename__ = "products" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String) + price = Column(Float) + description = Column(String,nullable=True) + company_id = Column(Integer, ForeignKey("companies.id")) + + company = relationship("Company", back_populates="products") diff --git a/app/models/user.py b/app/models/user.py new file mode 100644 index 0000000..4b4cdfb --- /dev/null +++ b/app/models/user.py @@ -0,0 +1,13 @@ +from sqlalchemy import Column, Integer, String +from sqlalchemy.orm import relationship +from app.database import Base + + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + username = Column(String, unique=True) + password = Column(String) + + company= relationship("Company",back_populates="user", uselist=False) diff --git a/app/schemas/company.py b/app/schemas/company.py new file mode 100644 index 0000000..7fa504e --- /dev/null +++ b/app/schemas/company.py @@ -0,0 +1,18 @@ +from pydantic import BaseModel +from typing import List, Optional +from app.schemas.product import ProductResponse + + +class CompanyCreate(BaseModel): + name: str + location: str + + +class CompanyResponse(BaseModel): + id: int + name: str + location: str + products: List[ProductResponse] = [] + + class Config: + from_attributes = True diff --git a/app/schemas/model.py b/app/schemas/model.py new file mode 100644 index 0000000..8ce1d9c --- /dev/null +++ b/app/schemas/model.py @@ -0,0 +1,28 @@ +from pydantic import BaseModel +from typing import Optional + +class Product(BaseModel): + id: int + name: str + description: str + price: float + quantity: int + + class Config: + from_attributes = True + + + + + + + +class Post(BaseModel): + post_id: Optional [int] + title: str + description: str + + +class Token(BaseModel): + access_token: str + token_type: str diff --git a/app/schemas/product.py b/app/schemas/product.py new file mode 100644 index 0000000..77ed9f7 --- /dev/null +++ b/app/schemas/product.py @@ -0,0 +1,18 @@ +from typing import Optional +from pydantic import BaseModel + + +class ProductCreate(BaseModel): + name: str + price: float + description: Optional[str] = None + + +class ProductResponse(BaseModel): + id: int + name: str + price: float + description: Optional[str] = None + + class Config: + from_attributes = True diff --git a/app/schemas/user.py b/app/schemas/user.py new file mode 100644 index 0000000..0e5a05c --- /dev/null +++ b/app/schemas/user.py @@ -0,0 +1,15 @@ +from pydantic import BaseModel + + +class UserCreate(BaseModel): + username: str + password: str + + +class UserResponse(BaseModel): + id: int + username: str + password: str + + class Config: + from_attributes = True diff --git a/app/services/company_service.py b/app/services/company_service.py new file mode 100644 index 0000000..e0b6155 --- /dev/null +++ b/app/services/company_service.py @@ -0,0 +1,60 @@ +from fastapi import HTTPException, status +from sqlalchemy.orm import Session +from app.models.company import Company +from app.schemas.company import CompanyCreate + + +class CompanyService: + def __init__(self, db: Session): + self.db = db + + def create_company(self, user_id: int, company_data: CompanyCreate): + existing = self.db.query(Company).filter( + Company.user_id == user_id).first() + if existing: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="user already has a company") + + new_company = Company( + name=company_data.name, + location=company_data.location, + user_id=user_id + ) + self.db.add(new_company) + self.db.commit() + self.db.refresh(new_company) + return new_company + + def get_my_company(self, user_id: int): + company = self.db.query(Company).filter( + Company.user_id == user_id).first() + if not company: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Company not found") + return company + + def edit_company(self, user_id: int, company_data: CompanyCreate): + company = self.db.query(Company).filter( + Company.user_id == user_id + ).first() + if not company: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="company not found" + ) + company.name = company_data.name + company.location = company_data.location + self.db.commit() + self.db.refresh(company) + return company + + def delete_company(self, user_id: int): + company = self.db.query(Company).filter( + Company.user_id == user_id + ).first() + if not company: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="company not found" + ) + self.db.delete(company) + self.db.commit() + return {"detail": "company deleted"} diff --git a/app/services/product_service.py b/app/services/product_service.py new file mode 100644 index 0000000..c9db8c9 --- /dev/null +++ b/app/services/product_service.py @@ -0,0 +1,47 @@ +from fastapi import HTTPException +from sqlalchemy.orm import Session +from app.models.product import Product +from app.schemas.product import ProductCreate + + +class ProductService: + def __init__(self, db: Session): + self.db = db + + def create_product(self, company_id: int, product_data: ProductCreate): + new_product = Product( + name=product_data.name, + price=product_data.price, + description=product_data.description, + company_id=company_id + ) + self.db.add(new_product) + self.db.commit() + self.db.refresh(new_product) + return new_product + + def list_products(self): + products = self.db.query(Product).all() + return products + + def get_product(self, product_id: int): + product = self.db.query(Product).filter( + Product.id == product_id).first() + if not product: + raise HTTPException(status_code=404, detail="Product not found") + return product + + def update_product(self, product_id: int, product_data: ProductCreate): + product = self.get_product(product_id) + product.name = product_data.name + product.price = product_data.price + product.description = product_data.description + self.db.commit() + self.db.refresh(product) + return product + + def delete_product(self, product_id: int): + product = self.get_product(product_id) + self.db.delete(product) + self.db.commit() + return {"detail": "Product deleted"} diff --git a/app/services/user_service.py b/app/services/user_service.py new file mode 100644 index 0000000..9f6e614 --- /dev/null +++ b/app/services/user_service.py @@ -0,0 +1,33 @@ +from sqlalchemy.orm import Session +from app.schemas.user import UserCreate +from app.models.user import User +from app.auth import get_password_hash, verify_password, create_access_token +from fastapi import HTTPException, status + + +class UserService: + def __init__(self, db: Session): + self.db = db + + def create_user(self, user: UserCreate): + db_user = self.db.query(User).filter( + User.username == user.username).first() + if db_user: + raise ValueError("Username already registered") + hashed_pw = get_password_hash(user.password) + new_user = User(username=user.username, password=hashed_pw) + self.db.add(new_user) + self.db.commit() + self.db.refresh(new_user) + return new_user + +# def login_user(self, user: UserCreate): +# db_user = self.db.query(User).filter(User.username == user.username).first() +# if not db_user or not verify_password(user.password, db_user.password): +# raise HTTPException( +# status_code=status.HTTP_401_UNAUTHORIZED, +# detail="Invalid credentials" +# ) + +# token = create_access_token(db_user.id) +# return {"access_token": token, "token_type": "bearer"} diff --git a/auth.py b/auth.py deleted file mode 100644 index bf4fd9b..0000000 --- a/auth.py +++ /dev/null @@ -1,56 +0,0 @@ -from datetime import datetime, timedelta -from typing import Annotated -from fastapi import APIRouter, Depends, HTTPException, status -from sqlalchemy.orm import Session -from db.database import SessionLocal -import db.database_model as database_model -from passlib.context import CryptContext -from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm -from jose import JWTError, jwt - - -SECRET_KEY = "af3287c8391bb9f4f7a72feb3b85f72e1d5bd07cbf4fa4ad9497c78412923312" -ALGORITHM = "HS256" -ACCESS_TOKEN_EXPIRE_MINUTES = 30 - -bcrypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto") -oauth2_bearer = OAuth2PasswordBearer(tokenUrl="/api/v1/users/token") - - -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() - -# create token here - - -def create_access_token(username: str, user_id: int, expires_delta: timedelta = None): - encode = {"sub": username, "id": user_id} - expire = datetime.utcnow() + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)) - encode.update({"exp": expire}) - return jwt.encode(encode, SECRET_KEY, algorithm=ALGORITHM) - -# verify token here - - -def get_current_user(token: str = Depends(oauth2_bearer), db: Session = Depends(get_db)): - try: - payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") - user_id: int = payload.get("id") - if username is None or user_id is None: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalidate credentials") - user = db.query(database_model.User).filter( - database_model.User.id == user_id).first() - if user is None: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail="user not found" - ) - return user - except JWTError: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid Token") diff --git a/db/database_model.py b/db/database_model.py deleted file mode 100644 index 71baa17..0000000 --- a/db/database_model.py +++ /dev/null @@ -1,40 +0,0 @@ -from sqlalchemy import Column, Integer, String, Float, ForeignKey - -from sqlalchemy.ext.declarative import declarative_base - -Base = declarative_base() - - -class UserFile(Base): - __tablename__ = "user_files" - id = Column(Integer, primary_key=True, index=True) - filename = Column(String) # safe unique name like "a1b2c3.jpg" - original_name = Column(String) # "my_photo.jpg" - user_id = Column(Integer, ForeignKey("users.id")) - - -class Product(Base): - - __tablename__ = "products" - - id = Column(Integer, primary_key=True, index=True) - name = Column(String) - description = Column(String) - price = Column(Float) - quantity = Column(Integer) - - -class User(Base): - __tablename__ = "users" - - id = Column(Integer, primary_key=True, index=True) - username = Column(String, unique=True, index=True) - password = Column(String) - - -class Post(Base): - __tablename__ = "posts" - - post_id = Column(Integer, primary_key=True, index=True) - title = Column(String) - description = Column(String) diff --git a/main.py b/main.py deleted file mode 100644 index 81d259a..0000000 --- a/main.py +++ /dev/null @@ -1,32 +0,0 @@ -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware -import db.database_model as database_model -from db.database import engine -from routes import file_routes, post_routes, products_routes, user_route - -version = "v1" -app = FastAPI(title="Fastapi ", - description="this is learning project.", - version=version,) - - -database_model.Base.metadata.create_all(bind=engine) - - -app.include_router(products_routes.router, - prefix=f"/api/{version}/products", tags=['Products']) -app.include_router(file_routes.router, - prefix=f"/api/{version}/files", tags=['Files']) -app.include_router(user_route.router, - prefix=f"/api/{version}/users", tags=['Users']) -app.include_router(post_routes.router, - prefix=f"/api/{version}/posts", tags=["Posts"]) - -@app.get("/") -def greet(): - return {"message": "Hello, World!"} - - - - - \ No newline at end of file diff --git a/routes/__init__.py b/routes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/routes/__pycache__/__init__.cpython-313.pyc b/routes/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 598dd488b4e7562b7aeb22bb539df23f35435785..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 162 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 diff --git a/uploads/88000450138746dcae8b06ee5fd6e40a.txt b/uploads/88000450138746dcae8b06ee5fd6e40a.txt deleted file mode 100644 index 693a603..0000000 --- a/uploads/88000450138746dcae8b06ee5fd6e40a.txt +++ /dev/null @@ -1,57 +0,0 @@ -aiomysql==0.2.0 -alembic==1.16.5 -annotated-types==0.7.0 -anyio==4.11.0 -asyncpg==0.30.0 -bcrypt==5.0.0 -certifi==2025.8.3 -cffi==2.0.0 -click==8.3.0 -colorama==0.4.6 -cryptography==46.0.2 -dnspython==2.8.0 -ecdsa==0.19.1 -email-validator==2.3.0 -fastapi==0.118.0 -fastapi-cli==0.0.13 -fastapi-cloud-cli==0.2.1 -greenlet==3.2.4 -h11==0.16.0 -httpcore==1.0.9 -httptools==0.6.4 -httpx==0.28.1 -idna==3.10 -Jinja2==3.1.6 -Mako==1.3.10 -markdown-it-py==4.0.0 -MarkupSafe==3.0.3 -mdurl==0.1.2 -passlib==1.7.4 -pyasn1==0.6.1 -pycparser==2.23 -pydantic==2.11.9 -pydantic_core==2.33.2 -Pygments==2.19.2 -PyMySQL==1.1.2 -python-decouple==3.8 -python-dotenv==1.1.1 -python-jose==3.5.0 -python-multipart==0.0.20 -PyYAML==6.0.3 -rich==14.1.0 -rich-toolkit==0.15.1 -rignore==0.6.4 -rsa==4.9.1 -sentry-sdk==2.39.0 -shellingham==1.5.4 -six==1.17.0 -sniffio==1.3.1 -SQLAlchemy==2.0.43 -starlette==0.48.0 -typer==0.19.2 -typing-inspection==0.4.2 -typing_extensions==4.15.0 -urllib3==2.5.0 -uvicorn==0.37.0 -watchfiles==1.1.0 -websockets==15.0.1 diff --git a/uploads/FASTAPI_NOTES.docx b/uploads/FASTAPI_NOTES.docx deleted file mode 100644 index 5e8f7201b3ae97b78788d360fcbdd4dff400dc60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24037 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 diff --git a/uploads/main.py b/uploads/main.py deleted file mode 100644 index 1146afd..0000000 --- a/uploads/main.py +++ /dev/null @@ -1,56 +0,0 @@ -from fastapi import FastAPI, Response -from typing import Optional -from fastapi.params import Body -from pydantic import BaseModel -from random import randrange - -app = FastAPI() - - -class Post(BaseModel): - title: str - content: str - publish: bool = True - rating: Optional[int] = None - - -my_posts = [ - {"title": "title of post 1", "content": "content of post 1", "id": 1}, - {"title": "Fav anime", "content": "onpiece or naruto", "id": 2} -] - - -def find_post(id): - for p in my_posts: - if p['id'] == id: - return p - - -@app.get("/") -async def root(): - return {"message": "Hello World"} - - -@app.get('/posts') -def get_posts(): - return { - "data": my_posts - } - - -@app.post("/posts") -async def create_post(post: Post): - post_dict = post.dict() - post_dict['id'] = randrange(0, 1000000) - my_posts.append(post_dict) - return { - "data": post_dict - } - - -@app.get('/posts/{id}') -def get_post(id: int, response: Response): - post = find_post(id) - if not post: - response.status_code = 404 - return {"post_detail": post}