Merge pull request 'VORKOUT-7 : fix' (#10) from VORKOUT-7fix into master

Reviewed-on: #10
Reviewed-by: Vladislav Syrochkin <vlad.dev@heado.ru>
This commit is contained in:
ivan.dev 2025-06-09 13:06:37 +05:00
commit 2bf0f20e73
20 changed files with 168 additions and 169 deletions

View File

@ -18,17 +18,19 @@ async def get_user_accaunt_page(connection: AsyncConnection, page, limit) -> Opt
Получает список ползовелей заданных значениями page, limit.
"""
first_user = page*limit-(limit)
first_user = page * limit - (limit)
query = (
select(account_table.c.id,
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,
account_table.c.status)
select(
account_table.c.id,
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,
account_table.c.status,
)
.order_by(account_table.c.id)
.offset(first_user)
.limit(limit)
@ -45,11 +47,7 @@ async def get_user_accaunt_page(connection: AsyncConnection, page, limit) -> Opt
validated_users = all_user_adapter.validate_python(users_data)
return AllUserResponse(
users=validated_users,
amount_count=total_count,
amount_pages=total_pages
)
return AllUserResponse(users=validated_users, amount_count=total_count, amount_pages=total_pages)
async def get_user_by_id(connection: AsyncConnection, id: int) -> Optional[User]:

View File

@ -10,8 +10,6 @@ from api.db.tables.account import account_keyring_table
from api.schemas.account.account_keyring import AccountKeyring
async def get_key_by_id(connection: AsyncConnection, key_id: str) -> Optional[AccountKeyring]:
"""
Получает key по key_id.

View File

@ -1,3 +1,5 @@
import enum
from sqlalchemy import Table, Column, String, Enum as SQLAEnum, JSON, ForeignKey, DateTime, Index
from sqlalchemy.sql import func
@ -7,17 +9,18 @@ from api.db.sql_types import UnsignedInt
from api.db import metadata
class AccountRole(str,Enum):
OWNER = 'OWNER'
ADMIN = 'ADMIN'
EDITOR = 'EDITOR'
VIEWER = 'VIEWER'
class AccountRole(enum.StrEnum):
OWNER = "OWNER"
ADMIN = "ADMIN"
EDITOR = "EDITOR"
VIEWER = "VIEWER"
class AccountStatus(str,Enum):
ACTIVE = 'ACTIVE'
DISABLED = 'DISABLED'
BLOCKED = 'BLOCKED'
DELETED = 'DELETED'
class AccountStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
BLOCKED = "BLOCKED"
DELETED = "DELETED"
account_table = Table(
@ -38,13 +41,14 @@ account_table = Table(
)
class KeyType(str,Enum):
class KeyType(enum.StrEnum):
PASSWORD = "PASSWORD"
ACCESS_TOKEN = "ACCESS_TOKEN"
REFRESH_TOKEN = "REFRESH_TOKEN"
API_KEY = "API_KEY"
class KeyStatus(str,Enum):
class KeyStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
EXPIRED = "EXPIRED"
DELETED = "DELETED"

View File

@ -1,3 +1,5 @@
import enum
from sqlalchemy import Table, Column, Integer, String, Enum as SQLAEnum, JSON, ForeignKey, DateTime, Index
from sqlalchemy.sql import func
from enum import Enum, auto
@ -7,15 +9,15 @@ from api.db.sql_types import UnsignedInt
from api.db import metadata
class EventState(str, Enum):
AUTO = auto()
DESCRIPTED = auto()
class EventState(enum.StrEnum):
AUTO = "AUTO"
DESCRIPTED = "DESCRIPTED"
class EventStatus(str, Enum):
ACTIVE = auto()
DISABLED = auto()
DELETED = auto()
class EventStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
DELETED = "DELETED"
list_events_table = Table(

View File

@ -1,3 +1,5 @@
import enum
from sqlalchemy import (
Table,
Column,
@ -19,11 +21,11 @@ from api.db.sql_types import UnsignedInt
from api.db import metadata
class ProcessStatus(str, Enum):
ACTIVE = auto()
STOPPING = auto()
STOPPED = auto()
DELETED = auto()
class ProcessStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
STOPPING = "STOPPING"
STOPPED = "STOPPED"
DELETED = "DELETED"
process_schema_table = Table(
@ -57,10 +59,10 @@ process_version_archive_table = Table(
)
class NodeStatus(str, Enum):
ACTIVE = auto()
DISABLED = auto()
DELETED = auto()
class NodeStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
DELETED = "DELETED"
class NodeType(Enum):
@ -83,11 +85,11 @@ ps_node_table = Table(
)
class NodeLinkStatus(str, Enum):
ACTIVE = auto()
STOPPING = auto()
STOPPED = auto()
DELETED = auto()
class NodeLinkStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
STOPPING = "STOPPING"
STOPPED = "STOPPED"
DELETED = "DELETED"
node_link_table = Table(

View File

@ -2,7 +2,6 @@ from fastapi import (
APIRouter,
Depends,
HTTPException,
Request,
status,
)
@ -11,49 +10,51 @@ from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep
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.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
from api.db.tables.account import AccountStatus
from api.schemas.endpoints.account import UserUpdate,AllUserResponse
from api.schemas.base import bearer_schema
from api.schemas.endpoints.account import UserUpdate, AllUserResponse
from api.services.auth import get_current_user
from api.services.user_role_validation import db_user_role_validation
from api.services.update_data_validation import update_user_data_changes
api_router = APIRouter(
prefix="/account",
tags=["User accountModel"],
)
@api_router.get("",response_model=AllUserResponse)
@api_router.get("", dependencies=[Depends(bearer_schema)], response_model=AllUserResponse)
async def get_all_account(
request: Request,
page: int ,
limit: int ,
connection: AsyncConnection = Depends(get_connection_dep)
):
current_user = request.state.current_user
page: int = 1,
limit: int = 10,
connection: AsyncConnection = Depends(get_connection_dep),
current_user=Depends(get_current_user),
):
authorize_user = await db_user_role_validation(connection, current_user)
user_list = await get_user_accaunt_page(connection,page,limit)
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")
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)):
current_user = request.state.current_user
@api_router.get("/{user_id}", dependencies=[Depends(bearer_schema)], response_model=User)
async def get_account(
user_id: int, connection: AsyncConnection = Depends(get_connection_dep), current_user=Depends(get_current_user)
):
authorize_user = await db_user_role_validation(connection, current_user)
user = await get_user_by_id(connection, user_id)
@ -64,23 +65,16 @@ async def get_account(user_id: int, request: Request, connection: AsyncConnectio
return user
@api_router.post("", response_model=User)
@api_router.post("", dependencies=[Depends(bearer_schema)], response_model=User)
async def create_account(
user: UserUpdate,
request: Request,
connection: AsyncConnection = Depends(get_connection_dep)
):
current_user = request.state.current_user
user: UserUpdate, connection: AsyncConnection = Depends(get_connection_dep), current_user=Depends(get_current_user)
):
authorize_user = await db_user_role_validation(connection, current_user)
user_validation = await get_user_by_login(connection, user.login)
if user_validation is None:
await create_user(connection,user,authorize_user.id)
await create_user(connection, user, authorize_user.id)
user_new = await get_user_by_login(connection, user.login)
return user_new
@ -90,12 +84,13 @@ async def create_account(
)
@api_router.put("/{user_id}", response_model=User)
@api_router.put("/{user_id}", dependencies=[Depends(bearer_schema)], response_model=User)
async def update_account(
user_id: int, request: Request, user_update: UserUpdate, connection: AsyncConnection = Depends(get_connection_dep)
user_id: int,
user_update: UserUpdate,
connection: AsyncConnection = Depends(get_connection_dep),
current_user=Depends(get_current_user),
):
current_user = request.state.current_user
authorize_user = await db_user_role_validation(connection, current_user)
user = await get_user_by_id(connection, user_id)
@ -111,21 +106,15 @@ async def update_account(
await update_user_by_id(connection, update_values, user)
user = await get_user_by_id(connection, user_id)
return user
@api_router.delete("/{user_id}", response_model=User)
@api_router.delete("/{user_id}", dependencies=[Depends(bearer_schema)], response_model=User)
async def delete_account(
user_id: int,
request: Request,
connection: AsyncConnection = Depends(get_connection_dep)
):
current_user = request.state.current_user
user_id: int, connection: AsyncConnection = Depends(get_connection_dep), current_user=Depends(get_current_user)
):
authorize_user = await db_user_role_validation(connection, current_user)
user = await get_user_by_id(connection, user_id)
@ -139,10 +128,8 @@ async def delete_account(
if update_values is None:
return user
await update_user_by_id(connection, update_values, user)
user = await get_user_by_id(connection, user_id)
return user

View File

@ -86,8 +86,7 @@ async def login_for_access_token(
return Access(access_token=access_token)
@api_router.post("/refresh",response_model=Access)
@api_router.post("/refresh", response_model=Access)
async def refresh(
request: Request, connection: AsyncConnection = Depends(get_connection_dep), Authorize: AuthJWT = Depends()
):

View File

@ -4,7 +4,6 @@ from fastapi import (
Depends,
Form,
HTTPException,
Request,
Response,
status,
)
@ -14,14 +13,15 @@ from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep
from api.db.logic.keyring import get_key_by_id,create_key,update_key_by_id
from api.db.logic.keyring import get_key_by_id, create_key, update_key_by_id
from api.db.tables.account import KeyStatus
from api.schemas.base import bearer_schema
from api.schemas.endpoints.account_keyring import AccountKeyringUpdate
from api.schemas.account.account_keyring import AccountKeyring
from api.services.auth import get_current_user
from api.services.user_role_validation import db_user_role_validation
from api.services.update_data_validation import update_key_data_changes
@ -33,15 +33,10 @@ api_router = APIRouter(
)
@api_router.get("/{user_id}/{key_id}", response_model=AccountKeyring)
@api_router.get("/{user_id}/{key_id}", dependencies=[Depends(bearer_schema)], response_model=AccountKeyring)
async def get_keyring(
key_id: str,
request: Request,
connection: AsyncConnection = Depends(get_connection_dep)
):
current_user = request.state.current_user
key_id: str, connection: AsyncConnection = Depends(get_connection_dep), current_user=Depends(get_current_user)
):
authorize_user = await db_user_role_validation(connection, current_user)
keyring = await get_key_by_id(connection, key_id)
@ -52,22 +47,24 @@ async def get_keyring(
return keyring
@api_router.post("/{user_id}/{key_id}", response_model=AccountKeyring)
@api_router.post("/{user_id}/{key_id}", dependencies=[Depends(bearer_schema)], response_model=AccountKeyring)
async def create_keyring(
user_id: int,
key_id: str,
request: Request,
key: AccountKeyringUpdate,
connection: AsyncConnection = Depends(get_connection_dep),
current_user=Depends(get_current_user),
):
current_user = request.state.current_user
authorize_user = await db_user_role_validation(connection, current_user)
keyring = await get_key_by_id(connection, key_id)
if keyring is None:
keyring_new = await create_key(connection,key, key_id, )
keyring_new = await create_key(
connection,
key,
key_id,
)
return keyring_new
else:
@ -76,16 +73,14 @@ async def create_keyring(
)
@api_router.put("/{user_id}/{key_id}", response_model=AccountKeyring)
@api_router.put("/{user_id}/{key_id}", dependencies=[Depends(bearer_schema)], response_model=AccountKeyring)
async def update_keyring(
user_id: int,
key_id: str,
request: Request,
keyring_update: AccountKeyringUpdate,
connection: AsyncConnection = Depends(get_connection_dep),
current_user=Depends(get_current_user),
):
current_user = request.state.current_user
authorize_user = await db_user_role_validation(connection, current_user)
keyring = await get_key_by_id(connection, key_id)
@ -101,17 +96,18 @@ async def update_keyring(
await update_key_by_id(connection, update_values, keyring)
keyring = await get_key_by_id(connection, key_id)
return keyring
@api_router.delete("/{user_id}/{key_id}", response_model=AccountKeyring)
async def delete_keyring(
user_id: int, key_id: str, request: Request, connection: AsyncConnection = Depends(get_connection_dep)
):
current_user = request.state.current_user
@api_router.delete("/{user_id}/{key_id}", dependencies=[Depends(bearer_schema)], response_model=AccountKeyring)
async def delete_keyring(
user_id: int,
key_id: str,
connection: AsyncConnection = Depends(get_connection_dep),
current_user=Depends(get_current_user),
):
authorize_user = await db_user_role_validation(connection, current_user)
keyring = await get_key_by_id(connection, key_id)
@ -127,7 +123,6 @@ async def delete_keyring(
await update_key_by_id(connection, update_values, keyring)
keyring = await get_key_by_id(connection, key_id)
return keyring

View File

@ -13,7 +13,9 @@ from fastapi import (
from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep
from api.db.logic.account import get_user_by_id, update_user_by_id,get_user_by_login
from api.db.logic.account import get_user_by_id, update_user_by_id, get_user_by_login
from api.schemas.base import bearer_schema
from api.services.auth import get_current_user
from api.services.update_data_validation import update_user_data_changes
from api.schemas.endpoints.account import UserUpdate
@ -26,14 +28,10 @@ api_router = APIRouter(
)
@api_router.get("",response_model=User)
@api_router.get("", dependencies=[Depends(bearer_schema)], response_model=User)
async def get_profile(
request: Request,
connection: AsyncConnection = Depends(get_connection_dep),
connection: AsyncConnection = Depends(get_connection_dep), current_user=Depends(get_current_user)
):
# Извлекаем текущего пользователя из request.state
current_user = request.state.current_user
user = await get_user_by_login(connection, current_user)
if user is None:
@ -42,14 +40,12 @@ async def get_profile(
return user
@api_router.put("",response_model=User)
@api_router.put("", dependencies=[Depends(bearer_schema)], response_model=User)
async def update_profile(
request: Request,
user_updata: UserUpdate,
connection: AsyncConnection = Depends(get_connection_dep),
current_user=Depends(get_current_user),
):
current_user = request.state.current_user
user = await get_user_by_login(connection, current_user)
if user is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Account not found")

View File

@ -1,11 +1,13 @@
import datetime
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, EmailStr, Field
from api.db.tables.account import AccountRole,AccountStatus
from pydantic import EmailStr, Field
from api.db.tables.account import AccountRole, AccountStatus
from api.schemas.base import Base
class User(BaseModel):
class User(Base):
id: Optional[int] = None
name: str = Field(..., max_length=100)
login: str = Field(..., max_length=100)

View File

@ -1,11 +1,13 @@
import datetime
from typing import Optional
from pydantic import BaseModel, Field
from pydantic import Field
from datetime import datetime
from api.db.tables.account import KeyType,KeyStatus
from api.db.tables.account import KeyType, KeyStatus
from api.schemas.base import Base
class AccountKeyring(BaseModel):
class AccountKeyring(Base):
owner_id: int
key_type: KeyType
key_id: Optional[str] = Field(None, max_length=40)

View File

@ -1,7 +1,11 @@
from fastapi.security import HTTPBearer
from pydantic import BaseModel, ConfigDict
from pydantic.alias_generators import to_camel
bearer_schema = HTTPBearer() # схема для авторизации в swagger
class Base(BaseModel):
model_config = ConfigDict(
from_attributes=True,

View File

@ -1,12 +1,13 @@
from typing import Optional, List
from datetime import datetime
from pydantic import BaseModel, EmailStr, Field, TypeAdapter
from pydantic import EmailStr, Field, TypeAdapter
from api.db.tables.account import AccountRole,AccountStatus
from api.db.tables.account import AccountRole, AccountStatus
from api.schemas.base import Base
class UserUpdate(BaseModel):
class UserUpdate(Base):
id: Optional[int] = None
name: Optional[str] = Field(None, max_length=100)
login: Optional[str] = Field(None, max_length=100)
@ -18,7 +19,8 @@ class UserUpdate(BaseModel):
created_at: Optional[datetime] = None
status: Optional[AccountStatus] = None
class AllUser(BaseModel):
class AllUser(Base):
id: int
name: str
login: str
@ -29,9 +31,10 @@ class AllUser(BaseModel):
status: AccountStatus
class AllUserResponse(BaseModel):
class AllUserResponse(Base):
users: List[AllUser]
amount_count: int
amount_pages: int
all_user_adapter = TypeAdapter(List[AllUser])

View File

@ -1,11 +1,13 @@
import datetime
from typing import Optional
from pydantic import BaseModel, Field
from pydantic import Field
from datetime import datetime
from api.db.tables.account import KeyType,KeyStatus
from api.db.tables.account import KeyType, KeyStatus
from api.schemas.base import Base
class AccountKeyringUpdate(BaseModel):
class AccountKeyringUpdate(Base):
owner_id: Optional[int] = None
key_type: Optional[KeyType] = None
key_id: Optional[str] = Field(None, max_length=40)

View File

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

View File

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

View File

@ -1,4 +1,4 @@
from pydantic import Field, conint
from pydantic import Field
from typing import Dict, Any
from datetime import datetime
from enum import Enum

View File

@ -1,3 +1,4 @@
from fastapi import Request, HTTPException
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.logic.auth import get_user
@ -9,11 +10,17 @@ from api.db.tables.account import AccountStatus
from api.utils.hasher import Hasher
async def get_current_user(request: Request) -> Optional[User]:
if not hasattr(request.state, "current_user"):
return HTTPException(status_code=401, detail="Unauthorized")
return request.state.current_user
async def authenticate_user(connection: AsyncConnection, username: str, password: str) -> Optional[User]:
sql_user, sql_password = await get_user(connection, username)
if not sql_user or sql_user.status != AccountStatus.ACTIVE :
return None
if not sql_user or sql_user.status != AccountStatus.ACTIVE:
return None
hasher = Hasher()
if not hasher.verify_data(password, sql_password.key_value):
return None

View File

@ -1,9 +1,9 @@
from enum import Enum
from typing import Optional
from api.schemas.endpoints.account import UserUpdate
from api.db.tables.account import KeyType,KeyStatus
from api.db.tables.account import KeyType, KeyStatus
from api.schemas.endpoints.account_keyring import AccountKeyringUpdate
from api.db.tables.account import AccountRole,AccountStatus
from api.db.tables.account import AccountRole, AccountStatus
def update_user_data_changes(update_data: UserUpdate, user) -> Optional[dict]:

View File

@ -6,12 +6,8 @@ from api.db.logic.account import get_user_by_login
from api.db.tables.account import AccountRole
async def db_user_role_validation(connection, current_user):
authorize_user = await get_user_by_login(connection, current_user)
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")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="You do not have enough permissions")
return authorize_user