From 8065ead8139c643cffd018b173dab3523ff722c2 Mon Sep 17 00:00:00 2001 From: roma-dxunvrs Date: Sat, 6 Dec 2025 18:54:43 +0300 Subject: [PATCH] Register add, refactor --- app/{routers => api}/__init__.py | 0 app/api/v1/__init__.py | 0 app/api/v1/endpoints.py | 40 ++++++++++++++ app/db/__init__.py | 0 app/db/demo.py | 21 +++++++ app/routers/demo.py | 95 -------------------------------- app/schemas/token.py | 2 +- app/schemas/user.py | 3 +- app/utils/validate_utils.py | 86 +++++++++++++++++++++++++++++ main.py | 4 +- 10 files changed, 151 insertions(+), 100 deletions(-) rename app/{routers => api}/__init__.py (100%) create mode 100644 app/api/v1/__init__.py create mode 100644 app/api/v1/endpoints.py create mode 100644 app/db/__init__.py create mode 100644 app/db/demo.py delete mode 100644 app/routers/demo.py create mode 100644 app/utils/validate_utils.py diff --git a/app/routers/__init__.py b/app/api/__init__.py similarity index 100% rename from app/routers/__init__.py rename to app/api/__init__.py diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/v1/endpoints.py b/app/api/v1/endpoints.py new file mode 100644 index 0000000..416ba76 --- /dev/null +++ b/app/api/v1/endpoints.py @@ -0,0 +1,40 @@ +from app.schemas.user import UserSchema +from app.schemas.token import TokenSchema + +from app.db.demo import users_db + +from app.utils.jwt_utlis import encode_jwt +from app.utils.validate_utils import validate_auth_user, get_current_auth_user, validate_register_user + +from fastapi import APIRouter, Depends + +router = APIRouter(prefix="/auth") + +@router.post("/login/", response_model=TokenSchema) +def auth_user_with_password( + user: UserSchema = Depends(validate_auth_user) +): + jwt_payload = { + "username": user.username + } + token = encode_jwt(jwt_payload) + return TokenSchema( + access_token=token, + token_type="Bearer" + ) + +@router.post("/register/") +def sign_up( + user: UserSchema = Depends(validate_register_user) +): + users_db[user.username] = user + + return {"Message": "User added", + "Username": user.username} + +@router.get("/validate/") +def validate_user( + user: UserSchema = Depends(get_current_auth_user) +): + return {"Message": "Validate pass", + "Username": user.username} diff --git a/app/db/__init__.py b/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/db/demo.py b/app/db/demo.py new file mode 100644 index 0000000..cf54ee7 --- /dev/null +++ b/app/db/demo.py @@ -0,0 +1,21 @@ +# This folder should be used for working with the database +# before I use dictionaries + +from app.schemas.user import UserSchema +from app.utils.bcrypt_utils import hash_password + +john = UserSchema( + username="john", + password=hash_password("qwerty"), + # email="john@example.com" +) + +sam = UserSchema( + username="sam", + password=hash_password("secret"), +) + +users_db: dict[str, UserSchema] = { + john.username: john, + sam.username: sam +} \ No newline at end of file diff --git a/app/routers/demo.py b/app/routers/demo.py deleted file mode 100644 index a2f7385..0000000 --- a/app/routers/demo.py +++ /dev/null @@ -1,95 +0,0 @@ -from app.schemas.user import UserSchema -from app.schemas.token import TokenInfo -from app.utils.bcrypt_utils import * -from app.utils.jwt_utlis import * - -from jwt.exceptions import InvalidTokenError -from fastapi import APIRouter, Depends, Form, HTTPException -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials - -http_bearer = HTTPBearer() - -router = APIRouter(prefix="/jwt", tags=["JWT"]) - -john = UserSchema( - username="john", - password=hash_password("qwerty"), - email="john@example.com" -) - -sam = UserSchema( - username="sam", - password=hash_password("secret"), -) - -users_db: dict[str, UserSchema] = { - john.username: john, - sam.username: sam -} - -def validate_auth_user( - username: str = Form(), - password: str = Form() -): - unauthed_exc = HTTPException( - status_code=401, - detail="Invalid username or password" - ) - if not (user := users_db.get(username)): - raise unauthed_exc - - if validate_password( - password=password, - hashed_password=user.password - ): - return user - raise unauthed_exc - -def get_current_token_payload( - credentials: HTTPAuthorizationCredentials = Depends(http_bearer) -) -> UserSchema: - token = credentials.credentials - try: - payload = decode_jwt( - token=token - ) - except InvalidTokenError: - raise HTTPException( - status_code=401, - detail="Invalid token error" - ) - return payload - -def get_current_auth_user( - payload: dict = Depends(get_current_token_payload) -) -> UserSchema: - username: str = payload.get("username") - if not (user := users_db.get(username)): - raise HTTPException( - status_code=401, - detail="Token invalid" # fr user not found - ) - return user - -@router.post("/login/", response_model=TokenInfo) -def auth_user_issue_jwt( - user: UserSchema = Depends(validate_auth_user) -): - jwt_payload = { - "username": user.username, - "email": user.email - } - token = encode_jwt(jwt_payload) - return TokenInfo( - access_token=token, - token_type="Bearer" - ) - -@router.get("/users/me/") -def auth_user_check_self_info( - user: UserSchema = Depends(get_current_auth_user) -): - return { - "username": user.username, - "email": user.email - } \ No newline at end of file diff --git a/app/schemas/token.py b/app/schemas/token.py index 4dbc71b..bea8072 100644 --- a/app/schemas/token.py +++ b/app/schemas/token.py @@ -1,5 +1,5 @@ from pydantic import BaseModel -class TokenInfo(BaseModel): +class TokenSchema(BaseModel): access_token: str token_type: str \ No newline at end of file diff --git a/app/schemas/user.py b/app/schemas/user.py index 02c56ec..43367f1 100644 --- a/app/schemas/user.py +++ b/app/schemas/user.py @@ -6,5 +6,4 @@ class UserSchema(BaseModel): username: str password: bytes - email: EmailStr | None = None - active: bool = True \ No newline at end of file + # email: EmailStr \ No newline at end of file diff --git a/app/utils/validate_utils.py b/app/utils/validate_utils.py new file mode 100644 index 0000000..1053a22 --- /dev/null +++ b/app/utils/validate_utils.py @@ -0,0 +1,86 @@ +from app.schemas.user import UserSchema + +from app.db.demo import users_db + +from app.utils.bcrypt_utils import validate_password, hash_password +from app.utils.jwt_utlis import decode_jwt + + +from fastapi import Form, HTTPException, Depends +from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer + +from jwt.exceptions import InvalidTokenError + +http_bearer = HTTPBearer() + +def validate_auth_user( + username: str = Form(), + password: str = Form() +) -> UserSchema | HTTPException: + + if not (user := users_db.get(username)): + raise HTTPException( + status_code=401, + detail="Invalid username or password" + ) + + is_password_valid = validate_password(password=password, hashed_password=user.password) + + if not is_password_valid: + raise HTTPException( + status_code=401, + detail="Invalid username or password") + return user + +def validate_register_user( + username: str = Form(), + password: str = Form(), + confirm_password: str = Form() +) -> UserSchema | HTTPException: + + if users_db.get(username): + raise HTTPException( + status_code=409, + detail="User with this name already exists" + ) + + if password != confirm_password: + raise HTTPException( + status_code=400, + detail="Passwords don't match" + ) + + user = UserSchema( + username=username, + password=hash_password(password) + ) + + return user + +def get_current_token_payload( + credentials: HTTPAuthorizationCredentials = Depends(http_bearer) +) -> UserSchema | HTTPException: + + token = credentials.credentials + try: + payload = decode_jwt( + token=token + ) + except InvalidTokenError: + raise HTTPException( + status_code=401, + detail="Invalid token error" + ) + return payload + +def get_current_auth_user( + payload: dict = Depends(get_current_token_payload) +) -> UserSchema | HTTPException: + + username: str = payload.get("username") + if not (user := users_db.get(username)): + raise HTTPException( + status_code=401, + detail="Token invalid" # for real user not found + ) + return user \ No newline at end of file diff --git a/main.py b/main.py index f3a2ca4..1f2ceca 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,9 @@ from fastapi import FastAPI -from app.routers import demo +from app.api.v1 import endpoints import uvicorn app = FastAPI() -app.include_router(demo.router) +app.include_router(endpoints.router) if __name__ == "__main__": uvicorn.run("main:app", reload=True) \ No newline at end of file