From 61e42dff2aeb6d15b151d763cff265ebc11f143c Mon Sep 17 00:00:00 2001 From: WinerTy Date: Fri, 12 Dec 2025 13:20:22 +0500 Subject: [PATCH] Feat: Maybe grpc??? --- docker-compose.yml | 11 +- pyproject.toml | 1 + realtimemap/api/v1/auth/view.py | 48 -------- realtimemap/core/app/create_app.py | 2 +- realtimemap/core/config/config.py | 3 +- realtimemap/core/config/grpc.py | 5 + .../modules/gamefication/dependencies.py | 2 +- .../gamefication/strategy}/__init__.py | 0 realtimemap/modules/user/dependencies.py | 2 +- .../modules/user/service_depenencies.py | 11 ++ realtimemap/modules/user_ban/dependencies.py | 2 +- .../modules/user_subscription/dependencies.py | 2 +- realtimemap/run_grpc.py | 6 + .../v1/gamefication => transport}/__init__.py | 0 .../grpc}/__init__.py | 0 .../grpc/generated}/__init__.py | 0 .../grpc/generated/user_service_pb2.py | 40 +++++++ .../grpc/generated/user_service_pb2.pyi | 23 ++++ .../grpc/generated/user_service_pb2_grpc.py | 107 ++++++++++++++++++ .../transport/grpc/protos/user_service.proto | 18 +++ realtimemap/transport/grpc/server.py | 13 +++ .../grpc/service/__init__.py} | 0 .../transport/grpc/service/user_service.py | 42 +++++++ realtimemap/transport/http/__init__.py | 0 .../{ => transport/http}/api/__init__.py | 0 .../{ => transport/http}/api/v1/__init__.py | 0 .../http}/api/v1/auth/__init__.py | 0 .../http}/api/v1/auth/fastapi_users.py | 0 .../transport/http/api/v1/auth/view.py | 91 +++++++++++++++ .../http/api/v1/category/__init__.py | 0 .../http}/api/v1/category/view.py | 0 .../http/api/v1/gamefication/__init__.py | 0 .../http}/api/v1/gamefication/level_view.py | 0 .../http}/api/v1/mark/__init__.py | 0 .../http}/api/v1/mark/comment_view.py | 2 +- .../{ => transport/http}/api/v1/mark/view.py | 5 +- .../http/api/v1/subscription/__init__.py | 0 .../http}/api/v1/subscription/view.py | 2 +- .../transport/http/api/v1/users/__init__.py | 0 .../http}/api/v1/users/chat_view.py | 2 +- .../{ => transport/http}/api/v1/users/view.py | 5 +- uv.lock | 90 +++++++++++++++ 42 files changed, 475 insertions(+), 60 deletions(-) delete mode 100644 realtimemap/api/v1/auth/view.py create mode 100644 realtimemap/core/config/grpc.py rename realtimemap/{api/v1/category => modules/gamefication/strategy}/__init__.py (100%) create mode 100644 realtimemap/run_grpc.py rename realtimemap/{api/v1/gamefication => transport}/__init__.py (100%) rename realtimemap/{api/v1/subscription => transport/grpc}/__init__.py (100%) rename realtimemap/{api/v1/users => transport/grpc/generated}/__init__.py (100%) create mode 100644 realtimemap/transport/grpc/generated/user_service_pb2.py create mode 100644 realtimemap/transport/grpc/generated/user_service_pb2.pyi create mode 100644 realtimemap/transport/grpc/generated/user_service_pb2_grpc.py create mode 100644 realtimemap/transport/grpc/protos/user_service.proto create mode 100644 realtimemap/transport/grpc/server.py rename realtimemap/{static/css/.gitkeep => transport/grpc/service/__init__.py} (100%) create mode 100644 realtimemap/transport/grpc/service/user_service.py create mode 100644 realtimemap/transport/http/__init__.py rename realtimemap/{ => transport/http}/api/__init__.py (100%) rename realtimemap/{ => transport/http}/api/v1/__init__.py (100%) rename realtimemap/{ => transport/http}/api/v1/auth/__init__.py (100%) rename realtimemap/{ => transport/http}/api/v1/auth/fastapi_users.py (100%) create mode 100644 realtimemap/transport/http/api/v1/auth/view.py create mode 100644 realtimemap/transport/http/api/v1/category/__init__.py rename realtimemap/{ => transport/http}/api/v1/category/view.py (100%) create mode 100644 realtimemap/transport/http/api/v1/gamefication/__init__.py rename realtimemap/{ => transport/http}/api/v1/gamefication/level_view.py (100%) rename realtimemap/{ => transport/http}/api/v1/mark/__init__.py (100%) rename realtimemap/{ => transport/http}/api/v1/mark/comment_view.py (97%) rename realtimemap/{ => transport/http}/api/v1/mark/view.py (98%) create mode 100644 realtimemap/transport/http/api/v1/subscription/__init__.py rename realtimemap/{ => transport/http}/api/v1/subscription/view.py (97%) create mode 100644 realtimemap/transport/http/api/v1/users/__init__.py rename realtimemap/{ => transport/http}/api/v1/users/chat_view.py (97%) rename realtimemap/{ => transport/http}/api/v1/users/view.py (94%) diff --git a/docker-compose.yml b/docker-compose.yml index 9595100..074bb2f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,7 +66,7 @@ services: condition: service_started restart: unless-stopped healthcheck: - test: ["CMD-SHELL", "celery --app core.celery.celery_app inspect ping -d celery@$$HOSTNAME"] + test: [ "CMD-SHELL", "celery --app core.celery.celery_app inspect ping -d celery@$$HOSTNAME" ] interval: 30s timeout: 10s retries: 3 @@ -104,6 +104,15 @@ services: condition: service_started restart: unless-stopped + grpc-service: + build: . + container_name: grpc-container + command: ../.venv/bin/python ./run_grpc.py + ports: + - "50052:50052" + networks: + - app-network + networks: app-network: driver: bridge diff --git a/pyproject.toml b/pyproject.toml index f414bb0..0ec9e9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ dependencies = [ "black>=25.1.0", "yookassa>=3.7.0", "redis>=4.6.0", + "grpcio-tools>=1.76.0", ] [dependency-groups] diff --git a/realtimemap/api/v1/auth/view.py b/realtimemap/api/v1/auth/view.py deleted file mode 100644 index b058f20..0000000 --- a/realtimemap/api/v1/auth/view.py +++ /dev/null @@ -1,48 +0,0 @@ -from fastapi import APIRouter - -from core.config import conf -from dependencies.auth.backend import authentication_backend, oauth_backend -from modules.user.schemas import UserRead, UserCreate -from .fastapi_users import fastapi_users - -router = APIRouter( - prefix="/auth", - tags=["Auth"], -) - - -router.include_router( - router=fastapi_users.get_auth_router( - authentication_backend, - ) -) - -router.include_router( - router=fastapi_users.get_register_router( - UserRead, - UserCreate, - ), -) - -if conf.api.v1.auth.activate_google_auth: - from auth.oauth import google_oauth_client - - router.include_router( - fastapi_users.get_oauth_router( - google_oauth_client, - oauth_backend, - conf.api.v1.auth.verification_token_secret, - ), - prefix="/google", - tags=["Auth"], - ) - -router.include_router( - fastapi_users.get_verify_router(UserRead), - tags=["Auth"], -) - -router.include_router( - fastapi_users.get_reset_password_router(), - tags=["Auth"], -) diff --git a/realtimemap/core/app/create_app.py b/realtimemap/core/app/create_app.py index 0ca46eb..bd94cb5 100644 --- a/realtimemap/core/app/create_app.py +++ b/realtimemap/core/app/create_app.py @@ -12,9 +12,9 @@ from starlette.staticfiles import StaticFiles from admin import setup_admin -from api.v1 import router as v1_router from core.config import conf from middleware import ProcessTimeMiddleware +from transport.http.api.v1 import router as v1_router from .exception_handler import register_exception_handler from .lifespan import lifespan from .socket import sio_app diff --git a/realtimemap/core/config/config.py b/realtimemap/core/config/config.py index e16e12e..803a4e6 100644 --- a/realtimemap/core/config/config.py +++ b/realtimemap/core/config/config.py @@ -1,4 +1,3 @@ -from functools import lru_cache from pathlib import Path from pydantic_settings import BaseSettings, SettingsConfigDict @@ -7,6 +6,7 @@ from .celery import CeleryConfig from .database import DatabaseConfig from .frontend import FrontendConfig +from .grpc import GRPCConfig from .logging import LoggingConfig from .payment import YooKassaPayment from .redis import RedisConfig @@ -32,6 +32,7 @@ class AppConfig(BaseSettings): log: LoggingConfig = LoggingConfig() payment: YooKassaPayment + grpc: GRPCConfig = GRPCConfig() mode: str = "dev" model_config = SettingsConfigDict( diff --git a/realtimemap/core/config/grpc.py b/realtimemap/core/config/grpc.py new file mode 100644 index 0000000..f881032 --- /dev/null +++ b/realtimemap/core/config/grpc.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class GRPCConfig(BaseModel): + port:str = "[::]:50052" \ No newline at end of file diff --git a/realtimemap/modules/gamefication/dependencies.py b/realtimemap/modules/gamefication/dependencies.py index ee73826..6b0e063 100644 --- a/realtimemap/modules/gamefication/dependencies.py +++ b/realtimemap/modules/gamefication/dependencies.py @@ -28,7 +28,7 @@ DBSession = Annotated[AsyncSession, Depends(get_session)] -async def get_pg_level_repository( +def get_pg_level_repository( session: DBSession, ) -> "LevelRepository": adapter = PgAdapter[Level, None, None](session, Level) diff --git a/realtimemap/api/v1/category/__init__.py b/realtimemap/modules/gamefication/strategy/__init__.py similarity index 100% rename from realtimemap/api/v1/category/__init__.py rename to realtimemap/modules/gamefication/strategy/__init__.py diff --git a/realtimemap/modules/user/dependencies.py b/realtimemap/modules/user/dependencies.py index 7f6267e..449d1cb 100644 --- a/realtimemap/modules/user/dependencies.py +++ b/realtimemap/modules/user/dependencies.py @@ -15,7 +15,7 @@ DBSession = Annotated[AsyncSession, Depends(get_session)] -async def get_pg_user_repository( +def get_pg_user_repository( session: DBSession, ) -> "UserRepository": adapter = PgAdapter[User, UserCreate, UserUpdate](session, User) diff --git a/realtimemap/modules/user/service_depenencies.py b/realtimemap/modules/user/service_depenencies.py index f221eca..c742b40 100644 --- a/realtimemap/modules/user/service_depenencies.py +++ b/realtimemap/modules/user/service_depenencies.py @@ -15,6 +15,7 @@ UsersBanRepository, UserSubscriptionRepository, ) + from sqlalchemy.ext.asyncio import AsyncSession async def get_user_service( @@ -26,3 +27,13 @@ async def get_user_service( level_repo: Annotated["LevelRepository", Depends(get_pg_level_repository)], ) -> "UserService": return UserService(user_repo, user_ban_repo, user_subs_repo, level_repo) + + +# Для gRPC! +async def create_user_service(session: "AsyncSession") -> "UserService": + user_repo = get_pg_user_repository(session) + user_ban_repo = get_user_ban_repository(session) + user_subs_repo = get_user_subscription_repository(session) + level_repo = get_pg_level_repository(session) + + return UserService(user_repo, user_ban_repo, user_subs_repo, level_repo) diff --git a/realtimemap/modules/user_ban/dependencies.py b/realtimemap/modules/user_ban/dependencies.py index 94c872f..8b9be89 100644 --- a/realtimemap/modules/user_ban/dependencies.py +++ b/realtimemap/modules/user_ban/dependencies.py @@ -13,7 +13,7 @@ from core.common.repository import UsersBanRepository -async def get_user_ban_repository( +def get_user_ban_repository( session: Annotated["AsyncSession", Depends(get_session)], ) -> "UsersBanRepository": adapter = PgAdapter[UsersBan, UsersBanCreate, UpdateUsersBan](session, UsersBan) diff --git a/realtimemap/modules/user_subscription/dependencies.py b/realtimemap/modules/user_subscription/dependencies.py index d924d8c..d2c0504 100644 --- a/realtimemap/modules/user_subscription/dependencies.py +++ b/realtimemap/modules/user_subscription/dependencies.py @@ -16,7 +16,7 @@ from core.common.repository import UserSubscriptionRepository -async def get_user_subscription_repository( +def get_user_subscription_repository( session: Annotated["AsyncSession", Depends(get_session)], ) -> "UserSubscriptionRepository": adapter = PgAdapter[ diff --git a/realtimemap/run_grpc.py b/realtimemap/run_grpc.py new file mode 100644 index 0000000..9792e9a --- /dev/null +++ b/realtimemap/run_grpc.py @@ -0,0 +1,6 @@ +import asyncio + +from transport.grpc.server import create_grpc_server + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(create_grpc_server()) diff --git a/realtimemap/api/v1/gamefication/__init__.py b/realtimemap/transport/__init__.py similarity index 100% rename from realtimemap/api/v1/gamefication/__init__.py rename to realtimemap/transport/__init__.py diff --git a/realtimemap/api/v1/subscription/__init__.py b/realtimemap/transport/grpc/__init__.py similarity index 100% rename from realtimemap/api/v1/subscription/__init__.py rename to realtimemap/transport/grpc/__init__.py diff --git a/realtimemap/api/v1/users/__init__.py b/realtimemap/transport/grpc/generated/__init__.py similarity index 100% rename from realtimemap/api/v1/users/__init__.py rename to realtimemap/transport/grpc/generated/__init__.py diff --git a/realtimemap/transport/grpc/generated/user_service_pb2.py b/realtimemap/transport/grpc/generated/user_service_pb2.py new file mode 100644 index 0000000..9c309ba --- /dev/null +++ b/realtimemap/transport/grpc/generated/user_service_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: user_service.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'user_service.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12user_service.proto\x12\x0buserservice\"\x19\n\x0bUserRequest\x12\n\n\x02id\x18\x01 \x01(\x03\"Q\n\x0cUserResponse\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08username\x18\x02 \x01(\t\x12\r\n\x05\x65mail\x18\x03 \x01(\t\x12\x14\n\x0cis_superuser\x18\x04 \x01(\x08\x32S\n\x0bUserService\x12\x44\n\x0bGetUserById\x12\x18.userservice.UserRequest\x1a\x19.userservice.UserResponse\"\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'user_service_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_USERREQUEST']._serialized_start=35 + _globals['_USERREQUEST']._serialized_end=60 + _globals['_USERRESPONSE']._serialized_start=62 + _globals['_USERRESPONSE']._serialized_end=143 + _globals['_USERSERVICE']._serialized_start=145 + _globals['_USERSERVICE']._serialized_end=228 +# @@protoc_insertion_point(module_scope) diff --git a/realtimemap/transport/grpc/generated/user_service_pb2.pyi b/realtimemap/transport/grpc/generated/user_service_pb2.pyi new file mode 100644 index 0000000..ae39d69 --- /dev/null +++ b/realtimemap/transport/grpc/generated/user_service_pb2.pyi @@ -0,0 +1,23 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class UserRequest(_message.Message): + __slots__ = ("id",) + ID_FIELD_NUMBER: _ClassVar[int] + id: int + def __init__(self, id: _Optional[int] = ...) -> None: ... + +class UserResponse(_message.Message): + __slots__ = ("id", "username", "email", "is_superuser") + ID_FIELD_NUMBER: _ClassVar[int] + USERNAME_FIELD_NUMBER: _ClassVar[int] + EMAIL_FIELD_NUMBER: _ClassVar[int] + IS_SUPERUSER_FIELD_NUMBER: _ClassVar[int] + id: int + username: str + email: str + is_superuser: bool + def __init__(self, id: _Optional[int] = ..., username: _Optional[str] = ..., email: _Optional[str] = ..., is_superuser: bool = ...) -> None: ... diff --git a/realtimemap/transport/grpc/generated/user_service_pb2_grpc.py b/realtimemap/transport/grpc/generated/user_service_pb2_grpc.py new file mode 100644 index 0000000..abb15df --- /dev/null +++ b/realtimemap/transport/grpc/generated/user_service_pb2_grpc.py @@ -0,0 +1,107 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from . import user_service_pb2 as user__service__pb2 + +GRPC_GENERATED_VERSION = "1.76.0" +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + + +try: + from grpc._utilities import first_version_is_lower + + _version_not_supported = first_version_is_lower( + GRPC_VERSION, GRPC_GENERATED_VERSION + ) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f"The grpc package installed is at version {GRPC_VERSION}," + + " but the generated code in user_service_pb2_grpc.py depends on" + + f" grpcio>={GRPC_GENERATED_VERSION}." + + f" Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}" + + f" or downgrade your generated code using grpcio-tools<={GRPC_VERSION}." + ) + + +class UserServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.GetUserById = channel.unary_unary( + "/userservice.UserService/GetUserById", + request_serializer=user__service__pb2.UserRequest.SerializeToString, + response_deserializer=user__service__pb2.UserResponse.FromString, + _registered_method=True, + ) + + +class UserServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def GetUserById(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + +def add_UserServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + "GetUserById": grpc.unary_unary_rpc_method_handler( + servicer.GetUserById, + request_deserializer=user__service__pb2.UserRequest.FromString, + response_serializer=user__service__pb2.UserResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + "userservice.UserService", rpc_method_handlers + ) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers( + "userservice.UserService", rpc_method_handlers + ) + + +# This class is part of an EXPERIMENTAL API. +class UserService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def GetUserById( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, + target, + "/userservice.UserService/GetUserById", + user__service__pb2.UserRequest.SerializeToString, + user__service__pb2.UserResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True, + ) diff --git a/realtimemap/transport/grpc/protos/user_service.proto b/realtimemap/transport/grpc/protos/user_service.proto new file mode 100644 index 0000000..831e25e --- /dev/null +++ b/realtimemap/transport/grpc/protos/user_service.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package userservice; + +service UserService{ + rpc GetUserById(UserRequest) returns (UserResponse) {} +} + +message UserRequest { + int64 id = 1; +} + +message UserResponse { + int64 id = 1; + string username = 2; + string email = 3; + bool is_superuser = 4; +} diff --git a/realtimemap/transport/grpc/server.py b/realtimemap/transport/grpc/server.py new file mode 100644 index 0000000..9e9f4f8 --- /dev/null +++ b/realtimemap/transport/grpc/server.py @@ -0,0 +1,13 @@ +import grpc.aio + +from core.config import conf +from transport.grpc.generated import user_service_pb2_grpc +from transport.grpc.service.user_service import UserService + + +async def create_grpc_server(): + server = grpc.aio.server() + user_service_pb2_grpc.add_UserServiceServicer_to_server(UserService(), server) + server.add_insecure_port(conf.grpc.port) + await server.start() + await server.wait_for_termination() diff --git a/realtimemap/static/css/.gitkeep b/realtimemap/transport/grpc/service/__init__.py similarity index 100% rename from realtimemap/static/css/.gitkeep rename to realtimemap/transport/grpc/service/__init__.py diff --git a/realtimemap/transport/grpc/service/user_service.py b/realtimemap/transport/grpc/service/user_service.py new file mode 100644 index 0000000..9845818 --- /dev/null +++ b/realtimemap/transport/grpc/service/user_service.py @@ -0,0 +1,42 @@ +from typing import Optional, TYPE_CHECKING + +import grpc + +from database.helper import db_helper +from modules.user.service_depenencies import create_user_service +from transport.grpc.generated import user_service_pb2 +from transport.grpc.generated.user_service_pb2_grpc import UserServiceServicer + +if TYPE_CHECKING: + from modules import User + + +class UserService(UserServiceServicer): + async def GetUserById(self, request, context): + async with db_helper.session_factory() as session: + try: + # Создаем user_service с сессией + user_service = await create_user_service(session) + + # Получаем пользователя + user: Optional["User"] = await user_service.user_repo.get_by_id( + request.id + ) + + if user is None: + await context.abort( + grpc.StatusCode.NOT_FOUND, + f"User with id {request.id} not found", + ) + + # Коммит происходит автоматически при выходе из контекста + return user_service_pb2.UserResponse( + id=user.id, + username=user.username, + email=user.email, + is_superuser=user.is_superuser, + ) + except Exception as e: + print(str(e)) + # Rollback происходит автоматически + await context.abort(grpc.StatusCode.INTERNAL, str(e)) diff --git a/realtimemap/transport/http/__init__.py b/realtimemap/transport/http/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/realtimemap/api/__init__.py b/realtimemap/transport/http/api/__init__.py similarity index 100% rename from realtimemap/api/__init__.py rename to realtimemap/transport/http/api/__init__.py diff --git a/realtimemap/api/v1/__init__.py b/realtimemap/transport/http/api/v1/__init__.py similarity index 100% rename from realtimemap/api/v1/__init__.py rename to realtimemap/transport/http/api/v1/__init__.py diff --git a/realtimemap/api/v1/auth/__init__.py b/realtimemap/transport/http/api/v1/auth/__init__.py similarity index 100% rename from realtimemap/api/v1/auth/__init__.py rename to realtimemap/transport/http/api/v1/auth/__init__.py diff --git a/realtimemap/api/v1/auth/fastapi_users.py b/realtimemap/transport/http/api/v1/auth/fastapi_users.py similarity index 100% rename from realtimemap/api/v1/auth/fastapi_users.py rename to realtimemap/transport/http/api/v1/auth/fastapi_users.py diff --git a/realtimemap/transport/http/api/v1/auth/view.py b/realtimemap/transport/http/api/v1/auth/view.py new file mode 100644 index 0000000..b5daabb --- /dev/null +++ b/realtimemap/transport/http/api/v1/auth/view.py @@ -0,0 +1,91 @@ +from typing import Annotated, TYPE_CHECKING + +from fastapi import APIRouter, Response +from fastapi.params import Depends + +from core.config import conf +from dependencies.auth.backend import authentication_backend, oauth_backend +from errors.http2 import AuthenticationError +from modules.user.schemas import UserRead, UserCreate +from modules.user.service_depenencies import get_user_service +from .fastapi_users import fastapi_users, get_current_user + +if TYPE_CHECKING: + from modules.user.model import User + from modules.user.service import UserService + + +router = APIRouter( + prefix="/auth", + tags=["Auth"], +) + + +router.include_router( + router=fastapi_users.get_auth_router( + authentication_backend, + ) +) + +router.include_router( + router=fastapi_users.get_register_router( + UserRead, + UserCreate, + ), +) + +if conf.api.v1.auth.activate_google_auth: + from auth.oauth import google_oauth_client + + router.include_router( + fastapi_users.get_oauth_router( + google_oauth_client, + oauth_backend, + conf.api.v1.auth.verification_token_secret, + ), + prefix="/google", + tags=["Auth"], + ) + +router.include_router( + fastapi_users.get_verify_router(UserRead), + tags=["Auth"], +) + +router.include_router( + fastapi_users.get_reset_password_router(), + tags=["Auth"], +) + + +@router.get("/token-validate") +async def verify_request_token( + user: Annotated["User", Depends(get_current_user)], + service: Annotated["UserService", Depends(get_user_service)], + response: Response, +): + """ + Эндпоинт для аутентификации микросервисов. + Проверять валиден ли токен. + Проверяет есть ли активный бан. + Args: + service: Зависимость для пользовательского сервиса + user: Зависимость на получение пользователя + response: ответ + + Returns: ORJSONResponse + + """ + + if not user: + raise AuthenticationError() + + active_ban = await service.is_ban(user.id) + + response.headers["X-User-ID"] = str(user.id) + response.headers["X-User-Email"] = user.email + response.headers["X-User-Ban"] = "true" if active_ban else "false" + response.headers["X-User-Admin"] = "true" if user.is_superuser else "false" + response.status_code = 200 + + return diff --git a/realtimemap/transport/http/api/v1/category/__init__.py b/realtimemap/transport/http/api/v1/category/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/realtimemap/api/v1/category/view.py b/realtimemap/transport/http/api/v1/category/view.py similarity index 100% rename from realtimemap/api/v1/category/view.py rename to realtimemap/transport/http/api/v1/category/view.py diff --git a/realtimemap/transport/http/api/v1/gamefication/__init__.py b/realtimemap/transport/http/api/v1/gamefication/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/realtimemap/api/v1/gamefication/level_view.py b/realtimemap/transport/http/api/v1/gamefication/level_view.py similarity index 100% rename from realtimemap/api/v1/gamefication/level_view.py rename to realtimemap/transport/http/api/v1/gamefication/level_view.py diff --git a/realtimemap/api/v1/mark/__init__.py b/realtimemap/transport/http/api/v1/mark/__init__.py similarity index 100% rename from realtimemap/api/v1/mark/__init__.py rename to realtimemap/transport/http/api/v1/mark/__init__.py diff --git a/realtimemap/api/v1/mark/comment_view.py b/realtimemap/transport/http/api/v1/mark/comment_view.py similarity index 97% rename from realtimemap/api/v1/mark/comment_view.py rename to realtimemap/transport/http/api/v1/mark/comment_view.py index 529c712..6401182 100644 --- a/realtimemap/api/v1/mark/comment_view.py +++ b/realtimemap/transport/http/api/v1/mark/comment_view.py @@ -2,7 +2,6 @@ from fastapi import APIRouter, Depends -from api.v1.auth.fastapi_users import get_current_user_without_ban from core.common.schemas import PaginationParams, PaginationResponse from dependencies.checker import check_mark_exist from modules.mark_comment.dependencies import get_mark_comment_service @@ -14,6 +13,7 @@ ReadComment, ReadCommentReply, ) +from transport.http.api.v1.auth.fastapi_users import get_current_user_without_ban from utils.cache.decorator import custom_cache if TYPE_CHECKING: diff --git a/realtimemap/api/v1/mark/view.py b/realtimemap/transport/http/api/v1/mark/view.py similarity index 98% rename from realtimemap/api/v1/mark/view.py rename to realtimemap/transport/http/api/v1/mark/view.py index 47ebef5..d2bfadc 100644 --- a/realtimemap/api/v1/mark/view.py +++ b/realtimemap/transport/http/api/v1/mark/view.py @@ -10,7 +10,6 @@ Response, ) -from api.v1.auth.fastapi_users import Annotated, get_current_user_without_ban from dependencies.notification import ( get_mark_notification_service, ) @@ -27,6 +26,10 @@ ) from modules.mark.service import MarkService from modules.notification import MarkNotificationService +from transport.http.api.v1.auth.fastapi_users import ( + Annotated, + get_current_user_without_ban, +) from utils.cache.decorator import custom_cache if TYPE_CHECKING: diff --git a/realtimemap/transport/http/api/v1/subscription/__init__.py b/realtimemap/transport/http/api/v1/subscription/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/realtimemap/api/v1/subscription/view.py b/realtimemap/transport/http/api/v1/subscription/view.py similarity index 97% rename from realtimemap/api/v1/subscription/view.py rename to realtimemap/transport/http/api/v1/subscription/view.py index c33a763..3aa3987 100644 --- a/realtimemap/api/v1/subscription/view.py +++ b/realtimemap/transport/http/api/v1/subscription/view.py @@ -3,7 +3,6 @@ from fastapi import APIRouter, Depends, Request from fastapi.responses import ORJSONResponse -from api.v1.auth.fastapi_users import get_current_user_without_ban from dependencies.payment import get_yookassa_client from integrations.payment.yookassa import YookassaClient from modules.subscription.dependencies import ( @@ -14,6 +13,7 @@ from modules.subscription.schemas import ReadSubscriptionPlan from modules.subscription.service import SubscriptionService from modules.user_subscription.schemas import CreateSubscriptionRequest +from transport.http.api.v1.auth.fastapi_users import get_current_user_without_ban if TYPE_CHECKING: from modules import User diff --git a/realtimemap/transport/http/api/v1/users/__init__.py b/realtimemap/transport/http/api/v1/users/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/realtimemap/api/v1/users/chat_view.py b/realtimemap/transport/http/api/v1/users/chat_view.py similarity index 97% rename from realtimemap/api/v1/users/chat_view.py rename to realtimemap/transport/http/api/v1/users/chat_view.py index 3729c35..927e49d 100644 --- a/realtimemap/api/v1/users/chat_view.py +++ b/realtimemap/transport/http/api/v1/users/chat_view.py @@ -2,7 +2,6 @@ from fastapi import APIRouter, Depends, BackgroundTasks -from api.v1.auth.fastapi_users import get_current_user_without_ban from dependencies.checker import check_message_exist, check_chat_exist from dependencies.notification import get_chat_notification_service from modules import User @@ -17,6 +16,7 @@ MessageParamsRequest, ) from modules.notification import ChatNotificationService +from transport.http.api.v1.auth.fastapi_users import get_current_user_without_ban router = APIRouter( prefix="/chats", diff --git a/realtimemap/api/v1/users/view.py b/realtimemap/transport/http/api/v1/users/view.py similarity index 94% rename from realtimemap/api/v1/users/view.py rename to realtimemap/transport/http/api/v1/users/view.py index 5153d42..97782cc 100644 --- a/realtimemap/api/v1/users/view.py +++ b/realtimemap/transport/http/api/v1/users/view.py @@ -4,10 +4,13 @@ from fastapi_limiter.depends import RateLimiter from starlette.responses import Response -from api.v1.auth.fastapi_users import get_current_user_without_ban, get_current_user from modules.user.schemas import UserRead, UserUpdate, UserRequestParams, DetailUserRead from modules.user.service import UserService from modules.user.service_depenencies import get_user_service +from transport.http.api.v1.auth.fastapi_users import ( + get_current_user_without_ban, + get_current_user, +) if TYPE_CHECKING: from modules import User diff --git a/uv.lock b/uv.lock index 71946ac..d8950d6 100644 --- a/uv.lock +++ b/uv.lock @@ -1052,6 +1052,70 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" }, ] +[[package]] +name = "grpcio" +version = "1.76.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182, upload-time = "2025-10-21T16:23:12.106Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716, upload-time = "2025-10-21T16:21:48.475Z" }, + { url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522, upload-time = "2025-10-21T16:21:51.142Z" }, + { url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558, upload-time = "2025-10-21T16:21:54.213Z" }, + { url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990, upload-time = "2025-10-21T16:21:56.476Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387, upload-time = "2025-10-21T16:21:59.051Z" }, + { url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668, upload-time = "2025-10-21T16:22:02.049Z" }, + { url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928, upload-time = "2025-10-21T16:22:04.984Z" }, + { url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983, upload-time = "2025-10-21T16:22:07.881Z" }, + { url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727, upload-time = "2025-10-21T16:22:10.032Z" }, + { url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799, upload-time = "2025-10-21T16:22:12.709Z" }, + { url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417, upload-time = "2025-10-21T16:22:15.02Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219, upload-time = "2025-10-21T16:22:17.954Z" }, + { url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826, upload-time = "2025-10-21T16:22:20.721Z" }, + { url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550, upload-time = "2025-10-21T16:22:23.637Z" }, + { url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564, upload-time = "2025-10-21T16:22:26.016Z" }, + { url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236, upload-time = "2025-10-21T16:22:28.362Z" }, + { url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795, upload-time = "2025-10-21T16:22:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214, upload-time = "2025-10-21T16:22:33.831Z" }, + { url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961, upload-time = "2025-10-21T16:22:36.468Z" }, + { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462, upload-time = "2025-10-21T16:22:39.772Z" }, +] + +[[package]] +name = "grpcio-tools" +version = "1.76.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "grpcio" }, + { name = "protobuf" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a0/77/17d60d636ccd86a0db0eccc24d02967bbc3eea86b9db7324b04507ebaa40/grpcio_tools-1.76.0.tar.gz", hash = "sha256:ce80169b5e6adf3e8302f3ebb6cb0c3a9f08089133abca4b76ad67f751f5ad88", size = 5390807, upload-time = "2025-10-21T16:26:55.416Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/01/b16fe73f129df49811d886dc99d3813a33cf4d1c6e101252b81c895e929f/grpcio_tools-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ff48969f81858397ef33a36b326f2dbe2053a48b254593785707845db73c8f44", size = 2546312, upload-time = "2025-10-21T16:25:37.138Z" }, + { url = "https://files.pythonhosted.org/packages/25/17/2594c5feb76bb0b25bfbf91ec1075b276e1b2325e4bc7ea649a7b5dbf353/grpcio_tools-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa2f030fd0ef17926026ee8e2b700e388d3439155d145c568fa6b32693277613", size = 5839627, upload-time = "2025-10-21T16:25:40.082Z" }, + { url = "https://files.pythonhosted.org/packages/c7/c6/097b1aa26fbf72fb3cdb30138a2788529e4f10d8759de730a83f5c06726e/grpcio_tools-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bacbf3c54f88c38de8e28f8d9b97c90b76b105fb9ddef05d2c50df01b32b92af", size = 2592817, upload-time = "2025-10-21T16:25:42.301Z" }, + { url = "https://files.pythonhosted.org/packages/03/78/d1d985b48592a674509a85438c1a3d4c36304ddfc99d1b05d27233b51062/grpcio_tools-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0d4e4afe9a0e3c24fad2f1af45f98cf8700b2bfc4d790795756ba035d2ea7bdc", size = 2905186, upload-time = "2025-10-21T16:25:44.395Z" }, + { url = "https://files.pythonhosted.org/packages/b9/0e/770afbb47f0b5f594b93a7b46a95b892abda5eebe60efb511e96cee52170/grpcio_tools-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fbbd4e1fc5af98001ceef5e780e8c10921d94941c3809238081e73818ef707f1", size = 2656188, upload-time = "2025-10-21T16:25:46.942Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2b/017c2fcf4c5d3cf00cf7d5ce21eb88521de0d89bdcf26538ad2862ec6d07/grpcio_tools-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b05efe5a59883ab8292d596657273a60e0c3e4f5a9723c32feb9fc3a06f2f3ef", size = 3109141, upload-time = "2025-10-21T16:25:49.137Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5f/2495f88e3d50c6f2c2da2752bad4fa3a30c52ece6c9d8b0c636cd8b1430b/grpcio_tools-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:be483b90e62b7892eb71fa1fc49750bee5b2ee35b5ec99dd2b32bed4bedb5d71", size = 3657892, upload-time = "2025-10-21T16:25:52.362Z" }, + { url = "https://files.pythonhosted.org/packages/5e/1d/c4f39d31b19d9baf35d900bf3f969ce1c842f63a8560c8003ed2e5474760/grpcio_tools-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:630cd7fd3e8a63e20703a7ad816979073c2253e591b5422583c27cae2570de73", size = 3324778, upload-time = "2025-10-21T16:25:54.629Z" }, + { url = "https://files.pythonhosted.org/packages/b4/b6/35ee3a6e4af85a93da28428f81f4b29bcb36f6986b486ad71910fcc02e25/grpcio_tools-1.76.0-cp313-cp313-win32.whl", hash = "sha256:eb2567280f9f6da5444043f0e84d8408c7a10df9ba3201026b30e40ef3814736", size = 993084, upload-time = "2025-10-21T16:25:56.52Z" }, + { url = "https://files.pythonhosted.org/packages/f3/7a/5bd72344d86ee860e5920c9a7553cfe3bc7b1fce79f18c00ac2497f5799f/grpcio_tools-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:0071b1c0bd0f5f9d292dca4efab32c92725d418e57f9c60acdc33c0172af8b53", size = 1158151, upload-time = "2025-10-21T16:25:58.468Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c0/aa20eebe8f3553b7851643e9c88d237c3a6ca30ade646897e25dbb27be99/grpcio_tools-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:c53c5719ef2a435997755abde3826ba4087174bd432aa721d8fac781fcea79e4", size = 2546297, upload-time = "2025-10-21T16:26:01.258Z" }, + { url = "https://files.pythonhosted.org/packages/d9/98/6af702804934443c1d0d4d27d21b990d92d22ddd1b6bec6b056558cbbffa/grpcio_tools-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:e3db1300d7282264639eeee7243f5de7e6a7c0283f8bf05d66c0315b7b0f0b36", size = 5839804, upload-time = "2025-10-21T16:26:05.495Z" }, + { url = "https://files.pythonhosted.org/packages/ea/8d/7725fa7b134ef8405ffe0a37c96eeb626e5af15d70e1bdac4f8f1abf842e/grpcio_tools-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b018a4b7455a7e8c16d0fdb3655a6ba6c9536da6de6c5d4f11b6bb73378165b", size = 2593922, upload-time = "2025-10-21T16:26:07.563Z" }, + { url = "https://files.pythonhosted.org/packages/de/ff/5b6b5012c79fa72f9107dc13f7226d9ce7e059ea639fd8c779e0dd284386/grpcio_tools-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ec6e4de3866e47cfde56607b1fae83ecc5aa546e06dec53de11f88063f4b5275", size = 2905327, upload-time = "2025-10-21T16:26:09.668Z" }, + { url = "https://files.pythonhosted.org/packages/24/01/2691d369ea462cd6b6c92544122885ca01f7fa5ac75dee023e975e675858/grpcio_tools-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b8da4d828883913f1852bdd67383713ae5c11842f6c70f93f31893eab530aead", size = 2656214, upload-time = "2025-10-21T16:26:11.773Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e7/3f8856e6ec3dd492336a91572993344966f237b0e3819fbe96437b19d313/grpcio_tools-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5c120c2cf4443121800e7f9bcfe2e94519fa25f3bb0b9882359dd3b252c78a7b", size = 3109889, upload-time = "2025-10-21T16:26:15.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e4/ce5248072e47db276dc7e069e93978dcde490c959788ce7cce8081d0bfdc/grpcio_tools-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8b7df5591d699cd9076065f1f15049e9c3597e0771bea51c8c97790caf5e4197", size = 3657939, upload-time = "2025-10-21T16:26:17.34Z" }, + { url = "https://files.pythonhosted.org/packages/f6/df/81ff88af93c52135e425cd5ec9fe8b186169c7d5f9e0409bdf2bbedc3919/grpcio_tools-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a25048c5f984d33e3f5b6ad7618e98736542461213ade1bd6f2fcfe8ce804e3d", size = 3324752, upload-time = "2025-10-21T16:26:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/35/3d/f6b83044afbf6522254a3b509515a00fed16a819c87731a478dbdd1d35c1/grpcio_tools-1.76.0-cp314-cp314-win32.whl", hash = "sha256:4b77ce6b6c17869858cfe14681ad09ed3a8a80e960e96035de1fd87f78158740", size = 1015578, upload-time = "2025-10-21T16:26:22.517Z" }, + { url = "https://files.pythonhosted.org/packages/95/4d/31236cddb7ffb09ba4a49f4f56d2608fec3bbb21c7a0a975d93bca7cd22e/grpcio_tools-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:2ccd2c8d041351cc29d0fc4a84529b11ee35494a700b535c1f820b642f2a72fc", size = 1190242, upload-time = "2025-10-21T16:26:25.296Z" }, +] + [[package]] name = "gunicorn" version = "23.0.0" @@ -1758,6 +1822,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] +[[package]] +name = "protobuf" +version = "6.33.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296, upload-time = "2025-12-06T00:17:53.311Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603, upload-time = "2025-12-06T00:17:41.114Z" }, + { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930, upload-time = "2025-12-06T00:17:43.278Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621, upload-time = "2025-12-06T00:17:44.445Z" }, + { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460, upload-time = "2025-12-06T00:17:45.678Z" }, + { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168, upload-time = "2025-12-06T00:17:46.813Z" }, + { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270, upload-time = "2025-12-06T00:17:48.253Z" }, + { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501, upload-time = "2025-12-06T00:17:52.211Z" }, +] + [[package]] name = "psycopg2" version = "2.9.11" @@ -2132,6 +2211,7 @@ dependencies = [ { name = "flower" }, { name = "geoalchemy2" }, { name = "geojson-pydantic" }, + { name = "grpcio-tools" }, { name = "gunicorn" }, { name = "itsdangerous" }, { name = "orjson" }, @@ -2184,6 +2264,7 @@ requires-dist = [ { name = "flower", specifier = ">=2.0.1" }, { name = "geoalchemy2", specifier = ">=0.17.1" }, { name = "geojson-pydantic", specifier = ">=2.0.0" }, + { name = "grpcio-tools", specifier = ">=1.76.0" }, { name = "gunicorn", specifier = ">=23.0.0" }, { name = "itsdangerous", specifier = ">=2.2.0" }, { name = "orjson", specifier = ">=3.10.18" }, @@ -2360,6 +2441,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4b/b6/ce7c502a366f4835b1f9c057753f6989a92d3c70cbadb168193f5fb7499b/sentry_sdk-2.46.0-py2.py3-none-any.whl", hash = "sha256:4eeeb60198074dff8d066ea153fa6f241fef1668c10900ea53a4200abc8da9b1", size = 406266, upload-time = "2025-11-24T09:34:12.114Z" }, ] +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + [[package]] name = "shapely" version = "2.1.2"