#pragma once #include "adminive/collection.hpp" #include "adminive/http.hpp" #include "httplib.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace adminive { template void write_http_json(httplib::Response& response, const Json& value) { response.set_content(dump_json(value, 2), "application/json; charset=utf-8"); } template void write_http_response(httplib::Response& response, Http_Response value) { response.status = value.status; write_http_json(response, value.body); } template void write_http_error(httplib::Response& response, int status, const Update_Result& update) { write_http_response(response, make_http_error(status, update)); } template requires Writable_Adapted_Object && std::copy_constructible> class Http_Resource { public: using Service = Resource_Service; using Transaction = typename Service::Transaction; Http_Resource(T& value, std::string path, Transaction transaction = {}, Lock* shared_lock = nullptr) : service_(std::make_shared(value, std::move(path), std::move(transaction), shared_lock)) {} template explicit Http_Resource(Managed_Value, Policy>& value, std::string path, Transaction transaction = {}) : service_(std::make_shared(value, std::move(path), std::move(transaction))) {} template requires (!std::is_const_v) && std::same_as::value_type, std::remove_cvref_t> explicit Http_Resource(Managed_Field value, std::string path, Transaction transaction = {}) : service_(std::make_shared(value, std::move(path), std::move(transaction))) {} Json amis_schema() const { return service_->amis_schema(); } void bind(httplib::Server& server) const { const auto service = service_; server.Get(service->path() + "/descriptor", [service](const httplib::Request&, httplib::Response& response) { write_http_response(response, service->descriptor_response()); }); server.Get(service->path() + "/data", [service](const httplib::Request&, httplib::Response& response) { write_http_response(response, service->data_response()); }); server.Get(service->path() + "/amis", [service](const httplib::Request&, httplib::Response& response) { write_http_response(response, service->amis_response()); }); server.Post(service->path() + "/data", [service](const httplib::Request& request, httplib::Response& response) { write_http_response(response, service->update_response(request.body)); }); } private: std::shared_ptr service_; }; template requires std::default_initializable && std::copy_constructible && std::assignable_from class Http_Collection_Resource { public: using Service = Collection_Service; explicit Http_Collection_Resource(std::string path, std::vector initial = {}) : service_(std::make_shared(std::move(path), std::move(initial))) {} template void register_overview_status(std::string api, std::uint64_t interval = 2000) { service_->template register_overview_status(std::move(api), interval); } template void register_status(std::uint64_t interval = 2000) { service_->template register_status(interval); } std::size_t size() const { return service_->size(); } Json items_json() const { return service_->items_json(); } Json amis_schema() const { return service_->amis_schema(); } void bind(httplib::Server& server) const { const auto service = service_; server.Get(service->path() + "/descriptor", [service](const httplib::Request&, httplib::Response& response) { write_http_response(response, service->descriptor_response()); }); server.Get(service->path() + "/amis", [service](const httplib::Request&, httplib::Response& response) { write_http_response(response, service->amis_response()); }); if(service->has_status()) { server.Get(service->path() + "/status/descriptor", [service](const httplib::Request&, httplib::Response& response) { write_http_response(response, service->status_descriptor_response()); }); server.Get(status_pattern(service->path()), [service](const httplib::Request& request, httplib::Response& response) { write_http_response(response, service->status_response(read_route_id(request))); }); } server.Get(service->path(), [service](const httplib::Request& request, httplib::Response& response) { Collection_Query query; query.page = read_size_parameter(request, "page", 1); query.per_page = read_size_parameter(request, "perPage", 20); if(request.has_param("orderBy")) { query.order_by = request.get_param_value("orderBy"); query.order_dir = request.has_param("orderDir") ? request.get_param_value("orderDir") : "asc"; } write_http_response(response, service->list_response(std::move(query))); }); server.Get(item_pattern(service->path()), [service](const httplib::Request& request, httplib::Response& response) { write_http_response(response, service->item_response(read_route_id(request))); }); server.Post(service->path(), [service](const httplib::Request& request, httplib::Response& response) { write_http_response(response, service->create_response(request.body)); }); if(service->user_reorderable()) { server.Post(service->path() + "/order", [service](const httplib::Request& request, httplib::Response& response) { write_http_response(response, service->reorder_response(request.body)); }); } server.Put(item_pattern(service->path()), [service](const httplib::Request& request, httplib::Response& response) { write_http_response(response, service->update_response(read_route_id(request), request.body)); }); server.Patch(item_pattern(service->path()), [service](const httplib::Request& request, httplib::Response& response) { write_http_response(response, service->update_response(read_route_id(request), request.body)); }); server.Delete(item_pattern(service->path()), [service](const httplib::Request& request, httplib::Response& response) { write_http_response(response, service->delete_response(read_route_id(request))); }); } private: static std::size_t read_size_parameter(const httplib::Request& request, const char* name, std::size_t fallback) { if(!request.has_param(name)) { return fallback; } const auto text = request.get_param_value(name); std::size_t value{}; const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), value); if(error != std::errc{} || end != text.data() + text.size() || value == 0) { return fallback; } return value; } static std::uint64_t read_route_id(const httplib::Request& request) { const std::string text = request.matches[1].str(); std::uint64_t id{}; const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), id); if(error != std::errc{} || end != text.data() + text.size()) { return 0; } return id; } static std::string item_pattern(const std::string& path) { return path + R"(/(\d+))"; } static std::string status_pattern(const std::string& path) { return path + R"(/(\d+)/status)"; } std::shared_ptr service_; }; }