// // Created by s-Kaonnull on 25.05.2021. // #include "api.hpp" #include "tables.hpp" #include #include #include #include #include #include #include using json = nlohmann::json; Api::Api(soci::session& sql, httplib::Server& server) : sql(sql) { RegisterServerHandles(server); } void Api::RegisterServerHandles(httplib::Server& server) { spdlog::info("Register api endpoints..."); server.Post("/api/add", std::bind_front(&Api::add, this)); server.Get("/api/files", std::bind_front(&Api::files, this)); server.Get("/api/file/(\\d+)", std::bind_front(&Api::file, this)); server.Options("/(.*)", [this](httplib::Request const& rq, httplib::Response& rp) {}); server.set_pre_routing_handler([this](httplib::Request const& rq, httplib::Response& rp) { this->set_cross_headers(rp); spdlog::info("Request {}", rq.method); return httplib::Server::HandlerResponse::Unhandled; }); } void Api::add(const httplib::Request& rq, httplib::Response& rs) { try { auto data = json::parse(rq.body); File f; f.url = data["url"]; f.status = FileStatus::PENDING; sql << "INSERT INTO Files (url, status) values(:url, :status)", soci::use(f.url, "url"), soci::use(f.status, "status"); long long int tmp; sql.get_last_insert_id("Files", tmp); f.id = tmp; json jr; jr["status"] = "Ok"; jr["id"] = f.id; spdlog::info("New Task: {}", f); rs.set_content(jr.dump(), "application/json"); } catch (json::exception const& e) { spdlog::error("Api Error: {}", e.what()); rs.status = 400; rs.set_content(std::format("Json Error: {}", e.what()), "text/plain"); } } void Api::files(httplib::Request const& rq, httplib::Response& rs) { try { json res; soci::rowset data = (sql.prepare << "SELECT * FROM Files ORDER BY id DESC"); for (auto const& el : data) { json current; current["id"] = el.id; current["status"] = magic_enum::enum_name(el.status); current["status_id"] = static_cast(el.status); current["source"] = el.url; current["name"] = fmt::format("File {}", el.id); current["file_name"] = "Unknown"; if (el.status == FileStatus::COMPLETE && false) { std::string filename; soci::rowset ret = (sql.prepare << "SELECT path From Downloads WHERE id=:id", soci::use(el.id, "id")); if (ret.begin() != ret.end()) { std::filesystem::path p(*ret.begin()); current["file_name"] = p.filename(); } } res["queue"].push_back(current); } rs.set_content(res.dump(), "application/json"); } catch (std::exception const& e) { spdlog::error(e.what()); throw e; } } void Api::file(const httplib::Request& rq, httplib::Response& rs) { if (!rq.matches[1].matched) { rs.status = 404; rs.set_content("Parameter error", "text/plain"); return; } auto file = rq.matches[1]; spdlog::info("File Request: {}", file); try { Download dw; sql << fmt::format("SELECT * FROM Downloads WHERE id={};", file), soci::into(dw); std::ifstream fs(dw.path, std::ios_base::binary); fs.seekg(0, std::ios_base::end); auto size = fs.tellg(); fs.seekg(0); rs.body.resize(static_cast(size)); fs.read(&rs.body[0], static_cast(size)); rs.set_header("Content-Disposition", fmt::format("attachment; filename={};", dw.path)); if (auto [match, type] = ctre::match(dw.path); match) { spdlog::debug(type); rs.set_header("content-type", fmt::format("application/{}", type.to_view())); } } catch (std::exception const& e) { spdlog::error(e.what()); rs.status = 404; rs.set_content("File Not Found", "text/plain"); } } void Api::set_cross_headers(httplib::Response& rs) { rs.set_header("Access-Control-Allow-Origin", "*"); rs.set_header("Access-Control-Allow-Methods", "GET, POST, PUT"); rs.set_header("Access-Control-Allow-Headers", "Content-Type"); }