main
Сергей Ванюшкин 2024-03-22 07:34:33 +03:00
parent 8af26224e4
commit 0d9d69d206
9 changed files with 64 additions and 113 deletions

View File

@ -15,6 +15,6 @@ async def lifespan(app: FastAPI):
def create_app() -> FastAPI: def create_app() -> FastAPI:
app = FastAPI(lifespan=lifespan) app = FastAPI(lifespan=lifespan)
app.include_router(user_router)
app.include_router(auth_router) app.include_router(auth_router)
app.include_router(user_router)
return app return app

View File

@ -0,0 +1,14 @@
from uuid import UUID
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from . import Base
class ReferModel(Base):
__tablename__ = "referals"
owner: Mapped[UUID] = mapped_column(ForeignKey("user.id"))
email: Mapped[str] = mapped_column(unique=True)
hashed_password: Mapped[str]

View File

@ -1,4 +1,4 @@
from sqlalchemy import delete, insert, select, update from sqlalchemy import insert, select
from sqlalchemy.ext.asyncio.session import AsyncSession from sqlalchemy.ext.asyncio.session import AsyncSession
from ..models import UserModel from ..models import UserModel
@ -17,32 +17,11 @@ class UserRepository:
res = await self.session.execute(stmt) res = await self.session.execute(stmt)
return UserReadDTO.model_validate(res.scalar_one()) return UserReadDTO.model_validate(res.scalar_one())
async def find_all(self) -> list[UserReadDTO]:
stmt = select(UserModel)
res = await self.session.execute(stmt)
res = [UserReadDTO.model_validate(row) for row in res.scalars().all()]
return res
async def find_one(self, filter: dict) -> UserReadDTO | None: async def find_one(self, filter: dict) -> UserReadDTO | None:
stmt = select(UserModel).filter_by(**filter) stmt = select(UserModel).filter_by(**filter)
res = await self.session.execute(stmt) res = await self.session.execute(stmt)
res = res.scalar_one_or_none() res = res.scalar_one_or_none()
print(res)
if res is not None: if res is not None:
return UserReadDTO.model_validate(res) return UserReadDTO.model_validate(res)
return None return None
async def patch_one(self, filter: dict, data: UserDBDTO) -> UserReadDTO | None:
stmt = update(UserModel).where(UserModel.id == filter["id"]).values(**data.model_dump()).returning(UserModel)
res = await self.session.execute(stmt)
res = res.scalar_one_or_none()
if res is not None:
return UserReadDTO.model_validate(res)
return None
async def delete_one(self, filter: dict) -> None:
stmt = delete(UserModel).filter_by(**filter)
await self.session.execute(stmt)
return None

View File

@ -1,17 +1,31 @@
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
from test_api.schemas.token import TokenSchema from test_api.schemas.token import TokenSchema
from test_api.schemas.user_schema import UserReadDTO, UserWriteDTO
from test_api.services.auth import AuthService from test_api.services.auth import AuthService
from test_api.services.user import UserService
from ..di import get_auth_service from ..di import get_auth_service, get_user_service
router = APIRouter(prefix="/token") router = APIRouter()
@router.post("", response_model=TokenSchema) @router.post("/token", response_model=TokenSchema, tags=["auth"])
async def authenticate( async def authenticate(
login: OAuth2PasswordRequestForm = Depends(), login: OAuth2PasswordRequestForm = Depends(),
auth: AuthService = Depends(get_auth_service), auth: AuthService = Depends(get_auth_service),
) -> TokenSchema | None: ) -> TokenSchema | None:
return await auth.authenticate(login) return await auth.authenticate(login)
@router.post("/register", response_model=UserReadDTO, tags=["auth"], status_code=201)
async def register(
data: UserWriteDTO,
user_service: UserService = Depends(get_user_service),
) -> UserReadDTO:
res = await user_service.add_one(data)
if not isinstance(res, UserReadDTO):
raise HTTPException(status_code=400, detail=res)
return res

View File

@ -1,72 +1,27 @@
from uuid import UUID from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from test_api.services.auth import get_current_user from test_api.services.auth import get_current_user
from ..di import get_user_service from ..di import get_user_service
from ..schemas import UserDTO, UserReadDTO from ..schemas import RefDTO, UserDTO, UserReadDTO
from ..schemas.user_schema import UserWriteDTO
from ..services import UserService from ..services import UserService
router = APIRouter( router = APIRouter(
prefix="/api/v1", prefix="/user",
) )
@router.get( @router.get("/me", response_model=UserReadDTO, tags=["user"])
"/users",
response_model=list[UserReadDTO],
tags=["users"],
)
async def get_user_list(
user_service: UserService = Depends(get_user_service),
user: UserDTO = Depends(get_current_user),
) -> list[UserReadDTO] | str:
return await user_service.get_all_users()
@router.post("/users", response_model=UserReadDTO, tags=["user"], status_code=201)
async def add_user(
data: UserWriteDTO,
user_service: UserService = Depends(get_user_service),
) -> UserReadDTO:
res = await user_service.add_one(data)
if not isinstance(res, UserReadDTO):
raise HTTPException(status_code=400, detail=res)
return res
@router.get("/user/{user_uuid}", response_model=UserReadDTO, tags=["user"])
async def get_user( async def get_user(
user_uuid: UUID,
user_service: UserService = Depends(get_user_service),
) -> UserReadDTO | dict:
res = await user_service.get_user(id=user_uuid)
if not isinstance(res, UserReadDTO):
raise HTTPException(status_code=400, detail=res)
return res
@router.patch("/user/{user_uuid}", response_model=UserReadDTO, tags=["user"])
async def patch_user(
user_uuid: UUID,
data: UserWriteDTO,
user_service: UserService = Depends(get_user_service), user_service: UserService = Depends(get_user_service),
user: UserDTO = Depends(get_current_user), user: UserDTO = Depends(get_current_user),
) -> UserReadDTO | dict: ) -> UserReadDTO | None:
if user_uuid != user.id: return await user_service.get_user(email=user.email)
raise HTTPException(401, "No premission")
res = await user_service.patch_one(id=user_uuid, data=data)
if not isinstance(res, UserReadDTO):
raise HTTPException(status_code=400, detail=res)
return res
@router.delete("/user/{user_uuid}", status_code=200, tags=["user"]) @router.get("/me/create_ref", status_code=201, response_model=RefDTO)
async def delete_user( async def create_refer_code(
user_uuid: UUID,
user_service: UserService = Depends(get_user_service), user_service: UserService = Depends(get_user_service),
) -> None: user: UserDTO = Depends(get_current_user),
await user_service.delete_user(id=user_uuid) ) -> RefDTO | None:
return None return await user_service.create_refer_code(email=user.email)

View File

@ -1,4 +1,5 @@
from .base_schema import BaseDTO, ReadDTO, WriteDTO from .base_schema import BaseDTO, ReadDTO, WriteDTO
from .ref import RefDTO
from .user_schema import UserDTO, UserReadDTO, UserWriteDTO from .user_schema import UserDTO, UserReadDTO, UserWriteDTO
__all__ = ( __all__ = (
@ -8,4 +9,5 @@ __all__ = (
"UserDTO", "UserDTO",
"UserWriteDTO", "UserWriteDTO",
"UserReadDTO", "UserReadDTO",
"RefDTO",
) )

5
test_api/schemas/ref.py Normal file
View File

@ -0,0 +1,5 @@
from pydantic import BaseModel
class RefDTO(BaseModel):
refer_code: str

View File

@ -21,14 +21,14 @@ async def get_current_user(token: str = Depends(oauth2_schema)):
try: try:
payload = jwt.decode(token, "fsgddfsgdfgs", algorithms=["HS256"]) payload = jwt.decode(token, "fsgddfsgdfgs", algorithms=["HS256"])
name: str | None = payload.get("name") name: str = payload.get("name", "")
sub: str | None = payload.get("sub") sub: str = payload.get("sub", "")
expires_at: str | None = payload.get("expires_at") expires_at: str = payload.get("expires_at", "")
if sub is None: if not sub:
raise HTTPException(401, "Invalid credentials") raise HTTPException(401, "Invalid credentials")
if expires_at is not None: if expires_at:
if is_expired(expires_at): if is_expired(expires_at):
raise HTTPException(401, "Invalid credentials") raise HTTPException(401, "Invalid credentials")
@ -49,12 +49,10 @@ class AuthService:
self.crypto_context = CryptContext(schemes="bcrypt") self.crypto_context = CryptContext(schemes="bcrypt")
async def authenticate(self, login: OAuth2PasswordRequestForm = Depends()) -> TokenSchema | None: async def authenticate(self, login: OAuth2PasswordRequestForm = Depends()) -> TokenSchema | None:
print(login)
async with self.uow: async with self.uow:
user = await self.uow.users.find_one(filter={"email": login.username}) user = await self.uow.users.find_one(filter={"email": login.username})
if user.hashed_password is None: if user is None or user.hashed_password is None:
raise HTTPException(401, "Incorrect password") raise HTTPException(401, "Incorrect password")
else: else:
if not self.crypto_context.verify(login.password, user.hashed_password): if not self.crypto_context.verify(login.password, user.hashed_password):

View File

@ -1,5 +1,3 @@
from uuid import UUID
from passlib.context import CryptContext from passlib.context import CryptContext
from ..schemas.user_schema import UserDBDTO, UserWriteDTO from ..schemas.user_schema import UserDBDTO, UserWriteDTO
@ -11,11 +9,6 @@ class UserService:
self.uow = uow self.uow = uow
self.crypto_context = CryptContext(schemes="bcrypt") self.crypto_context = CryptContext(schemes="bcrypt")
async def get_all_users(self):
async with self.uow:
res = await self.uow.users.find_all()
return res
async def add_one(self, data: UserWriteDTO): async def add_one(self, data: UserWriteDTO):
new_data = data.model_dump() new_data = data.model_dump()
new_data["hashed_password"] = self.crypto_context.hash(new_data.pop("password")) new_data["hashed_password"] = self.crypto_context.hash(new_data.pop("password"))
@ -24,21 +17,12 @@ class UserService:
res = await self.uow.users.add_one(data=dataf) res = await self.uow.users.add_one(data=dataf)
return res return res
async def get_user(self, id: UUID): async def get_user(self, email: str):
async with self.uow: async with self.uow:
res = await self.uow.users.find_one(filter={"id": id}) user = await self.uow.users.find_one(filter={"email": email})
return res return user
async def patch_one(self, id: UUID, data: UserWriteDTO):
new_data = data.model_dump()
new_data["hashed_password"] = self.crypto_context.hash(new_data.pop("password"))
dataf = UserDBDTO(**new_data)
async def create_refer_code(self, email: str):
async with self.uow: async with self.uow:
res = await self.uow.users.patch_one(filter={"id": id}, data=dataf) user = await self.uow.users.find_one(filter={"email": email})
return res print(user)
async def delete_user(self, id: UUID) -> None | str:
async with self.uow:
res = await self.uow.users.delete_one(filter={"id": id})
return res