diff --git a/database/interface.h b/database/interface.h index 5173234..9c5c0c4 100644 --- a/database/interface.h +++ b/database/interface.h @@ -48,6 +48,7 @@ public: virtual Session findSession(const std::string& accessToken) = 0; virtual std::vector listAssets(uint32_t owner) = 0; virtual Asset addAsset(const Asset& asset) = 0; + virtual void updateAsset(const Asset& asset) = 0; virtual bool deleteAsset(uint32_t assetId, uint32_t actorId) = 0; virtual std::vector listUsedCurrencies(uint32_t owner) = 0; diff --git a/database/mysql/mysql.cpp b/database/mysql/mysql.cpp index be6695f..2ae8770 100644 --- a/database/mysql/mysql.cpp +++ b/database/mysql/mysql.cpp @@ -25,8 +25,10 @@ constexpr const char* selectSession = "SELECT id, owner, access, renew FROM sess constexpr const char* selectAssets = "SELECT id, owner, currency, title, icon, color, archived FROM assets where owner = ?"; constexpr const char* insertAsset = "INSERT INTO assets (`owner`, `currency`, `title`, `icon`, `color`, `archived`, `type`)" " VALUES (?, ?, ?, ?, ?, ?, 1)"; +constexpr const char* updateAssetQuery = "UPDATE assets SET `owner` = ?, `currency` = ?, `title` = ?, `icon` = ?, `color` = ?, `archived` = ?" + " WHERE `id` = ?"; constexpr const char* removeAsset = "DELETE FROM assets where `id` = ? AND `owner` = ?"; -constexpr const char* selectUsedCurrencies = "SELECT c.id, c.code, c.title, c.manual, c.icon FROM currencies c" +constexpr const char* selectUsedCurrencies = "SELECT DISTINCT c.id, c.code, c.title, c.manual, c.icon FROM currencies c" " JOIN assets a ON c.id = a.currency" " WHERE a.owner = ?"; @@ -366,6 +368,21 @@ DB::Asset DB::MySQL::addAsset(const Asset& asset) { return result; } +void DB::MySQL::updateAsset(const Asset& asset) { + MYSQL* con = &connection; + Asset result = asset; + + Statement update(con, updateAssetQuery); + update.bind(&result.owner, MYSQL_TYPE_LONG, true); + update.bind(&result.currency, MYSQL_TYPE_LONG, true); + update.bind(result.title.data(), MYSQL_TYPE_STRING); + update.bind(result.icon.data(), MYSQL_TYPE_STRING); + update.bind(&result.color, MYSQL_TYPE_LONG, true); + update.bind(&result.archived, MYSQL_TYPE_TINY); + update.bind(&result.id, MYSQL_TYPE_LONG, true); + update.execute(); +} + bool DB::MySQL::deleteAsset(uint32_t assetId, uint32_t actorId) { Statement del(&connection, removeAsset); del.bind(&assetId, MYSQL_TYPE_LONG, true); diff --git a/database/mysql/mysql.h b/database/mysql/mysql.h index 81bdcf5..bd703bb 100644 --- a/database/mysql/mysql.h +++ b/database/mysql/mysql.h @@ -36,6 +36,7 @@ public: Session findSession (const std::string& accessToken) override; std::vector listAssets (uint32_t owner) override; Asset addAsset (const Asset& asset) override; + void updateAsset (const Asset& asset) override; bool deleteAsset(uint32_t assetId, uint32_t actorId) override; std::vector listUsedCurrencies(uint32_t owner) override; diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index a660a29..587a51c 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -11,6 +11,7 @@ set(HEADERS assets.h addasset.h deleteasset.h + updateasset.h currencies.h ) @@ -24,6 +25,7 @@ set(SOURCES assets.cpp addasset.cpp deleteasset.cpp + updateasset.cpp currencies.cpp ) diff --git a/handler/listassets.h b/handler/listassets.h deleted file mode 100644 index e69de29..0000000 diff --git a/handler/mycurrencies.cpp b/handler/mycurrencies.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/handler/mycurrencies.h b/handler/mycurrencies.h deleted file mode 100644 index e69de29..0000000 diff --git a/handler/updateasset.cpp b/handler/updateasset.cpp new file mode 100644 index 0000000..af06610 --- /dev/null +++ b/handler/updateasset.cpp @@ -0,0 +1,84 @@ +//SPDX-FileCopyrightText: 2024 Yury Gubich +//SPDX-License-Identifier: GPL-3.0-or-later + +#include "updateasset.h" + +#include + +#include "server/server.h" +#include "server/session.h" +#include "database/exceptions.h" + +Handler::UpdateAsset::UpdateAsset (const std::shared_ptr& server): + Handler("updateAsset", Request::Method::post), + server(server) +{} + +void Handler::UpdateAsset::handle (Request& request) { + std::string access = request.getAuthorizationToken(); + if (access.empty()) + return error(request, Response::Status::unauthorized); + + if (access.size() != 32) + return error(request, Response::Status::badRequest); + + std::shared_ptr srv = server.lock(); + if (!srv) + return error(request, Response::Status::internalError); + + std::map form = request.getForm(); + std::map::const_iterator itr = form.find("id"); + if (itr == form.end()) + return error(request, Response::Status::badRequest); + + DB::Asset asset; + asset.id = std::stoul(itr->second); + + itr = form.find("currency"); + if (itr == form.end()) + return error(request, Response::Status::badRequest); + + asset.currency = std::stoul(itr->second); + //TODO validate the currency + + itr = form.find("title"); + if (itr == form.end()) + return error(request, Response::Status::badRequest); + + asset.title = itr->second; + + itr = form.find("icon"); + if (itr == form.end()) + return error(request, Response::Status::badRequest); + + asset.icon = itr->second; + + try { + itr = form.find("color"); + if (itr != form.end()) + asset.color = std::stoul(itr->second); + } catch (const std::exception& e) { + std::cerr << "Insignificant error parsing color during asset addition: " << e.what() << std::endl; + } + + try { + Session& session = srv->getSession(access); + + asset.owner = session.owner; + srv->getDatabase()->updateAsset(asset); + + Response& res = request.createResponse(Response::Status::ok); + res.send(); + + session.assetChanged(asset); + + } catch (const DB::NoSession& e) { + return error(request, Response::Status::unauthorized); + } catch (const std::exception& e) { + std::cerr << "Exception on " << path << ":\n\t" << e.what() << std::endl; + return error(request, Response::Status::internalError); + } catch (...) { + std::cerr << "Unknown exception on " << path << std::endl; + return error(request, Response::Status::internalError); + } +} diff --git a/handler/updateasset.h b/handler/updateasset.h new file mode 100644 index 0000000..33ea6e4 --- /dev/null +++ b/handler/updateasset.h @@ -0,0 +1,20 @@ +//SPDX-FileCopyrightText: 2024 Yury Gubich +//SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "handler.h" + +class Server; +namespace Handler { +class UpdateAsset : public Handler { +public: + UpdateAsset (const std::shared_ptr& server); + virtual void handle (Request& request) override; + +private: + std::weak_ptr server; +}; +} diff --git a/run.sh.in b/run.sh.in index d6eecee..53b7203 100644 --- a/run.sh.in +++ b/run.sh.in @@ -25,4 +25,4 @@ fi start_service "mariadb" start_service "httpd" -./@PROJECT_NAME@ +$(dirname "$0")/@PROJECT_NAME@ diff --git a/server/server.cpp b/server/server.cpp index 5964e5e..ae8a962 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -16,6 +16,7 @@ #include "handler/addasset.h" #include "handler/deleteasset.h" #include "handler/currencies.h" +#include "handler/updateasset.h" #include "taskmanager/route.h" @@ -77,6 +78,7 @@ void Server::run (int socketDescriptor) { router->addRoute(std::make_unique(srv)); router->addRoute(std::make_unique(srv)); router->addRoute(std::make_unique(srv)); + router->addRoute(std::make_unique(srv)); taskManager->start(); scheduler->start(); diff --git a/server/session.cpp b/server/session.cpp index 3e29d14..c9c38e5 100644 --- a/server/session.cpp +++ b/server/session.cpp @@ -110,17 +110,53 @@ void Session::assetAdded (const DB::Asset& asset) { checkUpdates(); } +void Session::assetChanged (const DB::Asset& asset) { + std::lock_guard lock(mtx); + std::map& assets = cache["assets"]; + auto itr = assets.find("changed"); + if (itr == assets.end()) + itr = assets.emplace("changed", nlohmann::json::array()).first; + + removeByID(itr->second, asset.id); + itr->second.push_back(asset.toJSON()); + + checkUpdates(); +} + void Session::assetRemoved (unsigned int assetId) { std::lock_guard lock(mtx); std::map& assets = cache["assets"]; - auto addedItr = assets.find("removed"); - if (addedItr == assets.end()) - addedItr = assets.emplace("removed", nlohmann::json::array()).first; + auto itr = assets.find("added"); + if (itr != assets.end()) + removeByID(itr->second, assetId); + else { + itr = assets.find("removed"); + if (itr == assets.end()) + itr = assets.emplace("removed", nlohmann::json::array()).first; + + itr->second.push_back(assetId); + } + + itr = assets.find("changed"); + if (itr != assets.end()) + removeByID(itr->second, assetId); - addedItr->second.push_back(assetId); checkUpdates(); } +void Session::removeByID(nlohmann::json& array, unsigned int id) { + array.erase( + std::remove_if( + array.begin(), + array.end(), + [id](const nlohmann::json& item) { + return item["id"].get() == id; + } + ), + array.end() + ); +} + void Session::checkUpdates () { std::shared_ptr sch = scheduler.lock(); if (polling) { diff --git a/server/session.h b/server/session.h index 75c55ca..b82ad3a 100644 --- a/server/session.h +++ b/server/session.h @@ -38,6 +38,7 @@ public: const unsigned int owner; void assetAdded (const DB::Asset& asset); + void assetChanged (const DB::Asset& asset); void assetRemoved (unsigned int assetId); private: @@ -45,6 +46,8 @@ private: void sendUpdates (std::unique_ptr request); void checkUpdates (); + void static removeByID (nlohmann::json& array, unsigned int id); + private: std::weak_ptr scheduler; std::string access;