сделал CRUD для profiles, а также сделал смену пароля
This commit is contained in:
parent
e58752fa99
commit
2903330a2f
@ -1,3 +1,6 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.domain.models import Profile
|
from app.domain.models import Profile
|
||||||
@ -7,8 +10,23 @@ class ProfilesRepository:
|
|||||||
def __init__(self, db: AsyncSession):
|
def __init__(self, db: AsyncSession):
|
||||||
self.db = db
|
self.db = db
|
||||||
|
|
||||||
|
async def get_by_id(self, profile_id: int) -> Optional[Profile]:
|
||||||
|
stmt = select(Profile).filter_by(id=profile_id)
|
||||||
|
result = await self.db.execute(stmt)
|
||||||
|
return result.scalars().first()
|
||||||
|
|
||||||
async def create(self, profile: Profile) -> Profile:
|
async def create(self, profile: Profile) -> Profile:
|
||||||
self.db.add(profile)
|
self.db.add(profile)
|
||||||
await self.db.commit()
|
await self.db.commit()
|
||||||
await self.db.refresh(profile)
|
await self.db.refresh(profile)
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
|
async def update(self, profile: Profile) -> Profile:
|
||||||
|
await self.db.merge(profile)
|
||||||
|
await self.db.commit()
|
||||||
|
return profile
|
||||||
|
|
||||||
|
async def delete(self, profile: Profile) -> Profile:
|
||||||
|
await self.db.delete(profile)
|
||||||
|
await self.db.commit()
|
||||||
|
return profile
|
||||||
|
|||||||
@ -40,3 +40,8 @@ class UsersRepository:
|
|||||||
await self.db.commit()
|
await self.db.commit()
|
||||||
await self.db.refresh(user)
|
await self.db.refresh(user)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
async def update(self, user: User) -> User:
|
||||||
|
await self.db.merge(user)
|
||||||
|
await self.db.commit()
|
||||||
|
return user
|
||||||
|
|||||||
57
API/app/contollers/profiles_router.py
Normal file
57
API/app/contollers/profiles_router.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.database.session import get_db
|
||||||
|
from app.domain.entities.profile import ProfileEntity
|
||||||
|
from app.infrastructure.dependencies import get_current_user, require_admin
|
||||||
|
from app.infrastructure.profiles_service import ProfilesService
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
'/',
|
||||||
|
response_model=Optional[ProfileEntity],
|
||||||
|
summary='Create a new profile',
|
||||||
|
description='Creates a new profile',
|
||||||
|
)
|
||||||
|
async def create_team(
|
||||||
|
profile: ProfileEntity,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user=Depends(require_admin),
|
||||||
|
):
|
||||||
|
profiles_service = ProfilesService(db)
|
||||||
|
return await profiles_service.create_profile(profile)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put(
|
||||||
|
'/{profile_id}/',
|
||||||
|
response_model=Optional[ProfileEntity],
|
||||||
|
summary='Update a profile',
|
||||||
|
description='Updates a profile',
|
||||||
|
)
|
||||||
|
async def create_team(
|
||||||
|
profile_id: int,
|
||||||
|
profile: ProfileEntity,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user=Depends(require_admin),
|
||||||
|
):
|
||||||
|
profiles_service = ProfilesService(db)
|
||||||
|
return await profiles_service.update_profile(profile_id, profile, user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete(
|
||||||
|
'/{profile_id}/',
|
||||||
|
response_model=Optional[ProfileEntity],
|
||||||
|
summary='Delete a profile',
|
||||||
|
description='Delete a profile',
|
||||||
|
)
|
||||||
|
async def create_team(
|
||||||
|
profile_id: int,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user=Depends(require_admin),
|
||||||
|
):
|
||||||
|
profiles_service = ProfilesService(db)
|
||||||
|
return await profiles_service.delete(profile_id, user)
|
||||||
27
API/app/contollers/users_router.py
Normal file
27
API/app/contollers/users_router.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.database.session import get_db
|
||||||
|
from app.domain.entities.user import UserEntity
|
||||||
|
from app.infrastructure.dependencies import require_admin
|
||||||
|
from app.infrastructure.users_service import UsersService
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.put(
|
||||||
|
'/{user_id}/',
|
||||||
|
response_model=Optional[UserEntity],
|
||||||
|
summary='Change user password',
|
||||||
|
description='Change user password',
|
||||||
|
)
|
||||||
|
async def create_team(
|
||||||
|
user_id: int,
|
||||||
|
new_password: str,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user=Depends(require_admin),
|
||||||
|
):
|
||||||
|
users_service = UsersService(db)
|
||||||
|
return await users_service.change_user_password(user_id, new_password)
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
from fastapi import Depends, HTTPException, Security
|
from fastapi import Depends, HTTPException, Security
|
||||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||||
@ -15,7 +17,7 @@ security = HTTPBearer()
|
|||||||
async def get_current_user(
|
async def get_current_user(
|
||||||
credentials: HTTPAuthorizationCredentials = Security(security),
|
credentials: HTTPAuthorizationCredentials = Security(security),
|
||||||
db: AsyncSession = Depends(get_db)
|
db: AsyncSession = Depends(get_db)
|
||||||
):
|
) -> Optional[User]:
|
||||||
auth_data = get_auth_data()
|
auth_data = get_auth_data()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -36,7 +38,7 @@ async def get_current_user(
|
|||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
def require_admin(user: User = Depends(get_current_user)):
|
def require_admin(user: User = Depends(get_current_user)) -> Optional[User]:
|
||||||
if user.profile.role.title != "Администратор":
|
if user.profile.role.title != "Администратор":
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied")
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied")
|
||||||
|
|
||||||
|
|||||||
135
API/app/infrastructure/profiles_service.py
Normal file
135
API/app/infrastructure/profiles_service.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import HTTPException, status
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.application.profiles_repository import ProfilesRepository
|
||||||
|
from app.application.roles_repository import RolesRepository
|
||||||
|
from app.application.teams_repository import TeamsRepository
|
||||||
|
from app.application.users_repository import UsersRepository
|
||||||
|
from app.domain.entities.profile import ProfileEntity
|
||||||
|
from app.domain.models import Profile, User
|
||||||
|
|
||||||
|
|
||||||
|
class ProfilesService:
|
||||||
|
def __init__(self, db: AsyncSession):
|
||||||
|
self.profiles_repository = ProfilesRepository(db)
|
||||||
|
self.teams_repository = TeamsRepository(db)
|
||||||
|
self.roles_repository = RolesRepository(db)
|
||||||
|
self.users_repository = UsersRepository(db)
|
||||||
|
|
||||||
|
async def create_profile(self, profile: ProfileEntity) -> Optional[ProfileEntity]:
|
||||||
|
team = await self.teams_repository.get_by_id(profile.team_id)
|
||||||
|
if team is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="The team with this ID was not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
role = await self.roles_repository.get_by_id(profile.role_id)
|
||||||
|
if role is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="The role with this ID was not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
profile_model = self.entity_to_model(profile)
|
||||||
|
|
||||||
|
profile_model = await self.profiles_repository.create(profile_model)
|
||||||
|
|
||||||
|
return self.model_to_entity(profile_model)
|
||||||
|
|
||||||
|
async def update_profile(self, profile_id: int, profile: ProfileEntity, user: User) -> Optional[
|
||||||
|
ProfileEntity
|
||||||
|
]:
|
||||||
|
user = await self.users_repository.get_by_id(user.id)
|
||||||
|
if user is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="The user with this ID was not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
profile_model = await self.profiles_repository.get_by_id(profile_id)
|
||||||
|
if profile_model.id != user.profile_id and user.profile.role.title != 'Администратор':
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Permission denied",
|
||||||
|
)
|
||||||
|
|
||||||
|
team = await self.teams_repository.get_by_id(profile.team_id)
|
||||||
|
if team is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="The team with this ID was not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
role = await self.roles_repository.get_by_id(profile.role_id)
|
||||||
|
if role is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="The role with this ID was not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
profile_model.first_name = profile.first_name
|
||||||
|
profile_model.last_name = profile.last_name
|
||||||
|
profile_model.patronymic = profile.patronymic
|
||||||
|
profile_model.birthday = profile.birthday
|
||||||
|
profile_model.email = profile.email
|
||||||
|
profile_model.phone = profile.phone
|
||||||
|
profile_model.role_id = profile.role_id
|
||||||
|
profile_model.team_id = profile.team_id
|
||||||
|
|
||||||
|
profile_model = await self.profiles_repository.update(profile_model)
|
||||||
|
|
||||||
|
return self.model_to_entity(profile_model)
|
||||||
|
|
||||||
|
async def delete(self, profile_id: int, user: User):
|
||||||
|
user = await self.users_repository.get_by_id(user.id)
|
||||||
|
if user is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="The user with this ID was not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
profile_model = await self.profiles_repository.get_by_id(profile_id)
|
||||||
|
if profile_model.id != user.profile_id and user.profile.role.title != 'Администратор':
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Permission denied",
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await self.profiles_repository.delete(profile_model)
|
||||||
|
|
||||||
|
return self.model_to_entity(result)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def model_to_entity(profile_model: Profile) -> ProfileEntity:
|
||||||
|
return ProfileEntity(
|
||||||
|
id=profile_model.id,
|
||||||
|
first_name=profile_model.first_name,
|
||||||
|
last_name=profile_model.last_name,
|
||||||
|
patronymic=profile_model.patronymic,
|
||||||
|
birthday=profile_model.birthday,
|
||||||
|
email=profile_model.email,
|
||||||
|
phone=profile_model.phone,
|
||||||
|
role_id=profile_model.role_id,
|
||||||
|
team_id=profile_model.team_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def entity_to_model(profile_entity: ProfileEntity) -> Profile:
|
||||||
|
profile_model = Profile(
|
||||||
|
first_name=profile_entity.first_name,
|
||||||
|
last_name=profile_entity.last_name,
|
||||||
|
patronymic=profile_entity.patronymic,
|
||||||
|
birthday=profile_entity.birthday,
|
||||||
|
email=profile_entity.email,
|
||||||
|
phone=profile_entity.phone,
|
||||||
|
role_id=profile_entity.role_id,
|
||||||
|
team_id=profile_entity.team_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
if profile_entity.id is not None:
|
||||||
|
profile_model.id = profile_entity.id
|
||||||
|
|
||||||
|
return profile_model
|
||||||
@ -35,6 +35,12 @@ class UsersService:
|
|||||||
detail="The role with this ID was not found",
|
detail="The role with this ID was not found",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not self.is_strong_password(register_entity.password):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="The password is too weak",
|
||||||
|
)
|
||||||
|
|
||||||
user_model, profile_model = self.register_entity_to_models(register_entity)
|
user_model, profile_model = self.register_entity_to_models(register_entity)
|
||||||
|
|
||||||
profile_model = await self.profiles_repository.create(profile_model)
|
profile_model = await self.profiles_repository.create(profile_model)
|
||||||
@ -46,6 +52,27 @@ class UsersService:
|
|||||||
|
|
||||||
return user_entity
|
return user_entity
|
||||||
|
|
||||||
|
async def change_user_password(self, user_id: int, new_password: str) -> Optional[UserEntity]:
|
||||||
|
user_model = await self.users_repository.get_by_id(user_id)
|
||||||
|
|
||||||
|
if user_model is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="The user with this ID was not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.is_strong_password(new_password):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="The password is too weak",
|
||||||
|
)
|
||||||
|
|
||||||
|
user_model.set_password(new_password)
|
||||||
|
|
||||||
|
user_model = await self.users_repository.update(user_model)
|
||||||
|
|
||||||
|
return self.user_model_to_entity(user_model)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_strong_password(password):
|
def is_strong_password(password):
|
||||||
if len(password) < 8:
|
if len(password) < 8:
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
from app.contollers.register_router import router as register_router
|
|
||||||
from app.contollers.auth_router import router as auth_router
|
from app.contollers.auth_router import router as auth_router
|
||||||
|
from app.contollers.profiles_router import router as profiles_router
|
||||||
|
from app.contollers.register_router import router as register_router
|
||||||
from app.contollers.teams_router import router as team_router
|
from app.contollers.teams_router import router as team_router
|
||||||
|
from app.contollers.users_router import router as users_router
|
||||||
from app.settings import settings
|
from app.settings import settings
|
||||||
|
|
||||||
|
|
||||||
@ -18,9 +20,11 @@ def start_app():
|
|||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|
||||||
api_app.include_router(register_router, prefix=f'{settings.PREFIX}/register', tags=['register'])
|
|
||||||
api_app.include_router(auth_router, prefix=f'{settings.PREFIX}/auth', tags=['auth'])
|
api_app.include_router(auth_router, prefix=f'{settings.PREFIX}/auth', tags=['auth'])
|
||||||
|
api_app.include_router(profiles_router, prefix=f'{settings.PREFIX}/profiles', tags=['profiles'])
|
||||||
|
api_app.include_router(register_router, prefix=f'{settings.PREFIX}/register', tags=['register'])
|
||||||
api_app.include_router(team_router, prefix=f'{settings.PREFIX}/teams', tags=['teams'])
|
api_app.include_router(team_router, prefix=f'{settings.PREFIX}/teams', tags=['teams'])
|
||||||
|
api_app.include_router(users_router, prefix=f'{settings.PREFIX}/users', tags=['users'])
|
||||||
|
|
||||||
return api_app
|
return api_app
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user