generated from Sithas/conan_template
318 lines
11 KiB
C++
318 lines
11 KiB
C++
#pragma once
|
|
|
|
#include <boost/url.hpp>
|
|
#include <mysqlx/xdevapi.h>
|
|
#include <mysqlx/common/api.h>
|
|
|
|
#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 "../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 Body, class Allocator, class ResponseType, class Send>
|
|
class RootExecutor
|
|
{
|
|
using IRouteExecutor = IExecutor<Body, Allocator, boost::beast::http::string_body>;
|
|
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 IRouteController = IController<Body, Allocator, boost::beast::http::string_body>;
|
|
using RouteController = Controller<Body, Allocator, boost::beast::http::string_body>;
|
|
using RoutesPathes = std::unordered_map<std::string, std::unique_ptr<IRouteController>>;
|
|
using Request = boost::beast::http::request<Body, boost::beast::http::basic_fields<Allocator>>;
|
|
using StringResponse = boost::beast::http::response<boost::beast::http::string_body>;
|
|
using EmptyResponse = boost::beast::http::response<boost::beast::http::empty_body>;
|
|
using FileResponse = boost::beast::http::response<boost::beast::http::file_body>;
|
|
|
|
private:
|
|
RoutesPathes routes_pathes_;
|
|
mysqlx::Session& session_;
|
|
const std::shared_ptr<IUserDAO>& user_dao_;
|
|
const std::shared_ptr<IAuthDAO>& auth_dao_;
|
|
const std::shared_ptr<IMedicationsDAO>& medications_dao_;
|
|
const std::shared_ptr<IUserTreatmentSchemeDAO>& user_treatment_scheme_dao_;
|
|
const std::shared_ptr<IDiariesDAO>& diaries_dao_;
|
|
|
|
public:
|
|
RootExecutor(
|
|
mysqlx::Session& session,
|
|
const std::shared_ptr<IUserDAO>& user_dao,
|
|
const std::shared_ptr<IAuthDAO>& auth_dao,
|
|
const std::shared_ptr<IMedicationsDAO>& medications_dao,
|
|
const std::shared_ptr<IUserTreatmentSchemeDAO>& user_treatment_scheme_dao,
|
|
const std::shared_ptr<IDiariesDAO>& 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<RouteController>(
|
|
typename RouteController::HTTPMethodsToExecutors{
|
|
{
|
|
boost::beast::http::verb::post, std::make_shared<RouteAuthRegistrationExecutor>(
|
|
session_,
|
|
user_dao_
|
|
)
|
|
}
|
|
}
|
|
);
|
|
|
|
routes_pathes_["/api/v1/Auth/Login"] = std::make_unique<RouteController>(
|
|
typename RouteController::HTTPMethodsToExecutors{
|
|
{
|
|
boost::beast::http::verb::post,
|
|
std::make_shared<RouteAuthLoginExecutor>(session_, user_dao_, auth_dao_)
|
|
}
|
|
}
|
|
);
|
|
|
|
routes_pathes_["/api/v1/Auth/Logout"] = std::make_unique<RouteController>(
|
|
typename RouteController::HTTPMethodsToExecutors{
|
|
{
|
|
boost::beast::http::verb::post,
|
|
std::make_shared<RouteAuthLogoutExecutor>(session_, auth_dao_)
|
|
}
|
|
}
|
|
);
|
|
|
|
routes_pathes_["/api/v1/User/Medications"] = std::make_unique<RouteController>(
|
|
typename RouteController::HTTPMethodsToExecutors{
|
|
{
|
|
boost::beast::http::verb::get,
|
|
std::make_shared<RouteGetUserMedicationsExecutor>(session_, auth_dao_, medications_dao_)
|
|
},
|
|
{
|
|
boost::beast::http::verb::post,
|
|
std::make_shared<RoutePostUserMedicationsExecutor>(session_, auth_dao_, medications_dao_)
|
|
},
|
|
}
|
|
);
|
|
|
|
routes_pathes_["/api/v1/UserTreatmentSchemes"] = std::make_unique<RouteController>(
|
|
typename RouteController::HTTPMethodsToExecutors{
|
|
{
|
|
boost::beast::http::verb::get,
|
|
std::make_shared<RouteGetUserTreatmentSchemeExecutor>(session_, auth_dao_,
|
|
user_treatment_scheme_dao_)
|
|
},
|
|
{
|
|
boost::beast::http::verb::post,
|
|
std::make_shared<RoutePostUserTreatmentSchemeExecutor>(session_, auth_dao_,
|
|
user_treatment_scheme_dao_)
|
|
}
|
|
}
|
|
);
|
|
|
|
routes_pathes_["/api/v1/Diaries"] = std::make_unique<RouteController>(
|
|
typename RouteController::HTTPMethodsToExecutors{
|
|
{
|
|
boost::beast::http::verb::get,
|
|
std::make_shared<RouteGetDiariesExecutor>(session_, auth_dao_,
|
|
diaries_dao_)
|
|
},
|
|
{
|
|
boost::beast::http::verb::post,
|
|
std::make_shared<RoutePostDiaryExecutor>(session_, auth_dao_,
|
|
diaries_dao_)
|
|
},
|
|
}
|
|
);
|
|
}
|
|
|
|
void operator ()(
|
|
boost::beast::string_view doc_root,
|
|
Request&& req,
|
|
Send&& send
|
|
)
|
|
{
|
|
namespace urls = boost::urls;
|
|
|
|
const std::string& route = req.target();
|
|
const bool is_match_route = routes_pathes_.count(route);
|
|
|
|
if (is_match_route)
|
|
{
|
|
std::optional<std::shared_ptr<IRouteExecutor>> maybe_executor_ptr = routes_pathes_
|
|
.at(route)
|
|
->FindExecutor(req.method());
|
|
|
|
if (maybe_executor_ptr.has_value())
|
|
{
|
|
IRouteExecutor& executor = *maybe_executor_ptr.value();
|
|
|
|
try
|
|
{
|
|
boost::beast::http::response<ResponseType> 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("/api/v1/Diaries/123").value();
|
|
// auto segs = parsed_view.segments();
|
|
// std::vector<std::string_view> parts;
|
|
//
|
|
// for (auto s : segs)
|
|
// parts.push_back(s);
|
|
//
|
|
// if (parts.size() == 4 &&
|
|
// parts[0] == "api" &&
|
|
// parts[2] == "Diaries")
|
|
// {
|
|
// std::string uuid = std::string(parts[3]);
|
|
// }
|
|
|
|
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;
|
|
}
|
|
};
|
|
}
|