#pragma once #include #include #include #include "IExecutor.h" #include "IController.h" #include "Controller.h" #include "AuthRegistrationExecutor.h" #include "AuthLoginExecutor.h" #include "AuthLogoutExecutor.h" #include "GetUserMedicationsExecutor.h" #include "GetUserTreatmentSchemeExecutor.h" #include "PostUserTreatmentSchemeExecutor.h" #include "PostUserMedicationsExecutor.h" #include "GetDiariesExecutor.h" #include "PostDiaryExecutor.h" #include "PutDiaryExecutor.h" #include "DeleteDiaryExecutor.h" #include "../DAO/IUserDAO.h" #include "../DAO/IAuthDAO.h" #include "../DAO/IDiariesDAO.h" #include "../DAO/IMedicationsDAO.h" #include "../DAO/IUserTreatmentSchemesDAO.h" #include "./../helpers/helpers.h" #include "./../exceptions/session_exception.h" namespace uad { template class RootExecutor { using IRouteExecutor = IExecutor; using RouteAuthRegistrationExecutor = AuthRegistrationExecutor< Body, Allocator, boost::beast::http::string_body>; using RouteAuthLoginExecutor = AuthLoginExecutor< Body, Allocator, boost::beast::http::string_body>; using RouteAuthLogoutExecutor = AuthLogoutExecutor< Body, Allocator, boost::beast::http::string_body>; using RouteGetUserMedicationsExecutor = GetUserMedicationsExecutor< Body, Allocator, boost::beast::http::string_body>; using RoutePostUserMedicationsExecutor = PostUserMedicationsExecutor< Body, Allocator, boost::beast::http::string_body>; using RouteGetUserTreatmentSchemeExecutor = GetUserTreatmentSchemeExecutor< Body, Allocator, boost::beast::http::string_body>; using RoutePostUserTreatmentSchemeExecutor = PostUserTreatmentSchemeExecutor< Body, Allocator, boost::beast::http::string_body>; using RouteGetDiariesExecutor = GetDiariesExecutor< Body, Allocator, boost::beast::http::string_body>; using RoutePostDiaryExecutor = PostDiaryExecutor< Body, Allocator, boost::beast::http::string_body>; using RoutePutDiaryExecutor = PutDiaryExecutor< Body, Allocator, boost::beast::http::string_body>; using RouteDeleteDiaryExecutor = DeleteDiaryExecutor< Body, Allocator, boost::beast::http::string_body>; using IRouteController = IController; using RouteController = Controller; using RoutesPathes = std::unordered_map>; using Request = boost::beast::http::request>; using StringResponse = boost::beast::http::response; using EmptyResponse = boost::beast::http::response; using FileResponse = boost::beast::http::response; private: RoutesPathes routes_pathes_; mysqlx::Session& session_; const std::shared_ptr& user_dao_; const std::shared_ptr& auth_dao_; const std::shared_ptr& medications_dao_; const std::shared_ptr& user_treatment_scheme_dao_; const std::shared_ptr& diaries_dao_; public: RootExecutor( mysqlx::Session& session, const std::shared_ptr& user_dao, const std::shared_ptr& auth_dao, const std::shared_ptr& medications_dao, const std::shared_ptr& user_treatment_scheme_dao, const std::shared_ptr& diaries_dao ) : session_(session), user_dao_(user_dao), auth_dao_(auth_dao), medications_dao_(medications_dao), user_treatment_scheme_dao_(user_treatment_scheme_dao), diaries_dao_(diaries_dao) { routes_pathes_["/api/v1/Auth/Register"] = std::make_unique( typename RouteController::HTTPMethodsToExecutors{ { boost::beast::http::verb::post, std::make_shared( session_, user_dao_ ) } } ); routes_pathes_["/api/v1/Auth/Login"] = std::make_unique( typename RouteController::HTTPMethodsToExecutors{ { boost::beast::http::verb::post, std::make_shared(session_, user_dao_, auth_dao_) } } ); routes_pathes_["/api/v1/Auth/Logout"] = std::make_unique( typename RouteController::HTTPMethodsToExecutors{ { boost::beast::http::verb::post, std::make_shared(session_, auth_dao_) } } ); routes_pathes_["/api/v1/User/Medications"] = std::make_unique( typename RouteController::HTTPMethodsToExecutors{ { boost::beast::http::verb::get, std::make_shared(session_, auth_dao_, medications_dao_) }, { boost::beast::http::verb::post, std::make_shared(session_, auth_dao_, medications_dao_) }, } ); routes_pathes_["/api/v1/UserTreatmentSchemes"] = std::make_unique( typename RouteController::HTTPMethodsToExecutors{ { boost::beast::http::verb::get, std::make_shared(session_, auth_dao_, user_treatment_scheme_dao_) }, { boost::beast::http::verb::post, std::make_shared(session_, auth_dao_, user_treatment_scheme_dao_) } } ); routes_pathes_["/api/v1/Diaries"] = std::make_unique( typename RouteController::HTTPMethodsToExecutors{ { boost::beast::http::verb::get, std::make_shared(session_, auth_dao_, diaries_dao_) }, { boost::beast::http::verb::post, std::make_shared(session_, auth_dao_, diaries_dao_) }, } ); } void operator ()( boost::beast::string_view doc_root, Request&& req, Send&& send ) { namespace urls = boost::urls; const bool is_match_route = routes_pathes_.count(req.target()); if (is_match_route) { std::optional> maybe_executor_ptr = routes_pathes_ .at(req.target()) ->FindExecutor(req.method()); if (maybe_executor_ptr.has_value()) { IRouteExecutor& executor = *maybe_executor_ptr.value(); try { boost::beast::http::response res = executor(std::move(req)); return send(std::move(res)); } catch (const session_exception& e) { return send(SendSessionExceptionError(std::move(req), e)); } } } urls::url_view parsed_view = urls::parse_uri_reference(req.target()).value(); auto segs = parsed_view.segments(); std::vector parts; for (auto s : segs) parts.push_back(s); if (parts.size() == 4 && parts[0] == "api" && parts[2] == "Diaries") { try { std::string uuid = std::string(parts[3]); if (req.method() == boost::beast::http::verb::put) { return send(RoutePutDiaryExecutor( session_, auth_dao_, diaries_dao_ )(std::move(req), uuid)); } if (req.method() == boost::beast::http::verb::delete_) { return send(RouteDeleteDiaryExecutor( session_, auth_dao_, diaries_dao_ )(std::move(req), uuid)); } } catch (const session_exception& e) { return send(SendSessionExceptionError(std::move(req), e)); } } if (req.method() != boost::beast::http::verb::get && req.method() != boost::beast::http::verb::head) return send(SendBadRequest(std::move(req), "Unknown boost::beast::HTTP-method")); if (req.target().empty() || req.target()[0] != '/' || req.target().find("..") != boost::beast::string_view::npos) return send(SendBadRequest(std::move(req), "Illegal request-target")); std::string path = PathCat(doc_root, req.target()); if (req.target().back() == '/') path.append("index.html"); boost::beast::error_code ec; boost::beast::http::file_body::value_type body; body.open(path.c_str(), boost::beast::file_mode::scan, ec); if (ec == boost::beast::errc::no_such_file_or_directory) return send(SendNotFound(std::move(req), req.target())); if (ec) return send(SendServerError(std::move(req), ec.message())); auto const size = body.size(); if (req.method() == boost::beast::http::verb::head) { EmptyResponse res{ boost::beast::http::status::ok, req.version() }; res.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); res.set(boost::beast::http::field::content_type, MimeType(path)); res.content_length(size); res.keep_alive(req.keep_alive()); return send(std::move(res)); } FileResponse res{ std::piecewise_construct, std::make_tuple(std::move(body)), std::make_tuple(boost::beast::http::status::ok, req.version()) }; res.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); res.set(boost::beast::http::field::content_type, MimeType(path)); res.content_length(size); res.keep_alive(req.keep_alive()); return send(std::move(res)); } private: StringResponse SendBadRequest( Request&& req, boost::beast::string_view why ) { StringResponse res{ boost::beast::http::status::bad_request, req.version() }; res.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); res.set(boost::beast::http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = std::string(why); res.prepare_payload(); return res; } StringResponse SendNotFound( Request&& req, boost::beast::string_view target ) { StringResponse res{ boost::beast::http::status::not_found, req.version() }; res.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); res.set(boost::beast::http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = "The resource '" + std::string(target) + "' was not found."; res.prepare_payload(); return res; } StringResponse SendServerError( Request&& req, boost::beast::string_view what ) { StringResponse res{ boost::beast::http::status::internal_server_error, req.version() }; res.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); res.set(boost::beast::http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = "An error occurred: '" + std::string(what) + "'"; res.prepare_payload(); return res; } StringResponse SendSessionExceptionError(Request&& req, const session_exception& e) { StringResponse res{e.code, req.version()}; boost::json::value response_body; response_body.emplace_object(); response_body.as_object().emplace("Result", e.what()); res.body() = serialize(response_body); res.set(boost::beast::http::field::content_type, "application/json"); res.content_length(res.body().size()); return res; } }; }