Compare commits

...

26 Commits

Author SHA1 Message Date
2bf0f20e73 Merge pull request 'VORKOUT-7 : fix' (#10) from VORKOUT-7fix into master
Reviewed-on: #10
Reviewed-by: Vladislav Syrochkin <vlad.dev@heado.ru>
2025-06-09 13:06:37 +05:00
abd87b46b3 refactor(tables): change enum, str to StrEnum 2025-06-09 13:03:33 +05:00
787dc0e8f8 refactor: refactor api project with ruff 2025-06-09 12:12:48 +05:00
2e4e9d1113 feat: add bearer schema to all endpoints with auth 2025-06-09 12:10:40 +05:00
c1d315d6e9 feat: add bearer schema and get_current_user function 2025-06-09 11:51:13 +05:00
TheNoxium
a31758192d fix: model 2025-06-09 02:10:46 +05:00
8965365afc Merge pull request 'VORKOUT-7' (#9) from VORKOUT-7 into master
Reviewed-on: #9
Reviewed-by: cyrussmeat <dr.cyrill@gmail.com>
2025-06-05 16:29:14 +05:00
TheNoxium
c68286f7cc fix:merge error 2025-06-05 14:55:38 +05:00
TheNoxium
58cc23f79b fix: mrege error 2025-06-05 14:41:18 +05:00
TheNoxium
48d52bf903 Merge branch 'master' into VORKOUT-7 2025-06-05 13:04:25 +05:00
TheNoxium
66c60cfc59 fix: name 2025-06-04 12:03:59 +05:00
TheNoxium
c60d19262e fix: model, name 2025-06-03 13:01:10 +05:00
TheNoxium
31236d558f feat: model update 2025-06-02 15:56:44 +05:00
TheNoxium
5094b84675 fix: TypeAdapter 2025-05-30 08:51:49 +05:00
7e1fe6f5c4 fix(api): fix pyproject.toml and poetry.lock files 2025-05-29 15:25:03 +05:00
TheNoxium
320c13183f feat: add TypeAdapter 2025-05-28 14:10:00 +05:00
TheNoxium
98a4692247 feat: pydantic models for swagger 2025-05-26 13:45:40 +05:00
8613cbdaac chore: update the patch version to 0.0.3 2025-05-23 18:08:32 +05:00
5f981e8ce1 Merge pull request 'VORKOUT-13' (#8) from VORKOUT-13 into master
Reviewed-on: #8
Reviewed-by: cyrussmeat <dr.cyrill@gmail.com>
2025-05-23 18:07:39 +05:00
98e425862c refactor(account): remove unused print 2025-05-23 18:05:06 +05:00
TheNoxium
96dbc744d7 feat: add query params 2025-05-23 12:48:09 +05:00
TheNoxium
e47e449a36 feat: add page, limit 2025-05-23 12:41:32 +05:00
86d48d0d1c refactor(base): replace to_camel to to_camel from pydantic 2025-05-21 15:49:23 +05:00
TheNoxium
c3c421f66f feat: get all users 2025-05-21 14:58:06 +05:00
fe91bb7103 feat: add base class for all schemas and to camel case mapper 2025-05-21 12:44:28 +05:00
f97d419467 Merge pull request 'VORKOUT-12' (#7) from VORKOUT-12 into master
Reviewed-on: #7
Reviewed-by: ivan.dev <ivan.dev@heado.ru>
Reviewed-by: cyrussmeat <dr.cyrill@gmail.com>
2025-05-21 11:53:30 +05:00
25 changed files with 867 additions and 253 deletions

View File

@ -1,18 +1,56 @@
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]:
"""
Получает список ползовелей заданных значениями page, 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,
)
.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)
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, amount_count=total_count, amount_pages=total_pages)
async def get_user_by_id(connection: AsyncConnection, id: int) -> Optional[User]:
""" """
Получает юзера по id. Получает юзера по id.
""" """
@ -36,7 +74,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 +98,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,9 @@ 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 +34,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,26 @@
import enum
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(enum.StrEnum):
OWNER = auto() OWNER = "OWNER"
ADMIN = auto() ADMIN = "ADMIN"
EDITOR = auto() EDITOR = "EDITOR"
VIEWER = auto() VIEWER = "VIEWER"
class AccountStatus(str, Enum): class AccountStatus(enum.StrEnum):
ACTIVE = auto() ACTIVE = "ACTIVE"
DISABLED = auto() DISABLED = "DISABLED"
BLOCKED = auto() BLOCKED = "BLOCKED"
DELETED = auto() DELETED = "DELETED"
account_table = Table( account_table = Table(
@ -39,17 +41,17 @@ account_table = Table(
) )
class KeyType(str, Enum): class KeyType(enum.StrEnum):
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(enum.StrEnum):
ACTIVE = auto() ACTIVE = "ACTIVE"
EXPIRED = auto() EXPIRED = "EXPIRED"
DELETED = auto() DELETED = "DELETED"
account_keyring_table = Table( account_keyring_table = Table(

View File

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

View File

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

View File

@ -1,11 +1,7 @@
from fastapi import ( from fastapi import (
APIRouter, APIRouter,
Body,
Depends, Depends,
Form,
HTTPException, HTTPException,
Request,
Response,
status, status,
) )
@ -14,11 +10,19 @@ 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,
from api.schemas.account.account import User, Status update_user_by_id,
from api.schemas.endpoints.account import UserUpdate 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.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.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
@ -30,12 +34,30 @@ api_router = APIRouter(
) )
@api_router.get("/{user_id}") @api_router.get("", dependencies=[Depends(bearer_schema)], response_model=AllUserResponse)
async def get_account(user_id: int, request: Request, connection: AsyncConnection = Depends(get_connection_dep)): async def get_all_account(
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) authorize_user = await db_user_role_validation(connection, current_user)
user = await get_user_id(connection, user_id) 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}", 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)
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 +65,17 @@ async def get_account(user_id: int, request: Request, connection: AsyncConnectio
return user return user
@api_router.post("") @api_router.post("", dependencies=[Depends(bearer_schema)], response_model=User)
async def create_account(user: UserUpdate, request: Request, connection: AsyncConnection = Depends(get_connection_dep)): async def create_account(
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) 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) await create_user(connection, user, authorize_user.id)
user_new = await get_user_login(connection, user.login) user_new = await get_user_by_login(connection, user.login)
return user_new return user_new
else: else:
@ -62,15 +84,16 @@ async def create_account(user: UserUpdate, request: Request, connection: AsyncCo
) )
@api_router.put("/{user_id}") @api_router.put("/{user_id}", dependencies=[Depends(bearer_schema)], 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,
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) 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 +104,32 @@ 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}") @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)): async def delete_account(
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) 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) 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

View File

@ -2,18 +2,14 @@ from datetime import datetime, timedelta, timezone
from fastapi import ( from fastapi import (
APIRouter, APIRouter,
Body,
Depends, Depends,
Form,
HTTPException, HTTPException,
Request, Request,
Response, Response,
status, status,
) )
from loguru import logger from loguru import logger
from pydantic.main import BaseModel
from fastapi_jwt_auth import AuthJWT from fastapi_jwt_auth import AuthJWT
from pydantic import BaseModel from pydantic import BaseModel
@ -26,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 from api.schemas.endpoints.auth import Auth, Access
api_router = APIRouter( api_router = APIRouter(
prefix="/auth", prefix="/auth",
@ -52,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,
@ -87,15 +83,10 @@ async def login_for_access_token(
Authorize.set_refresh_cookies(refresh_token) Authorize.set_refresh_cookies(refresh_token)
return { return Access(access_token=access_token)
"access_token": access_token,
# "access_token_expires": access_token_expires_time,
# "refresh_token": refresh_token,
# "refresh_token_expires": refresh_token_expires_time
}
@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()
): ):
@ -121,9 +112,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 { return Access(access_token=new_access_token)
"access_token": new_access_token,
# "access_token_expires": access_token_expires_time,
# "refresh_token": refresh_token,
# "refresh_token_expires": refresh_token_expires_time
}

View File

@ -4,7 +4,6 @@ from fastapi import (
Depends, Depends,
Form, Form,
HTTPException, HTTPException,
Request,
Response, Response,
status, status,
) )
@ -14,13 +13,15 @@ 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.base import bearer_schema
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
from api.services.auth import get_current_user
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_key_data_changes from api.services.update_data_validation import update_key_data_changes
@ -32,13 +33,13 @@ api_router = APIRouter(
) )
@api_router.get("/{user_id}/{key_id}") @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)): async def get_keyring(
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) 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,27 +47,25 @@ 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}", dependencies=[Depends(bearer_schema)], response_model=AccountKeyring)
async def create_keyring( async def create_keyring(
user_id: int, user_id: int,
key_id: str, key_id: str,
request: Request,
key: AccountKeyringUpdate, key: AccountKeyringUpdate,
connection: AsyncConnection = Depends(get_connection_dep), 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) 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, connection,
key, key,
key_id, key_id,
) )
return user_new return keyring_new
else: else:
raise HTTPException( raise HTTPException(
@ -74,19 +73,17 @@ async def create_keyring(
) )
@api_router.put("/{user_id}/{key_id}") @api_router.put("/{user_id}/{key_id}", dependencies=[Depends(bearer_schema)], response_model=AccountKeyring)
async def update_keyring( async def update_keyring(
user_id: int, user_id: int,
key_id: str, key_id: str,
request: Request,
keyring_update: AccountKeyringUpdate, keyring_update: AccountKeyringUpdate,
connection: AsyncConnection = Depends(get_connection_dep), 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) 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,34 +94,35 @@ 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}") @api_router.delete("/{user_id}/{key_id}", dependencies=[Depends(bearer_schema)], response_model=AccountKeyring)
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,
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) 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,13 @@ 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.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.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,15 +28,11 @@ api_router = APIRouter(
) )
@api_router.get("") @api_router.get("", dependencies=[Depends(bearer_schema)], response_model=User)
async def get_profile( async def get_profile(
request: Request, connection: AsyncConnection = Depends(get_connection_dep), current_user=Depends(get_current_user)
connection: AsyncConnection = Depends(get_connection_dep),
): ):
# Извлекаем текущего пользователя из request.state user = await get_user_by_login(connection, current_user)
current_user = request.state.current_user
user = await get_user_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,15 +40,13 @@ async def get_profile(
return user return user
@api_router.put("") @api_router.put("", dependencies=[Depends(bearer_schema)], response_model=User)
async def update_profile( async def update_profile(
request: Request,
user_updata: UserUpdate, user_updata: UserUpdate,
connection: AsyncConnection = Depends(get_connection_dep), 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)
user = await get_user_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 +56,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,34 +1,20 @@
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 BaseModel, EmailStr, Field from pydantic import EmailStr, Field
from api.db.tables.account import AccountRole, AccountStatus
# Модель для хранения информации из запроса from api.schemas.base import Base
class Role(Enum): class User(Base):
OWNER = "OWNER"
ADMIN = "ADMIN"
EDITOR = "EDITOR"
VIEWER = "VIEWER"
class Status(Enum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
BLOCKED = "BLOCKED"
DELETED = "DELETED"
class User(BaseModel):
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,30 +1,17 @@
import datetime import datetime
from enum import Enum from typing import Optional
from typing import Optional, Dict 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 TypeKey(Enum): class AccountKeyring(Base):
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(BaseModel):
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

14
api/api/schemas/base.py Normal file
View File

@ -0,0 +1,14 @@
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,
alias_generator=to_camel,
populate_by_name=True,
)

View File

@ -1,33 +1,40 @@
from enum import Enum from typing import Optional, List
from typing import Optional
from datetime import datetime from datetime import datetime
from pydantic import BaseModel, EmailStr, Field from pydantic import EmailStr, Field, TypeAdapter
# Таблица для получения информации из запроса from api.db.tables.account import AccountRole, AccountStatus
from api.schemas.base import Base
class Role(Enum): class UserUpdate(Base):
OWNER = "OWNER"
ADMIN = "ADMIN"
EDITOR = "EDITOR"
VIEWER = "VIEWER"
class Status(Enum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
BLOCKED = "BLOCKED"
DELETED = "DELETED"
class UserUpdate(BaseModel):
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(Base):
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(Base):
users: List[AllUser]
amount_count: int
amount_pages: int
all_user_adapter = TypeAdapter(List[AllUser])

View File

@ -1,30 +1,17 @@
import datetime import datetime
from enum import Enum
from typing import Optional from typing import Optional
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 TypeKey(Enum): class AccountKeyringUpdate(Base):
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(BaseModel):
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,12 +1,16 @@
from pydantic import BaseModel from api.schemas.base import Base
# Таблица для получения информации из запроса # Таблица для получения информации из запроса
class Auth(BaseModel): class Auth(Base):
login: str login: str
password: str password: str
class Refresh(BaseModel): class Refresh(Base):
refresh_token: str refresh_token: str
class Access(Base):
access_token: str

View File

@ -1,8 +1,10 @@
from pydantic import BaseModel, Field from pydantic import Field
from typing import Dict, Any 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
class State(Enum): class State(Enum):
AUTO = "Auto" AUTO = "Auto"
@ -15,7 +17,7 @@ class Status(Enum):
DELETED = "Deleted" DELETED = "Deleted"
class ListEvent(BaseModel): class ListEvent(Base):
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

@ -1,8 +1,10 @@
from pydantic import BaseModel, Field, conint from pydantic import Field
from typing import Dict, Any 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
class Status(Enum): class Status(Enum):
ACTIVE = "Active" ACTIVE = "Active"
@ -11,7 +13,7 @@ class Status(Enum):
DELETED = "Deleted" DELETED = "Deleted"
class MyModel(BaseModel): class MyModel(Base):
id: int id: int
link_name: str = Field(..., max_length=20) link_name: str = Field(..., max_length=20)
node_id: int node_id: int

View File

@ -1,8 +1,10 @@
from pydantic import BaseModel, Field from pydantic import Field
from typing import Dict, Any 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
class Status(Enum): class Status(Enum):
ACTIVE = "Active" ACTIVE = "Active"
@ -11,7 +13,7 @@ class Status(Enum):
DELETED = "Deleted" DELETED = "Deleted"
class ProcessSchema(BaseModel): class ProcessSchema(Base):
id: int id: int
title: str = Field(..., max_length=100) title: str = Field(..., max_length=100)
description: str description: str

View File

@ -1,9 +1,10 @@
from pydantic import BaseModel, Field
from typing import Dict, Any from typing import Dict, Any
from datetime import datetime from datetime import datetime
from api.schemas.base import Base
class ProcessStatusSchema(BaseModel):
class ProcessStatusSchema(Base):
id: int id: int
version: int version: int
snapshot: Dict[str, Any] snapshot: Dict[str, Any]

View File

@ -1,8 +1,9 @@
from pydantic import BaseModel
from datetime import datetime from datetime import datetime
from typing import Dict, Any from typing import Dict, Any
from enum import Enum from enum import Enum
from api.schemas.base import Base
class NodeType(Enum): class NodeType(Enum):
pass pass
@ -14,7 +15,7 @@ class Status(Enum):
DELETED = "Deleted" DELETED = "Deleted"
class Ps_Node(BaseModel): class Ps_Node(Base):
id: int id: int
ps_id: int ps_id: int
node_type: NodeType node_type: NodeType

View File

@ -1,16 +1,25 @@
from fastapi import Request, HTTPException
from typing import Optional from typing import Optional
from sqlalchemy.ext.asyncio import AsyncConnection 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 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]: 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):

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,12 @@ 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) authorize_user = await get_user_by_login(connection, current_user)
if authorize_user.role not in {Role.OWNER, Role.ADMIN}: 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 return authorize_user

565
api/poetry.lock generated
View File

@ -106,6 +106,18 @@ doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)",
test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""]
trio = ["trio (>=0.26.1)"] trio = ["trio (>=0.26.1)"]
[[package]]
name = "certifi"
version = "2025.4.26"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
groups = ["main"]
files = [
{file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"},
{file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"},
]
[[package]] [[package]]
name = "cffi" name = "cffi"
version = "1.17.1" version = "1.17.1"
@ -338,14 +350,40 @@ files = [
] ]
[package.dependencies] [package.dependencies]
email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"standard\""}
fastapi-cli = {version = ">=0.0.5", extras = ["standard"], optional = true, markers = "extra == \"standard\""}
httpx = {version = ">=0.23.0", optional = true, markers = "extra == \"standard\""}
jinja2 = {version = ">=3.1.5", optional = true, markers = "extra == \"standard\""}
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
python-multipart = {version = ">=0.0.18", optional = true, markers = "extra == \"standard\""}
starlette = ">=0.40.0,<0.47.0" starlette = ">=0.40.0,<0.47.0"
typing-extensions = ">=4.8.0" typing-extensions = ">=4.8.0"
uvicorn = {version = ">=0.12.0", extras = ["standard"], optional = true, markers = "extra == \"standard\""}
[package.extras] [package.extras]
all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "fastapi-cli"
version = "0.0.7"
description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "fastapi_cli-0.0.7-py3-none-any.whl", hash = "sha256:d549368ff584b2804336c61f192d86ddea080c11255f375959627911944804f4"},
{file = "fastapi_cli-0.0.7.tar.gz", hash = "sha256:02b3b65956f526412515907a0793c9094abd4bfb5457b389f645b0ea6ba3605e"},
]
[package.dependencies]
rich-toolkit = ">=0.11.1"
typer = ">=0.12.3"
uvicorn = {version = ">=0.15.0", extras = ["standard"]}
[package.extras]
standard = ["uvicorn[standard] (>=0.15.0)"]
[[package]] [[package]]
name = "fastapi-jwt-auth" name = "fastapi-jwt-auth"
version = "0.5.0" version = "0.5.0"
@ -472,6 +510,109 @@ files = [
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
] ]
[[package]]
name = "httpcore"
version = "1.0.8"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be"},
{file = "httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad"},
]
[package.dependencies]
certifi = "*"
h11 = ">=0.13,<0.15"
[package.extras]
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
trio = ["trio (>=0.22.0,<1.0)"]
[[package]]
name = "httptools"
version = "0.6.4"
description = "A collection of framework independent HTTP protocol utils."
optional = false
python-versions = ">=3.8.0"
groups = ["main"]
files = [
{file = "httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0"},
{file = "httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da"},
{file = "httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1"},
{file = "httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50"},
{file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959"},
{file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4"},
{file = "httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c"},
{file = "httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069"},
{file = "httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a"},
{file = "httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975"},
{file = "httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636"},
{file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721"},
{file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988"},
{file = "httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17"},
{file = "httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2"},
{file = "httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44"},
{file = "httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1"},
{file = "httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2"},
{file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81"},
{file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f"},
{file = "httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970"},
{file = "httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660"},
{file = "httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083"},
{file = "httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3"},
{file = "httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071"},
{file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5"},
{file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0"},
{file = "httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8"},
{file = "httptools-0.6.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d3f0d369e7ffbe59c4b6116a44d6a8eb4783aae027f2c0b366cf0aa964185dba"},
{file = "httptools-0.6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:94978a49b8f4569ad607cd4946b759d90b285e39c0d4640c6b36ca7a3ddf2efc"},
{file = "httptools-0.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dc6a8e399e15ea525305a2ddba998b0af5caa2566bcd79dcbe8948181eeaff"},
{file = "httptools-0.6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab9ba8dcf59de5181f6be44a77458e45a578fc99c31510b8c65b7d5acc3cf490"},
{file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc411e1c0a7dcd2f902c7c48cf079947a7e65b5485dea9decb82b9105ca71a43"},
{file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d54efd20338ac52ba31e7da78e4a72570cf729fac82bc31ff9199bedf1dc7440"},
{file = "httptools-0.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:df959752a0c2748a65ab5387d08287abf6779ae9165916fe053e68ae1fbdc47f"},
{file = "httptools-0.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85797e37e8eeaa5439d33e556662cc370e474445d5fab24dcadc65a8ffb04003"},
{file = "httptools-0.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:db353d22843cf1028f43c3651581e4bb49374d85692a85f95f7b9a130e1b2cab"},
{file = "httptools-0.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ffd262a73d7c28424252381a5b854c19d9de5f56f075445d33919a637e3547"},
{file = "httptools-0.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c346571fa50d2e9856a37d7cd9435a25e7fd15e236c397bf224afaa355fe9"},
{file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aafe0f1918ed07b67c1e838f950b1c1fabc683030477e60b335649b8020e1076"},
{file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0e563e54979e97b6d13f1bbc05a96109923e76b901f786a5eae36e99c01237bd"},
{file = "httptools-0.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:b799de31416ecc589ad79dd85a0b2657a8fe39327944998dea368c1d4c9e55e6"},
{file = "httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c"},
]
[package.extras]
test = ["Cython (>=0.29.24)"]
[[package]]
name = "httpx"
version = "0.28.1"
description = "The next generation HTTP client."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
{file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
]
[package.dependencies]
anyio = "*"
certifi = "*"
httpcore = "==1.*"
idna = "*"
[package.extras]
brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.10" version = "3.10"
@ -487,6 +628,24 @@ files = [
[package.extras] [package.extras]
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
[[package]]
name = "jinja2"
version = "3.1.6"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
{file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
]
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]] [[package]]
name = "loguru" name = "loguru"
version = "0.7.3" version = "0.7.3"
@ -526,6 +685,31 @@ babel = ["Babel"]
lingua = ["lingua"] lingua = ["lingua"]
testing = ["pytest"] testing = ["pytest"]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
{file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
]
[package.dependencies]
mdurl = ">=0.1,<1.0"
[package.extras]
benchmarking = ["psutil", "pytest", "pytest-benchmark"]
code-style = ["pre-commit (>=3.0,<4.0)"]
compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
linkify = ["linkify-it-py (>=1,<3)"]
plugins = ["mdit-py-plugins"]
profiling = ["gprof2dot"]
rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]] [[package]]
name = "markupsafe" name = "markupsafe"
version = "3.0.2" version = "3.0.2"
@ -597,6 +781,18 @@ files = [
{file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
] ]
[[package]]
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]] [[package]]
name = "multidict" name = "multidict"
version = "6.2.0" version = "6.2.0"
@ -992,6 +1188,21 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0
toml = ["tomli (>=2.0.1)"] toml = ["tomli (>=2.0.1)"]
yaml = ["pyyaml (>=6.0.1)"] yaml = ["pyyaml (>=6.0.1)"]
[[package]]
name = "pygments"
version = "2.19.1"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
{file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
]
[package.extras]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]] [[package]]
name = "pyjwt" name = "pyjwt"
version = "1.7.1" version = "1.7.1"
@ -1052,6 +1263,105 @@ files = [
{file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"},
] ]
[[package]]
name = "pyyaml"
version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
name = "rich"
version = "14.0.0"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
python-versions = ">=3.8.0"
groups = ["main"]
files = [
{file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"},
{file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"},
]
[package.dependencies]
markdown-it-py = ">=2.2.0"
pygments = ">=2.13.0,<3.0.0"
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "rich-toolkit"
version = "0.14.7"
description = "Rich toolkit for building command-line applications"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "rich_toolkit-0.14.7-py3-none-any.whl", hash = "sha256:def05cc6e0f1176d6263b6a26648f16a62c4563b277ca2f8538683acdba1e0da"},
{file = "rich_toolkit-0.14.7.tar.gz", hash = "sha256:6cca5a68850cc5778915f528eb785662c27ba3b4b2624612cce8340fa9701c5e"},
]
[package.dependencies]
click = ">=8.1.7"
rich = ">=13.7.1"
typing-extensions = ">=4.12.2"
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.11.10" version = "0.11.10"
@ -1080,6 +1390,18 @@ files = [
{file = "ruff-0.11.10.tar.gz", hash = "sha256:d522fb204b4959909ecac47da02830daec102eeb100fb50ea9554818d47a5fa6"}, {file = "ruff-0.11.10.tar.gz", hash = "sha256:d522fb204b4959909ecac47da02830daec102eeb100fb50ea9554818d47a5fa6"},
] ]
[[package]]
name = "shellingham"
version = "1.5.4"
description = "Tool to Detect Surrounding Shell"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"},
{file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
]
[[package]] [[package]]
name = "sniffio" name = "sniffio"
version = "1.3.1" version = "1.3.1"
@ -1208,6 +1530,24 @@ anyio = ">=3.6.2,<5"
[package.extras] [package.extras]
full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"]
[[package]]
name = "typer"
version = "0.16.0"
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855"},
{file = "typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b"},
]
[package.dependencies]
click = ">=8.0.0"
rich = ">=10.11.0"
shellingham = ">=1.3.0"
typing-extensions = ">=3.7.4.3"
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.12.2" version = "4.12.2"
@ -1249,11 +1589,234 @@ files = [
[package.dependencies] [package.dependencies]
click = ">=7.0" click = ">=7.0"
colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""}
h11 = ">=0.8" h11 = ">=0.8"
httptools = {version = ">=0.6.3", optional = true, markers = "extra == \"standard\""}
python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""}
uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""}
[package.extras] [package.extras]
standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"]
[[package]]
name = "uvloop"
version = "0.21.0"
description = "Fast implementation of asyncio event loop on top of libuv"
optional = false
python-versions = ">=3.8.0"
groups = ["main"]
markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\""
files = [
{file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"},
{file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"},
{file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26"},
{file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb"},
{file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f"},
{file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c"},
{file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8"},
{file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0"},
{file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e"},
{file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb"},
{file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6"},
{file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d"},
{file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c"},
{file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2"},
{file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d"},
{file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc"},
{file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb"},
{file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f"},
{file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281"},
{file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af"},
{file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6"},
{file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816"},
{file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc"},
{file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553"},
{file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414"},
{file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206"},
{file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe"},
{file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79"},
{file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a"},
{file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc"},
{file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b"},
{file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2"},
{file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0"},
{file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75"},
{file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd"},
{file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff"},
{file = "uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3"},
]
[package.extras]
dev = ["Cython (>=3.0,<4.0)", "setuptools (>=60)"]
docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"]
[[package]]
name = "watchfiles"
version = "1.0.5"
description = "Simple, modern and high performance file watching and code reload in python."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "watchfiles-1.0.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5c40fe7dd9e5f81e0847b1ea64e1f5dd79dd61afbedb57759df06767ac719b40"},
{file = "watchfiles-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c0db396e6003d99bb2d7232c957b5f0b5634bbd1b24e381a5afcc880f7373fb"},
{file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b551d4fb482fc57d852b4541f911ba28957d051c8776e79c3b4a51eb5e2a1b11"},
{file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:830aa432ba5c491d52a15b51526c29e4a4b92bf4f92253787f9726fe01519487"},
{file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a16512051a822a416b0d477d5f8c0e67b67c1a20d9acecb0aafa3aa4d6e7d256"},
{file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe0cbc787770e52a96c6fda6726ace75be7f840cb327e1b08d7d54eadc3bc85"},
{file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d363152c5e16b29d66cbde8fa614f9e313e6f94a8204eaab268db52231fe5358"},
{file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee32c9a9bee4d0b7bd7cbeb53cb185cf0b622ac761efaa2eba84006c3b3a614"},
{file = "watchfiles-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29c7fd632ccaf5517c16a5188e36f6612d6472ccf55382db6c7fe3fcccb7f59f"},
{file = "watchfiles-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e637810586e6fe380c8bc1b3910accd7f1d3a9a7262c8a78d4c8fb3ba6a2b3d"},
{file = "watchfiles-1.0.5-cp310-cp310-win32.whl", hash = "sha256:cd47d063fbeabd4c6cae1d4bcaa38f0902f8dc5ed168072874ea11d0c7afc1ff"},
{file = "watchfiles-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:86c0df05b47a79d80351cd179893f2f9c1b1cae49d96e8b3290c7f4bd0ca0a92"},
{file = "watchfiles-1.0.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:237f9be419e977a0f8f6b2e7b0475ababe78ff1ab06822df95d914a945eac827"},
{file = "watchfiles-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0da39ff917af8b27a4bdc5a97ac577552a38aac0d260a859c1517ea3dc1a7c4"},
{file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cfcb3952350e95603f232a7a15f6c5f86c5375e46f0bd4ae70d43e3e063c13d"},
{file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68b2dddba7a4e6151384e252a5632efcaa9bc5d1c4b567f3cb621306b2ca9f63"},
{file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95cf944fcfc394c5f9de794ce581914900f82ff1f855326f25ebcf24d5397418"},
{file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf6cd9f83d7c023b1aba15d13f705ca7b7d38675c121f3cc4a6e25bd0857ee9"},
{file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:852de68acd6212cd6d33edf21e6f9e56e5d98c6add46f48244bd479d97c967c6"},
{file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5730f3aa35e646103b53389d5bc77edfbf578ab6dab2e005142b5b80a35ef25"},
{file = "watchfiles-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:18b3bd29954bc4abeeb4e9d9cf0b30227f0f206c86657674f544cb032296acd5"},
{file = "watchfiles-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ba5552a1b07c8edbf197055bc9d518b8f0d98a1c6a73a293bc0726dce068ed01"},
{file = "watchfiles-1.0.5-cp311-cp311-win32.whl", hash = "sha256:2f1fefb2e90e89959447bc0420fddd1e76f625784340d64a2f7d5983ef9ad246"},
{file = "watchfiles-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:b6e76ceb1dd18c8e29c73f47d41866972e891fc4cc7ba014f487def72c1cf096"},
{file = "watchfiles-1.0.5-cp311-cp311-win_arm64.whl", hash = "sha256:266710eb6fddc1f5e51843c70e3bebfb0f5e77cf4f27129278c70554104d19ed"},
{file = "watchfiles-1.0.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5eb568c2aa6018e26da9e6c86f3ec3fd958cee7f0311b35c2630fa4217d17f2"},
{file = "watchfiles-1.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a04059f4923ce4e856b4b4e5e783a70f49d9663d22a4c3b3298165996d1377f"},
{file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e380c89983ce6e6fe2dd1e1921b9952fb4e6da882931abd1824c092ed495dec"},
{file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fe43139b2c0fdc4a14d4f8d5b5d967f7a2777fd3d38ecf5b1ec669b0d7e43c21"},
{file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee0822ce1b8a14fe5a066f93edd20aada932acfe348bede8aa2149f1a4489512"},
{file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a0dbcb1c2d8f2ab6e0a81c6699b236932bd264d4cef1ac475858d16c403de74d"},
{file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2014a2b18ad3ca53b1f6c23f8cd94a18ce930c1837bd891262c182640eb40a6"},
{file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f6ae86d5cb647bf58f9f655fcf577f713915a5d69057a0371bc257e2553234"},
{file = "watchfiles-1.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1a7bac2bde1d661fb31f4d4e8e539e178774b76db3c2c17c4bb3e960a5de07a2"},
{file = "watchfiles-1.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ab626da2fc1ac277bbf752446470b367f84b50295264d2d313e28dc4405d663"},
{file = "watchfiles-1.0.5-cp312-cp312-win32.whl", hash = "sha256:9f4571a783914feda92018ef3901dab8caf5b029325b5fe4558c074582815249"},
{file = "watchfiles-1.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:360a398c3a19672cf93527f7e8d8b60d8275119c5d900f2e184d32483117a705"},
{file = "watchfiles-1.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:1a2902ede862969077b97523987c38db28abbe09fb19866e711485d9fbf0d417"},
{file = "watchfiles-1.0.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0b289572c33a0deae62daa57e44a25b99b783e5f7aed81b314232b3d3c81a11d"},
{file = "watchfiles-1.0.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a056c2f692d65bf1e99c41045e3bdcaea3cb9e6b5a53dcaf60a5f3bd95fc9763"},
{file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9dca99744991fc9850d18015c4f0438865414e50069670f5f7eee08340d8b40"},
{file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:894342d61d355446d02cd3988a7326af344143eb33a2fd5d38482a92072d9563"},
{file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab44e1580924d1ffd7b3938e02716d5ad190441965138b4aa1d1f31ea0877f04"},
{file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6f9367b132078b2ceb8d066ff6c93a970a18c3029cea37bfd7b2d3dd2e5db8f"},
{file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2e55a9b162e06e3f862fb61e399fe9f05d908d019d87bf5b496a04ef18a970a"},
{file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0125f91f70e0732a9f8ee01e49515c35d38ba48db507a50c5bdcad9503af5827"},
{file = "watchfiles-1.0.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:13bb21f8ba3248386337c9fa51c528868e6c34a707f729ab041c846d52a0c69a"},
{file = "watchfiles-1.0.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:839ebd0df4a18c5b3c1b890145b5a3f5f64063c2a0d02b13c76d78fe5de34936"},
{file = "watchfiles-1.0.5-cp313-cp313-win32.whl", hash = "sha256:4a8ec1e4e16e2d5bafc9ba82f7aaecfeec990ca7cd27e84fb6f191804ed2fcfc"},
{file = "watchfiles-1.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:f436601594f15bf406518af922a89dcaab416568edb6f65c4e5bbbad1ea45c11"},
{file = "watchfiles-1.0.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2cfb371be97d4db374cba381b9f911dd35bb5f4c58faa7b8b7106c8853e5d225"},
{file = "watchfiles-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a3904d88955fda461ea2531fcf6ef73584ca921415d5cfa44457a225f4a42bc1"},
{file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b7a21715fb12274a71d335cff6c71fe7f676b293d322722fe708a9ec81d91f5"},
{file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dfd6ae1c385ab481766b3c61c44aca2b3cd775f6f7c0fa93d979ddec853d29d5"},
{file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b659576b950865fdad31fa491d31d37cf78b27113a7671d39f919828587b429b"},
{file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1909e0a9cd95251b15bff4261de5dd7550885bd172e3536824bf1cf6b121e200"},
{file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:832ccc221927c860e7286c55c9b6ebcc0265d5e072f49c7f6456c7798d2b39aa"},
{file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fbb6102b3296926d0c62cfc9347f6237fb9400aecd0ba6bbda94cae15f2b3b"},
{file = "watchfiles-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:15ac96dd567ad6c71c71f7b2c658cb22b7734901546cd50a475128ab557593ca"},
{file = "watchfiles-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b6227351e11c57ae997d222e13f5b6f1f0700d84b8c52304e8675d33a808382"},
{file = "watchfiles-1.0.5-cp39-cp39-win32.whl", hash = "sha256:974866e0db748ebf1eccab17862bc0f0303807ed9cda465d1324625b81293a18"},
{file = "watchfiles-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:9848b21ae152fe79c10dd0197304ada8f7b586d3ebc3f27f43c506e5a52a863c"},
{file = "watchfiles-1.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f59b870db1f1ae5a9ac28245707d955c8721dd6565e7f411024fa374b5362d1d"},
{file = "watchfiles-1.0.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9475b0093767e1475095f2aeb1d219fb9664081d403d1dff81342df8cd707034"},
{file = "watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc533aa50664ebd6c628b2f30591956519462f5d27f951ed03d6c82b2dfd9965"},
{file = "watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed1cd825158dcaae36acce7b2db33dcbfd12b30c34317a88b8ed80f0541cc57"},
{file = "watchfiles-1.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:554389562c29c2c182e3908b149095051f81d28c2fec79ad6c8997d7d63e0009"},
{file = "watchfiles-1.0.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a74add8d7727e6404d5dc4dcd7fac65d4d82f95928bbee0cf5414c900e86773e"},
{file = "watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb1489f25b051a89fae574505cc26360c8e95e227a9500182a7fe0afcc500ce0"},
{file = "watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0901429650652d3f0da90bad42bdafc1f9143ff3605633c455c999a2d786cac"},
{file = "watchfiles-1.0.5.tar.gz", hash = "sha256:b7529b5dcc114679d43827d8c35a07c493ad6f083633d573d81c660abc5979e9"},
]
[package.dependencies]
anyio = ">=3.0.0"
[[package]]
name = "websockets"
version = "15.0.1"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b"},
{file = "websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205"},
{file = "websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a"},
{file = "websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e"},
{file = "websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf"},
{file = "websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb"},
{file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d"},
{file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9"},
{file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c"},
{file = "websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256"},
{file = "websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41"},
{file = "websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431"},
{file = "websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57"},
{file = "websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905"},
{file = "websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562"},
{file = "websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792"},
{file = "websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413"},
{file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8"},
{file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3"},
{file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf"},
{file = "websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85"},
{file = "websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065"},
{file = "websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3"},
{file = "websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665"},
{file = "websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2"},
{file = "websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215"},
{file = "websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5"},
{file = "websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65"},
{file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe"},
{file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4"},
{file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597"},
{file = "websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9"},
{file = "websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7"},
{file = "websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931"},
{file = "websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675"},
{file = "websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151"},
{file = "websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22"},
{file = "websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f"},
{file = "websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8"},
{file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375"},
{file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d"},
{file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4"},
{file = "websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa"},
{file = "websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561"},
{file = "websockets-15.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5"},
{file = "websockets-15.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a"},
{file = "websockets-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b"},
{file = "websockets-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770"},
{file = "websockets-15.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb"},
{file = "websockets-15.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054"},
{file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee"},
{file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed"},
{file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880"},
{file = "websockets-15.0.1-cp39-cp39-win32.whl", hash = "sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411"},
{file = "websockets-15.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123"},
{file = "websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f"},
{file = "websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee"},
]
[[package]] [[package]]
name = "win32-setctime" name = "win32-setctime"
version = "1.2.0" version = "1.2.0"
@ -1370,4 +1933,4 @@ propcache = ">=0.2.0"
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.11,<4.0" python-versions = ">=3.11,<4.0"
content-hash = "22129fa3f5a2cc1190af6c7645f3dd8b4ab0f873b120987cd2e06772863e7dc8" content-hash = "5ed129fde2c5d7b3518fbb2fe2ce79f0bad2aa1060304d45a7bc26d35f7ab46b"

View File

@ -1,6 +1,6 @@
[project] [project]
name = "api" name = "api"
version = "0.0.2" version = "0.0.3"
description = "" description = ""
authors = [ authors = [
{name = "Vladislav",email = "vlad.dev@heado.ru"} {name = "Vladislav",email = "vlad.dev@heado.ru"}
@ -11,7 +11,7 @@ dependencies = [
"sqlalchemy[pymysql,aiomysql] (>=2.0.39,<3.0.0)", "sqlalchemy[pymysql,aiomysql] (>=2.0.39,<3.0.0)",
"alembic (>=1.15.1,<2.0.0)", "alembic (>=1.15.1,<2.0.0)",
"aio-pika (>=9.5.5,<10.0.0)", "aio-pika (>=9.5.5,<10.0.0)",
"fastapi[standart] (>=0.115.11,<0.116.0)", "fastapi[standard] (>=0.115.11,<0.116.0)",
"uvicorn (>=0.34.0,<0.35.0)", "uvicorn (>=0.34.0,<0.35.0)",
"loguru (>=0.7.3,<0.8.0)", "loguru (>=0.7.3,<0.8.0)",
"pydantic-settings (>=2.8.1,<3.0.0)", "pydantic-settings (>=2.8.1,<3.0.0)",