Older/Server/SharedState.cpp

158 lines
7.3 KiB
C++
Raw Normal View History

2023-07-21 16:17:01 +08:00
#include "SharedState.h"
#include "HttpSession.h"
#include "ServiceLogic.h"
#include "WeChatContext/CorporationContext.h"
#include "WebsocketSession.h"
2024-01-03 22:44:36 +08:00
#include "Database.h"
2023-07-21 16:17:01 +08:00
SharedState::SharedState(boost::asio::io_context &ioContext, std::string doc_root)
2023-12-31 01:13:26 +08:00
: m_ioContext(ioContext), m_router{std::make_shared<boost::urls::router<Handler>>()},
m_docRoot(std::move(doc_root)) {
2023-07-21 16:17:01 +08:00
2023-12-31 01:13:26 +08:00
m_router->insert("/{path*}",[this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
using namespace boost::beast;
boost::urls::url_view view(request.target());
auto target = view.path();
LOG(info) << target;
if (target.find("..") != boost::beast::string_view::npos) {
session.reply(ServiceLogic::badRequest(request, "Illegal request-target"));
return;
}
std::string path = ResponseUtility::pathCat(m_docRoot, target);
if (target.back() == '/') path.append("index.html");
if (std::filesystem::is_directory(path)) path.append("/index.html");
boost::beast::error_code ec;
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) {
std::ostringstream oss;
oss << "The resource '" << target << "' was not found.";
2024-01-02 22:48:00 +08:00
LOG(error) << oss.str();
2023-12-31 01:13:26 +08:00
session.errorReply(request, http::status::not_found, oss.str());
return;
} else if (ec) {
session.reply(ServiceLogic::serverError(request, ec.message()));
return;
}
auto const size = body.size();
http::response<http::file_body> res{std::piecewise_construct, std::make_tuple(std::move(body)),
std::make_tuple(http::status::ok, request.version())};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, ResponseUtility::mimeType(path));
res.content_length(size);
res.keep_alive(request.keep_alive());
session.reply(std::move(res));
2023-07-21 16:17:01 +08:00
});
m_router->insert("/wechat/{session*}",
2023-12-31 01:13:26 +08:00
[this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
2023-07-21 16:17:01 +08:00
ServiceLogic::onWechat(shared_from_this(), request,
[&session](auto &&response) { session.reply(std::move(response)); });
});
2023-12-31 01:13:26 +08:00
m_router->insert("/api/v1/tasklist",[this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
using namespace boost::beast;
2024-01-03 22:44:36 +08:00
auto database = Amass::Singleton<Database>::instance();
auto tasks = database->tasks();
2024-01-02 22:48:00 +08:00
http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()};
2023-12-31 01:13:26 +08:00
s.set(http::field::server, BOOST_BEAST_VERSION_STRING);
2024-01-02 22:48:00 +08:00
s.set(http::field::content_type, "application/json;charset=UTF-8");
2023-12-31 01:13:26 +08:00
s.keep_alive(request.keep_alive());
2024-01-03 22:44:36 +08:00
s.body() = boost::json::serialize(tasks);
2024-01-02 22:48:00 +08:00
s.prepare_payload();
session.reply(std::move(s));
});
2024-01-03 22:44:36 +08:00
m_router->insert("/api/v1/task/add", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
2024-01-02 22:48:00 +08:00
using namespace boost::beast;
2024-01-03 22:44:36 +08:00
LOG(info) << "add task: " << request.body();
auto database = Amass::Singleton<Database>::instance();
auto root = boost::json::parse(request.body());
bool ret = database->addTask(root.at("createTime").as_int64(), std::string(root.at("content").as_string()),
std::string(root.at("comment").as_string()));
2024-01-02 22:48:00 +08:00
boost::json::object reply;
2024-01-03 22:44:36 +08:00
reply["status"] = ret ? 0 : -1;
http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()};
s.set(http::field::server, BOOST_BEAST_VERSION_STRING);
s.set(http::field::content_type, "application/json;charset=UTF-8");
s.keep_alive(request.keep_alive());
s.body() = boost::json::serialize(reply);
s.prepare_payload();
session.reply(std::move(s));
});
2024-01-02 22:48:00 +08:00
2024-01-03 22:44:36 +08:00
m_router->insert("/api/v1/task/delete/{id}", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
using namespace boost::beast;
LOG(info) << "delete task: " << matches.at("id");
auto database = Amass::Singleton<Database>::instance();
auto status = database->removeTask(std::stoi(matches.at("id")) );
boost::json::object reply;
reply["status"] = status? 0 : -1;
2024-01-02 22:48:00 +08:00
http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()};
s.set(http::field::server, BOOST_BEAST_VERSION_STRING);
s.set(http::field::content_type, "application/json;charset=UTF-8");
s.keep_alive(request.keep_alive());
s.body() = boost::json::serialize(reply);
2023-12-31 01:13:26 +08:00
s.prepare_payload();
session.reply(std::move(s));
});
2023-07-21 16:17:01 +08:00
2023-12-31 01:13:26 +08:00
m_router->insert("/trigger-ci.hook", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
2023-07-21 16:17:01 +08:00
LOG(info) << "webhook: " << request;
session.reply(ServiceLogic::make_200<boost::beast::http::string_body>(request, "Main page\n", "text/html"));
});
2023-12-31 01:13:26 +08:00
m_router->insert("/notify", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
auto corp = Amass::Singleton<CorporationContext>::instance();
corp->notify(request);
session.reply(
ServiceLogic::make_200<boost::beast::http::string_body>(request, "notify successed.\n", "text/html"));
});
2023-07-21 16:17:01 +08:00
}
const SharedState::Handler *SharedState::find(boost::urls::segments_encoded_view path,
2023-12-31 01:13:26 +08:00
boost::urls::matches_base &matches) const noexcept {
2023-07-21 16:17:01 +08:00
return m_router->find(path, matches);
}
std::string_view SharedState::galleryRoot() const noexcept {
return m_galleryRoot;
}
void SharedState::setFileRoot(const std::string_view &root) {
if (m_fileRoot == root) return;
m_fileRoot = root;
}
void SharedState::join(WebSocketSession *session) {
std::lock_guard<std::mutex> lock(mutex_);
sessions_.insert(session);
}
void SharedState::leave(WebSocketSession *session) {
std::lock_guard<std::mutex> lock(mutex_);
sessions_.erase(session);
}
void SharedState::send(std::string message) {
// Put the message in a shared pointer so we can re-use it for each client
auto const ss = std::make_shared<std::string const>(std::move(message));
// Make a local list of all the weak pointers representing
// the sessions, so we can do the actual sending without
// holding the mutex:
std::vector<std::weak_ptr<WebSocketSession>> v;
{
std::lock_guard<std::mutex> lock(mutex_);
v.reserve(sessions_.size());
for (auto p : sessions_) v.emplace_back(p->weak_from_this());
}
// For each session in our local list, try to acquire a strong
// pointer. If successful, then send the message on that session.
for (auto const &wp : v)
if (auto sp = wp.lock()) sp->send(ss);
}