VORKOUT-7 #9

Merged
ivan.dev merged 12 commits from VORKOUT-7 into master 2025-06-05 16:29:14 +05:00
16 changed files with 237 additions and 208 deletions

View File

@ -1,18 +1,58 @@
from typing import Optional from typing import Optional
import math
from datetime import datetime, timezone from datetime import datetime, timezone
from sqlalchemy import insert, select from sqlalchemy import insert, select, func
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from enum import Enum from enum import Enum
from api.db.tables.account import account_table from api.db.tables.account import account_table
from api.schemas.account.account import User from api.schemas.account.account import User
from api.schemas.endpoints.account import UserUpdate, Role, Status from api.schemas.endpoints.account import AllUserResponse, all_user_adapter
async def get_user_id(connection: AsyncConnection, id: int) -> Optional[User]: async def get_user_accaunt_page(connection: AsyncConnection, page, limit) -> Optional[User]:
ivan.dev marked this conversation as resolved Outdated

Надо переименовать метод и изменить описание, сейчас же не только login и id

Надо переименовать метод и изменить описание, сейчас же не только `login` и `id`
"""
Получает список ползовелей заданных значениями page, limit.
"""
first_user = page*limit-(limit)
ivan.dev marked this conversation as resolved Outdated

Давай все-таки через offset делать, а то вычислять по id как-то сомнительно выглядит

Давай все-таки через `offset` делать, а то вычислять по `id` как-то сомнительно выглядит
query = (
select(account_table.c.id,
ivan.dev marked this conversation as resolved Outdated
  1. Тебе нужен адаптер для list[AllUser]. В текущем виде это не имеет смысла, это равносильно простому созданию класса по кейвордам.
  2. В документации написано, что такие конструкции лучше создавать один раз, соответственно стоит их делать глобальными тогда.
1. Тебе нужен адаптер для `list[AllUser]`. В текущем виде это не имеет смысла, это равносильно простому созданию класса по кейвордам. 2. В документации написано, что такие конструкции лучше создавать один раз, соответственно стоит их делать глобальными тогда.
account_table.c.name,
account_table.c.login,
account_table.c.email,
account_table.c.bind_tenant_id,
account_table.c.role,
account_table.c.created_at,
ivan.dev marked this conversation as resolved Outdated

Можно вместо этого использовать parse_obj_as из pydantic.tools, чтобы парсить запрос сразу из бд в модель User
Можешь посмотреть в репозитории бэка

Можно вместо этого использовать `parse_obj_as` из `pydantic.tools`, чтобы парсить запрос сразу из бд в модель `User` Можешь посмотреть в [репозитории](https://gitea.heado.ru/dinar/backend/src/branch/master/backend/db/logic/issues.py#L93) бэка

parse_obj_as устарело в Pydantic 2, не используйте это. Для подобных целей создали TypeAdapter.

`parse_obj_as` устарело в Pydantic 2, не используйте это. Для подобных целей создали [TypeAdapter](https://docs.pydantic.dev/2.11/concepts/type_adapter/).
account_table.c.status)
.order_by(account_table.c.id)
.offset(first_user)
.limit(limit)
)
count_query = select(func.count()).select_from(account_table)
result = await connection.execute(query)
count_result = await connection.execute(count_query)
ivan.dev marked this conversation as resolved Outdated

А почему ты только эти поля селектишь? Просто для теста взял?

А почему ты только эти поля селектишь? Просто для теста взял?

Да.

Да.
users_data = result.mappings().all()
total_count = count_result.scalar()
total_pages = math.ceil(total_count / limit)
validated_users = all_user_adapter.validate_python(users_data)
return AllUserResponse(
users=validated_users,
ivan.dev marked this conversation as resolved Outdated

Тут нужно вернуть обыкновенное заполнение класса и его возврат.

Тут нужно вернуть обыкновенное заполнение класса и его возврат.

Так сразу users_data можно сюда передать? На худой случай ._asdict() есть.

Так сразу `users_data` можно сюда передать? На худой случай [._asdict()](https://docs.sqlalchemy.org/en/20/core/connections.html#sqlalchemy.engine.Row._asdict) есть.
amount_count=total_count,
amount_pages=total_pages
)
async def get_user_by_id(connection: AsyncConnection, id: int) -> Optional[User]:
""" """
Получает юзера по id. Получает юзера по id.
""" """
@ -36,7 +76,7 @@ async def get_user_id(connection: AsyncConnection, id: int) -> Optional[User]:
return User.model_validate(user_data) return User.model_validate(user_data)
async def get_user_login(connection: AsyncConnection, login: str) -> Optional[User]: async def get_user_by_login(connection: AsyncConnection, login: str) -> Optional[User]:
""" """
Получает юзера по login. Получает юзера по login.
""" """
@ -60,7 +100,7 @@ async def get_user_login(connection: AsyncConnection, login: str) -> Optional[Us
return User.model_validate(user_data) return User.model_validate(user_data)
async def update_user_id(connection: AsyncConnection, update_values, user) -> Optional[User]: async def update_user_by_id(connection: AsyncConnection, update_values, user) -> Optional[User]:
""" """
Вносит изменеия в нужное поле таблицы account_table. Вносит изменеия в нужное поле таблицы account_table.
""" """

View File

@ -8,10 +8,11 @@ from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.tables.account import account_keyring_table from api.db.tables.account import account_keyring_table
from api.schemas.account.account_keyring import AccountKeyring from api.schemas.account.account_keyring import AccountKeyring
from api.schemas.endpoints.account_keyring import AccountKeyringUpdate, StatusKey, TypeKey
async def get_key_id(connection: AsyncConnection, key_id: str) -> Optional[AccountKeyring]:
async def get_key_by_id(connection: AsyncConnection, key_id: str) -> Optional[AccountKeyring]:
""" """
Получает key по key_id. Получает key по key_id.
""" """
@ -35,7 +36,7 @@ async def get_key_id(connection: AsyncConnection, key_id: str) -> Optional[Accou
return AccountKeyring.model_validate(user_data) return AccountKeyring.model_validate(user_data)
async def update_key_id(connection: AsyncConnection, update_values, key) -> Optional[AccountKeyring]: async def update_key_by_id(connection: AsyncConnection, update_values, key) -> Optional[AccountKeyring]:
""" """
Вносит изменеия в нужное поле таблицы account_keyring_table. Вносит изменеия в нужное поле таблицы account_keyring_table.
""" """

View File

@ -1,24 +1,23 @@
from sqlalchemy import Table, Column, String, Enum as SQLAEnum, JSON, ForeignKey, DateTime, Index from sqlalchemy import Table, Column, String, Enum as SQLAEnum, JSON, ForeignKey, DateTime, Index
from sqlalchemy.sql import func from sqlalchemy.sql import func
from enum import Enum, auto from enum import Enum
from api.db.sql_types import UnsignedInt from api.db.sql_types import UnsignedInt
from api.db import metadata from api.db import metadata
class AccountRole(str, Enum): class AccountRole(str,Enum):
OWNER = auto() OWNER = 'OWNER'
ADMIN = auto() ADMIN = 'ADMIN'
EDITOR = auto() EDITOR = 'EDITOR'
VIEWER = auto() VIEWER = 'VIEWER'
class AccountStatus(str,Enum):
class AccountStatus(str, Enum): ACTIVE = 'ACTIVE'
ACTIVE = auto() DISABLED = 'DISABLED'
DISABLED = auto() BLOCKED = 'BLOCKED'
BLOCKED = auto() DELETED = 'DELETED'
DELETED = auto()
account_table = Table( account_table = Table(
@ -39,17 +38,16 @@ account_table = Table(
) )
class KeyType(str, Enum): class KeyType(str,Enum):
PASSWORD = auto() PASSWORD = "PASSWORD"
ACCESS_TOKEN = auto() ACCESS_TOKEN = "ACCESS_TOKEN"
REFRESH_TOKEN = auto() REFRESH_TOKEN = "REFRESH_TOKEN"
API_KEY = auto() API_KEY = "API_KEY"
class KeyStatus(str,Enum):
class KeyStatus(str, Enum): ACTIVE = "ACTIVE"
ACTIVE = auto() EXPIRED = "EXPIRED"
EXPIRED = auto() DELETED = "DELETED"
DELETED = auto()
account_keyring_table = Table( account_keyring_table = Table(

View File

@ -1,11 +1,8 @@
from fastapi import ( from fastapi import (
APIRouter, APIRouter,
Body,
Depends, Depends,
Form,
HTTPException, HTTPException,
Request, Request,
Response,
status, status,
) )
@ -14,28 +11,52 @@ from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep from api.db.connection.session import get_connection_dep
from api.db.logic.account import get_user_id, update_user_id, create_user, get_user_login from api.db.logic.account import get_user_by_id, update_user_by_id, create_user,get_user_by_login,get_user_accaunt_page
from api.schemas.account.account import User, Status from api.schemas.account.account import User
from api.schemas.endpoints.account import UserUpdate from api.db.tables.account import AccountStatus
from api.schemas.endpoints.account import UserUpdate,AllUserResponse
from api.services.user_role_validation import db_user_role_validation from api.services.user_role_validation import db_user_role_validation
from api.services.update_data_validation import update_user_data_changes from api.services.update_data_validation import update_user_data_changes
api_router = APIRouter( api_router = APIRouter(
prefix="/account", prefix="/account",
tags=["User accountModel"], tags=["User accountModel"],
) )
@api_router.get("",response_model=AllUserResponse)
async def get_all_account(
@api_router.get("/{user_id}") request: Request,
page: int ,
limit: int ,
connection: AsyncConnection = Depends(get_connection_dep)
):
current_user = request.state.current_user
authorize_user = await db_user_role_validation(connection, current_user)
user_list = await get_user_accaunt_page(connection,page,limit)
if user_list is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Accounts not found")
return user_list
@api_router.get("/{user_id}", response_model=User)
async def get_account(user_id: int, request: Request, connection: AsyncConnection = Depends(get_connection_dep)): async def get_account(user_id: int, request: Request, connection: AsyncConnection = Depends(get_connection_dep)):
current_user = request.state.current_user current_user = request.state.current_user
authorize_user = await db_user_role_validation(connection, current_user) authorize_user = await db_user_role_validation(connection, current_user)
user = await get_user_id(connection, user_id) user = await get_user_by_id(connection, user_id)
if user is None: if user is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found")
@ -43,17 +64,24 @@ async def get_account(user_id: int, request: Request, connection: AsyncConnectio
return user return user
@api_router.post("")
async def create_account(user: UserUpdate, request: Request, connection: AsyncConnection = Depends(get_connection_dep)): @api_router.post("", response_model=User)
async def create_account(
user: UserUpdate,
request: Request,
connection: AsyncConnection = Depends(get_connection_dep)
):
current_user = request.state.current_user current_user = request.state.current_user
authorize_user = await db_user_role_validation(connection, current_user) authorize_user = await db_user_role_validation(connection, current_user)
user_validation = await get_user_login(connection, user.login) user_validation = await get_user_by_login(connection, user.login)
if user_validation is None: if user_validation is None:
await create_user(connection, user, authorize_user.id)
user_new = await get_user_login(connection, user.login) await create_user(connection,user,authorize_user.id)
user_new = await get_user_by_login(connection, user.login)
return user_new return user_new
else: else:
@ -62,7 +90,7 @@ async def create_account(user: UserUpdate, request: Request, connection: AsyncCo
) )
@api_router.put("/{user_id}") @api_router.put("/{user_id}", response_model=User)
async def update_account( async def update_account(
user_id: int, request: Request, user_update: UserUpdate, connection: AsyncConnection = Depends(get_connection_dep) user_id: int, request: Request, user_update: UserUpdate, connection: AsyncConnection = Depends(get_connection_dep)
): ):
@ -70,7 +98,7 @@ async def update_account(
authorize_user = await db_user_role_validation(connection, current_user) authorize_user = await db_user_role_validation(connection, current_user)
user = await get_user_id(connection, user_id) user = await get_user_by_id(connection, user_id)
if user is None: if user is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found")
@ -81,32 +109,40 @@ async def update_account(
user_update_data = User.model_validate({**user.model_dump(), **update_values}) user_update_data = User.model_validate({**user.model_dump(), **update_values})
await update_user_id(connection, update_values, user) await update_user_by_id(connection, update_values, user)
user = await get_user_id(connection, user_id)
user = await get_user_by_id(connection, user_id)
return user return user
@api_router.delete("/{user_id}", response_model=User)
async def delete_account(
user_id: int,
request: Request,
connection: AsyncConnection = Depends(get_connection_dep)
):
@api_router.delete("/{user_id}")
async def delete_account(user_id: int, request: Request, connection: AsyncConnection = Depends(get_connection_dep)):
current_user = request.state.current_user current_user = request.state.current_user
authorize_user = await db_user_role_validation(connection, current_user) authorize_user = await db_user_role_validation(connection, current_user)
user = await get_user_id(connection, user_id) user = await get_user_by_id(connection, user_id)
if user is None: if user is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found")
user_update = UserUpdate(status=Status.DELETED.value) user_update = UserUpdate(status=AccountStatus.DELETED.value)
update_values = update_user_data_changes(user_update, user) update_values = update_user_data_changes(user_update, user)
if update_values is None: if update_values is None:
return user return user
await update_user_id(connection, update_values, user)
user = await get_user_id(connection, user_id) await update_user_by_id(connection, update_values, user)
user = await get_user_by_id(connection, user_id)
return user return user

View File

@ -2,9 +2,7 @@ from datetime import datetime, timedelta, timezone
from fastapi import ( from fastapi import (
APIRouter, APIRouter,
Body,
Depends, Depends,
Form,
HTTPException, HTTPException,
Request, Request,
Response, Response,
@ -24,7 +22,7 @@ from api.services.auth import authenticate_user
from api.db.logic.auth import add_new_refresh_token, upgrade_old_refresh_token from api.db.logic.auth import add_new_refresh_token, upgrade_old_refresh_token
from api.schemas.endpoints.auth import Auth, AccessToken from api.schemas.endpoints.auth import Auth, Access
api_router = APIRouter( api_router = APIRouter(
prefix="/auth", prefix="/auth",
@ -50,7 +48,7 @@ def get_config():
return Settings() return Settings()
@api_router.post("") @api_router.post("", response_model=Access)
async def login_for_access_token( async def login_for_access_token(
user: Auth, user: Auth,
response: Response, response: Response,
@ -85,10 +83,11 @@ async def login_for_access_token(
Authorize.set_refresh_cookies(refresh_token) Authorize.set_refresh_cookies(refresh_token)
return AccessToken(access_token=access_token) return Access(access_token=access_token)
@api_router.post("/refresh")
@api_router.post("/refresh",response_model=Access)
async def refresh( async def refresh(
request: Request, connection: AsyncConnection = Depends(get_connection_dep), Authorize: AuthJWT = Depends() request: Request, connection: AsyncConnection = Depends(get_connection_dep), Authorize: AuthJWT = Depends()
): ):
@ -114,4 +113,4 @@ async def refresh(
new_access_token = Authorize.create_access_token(subject=current_user, expires_time=access_token_expires) new_access_token = Authorize.create_access_token(subject=current_user, expires_time=access_token_expires)
return AccessToken(access_token=new_access_token) return Access(access_token=new_access_token)

View File

@ -14,10 +14,11 @@ from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep from api.db.connection.session import get_connection_dep
from api.db.logic.keyring import get_key_id, create_key, update_key_id from api.db.logic.keyring import get_key_by_id,create_key,update_key_by_id
from api.schemas.account.account import Status
from api.db.tables.account import KeyStatus
from api.schemas.endpoints.account_keyring import AccountKeyringUpdate from api.schemas.endpoints.account_keyring import AccountKeyringUpdate
from api.schemas.account.account_keyring import AccountKeyring from api.schemas.account.account_keyring import AccountKeyring
@ -32,13 +33,18 @@ api_router = APIRouter(
) )
@api_router.get("/{user_id}/{key_id}") @api_router.get("/{user_id}/{key_id}", response_model=AccountKeyring)
async def get_keyring(key_id: str, request: Request, connection: AsyncConnection = Depends(get_connection_dep)): async def get_keyring(
key_id: str,
request: Request,
connection: AsyncConnection = Depends(get_connection_dep)
):
current_user = request.state.current_user current_user = request.state.current_user
authorize_user = await db_user_role_validation(connection, current_user) authorize_user = await db_user_role_validation(connection, current_user)
keyring = await get_key_id(connection, key_id) keyring = await get_key_by_id(connection, key_id)
if keyring is None: if keyring is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Key not found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Key not found")
@ -46,7 +52,7 @@ async def get_keyring(key_id: str, request: Request, connection: AsyncConnection
return keyring return keyring
@api_router.post("/{user_id}/{key_id}") @api_router.post("/{user_id}/{key_id}", response_model=AccountKeyring)
async def create_keyring( async def create_keyring(
user_id: int, user_id: int,
key_id: str, key_id: str,
@ -58,15 +64,11 @@ async def create_keyring(
authorize_user = await db_user_role_validation(connection, current_user) authorize_user = await db_user_role_validation(connection, current_user)
keyring = await get_key_id(connection, key_id) keyring = await get_key_by_id(connection, key_id)
if keyring is None: if keyring is None:
user_new = await create_key( keyring_new = await create_key(connection,key, key_id, )
connection, return keyring_new
key,
key_id,
)
return user_new
else: else:
raise HTTPException( raise HTTPException(
@ -74,7 +76,7 @@ async def create_keyring(
) )
@api_router.put("/{user_id}/{key_id}") @api_router.put("/{user_id}/{key_id}", response_model=AccountKeyring)
async def update_keyring( async def update_keyring(
user_id: int, user_id: int,
key_id: str, key_id: str,
@ -86,7 +88,7 @@ async def update_keyring(
authorize_user = await db_user_role_validation(connection, current_user) authorize_user = await db_user_role_validation(connection, current_user)
keyring = await get_key_id(connection, key_id) keyring = await get_key_by_id(connection, key_id)
if keyring is None: if keyring is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="keyring not found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="keyring not found")
@ -97,14 +99,14 @@ async def update_keyring(
keyring_update_data = AccountKeyring.model_validate({**keyring.model_dump(), **update_values}) keyring_update_data = AccountKeyring.model_validate({**keyring.model_dump(), **update_values})
await update_key_id(connection, update_values, keyring) await update_key_by_id(connection, update_values, keyring)
keyring = await get_key_id(connection, key_id)
keyring = await get_key_by_id(connection, key_id)
return keyring return keyring
@api_router.delete("/{user_id}/{key_id}", response_model=AccountKeyring)
@api_router.delete("/{user_id}/{key_id}")
async def delete_keyring( async def delete_keyring(
user_id: int, key_id: str, request: Request, connection: AsyncConnection = Depends(get_connection_dep) user_id: int, key_id: str, request: Request, connection: AsyncConnection = Depends(get_connection_dep)
): ):
@ -112,19 +114,20 @@ async def delete_keyring(
authorize_user = await db_user_role_validation(connection, current_user) authorize_user = await db_user_role_validation(connection, current_user)
keyring = await get_key_id(connection, key_id) keyring = await get_key_by_id(connection, key_id)
if keyring is None: if keyring is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="keyring not found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="keyring not found")
keyring_update = AccountKeyringUpdate(status=Status.DELETED.value) keyring_update = AccountKeyringUpdate(status=KeyStatus.DELETED.value)
update_values = update_key_data_changes(keyring_update, keyring) update_values = update_key_data_changes(keyring_update, keyring)
if update_values is None: if update_values is None:
return keyring return keyring
await update_key_id(connection, update_values, keyring) await update_key_by_id(connection, update_values, keyring)
keyring = await get_key_id(connection, key_id)
keyring = await get_key_by_id(connection, key_id)
return keyring return keyring

View File

@ -13,10 +13,11 @@ from fastapi import (
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep from api.db.connection.session import get_connection_dep
from api.db.logic.account import get_user_id, update_user_id, get_user_login from api.db.logic.account import get_user_by_id, update_user_by_id,get_user_by_login
from api.services.update_data_validation import update_user_data_changes from api.services.update_data_validation import update_user_data_changes
from api.schemas.endpoints.account import UserUpdate from api.schemas.endpoints.account import UserUpdate
from api.schemas.account.account import User
api_router = APIRouter( api_router = APIRouter(
@ -25,7 +26,7 @@ api_router = APIRouter(
) )
@api_router.get("") @api_router.get("",response_model=User)
async def get_profile( async def get_profile(
request: Request, request: Request,
connection: AsyncConnection = Depends(get_connection_dep), connection: AsyncConnection = Depends(get_connection_dep),
@ -33,7 +34,7 @@ async def get_profile(
# Извлекаем текущего пользователя из request.state # Извлекаем текущего пользователя из request.state
current_user = request.state.current_user current_user = request.state.current_user
user = await get_user_login(connection, current_user) user = await get_user_by_login(connection, current_user)
if user is None: if user is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found")
@ -41,7 +42,7 @@ async def get_profile(
return user return user
@api_router.put("") @api_router.put("",response_model=User)
async def update_profile( async def update_profile(
request: Request, request: Request,
user_updata: UserUpdate, user_updata: UserUpdate,
@ -49,7 +50,7 @@ async def update_profile(
): ):
current_user = request.state.current_user current_user = request.state.current_user
user = await get_user_login(connection, current_user) user = await get_user_by_login(connection, current_user)
if user is None: if user is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found")
@ -59,9 +60,9 @@ async def update_profile(
if update_values is None: if update_values is None:
return user return user
await update_user_id(connection, update_values, user) await update_user_by_id(connection, update_values, user)
user = await get_user_id(connection, user.id) user = await get_user_by_id(connection, user.id)
return user return user
else: else:

View File

@ -1,37 +1,18 @@
import datetime import datetime
from enum import Enum
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
from pydantic import EmailStr, Field from pydantic import BaseModel, EmailStr, Field
from api.db.tables.account import AccountRole,AccountStatus
from api.schemas.base import Base
# Модель для хранения информации из запроса class User(BaseModel):

Нет импорта для Base

Нет импорта для `Base`

Надо наследовать от нашего Base, а не того, который из pydantic

Надо наследовать от нашего [`Base`](https://gitea.heado.ru/Vorkout/connect/src/branch/master/api/api/schemas/base.py#L5), а не того, который из `pydantic`
class Role(Enum):
OWNER = "OWNER"
ADMIN = "ADMIN"
EDITOR = "EDITOR"
VIEWER = "VIEWER"
class Status(Enum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
BLOCKED = "BLOCKED"
DELETED = "DELETED"
class User(Base):
id: Optional[int] = None id: Optional[int] = None
name: str = Field(..., max_length=100) name: str = Field(..., max_length=100)
login: str = Field(..., max_length=100) login: str = Field(..., max_length=100)
email: Optional[EmailStr] = Field(None, max_length=100) # Электронная почта (может быть None) email: Optional[EmailStr] = Field(None, max_length=100) # Электронная почта (может быть None)
bind_tenant_id: Optional[str] = Field(None, max_length=40) bind_tenant_id: Optional[str] = Field(None, max_length=40)
role: Role role: AccountRole
meta: dict meta: dict
creator_id: Optional[int] = None creator_id: Optional[int] = None
created_at: datetime created_at: datetime
status: Status status: AccountStatus

View File

@ -1,33 +1,15 @@
import datetime import datetime
from enum import Enum from typing import Optional
from typing import Optional, Dict from pydantic import BaseModel, Field
from pydantic import Field
from datetime import datetime from datetime import datetime
from api.db.tables.account import KeyType,KeyStatus
from api.schemas.base import Base
# Модель для хранения информации из запроса class AccountKeyring(BaseModel):

Нет импорта Base

Нет импорта `Base`

Тоже самое что и с User схемой

Тоже самое что и с `User` схемой
class TypeKey(Enum):
PASSWORD = "PASSWORD"
ACCESS_TOKEN = "ACCESS_TOKEN"
REFRESH_TOKEN = "REFRESH_TOKEN"
API_KEY = "API_KEY"
class StatusKey(Enum):
ACTIVE = "ACTIVE"
EXPIRED = "EXPIRED"
DELETED = "DELETED"
class AccountKeyring(Base):
owner_id: int owner_id: int
key_type: TypeKey # Используем тот же KeyType key_type: KeyType
key_id: Optional[str] = Field(None, max_length=40) # Изменено на None как default key_id: Optional[str] = Field(None, max_length=40)
key_value: str = Field(..., max_length=255) key_value: str = Field(..., max_length=255)
created_at: datetime created_at: datetime
expiry: Optional[datetime] = None expiry: Optional[datetime] = None
status: StatusKey status: KeyStatus

View File

@ -1,36 +1,37 @@
from enum import Enum from typing import Optional, List
from typing import Optional
from datetime import datetime from datetime import datetime
from pydantic import EmailStr, Field from pydantic import BaseModel, EmailStr, Field, TypeAdapter
from api.schemas.base import Base from api.db.tables.account import AccountRole,AccountStatus
# Таблица для получения информации из запроса
class UserUpdate(BaseModel):

Тут тоже с импортами какая-то путаница

Тут тоже с импортами какая-то путаница
class Role(Enum):
OWNER = "OWNER"
ADMIN = "ADMIN"
EDITOR = "EDITOR"
VIEWER = "VIEWER"
class Status(Enum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
BLOCKED = "BLOCKED"
DELETED = "DELETED"
class UserUpdate(Base):
id: Optional[int] = None id: Optional[int] = None
name: Optional[str] = Field(None, max_length=100) name: Optional[str] = Field(None, max_length=100)
login: Optional[str] = Field(None, max_length=100) login: Optional[str] = Field(None, max_length=100)
email: Optional[EmailStr] = None email: Optional[EmailStr] = None
bind_tenant_id: Optional[str] = Field(None, max_length=40) bind_tenant_id: Optional[str] = Field(None, max_length=40)
role: Optional[Role] = None role: Optional[AccountRole] = None
meta: Optional[dict] = None meta: Optional[dict] = None
creator_id: Optional[int] = None creator_id: Optional[int] = None
created_at: Optional[datetime] = None created_at: Optional[datetime] = None
status: Optional[Status] = None status: Optional[AccountStatus] = None
class AllUser(BaseModel):
id: int
name: str
login: str
email: Optional[EmailStr] = None
bind_tenant_id: Optional[str] = None
role: AccountRole
created_at: datetime
status: AccountStatus
class AllUserResponse(BaseModel):
users: List[AllUser]
amount_count: int
amount_pages: int
ivan.dev marked this conversation as resolved Outdated

email и bind_tenant_id в бд определены так, что могут быть None, поэтому тут надо тоже это описать, а то, например, пользователь vorkout не проходит валидацию

`email` и `bind_tenant_id` в бд определены так, что могут быть `None`, поэтому тут надо тоже это описать, а то, например, пользователь `vorkout` не проходит валидацию
all_user_adapter = TypeAdapter(List[AllUser])

View File

@ -1,33 +1,15 @@
import datetime import datetime
from enum import Enum
from typing import Optional from typing import Optional
from pydantic import Field from pydantic import BaseModel, Field
from datetime import datetime from datetime import datetime
from api.db.tables.account import KeyType,KeyStatus
from api.schemas.base import Base
# Таблица для получения информации из запроса class AccountKeyringUpdate(BaseModel):

Не хватает испорта Base

Не хватает испорта `Base`
class TypeKey(Enum):
PASSWORD = "PASSWORD"
ACCESS_TOKEN = "ACCESS_TOKEN"
REFRESH_TOKEN = "REFRESH_TOKEN"
API_KEY = "API_KEY"
class StatusKey(Enum):
ACTIVE = "ACTIVE"
EXPIRED = "EXPIRED"
DELETED = "DELETED"
class AccountKeyringUpdate(Base):
owner_id: Optional[int] = None owner_id: Optional[int] = None
key_type: Optional[TypeKey] = None key_type: Optional[KeyType] = None
key_id: Optional[str] = Field(None, max_length=40) key_id: Optional[str] = Field(None, max_length=40)
key_value: Optional[str] = Field(None, max_length=255) key_value: Optional[str] = Field(None, max_length=255)
created_at: Optional[datetime] = None created_at: Optional[datetime] = None
expiry: Optional[datetime] = None expiry: Optional[datetime] = None
status: Optional[StatusKey] = None status: Optional[KeyStatus] = None

View File

@ -1,17 +1,14 @@
from api.schemas.base import Base from api.schemas.base import BaseModel
# Таблица для получения информации из запроса # Таблица для получения информации из запроса
class Auth(Base): class Auth(BaseModel):
login: str login: str
password: str password: str
class Refresh(BaseModel,):
class AccessToken(Base):
access_token: str
class Refresh(Base):
refresh_token: str refresh_token: str
class Access(BaseModel):
access_token: str

View File

@ -3,7 +3,7 @@ from typing import Dict, Any
from datetime import datetime from datetime import datetime
from enum import Enum from enum import Enum
from api.schemas.base import Base from api.schemas.base import BaseModel
class State(Enum): class State(Enum):
@ -17,7 +17,7 @@ class Status(Enum):
DELETED = "Deleted" DELETED = "Deleted"
class ListEvent(Base): class ListEvent(BaseModel):
id: int id: int
name: str = Field(..., max_length=40) name: str = Field(..., max_length=40)
title: str = Field(..., max_length=64) title: str = Field(..., max_length=64)

View File

@ -3,15 +3,17 @@ from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.logic.auth import get_user from api.db.logic.auth import get_user
# # from backend.schemas.users.token import TokenData # # from backend.schemas.users.token import TokenData
from api.schemas.account.account import User, Status from api.schemas.account.account import User
from api.db.tables.account import AccountStatus
from api.utils.hasher import Hasher from api.utils.hasher import Hasher
async def authenticate_user(connection: AsyncConnection, username: str, password: str) -> Optional[User]: async def authenticate_user(connection: AsyncConnection, username: str, password: str) -> Optional[User]:
sql_user, sql_password = await get_user(connection, username) sql_user, sql_password = await get_user(connection, username)
if not sql_user or sql_user.status != Status.ACTIVE: if not sql_user or sql_user.status != AccountStatus.ACTIVE :
return None return None
hasher = Hasher() hasher = Hasher()
if not hasher.verify_data(password, sql_password.key_value): if not hasher.verify_data(password, sql_password.key_value):
return None return None

View File

@ -1,7 +1,9 @@
from enum import Enum from enum import Enum
from typing import Optional from typing import Optional
from api.schemas.endpoints.account import UserUpdate, Role, Status from api.schemas.endpoints.account import UserUpdate
from api.schemas.endpoints.account_keyring import AccountKeyringUpdate, StatusKey, TypeKey from api.db.tables.account import KeyType,KeyStatus
from api.schemas.endpoints.account_keyring import AccountKeyringUpdate
from api.db.tables.account import AccountRole,AccountStatus
def update_user_data_changes(update_data: UserUpdate, user) -> Optional[dict]: def update_user_data_changes(update_data: UserUpdate, user) -> Optional[dict]:
@ -18,7 +20,7 @@ def update_user_data_changes(update_data: UserUpdate, user) -> Optional[dict]:
if value is None: if value is None:
continue continue
if isinstance(value, (Role, Status)): if isinstance(value, (AccountRole, AccountStatus)):
update_values[field] = value.value update_values[field] = value.value
else: else:
update_values[field] = value update_values[field] = value
@ -52,7 +54,7 @@ def update_key_data_changes(update_data: AccountKeyringUpdate, key) -> Optional[
if value is None: if value is None:
continue continue
if isinstance(value, (TypeKey, StatusKey)): if isinstance(value, (KeyType, KeyStatus)):
update_values[field] = value.value update_values[field] = value.value
else: else:
update_values[field] = value update_values[field] = value

View File

@ -2,12 +2,16 @@ from fastapi import (
HTTPException, HTTPException,
status, status,
) )
from api.db.logic.account import get_user_login from api.db.logic.account import get_user_by_login
from api.schemas.account.account import Role, Status from api.db.tables.account import AccountRole
async def db_user_role_validation(connection, current_user): async def db_user_role_validation(connection, current_user):
authorize_user = await get_user_login(connection, current_user)
if authorize_user.role not in {Role.OWNER, Role.ADMIN}: authorize_user = await get_user_by_login(connection, current_user)
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="You do not have enough permissions") if authorize_user.role not in {AccountRole.OWNER, AccountRole.ADMIN}:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You do not have enough permissions")
return authorize_user return authorize_user