From f045eb22b27f058d9de01f134d087ca28ddef78c Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 19 Dec 2025 17:48:16 +0300 Subject: [PATCH] feat(tgbot-back): Add functions for processing user authentication --- modules/bot/CMakeLists.txt | 4 ++ .../bot/back/include/AuthImpersonation.hpp | 35 ++++++++++ modules/bot/back/include/BotToServer.hpp | 4 ++ modules/bot/back/src/AuthImpersonation.cpp | 34 ++++++++++ modules/bot/back/src/BotToServer.cpp | 64 +++++++++++++------ modules/bot/front/src/handleNavigation.cpp | 2 +- 6 files changed, 122 insertions(+), 21 deletions(-) create mode 100644 modules/bot/back/include/AuthImpersonation.hpp create mode 100644 modules/bot/back/src/AuthImpersonation.cpp diff --git a/modules/bot/CMakeLists.txt b/modules/bot/CMakeLists.txt index 7a8948d..b6254c1 100644 --- a/modules/bot/CMakeLists.txt +++ b/modules/bot/CMakeLists.txt @@ -11,6 +11,9 @@ list(APPEND SOURCES ${SRC_BACK}) file(GLOB_RECURSE SRC_API "generated-client/src/*.cpp") list(APPEND SOURCES ${SRC_API}) +file(GLOB_RECURSE SRC_AUTH "generated-client-auth/src/*.cpp") +list(APPEND SOURCES ${SRC_AUTH}) + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") @@ -26,6 +29,7 @@ include_directories(/usr/local/include ${OPENSSL_INCLUDE_DIR} ${Boost_INCLUDE_DI include_directories(front/include/) include_directories(back/include) include_directories(generated-client/include) +include_directories(generated-client-auth/include) if (CURL_FOUND) include_directories(${CURL_INCLUDE_DIRS}) add_definitions(-DHAVE_CURL) diff --git a/modules/bot/back/include/AuthImpersonation.hpp b/modules/bot/back/include/AuthImpersonation.hpp new file mode 100644 index 0000000..67d25fe --- /dev/null +++ b/modules/bot/back/include/AuthImpersonation.hpp @@ -0,0 +1,35 @@ +// AuthImpersonationClient.hpp + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "AuthClient/ApiClient.h" +#include "AuthClient/ApiConfiguration.h" +#include "AuthClient/api/AuthApi.h" +#include "AuthClient/model/GetImpersonationToken_request.h" +#include "AuthClient/model/GetImpersonationToken_200_response.h" + + +namespace nyanimed { + +class AuthImpersonationClient { +public: + AuthImpersonationClient(); + + // Потокобезопасный вызов — не модифицирует состояние + pplx::task> + getImpersonationToken(int64_t userId) const; + +private: + std::string m_baseUrl; + std::string m_authToken; + std::shared_ptr m_apiClient; + std::shared_ptr m_authApi; +}; + +} // namespace nyanimed \ No newline at end of file diff --git a/modules/bot/back/include/BotToServer.hpp b/modules/bot/back/include/BotToServer.hpp index 7f10fb6..d4b132b 100644 --- a/modules/bot/back/include/BotToServer.hpp +++ b/modules/bot/back/include/BotToServer.hpp @@ -15,6 +15,8 @@ #include #include +#include "AuthImpersonation.hpp" + using namespace org::openapitools::client::api; class BotToServer { @@ -28,4 +30,6 @@ private: std::shared_ptr apiconfiguration; std::shared_ptr apiclient; std::shared_ptr api; + + nyanimed::AuthImpersonationClient authClient; }; \ No newline at end of file diff --git a/modules/bot/back/src/AuthImpersonation.cpp b/modules/bot/back/src/AuthImpersonation.cpp new file mode 100644 index 0000000..f111922 --- /dev/null +++ b/modules/bot/back/src/AuthImpersonation.cpp @@ -0,0 +1,34 @@ +#include "AuthImpersonation.hpp" + +nyanimed::AuthImpersonationClient::AuthImpersonationClient() { + const char* baseUrlEnv = std::getenv("NYANIMEDBAUTHURL"); + const char* tokenEnv = std::getenv("NYANIMEDBAUTHTOKEN"); + + if (!baseUrlEnv || std::string(baseUrlEnv).empty()) { + throw std::runtime_error("Missing required environment variable: NYANIMEDBAUTHURL"); + } + if (!tokenEnv || std::string(tokenEnv).empty()) { + throw std::runtime_error("Missing required environment variable: NYANIMEDBAUTHTOKEN"); + } + + m_baseUrl = baseUrlEnv; + m_authToken = tokenEnv; + + auto config = std::make_shared(); + config->setBaseUrl(utility::conversions::to_string_t(m_baseUrl)); + + m_apiClient = std::make_shared(config); + m_authApi = std::make_shared(m_apiClient); +} + +pplx::task> +nyanimed::AuthImpersonationClient::getImpersonationToken(int64_t userId) const { + auto request = std::make_shared(); + request->setUserId(userId); + //request->setExternalId(externalId); + + std::map headers; + headers[U("Authorization")] = U("Bearer ") + utility::conversions::to_string_t(m_authToken); + + return m_authApi->getImpersonationToken(request, headers); +} \ No newline at end of file diff --git a/modules/bot/back/src/BotToServer.cpp b/modules/bot/back/src/BotToServer.cpp index 8897ac4..e9d33ff 100644 --- a/modules/bot/back/src/BotToServer.cpp +++ b/modules/bot/back/src/BotToServer.cpp @@ -45,28 +45,52 @@ static BotStructs::Title mapUserTitleToBotTitle( } pplx::task> BotToServer::fetchUserTitlesAsync(const std::string& userId) { - utility::string_t userIdW = utility::conversions::to_string_t(userId); - int32_t limit = static_cast(BotConstants::DISP_TITLES_NUM); + // Шаг 1: Получаем impersonation-токен + auto impersonationTask = authClient.getImpersonationToken(std::stoi(userId)); - auto responseTask = api->getUserTitles( - userIdW, - boost::none, // cursor - boost::none, // sort - boost::none, // sortForward - boost::none, // word - boost::none, // status - boost::none, // watchStatus - boost::none, // rating - boost::none, // myRate - boost::none, // releaseYear - boost::none, // releaseSeason - limit, - boost::none // fields - ); - - return responseTask.then([=](pplx::task> task) { + // Шаг 2: После получения токена — делаем запрос getUserTitles с этим токеном + return impersonationTask.then([=](pplx::task> tokenTask) { try { - auto response = task.get(); + auto tokenResponse = tokenTask.get(); + if (!tokenResponse) { + throw std::runtime_error("Null response from getImpersonationToken"); + } + + utility::string_t accessToken = utility::conversions::to_string_t(tokenResponse->getAccessToken()); + + // Формируем заголовки с токеном + std::map customHeaders; + customHeaders[U("Cookie")] = U("access_token=") + accessToken; + + // Подготавливаем параметры запроса + utility::string_t userIdW = utility::conversions::to_string_t(userId); + int32_t limit = static_cast(BotConstants::DISP_TITLES_NUM); + + // Шаг 3: Выполняем getUserTitles с кастомными заголовками + return api->getUserTitles( + userIdW, + boost::none, // cursor + boost::none, // sort + boost::none, // sortForward + boost::none, // word + boost::none, // status + boost::none, // watchStatus + boost::none, // rating + boost::none, // myRate + boost::none, // releaseYear + boost::none, // releaseSeason + limit, + boost::none, // fields + customHeaders + ); + + } catch (const std::exception& e) { + std::cerr << "Error obtaining impersonation token: " << e.what() << std::endl; + throw; // Пробрасываем, чтобы цепочка task.then завершилась с ошибкой + } + }).then([=](pplx::task> responseTask) { + try { + auto response = responseTask.get(); if (!response) { throw std::runtime_error("Null response from getUserTitles"); } diff --git a/modules/bot/front/src/handleNavigation.cpp b/modules/bot/front/src/handleNavigation.cpp index b8af2ef..ded36b1 100644 --- a/modules/bot/front/src/handleNavigation.cpp +++ b/modules/bot/front/src/handleNavigation.cpp @@ -78,7 +78,7 @@ void BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query) { editMessage(chatId, messageId, showMainMenu()); return; case UserState::VIEWING_MY_TITLES: - server_.fetchUserTitlesAsync(std::to_string(2)) // ALARM: тестовое значение вместо userId + server_.fetchUserTitlesAsync(std::to_string(22)) // ALARM: тестовое значение вместо userId .then([this, chatId, messageId](pplx::task> t) { try { auto titles = t.get();