From 4db83fb129908ffcaf75b5aa9bef1fd3ee2f3dab Mon Sep 17 00:00:00 2001 From: David Baque Date: Sun, 10 Mar 2024 21:19:59 +0100 Subject: [PATCH 01/48] task #53: create auth controller --- .vscode/c_cpp_properties.json | 16 ++++++++++++++++ .vscode/settings.json | 6 ++++++ src/controllers/auth_controller.cpp | 9 +++++++++ src/controllers/auth_controller.h | 16 ++++++++++++++++ src/routes/auth_routes.cpp | 4 +--- src/routes/auth_routes.h | 2 ++ 6 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json create mode 100644 src/controllers/auth_controller.cpp create mode 100644 src/controllers/auth_controller.h diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..4039bef --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "c17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d0b5680 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "cmake.configureOnOpen": true, + "files.associations": { + "string": "cpp" + } +} \ No newline at end of file diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp new file mode 100644 index 0000000..fd880a5 --- /dev/null +++ b/src/controllers/auth_controller.cpp @@ -0,0 +1,9 @@ +#include "auth_controller.h" + +void UserController::create_user(pqxx::connection& db, const crow::request& req, crow::response& res) { + res.code = 200; + crow::json::wvalue x({{"message", "Hello, World!"}}); + x["message2"] = "Hello, World.. Again!"; + res.write(x.dump()); + res.end(); +} \ No newline at end of file diff --git a/src/controllers/auth_controller.h b/src/controllers/auth_controller.h new file mode 100644 index 0000000..de4699b --- /dev/null +++ b/src/controllers/auth_controller.h @@ -0,0 +1,16 @@ +#ifndef USER_CONTROLLER_H +#define USER_CONTROLLER_H + +#include +#include +#include + +#include "crow.h" + +class UserController { + public: + static std::string index(pqxx::connection& db); + static void create_user(pqxx::connection& db, const crow::request& req, crow::response& res); +}; + +#endif \ No newline at end of file diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index 0af3027..e7b7b0b 100644 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -3,8 +3,6 @@ void initialize_auth_routes(crow::SimpleApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/auth/register") ([&db](const crow::request& req, crow::response& res) { - res.code = 200; - res.body = "Register"; - res.end(); + UserController::create_user(db, req, res); }); } diff --git a/src/routes/auth_routes.h b/src/routes/auth_routes.h index 5f474be..e04fcb4 100644 --- a/src/routes/auth_routes.h +++ b/src/routes/auth_routes.h @@ -5,6 +5,8 @@ #include "crow.h" +#include "../controllers/auth_controller.h" + void initialize_auth_routes(crow::SimpleApp& app, pqxx::connection& db); #endif From 8d2080cef21efabbce52dce19c0e7742625a8963 Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Sun, 10 Mar 2024 21:29:09 +0100 Subject: [PATCH 02/48] fix: minor fixes --- .vscode/c_cpp_properties.json | 16 ---------------- .vscode/settings.json | 6 ------ README.md | 1 + src/routes/auth_routes.cpp | 3 +-- 4 files changed, 2 insertions(+), 24 deletions(-) delete mode 100644 .vscode/c_cpp_properties.json delete mode 100644 .vscode/settings.json create mode 100644 README.md diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 4039bef..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**" - ], - "defines": [], - "compilerPath": "/usr/bin/gcc", - "cStandard": "c17", - "cppStandard": "gnu++17", - "intelliSenseMode": "linux-gcc-x64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index d0b5680..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cmake.configureOnOpen": true, - "files.associations": { - "string": "cpp" - } -} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..781894a --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Backend diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index e7b7b0b..b964fd5 100644 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -1,8 +1,7 @@ #include "auth_routes.h" void initialize_auth_routes(crow::SimpleApp& app, pqxx::connection& db) { - CROW_ROUTE(app, "/api/auth/register") - ([&db](const crow::request& req, crow::response& res) { + CROW_ROUTE(app, "/api/auth/register").methods("POST"_method)([&db](const crow::request& req, crow::response& res) { UserController::create_user(db, req, res); }); } From 3fa09e9b44e49167ba06ecee2d8c4f74ff75e0f0 Mon Sep 17 00:00:00 2001 From: Voooigt Date: Sun, 10 Mar 2024 22:53:54 +0100 Subject: [PATCH 03/48] task #54: create user model --- src/models/user_model.cpp | 22 ++++++++++++++++++++++ src/models/user_model.h | 25 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/models/user_model.cpp create mode 100644 src/models/user_model.h diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp new file mode 100644 index 0000000..5e3ac9d --- /dev/null +++ b/src/models/user_model.cpp @@ -0,0 +1,22 @@ +#include "user_model.h" + +TestModel::TestModel(std::string id) : _id(id) {} + +std::string TestModel::getId() { + return this._id; +} + +UserModel UserModel::NewUser(std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type) { + pqxx::work txn(db); + + txn.exec(" + INSERT INTO users (password, email, username, image_url, balance, type) + VALUES $1, $2, $3, $4, $5, $6)", + password, email, username, image_url, balance, type); + + pqxx::result result = txn.exec("SELECT id FROM users WHERE email = $1 ", email); + + std::string res = result[0][0].as(); + txn.commit(); + return TestModel(res); +} \ No newline at end of file diff --git a/src/models/user_model.h b/src/models/user_model.h new file mode 100644 index 0000000..c2e0d4e --- /dev/null +++ b/src/models/user_model.h @@ -0,0 +1,25 @@ +#ifndef TEST_MODEL_H +#define TEST_MODEL_H + +#include +#include + +class UserModel { + private: + std::string _id; + std::string _password; + std::string _mail; + std::string _username; + std::string _image_url; + int _balance; + std::string _type; + + public: + UserModel(std::string id); + + std::string getId(); + + static UserModel NewUser(std::string password, std::string email, std::string username, std::string image_url, std::string type); +}; + +#endif \ No newline at end of file From e92ef38a54ed6241eaee83cb1879f24ba86fba8a Mon Sep 17 00:00:00 2001 From: Joaquin Touron <43842304+JayexDesigns@users.noreply.github.com> Date: Sun, 10 Mar 2024 23:04:48 +0100 Subject: [PATCH 04/48] update: changed ifndef --- src/models/user_model.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/models/user_model.h b/src/models/user_model.h index c2e0d4e..8f318e3 100644 --- a/src/models/user_model.h +++ b/src/models/user_model.h @@ -1,5 +1,5 @@ -#ifndef TEST_MODEL_H -#define TEST_MODEL_H +#ifndef USER_MODEL_H +#define USER_MODEL_H #include #include @@ -22,4 +22,4 @@ class UserModel { static UserModel NewUser(std::string password, std::string email, std::string username, std::string image_url, std::string type); }; -#endif \ No newline at end of file +#endif From 9d849e8d674c9a36c472bcd728e5e7fbad41c3eb Mon Sep 17 00:00:00 2001 From: David Baque Date: Sun, 10 Mar 2024 23:24:38 +0100 Subject: [PATCH 05/48] task #59: update controller --- .vscode/c_cpp_properties.json | 19 +++++++++++++++++++ .vscode/settings.json | 3 +++ CMakeLists.txt | 2 ++ .../controllers/auth_controller.h | 0 .../controllers/test_controller.h | 2 +- {src => include}/models/test_model.h | 0 {src => include}/routes/auth_routes.h | 3 +-- {src => include}/routes/test_routes.h | 2 +- src/controllers/auth_controller.cpp | 18 +++++++++++++----- src/controllers/test_controller.cpp | 2 +- src/main.cpp | 4 ++-- src/models/test_model.cpp | 2 +- src/routes/auth_routes.cpp | 2 +- src/routes/test_routes.cpp | 2 +- 14 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json rename {src => include}/controllers/auth_controller.h (100%) rename {src => include}/controllers/test_controller.h (84%) rename {src => include}/models/test_model.h (100%) rename {src => include}/routes/auth_routes.h (78%) rename {src => include}/routes/test_routes.h (79%) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..000738e --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,19 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc-10", + "cStandard": "c17", + "cppStandard": "gnu++20", + "intelliSenseMode": "linux-gcc-x64", + "forcedInclude": [ + "format" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cd7798d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.configureOnOpen": false +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e613ce2..24567ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,4 +12,6 @@ find_library(PQ_LIB pq) file(GLOB_RECURSE SOURCES "src/*.cpp") add_executable(backend ${SOURCES}) +target_include_directories(backend PUBLIC include) + target_link_libraries(backend ${PQXX_LIB} ${PQ_LIB}) diff --git a/src/controllers/auth_controller.h b/include/controllers/auth_controller.h similarity index 100% rename from src/controllers/auth_controller.h rename to include/controllers/auth_controller.h diff --git a/src/controllers/test_controller.h b/include/controllers/test_controller.h similarity index 84% rename from src/controllers/test_controller.h rename to include/controllers/test_controller.h index 35a28f7..5dcce6a 100644 --- a/src/controllers/test_controller.h +++ b/include/controllers/test_controller.h @@ -4,7 +4,7 @@ #include #include -#include "../models/test_model.h" +#include "models/test_model.h" class TestController { public: diff --git a/src/models/test_model.h b/include/models/test_model.h similarity index 100% rename from src/models/test_model.h rename to include/models/test_model.h diff --git a/src/routes/auth_routes.h b/include/routes/auth_routes.h similarity index 78% rename from src/routes/auth_routes.h rename to include/routes/auth_routes.h index e04fcb4..19a994b 100644 --- a/src/routes/auth_routes.h +++ b/include/routes/auth_routes.h @@ -3,10 +3,9 @@ #include +#include "controllers/auth_controller.h" #include "crow.h" -#include "../controllers/auth_controller.h" - void initialize_auth_routes(crow::SimpleApp& app, pqxx::connection& db); #endif diff --git a/src/routes/test_routes.h b/include/routes/test_routes.h similarity index 79% rename from src/routes/test_routes.h rename to include/routes/test_routes.h index f35f02c..477d393 100644 --- a/src/routes/test_routes.h +++ b/include/routes/test_routes.h @@ -3,7 +3,7 @@ #include -#include "../controllers/test_controller.h" +#include "controllers/test_controller.h" #include "crow.h" void initialize_test_routes(crow::SimpleApp& app, pqxx::connection& db); diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index fd880a5..566b08a 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -1,9 +1,17 @@ -#include "auth_controller.h" +#include "controllers/auth_controller.h" void UserController::create_user(pqxx::connection& db, const crow::request& req, crow::response& res) { - res.code = 200; - crow::json::wvalue x({{"message", "Hello, World!"}}); - x["message2"] = "Hello, World.. Again!"; + /* res.code = 200; + auto y = crow::json::load(req.body); + + crow::json::wvalue x({{"message", "Hello, World!"}}); + x["message2"] = "Hello!"; + res.write(x.dump()); + res.end(); */ + + auto y = crow::json::load(req.body); + crow::json::wvalue x(y); + res.write(x.dump()); res.end(); -} \ No newline at end of file +} diff --git a/src/controllers/test_controller.cpp b/src/controllers/test_controller.cpp index a83670b..abf62ba 100644 --- a/src/controllers/test_controller.cpp +++ b/src/controllers/test_controller.cpp @@ -1,4 +1,4 @@ -#include "test_controller.h" +#include "../../include/controllers/test_controller.h" std::string TestController::index(pqxx::connection& db) { try { diff --git a/src/main.cpp b/src/main.cpp index c550182..8d45311 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,9 +4,9 @@ #include #include -#include "./routes/auth_routes.h" -#include "./routes/test_routes.h" #include "crow.h" +#include "routes/auth_routes.h" +#include "routes/test_routes.h" int main() { try { diff --git a/src/models/test_model.cpp b/src/models/test_model.cpp index 89e1045..38f35d2 100644 --- a/src/models/test_model.cpp +++ b/src/models/test_model.cpp @@ -1,4 +1,4 @@ -#include "test_model.h" +#include "models/test_model.h" TestModel::TestModel(std::string time) : time(time) {} diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index b964fd5..2deec09 100644 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -1,4 +1,4 @@ -#include "auth_routes.h" +#include "routes/auth_routes.h" void initialize_auth_routes(crow::SimpleApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/auth/register").methods("POST"_method)([&db](const crow::request& req, crow::response& res) { diff --git a/src/routes/test_routes.cpp b/src/routes/test_routes.cpp index 94168b6..4cb4141 100644 --- a/src/routes/test_routes.cpp +++ b/src/routes/test_routes.cpp @@ -1,4 +1,4 @@ -#include "test_routes.h" +#include "routes/test_routes.h" void initialize_test_routes(crow::SimpleApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/test") From ed70e29a01c4641e6580c2a9622a98854d041b20 Mon Sep 17 00:00:00 2001 From: David Baque Date: Sun, 10 Mar 2024 23:32:50 +0100 Subject: [PATCH 06/48] add .gitignore --- .gitignore | 1 + .vscode/c_cpp_properties.json | 19 ------------------- .vscode/settings.json | 3 --- 3 files changed, 1 insertion(+), 22 deletions(-) create mode 100644 .gitignore delete mode 100644 .vscode/c_cpp_properties.json delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbe9c82 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/ \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 000738e..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**" - ], - "defines": [], - "compilerPath": "/usr/bin/gcc-10", - "cStandard": "c17", - "cppStandard": "gnu++20", - "intelliSenseMode": "linux-gcc-x64", - "forcedInclude": [ - "format" - ] - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index cd7798d..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "cmake.configureOnOpen": false -} \ No newline at end of file From cb74d32831c6bc2a603dc815b371b3da6d3c8f2f Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Mon, 11 Mar 2024 01:57:35 +0100 Subject: [PATCH 07/48] fix: minor fixes --- .gitignore | 2 +- include/controllers/auth_controller.h | 10 ++-------- include/controllers/test_controller.h | 5 ----- include/models/test_model.h | 5 ----- {src => include}/models/user_model.h | 7 +------ include/routes/auth_routes.h | 5 ----- include/routes/test_routes.h | 5 ----- src/controllers/auth_controller.cpp | 4 ++-- src/controllers/test_controller.cpp | 2 +- src/models/user_model.cpp | 21 +++++++++------------ src/routes/auth_routes.cpp | 2 +- 11 files changed, 17 insertions(+), 51 deletions(-) rename {src => include}/models/user_model.h (60%) diff --git a/.gitignore b/.gitignore index dbe9c82..1d74e21 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -.vscode/ \ No newline at end of file +.vscode/ diff --git a/include/controllers/auth_controller.h b/include/controllers/auth_controller.h index de4699b..539fe95 100644 --- a/include/controllers/auth_controller.h +++ b/include/controllers/auth_controller.h @@ -1,16 +1,10 @@ -#ifndef USER_CONTROLLER_H -#define USER_CONTROLLER_H - #include #include #include #include "crow.h" -class UserController { +class AuthController { public: - static std::string index(pqxx::connection& db); - static void create_user(pqxx::connection& db, const crow::request& req, crow::response& res); + static void register_user(pqxx::connection& db, const crow::request& req, crow::response& res); }; - -#endif \ No newline at end of file diff --git a/include/controllers/test_controller.h b/include/controllers/test_controller.h index 5dcce6a..62da7db 100644 --- a/include/controllers/test_controller.h +++ b/include/controllers/test_controller.h @@ -1,6 +1,3 @@ -#ifndef TEST_CONTROLLER_H -#define TEST_CONTROLLER_H - #include #include @@ -10,5 +7,3 @@ class TestController { public: static std::string index(pqxx::connection& db); }; - -#endif diff --git a/include/models/test_model.h b/include/models/test_model.h index 43266dc..61dbf0f 100644 --- a/include/models/test_model.h +++ b/include/models/test_model.h @@ -1,6 +1,3 @@ -#ifndef TEST_MODEL_H -#define TEST_MODEL_H - #include #include @@ -15,5 +12,3 @@ class TestModel { static TestModel timeNow(pqxx::connection& db); }; - -#endif diff --git a/src/models/user_model.h b/include/models/user_model.h similarity index 60% rename from src/models/user_model.h rename to include/models/user_model.h index 8f318e3..2022904 100644 --- a/src/models/user_model.h +++ b/include/models/user_model.h @@ -1,6 +1,3 @@ -#ifndef USER_MODEL_H -#define USER_MODEL_H - #include #include @@ -19,7 +16,5 @@ class UserModel { std::string getId(); - static UserModel NewUser(std::string password, std::string email, std::string username, std::string image_url, std::string type); + static UserModel create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type); }; - -#endif diff --git a/include/routes/auth_routes.h b/include/routes/auth_routes.h index 19a994b..68fabed 100644 --- a/include/routes/auth_routes.h +++ b/include/routes/auth_routes.h @@ -1,11 +1,6 @@ -#ifndef AUTH_ROUTES_H -#define AUTH_ROUTES_H - #include #include "controllers/auth_controller.h" #include "crow.h" void initialize_auth_routes(crow::SimpleApp& app, pqxx::connection& db); - -#endif diff --git a/include/routes/test_routes.h b/include/routes/test_routes.h index 477d393..4c9679b 100644 --- a/include/routes/test_routes.h +++ b/include/routes/test_routes.h @@ -1,11 +1,6 @@ -#ifndef TEST_ROUTES_H -#define TEST_ROUTES_H - #include #include "controllers/test_controller.h" #include "crow.h" void initialize_test_routes(crow::SimpleApp& app, pqxx::connection& db); - -#endif diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index 566b08a..cb8589c 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -1,8 +1,8 @@ #include "controllers/auth_controller.h" -void UserController::create_user(pqxx::connection& db, const crow::request& req, crow::response& res) { +void AuthController::register_user(pqxx::connection& db, const crow::request& req, crow::response& res) { /* res.code = 200; - auto y = crow::json::load(req.body); + auto y = crow::json::load(req.body); crow::json::wvalue x({{"message", "Hello, World!"}}); x["message2"] = "Hello!"; diff --git a/src/controllers/test_controller.cpp b/src/controllers/test_controller.cpp index abf62ba..8d92fd8 100644 --- a/src/controllers/test_controller.cpp +++ b/src/controllers/test_controller.cpp @@ -1,4 +1,4 @@ -#include "../../include/controllers/test_controller.h" +#include "controllers/test_controller.h" std::string TestController::index(pqxx::connection& db) { try { diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 5e3ac9d..beeec1b 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -1,22 +1,19 @@ -#include "user_model.h" +#include "models/user_model.h" -TestModel::TestModel(std::string id) : _id(id) {} +UserModel::UserModel(std::string id) : _id(id) {} -std::string TestModel::getId() { - return this._id; +std::string UserModel::getId() { + return _id; } -UserModel UserModel::NewUser(std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type) { +UserModel UserModel::create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type) { pqxx::work txn(db); - txn.exec(" - INSERT INTO users (password, email, username, image_url, balance, type) - VALUES $1, $2, $3, $4, $5, $6)", - password, email, username, image_url, balance, type); + txn.exec_params("INSERT INTO users (password, email, username, image_url, balance, type) VALUES $1, $2, $3, $4, $5, $6)", password, email, username, image_url, balance, type); - pqxx::result result = txn.exec("SELECT id FROM users WHERE email = $1 ", email); + pqxx::result result = txn.exec_params("SELECT id FROM users WHERE email = $1", email); std::string res = result[0][0].as(); txn.commit(); - return TestModel(res); -} \ No newline at end of file + return UserModel(res); +} diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index 2deec09..9a72725 100644 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -2,6 +2,6 @@ void initialize_auth_routes(crow::SimpleApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/auth/register").methods("POST"_method)([&db](const crow::request& req, crow::response& res) { - UserController::create_user(db, req, res); + AuthController::register_user(db, req, res); }); } From 7d057be852ac2194af04c7c18f6a16097ae43e25 Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Mon, 11 Mar 2024 09:19:27 +0100 Subject: [PATCH 08/48] task #60: connect register route, controller and model --- include/controllers/auth_controller.h | 1 + src/controllers/auth_controller.cpp | 18 +++++++++--------- src/models/user_model.cpp | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/include/controllers/auth_controller.h b/include/controllers/auth_controller.h index 539fe95..9a400ae 100644 --- a/include/controllers/auth_controller.h +++ b/include/controllers/auth_controller.h @@ -3,6 +3,7 @@ #include #include "crow.h" +#include "models/user_model.h" class AuthController { public: diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index cb8589c..97f0c57 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -1,17 +1,17 @@ #include "controllers/auth_controller.h" void AuthController::register_user(pqxx::connection& db, const crow::request& req, crow::response& res) { - /* res.code = 200; - auto y = crow::json::load(req.body); + crow::json::rvalue body = crow::json::load(req.body); - crow::json::wvalue x({{"message", "Hello, World!"}}); - x["message2"] = "Hello!"; - res.write(x.dump()); - res.end(); */ + std::string password = body["password"].s(); + std::string username = body["username"].s(); + std::string email = body["email"].s(); + std::string type = body["type"].s(); - auto y = crow::json::load(req.body); - crow::json::wvalue x(y); + UserModel user = UserModel::create_user(db, password, email, username, "", 0, type); - res.write(x.dump()); + crow::json::wvalue data({{"id", user.getId()}}); + res.code = 200; + res.write(data.dump()); res.end(); } diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index beeec1b..d99ae49 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -9,7 +9,7 @@ std::string UserModel::getId() { UserModel UserModel::create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type) { pqxx::work txn(db); - txn.exec_params("INSERT INTO users (password, email, username, image_url, balance, type) VALUES $1, $2, $3, $4, $5, $6)", password, email, username, image_url, balance, type); + txn.exec_params("INSERT INTO users (password, email, username, image_url, balance, type) VALUES ($1, $2, $3, $4, $5, $6)", password, email, username, image_url, balance, type); pqxx::result result = txn.exec_params("SELECT id FROM users WHERE email = $1", email); From bc3ea01465d3bfedd15e76290b9f65bb9f45601e Mon Sep 17 00:00:00 2001 From: David Baque Date: Mon, 11 Mar 2024 18:41:07 +0100 Subject: [PATCH 09/48] task #62: user password encryption --- CMakeLists.txt | 20 +++++++++++++++++--- Dockerfile.dev | 9 +++++++++ libbcrypt | 1 + src/controllers/auth_controller.cpp | 8 +++++++- 4 files changed, 34 insertions(+), 4 deletions(-) create mode 160000 libbcrypt diff --git a/CMakeLists.txt b/CMakeLists.txt index 24567ad..0dc9a0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,15 +3,29 @@ cmake_minimum_required(VERSION 3.18) project(backend) set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lpqxx -lpq") -set(PQXX /usr/local/include/pqxx) +# Agrega las opciones de compilación +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + +# Encuentra las bibliotecas necesarias find_library(PQXX_LIB pqxx) find_library(PQ_LIB pq) +find_library(BCRYPT_LIB bcrypt) + +# Si bcrypt está instalado en un lugar no estándar, puedes usar find_path para encontrar la carpeta de inclusión +# find_path(BCRYPT_INCLUDE_DIR bcrypt/bcrypt.h) +# Establece las fuentes de tu proyecto file(GLOB_RECURSE SOURCES "src/*.cpp") + +# Crea el ejecutable add_executable(backend ${SOURCES}) +# Agrega las carpetas de inclusión target_include_directories(backend PUBLIC include) -target_link_libraries(backend ${PQXX_LIB} ${PQ_LIB}) +# Enlaza las bibliotecas necesarias +target_link_libraries(backend ${PQXX_LIB} ${PQ_LIB} ${BCRYPT_LIB}) + +# Si bcrypt está en un lugar no estándar, también agrega el directorio de inclusión +# target_include_directories(backend PUBLIC ${BCRYPT_INCLUDE_DIR}) diff --git a/Dockerfile.dev b/Dockerfile.dev index 0416993..8194ad0 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -10,6 +10,15 @@ RUN dpkg -i crow-v1.0+5.deb COPY . . +RUN cd libbcrypt && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make && \ + make install && \ + ldconfig + + RUN chmod 777 hot-reload.sh CMD ["./hot-reload.sh"] diff --git a/libbcrypt b/libbcrypt new file mode 160000 index 0000000..d6523c3 --- /dev/null +++ b/libbcrypt @@ -0,0 +1 @@ +Subproject commit d6523c370de6e724ce4ec703e2449b5b028ea3b1 diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index 97f0c57..b80e2e8 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -1,5 +1,9 @@ #include "controllers/auth_controller.h" +#include + +#include "bcrypt/BCrypt.hpp" + void AuthController::register_user(pqxx::connection& db, const crow::request& req, crow::response& res) { crow::json::rvalue body = crow::json::load(req.body); @@ -8,7 +12,9 @@ void AuthController::register_user(pqxx::connection& db, const crow::request& re std::string email = body["email"].s(); std::string type = body["type"].s(); - UserModel user = UserModel::create_user(db, password, email, username, "", 0, type); + std::string hash = BCrypt::generateHash(password); + + UserModel user = UserModel::create_user(db, hash, email, username, "", 0, type); crow::json::wvalue data({{"id", user.getId()}}); res.code = 200; From 6aae4e0b96f82e9ef2c44e90c54d9428f9ee10b6 Mon Sep 17 00:00:00 2001 From: David Baque Date: Mon, 11 Mar 2024 19:53:10 +0100 Subject: [PATCH 10/48] fix bug BCrypt.cpp installation --- Dockerfile.dev | 5 +++-- libbcrypt | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 160000 libbcrypt diff --git a/Dockerfile.dev b/Dockerfile.dev index 8194ad0..6b36567 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,6 +1,6 @@ FROM gcc:latest -RUN apt-get update && apt-get install -y libpqxx-dev libboost-dev cmake inotify-tools +RUN apt-get update && apt-get install -y libpqxx-dev libboost-dev cmake inotify-tools git WORKDIR /app @@ -10,7 +10,8 @@ RUN dpkg -i crow-v1.0+5.deb COPY . . -RUN cd libbcrypt && \ +RUN git clone https://github.com/trusch/libbcrypt.git && \ + cd libbcrypt && \ mkdir build && \ cd build && \ cmake .. && \ diff --git a/libbcrypt b/libbcrypt deleted file mode 160000 index d6523c3..0000000 --- a/libbcrypt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d6523c370de6e724ce4ec703e2449b5b028ea3b1 From 9d8ba882c86168c9de2b096d90d061697378b629 Mon Sep 17 00:00:00 2001 From: Mampiz Date: Mon, 11 Mar 2024 22:46:59 +0100 Subject: [PATCH 11/48] task #63: validate user exist register --- include/models/user_model.h | 2 ++ src/controllers/auth_controller.cpp | 39 +++++++++++++++++++++-------- src/models/user_model.cpp | 18 +++++++++++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/include/models/user_model.h b/include/models/user_model.h index 2022904..ba05253 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -17,4 +17,6 @@ class UserModel { std::string getId(); static UserModel create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type); + static bool user_exist(pqxx::connection& db, std::string email); + }; diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index b80e2e8..bf07356 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -5,19 +5,36 @@ #include "bcrypt/BCrypt.hpp" void AuthController::register_user(pqxx::connection& db, const crow::request& req, crow::response& res) { - crow::json::rvalue body = crow::json::load(req.body); + try { + crow::json::rvalue body = crow::json::load(req.body); - std::string password = body["password"].s(); - std::string username = body["username"].s(); - std::string email = body["email"].s(); - std::string type = body["type"].s(); + std::string password = body["password"].s(); + std::string username = body["username"].s(); + std::string email = body["email"].s(); + std::string type = body["type"].s(); - std::string hash = BCrypt::generateHash(password); + std::string hash = BCrypt::generateHash(password); - UserModel user = UserModel::create_user(db, hash, email, username, "", 0, type); + if (UserModel::user_exist(db, email)) { + res.code = 400; + crow::json::wvalue error({{"error", "user already exists"}}); + res.write(error.dump()); + res.end(); + return; + } - crow::json::wvalue data({{"id", user.getId()}}); - res.code = 200; - res.write(data.dump()); - res.end(); + UserModel user = UserModel::create_user(db, hash, email, username, "", 0, type); + + crow::json::wvalue data({{"id", user.getId()}}); + res.code = 200; + res.write(data.dump()); + res.end(); + + } catch (const std::exception& e) { + std::cerr << "Error in register_user: " << e.what() << std::endl; + res.code = 500; + crow::json::wvalue error({{"error", "internal server error"}}); + res.write(error.dump()); + res.end(); + } } diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index d99ae49..13333fb 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -1,5 +1,7 @@ #include "models/user_model.h" +#include + UserModel::UserModel(std::string id) : _id(id) {} std::string UserModel::getId() { @@ -17,3 +19,19 @@ UserModel UserModel::create_user(pqxx::connection& db, std::string password, std txn.commit(); return UserModel(res); } + +bool UserModel::user_exist(pqxx::connection& db, std::string email) { + try { + pqxx::work txn(db); + + pqxx::result result = txn.exec_params("SELECT username FROM users WHERE email = $1", email); + + bool userExists = !result.empty() && !result[0][0].is_null(); + + txn.commit(); + + return userExists; + } catch (const std::exception& e) { + return false; + } +} From 7487cfc5e730540d74374ebfafb62025757f1eab Mon Sep 17 00:00:00 2001 From: 0xERuiz Date: Tue, 12 Mar 2024 12:40:37 +0100 Subject: [PATCH 12/48] =?UTF-8?q?task=20#66:=20username=5Fexist=20a=C3=B1a?= =?UTF-8?q?dido?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/models/user_model.h | 1 + src/controllers/auth_controller.cpp | 23 ++++++++++++++++++----- src/models/user_model.cpp | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/include/models/user_model.h b/include/models/user_model.h index ba05253..bf4e631 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -18,5 +18,6 @@ class UserModel { static UserModel create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type); static bool user_exist(pqxx::connection& db, std::string email); + static bool username_exist(pqxx::connection& db, std::string username); }; diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index bf07356..9f33df8 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -4,8 +4,10 @@ #include "bcrypt/BCrypt.hpp" -void AuthController::register_user(pqxx::connection& db, const crow::request& req, crow::response& res) { - try { +void AuthController::register_user(pqxx::connection &db, const crow::request &req, crow::response &res) +{ + try + { crow::json::rvalue body = crow::json::load(req.body); std::string password = body["password"].s(); @@ -15,7 +17,8 @@ void AuthController::register_user(pqxx::connection& db, const crow::request& re std::string hash = BCrypt::generateHash(password); - if (UserModel::user_exist(db, email)) { + if (UserModel::user_exist(db, email)) + { res.code = 400; crow::json::wvalue error({{"error", "user already exists"}}); res.write(error.dump()); @@ -23,14 +26,24 @@ void AuthController::register_user(pqxx::connection& db, const crow::request& re return; } + if (UserModel::username_exist(db, username)) + { + res.code = 400; + crow::json::wvalue error({{"error", "username already exists"}}); + res.write(error.dump()); + res.end(); + return; + } + UserModel user = UserModel::create_user(db, hash, email, username, "", 0, type); crow::json::wvalue data({{"id", user.getId()}}); res.code = 200; res.write(data.dump()); res.end(); - - } catch (const std::exception& e) { + } + catch (const std::exception &e) + { std::cerr << "Error in register_user: " << e.what() << std::endl; res.code = 500; crow::json::wvalue error({{"error", "internal server error"}}); diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 13333fb..3d13c50 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -35,3 +35,19 @@ bool UserModel::user_exist(pqxx::connection& db, std::string email) { return false; } } + +bool UserModel::username_exist(pqxx::connection& db, std::string username) { + try { + pqxx::work txn(db); + + pqxx::result result = txn.exec_params("SELECT email FROM users WHERE username = $1", username); + + bool usernameExists = !result.empty() && !result[0][0].is_null(); + + txn.commit(); + + return usernameExists; + } catch (const std::exception& e) { + return false; + } +} From 67c12006925f952b8d57b3210aafc1c6079abded Mon Sep 17 00:00:00 2001 From: Voooigt Date: Tue, 12 Mar 2024 14:22:26 +0100 Subject: [PATCH 13/48] task #69: create route to get all users --- include/controllers/test_controller.h | 5 +++++ include/models/test_model.h | 5 +++++ include/routes/user_routes.h | 6 ++++++ src/main.cpp | 2 ++ src/routes/user_routes.cpp | 10 ++++++++++ 5 files changed, 28 insertions(+) create mode 100644 include/routes/user_routes.h create mode 100644 src/routes/user_routes.cpp diff --git a/include/controllers/test_controller.h b/include/controllers/test_controller.h index 62da7db..b3274be 100644 --- a/include/controllers/test_controller.h +++ b/include/controllers/test_controller.h @@ -1,3 +1,6 @@ +#ifndef TEST_CONTROLLER_h +#define TEST_CONTROLLER_h + #include #include @@ -7,3 +10,5 @@ class TestController { public: static std::string index(pqxx::connection& db); }; + +#endif \ No newline at end of file diff --git a/include/models/test_model.h b/include/models/test_model.h index 61dbf0f..8512341 100644 --- a/include/models/test_model.h +++ b/include/models/test_model.h @@ -1,3 +1,6 @@ +#ifndef TEST_MODEL_h +#define TEST_MODEL_h + #include #include @@ -12,3 +15,5 @@ class TestModel { static TestModel timeNow(pqxx::connection& db); }; + +#endif diff --git a/include/routes/user_routes.h b/include/routes/user_routes.h new file mode 100644 index 0000000..e986688 --- /dev/null +++ b/include/routes/user_routes.h @@ -0,0 +1,6 @@ +#include + +#include "controllers/test_controller.h" +#include "crow.h" + +void initialize_user_routes(crow::SimpleApp& app, pqxx::connection& db); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8d45311..b74ea77 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "crow.h" #include "routes/auth_routes.h" #include "routes/test_routes.h" +#include "routes/user_routes.h" int main() { try { @@ -30,6 +31,7 @@ int main() { initialize_auth_routes(app, conn); initialize_test_routes(app, conn); + initialize_user_routes(app, conn); app.port(HTTP_PORT).multithreaded().run(); conn.disconnect(); diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp new file mode 100644 index 0000000..e90fb0e --- /dev/null +++ b/src/routes/user_routes.cpp @@ -0,0 +1,10 @@ +#include "routes/user_routes.h" + +void initialize_user_routes(crow::SimpleApp& app, pqxx::connection& db) { + CROW_ROUTE(app, "/api/users").methods("GET"_method)([&db](const crow::request& req, crow::response& res) { + // UserController::get_all_users(db, req, res); + res.code = 200; + res.body = TestController::index(db); + res.end(); + }); +} From 4a08888f704b7bbc021f041c2e040c44f58c9f3c Mon Sep 17 00:00:00 2001 From: leobelab Date: Tue, 12 Mar 2024 16:55:29 +0100 Subject: [PATCH 14/48] task #71: get_users created --- include/models/user_model.h | 12 ++++++--- src/controllers/auth_controller.cpp | 16 ++++-------- src/models/user_model.cpp | 40 +++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/include/models/user_model.h b/include/models/user_model.h index bf4e631..94c4a71 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -1,11 +1,11 @@ #include #include +#include class UserModel { private: std::string _id; - std::string _password; - std::string _mail; + std::string _email; std::string _username; std::string _image_url; int _balance; @@ -13,11 +13,17 @@ class UserModel { public: UserModel(std::string id); + UserModel(std::string id, std::string email, std::string username, std::string image_url, int balance, std::string type); std::string getId(); + std::string getEmail(); + std::string getUsername(); + std::string getImageUrl(); + int getBalance(); + std::string getType(); static UserModel create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type); static bool user_exist(pqxx::connection& db, std::string email); static bool username_exist(pqxx::connection& db, std::string username); - + static std::vector get_users(pqxx::connection& db); }; diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index 9f33df8..e64a9e8 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -4,10 +4,8 @@ #include "bcrypt/BCrypt.hpp" -void AuthController::register_user(pqxx::connection &db, const crow::request &req, crow::response &res) -{ - try - { +void AuthController::register_user(pqxx::connection &db, const crow::request &req, crow::response &res) { + try { crow::json::rvalue body = crow::json::load(req.body); std::string password = body["password"].s(); @@ -17,8 +15,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re std::string hash = BCrypt::generateHash(password); - if (UserModel::user_exist(db, email)) - { + if (UserModel::user_exist(db, email)) { res.code = 400; crow::json::wvalue error({{"error", "user already exists"}}); res.write(error.dump()); @@ -26,8 +23,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re return; } - if (UserModel::username_exist(db, username)) - { + if (UserModel::username_exist(db, username)) { res.code = 400; crow::json::wvalue error({{"error", "username already exists"}}); res.write(error.dump()); @@ -41,9 +37,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re res.code = 200; res.write(data.dump()); res.end(); - } - catch (const std::exception &e) - { + } catch (const std::exception &e) { std::cerr << "Error in register_user: " << e.what() << std::endl; res.code = 500; crow::json::wvalue error({{"error", "internal server error"}}); diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 3d13c50..8aa4275 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -4,10 +4,32 @@ UserModel::UserModel(std::string id) : _id(id) {} +UserModel::UserModel(std::string id, std::string email, std::string username, std::string image_url, int balance, std::string type) : _id(id), _email(email), _username(username), _image_url(image_url), _balance(balance), _type(type) {} + std::string UserModel::getId() { return _id; } +std::string UserModel::getEmail() { + return _email; +} + +std::string UserModel::getUsername() { + return _username; +} + +std::string UserModel::getImageUrl() { + return _image_url; +} + +int UserModel::getBalance() { + return _balance; +} + +std::string UserModel::getType() { + return _type; +} + UserModel UserModel::create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type) { pqxx::work txn(db); @@ -51,3 +73,21 @@ bool UserModel::username_exist(pqxx::connection& db, std::string username) { return false; } } + +std::vector UserModel::get_users(pqxx::connection& db) { + std::vector all_users; + + pqxx::work txn(db); + + pqxx::result result = txn.exec("SELECT id, email, username, image_url, balance, type FROM users"); + + for (const auto& row : result) { + UserModel user(row["id"].as(), row["email"].as(), row["username"].as(), row["image_url"].as(), row["balance"].as(), row["type"].as()); + + all_users.push_back(user); + } + + txn.commit(); + + return all_users; +} From 22e486560bf8b9880bb2a1dea44644e58f399cf0 Mon Sep 17 00:00:00 2001 From: Voooigt Date: Tue, 12 Mar 2024 17:47:31 +0100 Subject: [PATCH 15/48] tast #70: create controller get users --- include/controllers/user_controller.h | 12 ++++++++++++ include/models/user_model.h | 5 +++++ include/routes/user_routes.h | 2 +- src/controllers/user_controller.cpp | 28 +++++++++++++++++++++++++++ src/routes/user_routes.cpp | 5 +---- 5 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 include/controllers/user_controller.h create mode 100644 src/controllers/user_controller.cpp diff --git a/include/controllers/user_controller.h b/include/controllers/user_controller.h new file mode 100644 index 0000000..3bbabfe --- /dev/null +++ b/include/controllers/user_controller.h @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +#include "crow.h" +#include "models/user_model.h" + +class UserController { + public: + static void get_users(pqxx::connection& db, const crow::request& req, crow::response& res); +}; \ No newline at end of file diff --git a/include/models/user_model.h b/include/models/user_model.h index 94c4a71..d28c000 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -1,3 +1,6 @@ +#ifndef USER_MODEL_h +#define USER_MODEL_h + #include #include #include @@ -27,3 +30,5 @@ class UserModel { static bool username_exist(pqxx::connection& db, std::string username); static std::vector get_users(pqxx::connection& db); }; + +#endif diff --git a/include/routes/user_routes.h b/include/routes/user_routes.h index e986688..d2b6b51 100644 --- a/include/routes/user_routes.h +++ b/include/routes/user_routes.h @@ -1,6 +1,6 @@ #include -#include "controllers/test_controller.h" +#include "controllers/user_controller.h" #include "crow.h" void initialize_user_routes(crow::SimpleApp& app, pqxx::connection& db); \ No newline at end of file diff --git a/src/controllers/user_controller.cpp b/src/controllers/user_controller.cpp new file mode 100644 index 0000000..e969dc3 --- /dev/null +++ b/src/controllers/user_controller.cpp @@ -0,0 +1,28 @@ +#include "controllers/user_controller.h" + +void UserController::get_users(pqxx::connection &db, const crow::request &req, crow::response &res) { + try { + std::vector allUsers = UserModel::get_users(db); + crow::json::wvalue::list users; + for (unsigned int i = 0; i < allUsers.size(); i++) { + crow::json::wvalue user; + user["id"] = allUsers[i].getId(); + user["email"] = allUsers[i].getEmail(); + user["username"] = allUsers[i].getUsername(); + user["image_url"] = allUsers[i].getImageUrl(); + user["balance"] = allUsers[i].getBalance(); + user["type"] = allUsers[i].getType(); + users.push_back(user); + } + crow::json::wvalue data{{"users", users}}; + res.code = 200; + res.write(data.dump()); + res.end(); + } catch (const std::exception &e) { + std::cerr << "Error in get users: " << e.what() << std::endl; + res.code = 500; + crow::json::wvalue error({{"error", "internal server error"}}); + res.write(error.dump()); + res.end(); + } +} diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp index e90fb0e..f382448 100644 --- a/src/routes/user_routes.cpp +++ b/src/routes/user_routes.cpp @@ -2,9 +2,6 @@ void initialize_user_routes(crow::SimpleApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/users").methods("GET"_method)([&db](const crow::request& req, crow::response& res) { - // UserController::get_all_users(db, req, res); - res.code = 200; - res.body = TestController::index(db); - res.end(); + UserController::get_users(db, req, res); }); } From fbe57d6bee64fbe1c75264319ee40f22068ae2f2 Mon Sep 17 00:00:00 2001 From: David Baque Date: Wed, 13 Mar 2024 00:00:57 +0100 Subject: [PATCH 16/48] task #72: validate data signup --- .../middlewares/user_validation_middleware.h | 13 ++++ include/routes/auth_routes.h | 5 +- include/routes/test_routes.h | 3 +- include/routes/user_routes.h | 5 +- include/utils/common.h | 18 +++++ include/utils/custom_regex.h | 16 +++++ include/utils/user_validations.h | 17 +++++ include/utils/utils.h | 7 ++ include/utils/validations.h | 3 + src/controllers/auth_controller.cpp | 5 +- src/main.cpp | 6 +- src/routes/auth_routes.cpp | 10 ++- src/routes/test_routes.cpp | 2 +- src/routes/user_routes.cpp | 2 +- src/utils/common.cpp | 4 ++ src/utils/custom_regex.cpp | 67 +++++++++++++++++++ src/utils/user_validations.cpp | 56 ++++++++++++++++ src/utils/utils.cpp | 15 +++++ src/utils/validations.cpp | 15 +++++ 19 files changed, 258 insertions(+), 11 deletions(-) create mode 100644 include/middlewares/user_validation_middleware.h create mode 100644 include/utils/common.h create mode 100644 include/utils/custom_regex.h create mode 100644 include/utils/user_validations.h create mode 100644 include/utils/utils.h create mode 100644 include/utils/validations.h create mode 100644 src/utils/common.cpp create mode 100644 src/utils/custom_regex.cpp create mode 100644 src/utils/user_validations.cpp create mode 100644 src/utils/utils.cpp create mode 100644 src/utils/validations.cpp diff --git a/include/middlewares/user_validation_middleware.h b/include/middlewares/user_validation_middleware.h new file mode 100644 index 0000000..a7d1f50 --- /dev/null +++ b/include/middlewares/user_validation_middleware.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "utils/user_validations.h" + +struct SignupValidation : crow::ILocalMiddleware { + struct context {}; + + void before_handle(crow::request& req, crow::response& res, context& ctx) {} + + void after_handle(crow::request& req, crow::response& res, context& ctx) {} +}; diff --git a/include/routes/auth_routes.h b/include/routes/auth_routes.h index 68fabed..9aa5be6 100644 --- a/include/routes/auth_routes.h +++ b/include/routes/auth_routes.h @@ -1,6 +1,7 @@ #include +#include "crow.h" #include "controllers/auth_controller.h" -#include "crow.h" +#include "utils/common.h" -void initialize_auth_routes(crow::SimpleApp& app, pqxx::connection& db); +void initialize_auth_routes(NebyApp& app, pqxx::connection& db); diff --git a/include/routes/test_routes.h b/include/routes/test_routes.h index 4c9679b..2373c1d 100644 --- a/include/routes/test_routes.h +++ b/include/routes/test_routes.h @@ -2,5 +2,6 @@ #include "controllers/test_controller.h" #include "crow.h" +#include "utils/common.h" -void initialize_test_routes(crow::SimpleApp& app, pqxx::connection& db); +void initialize_test_routes(NebyApp& app, pqxx::connection& db); diff --git a/include/routes/user_routes.h b/include/routes/user_routes.h index d2b6b51..e3c1457 100644 --- a/include/routes/user_routes.h +++ b/include/routes/user_routes.h @@ -1,6 +1,7 @@ #include +#include "crow.h" #include "controllers/user_controller.h" -#include "crow.h" +#include "utils/common.h" -void initialize_user_routes(crow::SimpleApp& app, pqxx::connection& db); \ No newline at end of file +void initialize_user_routes(NebyApp& app, pqxx::connection& db); \ No newline at end of file diff --git a/include/utils/common.h b/include/utils/common.h new file mode 100644 index 0000000..7f26622 --- /dev/null +++ b/include/utils/common.h @@ -0,0 +1,18 @@ +// common.h +#pragma once +#include + +#include +#include + +#include "bcrypt/BCrypt.hpp" + +// ** middleawares includes +#include "middlewares/user_validation_middleware.h" + +using NebyApp = crow::App; + +struct Roles { + static const std::string ADMIN; + static const std::string NEIGHBOR; +}; diff --git a/include/utils/custom_regex.h b/include/utils/custom_regex.h new file mode 100644 index 0000000..f936b2f --- /dev/null +++ b/include/utils/custom_regex.h @@ -0,0 +1,16 @@ +// custom_regex.h +#include +#include +#include +#include +#include + +#include + +bool is_correct_email(std::string email); + +bool is_correct_type(std::string type) ; + +bool is_correct_password(std::string password); + +bool is_correct_username(std::string username); \ No newline at end of file diff --git a/include/utils/user_validations.h b/include/utils/user_validations.h new file mode 100644 index 0000000..24cf9a8 --- /dev/null +++ b/include/utils/user_validations.h @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include + +#include + +bool is_correct_body(const crow::request &req, crow::response &res); + +bool validate_email(const std::string &email, crow::response &res); + +bool validate_type(const std::string &type, crow::response &res); + +bool validate_password(const std::string &password, crow::response &res); + +bool validate_username(const std::string &username, crow::response &res); \ No newline at end of file diff --git a/include/utils/utils.h b/include/utils/utils.h new file mode 100644 index 0000000..9e57e4f --- /dev/null +++ b/include/utils/utils.h @@ -0,0 +1,7 @@ +#include + +#include + +void handle_exception(crow::response &res, const std::string &error_message); + +void handle_error(crow::response &res, const std::string &error_message); \ No newline at end of file diff --git a/include/utils/validations.h b/include/utils/validations.h new file mode 100644 index 0000000..c023f91 --- /dev/null +++ b/include/utils/validations.h @@ -0,0 +1,3 @@ +#include + +bool validate_required_body_fields(const crow::json::rvalue &body, const std::vector &required_fields, crow::response &res); \ No newline at end of file diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index e64a9e8..20f0374 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -3,9 +3,12 @@ #include #include "bcrypt/BCrypt.hpp" +#include "utils/user_validations.h" void AuthController::register_user(pqxx::connection &db, const crow::request &req, crow::response &res) { try { + if (!is_correct_body(req, res)) return; + crow::json::rvalue body = crow::json::load(req.body); std::string password = body["password"].s(); @@ -17,7 +20,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re if (UserModel::user_exist(db, email)) { res.code = 400; - crow::json::wvalue error({{"error", "user already exists"}}); + crow::json::wvalue error({{"error", "email already exists"}}); res.write(error.dump()); res.end(); return; diff --git a/src/main.cpp b/src/main.cpp index b74ea77..66e324c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,8 @@ #include "routes/test_routes.h" #include "routes/user_routes.h" +#include "utils/common.h" + int main() { try { int HTTP_PORT = std::stoi(std::getenv("HTTP_PORT")); @@ -18,7 +20,7 @@ int main() { std::string DB_HOST = std::string(std::getenv("DB_HOST")); std::string DB_PORT = std::string(std::getenv("DB_PORT")); - crow::SimpleApp app; + NebyApp app; std::string connection_string = std::format("dbname={} user={} password={} host={} port={}", DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT); pqxx::connection conn(connection_string); @@ -30,7 +32,9 @@ int main() { } initialize_auth_routes(app, conn); + initialize_test_routes(app, conn); + initialize_user_routes(app, conn); app.port(HTTP_PORT).multithreaded().run(); diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index 9a72725..9c3ef3a 100644 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -1,7 +1,13 @@ #include "routes/auth_routes.h" -void initialize_auth_routes(crow::SimpleApp& app, pqxx::connection& db) { - CROW_ROUTE(app, "/api/auth/register").methods("POST"_method)([&db](const crow::request& req, crow::response& res) { +#include "middlewares/user_validation_middleware.h" + + +void initialize_auth_routes(NebyApp& app, pqxx::connection& db) { + CROW_ROUTE(app, "/api/auth/register") + .methods(crow::HTTPMethod::POST) + .CROW_MIDDLEWARES(app, SignupValidation)([&db](const crow::request& req, crow::response& res) { AuthController::register_user(db, req, res); + }); } diff --git a/src/routes/test_routes.cpp b/src/routes/test_routes.cpp index 4cb4141..ecfb882 100644 --- a/src/routes/test_routes.cpp +++ b/src/routes/test_routes.cpp @@ -1,6 +1,6 @@ #include "routes/test_routes.h" -void initialize_test_routes(crow::SimpleApp& app, pqxx::connection& db) { +void initialize_test_routes(NebyApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/test") ([&db](const crow::request& req, crow::response& res) { res.code = 200; diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp index f382448..a9d4a61 100644 --- a/src/routes/user_routes.cpp +++ b/src/routes/user_routes.cpp @@ -1,6 +1,6 @@ #include "routes/user_routes.h" -void initialize_user_routes(crow::SimpleApp& app, pqxx::connection& db) { +void initialize_user_routes(NebyApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/users").methods("GET"_method)([&db](const crow::request& req, crow::response& res) { UserController::get_users(db, req, res); }); diff --git a/src/utils/common.cpp b/src/utils/common.cpp new file mode 100644 index 0000000..2a0292c --- /dev/null +++ b/src/utils/common.cpp @@ -0,0 +1,4 @@ +#include "utils/common.h" + +const std::string Roles::ADMIN = "admin"; +const std::string Roles::NEIGHBOR = "neighbor"; \ No newline at end of file diff --git a/src/utils/custom_regex.cpp b/src/utils/custom_regex.cpp new file mode 100644 index 0000000..957f52d --- /dev/null +++ b/src/utils/custom_regex.cpp @@ -0,0 +1,67 @@ +#include + +bool is_correct_email(std::string email) { + std::regex patronEmail(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"); + return std::regex_match(email, patronEmail); +} + +bool is_correct_type(std::string type) { + return Roles::ADMIN == type || Roles::NEIGHBOR == type; +} + +//** GOOD PASSWORDS -> P@ssw0rd!, Tr0ub4dor&3, S3cur3P@ss, B1gB@ngTh3ory, F!sh1ngR0ck5 +//** BAD PASSWORDS -> password, 12345678, qwerty, letmein, abc123 +bool is_correct_password(std::string password) { + // Configuración de requisitos + int minLength = 8; + int minLowercase = 1; + int minUppercase = 1; + int minNumbers = 1; + int minSymbols = 1; + + // Contadores para cada tipo de carácter + int lowercaseCount = 0; + int uppercaseCount = 0; + int numbersCount = 0; + int symbolsCount = 0; + std::set uniqueCharacters; + + std::regex symbolRegex("[!@#$%^&*()_+\\-=\\[\\]{};':\",./<>?\\\\|]"); + + if (int(password.length()) < minLength) { + return false; + } + + for (char c : password) { + uniqueCharacters.insert(c); + + if (std::islower(c)) { + lowercaseCount++; + } else if (std::isupper(c)) { + uppercaseCount++; + } else if (std::isdigit(c)) { + numbersCount++; + } else if (std::regex_match(std::string(1, c), symbolRegex)) { + symbolsCount++; + } + } + + return (lowercaseCount >= minLowercase && + uppercaseCount >= minUppercase && + numbersCount >= minNumbers && + symbolsCount >= minSymbols && + int(uniqueCharacters.size()) >= minLength); +} + +//** GOOD USERNAMES -> Usuario123, SecureUser1, AlphaBeta45, GoodPassword23, ValidUsername +//** BAD USERNAMES -> Invalid!User, SpacesUser , Short, LongUsernameWithTooManyCharacters, Invalid Spaces +bool is_correct_username(std::string username) { + if (username.length() < 4 || username.length() > 30) + return false; + + for (char c : username) { + if (!std::isalnum(c)) + return false; + } + return true; +} \ No newline at end of file diff --git a/src/utils/user_validations.cpp b/src/utils/user_validations.cpp new file mode 100644 index 0000000..daaecd4 --- /dev/null +++ b/src/utils/user_validations.cpp @@ -0,0 +1,56 @@ +#include "utils/user_validations.h" + +bool is_correct_body(const crow::request &req, crow::response &res) { + try { + crow::json::rvalue body = crow::json::load(req.body); + + const std::vector required_fields = {"email", "password", "type", "username"}; + + if (!validate_required_body_fields(body, required_fields, res)) return false; + + if (!validate_email(body["email"].s(), res) || + !validate_type(body["type"].s(), res) || + !validate_password(body["password"].s(), res) || + !validate_username(body["username"].s(), res)) { + return false; + } + + return true; + + } catch (const std::exception &e) { + handle_exception(res, "register validation error"); + return false; + } +} + +bool validate_email(const std::string &email, crow::response &res) { + if (!is_correct_email(email)) { + handle_error(res, "incorrect email"); + return false; + } + return true; +} + +bool validate_type(const std::string &type, crow::response &res) { + if (!is_correct_type(type)) { + handle_error(res, "incorrect type user"); + return false; + } + return true; +} + +bool validate_password(const std::string &password, crow::response &res) { + if (!is_correct_password(password)) { + handle_error(res, "incorrect password"); + return false; + } + return true; +} + +bool validate_username(const std::string &username, crow::response &res) { + if (!is_correct_username(username)) { + handle_error(res, "incorrect username"); + return false; + } + return true; +} diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp new file mode 100644 index 0000000..0e362c4 --- /dev/null +++ b/src/utils/utils.cpp @@ -0,0 +1,15 @@ +#include + +void handle_exception(crow::response &res, const std::string &error_message) { + res.code = 500; + crow::json::wvalue error({{"error", error_message}}); + res.write(error.dump()); + res.end(); +} + +void handle_error(crow::response &res, const std::string &error_message) { + crow::json::wvalue error({{"error", error_message}}); + res.code = 400; + res.write(error.dump()); + res.end(); +} \ No newline at end of file diff --git a/src/utils/validations.cpp b/src/utils/validations.cpp new file mode 100644 index 0000000..6ef90e5 --- /dev/null +++ b/src/utils/validations.cpp @@ -0,0 +1,15 @@ +#include + +bool validate_required_body_fields(const crow::json::rvalue &body, const std::vector &required_fields, crow::response &res) { + for (const auto &field : required_fields) { + if (!body.has(field)) { + crow::json::wvalue error({{"error", "missing " + field + " field"}}); + res.code = 404; + res.write(error.dump()); + res.end(); + return false; + } + } + + return true; +} \ No newline at end of file From 397557bf9d013f2b519d01ce35eaa9daf7184140 Mon Sep 17 00:00:00 2001 From: David Baque Date: Thu, 14 Mar 2024 20:51:10 +0100 Subject: [PATCH 17/48] task #75: add testing setup --- Dockerfile.test | 17 +++++++++++++++ hot-reload-test.sh | 41 +++++++++++++++++++++++++++++++++++ test_setup/CMakeLists.txt | 30 +++++++++++++++++++++++++ test_setup/tests/main.cpp | 19 ++++++++++++++++ test_setup/tests/test_one.cpp | 18 +++++++++++++++ 5 files changed, 125 insertions(+) create mode 100644 Dockerfile.test create mode 100644 hot-reload-test.sh create mode 100644 test_setup/CMakeLists.txt create mode 100644 test_setup/tests/main.cpp create mode 100644 test_setup/tests/test_one.cpp diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 0000000..5fb7116 --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,17 @@ +FROM gcc:latest + +RUN apt-get update && apt-get install -y cmake inotify-tools git + +WORKDIR /app + +COPY . . + +RUN cd test_setup && \ + git submodule add https://github.com/google/googletest.git extern/googletest &&\ + git submodule add https://github.com/whoshuu/cpr.git extern/cpr && \ + ldconfig + + +RUN chmod 777 hot-reload-test.sh + +CMD ["./hot-reload-test.sh"] diff --git a/hot-reload-test.sh b/hot-reload-test.sh new file mode 100644 index 0000000..6a31332 --- /dev/null +++ b/hot-reload-test.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +compile_backend() { + echo "Compiling..." + cmake . + make +} + +run_backend() { + ./test & + BACKEND_PID=$! + echo "Running process with PID: $BACKEND_PID" +} + +monitor_changes() { + echo "Monitoring for changes..." + while true; do + inotifywait -r -e modify,move,create,delete ./ + + kill_backend + compile_backend + run_backend + done +} + +kill_backend() { + if [[ -n $BACKEND_PID ]]; then + echo "Killing process with PID: $BACKEND_PID" + kill $BACKEND_PID + wait $BACKEND_PID 2>/dev/null + fi +} + +main() { + cd test_setup/ + compile_backend + run_backend + monitor_changes +} + +main diff --git a/test_setup/CMakeLists.txt b/test_setup/CMakeLists.txt new file mode 100644 index 0000000..e69c109 --- /dev/null +++ b/test_setup/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.18) + +project(backend) + +set(CMAKE_CXX_STANDARD 20) + +# Agrega las opciones de compilación +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + +# Encuentra las bibliotecas necesarias +add_subdirectory(extern/cpr) +add_subdirectory(extern/googletest) + +# Si bcrypt está instalado en un lugar no estándar, puedes usar find_path para encontrar la carpeta de inclusión +# find_path(BCRYPT_INCLUDE_DIR bcrypt/bcrypt.h) + +# Establece las fuentes de tu proyecto +file(GLOB_RECURSE SOURCES "tests/*.cpp") + +# Crea el ejecutable +add_executable(test ${SOURCES}) + +# Agrega las carpetas de inclusión +target_include_directories(test PUBLIC include) + +# Enlaza las bibliotecas necesarias +target_link_libraries(test gtest gtest_main cpr) + +# Si bcrypt está en un lugar no estándar, también agrega el directorio de inclusión +# target_include_directories(backend PUBLIC ${BCRYPT_INCLUDE_DIR}) diff --git a/test_setup/tests/main.cpp b/test_setup/tests/main.cpp new file mode 100644 index 0000000..1c275e2 --- /dev/null +++ b/test_setup/tests/main.cpp @@ -0,0 +1,19 @@ +#include + +#include +// Incluye tus archivos de prueba aquí +// ... + + + +TEST(ddd, a) { + EXPECT_EQ(5+5, 5); +} + + +int main(int argc, char** argv) { + // Inicializa Google Test + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/test_setup/tests/test_one.cpp b/test_setup/tests/test_one.cpp new file mode 100644 index 0000000..fe212b2 --- /dev/null +++ b/test_setup/tests/test_one.cpp @@ -0,0 +1,18 @@ +#include +#include + +int sum(int a, int b) { + return a + b; +} + +TEST(test1, PositiveNumbers) { + EXPECT_EQ(sum(2, 3), 5); +} + +TEST(IntegrationTest, HttpRequest) { + auto response = cpr::Get(cpr::Url{"http://backend:3000/api/users"}); + + + EXPECT_EQ(response.status_code, 200); + +} From f7be679a44a4d1be8184d0282c46b00e54cbee7a Mon Sep 17 00:00:00 2001 From: David Baque Date: Thu, 14 Mar 2024 21:30:35 +0100 Subject: [PATCH 18/48] task #76: add json module --- Dockerfile.test | 1 + test_setup/CMakeLists.txt | 3 ++- test_setup/tests/main.cpp | 12 +----------- test_setup/tests/test_one.cpp | 32 ++++++++++++++++++++++++++------ 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/Dockerfile.test b/Dockerfile.test index 5fb7116..cbb9795 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -7,6 +7,7 @@ WORKDIR /app COPY . . RUN cd test_setup && \ + git submodule add https://github.com/nlohmann/json.git extern/json && \ git submodule add https://github.com/google/googletest.git extern/googletest &&\ git submodule add https://github.com/whoshuu/cpr.git extern/cpr && \ ldconfig diff --git a/test_setup/CMakeLists.txt b/test_setup/CMakeLists.txt index e69c109..eb3dc52 100644 --- a/test_setup/CMakeLists.txt +++ b/test_setup/CMakeLists.txt @@ -10,6 +10,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") # Encuentra las bibliotecas necesarias add_subdirectory(extern/cpr) add_subdirectory(extern/googletest) +add_subdirectory(extern/json) # Si bcrypt está instalado en un lugar no estándar, puedes usar find_path para encontrar la carpeta de inclusión # find_path(BCRYPT_INCLUDE_DIR bcrypt/bcrypt.h) @@ -24,7 +25,7 @@ add_executable(test ${SOURCES}) target_include_directories(test PUBLIC include) # Enlaza las bibliotecas necesarias -target_link_libraries(test gtest gtest_main cpr) +target_link_libraries(test gtest gtest_main cpr nlohmann_json::nlohmann_json) # Si bcrypt está en un lugar no estándar, también agrega el directorio de inclusión # target_include_directories(backend PUBLIC ${BCRYPT_INCLUDE_DIR}) diff --git a/test_setup/tests/main.cpp b/test_setup/tests/main.cpp index 1c275e2..399ac93 100644 --- a/test_setup/tests/main.cpp +++ b/test_setup/tests/main.cpp @@ -1,15 +1,5 @@ -#include - #include -// Incluye tus archivos de prueba aquí -// ... - - - -TEST(ddd, a) { - EXPECT_EQ(5+5, 5); -} - +#include int main(int argc, char** argv) { // Inicializa Google Test diff --git a/test_setup/tests/test_one.cpp b/test_setup/tests/test_one.cpp index fe212b2..76508ba 100644 --- a/test_setup/tests/test_one.cpp +++ b/test_setup/tests/test_one.cpp @@ -1,18 +1,38 @@ -#include #include +#include + +#include // Para std::getenv +#include +#include + +int HTTP_PORT = std::stoi(std::getenv("HTTP_PORT")); int sum(int a, int b) { - return a + b; + return a + b; } TEST(test1, PositiveNumbers) { - EXPECT_EQ(sum(2, 3), 5); + EXPECT_EQ(sum(2, 3), 5); } TEST(IntegrationTest, HttpRequest) { - auto response = cpr::Get(cpr::Url{"http://backend:3000/api/users"}); + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/users"; + auto response = cpr::Get(cpr::Url{url}); - EXPECT_EQ(response.status_code, 200); + EXPECT_EQ(response.status_code, 200); -} + auto json = nlohmann::json::parse(response.text); + + ASSERT_TRUE(json.contains("users")); + ASSERT_TRUE(json["users"].is_array()); + + for (const auto& user : json["users"]) { + ASSERT_TRUE(user.contains("id")); + ASSERT_TRUE(user.contains("image_url")); + ASSERT_TRUE(user.contains("email")); + ASSERT_TRUE(user.contains("username")); + ASSERT_TRUE(user.contains("balance")); + ASSERT_TRUE(user.contains("type")); + } +} \ No newline at end of file From c6fff3aef30594b860dc84a55590c5e783ab83a9 Mon Sep 17 00:00:00 2001 From: David Baque Date: Thu, 14 Mar 2024 23:40:07 +0100 Subject: [PATCH 19/48] task #36: jwt create token --- CMakeLists.txt | 9 ++++++++- Dockerfile.dev | 13 ++++++++----- include/utils/auth.h | 5 +++++ src/controllers/auth_controller.cpp | 7 ++++++- src/utils/auth.cpp | 11 +++++++++++ 5 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 include/utils/auth.h create mode 100644 src/utils/auth.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dc9a0d..a30cc40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,13 @@ find_library(PQXX_LIB pqxx) find_library(PQ_LIB pq) find_library(BCRYPT_LIB bcrypt) +include(FetchContent) +fetchcontent_declare(jwt-cpp + GIT_REPOSITORY https://github.com/Thalhammer/jwt-cpp.git + GIT_TAG 08bcf77a687fb06e34138e9e9fa12a4ecbe12332 # v0.7.0 release +) +set(JWT_BUILD_EXAMPLES OFF CACHE BOOL "disable building examples" FORCE) +fetchcontent_makeavailable(jwt-cpp) # Si bcrypt está instalado en un lugar no estándar, puedes usar find_path para encontrar la carpeta de inclusión # find_path(BCRYPT_INCLUDE_DIR bcrypt/bcrypt.h) @@ -25,7 +32,7 @@ add_executable(backend ${SOURCES}) target_include_directories(backend PUBLIC include) # Enlaza las bibliotecas necesarias -target_link_libraries(backend ${PQXX_LIB} ${PQ_LIB} ${BCRYPT_LIB}) +target_link_libraries(backend ${PQXX_LIB} ${PQ_LIB} ${BCRYPT_LIB} jwt-cpp::jwt-cpp) # Si bcrypt está en un lugar no estándar, también agrega el directorio de inclusión # target_include_directories(backend PUBLIC ${BCRYPT_INCLUDE_DIR}) diff --git a/Dockerfile.dev b/Dockerfile.dev index 6b36567..218d98b 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -10,14 +10,17 @@ RUN dpkg -i crow-v1.0+5.deb COPY . . -RUN git clone https://github.com/trusch/libbcrypt.git && \ - cd libbcrypt && \ +RUN apt install -y nlohmann-json3-dev libgtest-dev libssl-dev + +RUN git clone https://github.com/arun11299/cpp-jwt.git &&\ + git clone https://github.com/trusch/libbcrypt.git && \ + cd libbcrypt && \ mkdir build && \ cd build && \ cmake .. && \ - make && \ - make install && \ - ldconfig + make && \ + make install && \ + ldconfig RUN chmod 777 hot-reload.sh diff --git a/include/utils/auth.h b/include/utils/auth.h new file mode 100644 index 0000000..f03abb9 --- /dev/null +++ b/include/utils/auth.h @@ -0,0 +1,5 @@ +#include + +#include + +std::string create_token(const std::string& userId); \ No newline at end of file diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index 20f0374..2dea4b4 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -1,5 +1,7 @@ #include "controllers/auth_controller.h" +#include + #include #include "bcrypt/BCrypt.hpp" @@ -36,7 +38,10 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re UserModel user = UserModel::create_user(db, hash, email, username, "", 0, type); - crow::json::wvalue data({{"id", user.getId()}}); + std::string token = create_token(user.getId()); + + // crow::json::wvalue data({{"id", user.getId()}}); + crow::json::wvalue data({{"token", token}}); res.code = 200; res.write(data.dump()); res.end(); diff --git a/src/utils/auth.cpp b/src/utils/auth.cpp new file mode 100644 index 0000000..2e5d84a --- /dev/null +++ b/src/utils/auth.cpp @@ -0,0 +1,11 @@ +#include + +std::string create_token(const std::string& userId) { + auto token = jwt::create() + .set_type("JWS") + .set_issuer("auth0") + .set_payload_claim("id", jwt::claim(std::string(userId))) + .sign(jwt::algorithm::hs256{"secret"}); + return token; +} + From dc0b80f1bf80e5cf28a5d26c33012de15f840539 Mon Sep 17 00:00:00 2001 From: David Baque Date: Thu, 14 Mar 2024 23:43:52 +0100 Subject: [PATCH 20/48] task #77: verify token --- include/utils/auth.h | 6 ++++++ src/utils/auth.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 include/utils/auth.h create mode 100644 src/utils/auth.cpp diff --git a/include/utils/auth.h b/include/utils/auth.h new file mode 100644 index 0000000..a1f5a10 --- /dev/null +++ b/include/utils/auth.h @@ -0,0 +1,6 @@ +#include + +#include + +std::string create_token(const std::string& userId); +bool validate_token(const std::string& token); diff --git a/src/utils/auth.cpp b/src/utils/auth.cpp new file mode 100644 index 0000000..f23a395 --- /dev/null +++ b/src/utils/auth.cpp @@ -0,0 +1,26 @@ +#include + +std::string create_token(const std::string& userId) { + auto token = jwt::create() + .set_type("JWS") + .set_issuer("auth0") + .set_payload_claim("id", jwt::claim(std::string(userId))) + .sign(jwt::algorithm::hs256{"secret"}); + return token; +} + +bool validate_token(const std::string& token) { + try { + auto verifier = jwt::verify() + .with_issuer("auth0") + .allow_algorithm(jwt::algorithm::hs256{"secret"}); + auto decoded_token = jwt::decode(token); + + verifier.verify(decoded_token); + + return true; + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + return false; + } +} \ No newline at end of file From b6b8d748b4884c16ab749076a9c424123498191c Mon Sep 17 00:00:00 2001 From: David Baque Date: Fri, 15 Mar 2024 19:33:07 +0100 Subject: [PATCH 21/48] #73: improve user exist --- include/controllers/auth_controller.h | 3 ++- include/models/user_model.h | 3 +-- include/utils/utils.h | 2 +- src/controllers/auth_controller.cpp | 21 +++------------------ src/models/user_model.cpp | 20 ++------------------ src/utils/user_validations.cpp | 8 ++++---- src/utils/utils.cpp | 4 ++-- 7 files changed, 15 insertions(+), 46 deletions(-) diff --git a/include/controllers/auth_controller.h b/include/controllers/auth_controller.h index 9a400ae..9e637ad 100644 --- a/include/controllers/auth_controller.h +++ b/include/controllers/auth_controller.h @@ -1,9 +1,10 @@ #include #include #include - #include "crow.h" + #include "models/user_model.h" +#include class AuthController { public: diff --git a/include/models/user_model.h b/include/models/user_model.h index d28c000..ead2c16 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -26,8 +26,7 @@ class UserModel { std::string getType(); static UserModel create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type); - static bool user_exist(pqxx::connection& db, std::string email); - static bool username_exist(pqxx::connection& db, std::string username); + static bool user_exist(pqxx::connection& db, std::string email, std::string ussername); static std::vector get_users(pqxx::connection& db); }; diff --git a/include/utils/utils.h b/include/utils/utils.h index 9e57e4f..350bb66 100644 --- a/include/utils/utils.h +++ b/include/utils/utils.h @@ -4,4 +4,4 @@ void handle_exception(crow::response &res, const std::string &error_message); -void handle_error(crow::response &res, const std::string &error_message); \ No newline at end of file +void handle_error(crow::response &res, const std::string &error_message, const int &status_code); \ No newline at end of file diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index 2dea4b4..ee02660 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -20,19 +20,8 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re std::string hash = BCrypt::generateHash(password); - if (UserModel::user_exist(db, email)) { - res.code = 400; - crow::json::wvalue error({{"error", "email already exists"}}); - res.write(error.dump()); - res.end(); - return; - } - - if (UserModel::username_exist(db, username)) { - res.code = 400; - crow::json::wvalue error({{"error", "username already exists"}}); - res.write(error.dump()); - res.end(); + if (UserModel::user_exist(db, email, username)) { + handle_error(res, "user alredy exist", 400); return; } @@ -40,16 +29,12 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re std::string token = create_token(user.getId()); - // crow::json::wvalue data({{"id", user.getId()}}); crow::json::wvalue data({{"token", token}}); res.code = 200; res.write(data.dump()); res.end(); } catch (const std::exception &e) { std::cerr << "Error in register_user: " << e.what() << std::endl; - res.code = 500; - crow::json::wvalue error({{"error", "internal server error"}}); - res.write(error.dump()); - res.end(); + handle_error(res, "INTERNAL SERVER ERROR", 500); } } diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 8aa4275..dff306f 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -42,11 +42,11 @@ UserModel UserModel::create_user(pqxx::connection& db, std::string password, std return UserModel(res); } -bool UserModel::user_exist(pqxx::connection& db, std::string email) { +bool UserModel::user_exist(pqxx::connection& db, std::string email, std::string username) { try { pqxx::work txn(db); - pqxx::result result = txn.exec_params("SELECT username FROM users WHERE email = $1", email); + pqxx::result result = txn.exec_params("SELECT username FROM users WHERE email = $1 OR username = $2", email, username); bool userExists = !result.empty() && !result[0][0].is_null(); @@ -58,22 +58,6 @@ bool UserModel::user_exist(pqxx::connection& db, std::string email) { } } -bool UserModel::username_exist(pqxx::connection& db, std::string username) { - try { - pqxx::work txn(db); - - pqxx::result result = txn.exec_params("SELECT email FROM users WHERE username = $1", username); - - bool usernameExists = !result.empty() && !result[0][0].is_null(); - - txn.commit(); - - return usernameExists; - } catch (const std::exception& e) { - return false; - } -} - std::vector UserModel::get_users(pqxx::connection& db) { std::vector all_users; diff --git a/src/utils/user_validations.cpp b/src/utils/user_validations.cpp index daaecd4..8b44275 100644 --- a/src/utils/user_validations.cpp +++ b/src/utils/user_validations.cpp @@ -25,7 +25,7 @@ bool is_correct_body(const crow::request &req, crow::response &res) { bool validate_email(const std::string &email, crow::response &res) { if (!is_correct_email(email)) { - handle_error(res, "incorrect email"); + handle_error(res, "incorrect email", 400); return false; } return true; @@ -33,7 +33,7 @@ bool validate_email(const std::string &email, crow::response &res) { bool validate_type(const std::string &type, crow::response &res) { if (!is_correct_type(type)) { - handle_error(res, "incorrect type user"); + handle_error(res, "incorrect type user", 400); return false; } return true; @@ -41,7 +41,7 @@ bool validate_type(const std::string &type, crow::response &res) { bool validate_password(const std::string &password, crow::response &res) { if (!is_correct_password(password)) { - handle_error(res, "incorrect password"); + handle_error(res, "incorrect password", 400); return false; } return true; @@ -49,7 +49,7 @@ bool validate_password(const std::string &password, crow::response &res) { bool validate_username(const std::string &username, crow::response &res) { if (!is_correct_username(username)) { - handle_error(res, "incorrect username"); + handle_error(res, "incorrect username", 400); return false; } return true; diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 0e362c4..7e2315e 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -7,9 +7,9 @@ void handle_exception(crow::response &res, const std::string &error_message) { res.end(); } -void handle_error(crow::response &res, const std::string &error_message) { +void handle_error(crow::response &res, const std::string &error_message, const int &status_code) { crow::json::wvalue error({{"error", error_message}}); - res.code = 400; + res.code = status_code; res.write(error.dump()); res.end(); } \ No newline at end of file From 109c3cb3ccfbe866363f3c7450db3aed3545cb8a Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Sat, 16 Mar 2024 10:10:21 +0100 Subject: [PATCH 22/48] task #80: added pragma once to all headers --- include/controllers/auth_controller.h | 6 ++++-- include/controllers/test_controller.h | 5 +---- include/controllers/user_controller.h | 4 +++- include/models/test_model.h | 5 +---- include/models/user_model.h | 5 +---- include/routes/auth_routes.h | 4 +++- include/routes/test_routes.h | 2 ++ include/routes/user_routes.h | 6 ++++-- include/utils/auth.h | 2 ++ include/utils/common.h | 5 +---- include/utils/custom_regex.h | 15 ++++++++------- include/utils/user_validations.h | 7 +++++-- include/utils/utils.h | 6 ++++-- include/utils/validations.h | 6 ++++-- 14 files changed, 43 insertions(+), 35 deletions(-) diff --git a/include/controllers/auth_controller.h b/include/controllers/auth_controller.h index 9e637ad..a51a1ba 100644 --- a/include/controllers/auth_controller.h +++ b/include/controllers/auth_controller.h @@ -1,10 +1,12 @@ +#pragma once + #include #include #include -#include "crow.h" +#include "crow.h" #include "models/user_model.h" -#include +#include "utils/utils.h" class AuthController { public: diff --git a/include/controllers/test_controller.h b/include/controllers/test_controller.h index b3274be..315906e 100644 --- a/include/controllers/test_controller.h +++ b/include/controllers/test_controller.h @@ -1,5 +1,4 @@ -#ifndef TEST_CONTROLLER_h -#define TEST_CONTROLLER_h +#pragma once #include #include @@ -10,5 +9,3 @@ class TestController { public: static std::string index(pqxx::connection& db); }; - -#endif \ No newline at end of file diff --git a/include/controllers/user_controller.h b/include/controllers/user_controller.h index 3bbabfe..12187fc 100644 --- a/include/controllers/user_controller.h +++ b/include/controllers/user_controller.h @@ -1,3 +1,5 @@ +#pragma once + #include #include #include @@ -9,4 +11,4 @@ class UserController { public: static void get_users(pqxx::connection& db, const crow::request& req, crow::response& res); -}; \ No newline at end of file +}; diff --git a/include/models/test_model.h b/include/models/test_model.h index 8512341..67a4e02 100644 --- a/include/models/test_model.h +++ b/include/models/test_model.h @@ -1,5 +1,4 @@ -#ifndef TEST_MODEL_h -#define TEST_MODEL_h +#pragma once #include #include @@ -15,5 +14,3 @@ class TestModel { static TestModel timeNow(pqxx::connection& db); }; - -#endif diff --git a/include/models/user_model.h b/include/models/user_model.h index ead2c16..3c2a8a0 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -1,5 +1,4 @@ -#ifndef USER_MODEL_h -#define USER_MODEL_h +#pragma once #include #include @@ -29,5 +28,3 @@ class UserModel { static bool user_exist(pqxx::connection& db, std::string email, std::string ussername); static std::vector get_users(pqxx::connection& db); }; - -#endif diff --git a/include/routes/auth_routes.h b/include/routes/auth_routes.h index 9aa5be6..ab42174 100644 --- a/include/routes/auth_routes.h +++ b/include/routes/auth_routes.h @@ -1,7 +1,9 @@ +#pragma once + #include -#include "crow.h" #include "controllers/auth_controller.h" +#include "crow.h" #include "utils/common.h" void initialize_auth_routes(NebyApp& app, pqxx::connection& db); diff --git a/include/routes/test_routes.h b/include/routes/test_routes.h index 2373c1d..56ac65c 100644 --- a/include/routes/test_routes.h +++ b/include/routes/test_routes.h @@ -1,3 +1,5 @@ +#pragma once + #include #include "controllers/test_controller.h" diff --git a/include/routes/user_routes.h b/include/routes/user_routes.h index e3c1457..f3c3f1c 100644 --- a/include/routes/user_routes.h +++ b/include/routes/user_routes.h @@ -1,7 +1,9 @@ +#pragma once + #include -#include "crow.h" #include "controllers/user_controller.h" +#include "crow.h" #include "utils/common.h" -void initialize_user_routes(NebyApp& app, pqxx::connection& db); \ No newline at end of file +void initialize_user_routes(NebyApp& app, pqxx::connection& db); diff --git a/include/utils/auth.h b/include/utils/auth.h index a1f5a10..88d7a40 100644 --- a/include/utils/auth.h +++ b/include/utils/auth.h @@ -1,3 +1,5 @@ +#pragma once + #include #include diff --git a/include/utils/common.h b/include/utils/common.h index 7f26622..1ac754b 100644 --- a/include/utils/common.h +++ b/include/utils/common.h @@ -1,13 +1,10 @@ -// common.h #pragma once -#include #include #include #include "bcrypt/BCrypt.hpp" - -// ** middleawares includes +#include "crow.h" #include "middlewares/user_validation_middleware.h" using NebyApp = crow::App; diff --git a/include/utils/custom_regex.h b/include/utils/custom_regex.h index f936b2f..b79a63f 100644 --- a/include/utils/custom_regex.h +++ b/include/utils/custom_regex.h @@ -1,16 +1,17 @@ -// custom_regex.h -#include -#include +#pragma once + +#include + #include #include +#include #include - -#include +#include bool is_correct_email(std::string email); -bool is_correct_type(std::string type) ; +bool is_correct_type(std::string type); bool is_correct_password(std::string password); -bool is_correct_username(std::string username); \ No newline at end of file +bool is_correct_username(std::string username); diff --git a/include/utils/user_validations.h b/include/utils/user_validations.h index 24cf9a8..d0020c6 100644 --- a/include/utils/user_validations.h +++ b/include/utils/user_validations.h @@ -1,4 +1,5 @@ -#include +#pragma once + #include #include #include @@ -6,6 +7,8 @@ #include +#include "crow.h" + bool is_correct_body(const crow::request &req, crow::response &res); bool validate_email(const std::string &email, crow::response &res); @@ -14,4 +17,4 @@ bool validate_type(const std::string &type, crow::response &res); bool validate_password(const std::string &password, crow::response &res); -bool validate_username(const std::string &username, crow::response &res); \ No newline at end of file +bool validate_username(const std::string &username, crow::response &res); diff --git a/include/utils/utils.h b/include/utils/utils.h index 350bb66..f6ad346 100644 --- a/include/utils/utils.h +++ b/include/utils/utils.h @@ -1,7 +1,9 @@ -#include +#pragma once #include +#include "crow.h" + void handle_exception(crow::response &res, const std::string &error_message); -void handle_error(crow::response &res, const std::string &error_message, const int &status_code); \ No newline at end of file +void handle_error(crow::response &res, const std::string &error_message, const int &status_code); diff --git a/include/utils/validations.h b/include/utils/validations.h index c023f91..ccc31c7 100644 --- a/include/utils/validations.h +++ b/include/utils/validations.h @@ -1,3 +1,5 @@ -#include +#pragma once -bool validate_required_body_fields(const crow::json::rvalue &body, const std::vector &required_fields, crow::response &res); \ No newline at end of file +#include "crow.h" + +bool validate_required_body_fields(const crow::json::rvalue &body, const std::vector &required_fields, crow::response &res); From fa2e77c39195544a3537d854a8ff8ee51d6c49f9 Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Sat, 16 Mar 2024 10:34:42 +0100 Subject: [PATCH 23/48] task #80: removed test routes, controller and model --- include/controllers/test_controller.h | 11 ----------- include/models/test_model.h | 16 ---------------- include/routes/test_routes.h | 9 --------- src/controllers/test_controller.cpp | 10 ---------- src/main.cpp | 5 ----- src/models/test_model.cpp | 15 --------------- src/routes/test_routes.cpp | 10 ---------- 7 files changed, 76 deletions(-) delete mode 100644 include/controllers/test_controller.h delete mode 100644 include/models/test_model.h delete mode 100644 include/routes/test_routes.h delete mode 100644 src/controllers/test_controller.cpp delete mode 100644 src/models/test_model.cpp delete mode 100644 src/routes/test_routes.cpp diff --git a/include/controllers/test_controller.h b/include/controllers/test_controller.h deleted file mode 100644 index 315906e..0000000 --- a/include/controllers/test_controller.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include - -#include "models/test_model.h" - -class TestController { - public: - static std::string index(pqxx::connection& db); -}; diff --git a/include/models/test_model.h b/include/models/test_model.h deleted file mode 100644 index 67a4e02..0000000 --- a/include/models/test_model.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include - -class TestModel { - private: - std::string time; - - public: - TestModel(std::string time); - - std::string getTime(); - - static TestModel timeNow(pqxx::connection& db); -}; diff --git a/include/routes/test_routes.h b/include/routes/test_routes.h deleted file mode 100644 index 56ac65c..0000000 --- a/include/routes/test_routes.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -#include "controllers/test_controller.h" -#include "crow.h" -#include "utils/common.h" - -void initialize_test_routes(NebyApp& app, pqxx::connection& db); diff --git a/src/controllers/test_controller.cpp b/src/controllers/test_controller.cpp deleted file mode 100644 index 8d92fd8..0000000 --- a/src/controllers/test_controller.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "controllers/test_controller.h" - -std::string TestController::index(pqxx::connection& db) { - try { - TestModel time = TestModel::timeNow(db); - return std::format("Data from database, actual time: {}", time.getTime()); - } catch (const std::exception& e) { - return std::format("Error: {}", e.what()); - } -} diff --git a/src/main.cpp b/src/main.cpp index 66e324c..95b75e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,9 +6,7 @@ #include "crow.h" #include "routes/auth_routes.h" -#include "routes/test_routes.h" #include "routes/user_routes.h" - #include "utils/common.h" int main() { @@ -32,9 +30,6 @@ int main() { } initialize_auth_routes(app, conn); - - initialize_test_routes(app, conn); - initialize_user_routes(app, conn); app.port(HTTP_PORT).multithreaded().run(); diff --git a/src/models/test_model.cpp b/src/models/test_model.cpp deleted file mode 100644 index 38f35d2..0000000 --- a/src/models/test_model.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "models/test_model.h" - -TestModel::TestModel(std::string time) : time(time) {} - -std::string TestModel::getTime() { - return time; -} - -TestModel TestModel::timeNow(pqxx::connection& db) { - pqxx::work txn(db); - pqxx::result result = txn.exec("SELECT NOW()"); - std::string res = result[0][0].as(); - txn.commit(); - return TestModel(res); -} diff --git a/src/routes/test_routes.cpp b/src/routes/test_routes.cpp deleted file mode 100644 index ecfb882..0000000 --- a/src/routes/test_routes.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "routes/test_routes.h" - -void initialize_test_routes(NebyApp& app, pqxx::connection& db) { - CROW_ROUTE(app, "/api/test") - ([&db](const crow::request& req, crow::response& res) { - res.code = 200; - res.body = TestController::index(db); - res.end(); - }); -} From ca3919bc5be7c7d4ab65684d23a8bb7477f630b0 Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Sat, 16 Mar 2024 10:56:18 +0100 Subject: [PATCH 24/48] task #80: applied formatting --- CMakeLists.txt | 4 ++-- .../middlewares/user_validation_middleware.h | 3 +-- src/routes/auth_routes.cpp | 10 ++++----- src/routes/user_routes.cpp | 2 +- src/utils/common.cpp | 2 +- src/utils/custom_regex.cpp | 2 +- src/utils/utils.cpp | 2 +- src/utils/validations.cpp | 22 +++++++++---------- test_setup/tests/main.cpp | 11 +++++----- test_setup/tests/test_one.cpp | 2 +- 10 files changed, 29 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a30cc40..b5ab52d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,8 +14,8 @@ find_library(BCRYPT_LIB bcrypt) include(FetchContent) fetchcontent_declare(jwt-cpp - GIT_REPOSITORY https://github.com/Thalhammer/jwt-cpp.git - GIT_TAG 08bcf77a687fb06e34138e9e9fa12a4ecbe12332 # v0.7.0 release + GIT_REPOSITORY https://github.com/Thalhammer/jwt-cpp.git + GIT_TAG 08bcf77a687fb06e34138e9e9fa12a4ecbe12332 # v0.7.0 release ) set(JWT_BUILD_EXAMPLES OFF CACHE BOOL "disable building examples" FORCE) fetchcontent_makeavailable(jwt-cpp) diff --git a/include/middlewares/user_validation_middleware.h b/include/middlewares/user_validation_middleware.h index a7d1f50..a9127ed 100644 --- a/include/middlewares/user_validation_middleware.h +++ b/include/middlewares/user_validation_middleware.h @@ -1,7 +1,6 @@ #pragma once -#include - +#include "crow.h" #include "utils/user_validations.h" struct SignupValidation : crow::ILocalMiddleware { diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index 9c3ef3a..eb79e34 100644 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -2,12 +2,10 @@ #include "middlewares/user_validation_middleware.h" - void initialize_auth_routes(NebyApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/auth/register") - .methods(crow::HTTPMethod::POST) - .CROW_MIDDLEWARES(app, SignupValidation)([&db](const crow::request& req, crow::response& res) { - AuthController::register_user(db, req, res); - - }); + .methods(crow::HTTPMethod::POST) + .CROW_MIDDLEWARES(app, SignupValidation)([&db](const crow::request& req, crow::response& res) { + AuthController::register_user(db, req, res); + }); } diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp index a9d4a61..09f3622 100644 --- a/src/routes/user_routes.cpp +++ b/src/routes/user_routes.cpp @@ -1,7 +1,7 @@ #include "routes/user_routes.h" void initialize_user_routes(NebyApp& app, pqxx::connection& db) { - CROW_ROUTE(app, "/api/users").methods("GET"_method)([&db](const crow::request& req, crow::response& res) { + CROW_ROUTE(app, "/api/users").methods(crow::HTTPMethod::GET)([&db](const crow::request& req, crow::response& res) { UserController::get_users(db, req, res); }); } diff --git a/src/utils/common.cpp b/src/utils/common.cpp index 2a0292c..c106064 100644 --- a/src/utils/common.cpp +++ b/src/utils/common.cpp @@ -1,4 +1,4 @@ #include "utils/common.h" const std::string Roles::ADMIN = "admin"; -const std::string Roles::NEIGHBOR = "neighbor"; \ No newline at end of file +const std::string Roles::NEIGHBOR = "neighbor"; diff --git a/src/utils/custom_regex.cpp b/src/utils/custom_regex.cpp index 957f52d..b5a4ac7 100644 --- a/src/utils/custom_regex.cpp +++ b/src/utils/custom_regex.cpp @@ -64,4 +64,4 @@ bool is_correct_username(std::string username) { return false; } return true; -} \ No newline at end of file +} diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 7e2315e..09c88c8 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -12,4 +12,4 @@ void handle_error(crow::response &res, const std::string &error_message, const i res.code = status_code; res.write(error.dump()); res.end(); -} \ No newline at end of file +} diff --git a/src/utils/validations.cpp b/src/utils/validations.cpp index 6ef90e5..8e83bdd 100644 --- a/src/utils/validations.cpp +++ b/src/utils/validations.cpp @@ -1,15 +1,15 @@ #include bool validate_required_body_fields(const crow::json::rvalue &body, const std::vector &required_fields, crow::response &res) { - for (const auto &field : required_fields) { - if (!body.has(field)) { - crow::json::wvalue error({{"error", "missing " + field + " field"}}); - res.code = 404; - res.write(error.dump()); - res.end(); - return false; - } - } + for (const auto &field : required_fields) { + if (!body.has(field)) { + crow::json::wvalue error({{"error", "missing " + field + " field"}}); + res.code = 404; + res.write(error.dump()); + res.end(); + return false; + } + } - return true; -} \ No newline at end of file + return true; +} diff --git a/test_setup/tests/main.cpp b/test_setup/tests/main.cpp index 399ac93..926358d 100644 --- a/test_setup/tests/main.cpp +++ b/test_setup/tests/main.cpp @@ -1,9 +1,10 @@ -#include #include +#include + int main(int argc, char** argv) { - // Inicializa Google Test - ::testing::InitGoogleTest(&argc, argv); + // Inicializa Google Test + ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file + return RUN_ALL_TESTS(); +} diff --git a/test_setup/tests/test_one.cpp b/test_setup/tests/test_one.cpp index 76508ba..4033981 100644 --- a/test_setup/tests/test_one.cpp +++ b/test_setup/tests/test_one.cpp @@ -35,4 +35,4 @@ TEST(IntegrationTest, HttpRequest) { ASSERT_TRUE(user.contains("balance")); ASSERT_TRUE(user.contains("type")); } -} \ No newline at end of file +} From 363c794682d476760a4ad0ce0b98a0271c4e3ab3 Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Sat, 16 Mar 2024 11:31:37 +0100 Subject: [PATCH 25/48] task #80: renamed test directory --- Dockerfile.test | 2 +- hot-reload-test.sh | 2 +- {test_setup => test}/CMakeLists.txt | 0 {test_setup => test}/tests/main.cpp | 0 {test_setup => test}/tests/test_one.cpp | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename {test_setup => test}/CMakeLists.txt (100%) rename {test_setup => test}/tests/main.cpp (100%) rename {test_setup => test}/tests/test_one.cpp (100%) diff --git a/Dockerfile.test b/Dockerfile.test index cbb9795..415fe88 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -6,7 +6,7 @@ WORKDIR /app COPY . . -RUN cd test_setup && \ +RUN cd test && \ git submodule add https://github.com/nlohmann/json.git extern/json && \ git submodule add https://github.com/google/googletest.git extern/googletest &&\ git submodule add https://github.com/whoshuu/cpr.git extern/cpr && \ diff --git a/hot-reload-test.sh b/hot-reload-test.sh index 6a31332..f716b72 100644 --- a/hot-reload-test.sh +++ b/hot-reload-test.sh @@ -32,7 +32,7 @@ kill_backend() { } main() { - cd test_setup/ + cd test/ compile_backend run_backend monitor_changes diff --git a/test_setup/CMakeLists.txt b/test/CMakeLists.txt similarity index 100% rename from test_setup/CMakeLists.txt rename to test/CMakeLists.txt diff --git a/test_setup/tests/main.cpp b/test/tests/main.cpp similarity index 100% rename from test_setup/tests/main.cpp rename to test/tests/main.cpp diff --git a/test_setup/tests/test_one.cpp b/test/tests/test_one.cpp similarity index 100% rename from test_setup/tests/test_one.cpp rename to test/tests/test_one.cpp From d41f8d45e431dcf826442d53c12f4816c9c3dffb Mon Sep 17 00:00:00 2001 From: David Baque Date: Sun, 17 Mar 2024 22:02:16 +0100 Subject: [PATCH 26/48] task #74: generilze-register-controller --- include/controllers/auth_controller.h | 3 ++ include/models/community_model.h | 27 ++++++++++ include/models/user_model.h | 1 + include/utils/user_validations.h | 2 +- src/controllers/auth_controller.cpp | 20 +++++++- src/models/community_model.cpp | 72 +++++++++++++++++++++++++++ src/models/user_model.cpp | 13 +++++ src/utils/user_validations.cpp | 16 +++++- 8 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 include/models/community_model.h create mode 100644 src/models/community_model.cpp diff --git a/include/controllers/auth_controller.h b/include/controllers/auth_controller.h index a51a1ba..5c0c5ad 100644 --- a/include/controllers/auth_controller.h +++ b/include/controllers/auth_controller.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include #include #include diff --git a/include/models/community_model.h b/include/models/community_model.h new file mode 100644 index 0000000..f11a325 --- /dev/null +++ b/include/models/community_model.h @@ -0,0 +1,27 @@ +#ifndef COMMUNITY_MODEL_H +#define COMMUNITY_MODEL_H + +#include +#include +#include + +class CommunityModel { + private: + std::string _id; + std::string _community_name; + std::string _community_code; + + public: + CommunityModel(std::string id, std::string community_name, std::string community_code); + + std::string get_id() const; + std::string get_community_name() const; + std::string get_community_code() const; + + static std::string generate_community_code(); + + static CommunityModel create_community(pqxx::connection& db, std::string community_name); + static std::string get_community_id(pqxx::connection &db, std::string community_code); +}; + +#endif \ No newline at end of file diff --git a/include/models/user_model.h b/include/models/user_model.h index 3c2a8a0..4d8b4c4 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -27,4 +27,5 @@ class UserModel { static UserModel create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type); static bool user_exist(pqxx::connection& db, std::string email, std::string ussername); static std::vector get_users(pqxx::connection& db); + static void set_community_id(pqxx::connection& db, std::string community_id, std::string user_id); }; diff --git a/include/utils/user_validations.h b/include/utils/user_validations.h index d0020c6..bf227ec 100644 --- a/include/utils/user_validations.h +++ b/include/utils/user_validations.h @@ -9,7 +9,7 @@ #include "crow.h" -bool is_correct_body(const crow::request &req, crow::response &res); +bool is_correct_body_register(const crow::request &req, crow::response &res); bool validate_email(const std::string &email, crow::response &res); diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index ee02660..c009c10 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -9,7 +9,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &req, crow::response &res) { try { - if (!is_correct_body(req, res)) return; + if (!is_correct_body_register(req, res)) return; crow::json::rvalue body = crow::json::load(req.body); @@ -27,6 +27,24 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re UserModel user = UserModel::create_user(db, hash, email, username, "", 0, type); + if (type == Roles::ADMIN) { + // ! check if community exist with name + std::string community_name = body["community_name"].s(); + CommunityModel community = CommunityModel::create_community(db, community_name); + UserModel::set_community_id(db, community.get_id(), user.getId()); + + } else if (type == Roles::NEIGHBOR) { + // ! check if community exist with code + std::string community_code = body["community_code"].s(); + // ! TODO: + + std::string community_id = CommunityModel::get_community_id(db, community_code); + if (community_id == "") { + handle_error(res, "not community exist", 404); + } + UserModel::set_community_id(db, community_id, user.getId()); + } + std::string token = create_token(user.getId()); crow::json::wvalue data({{"token", token}}); diff --git a/src/models/community_model.cpp b/src/models/community_model.cpp new file mode 100644 index 0000000..92696b8 --- /dev/null +++ b/src/models/community_model.cpp @@ -0,0 +1,72 @@ +#include + +CommunityModel::CommunityModel(std::string id, std::string community_name, std::string community_code) : _id(id), _community_name(community_name), _community_code(community_code) { +} + +std::string CommunityModel::generate_community_code() { + const std::string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + const int codeLength = 8; + std::string code; + + std::srand(std::time(nullptr)); + + for (int i = 0; i < codeLength; ++i) { + code += charset[std::rand() % charset.size()]; + } + + return code; +} + +CommunityModel CommunityModel::create_community(pqxx::connection& db, std::string community_name) { + pqxx::work txn(db); + + std::string community_code = generate_community_code(); + + txn.exec_params("INSERT INTO communities (community_name, community_code) VALUES ($1, $2)", community_name, community_code); + + pqxx::result result = txn.exec_params("SELECT id FROM communities WHERE community_code = $1", community_code); + + std::string res = result[0][0].as(); + + txn.commit(); + + return CommunityModel(res, community_code, community_name); +} + +std::string CommunityModel::get_community_id(pqxx::connection& db, std::string community_code) { + try { + pqxx::work txn(db); + + // Construir la consulta SQL parametrizada para obtener el ID de la comunidad dado su código + std::string query = "SELECT id FROM communities WHERE community_code = $1"; + + // Ejecutar la consulta parametrizada + pqxx::result result = txn.exec_params(query, community_code); + + // Comprobar si se encontró la comunidad + if (!result.empty() && !result[0]["id"].is_null()) { + std::string community_id = result[0]["id"].as(); + txn.commit(); + return community_id; + } else { + // Si no se encuentra la comunidad, devolver un valor nulo o lanzar una excepción según sea necesario + txn.commit(); + return ""; // O cualquier otro valor que desees para indicar que la comunidad no se encontró + } + } catch (const std::exception& e) { + // Manejar cualquier excepción que ocurra durante la ejecución de la consulta + return ""; // O lanzar una excepción adecuada según sea necesario + } +} + +std::string CommunityModel::get_id() const { + return _id; +} + +std::string CommunityModel::get_community_name() const { + return _community_name; +} + +std::string CommunityModel::get_community_code() const { + return _community_code; +} \ No newline at end of file diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index dff306f..1993ad9 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -75,3 +75,16 @@ std::vector UserModel::get_users(pqxx::connection& db) { return all_users; } + +void UserModel::set_community_id(pqxx::connection &db , std::string community_id, std::string user_id) { + try { + pqxx::work txn(db); + + // Actualizar el community_id del usuario en la base de datos + txn.exec_params("UPDATE users SET community_id = $1 WHERE id = $2", community_id, user_id); + + txn.commit(); + } catch (const std::exception& e) { + std::cerr << "Error al establecer el community_id para el usuario " << user_id << ": " << e.what() << std::endl; + } +} \ No newline at end of file diff --git a/src/utils/user_validations.cpp b/src/utils/user_validations.cpp index 8b44275..c39964e 100644 --- a/src/utils/user_validations.cpp +++ b/src/utils/user_validations.cpp @@ -1,6 +1,6 @@ #include "utils/user_validations.h" -bool is_correct_body(const crow::request &req, crow::response &res) { +bool is_correct_body_register(const crow::request &req, crow::response &res) { try { crow::json::rvalue body = crow::json::load(req.body); @@ -15,6 +15,20 @@ bool is_correct_body(const crow::request &req, crow::response &res) { return false; } + if (body["type"].s() == Roles::ADMIN) { + const std::vector required_field = {"community_name"}; + if (!validate_required_body_fields(body, required_field, res)) { + handle_error(res, "missing community_name", 404); + return false; + } + } else if (body["type"] == Roles::NEIGHBOR) { + const std::vector required_field = {"community_code"}; + if (!validate_required_body_fields(body, required_field, res)) { + handle_error(res, "missing community_code", 404); + return false; + } + } + return true; } catch (const std::exception &e) { From ad571a11dc9de0f53644a3394a13cfbf5dd73b8d Mon Sep 17 00:00:00 2001 From: David Baque Date: Mon, 18 Mar 2024 18:07:04 +0100 Subject: [PATCH 27/48] task #49: create login route --- include/controllers/auth_controller.h | 2 + include/models/user_model.h | 4 ++ include/utils/auth.h | 2 +- include/utils/common.h | 3 +- include/utils/user_validations.h | 2 + src/controllers/auth_controller.cpp | 61 +++++++++++++++++++++++++-- src/models/user_model.cpp | 48 ++++++++++++++++++++- src/routes/auth_routes.cpp | 5 +++ src/utils/auth.cpp | 10 +++-- src/utils/user_validations.cpp | 18 ++++++++ 10 files changed, 146 insertions(+), 9 deletions(-) diff --git a/include/controllers/auth_controller.h b/include/controllers/auth_controller.h index 5c0c5ad..304c392 100644 --- a/include/controllers/auth_controller.h +++ b/include/controllers/auth_controller.h @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -14,4 +15,5 @@ class AuthController { public: static void register_user(pqxx::connection& db, const crow::request& req, crow::response& res); + static void login_user(pqxx::connection& db, const crow::request& req, crow::response& res); }; diff --git a/include/models/user_model.h b/include/models/user_model.h index 4d8b4c4..a73242a 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -28,4 +29,7 @@ class UserModel { static bool user_exist(pqxx::connection& db, std::string email, std::string ussername); static std::vector get_users(pqxx::connection& db); static void set_community_id(pqxx::connection& db, std::string community_id, std::string user_id); + + static std::unique_ptr get_user_by_email(pqxx::connection& db, std::string email); + static std::string get_password_by_email(pqxx::connection& db, std::string email); }; diff --git a/include/utils/auth.h b/include/utils/auth.h index 88d7a40..9d99913 100644 --- a/include/utils/auth.h +++ b/include/utils/auth.h @@ -4,5 +4,5 @@ #include -std::string create_token(const std::string& userId); +std::string create_token(const std::string& userId, const std::string& type); bool validate_token(const std::string& token); diff --git a/include/utils/common.h b/include/utils/common.h index 1ac754b..568d735 100644 --- a/include/utils/common.h +++ b/include/utils/common.h @@ -5,9 +5,10 @@ #include "bcrypt/BCrypt.hpp" #include "crow.h" +#include "crow/middlewares/cookie_parser.h" #include "middlewares/user_validation_middleware.h" -using NebyApp = crow::App; +using NebyApp = crow::App; struct Roles { static const std::string ADMIN; diff --git a/include/utils/user_validations.h b/include/utils/user_validations.h index bf227ec..853dbd8 100644 --- a/include/utils/user_validations.h +++ b/include/utils/user_validations.h @@ -11,6 +11,8 @@ bool is_correct_body_register(const crow::request &req, crow::response &res); +bool is_correct_body_login(const crow::request &req, crow::response &res); + bool validate_email(const std::string &email, crow::response &res); bool validate_type(const std::string &type, crow::response &res); diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index c009c10..fc05480 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -41,14 +41,23 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re std::string community_id = CommunityModel::get_community_id(db, community_code); if (community_id == "") { handle_error(res, "not community exist", 404); + return; } UserModel::set_community_id(db, community_id, user.getId()); } - std::string token = create_token(user.getId()); + std::string token = create_token(user.getId(), type); - crow::json::wvalue data({{"token", token}}); - res.code = 200; + std::string token_cookie = "token=" + token; + + res.add_header("Set-Cookie", token_cookie); + + crow::json::wvalue data; + data["user"] = { + {"id", user.getId()}, + }; + + res.code = 201; res.write(data.dump()); res.end(); } catch (const std::exception &e) { @@ -56,3 +65,49 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re handle_error(res, "INTERNAL SERVER ERROR", 500); } } + +void AuthController::login_user(pqxx::connection &db, const crow::request &req, crow::response &res) { + try { + if (!is_correct_body_login(req, res)) return; + + crow::json::rvalue body = crow::json::load(req.body); + + std::string email = body["email"].s(); + + std::unique_ptr user = UserModel::get_user_by_email(db, email); + + if (!user) { + handle_error(res, "user by email not exist", 404); + return; + } + + const std::string encrypt_password = UserModel::get_password_by_email(db, email); + + std::string password = body["password"].s(); + + if (BCrypt::validatePassword(password, encrypt_password)) { + std::string token = create_token(user.get()->getId(), user.get()->getType()); + + std::string token_cookie = "token=" + token; + + res.add_header("Set-Cookie", token_cookie); + + crow::json::wvalue data; + data["user"] = { + {"id", user.get()->getId()}, + {"type", user.get()->getType()}}; + + res.code = 200; + res.write(data.dump()); + + res.end(); + } else { + handle_error(res, "password invalid", 401); + return; + } + + } catch (const std::exception &e) { + std::cerr << "Error in register_user: " << e.what() << std::endl; + handle_error(res, "INTERNAL SERVER ERROR", 500); + } +} diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 1993ad9..3a538a7 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -76,7 +76,7 @@ std::vector UserModel::get_users(pqxx::connection& db) { return all_users; } -void UserModel::set_community_id(pqxx::connection &db , std::string community_id, std::string user_id) { +void UserModel::set_community_id(pqxx::connection& db, std::string community_id, std::string user_id) { try { pqxx::work txn(db); @@ -87,4 +87,50 @@ void UserModel::set_community_id(pqxx::connection &db , std::string community_id } catch (const std::exception& e) { std::cerr << "Error al establecer el community_id para el usuario " << user_id << ": " << e.what() << std::endl; } +} + +std::unique_ptr UserModel::get_user_by_email(pqxx::connection& db, std::string email) { + try { + pqxx::work txn(db); + + pqxx::result result = txn.exec_params("SELECT id, email, username, image_url, balance, type FROM users WHERE email = $1", email); + + txn.commit(); + + // Si no se encontró ningún usuario con ese correo electrónico, devolver nullptr + if (result.empty()) + return nullptr; + + // Si se encontró un usuario, crear una instancia de UserModel y devolverla + pqxx::row row = result[0]; + return std::make_unique(row["id"].as(), + row["email"].as(), + row["username"].as(), + row["image_url"].as(), + row["balance"].as(), + row["type"].as()); + } catch (const std::exception& e) { + std::cerr << "Error al obtener el usuario por correo electrónico: " << e.what() << std::endl; + return nullptr; + } +} + +std::string UserModel::get_password_by_email(pqxx::connection& db, std::string email) { + try { + pqxx::work txn(db); + + pqxx::result result = txn.exec_params("SELECT password FROM users WHERE email = $1", email); + + txn.commit(); + + // Si no se encontró ningún usuario con ese correo electrónico, devolver una cadena vacía + if (result.empty()) + return ""; + + // Si se encontró un usuario, devolver su contraseña + return result[0]["password"].as(); + } catch (const std::exception& e) { + std::cerr << "Error al obtener la contraseña por correo electrónico: " << e.what() << std::endl; + return ""; + } } \ No newline at end of file diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index eb79e34..1d5bcc3 100644 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -8,4 +8,9 @@ void initialize_auth_routes(NebyApp& app, pqxx::connection& db) { .CROW_MIDDLEWARES(app, SignupValidation)([&db](const crow::request& req, crow::response& res) { AuthController::register_user(db, req, res); }); + + CROW_ROUTE(app, "/api/auth/login") + .methods(crow::HTTPMethod::POST)([&db](const crow::request& req, crow::response& res) { + AuthController::login_user(db, req, res); + }); } diff --git a/src/utils/auth.cpp b/src/utils/auth.cpp index 8f782a4..b108519 100644 --- a/src/utils/auth.cpp +++ b/src/utils/auth.cpp @@ -1,11 +1,15 @@ #include -std::string create_token(const std::string& userId) { +std::string SECRET_JWT = std::string(std::getenv("SECRET_JWT")); + +std::string create_token(const std::string& userId, const std::string& type) { auto token = jwt::create() .set_type("JWS") .set_issuer("auth0") .set_payload_claim("id", jwt::claim(std::string(userId))) - .sign(jwt::algorithm::hs256{"secret"}); + .set_payload_claim("type", jwt::claim(std::string(type))) + .sign(jwt::algorithm::hs256{SECRET_JWT}); + return token; } @@ -13,7 +17,7 @@ bool validate_token(const std::string& token) { try { auto verifier = jwt::verify() .with_issuer("auth0") - .allow_algorithm(jwt::algorithm::hs256{"secret"}); + .allow_algorithm(jwt::algorithm::hs256{SECRET_JWT}); auto decoded_token = jwt::decode(token); verifier.verify(decoded_token); diff --git a/src/utils/user_validations.cpp b/src/utils/user_validations.cpp index c39964e..d122147 100644 --- a/src/utils/user_validations.cpp +++ b/src/utils/user_validations.cpp @@ -1,5 +1,23 @@ #include "utils/user_validations.h" +bool is_correct_body_login(const crow::request &req, crow::response &res) { + try { + crow::json::rvalue body = crow::json::load(req.body); + + const std::vector required_fields = {"email", "password"}; + + if (!validate_required_body_fields(body, required_fields, res)) return false; + + if (!validate_email(body["email"].s(), res) || !validate_password(body["password"].s(), res)) + return false; + + return true; + } catch (const std::exception &e) { + handle_exception(res, "login validation data error"); + return false; + } +} + bool is_correct_body_register(const crow::request &req, crow::response &res) { try { crow::json::rvalue body = crow::json::load(req.body); From 2123cc44e5284ea2851abb378e0240cf0409b746 Mon Sep 17 00:00:00 2001 From: Mampiz Date: Tue, 19 Mar 2024 18:50:48 +0100 Subject: [PATCH 28/48] task #84: get user id acabado --- include/controllers/user_controller.h | 1 + include/models/user_model.h | 1 + src/controllers/user_controller.cpp | 32 +++++++++++++++++++++++++++ src/models/user_model.cpp | 13 ++++++++++- src/routes/user_routes.cpp | 4 ++++ 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/include/controllers/user_controller.h b/include/controllers/user_controller.h index 12187fc..875c732 100644 --- a/include/controllers/user_controller.h +++ b/include/controllers/user_controller.h @@ -11,4 +11,5 @@ class UserController { public: static void get_users(pqxx::connection& db, const crow::request& req, crow::response& res); + static void get_user_by_id(pqxx::connection& db, const crow::request& req, const std::string& user_id, crow::response& res); }; diff --git a/include/models/user_model.h b/include/models/user_model.h index a73242a..dce0882 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -32,4 +32,5 @@ class UserModel { static std::unique_ptr get_user_by_email(pqxx::connection& db, std::string email); static std::string get_password_by_email(pqxx::connection& db, std::string email); + static UserModel get_user_by_id(pqxx::connection& db, const std::string& id); }; diff --git a/src/controllers/user_controller.cpp b/src/controllers/user_controller.cpp index e969dc3..343dc60 100644 --- a/src/controllers/user_controller.cpp +++ b/src/controllers/user_controller.cpp @@ -26,3 +26,35 @@ void UserController::get_users(pqxx::connection &db, const crow::request &req, c res.end(); } } + +void UserController::get_user_by_id(pqxx::connection &db, const crow::request &req, const std::string &user_id, crow::response &res) { + try { + if (user_id.empty()) { + res.code = 400; + crow::json::wvalue error({{"error", "id doesn't exist"}}); + res.write(error.dump()); + res.end(); + return; + } + + UserModel user = UserModel::get_user_by_id(db, user_id); + + crow::json::wvalue user_data; + user_data["id"] = user.getId(); + user_data["email"] = user.getEmail(); + user_data["username"] = user.getUsername(); + user_data["image_url"] = user.getImageUrl(); + user_data["balance"] = user.getBalance(); + user_data["type"] = user.getType(); + + crow::json::wvalue data{{"user", user_data}}; + res.code = 200; + res.write(data.dump()); + res.end(); + + } catch (const std::exception &e) { + std::cerr << "Error: " << e.what() << std::endl; + res.code = 500; + res.end(); + } +} diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 3a538a7..e2e7a77 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -133,4 +133,15 @@ std::string UserModel::get_password_by_email(pqxx::connection& db, std::string e std::cerr << "Error al obtener la contraseña por correo electrónico: " << e.what() << std::endl; return ""; } -} \ No newline at end of file +} + +UserModel UserModel::get_user_by_id(pqxx::connection& db, const std::string& id) { + pqxx::work txn(db); + pqxx::result result = txn.exec_params("SELECT id, email, username, image_url, balance, type FROM users WHERE id = $1", id); + + const auto& row = result[0]; + UserModel user(row["id"].as(), row["email"].as(), row["username"].as(), row["image_url"].as(), row["balance"].as(), row["type"].as()); + + txn.commit(); + return user; +} diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp index 09f3622..27c1b3b 100644 --- a/src/routes/user_routes.cpp +++ b/src/routes/user_routes.cpp @@ -4,4 +4,8 @@ void initialize_user_routes(NebyApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/users").methods(crow::HTTPMethod::GET)([&db](const crow::request& req, crow::response& res) { UserController::get_users(db, req, res); }); + + CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::GET)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { + UserController::get_user_by_id(db, req, user_id, res); + }); } From e16c8887d18f7c627f3c6e7fe98b443b8d1833bd Mon Sep 17 00:00:00 2001 From: Mampiz Date: Tue, 19 Mar 2024 21:15:29 +0100 Subject: [PATCH 29/48] task #84: get user by id improved --- include/controllers/user_controller.h | 3 +++ include/models/user_model.h | 4 ++++ include/utils/errors.h | 14 ++++++++++++++ src/controllers/user_controller.cpp | 15 +++++++++++---- src/models/user_model.cpp | 4 +--- src/utils/errors.cpp | 8 ++++++++ 6 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 include/utils/errors.h create mode 100644 src/utils/errors.cpp diff --git a/include/controllers/user_controller.h b/include/controllers/user_controller.h index 875c732..fe2ce03 100644 --- a/include/controllers/user_controller.h +++ b/include/controllers/user_controller.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include #include #include diff --git a/include/models/user_model.h b/include/models/user_model.h index dce0882..84cec59 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -1,5 +1,9 @@ #pragma once +#include "utils/errors.h" + +#include + #include #include #include diff --git a/include/utils/errors.h b/include/utils/errors.h new file mode 100644 index 0000000..1611b1b --- /dev/null +++ b/include/utils/errors.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +class data_not_found_exception : public std::exception { + private: + std::string _message; + + public: + data_not_found_exception(const std::string& msg); + virtual const char* what() const noexcept override; +}; \ No newline at end of file diff --git a/src/controllers/user_controller.cpp b/src/controllers/user_controller.cpp index 343dc60..4e778fc 100644 --- a/src/controllers/user_controller.cpp +++ b/src/controllers/user_controller.cpp @@ -52,9 +52,16 @@ void UserController::get_user_by_id(pqxx::connection &db, const crow::request &r res.write(data.dump()); res.end(); - } catch (const std::exception &e) { - std::cerr << "Error: " << e.what() << std::endl; - res.code = 500; - res.end(); + } catch (const pqxx::data_exception &e) { + handle_error(res, "invalid id", 400); + } + + catch (const data_not_found_exception &e) { + handle_error(res, e.what(), 404); + } + + catch (const std::exception &e) { + handle_error(res, e.what(), 500); } } + diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index e2e7a77..883250a 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -1,7 +1,5 @@ #include "models/user_model.h" -#include - UserModel::UserModel(std::string id) : _id(id) {} UserModel::UserModel(std::string id, std::string email, std::string username, std::string image_url, int balance, std::string type) : _id(id), _email(email), _username(username), _image_url(image_url), _balance(balance), _type(type) {} @@ -138,7 +136,7 @@ std::string UserModel::get_password_by_email(pqxx::connection& db, std::string e UserModel UserModel::get_user_by_id(pqxx::connection& db, const std::string& id) { pqxx::work txn(db); pqxx::result result = txn.exec_params("SELECT id, email, username, image_url, balance, type FROM users WHERE id = $1", id); - + if(result.empty()) throw data_not_found_exception("user doesn't exist"); const auto& row = result[0]; UserModel user(row["id"].as(), row["email"].as(), row["username"].as(), row["image_url"].as(), row["balance"].as(), row["type"].as()); diff --git a/src/utils/errors.cpp b/src/utils/errors.cpp new file mode 100644 index 0000000..8b37087 --- /dev/null +++ b/src/utils/errors.cpp @@ -0,0 +1,8 @@ +#include + +data_not_found_exception::data_not_found_exception(const std::string& msg) : _message(msg) {} + +const char* data_not_found_exception::what() const noexcept { + return _message.c_str(); +} + From 41ea4205646449ddd066673b2453a443b3d05035 Mon Sep 17 00:00:00 2001 From: Mampiz Date: Tue, 19 Mar 2024 21:23:25 +0100 Subject: [PATCH 30/48] task #86: delete user by id completed --- include/controllers/user_controller.h | 1 + include/models/user_model.h | 1 + src/controllers/user_controller.cpp | 22 ++++++++++++++++++++++ src/models/user_model.cpp | 15 +++++++++++++++ src/routes/user_routes.cpp | 4 ++++ 5 files changed, 43 insertions(+) diff --git a/include/controllers/user_controller.h b/include/controllers/user_controller.h index fe2ce03..97a0a6e 100644 --- a/include/controllers/user_controller.h +++ b/include/controllers/user_controller.h @@ -15,4 +15,5 @@ class UserController { public: static void get_users(pqxx::connection& db, const crow::request& req, crow::response& res); static void get_user_by_id(pqxx::connection& db, const crow::request& req, const std::string& user_id, crow::response& res); + static void delete_user_by_id(pqxx::connection& db, const std::string& user_id, crow::response& res); }; diff --git a/include/models/user_model.h b/include/models/user_model.h index 84cec59..ddd5ed6 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -37,4 +37,5 @@ class UserModel { static std::unique_ptr get_user_by_email(pqxx::connection& db, std::string email); static std::string get_password_by_email(pqxx::connection& db, std::string email); static UserModel get_user_by_id(pqxx::connection& db, const std::string& id); + static bool delete_by_id(pqxx::connection& db, const std::string& id); }; diff --git a/src/controllers/user_controller.cpp b/src/controllers/user_controller.cpp index 4e778fc..8880b6a 100644 --- a/src/controllers/user_controller.cpp +++ b/src/controllers/user_controller.cpp @@ -65,3 +65,25 @@ void UserController::get_user_by_id(pqxx::connection &db, const crow::request &r } } +void UserController::delete_user_by_id(pqxx::connection &db, const std::string &user_id, crow::response &res) { + try { + bool elimin = UserModel::delete_by_id(db, user_id); + + if (elimin) { + res.code = 200; + crow::json::wvalue response_message; + response_message["message"] = "User deleted successfully"; + res.write(response_message.dump()); + } else { + res.code = 404; + crow::json::wvalue error_message; + error_message["error"] = "User not found"; + res.write(error_message.dump()); + } + res.end(); + } catch (const std::exception &e) { + std::cerr << "Error deleting user: " << e.what() << std::endl; + res.code = 500; + res.end(); + } +} diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 883250a..9c34240 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -143,3 +143,18 @@ UserModel UserModel::get_user_by_id(pqxx::connection& db, const std::string& id) txn.commit(); return user; } + +bool UserModel::delete_by_id(pqxx::connection &db, const std::string& id) { + try { + pqxx::work txn(db); + + pqxx::result result = txn.exec_params("DELETE FROM users WHERE id = $1", id); + + txn.commit(); + + return true; + } catch (const std::exception &e) { + std::cerr << "Failed to delete user: " << e.what() << std::endl; + return false; + } +} diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp index 27c1b3b..9803313 100644 --- a/src/routes/user_routes.cpp +++ b/src/routes/user_routes.cpp @@ -8,4 +8,8 @@ void initialize_user_routes(NebyApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::GET)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { UserController::get_user_by_id(db, req, user_id, res); }); + + CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::DELETE)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { + UserController::delete_user_by_id(db, user_id, res); + }); } From adc3bbed4fc0872ad60963a495ca10f4ea23187b Mon Sep 17 00:00:00 2001 From: David Baque Date: Wed, 20 Mar 2024 13:04:00 +0100 Subject: [PATCH 31/48] add cookie token --- .../middlewares/user_validation_middleware.h | 12 ----- include/middlewares/verify_jwt.h | 47 +++++++++++++++++ include/models/notification_mdel.h | 35 +++++++++++++ include/models/service_model.h | 38 ++++++++++++++ include/routes/auth_routes.h | 5 +- include/utils/auth.h | 4 ++ include/utils/common.h | 18 +++++-- src/controllers/auth_controller.cpp | 51 ++++++++++++++++--- src/middlewares/verify_jwt.cpp | 0 src/routes/auth_routes.cpp | 12 ++--- src/utils/auth.cpp | 18 +++++++ src/utils/common.cpp | 7 +++ 12 files changed, 216 insertions(+), 31 deletions(-) delete mode 100644 include/middlewares/user_validation_middleware.h create mode 100644 include/middlewares/verify_jwt.h create mode 100644 include/models/notification_mdel.h create mode 100644 include/models/service_model.h create mode 100644 src/middlewares/verify_jwt.cpp diff --git a/include/middlewares/user_validation_middleware.h b/include/middlewares/user_validation_middleware.h deleted file mode 100644 index a9127ed..0000000 --- a/include/middlewares/user_validation_middleware.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "crow.h" -#include "utils/user_validations.h" - -struct SignupValidation : crow::ILocalMiddleware { - struct context {}; - - void before_handle(crow::request& req, crow::response& res, context& ctx) {} - - void after_handle(crow::request& req, crow::response& res, context& ctx) {} -}; diff --git a/include/middlewares/verify_jwt.h b/include/middlewares/verify_jwt.h new file mode 100644 index 0000000..2fedecd --- /dev/null +++ b/include/middlewares/verify_jwt.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +struct VerifyJWT : crow::ILocalMiddleware { + struct context {}; + + void before_handle(crow::request& req, crow::response& res, context& ctx) { + crow::json::wvalue body = crow::json::load(req.body); + + std::string token = get_token_cookie(req); + + if (!validate_token(token)) { + handle_error(res, "invalid token", 401); + return; + } + + auto decoded = jwt::decode(token); + + std::string id; + std::string type; + + // Acceder al payload del token decodificado + for (auto& e : decoded.get_payload_json()) { + std::cout << e.first << " = " << e.second << std::endl; + if (e.first == "id") { + id = e.second.get(); + } else if (e.first == "type") { + type = e.second.get(); + } + } + + body["id"] = id; + if (type == "admin") + + body["isAdmin"] = true; + else + body["isAdmin"] = false; + + req.body = body.dump(); + } + + void after_handle(crow::request& req, crow::response& res, context& ctx) {} +}; diff --git a/include/models/notification_mdel.h b/include/models/notification_mdel.h new file mode 100644 index 0000000..32949b2 --- /dev/null +++ b/include/models/notification_mdel.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +class NotificationModel { + private: + std::string _id; + std::string _sender_id; + std::string _receiver_id; + std::string _service_id; + std::string _status; + std::string _created_at; + std::string _updated_at; + + public: + NotificationModel(std::string id, std::string sender_id, std::string receiver_id, std::string service_id, std::string status); + + std::string get_id(); + std::string get_sender_id(); + std::string get_receiver_id(); + std::string get_service_id(); + std::string get_status(); + +}; + +/* CREATE TABLE IF NOT EXISTS notifications ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + sender_id UUID REFERENCES users(id), + receiver_id UUID REFERENCES users(id), + service_id UUID REFERENCES services(id), + status notification_status DEFAULT 'PENDING', -- PENDING, ACCEPTED, REFUSED + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + */ \ No newline at end of file diff --git a/include/models/service_model.h b/include/models/service_model.h new file mode 100644 index 0000000..0e816d4 --- /dev/null +++ b/include/models/service_model.h @@ -0,0 +1,38 @@ +#pragma once + +class ServiceModel { + private: + std::string _id; + std::string _creator_id; + std::string _title; + std::string _description; + int _price; + std::string _status; + std::string _buyer_user_id; + + public: + std::string get_id(); + std::string get_creator_id(); + std::string get_title(); + std::string get_description(); + std::string get_price(); + std::string get_status(); + std::string get_buyer_user_id(); + + ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string buyer_user_id = ""); + + // * static ServiceModel create_notification(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price); +}; + +/* +CREATE TABLE IF NOT EXISTS services ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + creator_id UUID REFERENCES users(id), + service_name VARCHAR(255) NOT NULL, + description TEXT, + price NUMERIC(10, 2), + status service_status DEFAULT 'OPEN', -- Estado del servicio: 'OPEN' o 'CLOSED' + usuario_venta_id UUID, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); */ diff --git a/include/routes/auth_routes.h b/include/routes/auth_routes.h index ab42174..e057d68 100644 --- a/include/routes/auth_routes.h +++ b/include/routes/auth_routes.h @@ -1,9 +1,10 @@ #pragma once +#include "crow.h" #include -#include "controllers/auth_controller.h" -#include "crow.h" #include "utils/common.h" +#include "controllers/auth_controller.h" +#include void initialize_auth_routes(NebyApp& app, pqxx::connection& db); diff --git a/include/utils/auth.h b/include/utils/auth.h index 9d99913..e065f99 100644 --- a/include/utils/auth.h +++ b/include/utils/auth.h @@ -1,8 +1,12 @@ #pragma once +#include #include #include std::string create_token(const std::string& userId, const std::string& type); + bool validate_token(const std::string& token); + +std::string get_token_cookie(crow::request& req); diff --git a/include/utils/common.h b/include/utils/common.h index 568d735..2bb8565 100644 --- a/include/utils/common.h +++ b/include/utils/common.h @@ -1,16 +1,26 @@ #pragma once +#include +#include #include #include #include "bcrypt/BCrypt.hpp" -#include "crow.h" -#include "crow/middlewares/cookie_parser.h" -#include "middlewares/user_validation_middleware.h" -using NebyApp = crow::App; +using NebyApp = crow::App; struct Roles { static const std::string ADMIN; static const std::string NEIGHBOR; }; + +struct NotificationStatus { + static const std::string PENDING; + static const std::string ACCEPTED; + static const std::string REFUSED; +}; + +struct ServiceStatus { + static const std::string OPEN; + static const std::string CLOSED; +}; diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index fc05480..1ef0539 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -2,6 +2,8 @@ #include +#include // Include the ctime header for time functions +#include #include #include "bcrypt/BCrypt.hpp" @@ -46,11 +48,23 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re UserModel::set_community_id(db, community_id, user.getId()); } - std::string token = create_token(user.getId(), type); + std::string jwtToken = create_token(user.getId(), type); + int expirationTimeSeconds = 3600; + time_t now = time(0); + time_t expirationTime = now + expirationTimeSeconds; - std::string token_cookie = "token=" + token; + tm *expiration_tm = gmtime(&expirationTime); + char expirationTimeStr[128]; + strftime(expirationTimeStr, 128, "%a, %d %b %Y %H:%M:%S GMT", expiration_tm); - res.add_header("Set-Cookie", token_cookie); + std::ostringstream cookieStream; + cookieStream << "token=" << jwtToken << "; "; + cookieStream << "Max-Age=" << expirationTimeSeconds << "; "; + cookieStream << "Expires=" << expirationTimeStr << "; "; + cookieStream << "Path=/api; "; + cookieStream << "Secure"; + + res.set_header("Set-Cookie", cookieStream.str()); crow::json::wvalue data; data["user"] = { @@ -68,8 +82,18 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re void AuthController::login_user(pqxx::connection &db, const crow::request &req, crow::response &res) { try { + /* crow::json::rvalue body = crow::json::load(req.body); + + std::string id = body["id"].s(); + + crow::json::wvalue data({{"id", id}}); + + res.code = 200; + res.write(data.dump()); + + res.end(); */ if (!is_correct_body_login(req, res)) return; - + crow::json::rvalue body = crow::json::load(req.body); std::string email = body["email"].s(); @@ -86,11 +110,24 @@ void AuthController::login_user(pqxx::connection &db, const crow::request &req, std::string password = body["password"].s(); if (BCrypt::validatePassword(password, encrypt_password)) { - std::string token = create_token(user.get()->getId(), user.get()->getType()); + std::string jwtToken = create_token(user.get()->getId(), user.get()->getType()); + + int expirationTimeSeconds = 3600; + time_t now = time(0); + time_t expirationTime = now + expirationTimeSeconds; + + tm *expiration_tm = gmtime(&expirationTime); + char expirationTimeStr[128]; + strftime(expirationTimeStr, 128, "%a, %d %b %Y %H:%M:%S GMT", expiration_tm); - std::string token_cookie = "token=" + token; + std::ostringstream cookieStream; + cookieStream << "token=" << jwtToken << "; "; + cookieStream << "Max-Age=" << expirationTimeSeconds << "; "; + cookieStream << "Expires=" << expirationTimeStr << "; "; + cookieStream << "Path=/api; "; + cookieStream << "Secure"; - res.add_header("Set-Cookie", token_cookie); + res.set_header("Set-Cookie", cookieStream.str()); crow::json::wvalue data; data["user"] = { diff --git a/src/middlewares/verify_jwt.cpp b/src/middlewares/verify_jwt.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index 1d5bcc3..31f5257 100644 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -1,16 +1,16 @@ -#include "routes/auth_routes.h" - -#include "middlewares/user_validation_middleware.h" +#include void initialize_auth_routes(NebyApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/auth/register") - .methods(crow::HTTPMethod::POST) - .CROW_MIDDLEWARES(app, SignupValidation)([&db](const crow::request& req, crow::response& res) { + .methods(crow::HTTPMethod::POST)([&db](const crow::request& req, crow::response& res) { AuthController::register_user(db, req, res); }); CROW_ROUTE(app, "/api/auth/login") - .methods(crow::HTTPMethod::POST)([&db](const crow::request& req, crow::response& res) { + .methods(crow::HTTPMethod::POST) + .CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res) { AuthController::login_user(db, req, res); }); } + +// ** middleware .CROW_MIDDLEWARES(app, VerifyJWT) diff --git a/src/utils/auth.cpp b/src/utils/auth.cpp index b108519..0162e3c 100644 --- a/src/utils/auth.cpp +++ b/src/utils/auth.cpp @@ -28,3 +28,21 @@ bool validate_token(const std::string& token) { return false; } } + +std::string get_token_cookie( crow::request& req) { + std::string tokenValue; + + const auto& cookiesHeader = req.get_header_value("Cookie"); + + std::istringstream iss(cookiesHeader); + std::string cookie; + while (std::getline(iss, cookie, ';')) { + size_t pos = cookie.find("token="); + if (pos != std::string::npos) { + tokenValue = cookie.substr(pos + 6); + return tokenValue; + } + } + + return ""; +} \ No newline at end of file diff --git a/src/utils/common.cpp b/src/utils/common.cpp index c106064..4f7aab2 100644 --- a/src/utils/common.cpp +++ b/src/utils/common.cpp @@ -2,3 +2,10 @@ const std::string Roles::ADMIN = "admin"; const std::string Roles::NEIGHBOR = "neighbor"; + +const std::string NotificationStatus::PENDING = "PENDING"; +const std::string NotificationStatus::ACCEPTED = "ACCEPTED"; +const std::string NotificationStatus::REFUSED = "REFUSED"; + +const std::string ServiceStatus::CLOSED = "CLOSED"; +const std::string ServiceStatus::OPEN = "OPEN"; From 080d0a049678622fac4db19e9cd41350629be06b Mon Sep 17 00:00:00 2001 From: David Baque Date: Thu, 21 Mar 2024 15:48:51 +0100 Subject: [PATCH 32/48] create service with test data --- include/controllers/service_controller.h | 21 ++++++++++++ include/middlewares/verify_jwt.h | 26 ++++++++++----- include/models/service_model.h | 9 ++++- include/routes/service_routes.h | 11 +++++++ include/routes/user_routes.h | 2 ++ src/controllers/service_controller.cpp | 25 ++++++++++++++ src/main.cpp | 3 ++ src/models/notification_model.cpp | 0 src/models/service_model.cpp | 42 ++++++++++++++++++++++++ src/routes/auth_routes.cpp | 2 +- src/routes/service_routes.cpp | 17 ++++++++++ src/routes/user_routes.cpp | 2 +- src/utils/auth.cpp | 7 ++-- 13 files changed, 152 insertions(+), 15 deletions(-) create mode 100644 include/controllers/service_controller.h create mode 100644 include/routes/service_routes.h create mode 100644 src/controllers/service_controller.cpp create mode 100644 src/models/notification_model.cpp create mode 100644 src/models/service_model.cpp create mode 100644 src/routes/service_routes.cpp diff --git a/include/controllers/service_controller.h b/include/controllers/service_controller.h new file mode 100644 index 0000000..664118f --- /dev/null +++ b/include/controllers/service_controller.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class ServiceController { + public: + /* static void get_users(pqxx::connection& db, const crow::request& req, crow::response& res); + static void get_user_by_id(pqxx::connection& db, const crow::request& req, const std::string& user_id, crow::response& res); + static void delete_user_by_id(pqxx::connection& db, const std::string& user_id, crow::response& res); */ + + static void create_service(pqxx::connection &db, const crow::request &req, crow::response &res); +}; \ No newline at end of file diff --git a/include/middlewares/verify_jwt.h b/include/middlewares/verify_jwt.h index 2fedecd..be9e821 100644 --- a/include/middlewares/verify_jwt.h +++ b/include/middlewares/verify_jwt.h @@ -9,10 +9,10 @@ struct VerifyJWT : crow::ILocalMiddleware { struct context {}; void before_handle(crow::request& req, crow::response& res, context& ctx) { - crow::json::wvalue body = crow::json::load(req.body); - std::string token = get_token_cookie(req); + std::cout << "token -> " << token << std::endl; + if (!validate_token(token)) { handle_error(res, "invalid token", 401); return; @@ -25,7 +25,6 @@ struct VerifyJWT : crow::ILocalMiddleware { // Acceder al payload del token decodificado for (auto& e : decoded.get_payload_json()) { - std::cout << e.first << " = " << e.second << std::endl; if (e.first == "id") { id = e.second.get(); } else if (e.first == "type") { @@ -33,14 +32,23 @@ struct VerifyJWT : crow::ILocalMiddleware { } } - body["id"] = id; - if (type == "admin") + std::cout << "holaa -> " << req.body << std::endl; + + if (req.body == "") { + crow::json::wvalue body; - body["isAdmin"] = true; - else - body["isAdmin"] = false; + body["id"] = id; + body["isAdmin"] = (type == "admin"); - req.body = body.dump(); + req.body = body.dump(); + } else { + crow::json::wvalue body = crow::json::load(req.body); + + body["id"] = id; + body["isAdmin"] = (type == "admin"); + + req.body = body.dump(); + } } void after_handle(crow::request& req, crow::response& res, context& ctx) {} diff --git a/include/models/service_model.h b/include/models/service_model.h index 0e816d4..d2f809d 100644 --- a/include/models/service_model.h +++ b/include/models/service_model.h @@ -1,5 +1,10 @@ #pragma once +#include +#include +#include +#include + class ServiceModel { private: std::string _id; @@ -15,12 +20,14 @@ class ServiceModel { std::string get_creator_id(); std::string get_title(); std::string get_description(); - std::string get_price(); + int get_price(); std::string get_status(); std::string get_buyer_user_id(); ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string buyer_user_id = ""); + static std::unique_ptr create_service(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price, bool isThrow = false); + // * static ServiceModel create_notification(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price); }; diff --git a/include/routes/service_routes.h b/include/routes/service_routes.h new file mode 100644 index 0000000..ea06bb4 --- /dev/null +++ b/include/routes/service_routes.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +void initialize_service_routes(NebyApp& app, pqxx::connection& db); \ No newline at end of file diff --git a/include/routes/user_routes.h b/include/routes/user_routes.h index f3c3f1c..df8d130 100644 --- a/include/routes/user_routes.h +++ b/include/routes/user_routes.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include "controllers/user_controller.h" diff --git a/src/controllers/service_controller.cpp b/src/controllers/service_controller.cpp new file mode 100644 index 0000000..ce40b50 --- /dev/null +++ b/src/controllers/service_controller.cpp @@ -0,0 +1,25 @@ +#include + +void ServiceController::create_service(pqxx::connection &db, const crow::request &req, crow::response &res) { + try { + crow::json::rvalue body = crow::json::load(req.body); + + std::string service_creator_id = body["id"].s(); + + std::unique_ptr service = ServiceModel::create_service(db, service_creator_id, "service_1", "description_1", 20); + + if (!service) { + handle_error(res, "service error controller", 400); + return; + } + + res.code = 201; + res.body = "todo ok"; + res.end(); + } catch (const pqxx::data_exception &e) { + handle_error(res, "invalid properties to create service", 400); + + } catch (const std::exception &e) { + handle_error(res, e.what(), 500); + } +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 95b75e0..4469716 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -31,6 +33,7 @@ int main() { initialize_auth_routes(app, conn); initialize_user_routes(app, conn); + initialize_service_routes(app, conn); app.port(HTTP_PORT).multithreaded().run(); conn.disconnect(); diff --git a/src/models/notification_model.cpp b/src/models/notification_model.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/models/service_model.cpp b/src/models/service_model.cpp new file mode 100644 index 0000000..b93277d --- /dev/null +++ b/src/models/service_model.cpp @@ -0,0 +1,42 @@ +#include + +ServiceModel::ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string buyer_user_id) : _id(id), _creator_id(creator_id), _title(title), _description(description), _price(price), _status(status), _buyer_user_id(buyer_user_id) {} + +std::string ServiceModel::get_id() { return _id; } +std::string ServiceModel::get_creator_id() { return _creator_id; } +std::string ServiceModel::get_title() { return _title; } +std::string ServiceModel::get_description() { return _description; } +int ServiceModel::get_price() { return _price; } +std::string ServiceModel::get_status() { return _status; } +std::string ServiceModel::get_buyer_user_id() { return _buyer_user_id; } + +// * ------------------------------------------------------------------- + +std::unique_ptr ServiceModel::create_service(pqxx::connection& db, std::string creator_id, std::string title, std::string description, int price, bool isThrow) { + pqxx::work txn(db); + + // Ejecutar la consulta para insertar el nuevo servicio en la base de datos + pqxx::result result = txn.exec_params("INSERT INTO services (creator_id, title, description, price) VALUES ($1, $2, $3, $4) RETURNING id", + creator_id, + title, + description, + price); + + // Commit de la transacción + txn.commit(); + + // Verificar si la inserción fue exitosa + if (result.empty()) { + if (isThrow) + throw data_not_found_exception("error create service model"); + + else + return nullptr; + } + + // Obtener el ID del servicio insertado + std::string service_id = result[0]["id"].as(); + + // Crear una instancia de ServiceModel y devolverla + return std::make_unique(service_id, creator_id, title, description, price, "OPEN"); +} diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index 31f5257..bef6148 100644 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -8,7 +8,7 @@ void initialize_auth_routes(NebyApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/auth/login") .methods(crow::HTTPMethod::POST) - .CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res) { + ([&db](const crow::request& req, crow::response& res) { AuthController::login_user(db, req, res); }); } diff --git a/src/routes/service_routes.cpp b/src/routes/service_routes.cpp new file mode 100644 index 0000000..020dab2 --- /dev/null +++ b/src/routes/service_routes.cpp @@ -0,0 +1,17 @@ +#include + +void initialize_service_routes(NebyApp& app, pqxx::connection& db) { + // ** GET /api/services + + // ** GET /api/services/:id + + // ** POST /api/services + + CROW_ROUTE(app, "/api/services").methods(crow::HTTPMethod::POST).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res) { + ServiceController::create_service(db, req, res); + }); + + // ** PUT /api/services/:id + + // ** DELETE /api/services/:id +} \ No newline at end of file diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp index 9803313..64cf81e 100644 --- a/src/routes/user_routes.cpp +++ b/src/routes/user_routes.cpp @@ -1,7 +1,7 @@ #include "routes/user_routes.h" void initialize_user_routes(NebyApp& app, pqxx::connection& db) { - CROW_ROUTE(app, "/api/users").methods(crow::HTTPMethod::GET)([&db](const crow::request& req, crow::response& res) { + CROW_ROUTE(app, "/api/users").methods(crow::HTTPMethod::GET).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res) { UserController::get_users(db, req, res); }); diff --git a/src/utils/auth.cpp b/src/utils/auth.cpp index 0162e3c..7da0597 100644 --- a/src/utils/auth.cpp +++ b/src/utils/auth.cpp @@ -29,18 +29,19 @@ bool validate_token(const std::string& token) { } } -std::string get_token_cookie( crow::request& req) { +std::string get_token_cookie(crow::request& req) { std::string tokenValue; const auto& cookiesHeader = req.get_header_value("Cookie"); std::istringstream iss(cookiesHeader); + std::string cookie; while (std::getline(iss, cookie, ';')) { size_t pos = cookie.find("token="); if (pos != std::string::npos) { - tokenValue = cookie.substr(pos + 6); - return tokenValue; + tokenValue = cookie.substr(pos + 6); + return tokenValue; } } From c6b893b87faf28bd9db618c36178932053d501bb Mon Sep 17 00:00:00 2001 From: David Baque Date: Thu, 21 Mar 2024 16:59:21 +0100 Subject: [PATCH 33/48] fix bug: register creates neighbor without community --- include/models/user_model.h | 1 + src/controllers/auth_controller.cpp | 6 +++++- src/models/user_model.cpp | 24 +++++++++++++----------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/models/user_model.h b/include/models/user_model.h index ddd5ed6..8573f27 100644 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -19,6 +19,7 @@ class UserModel { std::string _type; public: + UserModel(); UserModel(std::string id); UserModel(std::string id, std::string email, std::string username, std::string image_url, int balance, std::string type); diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index 1ef0539..3520f18 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -27,12 +27,15 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re return; } - UserModel user = UserModel::create_user(db, hash, email, username, "", 0, type); + // UserModel user = UserModel::create_user(db, hash, email, username, "", 0, type); + + UserModel user; if (type == Roles::ADMIN) { // ! check if community exist with name std::string community_name = body["community_name"].s(); CommunityModel community = CommunityModel::create_community(db, community_name); + user = UserModel::create_user(db, hash, email, username, "", 0, type); UserModel::set_community_id(db, community.get_id(), user.getId()); } else if (type == Roles::NEIGHBOR) { @@ -45,6 +48,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re handle_error(res, "not community exist", 404); return; } + user = UserModel::create_user(db, hash, email, username, "", 0, type); UserModel::set_community_id(db, community_id, user.getId()); } diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 9c34240..2a91bbb 100644 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -2,6 +2,8 @@ UserModel::UserModel(std::string id) : _id(id) {} +UserModel::UserModel() : _id("") {} + UserModel::UserModel(std::string id, std::string email, std::string username, std::string image_url, int balance, std::string type) : _id(id), _email(email), _username(username), _image_url(image_url), _balance(balance), _type(type) {} std::string UserModel::getId() { @@ -136,7 +138,7 @@ std::string UserModel::get_password_by_email(pqxx::connection& db, std::string e UserModel UserModel::get_user_by_id(pqxx::connection& db, const std::string& id) { pqxx::work txn(db); pqxx::result result = txn.exec_params("SELECT id, email, username, image_url, balance, type FROM users WHERE id = $1", id); - if(result.empty()) throw data_not_found_exception("user doesn't exist"); + if (result.empty()) throw data_not_found_exception("user doesn't exist"); const auto& row = result[0]; UserModel user(row["id"].as(), row["email"].as(), row["username"].as(), row["image_url"].as(), row["balance"].as(), row["type"].as()); @@ -144,17 +146,17 @@ UserModel UserModel::get_user_by_id(pqxx::connection& db, const std::string& id) return user; } -bool UserModel::delete_by_id(pqxx::connection &db, const std::string& id) { - try { - pqxx::work txn(db); +bool UserModel::delete_by_id(pqxx::connection& db, const std::string& id) { + try { + pqxx::work txn(db); - pqxx::result result = txn.exec_params("DELETE FROM users WHERE id = $1", id); + pqxx::result result = txn.exec_params("DELETE FROM users WHERE id = $1", id); - txn.commit(); + txn.commit(); - return true; - } catch (const std::exception &e) { - std::cerr << "Failed to delete user: " << e.what() << std::endl; - return false; - } + return true; + } catch (const std::exception& e) { + std::cerr << "Failed to delete user: " << e.what() << std::endl; + return false; + } } From 8a76304a64bd827c28e545357c9e8ca8c77a03b4 Mon Sep 17 00:00:00 2001 From: David Baque Date: Thu, 21 Mar 2024 17:28:40 +0100 Subject: [PATCH 34/48] task #90: create service --- include/controllers/service_controller.h | 10 +++++++--- include/middlewares/verify_jwt.h | 4 ---- include/models/service_model.h | 9 ++++++--- include/utils/common.h | 5 +++++ src/controllers/service_controller.cpp | 23 +++++++++++++++++++++-- src/models/service_model.cpp | 16 ++++++---------- src/utils/common.cpp | 3 +++ 7 files changed, 48 insertions(+), 22 deletions(-) diff --git a/include/controllers/service_controller.h b/include/controllers/service_controller.h index 664118f..8b3ce32 100644 --- a/include/controllers/service_controller.h +++ b/include/controllers/service_controller.h @@ -1,9 +1,6 @@ #pragma once #include -#include -#include -#include #include #include @@ -11,6 +8,13 @@ #include #include +// ** custom includes +#include +#include +#include +#include +// ** --------------------------------------------- + class ServiceController { public: /* static void get_users(pqxx::connection& db, const crow::request& req, crow::response& res); diff --git a/include/middlewares/verify_jwt.h b/include/middlewares/verify_jwt.h index be9e821..29513a6 100644 --- a/include/middlewares/verify_jwt.h +++ b/include/middlewares/verify_jwt.h @@ -11,8 +11,6 @@ struct VerifyJWT : crow::ILocalMiddleware { void before_handle(crow::request& req, crow::response& res, context& ctx) { std::string token = get_token_cookie(req); - std::cout << "token -> " << token << std::endl; - if (!validate_token(token)) { handle_error(res, "invalid token", 401); return; @@ -32,8 +30,6 @@ struct VerifyJWT : crow::ILocalMiddleware { } } - std::cout << "holaa -> " << req.body << std::endl; - if (req.body == "") { crow::json::wvalue body; diff --git a/include/models/service_model.h b/include/models/service_model.h index d2f809d..bf8226c 100644 --- a/include/models/service_model.h +++ b/include/models/service_model.h @@ -1,9 +1,10 @@ #pragma once +#include + #include #include #include -#include class ServiceModel { private: @@ -13,6 +14,7 @@ class ServiceModel { std::string _description; int _price; std::string _status; + std::string _type; std::string _buyer_user_id; public: @@ -22,11 +24,12 @@ class ServiceModel { std::string get_description(); int get_price(); std::string get_status(); + std::string get_type(); std::string get_buyer_user_id(); - ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string buyer_user_id = ""); + ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string type, std::string buyer_user_id = ""); - static std::unique_ptr create_service(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price, bool isThrow = false); + static std::unique_ptr create_service(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price, std::string type, bool isThrow = false); // * static ServiceModel create_notification(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price); }; diff --git a/include/utils/common.h b/include/utils/common.h index 2bb8565..bd13bd4 100644 --- a/include/utils/common.h +++ b/include/utils/common.h @@ -24,3 +24,8 @@ struct ServiceStatus { static const std::string OPEN; static const std::string CLOSED; }; + +struct ServiceType { + static const std::string OFFERED; + static const std::string REQUESTED; +}; \ No newline at end of file diff --git a/src/controllers/service_controller.cpp b/src/controllers/service_controller.cpp index ce40b50..b38a16c 100644 --- a/src/controllers/service_controller.cpp +++ b/src/controllers/service_controller.cpp @@ -4,17 +4,36 @@ void ServiceController::create_service(pqxx::connection &db, const crow::request try { crow::json::rvalue body = crow::json::load(req.body); + const std::vector required_fields = {"title", "description", "price", "id", "type"}; + + if (!validate_required_body_fields(body, required_fields, res)) return; + + std::string title = body["title"].s(); + std::string description = body["description"].s(); + int price = body["price"].i(); + std::string type = body["type"].s(); + std::string service_creator_id = body["id"].s(); - std::unique_ptr service = ServiceModel::create_service(db, service_creator_id, "service_1", "description_1", 20); + std::unique_ptr service = ServiceModel::create_service(db, service_creator_id, title, description, price, type); if (!service) { handle_error(res, "service error controller", 400); return; } + crow::json::wvalue data({ + {"type", service.get()->get_type()}, + {"status", service.get()->get_status()}, + {"price", service.get()->get_price()}, + {"creator_id", service.get()->get_creator_id()}, + {"description", service.get()->get_description()}, + {"title", service.get()->get_title()}, + {"id", service.get()->get_id()}, + }); + + res.write(data.dump()); res.code = 201; - res.body = "todo ok"; res.end(); } catch (const pqxx::data_exception &e) { handle_error(res, "invalid properties to create service", 400); diff --git a/src/models/service_model.cpp b/src/models/service_model.cpp index b93277d..4e02e7e 100644 --- a/src/models/service_model.cpp +++ b/src/models/service_model.cpp @@ -1,6 +1,6 @@ #include -ServiceModel::ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string buyer_user_id) : _id(id), _creator_id(creator_id), _title(title), _description(description), _price(price), _status(status), _buyer_user_id(buyer_user_id) {} +ServiceModel::ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string type, std::string buyer_user_id) : _id(id), _creator_id(creator_id), _title(title), _description(description), _price(price), _status(status), _type(type), _buyer_user_id(buyer_user_id) {} std::string ServiceModel::get_id() { return _id; } std::string ServiceModel::get_creator_id() { return _creator_id; } @@ -8,24 +8,22 @@ std::string ServiceModel::get_title() { return _title; } std::string ServiceModel::get_description() { return _description; } int ServiceModel::get_price() { return _price; } std::string ServiceModel::get_status() { return _status; } +std::string ServiceModel::get_type() { return _type; } std::string ServiceModel::get_buyer_user_id() { return _buyer_user_id; } // * ------------------------------------------------------------------- -std::unique_ptr ServiceModel::create_service(pqxx::connection& db, std::string creator_id, std::string title, std::string description, int price, bool isThrow) { +std::unique_ptr ServiceModel::create_service(pqxx::connection& db, std::string creator_id, std::string title, std::string description, int price, std::string type, bool isThrow) { pqxx::work txn(db); - // Ejecutar la consulta para insertar el nuevo servicio en la base de datos - pqxx::result result = txn.exec_params("INSERT INTO services (creator_id, title, description, price) VALUES ($1, $2, $3, $4) RETURNING id", + pqxx::result result = txn.exec_params("INSERT INTO services (creator_id, title, description, price, type) VALUES ($1, $2, $3, $4, $5) RETURNING id", creator_id, title, description, - price); + price, type); - // Commit de la transacción txn.commit(); - // Verificar si la inserción fue exitosa if (result.empty()) { if (isThrow) throw data_not_found_exception("error create service model"); @@ -34,9 +32,7 @@ std::unique_ptr ServiceModel::create_service(pqxx::connection& db, return nullptr; } - // Obtener el ID del servicio insertado std::string service_id = result[0]["id"].as(); - // Crear una instancia de ServiceModel y devolverla - return std::make_unique(service_id, creator_id, title, description, price, "OPEN"); + return std::make_unique(service_id, creator_id, title, description, price, "OPEN", type); } diff --git a/src/utils/common.cpp b/src/utils/common.cpp index 4f7aab2..a0a45a1 100644 --- a/src/utils/common.cpp +++ b/src/utils/common.cpp @@ -9,3 +9,6 @@ const std::string NotificationStatus::REFUSED = "REFUSED"; const std::string ServiceStatus::CLOSED = "CLOSED"; const std::string ServiceStatus::OPEN = "OPEN"; + +const std::string ServiceType::OFFERED = "OFFERED"; +const std::string ServiceType::REQUESTED = "REQUESTED"; From 83125bd0bdaa43faf60010c67733c199b2462fe3 Mon Sep 17 00:00:00 2001 From: David Baque Date: Fri, 22 Mar 2024 15:20:55 +0100 Subject: [PATCH 35/48] tests to testing missing fields register do it --- include/utils/custom_regex.h | 2 +- src/utils/custom_regex.cpp | 14 +++- test/tests/auth_routes_test.cpp | 129 ++++++++++++++++++++++++++++++++ test/tests/common.h | 2 + test/tests/test_one.cpp | 71 ++++++++++++++++-- 5 files changed, 209 insertions(+), 9 deletions(-) create mode 100644 test/tests/auth_routes_test.cpp create mode 100644 test/tests/common.h diff --git a/include/utils/custom_regex.h b/include/utils/custom_regex.h index b79a63f..b61218e 100644 --- a/include/utils/custom_regex.h +++ b/include/utils/custom_regex.h @@ -8,7 +8,7 @@ #include #include -bool is_correct_email(std::string email); +bool is_correct_email(const std::string &email); bool is_correct_type(std::string type); diff --git a/src/utils/custom_regex.cpp b/src/utils/custom_regex.cpp index b5a4ac7..b9967dc 100644 --- a/src/utils/custom_regex.cpp +++ b/src/utils/custom_regex.cpp @@ -1,8 +1,16 @@ #include -bool is_correct_email(std::string email) { - std::regex patronEmail(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"); - return std::regex_match(email, patronEmail); +bool is_correct_email(const std::string& email) { + static const std::regex pattern(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"); + static const std::regex doubleDotPattern(R"(.*\.\..*)"); + + // Verificar si hay dos puntos consecutivos en el email + if (std::regex_match(email, doubleDotPattern)) { + return false; + } + + // Verificar la estructura básica del correo electrónico + return std::regex_match(email, pattern); } bool is_correct_type(std::string type) { diff --git a/test/tests/auth_routes_test.cpp b/test/tests/auth_routes_test.cpp new file mode 100644 index 0000000..5f534cd --- /dev/null +++ b/test/tests/auth_routes_test.cpp @@ -0,0 +1,129 @@ +#include +#include + +#include // Para std::getenv +#include +#include +#include + +#include "common.h" + +// ** ---------- MISSING FIELDS ON REQ.BODY TESTS ---------- ** \\ + +TEST(REGISTER_MISSING_FIELDS, MissingEmail) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json post_data = { + {"password", "F!sh1ngR0ck5"}, + {"type", "admin"}, + {"username", "desssddddggdddddsssssdndis"}, + {"community_name", "test_jwsssssst_register"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 404) << "expect 404 status code with email missing"; +} + +TEST(REGISTER_MISSING_FIELDS, MissingUsername) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"password", "F!sh1ngR0ck5"}, + {"type", "admin"}, + {"community_name", "test_jwsssssst_register"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 404) << "expect 404 status code with username missing"; +} + +TEST(REGISTER_MISSING_FIELDS, MissingPassword) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"type", "admin"}, + {"username", "desssddddggdddddsssssdndis"}, + {"community_name", "test_jwsssssst_register"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 404) << "expect 404 status code with password missing"; +} + +TEST(REGISTER_MISSING_FIELDS, MissingType) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"password", "F!sh1ngR0ck5"}, + {"username", "example"}, + {"community_name", "test_jwsssssst_register"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 404) << "expect 404 status code with type missing"; +} + +TEST(REGISTER_MISSING_FIELDS, MissingCommunityName) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"password", "F!sh1ngR0ck5"}, + {"type", "admin"}, + {"username", "example"}, + }; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 404) << "expect 404 status code with community_name missing"; +} + +TEST(REGISTER_MISSING_FIELDS, MissingCommunityCode) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"password", "F!sh1ngR0ck5"}, + {"type", "neighbor"}, + {"username", "example"}, + }; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 404) << "expect 404 status code with community_code missing"; +} + +// ** ---------- VALIDATION REQ.BODY FIELDS TESTS ---------- ** \\ + +TEST(REGISTER_VALIDATIONS, IncorrectEmail) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + // Lista de direcciones de correo electrónico incorrectas + std::vector incorrect_emails = { + "example%@gmail.com", + "example@domain.", + "example@domain123", + "example@domain,com", + "example@domain.com.", + "example@@domain.com", + "example@domain..com", + "example@@domain..com", + "example@domain_com", + "example@domain.com_com"}; + + for (const auto& email : incorrect_emails) { + nlohmann::json post_data = { + {"email", email}, + {"password", "F!sh1ngR0ck5"}, + {"type", "admin"}, + {"username", "example"}, + {"community_name", "example_community_name"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 400) << "Expected 400 status code for incorrect email: " << email; + } +} \ No newline at end of file diff --git a/test/tests/common.h b/test/tests/common.h new file mode 100644 index 0000000..241652e --- /dev/null +++ b/test/tests/common.h @@ -0,0 +1,2 @@ + +int HTTP_PORT = std::stoi(std::getenv("HTTP_PORT")); \ No newline at end of file diff --git a/test/tests/test_one.cpp b/test/tests/test_one.cpp index 4033981..1ed0154 100644 --- a/test/tests/test_one.cpp +++ b/test/tests/test_one.cpp @@ -4,21 +4,81 @@ #include // Para std::getenv #include #include +#include -int HTTP_PORT = std::stoi(std::getenv("HTTP_PORT")); int sum(int a, int b) { return a + b; } +/* +std::string extractTokenFromSetCookie(const std::string& setCookieHeader) { -TEST(test1, PositiveNumbers) { - EXPECT_EQ(sum(2, 3), 5); + size_t pos = setCookieHeader.find(";"); + + std::string tokenPart = setCookieHeader.substr(0, pos); + + size_t equalPos = tokenPart.find("="); + + std::string token = tokenPart.substr(equalPos + 1); + + return token; } -TEST(IntegrationTest, HttpRequest) { +std::string simulateLoginAndGetToken() { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json post_data = { + {"email", "denssfffssdddddsssssssddis@gmail.com"}, + {"password", "F!sh1ngR0ck5"}, + {"type", "admin"}, + {"username", "desssddddggdddddsssssdndis"}, + {"community_name", "test_jwsssssst_register"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + for (const auto& cookie : response.cookies) { + std::cout << cookie.GetDomain() << ":"; + std::cout << cookie.IsIncludingSubdomains() << ":"; + std::cout << cookie.GetPath() << ":"; + std::cout << cookie.IsHttpsOnly() << ":"; + std::cout << cookie.GetExpiresString() << ":"; + std::cout << cookie.GetName() << ":"; + std::cout << cookie.GetValue() << std::endl; + } + if (response.status_code == 201) { + std::string token; + for (const auto& header : response.header) { + if (header.first == "Set-Cookie") { + token = extractTokenFromSetCookie(header.second); + break; + } + } + + std::cout << "Valor de la cookie token: " << token << std::endl; + + return token; + } else { + std::cerr << "La solicitud no fue exitosa. Código de estado: " << response.status_code << std::endl; + return ""; + } +} */ + +/* TEST(test1, PositiveNumbers) { + EXPECT_EQ(sum(2, 3), 56) << "custom messag test error"; +} */ + +/* TEST(IntegrationTest, HttpRequest) { + // Simular inicio de sesión y obtener token JWT + std::string token = simulateLoginAndGetToken(); + + // Construir la URL std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/users"; - auto response = cpr::Get(cpr::Url{url}); + // Crear un objeto de cookies con el token JWT + cpr::Cookies cookies; + cookies["token"] = token; + + // Realizar la solicitud GET con las cookies + auto response = cpr::Get(cpr::Url{url}, cpr::Cookies{cookies}); EXPECT_EQ(response.status_code, 200); @@ -36,3 +96,4 @@ TEST(IntegrationTest, HttpRequest) { ASSERT_TRUE(user.contains("type")); } } + */ \ No newline at end of file From b93fba6e47c0d667f8af1c52988980db44d57c8f Mon Sep 17 00:00:00 2001 From: David Baque Date: Fri, 22 Mar 2024 18:35:18 +0100 Subject: [PATCH 36/48] test validations fields register --- Dockerfile.test | 2 +- src/utils/custom_regex.cpp | 4 +- test/CMakeLists.txt | 5 +- test/tests/auth_routes_test.cpp | 191 +++++++++++++++++++++++++++++++- test/tests/common.h | 7 +- 5 files changed, 202 insertions(+), 7 deletions(-) diff --git a/Dockerfile.test b/Dockerfile.test index 415fe88..800bcec 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,6 +1,6 @@ FROM gcc:latest -RUN apt-get update && apt-get install -y cmake inotify-tools git +RUN apt-get update && apt-get install -y libpqxx-dev cmake inotify-tools git WORKDIR /app diff --git a/src/utils/custom_regex.cpp b/src/utils/custom_regex.cpp index b9967dc..d21b1e9 100644 --- a/src/utils/custom_regex.cpp +++ b/src/utils/custom_regex.cpp @@ -1,7 +1,7 @@ #include bool is_correct_email(const std::string& email) { - static const std::regex pattern(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"); + static const std::regex pattern(R"([a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"); static const std::regex doubleDotPattern(R"(.*\.\..*)"); // Verificar si hay dos puntos consecutivos en el email @@ -64,7 +64,7 @@ bool is_correct_password(std::string password) { //** GOOD USERNAMES -> Usuario123, SecureUser1, AlphaBeta45, GoodPassword23, ValidUsername //** BAD USERNAMES -> Invalid!User, SpacesUser , Short, LongUsernameWithTooManyCharacters, Invalid Spaces bool is_correct_username(std::string username) { - if (username.length() < 4 || username.length() > 30) + if (username.length() <= 5 || username.length() > 30) return false; for (char c : username) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index eb3dc52..066bb4c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,6 +7,9 @@ set(CMAKE_CXX_STANDARD 20) # Agrega las opciones de compilación set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") +find_library(PQXX_LIB pqxx) +find_library(PQ_LIB pq) + # Encuentra las bibliotecas necesarias add_subdirectory(extern/cpr) add_subdirectory(extern/googletest) @@ -25,7 +28,7 @@ add_executable(test ${SOURCES}) target_include_directories(test PUBLIC include) # Enlaza las bibliotecas necesarias -target_link_libraries(test gtest gtest_main cpr nlohmann_json::nlohmann_json) +target_link_libraries(test ${PQXX_LIB} ${PQ_LIB} gtest gtest_main cpr nlohmann_json::nlohmann_json) # Si bcrypt está en un lugar no estándar, también agrega el directorio de inclusión # target_include_directories(backend PUBLIC ${BCRYPT_INCLUDE_DIR}) diff --git a/test/tests/auth_routes_test.cpp b/test/tests/auth_routes_test.cpp index 5f534cd..eaa5a12 100644 --- a/test/tests/auth_routes_test.cpp +++ b/test/tests/auth_routes_test.cpp @@ -3,11 +3,46 @@ #include // Para std::getenv #include +#include #include #include #include "common.h" +// Declaración de la función limpiarTablaUsers +void limpiarTablaUsers() { + try { + // Establecer la conexión a la base de datos + std::string connection_string = std::format("dbname={} user={} password={} host={} port={}", DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT); + pqxx::connection conn(connection_string); + // pqxx::connection conn("dbname=mydatabase user=myuser password=mypassword hostaddr=127.0.0.1 port=5432"); + + if (conn.is_open()) { + // Crear un objeto de transacción + pqxx::work txn(conn); + + // Ejecutar la consulta para limpiar la tabla users + txn.exec("DELETE FROM users"); + txn.exec("DELETE FROM communities"); + + // Confirmar la transacción + txn.commit(); + + } else { + std::cerr << "Error al conectar a la base de datos." << std::endl; + } + } catch (const std::exception& e) { + std::cerr << "Error de excepción: " << e.what() << std::endl; + } +} +class RegisterValidations : public ::testing::Test { + protected: + void TearDown() override { + // Llamar a la función limpiarTablaUsers después de que se complete el test + limpiarTablaUsers(); + } +}; + // ** ---------- MISSING FIELDS ON REQ.BODY TESTS ---------- ** \\ TEST(REGISTER_MISSING_FIELDS, MissingEmail) { @@ -97,8 +132,41 @@ TEST(REGISTER_MISSING_FIELDS, MissingCommunityCode) { } // ** ---------- VALIDATION REQ.BODY FIELDS TESTS ---------- ** \\ + +TEST_F(RegisterValidations, CorrectEmail) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; -TEST(REGISTER_VALIDATIONS, IncorrectEmail) { + // Lista de direcciones de correo electrónico correctas + std::vector correct_emails = { + "user@example.com", + "user123@example.com", + "user.name@example.com", + "user_name@example.com", + "user+name@example.com", + "user-name@example.com", + "user@example.co", + "user@example.co.uk", + "user@example.xyz"}; + + for (const auto& email : correct_emails) { + // Verificar que el correo electrónico sea correcto antes de enviar la solicitud + nlohmann::json post_data = { + {"email", email}, + {"password", "F!sh1ngR0ck5"}, + {"type", "admin"}, + {"username", "example"}, + {"community_name", "example_community_name"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + // Verificar que el servidor responda con el código de estado 201 (Created) + EXPECT_EQ(response.status_code, 201) << "Expected 201 status code for correct email: " << email; + // Limpiar la tabla users después de cada prueba + limpiarTablaUsers(); + } +} + +TEST_F(RegisterValidations, IncorrectEmail) { std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; // Lista de direcciones de correo electrónico incorrectas @@ -126,4 +194,123 @@ TEST(REGISTER_VALIDATIONS, IncorrectEmail) { EXPECT_EQ(response.status_code, 400) << "Expected 400 status code for incorrect email: " << email; } -} \ No newline at end of file +} + +TEST_F(RegisterValidations, Correct_password) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + // Lista de direcciones de correo electrónico incorrectas + std::vector incorrect_passwords = { + "Tr0ub4dor&3", + "P@ssw0rd!", + "S3cur3P@ss", + "B1gB@ngTh3ory", + "F!sh1ngR0ck5"}; + + for (const auto& password : incorrect_passwords) { + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"username", "example"}, + {"password", password}, + {"type", "admin"}, + {"community_name", "example_community_name"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 201) << "Expected 201 status code for incorrect password: " << password; + limpiarTablaUsers(); + } +} + +TEST_F(RegisterValidations, Incorrect_Password) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + // Lista de direcciones de correo electrónico incorrectas + std::vector incorrect_passwords = { + "password", "12345678", "qwerty", "letmein", "abc123"}; + + for (const auto& password : incorrect_passwords) { + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"username", "example"}, + {"password", password}, + {"type", "admin"}, + {"community_name", "example_community_name"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 400) << "Expected 400 status code for incorrect password: " << password; + } +} + +//** BAD USERNAMES -> Invalid!User, SpacesUser , Short, LongUsernameWithTooManyCharacters, Invalid Spaces + +TEST_F(RegisterValidations, Incorrect_Username) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + // Lista de direcciones de correo electrónico incorrectas + std::vector incorrect_usernames = { + "Invalid!User", + "SpacesUser ", + "Short", + "LongUsernameWithTooManyCharacters", + "Invalid Spaces"}; + + for (const auto& username : incorrect_usernames) { + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"username", username}, + {"password", "P@ssw0rd!"}, + {"type", "admin"}, + {"community_name", "example_community_name"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 400) << "Expected 400 status code for incorrect username: " << username; + } +} + +//** GOOD USERNAMES -> Usuario123, SecureUser1, AlphaBeta45, GoodPassword23, ValidUsername + +TEST_F(RegisterValidations, Correct_username) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + // Lista de direcciones de correo electrónico incorrectas + std::vector incorrect_usernames = { + "SecureUser1", + "Usuario123", + "AlphaBeta45", + "GoodPassword23", + "ValidUsername"}; + + for (const auto& username : incorrect_usernames) { + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"username", username}, + {"password", "P@ssw0rd!"}, + {"type", "admin"}, + {"community_name", "example_community_name"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 201) << "Expected 201 status code for incorrect username: " << username; + limpiarTablaUsers(); + } +} + +TEST_F(RegisterValidations, Incorrect_type) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + std::string incorrect_type = "type_error"; + + nlohmann::json post_data = { + {"email", "example@gmail.com"}, + {"username", "example"}, + {"password", "P@ssw0rd!"}, + {"type", incorrect_type}, + {"community_name", "example_community_name"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{post_data.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 400) << "Expected 400 status code for incorrect username: " << incorrect_type; +} diff --git a/test/tests/common.h b/test/tests/common.h index 241652e..fd6ba1f 100644 --- a/test/tests/common.h +++ b/test/tests/common.h @@ -1,2 +1,7 @@ -int HTTP_PORT = std::stoi(std::getenv("HTTP_PORT")); \ No newline at end of file +int HTTP_PORT = std::stoi(std::getenv("HTTP_PORT")); +std::string DB_NAME = std::string(std::getenv("DB_NAME")); +std::string DB_USER = std::string(std::getenv("DB_USER")); +std::string DB_PASSWORD = std::string(std::getenv("DB_PASSWORD")); +std::string DB_HOST = std::string(std::getenv("DB_HOST")); +std::string DB_PORT = std::string(std::getenv("DB_PORT")); \ No newline at end of file From 3ec2db14ec5eb7ba255a609b30f5699e00355a50 Mon Sep 17 00:00:00 2001 From: David Baque Date: Fri, 22 Mar 2024 21:00:16 +0100 Subject: [PATCH 37/48] task #95: add test resgister endpoint --- src/controllers/auth_controller.cpp | 9 ++- test/tests/auth_routes_test.cpp | 108 ++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index 3520f18..50926de 100644 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -23,7 +23,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re std::string hash = BCrypt::generateHash(password); if (UserModel::user_exist(db, email, username)) { - handle_error(res, "user alredy exist", 400); + handle_error(res, "user already exists", 400); return; } @@ -45,7 +45,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re std::string community_id = CommunityModel::get_community_id(db, community_code); if (community_id == "") { - handle_error(res, "not community exist", 404); + handle_error(res, "not community exists", 404); return; } user = UserModel::create_user(db, hash, email, username, "", 0, type); @@ -70,10 +70,9 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re res.set_header("Set-Cookie", cookieStream.str()); - crow::json::wvalue data; - data["user"] = { + crow::json::wvalue data({ {"id", user.getId()}, - }; + }); res.code = 201; res.write(data.dump()); diff --git a/test/tests/auth_routes_test.cpp b/test/tests/auth_routes_test.cpp index eaa5a12..1a5643e 100644 --- a/test/tests/auth_routes_test.cpp +++ b/test/tests/auth_routes_test.cpp @@ -132,7 +132,7 @@ TEST(REGISTER_MISSING_FIELDS, MissingCommunityCode) { } // ** ---------- VALIDATION REQ.BODY FIELDS TESTS ---------- ** \\ - + TEST_F(RegisterValidations, CorrectEmail) { std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; @@ -243,8 +243,6 @@ TEST_F(RegisterValidations, Incorrect_Password) { } } -//** BAD USERNAMES -> Invalid!User, SpacesUser , Short, LongUsernameWithTooManyCharacters, Invalid Spaces - TEST_F(RegisterValidations, Incorrect_Username) { std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; @@ -270,8 +268,6 @@ TEST_F(RegisterValidations, Incorrect_Username) { } } -//** GOOD USERNAMES -> Usuario123, SecureUser1, AlphaBeta45, GoodPassword23, ValidUsername - TEST_F(RegisterValidations, Correct_username) { std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; @@ -314,3 +310,105 @@ TEST_F(RegisterValidations, Incorrect_type) { EXPECT_EQ(response.status_code, 400) << "Expected 400 status code for incorrect username: " << incorrect_type; } + +// ** ---------- GENERAL ERRORS TESTS ---------- ** \\ + +class RegisterGeneralErrors : public ::testing::Test { + protected: + void TearDown() override { + limpiarTablaUsers(); + } +}; + +TEST_F(RegisterGeneralErrors, UserAlredyExist) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json new_user = { + {"email", "example@gmail.com"}, + {"username", "example"}, + {"password", "P@ssw0rd!"}, + {"type", "admin"}, + {"community_name", "example_community_name"}}; + + nlohmann::json user_exist_email = { + {"email", "example@gmail.com"}, + {"username", "example1"}, + {"password", "P@ssw0rd!"}, + {"type", "admin"}, + {"community_name", "example_community_name"}}; + nlohmann::json user_exist_username = { + {"email", "example@gmail.com"}, + {"username", "example1"}, + {"password", "P@ssw0rd!"}, + {"type", "admin"}, + {"community_name", "example_community_name"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{new_user.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + auto response_email = cpr::Post(cpr::Url{url}, cpr::Body{user_exist_email.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + auto json = nlohmann::json::parse(response_email.text); + + ASSERT_TRUE(json.contains("error")); + std::string error_message_email = json["error"]; + EXPECT_EQ(error_message_email, "user already exists"); + EXPECT_EQ(response_email.status_code, 400); + + auto response_username = cpr::Post(cpr::Url{url}, cpr::Body{user_exist_username.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + json = nlohmann::json::parse(response_username.text); + + ASSERT_TRUE(json.contains("error")); + std::string error_message_username = json["error"]; + EXPECT_EQ(error_message_username, "user already exists"); + EXPECT_EQ(response_username.status_code, 400); +} + +TEST_F(RegisterGeneralErrors, CorrectSignup) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json new_user = { + {"email", "example@gmail.com"}, + {"username", "example"}, + {"password", "P@ssw0rd!"}, + {"type", "admin"}, + {"community_name", "example_community_name"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{new_user.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 201); + + auto json = nlohmann::json::parse(response.text); + + ASSERT_TRUE(json.contains("id")); + + std::string set_cookie_header = response.header["Set-Cookie"]; + + // Buscar la cookie "token" dentro del encabezado "Set-Cookie" + size_t token_pos = set_cookie_header.find("token="); + bool token_found = token_pos != std::string::npos; + + // Verificar que se encontró la cookie "token" + EXPECT_TRUE(token_found); +} + +TEST_F(RegisterGeneralErrors, Community_Not_Exists) { + std::string url = "http://backend:" + std::to_string(HTTP_PORT) + "/api/auth/register"; + + nlohmann::json new_user = { + {"email", "example@gmail.com"}, + {"username", "example"}, + {"password", "P@ssw0rd!"}, + {"type", "neighbor"}, + {"community_code", "example_community_code"}}; + + auto response = cpr::Post(cpr::Url{url}, cpr::Body{new_user.dump()}, cpr::Header{{"Content-Type", "application/json"}}); + + EXPECT_EQ(response.status_code, 404); + + auto json = nlohmann::json::parse(response.text); + + ASSERT_TRUE(json.contains("error")); + std::string error_message_username = json["error"]; + EXPECT_EQ(error_message_username, "not community exists"); +} \ No newline at end of file From 485521e75266199fb9afed7fb37c2b13f0dcc7e5 Mon Sep 17 00:00:00 2001 From: leobelab Date: Sun, 24 Mar 2024 16:59:35 +0100 Subject: [PATCH 38/48] start get services --- .gitignore | 0 CMakeLists.txt | 0 Dockerfile.dev | 0 Dockerfile.prod | 0 Dockerfile.test | 0 LICENSE | 0 README.md | 0 crow-v1.0+5.deb | Bin hot-reload-test.sh | 0 hot-reload.sh | 0 include/controllers/auth_controller.h | 0 include/controllers/service_controller.h | 3 ++- include/controllers/user_controller.h | 0 include/middlewares/verify_jwt.h | 0 include/models/community_model.h | 0 include/models/notification_mdel.h | 0 include/models/service_model.h | 5 +++- include/models/user_model.h | 5 ++-- include/routes/auth_routes.h | 0 include/routes/service_routes.h | 0 include/routes/user_routes.h | 0 include/utils/auth.h | 0 include/utils/common.h | 0 include/utils/custom_regex.h | 0 include/utils/errors.h | 0 include/utils/user_validations.h | 0 include/utils/utils.h | 0 include/utils/validations.h | 0 src/controllers/auth_controller.cpp | 0 src/controllers/service_controller.cpp | 33 ++++++++++++++++++++++- src/controllers/user_controller.cpp | 0 src/main.cpp | 0 src/middlewares/verify_jwt.cpp | 0 src/models/community_model.cpp | 0 src/models/notification_model.cpp | 0 src/models/service_model.cpp | 21 +++++++++++++++ src/models/user_model.cpp | 0 src/routes/auth_routes.cpp | 0 src/routes/service_routes.cpp | 6 ++++- src/routes/user_routes.cpp | 0 src/utils/auth.cpp | 0 src/utils/common.cpp | 0 src/utils/custom_regex.cpp | 0 src/utils/errors.cpp | 0 src/utils/user_validations.cpp | 0 src/utils/utils.cpp | 0 src/utils/validations.cpp | 0 test/CMakeLists.txt | 0 test/tests/auth_routes_test.cpp | 0 test/tests/common.h | 0 test/tests/main.cpp | 0 test/tests/test_one.cpp | 0 52 files changed, 66 insertions(+), 7 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 CMakeLists.txt mode change 100644 => 100755 Dockerfile.dev mode change 100644 => 100755 Dockerfile.prod mode change 100644 => 100755 Dockerfile.test mode change 100644 => 100755 LICENSE mode change 100644 => 100755 README.md mode change 100644 => 100755 crow-v1.0+5.deb mode change 100644 => 100755 hot-reload-test.sh mode change 100644 => 100755 hot-reload.sh mode change 100644 => 100755 include/controllers/auth_controller.h mode change 100644 => 100755 include/controllers/service_controller.h mode change 100644 => 100755 include/controllers/user_controller.h mode change 100644 => 100755 include/middlewares/verify_jwt.h mode change 100644 => 100755 include/models/community_model.h mode change 100644 => 100755 include/models/notification_mdel.h mode change 100644 => 100755 include/models/service_model.h mode change 100644 => 100755 include/models/user_model.h mode change 100644 => 100755 include/routes/auth_routes.h mode change 100644 => 100755 include/routes/service_routes.h mode change 100644 => 100755 include/routes/user_routes.h mode change 100644 => 100755 include/utils/auth.h mode change 100644 => 100755 include/utils/common.h mode change 100644 => 100755 include/utils/custom_regex.h mode change 100644 => 100755 include/utils/errors.h mode change 100644 => 100755 include/utils/user_validations.h mode change 100644 => 100755 include/utils/utils.h mode change 100644 => 100755 include/utils/validations.h mode change 100644 => 100755 src/controllers/auth_controller.cpp mode change 100644 => 100755 src/controllers/service_controller.cpp mode change 100644 => 100755 src/controllers/user_controller.cpp mode change 100644 => 100755 src/main.cpp mode change 100644 => 100755 src/middlewares/verify_jwt.cpp mode change 100644 => 100755 src/models/community_model.cpp mode change 100644 => 100755 src/models/notification_model.cpp mode change 100644 => 100755 src/models/service_model.cpp mode change 100644 => 100755 src/models/user_model.cpp mode change 100644 => 100755 src/routes/auth_routes.cpp mode change 100644 => 100755 src/routes/service_routes.cpp mode change 100644 => 100755 src/routes/user_routes.cpp mode change 100644 => 100755 src/utils/auth.cpp mode change 100644 => 100755 src/utils/common.cpp mode change 100644 => 100755 src/utils/custom_regex.cpp mode change 100644 => 100755 src/utils/errors.cpp mode change 100644 => 100755 src/utils/user_validations.cpp mode change 100644 => 100755 src/utils/utils.cpp mode change 100644 => 100755 src/utils/validations.cpp mode change 100644 => 100755 test/CMakeLists.txt mode change 100644 => 100755 test/tests/auth_routes_test.cpp mode change 100644 => 100755 test/tests/common.h mode change 100644 => 100755 test/tests/main.cpp mode change 100644 => 100755 test/tests/test_one.cpp diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100644 new mode 100755 diff --git a/Dockerfile.dev b/Dockerfile.dev old mode 100644 new mode 100755 diff --git a/Dockerfile.prod b/Dockerfile.prod old mode 100644 new mode 100755 diff --git a/Dockerfile.test b/Dockerfile.test old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/crow-v1.0+5.deb b/crow-v1.0+5.deb old mode 100644 new mode 100755 diff --git a/hot-reload-test.sh b/hot-reload-test.sh old mode 100644 new mode 100755 diff --git a/hot-reload.sh b/hot-reload.sh old mode 100644 new mode 100755 diff --git a/include/controllers/auth_controller.h b/include/controllers/auth_controller.h old mode 100644 new mode 100755 diff --git a/include/controllers/service_controller.h b/include/controllers/service_controller.h old mode 100644 new mode 100755 index 8b3ce32..05ab574 --- a/include/controllers/service_controller.h +++ b/include/controllers/service_controller.h @@ -21,5 +21,6 @@ class ServiceController { static void get_user_by_id(pqxx::connection& db, const crow::request& req, const std::string& user_id, crow::response& res); static void delete_user_by_id(pqxx::connection& db, const std::string& user_id, crow::response& res); */ - static void create_service(pqxx::connection &db, const crow::request &req, crow::response &res); + static void create_service(pqxx::connection& db, const crow::request& req, crow::response& res); + static void get_services(pqxx::connection& db, const crow::request& req, crow::response& res); }; \ No newline at end of file diff --git a/include/controllers/user_controller.h b/include/controllers/user_controller.h old mode 100644 new mode 100755 diff --git a/include/middlewares/verify_jwt.h b/include/middlewares/verify_jwt.h old mode 100644 new mode 100755 diff --git a/include/models/community_model.h b/include/models/community_model.h old mode 100644 new mode 100755 diff --git a/include/models/notification_mdel.h b/include/models/notification_mdel.h old mode 100644 new mode 100755 diff --git a/include/models/service_model.h b/include/models/service_model.h old mode 100644 new mode 100755 index bf8226c..1693b77 --- a/include/models/service_model.h +++ b/include/models/service_model.h @@ -5,6 +5,7 @@ #include #include #include +#include class ServiceModel { private: @@ -29,7 +30,9 @@ class ServiceModel { ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string type, std::string buyer_user_id = ""); - static std::unique_ptr create_service(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price, std::string type, bool isThrow = false); + static std::unique_ptr create_service(pqxx::connection& db, std::string creator_id, std::string title, std::string description, int price, std::string type, bool isThrow = false); + + static std::vector get_services(pqxx::connection& db, std::string status = ""); // * static ServiceModel create_notification(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price); }; diff --git a/include/models/user_model.h b/include/models/user_model.h old mode 100644 new mode 100755 index 8573f27..3e175c8 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -1,14 +1,13 @@ #pragma once -#include "utils/errors.h" - #include - #include #include #include #include +#include "utils/errors.h" + class UserModel { private: std::string _id; diff --git a/include/routes/auth_routes.h b/include/routes/auth_routes.h old mode 100644 new mode 100755 diff --git a/include/routes/service_routes.h b/include/routes/service_routes.h old mode 100644 new mode 100755 diff --git a/include/routes/user_routes.h b/include/routes/user_routes.h old mode 100644 new mode 100755 diff --git a/include/utils/auth.h b/include/utils/auth.h old mode 100644 new mode 100755 diff --git a/include/utils/common.h b/include/utils/common.h old mode 100644 new mode 100755 diff --git a/include/utils/custom_regex.h b/include/utils/custom_regex.h old mode 100644 new mode 100755 diff --git a/include/utils/errors.h b/include/utils/errors.h old mode 100644 new mode 100755 diff --git a/include/utils/user_validations.h b/include/utils/user_validations.h old mode 100644 new mode 100755 diff --git a/include/utils/utils.h b/include/utils/utils.h old mode 100644 new mode 100755 diff --git a/include/utils/validations.h b/include/utils/validations.h old mode 100644 new mode 100755 diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp old mode 100644 new mode 100755 diff --git a/src/controllers/service_controller.cpp b/src/controllers/service_controller.cpp old mode 100644 new mode 100755 index b38a16c..0174a01 --- a/src/controllers/service_controller.cpp +++ b/src/controllers/service_controller.cpp @@ -29,7 +29,7 @@ void ServiceController::create_service(pqxx::connection &db, const crow::request {"creator_id", service.get()->get_creator_id()}, {"description", service.get()->get_description()}, {"title", service.get()->get_title()}, - {"id", service.get()->get_id()}, + {"id", service.get()->get_id()}, }); res.write(data.dump()); @@ -41,4 +41,35 @@ void ServiceController::create_service(pqxx::connection &db, const crow::request } catch (const std::exception &e) { handle_error(res, e.what(), 500); } +} + +void ServiceController::get_services(pqxx::connection &db, const crow::request &req, crow::response &res) { + try { + auto status_param = req.url_params.get("status"); + std::string status = status_param; + std::vector allServices = ServiceModel::get_services(db, status); + if (!status_param) + allServices = ServiceModel::get_services(db); + + crow::json::wvalue::list services; + for (unsigned int i = 0; i < allServices.size(); i++) { + crow::json::wvalue service; + service["id"] = allServices[i].get_id(); + service["creator_id"] = allServices[i].get_creator_id(); + service["title"] = allServices[i].get_title(); + service["description"] = allServices[i].get_description(); + service["price"] = allServices[i].get_price(); + service["status"] = allServices[i].get_status(); + service["type"] = allServices[i].get_type(); + service["buyer_user_id"] = allServices[i].get_buyer_user_id(); + services.push_back(service); + } + + crow::json::wvalue data{{"services", services}}; + res.write(data.dump()); + res.code = 200; + res.end(); + } catch (const std::exception &e) { + handle_error(res, e.what(), 500); + } } \ No newline at end of file diff --git a/src/controllers/user_controller.cpp b/src/controllers/user_controller.cpp old mode 100644 new mode 100755 diff --git a/src/main.cpp b/src/main.cpp old mode 100644 new mode 100755 diff --git a/src/middlewares/verify_jwt.cpp b/src/middlewares/verify_jwt.cpp old mode 100644 new mode 100755 diff --git a/src/models/community_model.cpp b/src/models/community_model.cpp old mode 100644 new mode 100755 diff --git a/src/models/notification_model.cpp b/src/models/notification_model.cpp old mode 100644 new mode 100755 diff --git a/src/models/service_model.cpp b/src/models/service_model.cpp old mode 100644 new mode 100755 index 4e02e7e..7bfa712 --- a/src/models/service_model.cpp +++ b/src/models/service_model.cpp @@ -36,3 +36,24 @@ std::unique_ptr ServiceModel::create_service(pqxx::connection& db, return std::make_unique(service_id, creator_id, title, description, price, "OPEN", type); } + +std::vector ServiceModel::get_services(pqxx::connection& db, std::string status) { + std::vector all_services; + + pqxx::work txn(db); + + if (status == "OPEN" or status == "CLOSED") + pqxx::result result = txn.exec("SELECT id, creator_id, title, description, price, status, type, buyer_user_id FROM services WHERE status = $1", status); + else + pqxx::result result = txn.exec("SELECT id, creator_id, title, description, price, status, type, buyer_user_id FROM services"); + + for (const auto& row : result) { + ServiceModel service(row["id"].as(), row["creator_id"].as(), row["title"].as(), row["description"].as(), row["price"].as(), row["status"].as(), row["type"].as(), row["buyer_user_id"].as()); + + all_services.push_back(service); + } + + txn.commit(); + + return all_services; +} diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp old mode 100644 new mode 100755 diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp old mode 100644 new mode 100755 diff --git a/src/routes/service_routes.cpp b/src/routes/service_routes.cpp old mode 100644 new mode 100755 index 020dab2..4e8ec81 --- a/src/routes/service_routes.cpp +++ b/src/routes/service_routes.cpp @@ -3,13 +3,17 @@ void initialize_service_routes(NebyApp& app, pqxx::connection& db) { // ** GET /api/services + CROW_ROUTE(app, "/api/services").methods(crow::HTTPMethod::GET) { + ServiceController::get_services(db, req, res); + }; + // ** GET /api/services/:id // ** POST /api/services CROW_ROUTE(app, "/api/services").methods(crow::HTTPMethod::POST).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res) { ServiceController::create_service(db, req, res); - }); + }); // ** PUT /api/services/:id diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp old mode 100644 new mode 100755 diff --git a/src/utils/auth.cpp b/src/utils/auth.cpp old mode 100644 new mode 100755 diff --git a/src/utils/common.cpp b/src/utils/common.cpp old mode 100644 new mode 100755 diff --git a/src/utils/custom_regex.cpp b/src/utils/custom_regex.cpp old mode 100644 new mode 100755 diff --git a/src/utils/errors.cpp b/src/utils/errors.cpp old mode 100644 new mode 100755 diff --git a/src/utils/user_validations.cpp b/src/utils/user_validations.cpp old mode 100644 new mode 100755 diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp old mode 100644 new mode 100755 diff --git a/src/utils/validations.cpp b/src/utils/validations.cpp old mode 100644 new mode 100755 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt old mode 100644 new mode 100755 diff --git a/test/tests/auth_routes_test.cpp b/test/tests/auth_routes_test.cpp old mode 100644 new mode 100755 diff --git a/test/tests/common.h b/test/tests/common.h old mode 100644 new mode 100755 diff --git a/test/tests/main.cpp b/test/tests/main.cpp old mode 100644 new mode 100755 diff --git a/test/tests/test_one.cpp b/test/tests/test_one.cpp old mode 100644 new mode 100755 From 2a3c52f53675df4af2a38115098ec2c4d8248888 Mon Sep 17 00:00:00 2001 From: David Baque Date: Sun, 24 Mar 2024 19:37:23 +0100 Subject: [PATCH 39/48] test git fetch --- src/controllers/service_controller.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/service_controller.cpp b/src/controllers/service_controller.cpp index 0174a01..d474153 100755 --- a/src/controllers/service_controller.cpp +++ b/src/controllers/service_controller.cpp @@ -45,6 +45,7 @@ void ServiceController::create_service(pqxx::connection &db, const crow::request void ServiceController::get_services(pqxx::connection &db, const crow::request &req, crow::response &res) { try { + // ** comment to add auto status_param = req.url_params.get("status"); std::string status = status_param; std::vector allServices = ServiceModel::get_services(db, status); From 37e51f0736f04c75eb288873d61fc944d208ad07 Mon Sep 17 00:00:00 2001 From: David Baque Date: Sun, 24 Mar 2024 20:37:40 +0100 Subject: [PATCH 40/48] task #96: get services endpoint --- src/controllers/service_controller.cpp | 19 ++++++++++++++----- src/models/service_model.cpp | 20 ++++++++++++-------- src/routes/service_routes.cpp | 4 ++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/controllers/service_controller.cpp b/src/controllers/service_controller.cpp index d474153..d1aa19e 100755 --- a/src/controllers/service_controller.cpp +++ b/src/controllers/service_controller.cpp @@ -45,13 +45,21 @@ void ServiceController::create_service(pqxx::connection &db, const crow::request void ServiceController::get_services(pqxx::connection &db, const crow::request &req, crow::response &res) { try { - // ** comment to add - auto status_param = req.url_params.get("status"); - std::string status = status_param; - std::vector allServices = ServiceModel::get_services(db, status); - if (!status_param) + // ** comment to add + auto status = req.url_params.get("status"); + + std::vector allServices; + if (!status) { allServices = ServiceModel::get_services(db); + } else if (status && (std::string(status) == "OPEN" || std::string(status) == "CLOSED")) { + allServices = ServiceModel::get_services(db, status); + } else { + handle_error(res, "status not valid value", 400); + return; + } + + std::cout << "flag 3" << std::endl; crow::json::wvalue::list services; for (unsigned int i = 0; i < allServices.size(); i++) { crow::json::wvalue service; @@ -67,6 +75,7 @@ void ServiceController::get_services(pqxx::connection &db, const crow::request & } crow::json::wvalue data{{"services", services}}; + res.write(data.dump()); res.code = 200; res.end(); diff --git a/src/models/service_model.cpp b/src/models/service_model.cpp index 7bfa712..3ea7c99 100755 --- a/src/models/service_model.cpp +++ b/src/models/service_model.cpp @@ -39,21 +39,25 @@ std::unique_ptr ServiceModel::create_service(pqxx::connection& db, std::vector ServiceModel::get_services(pqxx::connection& db, std::string status) { std::vector all_services; - pqxx::work txn(db); + pqxx::result result; + + std::string query = "SELECT id, creator_id, title, description, price, status, type, buyer_user_id FROM services"; + if (status != "") + query += " WHERE status = '" + status + "'"; - if (status == "OPEN" or status == "CLOSED") - pqxx::result result = txn.exec("SELECT id, creator_id, title, description, price, status, type, buyer_user_id FROM services WHERE status = $1", status); - else - pqxx::result result = txn.exec("SELECT id, creator_id, title, description, price, status, type, buyer_user_id FROM services"); + result = txn.exec(query); for (const auto& row : result) { - ServiceModel service(row["id"].as(), row["creator_id"].as(), row["title"].as(), row["description"].as(), row["price"].as(), row["status"].as(), row["type"].as(), row["buyer_user_id"].as()); + std::string buyer_user_id; + if (!row["buyer_user_id"].is_null()) { + buyer_user_id = row["buyer_user_id"].as(); + } + ServiceModel service(row["id"].as(), row["creator_id"].as(), row["title"].as(), row["description"].as(), row["price"].as(), row["status"].as(), row["type"].as(), buyer_user_id); all_services.push_back(service); } - + std::cout << "flag 1 model" << std::endl; txn.commit(); - return all_services; } diff --git a/src/routes/service_routes.cpp b/src/routes/service_routes.cpp index 4e8ec81..5a28293 100755 --- a/src/routes/service_routes.cpp +++ b/src/routes/service_routes.cpp @@ -3,9 +3,9 @@ void initialize_service_routes(NebyApp& app, pqxx::connection& db) { // ** GET /api/services - CROW_ROUTE(app, "/api/services").methods(crow::HTTPMethod::GET) { + CROW_ROUTE(app, "/api/services").methods(crow::HTTPMethod::GET).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res) { ServiceController::get_services(db, req, res); - }; + }); // ** GET /api/services/:id From 354c4799809fde356d674266ea229a832b468e81 Mon Sep 17 00:00:00 2001 From: Voooigt Date: Tue, 26 Mar 2024 05:07:44 +0100 Subject: [PATCH 41/48] task #101: get user by id endpoint --- include/controllers/user_controller.h | 1 + include/models/user_model.h | 3 ++ src/controllers/user_controller.cpp | 72 +++++++++++++++++++++++++++ src/models/user_model.cpp | 40 +++++++++++++++ src/routes/user_routes.cpp | 4 ++ 5 files changed, 120 insertions(+) diff --git a/include/controllers/user_controller.h b/include/controllers/user_controller.h index 97a0a6e..dd51ef6 100755 --- a/include/controllers/user_controller.h +++ b/include/controllers/user_controller.h @@ -16,4 +16,5 @@ class UserController { static void get_users(pqxx::connection& db, const crow::request& req, crow::response& res); static void get_user_by_id(pqxx::connection& db, const crow::request& req, const std::string& user_id, crow::response& res); static void delete_user_by_id(pqxx::connection& db, const std::string& user_id, crow::response& res); + static void update_user_by_id(pqxx::connection& db, const std::string& user_id, crow::response& res); }; diff --git a/include/models/user_model.h b/include/models/user_model.h index 3e175c8..960f256 100755 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -7,6 +7,7 @@ #include #include "utils/errors.h" +#include "utils/user_validations.h" class UserModel { private: @@ -31,6 +32,7 @@ class UserModel { static UserModel create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type); static bool user_exist(pqxx::connection& db, std::string email, std::string ussername); + static bool user_exist_by_id(pqxx::connection& db, std::string id); static std::vector get_users(pqxx::connection& db); static void set_community_id(pqxx::connection& db, std::string community_id, std::string user_id); @@ -38,4 +40,5 @@ class UserModel { static std::string get_password_by_email(pqxx::connection& db, std::string email); static UserModel get_user_by_id(pqxx::connection& db, const std::string& id); static bool delete_by_id(pqxx::connection& db, const std::string& id); + static bool update_by_id(pqxx::connection& db, const std::string& id, const std::string username, const std::string email, const std::string password, const int balance); }; diff --git a/src/controllers/user_controller.cpp b/src/controllers/user_controller.cpp index 8880b6a..0860bcf 100755 --- a/src/controllers/user_controller.cpp +++ b/src/controllers/user_controller.cpp @@ -87,3 +87,75 @@ void UserController::delete_user_by_id(pqxx::connection &db, const std::string & res.end(); } } + +static void update_user_by_id(pqxx::connection &db, const std::string &user_id, crow::response &res) { + try { + bool userFound = UserModel::user_exist_by_id(user_id); + if (userFound) { + crow::json::rvalue update = crow::json::load(req.body); + if (update.has("username")) { + std::string temp_name = body["username"].s(); + if (!user_validations::validate_username(temp_name, res)) { + res.code = 400; + crow::json::wvalue error_message; + error_message["error"] = "incorrect username"; + res.write(error_message.dump()); + return; + } + } else { + std::string temp_name = "" + } + + if (update.has("email")) { + std::string temp_email = body["email"].s(); + if (!user_validations::validate_email(temp_email, res)) { + res.code = 400; + crow::json::wvalue error_message; + error_message["error"] = "incorrect email"; + res.write(error_message.dump()); + return; + } + } else { + std::string temp_email = "" + } + + if (update.has("password")) { + std::string temp_pass = body["password"].s(); + if (!user_validations::validate_password(temp_pass, res)) { + res.code = 400; + crow::json::wvalue error_message; + error_message["error"] = "incorrect password"; + res.write(error_message.dump()); + return; + } + } else { + std::string temp_pass = "" + } + std::string hash = BCrypt::generateHash(password); + + if (update.has("balance")) { + int temp_balance = body["balance"].i(); + if (temp_balance < 0) { + res.code = 400; + crow::json::wvalue error_message; + error_message["error"] = "incorrect balance"; + res.write(error_message.dump()); + return; + } + } else { + int temp_balance = -1 + } + + bool newUser = UserModel::update_by_id(db, user_id, temp_name, temp_email, hash, temp_balance); + } else { + res.code = 404; + crow::json::wvalue error_message; + error_message["error"] = "User not found"; + res.write(error_message.dump()); + } + } catch { + std::cerr << "Error updating user: " << e.what() << std::endl; + res.code = 500; + res.end(); + } +} diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 2a91bbb..a80b30c 100755 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -58,6 +58,22 @@ bool UserModel::user_exist(pqxx::connection& db, std::string email, std::string } } +bool UserModel::user_exist_by_id(pqxx::connection& db, std::string id) { + try { + pqxx::work txn(db); + + pqxx::result result = txn.exec_params("SELECT username FROM users WHERE id = $1", id); + + bool userExists = !result.empty() && !result[0][0].is_null(); + + txn.commit(); + + return userExists; + } catch (const std::exception& e) { + return false; + } +} + std::vector UserModel::get_users(pqxx::connection& db) { std::vector all_users; @@ -160,3 +176,27 @@ bool UserModel::delete_by_id(pqxx::connection& db, const std::string& id) { return false; } } + +static bool UserModel::update_by_id(pqxx::connection& db, const std::string& id, const std::string username, const std::string email, const std::string password, const int balance) { + try ̣{ + pqxx::work txn(db); + if (username != "") { + pqxx::result result = txn.exec_params("UPDATE users SET username = $1 WHERE id = $2", username, id); + } + if (email != "") { + pqxx::result result = txn.exec_params("UPDATE users SET email = $1 WHERE id = $2", email, id); + } + if (password != "") { + pqxx::result result = txn.exec_params("UPDATE users SET password = $1 WHERE id = $2", password, id); + } + if (balance != -1) { + pqxx::result result = txn.exec_params("UPDATE users SET balance = $1 WHERE id = $2", balance, id); + } + txn.commit(); + return true; + } + catch { + std::cerr << "Failed to update user: " << e.what() << std::endl; + return false; + } +} diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp index 64cf81e..2387aad 100755 --- a/src/routes/user_routes.cpp +++ b/src/routes/user_routes.cpp @@ -12,4 +12,8 @@ void initialize_user_routes(NebyApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::DELETE)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { UserController::delete_user_by_id(db, user_id, res); }); + + CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::PUT)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { + UserController::update_user_by_id(db, user_id, res); + } } From c42b14575d688f91e5d292b188fa1a6bda60843d Mon Sep 17 00:00:00 2001 From: Voooigt Date: Tue, 26 Mar 2024 17:31:12 +0100 Subject: [PATCH 42/48] task #101: solve errors and testing --- include/controllers/user_controller.h | 3 +- include/models/user_model.h | 2 +- src/controllers/user_controller.cpp | 53 +++++++++++---------------- src/models/user_model.cpp | 10 ++--- src/routes/user_routes.cpp | 10 ++--- 5 files changed, 33 insertions(+), 45 deletions(-) diff --git a/include/controllers/user_controller.h b/include/controllers/user_controller.h index dd51ef6..c5933c2 100755 --- a/include/controllers/user_controller.h +++ b/include/controllers/user_controller.h @@ -10,11 +10,12 @@ #include "crow.h" #include "models/user_model.h" +#include "utils/user_validations.h" class UserController { public: static void get_users(pqxx::connection& db, const crow::request& req, crow::response& res); static void get_user_by_id(pqxx::connection& db, const crow::request& req, const std::string& user_id, crow::response& res); static void delete_user_by_id(pqxx::connection& db, const std::string& user_id, crow::response& res); - static void update_user_by_id(pqxx::connection& db, const std::string& user_id, crow::response& res); + static void update_user_by_id(pqxx::connection& db, const std::string& user_id, const crow::request& req, crow::response& res); }; diff --git a/include/models/user_model.h b/include/models/user_model.h index 960f256..fd032ef 100755 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -40,5 +40,5 @@ class UserModel { static std::string get_password_by_email(pqxx::connection& db, std::string email); static UserModel get_user_by_id(pqxx::connection& db, const std::string& id); static bool delete_by_id(pqxx::connection& db, const std::string& id); - static bool update_by_id(pqxx::connection& db, const std::string& id, const std::string username, const std::string email, const std::string password, const int balance); + static bool update_by_id(pqxx::connection& db, const std::string& id, const std::string username, const std::string email, const std::string password); }; diff --git a/src/controllers/user_controller.cpp b/src/controllers/user_controller.cpp index 0860bcf..a7ad8ae 100755 --- a/src/controllers/user_controller.cpp +++ b/src/controllers/user_controller.cpp @@ -88,72 +88,63 @@ void UserController::delete_user_by_id(pqxx::connection &db, const std::string & } } -static void update_user_by_id(pqxx::connection &db, const std::string &user_id, crow::response &res) { +void UserController::update_user_by_id(pqxx::connection &db, const std::string &user_id, const crow::request &req, crow::response &res) { try { - bool userFound = UserModel::user_exist_by_id(user_id); + bool userFound = UserModel::user_exist_by_id(db, user_id); if (userFound) { crow::json::rvalue update = crow::json::load(req.body); + std::string temp_name = "", temp_pass = "", temp_email = ""; if (update.has("username")) { - std::string temp_name = body["username"].s(); - if (!user_validations::validate_username(temp_name, res)) { + temp_name = update["username"].s(); + if (!validate_username(temp_name, res)) { res.code = 400; crow::json::wvalue error_message; error_message["error"] = "incorrect username"; res.write(error_message.dump()); return; } - } else { - std::string temp_name = "" } - if (update.has("email")) { - std::string temp_email = body["email"].s(); - if (!user_validations::validate_email(temp_email, res)) { + temp_email = update["email"].s(); + if (!validate_email(temp_email, res)) { res.code = 400; crow::json::wvalue error_message; error_message["error"] = "incorrect email"; res.write(error_message.dump()); return; } - } else { - std::string temp_email = "" } - if (update.has("password")) { - std::string temp_pass = body["password"].s(); - if (!user_validations::validate_password(temp_pass, res)) { + temp_pass = update["password"].s(); + if (!validate_password(temp_pass, res)) { res.code = 400; crow::json::wvalue error_message; error_message["error"] = "incorrect password"; res.write(error_message.dump()); return; } - } else { - std::string temp_pass = "" } - std::string hash = BCrypt::generateHash(password); - - if (update.has("balance")) { - int temp_balance = body["balance"].i(); - if (temp_balance < 0) { - res.code = 400; - crow::json::wvalue error_message; - error_message["error"] = "incorrect balance"; - res.write(error_message.dump()); - return; - } + std::string hash = BCrypt::generateHash(temp_pass); + bool succes = UserModel::update_by_id(db, user_id, temp_name, temp_email, hash); + if (succes) { + res.code = 200; + crow::json::wvalue response_message; + response_message["message"] = "User updated successfully"; + res.write(response_message.dump()); } else { - int temp_balance = -1 + res.code = 400; + crow::json::wvalue error_message; + error_message["error"] = "Failed to update user"; + res.write(error_message.dump()); } - - bool newUser = UserModel::update_by_id(db, user_id, temp_name, temp_email, hash, temp_balance); + res.end(); } else { res.code = 404; crow::json::wvalue error_message; error_message["error"] = "User not found"; res.write(error_message.dump()); } - } catch { + } catch (const std::exception &e) { std::cerr << "Error updating user: " << e.what() << std::endl; res.code = 500; res.end(); diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index a80b30c..9bad22d 100755 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -177,8 +177,8 @@ bool UserModel::delete_by_id(pqxx::connection& db, const std::string& id) { } } -static bool UserModel::update_by_id(pqxx::connection& db, const std::string& id, const std::string username, const std::string email, const std::string password, const int balance) { - try ̣{ +bool UserModel::update_by_id(pqxx::connection& db, const std::string& id, const std::string username, const std::string email, const std::string password) { + try { pqxx::work txn(db); if (username != "") { pqxx::result result = txn.exec_params("UPDATE users SET username = $1 WHERE id = $2", username, id); @@ -189,13 +189,9 @@ static bool UserModel::update_by_id(pqxx::connection& db, const std::string& id, if (password != "") { pqxx::result result = txn.exec_params("UPDATE users SET password = $1 WHERE id = $2", password, id); } - if (balance != -1) { - pqxx::result result = txn.exec_params("UPDATE users SET balance = $1 WHERE id = $2", balance, id); - } txn.commit(); return true; - } - catch { + } catch (const std::exception& e) { std::cerr << "Failed to update user: " << e.what() << std::endl; return false; } diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp index 2387aad..57ea6f3 100755 --- a/src/routes/user_routes.cpp +++ b/src/routes/user_routes.cpp @@ -5,15 +5,15 @@ void initialize_user_routes(NebyApp& app, pqxx::connection& db) { UserController::get_users(db, req, res); }); - CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::GET)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { + CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::GET).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { UserController::get_user_by_id(db, req, user_id, res); }); - CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::DELETE)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { + CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::DELETE).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { UserController::delete_user_by_id(db, user_id, res); }); - CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::PUT)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { - UserController::update_user_by_id(db, user_id, res); - } + CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::PUT).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { + UserController::update_user_by_id(db, user_id, req, res); + }); } From b7c8d82a74d0ad2fe8dfe5bab17533b7b492ff95 Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Thu, 28 Mar 2024 04:47:59 +0100 Subject: [PATCH 43/48] task #104: applied formatting to all files and updated auth and user routes, controllers and model --- include/controllers/auth_controller.h | 8 +- include/controllers/service_controller.h | 3 +- include/controllers/user_controller.h | 14 +- include/models/community_model.h | 7 +- include/models/notification_mdel.h | 35 ---- include/models/notification_model.h | 23 +++ include/models/service_model.h | 14 -- include/models/user_model.h | 50 +++--- include/routes/auth_routes.h | 9 +- include/routes/service_routes.h | 8 +- include/routes/user_routes.h | 8 +- include/utils/auth.h | 1 - include/utils/common.h | 6 +- include/utils/custom_regex.h | 1 - include/utils/errors.h | 2 +- include/utils/user_validations.h | 4 +- include/utils/utils.h | 3 +- include/utils/validations.h | 2 +- src/controllers/auth_controller.cpp | 56 +++---- src/controllers/service_controller.cpp | 6 +- src/controllers/user_controller.cpp | 125 ++++++-------- src/main.cpp | 10 +- src/models/community_model.cpp | 13 +- src/models/service_model.cpp | 13 +- src/models/user_model.cpp | 205 +++++++++-------------- src/routes/auth_routes.cpp | 5 +- src/routes/service_routes.cpp | 2 +- src/routes/user_routes.cpp | 8 +- src/utils/auth.cpp | 2 +- src/utils/common.cpp | 16 +- src/utils/errors.cpp | 3 +- src/utils/user_validations.cpp | 2 +- test/tests/auth_routes_test.cpp | 6 +- test/tests/common.h | 3 +- test/tests/main.cpp | 1 - test/tests/test_one.cpp | 4 +- 36 files changed, 275 insertions(+), 403 deletions(-) delete mode 100755 include/models/notification_mdel.h create mode 100755 include/models/notification_model.h diff --git a/include/controllers/auth_controller.h b/include/controllers/auth_controller.h index 304c392..0270997 100755 --- a/include/controllers/auth_controller.h +++ b/include/controllers/auth_controller.h @@ -1,17 +1,15 @@ #pragma once +#include #include +#include #include - +#include #include #include #include #include -#include "crow.h" -#include "models/user_model.h" -#include "utils/utils.h" - class AuthController { public: static void register_user(pqxx::connection& db, const crow::request& req, crow::response& res); diff --git a/include/controllers/service_controller.h b/include/controllers/service_controller.h index 05ab574..2bd0f69 100755 --- a/include/controllers/service_controller.h +++ b/include/controllers/service_controller.h @@ -1,7 +1,6 @@ #pragma once #include - #include #include #include @@ -23,4 +22,4 @@ class ServiceController { static void create_service(pqxx::connection& db, const crow::request& req, crow::response& res); static void get_services(pqxx::connection& db, const crow::request& req, crow::response& res); -}; \ No newline at end of file +}; diff --git a/include/controllers/user_controller.h b/include/controllers/user_controller.h index c5933c2..0268f41 100755 --- a/include/controllers/user_controller.h +++ b/include/controllers/user_controller.h @@ -1,21 +1,19 @@ #pragma once +#include +#include #include +#include #include - #include #include #include #include -#include "crow.h" -#include "models/user_model.h" -#include "utils/user_validations.h" - class UserController { public: static void get_users(pqxx::connection& db, const crow::request& req, crow::response& res); - static void get_user_by_id(pqxx::connection& db, const crow::request& req, const std::string& user_id, crow::response& res); - static void delete_user_by_id(pqxx::connection& db, const std::string& user_id, crow::response& res); - static void update_user_by_id(pqxx::connection& db, const std::string& user_id, const crow::request& req, crow::response& res); + static void get_user_by_id(pqxx::connection& db, const crow::request& req, crow::response& res, const std::string& user_id); + static void delete_user_by_id(pqxx::connection& db, crow::response& res, const std::string& user_id); + static void update_user_by_id(pqxx::connection& db, const crow::request& req, crow::response& res, const std::string& user_id); }; diff --git a/include/models/community_model.h b/include/models/community_model.h index f11a325..35d6706 100755 --- a/include/models/community_model.h +++ b/include/models/community_model.h @@ -1,5 +1,4 @@ -#ifndef COMMUNITY_MODEL_H -#define COMMUNITY_MODEL_H +#pragma once #include #include @@ -21,7 +20,5 @@ class CommunityModel { static std::string generate_community_code(); static CommunityModel create_community(pqxx::connection& db, std::string community_name); - static std::string get_community_id(pqxx::connection &db, std::string community_code); + static std::string get_community_id(pqxx::connection& db, std::string community_code); }; - -#endif \ No newline at end of file diff --git a/include/models/notification_mdel.h b/include/models/notification_mdel.h deleted file mode 100755 index 32949b2..0000000 --- a/include/models/notification_mdel.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -class NotificationModel { - private: - std::string _id; - std::string _sender_id; - std::string _receiver_id; - std::string _service_id; - std::string _status; - std::string _created_at; - std::string _updated_at; - - public: - NotificationModel(std::string id, std::string sender_id, std::string receiver_id, std::string service_id, std::string status); - - std::string get_id(); - std::string get_sender_id(); - std::string get_receiver_id(); - std::string get_service_id(); - std::string get_status(); - -}; - -/* CREATE TABLE IF NOT EXISTS notifications ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - sender_id UUID REFERENCES users(id), - receiver_id UUID REFERENCES users(id), - service_id UUID REFERENCES services(id), - status notification_status DEFAULT 'PENDING', -- PENDING, ACCEPTED, REFUSED - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - */ \ No newline at end of file diff --git a/include/models/notification_model.h b/include/models/notification_model.h new file mode 100755 index 0000000..b9077c4 --- /dev/null +++ b/include/models/notification_model.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +class NotificationModel { + private: + std::string _id; + std::string _sender_id; + std::string _receiver_id; + std::string _service_id; + std::string _status; + std::string _created_at; + std::string _updated_at; + + public: + NotificationModel(std::string id, std::string sender_id, std::string receiver_id, std::string service_id, std::string status); + + std::string get_id(); + std::string get_sender_id(); + std::string get_receiver_id(); + std::string get_service_id(); + std::string get_status(); +}; diff --git a/include/models/service_model.h b/include/models/service_model.h index 1693b77..2fda4b0 100755 --- a/include/models/service_model.h +++ b/include/models/service_model.h @@ -1,7 +1,6 @@ #pragma once #include - #include #include #include @@ -36,16 +35,3 @@ class ServiceModel { // * static ServiceModel create_notification(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price); }; - -/* -CREATE TABLE IF NOT EXISTS services ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - creator_id UUID REFERENCES users(id), - service_name VARCHAR(255) NOT NULL, - description TEXT, - price NUMERIC(10, 2), - status service_status DEFAULT 'OPEN', -- Estado del servicio: 'OPEN' o 'CLOSED' - usuario_venta_id UUID, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); */ diff --git a/include/models/user_model.h b/include/models/user_model.h index fd032ef..124fd0a 100755 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -1,44 +1,48 @@ #pragma once +#include +#include #include #include #include #include #include -#include "utils/errors.h" -#include "utils/user_validations.h" - class UserModel { private: std::string _id; - std::string _email; + std::string _community_id; std::string _username; - std::string _image_url; - int _balance; + std::string _email; std::string _type; + int _balance; + std::string _created_at; + std::string _updated_at; public: - UserModel(); - UserModel(std::string id); - UserModel(std::string id, std::string email, std::string username, std::string image_url, int balance, std::string type); + UserModel(std::string id, std::string community_id, std::string username, std::string email, std::string type, int balance, std::string created_at, std::string updated_at); std::string getId(); - std::string getEmail(); + std::string getCommunityId(); std::string getUsername(); - std::string getImageUrl(); - int getBalance(); + std::string getEmail(); std::string getType(); + int getBalance(); + std::string getCreatedAt(); + std::string getUpdatedAt(); + + static std::unique_ptr create_user(pqxx::connection& db, const std::string& community_id, const std::string& username, const std::string& email, const std::string& password, const std::string& type, const int balance); + static std::vector> get_users(pqxx::connection& db); + + static bool exists_id(pqxx::connection& db, const std::string& id); + static bool exists_username(pqxx::connection& db, const std::string& username); + static bool exists_email(pqxx::connection& db, const std::string& email); + + static std::unique_ptr get_user_by_id(pqxx::connection& db, const std::string& id); + static std::unique_ptr get_user_by_username(pqxx::connection& db, const std::string& username); + static std::unique_ptr get_user_by_email(pqxx::connection& db, const std::string& email); + static std::string get_password_by_email(pqxx::connection& db, const std::string& email); - static UserModel create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type); - static bool user_exist(pqxx::connection& db, std::string email, std::string ussername); - static bool user_exist_by_id(pqxx::connection& db, std::string id); - static std::vector get_users(pqxx::connection& db); - static void set_community_id(pqxx::connection& db, std::string community_id, std::string user_id); - - static std::unique_ptr get_user_by_email(pqxx::connection& db, std::string email); - static std::string get_password_by_email(pqxx::connection& db, std::string email); - static UserModel get_user_by_id(pqxx::connection& db, const std::string& id); - static bool delete_by_id(pqxx::connection& db, const std::string& id); - static bool update_by_id(pqxx::connection& db, const std::string& id, const std::string username, const std::string email, const std::string password); + static bool delete_user_by_id(pqxx::connection& db, const std::string& id); + static bool update_user_by_id(pqxx::connection& db, const std::string& id, const std::string username = "", const std::string email = "", const std::string password = ""); }; diff --git a/include/routes/auth_routes.h b/include/routes/auth_routes.h index e057d68..32ea7b1 100755 --- a/include/routes/auth_routes.h +++ b/include/routes/auth_routes.h @@ -1,10 +1,9 @@ #pragma once -#include "crow.h" -#include - -#include "utils/common.h" -#include "controllers/auth_controller.h" +#include +#include #include +#include +#include void initialize_auth_routes(NebyApp& app, pqxx::connection& db); diff --git a/include/routes/service_routes.h b/include/routes/service_routes.h index ea06bb4..93a873d 100755 --- a/include/routes/service_routes.h +++ b/include/routes/service_routes.h @@ -1,11 +1,9 @@ #pragma once -#include - -#include - #include #include +#include #include +#include -void initialize_service_routes(NebyApp& app, pqxx::connection& db); \ No newline at end of file +void initialize_service_routes(NebyApp& app, pqxx::connection& db); diff --git a/include/routes/user_routes.h b/include/routes/user_routes.h index df8d130..9544751 100755 --- a/include/routes/user_routes.h +++ b/include/routes/user_routes.h @@ -1,11 +1,9 @@ #pragma once +#include +#include #include - +#include #include -#include "controllers/user_controller.h" -#include "crow.h" -#include "utils/common.h" - void initialize_user_routes(NebyApp& app, pqxx::connection& db); diff --git a/include/utils/auth.h b/include/utils/auth.h index e065f99..cf12b46 100755 --- a/include/utils/auth.h +++ b/include/utils/auth.h @@ -2,7 +2,6 @@ #include #include - #include std::string create_token(const std::string& userId, const std::string& type); diff --git a/include/utils/common.h b/include/utils/common.h index bd13bd4..faa8a5e 100755 --- a/include/utils/common.h +++ b/include/utils/common.h @@ -1,12 +1,10 @@ #pragma once #include #include - +#include #include #include -#include "bcrypt/BCrypt.hpp" - using NebyApp = crow::App; struct Roles { @@ -28,4 +26,4 @@ struct ServiceStatus { struct ServiceType { static const std::string OFFERED; static const std::string REQUESTED; -}; \ No newline at end of file +}; diff --git a/include/utils/custom_regex.h b/include/utils/custom_regex.h index b61218e..9dcb7d0 100755 --- a/include/utils/custom_regex.h +++ b/include/utils/custom_regex.h @@ -1,7 +1,6 @@ #pragma once #include - #include #include #include diff --git a/include/utils/errors.h b/include/utils/errors.h index 1611b1b..182d1d2 100755 --- a/include/utils/errors.h +++ b/include/utils/errors.h @@ -11,4 +11,4 @@ class data_not_found_exception : public std::exception { public: data_not_found_exception(const std::string& msg); virtual const char* what() const noexcept override; -}; \ No newline at end of file +}; diff --git a/include/utils/user_validations.h b/include/utils/user_validations.h index 853dbd8..ed74ed0 100755 --- a/include/utils/user_validations.h +++ b/include/utils/user_validations.h @@ -1,14 +1,12 @@ #pragma once +#include #include #include #include #include - #include -#include "crow.h" - bool is_correct_body_register(const crow::request &req, crow::response &res); bool is_correct_body_login(const crow::request &req, crow::response &res); diff --git a/include/utils/utils.h b/include/utils/utils.h index f6ad346..178badb 100755 --- a/include/utils/utils.h +++ b/include/utils/utils.h @@ -1,9 +1,8 @@ #pragma once +#include #include -#include "crow.h" - void handle_exception(crow::response &res, const std::string &error_message); void handle_error(crow::response &res, const std::string &error_message, const int &status_code); diff --git a/include/utils/validations.h b/include/utils/validations.h index ccc31c7..9a82d70 100755 --- a/include/utils/validations.h +++ b/include/utils/validations.h @@ -1,5 +1,5 @@ #pragma once -#include "crow.h" +#include bool validate_required_body_fields(const crow::json::rvalue &body, const std::vector &required_fields, crow::response &res); diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index 50926de..c9334b1 100755 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -1,58 +1,54 @@ -#include "controllers/auth_controller.h" - +#include #include - +#include +#include #include // Include the ctime header for time functions #include #include -#include "bcrypt/BCrypt.hpp" -#include "utils/user_validations.h" - void AuthController::register_user(pqxx::connection &db, const crow::request &req, crow::response &res) { try { if (!is_correct_body_register(req, res)) return; crow::json::rvalue body = crow::json::load(req.body); - std::string password = body["password"].s(); std::string username = body["username"].s(); std::string email = body["email"].s(); + std::string password = body["password"].s(); std::string type = body["type"].s(); + std::string community_id; std::string hash = BCrypt::generateHash(password); - if (UserModel::user_exist(db, email, username)) { - handle_error(res, "user already exists", 400); + if (UserModel::exists_username(db, username)) { + handle_error(res, "username already in use", 400); return; } - // UserModel user = UserModel::create_user(db, hash, email, username, "", 0, type); - - UserModel user; + if (UserModel::exists_email(db, email)) { + handle_error(res, "email already in use", 400); + return; + } if (type == Roles::ADMIN) { - // ! check if community exist with name - std::string community_name = body["community_name"].s(); - CommunityModel community = CommunityModel::create_community(db, community_name); - user = UserModel::create_user(db, hash, email, username, "", 0, type); - UserModel::set_community_id(db, community.get_id(), user.getId()); - + CommunityModel community = CommunityModel::create_community(db, body["community_name"].s()); + community_id = community.get_id(); } else if (type == Roles::NEIGHBOR) { - // ! check if community exist with code - std::string community_code = body["community_code"].s(); - // ! TODO: - - std::string community_id = CommunityModel::get_community_id(db, community_code); + community_id = CommunityModel::get_community_id(db, body["community_code"].s()); if (community_id == "") { - handle_error(res, "not community exists", 404); + handle_error(res, "community does not exist", 404); return; } - user = UserModel::create_user(db, hash, email, username, "", 0, type); - UserModel::set_community_id(db, community_id, user.getId()); } - std::string jwtToken = create_token(user.getId(), type); + std::unique_ptr user = UserModel::create_user(db, community_id, username, email, hash, type, 0); + + if (!user) { + handle_error(res, "internal server error", 500); + return; + } + + std::string jwtToken = create_token(user.get()->getId(), type); int expirationTimeSeconds = 3600; time_t now = time(0); time_t expirationTime = now + expirationTimeSeconds; @@ -71,7 +67,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re res.set_header("Set-Cookie", cookieStream.str()); crow::json::wvalue data({ - {"id", user.getId()}, + {"id", user.get()->getId()}, }); res.code = 201; @@ -79,7 +75,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re res.end(); } catch (const std::exception &e) { std::cerr << "Error in register_user: " << e.what() << std::endl; - handle_error(res, "INTERNAL SERVER ERROR", 500); + handle_error(res, "internal server error", 500); } } @@ -104,7 +100,7 @@ void AuthController::login_user(pqxx::connection &db, const crow::request &req, std::unique_ptr user = UserModel::get_user_by_email(db, email); if (!user) { - handle_error(res, "user by email not exist", 404); + handle_error(res, "no user found with this email", 404); return; } diff --git a/src/controllers/service_controller.cpp b/src/controllers/service_controller.cpp index d1aa19e..3207641 100755 --- a/src/controllers/service_controller.cpp +++ b/src/controllers/service_controller.cpp @@ -52,7 +52,7 @@ void ServiceController::get_services(pqxx::connection &db, const crow::request & if (!status) { allServices = ServiceModel::get_services(db); - } else if (status && (std::string(status) == "OPEN" || std::string(status) == "CLOSED")) { + } else if (status && (std::string(status) == "open" || std::string(status) == "closed")) { allServices = ServiceModel::get_services(db, status); } else { handle_error(res, "status not valid value", 400); @@ -75,11 +75,11 @@ void ServiceController::get_services(pqxx::connection &db, const crow::request & } crow::json::wvalue data{{"services", services}}; - + res.write(data.dump()); res.code = 200; res.end(); } catch (const std::exception &e) { handle_error(res, e.what(), 500); } -} \ No newline at end of file +} diff --git a/src/controllers/user_controller.cpp b/src/controllers/user_controller.cpp index a7ad8ae..fdac7df 100755 --- a/src/controllers/user_controller.cpp +++ b/src/controllers/user_controller.cpp @@ -1,17 +1,19 @@ -#include "controllers/user_controller.h" +#include void UserController::get_users(pqxx::connection &db, const crow::request &req, crow::response &res) { try { - std::vector allUsers = UserModel::get_users(db); + std::vector> allUsers = UserModel::get_users(db); crow::json::wvalue::list users; - for (unsigned int i = 0; i < allUsers.size(); i++) { + for (unsigned int i = 0; i < allUsers.size(); ++i) { crow::json::wvalue user; - user["id"] = allUsers[i].getId(); - user["email"] = allUsers[i].getEmail(); - user["username"] = allUsers[i].getUsername(); - user["image_url"] = allUsers[i].getImageUrl(); - user["balance"] = allUsers[i].getBalance(); - user["type"] = allUsers[i].getType(); + user["id"] = allUsers[i].get()->getId(); + user["community_id"] = allUsers[i].get()->getCommunityId(); + user["username"] = allUsers[i].get()->getUsername(); + user["email"] = allUsers[i].get()->getEmail(); + user["type"] = allUsers[i].get()->getType(); + user["balance"] = allUsers[i].get()->getBalance(); + user["created_at"] = allUsers[i].get()->getCreatedAt(); + user["updated_at"] = allUsers[i].get()->getUpdatedAt(); users.push_back(user); } crow::json::wvalue data{{"users", users}}; @@ -20,32 +22,33 @@ void UserController::get_users(pqxx::connection &db, const crow::request &req, c res.end(); } catch (const std::exception &e) { std::cerr << "Error in get users: " << e.what() << std::endl; - res.code = 500; - crow::json::wvalue error({{"error", "internal server error"}}); - res.write(error.dump()); - res.end(); + handle_error(res, "internal server error", 500); } } -void UserController::get_user_by_id(pqxx::connection &db, const crow::request &req, const std::string &user_id, crow::response &res) { +void UserController::get_user_by_id(pqxx::connection &db, const crow::request &req, crow::response &res, const std::string &user_id) { try { if (user_id.empty()) { - res.code = 400; - crow::json::wvalue error({{"error", "id doesn't exist"}}); - res.write(error.dump()); - res.end(); + handle_error(res, "id must be provided", 400); return; } - UserModel user = UserModel::get_user_by_id(db, user_id); + std::unique_ptr user = UserModel::get_user_by_id(db, user_id); + + if (!user) { + handle_error(res, "user not found", 404); + return; + } crow::json::wvalue user_data; - user_data["id"] = user.getId(); - user_data["email"] = user.getEmail(); - user_data["username"] = user.getUsername(); - user_data["image_url"] = user.getImageUrl(); - user_data["balance"] = user.getBalance(); - user_data["type"] = user.getType(); + user_data["id"] = user.get()->getId(); + user_data["community_id"] = user.get()->getCommunityId(); + user_data["username"] = user.get()->getUsername(); + user_data["email"] = user.get()->getEmail(); + user_data["type"] = user.get()->getType(); + user_data["balance"] = user.get()->getBalance(); + user_data["created_at"] = user.get()->getCreatedAt(); + user_data["updated_at"] = user.get()->getUpdatedAt(); crow::json::wvalue data{{"user", user_data}}; res.code = 200; @@ -65,88 +68,56 @@ void UserController::get_user_by_id(pqxx::connection &db, const crow::request &r } } -void UserController::delete_user_by_id(pqxx::connection &db, const std::string &user_id, crow::response &res) { +void UserController::delete_user_by_id(pqxx::connection &db, crow::response &res, const std::string &user_id) { try { - bool elimin = UserModel::delete_by_id(db, user_id); + bool deleted = UserModel::delete_user_by_id(db, user_id); - if (elimin) { + if (deleted) { res.code = 200; crow::json::wvalue response_message; - response_message["message"] = "User deleted successfully"; + response_message["message"] = "user deleted successfully"; res.write(response_message.dump()); - } else { - res.code = 404; - crow::json::wvalue error_message; - error_message["error"] = "User not found"; - res.write(error_message.dump()); - } - res.end(); + res.end(); + } else + handle_error(res, "user not found", 404); } catch (const std::exception &e) { std::cerr << "Error deleting user: " << e.what() << std::endl; - res.code = 500; - res.end(); + handle_error(res, "internal server error", 500); } } -void UserController::update_user_by_id(pqxx::connection &db, const std::string &user_id, const crow::request &req, crow::response &res) { +void UserController::update_user_by_id(pqxx::connection &db, const crow::request &req, crow::response &res, const std::string &user_id) { try { - bool userFound = UserModel::user_exist_by_id(db, user_id); + bool userFound = UserModel::exists_id(db, user_id); if (userFound) { crow::json::rvalue update = crow::json::load(req.body); std::string temp_name = "", temp_pass = "", temp_email = ""; if (update.has("username")) { temp_name = update["username"].s(); - if (!validate_username(temp_name, res)) { - res.code = 400; - crow::json::wvalue error_message; - error_message["error"] = "incorrect username"; - res.write(error_message.dump()); - return; - } + if (!validate_username(temp_name, res)) return; } if (update.has("email")) { temp_email = update["email"].s(); - if (!validate_email(temp_email, res)) { - res.code = 400; - crow::json::wvalue error_message; - error_message["error"] = "incorrect email"; - res.write(error_message.dump()); - return; - } + if (!validate_email(temp_email, res)) return; } if (update.has("password")) { temp_pass = update["password"].s(); - if (!validate_password(temp_pass, res)) { - res.code = 400; - crow::json::wvalue error_message; - error_message["error"] = "incorrect password"; - res.write(error_message.dump()); - return; - } + if (!validate_password(temp_pass, res)) return; } std::string hash = BCrypt::generateHash(temp_pass); - bool succes = UserModel::update_by_id(db, user_id, temp_name, temp_email, hash); + bool succes = UserModel::update_user_by_id(db, user_id, temp_name, temp_email, hash); if (succes) { res.code = 200; crow::json::wvalue response_message; response_message["message"] = "User updated successfully"; res.write(response_message.dump()); - } else { - res.code = 400; - crow::json::wvalue error_message; - error_message["error"] = "Failed to update user"; - res.write(error_message.dump()); - } - res.end(); - } else { - res.code = 404; - crow::json::wvalue error_message; - error_message["error"] = "User not found"; - res.write(error_message.dump()); - } + res.end(); + } else + handle_error(res, "internal server error", 500); + } else + handle_error(res, "user not found", 404); } catch (const std::exception &e) { std::cerr << "Error updating user: " << e.what() << std::endl; - res.code = 500; - res.end(); + handle_error(res, "internal server error", 500); } } diff --git a/src/main.cpp b/src/main.cpp index 4469716..41e6298 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,16 +1,14 @@ +#include +#include #include - +#include +#include #include #include #include #include #include -#include "crow.h" -#include "routes/auth_routes.h" -#include "routes/user_routes.h" -#include "utils/common.h" - int main() { try { int HTTP_PORT = std::stoi(std::getenv("HTTP_PORT")); diff --git a/src/models/community_model.cpp b/src/models/community_model.cpp index 92696b8..54807d9 100755 --- a/src/models/community_model.cpp +++ b/src/models/community_model.cpp @@ -22,15 +22,14 @@ CommunityModel CommunityModel::create_community(pqxx::connection& db, std::strin std::string community_code = generate_community_code(); - txn.exec_params("INSERT INTO communities (community_name, community_code) VALUES ($1, $2)", community_name, community_code); - - pqxx::result result = txn.exec_params("SELECT id FROM communities WHERE community_code = $1", community_code); - - std::string res = result[0][0].as(); + pqxx::result result = txn.exec_params("INSERT INTO communities (community_name, community_code) VALUES ($1, $2) RETURNING id, community_name, community_code", community_name, community_code); txn.commit(); - return CommunityModel(res, community_code, community_name); + return CommunityModel( + result[0]["id"].as(), + result[0]["community_code"].as(), + result[0]["community_name"].as()); } std::string CommunityModel::get_community_id(pqxx::connection& db, std::string community_code) { @@ -69,4 +68,4 @@ std::string CommunityModel::get_community_name() const { std::string CommunityModel::get_community_code() const { return _community_code; -} \ No newline at end of file +} diff --git a/src/models/service_model.cpp b/src/models/service_model.cpp index 3ea7c99..5c17744 100755 --- a/src/models/service_model.cpp +++ b/src/models/service_model.cpp @@ -16,11 +16,12 @@ std::string ServiceModel::get_buyer_user_id() { return _buyer_user_id; } std::unique_ptr ServiceModel::create_service(pqxx::connection& db, std::string creator_id, std::string title, std::string description, int price, std::string type, bool isThrow) { pqxx::work txn(db); - pqxx::result result = txn.exec_params("INSERT INTO services (creator_id, title, description, price, type) VALUES ($1, $2, $3, $4, $5) RETURNING id", - creator_id, - title, - description, - price, type); + pqxx::result result = txn.exec_params( + "INSERT INTO services (creator_id, title, description, price, type) VALUES ($1, $2, $3, $4, $5) RETURNING id", + creator_id, + title, + description, + price, type); txn.commit(); @@ -34,7 +35,7 @@ std::unique_ptr ServiceModel::create_service(pqxx::connection& db, std::string service_id = result[0]["id"].as(); - return std::make_unique(service_id, creator_id, title, description, price, "OPEN", type); + return std::make_unique(service_id, creator_id, title, description, price, "open", type); } std::vector ServiceModel::get_services(pqxx::connection& db, std::string status) { diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 9bad22d..9edb5eb 100755 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -1,137 +1,115 @@ -#include "models/user_model.h" +#include -UserModel::UserModel(std::string id) : _id(id) {} +UserModel::UserModel(std::string id, std::string community_id, std::string username, std::string email, std::string type, int balance, std::string created_at, std::string updated_at) : _id(id), _community_id(community_id), _username(username), _email(email), _type(type), _balance(balance), _created_at(created_at), _updated_at(updated_at) {} -UserModel::UserModel() : _id("") {} +std::string UserModel::getId() { return _id; } +std::string UserModel::getCommunityId() { return _community_id; } +std::string UserModel::getUsername() { return _username; } +std::string UserModel::getEmail() { return _email; } +std::string UserModel::getType() { return _type; } +int UserModel::getBalance() { return _balance; } +std::string UserModel::getCreatedAt() { return _created_at; } +std::string UserModel::getUpdatedAt() { return _updated_at; } -UserModel::UserModel(std::string id, std::string email, std::string username, std::string image_url, int balance, std::string type) : _id(id), _email(email), _username(username), _image_url(image_url), _balance(balance), _type(type) {} - -std::string UserModel::getId() { - return _id; -} - -std::string UserModel::getEmail() { - return _email; -} +std::unique_ptr UserModel::create_user(pqxx::connection& db, const std::string& community_id, const std::string& username, const std::string& email, const std::string& password, const std::string& type, const int balance) { + pqxx::work txn(db); -std::string UserModel::getUsername() { - return _username; -} + pqxx::result result = txn.exec_params("INSERT INTO users (community_id, username, email, password, type, balance) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, community_id, username, email, type, balance, created_at, updated_at", community_id, username, email, password, type, balance); -std::string UserModel::getImageUrl() { - return _image_url; -} + txn.commit(); -int UserModel::getBalance() { - return _balance; + return std::make_unique( + result[0]["id"].as(), + result[0]["community_id"].as(), + result[0]["username"].as(), + result[0]["email"].as(), + result[0]["type"].as(), + result[0]["balance"].as(), + result[0]["created_at"].as(), + result[0]["updated_at"].as()); } -std::string UserModel::getType() { - return _type; -} +std::vector> UserModel::get_users(pqxx::connection& db) { + std::vector> all_users; -UserModel UserModel::create_user(pqxx::connection& db, std::string password, std::string email, std::string username, std::string image_url, int balance, std::string type) { pqxx::work txn(db); - txn.exec_params("INSERT INTO users (password, email, username, image_url, balance, type) VALUES ($1, $2, $3, $4, $5, $6)", password, email, username, image_url, balance, type); - - pqxx::result result = txn.exec_params("SELECT id FROM users WHERE email = $1", email); + pqxx::result result = txn.exec("SELECT id, community_id, username, email, type, balance, created_at, updated_at FROM users"); - std::string res = result[0][0].as(); txn.commit(); - return UserModel(res); -} - -bool UserModel::user_exist(pqxx::connection& db, std::string email, std::string username) { - try { - pqxx::work txn(db); - - pqxx::result result = txn.exec_params("SELECT username FROM users WHERE email = $1 OR username = $2", email, username); - - bool userExists = !result.empty() && !result[0][0].is_null(); - - txn.commit(); - return userExists; - } catch (const std::exception& e) { - return false; + for (const auto& row : result) { + all_users.push_back(std::make_unique( + row["id"].as(), + row["community_id"].as(), + row["username"].as(), + row["email"].as(), + row["type"].as(), + row["balance"].as(), + row["created_at"].as(), + row["updated_at"].as())); } + + return all_users; } -bool UserModel::user_exist_by_id(pqxx::connection& db, std::string id) { +bool exists_user(pqxx::connection& db, const std::string& column, const std::string& value) { try { pqxx::work txn(db); - - pqxx::result result = txn.exec_params("SELECT username FROM users WHERE id = $1", id); - - bool userExists = !result.empty() && !result[0][0].is_null(); - + pqxx::result result = txn.exec_params(std::format("SELECT id FROM users WHERE {} = $1", column), value); txn.commit(); - + bool userExists = !result.empty() && !result[0][0].is_null(); return userExists; } catch (const std::exception& e) { return false; } } -std::vector UserModel::get_users(pqxx::connection& db) { - std::vector all_users; - - pqxx::work txn(db); - - pqxx::result result = txn.exec("SELECT id, email, username, image_url, balance, type FROM users"); - - for (const auto& row : result) { - UserModel user(row["id"].as(), row["email"].as(), row["username"].as(), row["image_url"].as(), row["balance"].as(), row["type"].as()); - - all_users.push_back(user); - } - - txn.commit(); - - return all_users; +bool UserModel::exists_id(pqxx::connection& db, const std::string& id) { + return exists_user(db, "id", id); } - -void UserModel::set_community_id(pqxx::connection& db, std::string community_id, std::string user_id) { - try { - pqxx::work txn(db); - - // Actualizar el community_id del usuario en la base de datos - txn.exec_params("UPDATE users SET community_id = $1 WHERE id = $2", community_id, user_id); - - txn.commit(); - } catch (const std::exception& e) { - std::cerr << "Error al establecer el community_id para el usuario " << user_id << ": " << e.what() << std::endl; - } +bool UserModel::exists_username(pqxx::connection& db, const std::string& username) { + return exists_user(db, "username", username); +} +bool UserModel::exists_email(pqxx::connection& db, const std::string& email) { + return exists_user(db, "email", email); } -std::unique_ptr UserModel::get_user_by_email(pqxx::connection& db, std::string email) { +std::unique_ptr get_user(pqxx::connection& db, const std::string& column, const std::string& value) { try { pqxx::work txn(db); - pqxx::result result = txn.exec_params("SELECT id, email, username, image_url, balance, type FROM users WHERE email = $1", email); + pqxx::result result = txn.exec_params(std::format("SELECT id, community_id, username, email, type, balance, created_at, updated_at FROM users WHERE {} = $1", column), value); txn.commit(); - // Si no se encontró ningún usuario con ese correo electrónico, devolver nullptr - if (result.empty()) - return nullptr; - - // Si se encontró un usuario, crear una instancia de UserModel y devolverla - pqxx::row row = result[0]; - return std::make_unique(row["id"].as(), - row["email"].as(), - row["username"].as(), - row["image_url"].as(), - row["balance"].as(), - row["type"].as()); + if (result.empty()) return nullptr; + + return std::make_unique( + result[0]["id"].as(), + result[0]["community_id"].as(), + result[0]["username"].as(), + result[0]["email"].as(), + result[0]["type"].as(), + result[0]["balance"].as(), + result[0]["created_at"].as(), + result[0]["updated_at"].as()); } catch (const std::exception& e) { - std::cerr << "Error al obtener el usuario por correo electrónico: " << e.what() << std::endl; return nullptr; } } -std::string UserModel::get_password_by_email(pqxx::connection& db, std::string email) { +std::unique_ptr UserModel::get_user_by_id(pqxx::connection& db, const std::string& id) { + return get_user(db, "id", id); +} +std::unique_ptr UserModel::get_user_by_username(pqxx::connection& db, const std::string& username) { + return get_user(db, "username", username); +} +std::unique_ptr UserModel::get_user_by_email(pqxx::connection& db, const std::string& email) { + return get_user(db, "email", email); +} + +std::string UserModel::get_password_by_email(pqxx::connection& db, const std::string& email) { try { pqxx::work txn(db); @@ -139,56 +117,37 @@ std::string UserModel::get_password_by_email(pqxx::connection& db, std::string e txn.commit(); - // Si no se encontró ningún usuario con ese correo electrónico, devolver una cadena vacía - if (result.empty()) - return ""; + if (result.empty()) return ""; - // Si se encontró un usuario, devolver su contraseña return result[0]["password"].as(); } catch (const std::exception& e) { - std::cerr << "Error al obtener la contraseña por correo electrónico: " << e.what() << std::endl; return ""; } } -UserModel UserModel::get_user_by_id(pqxx::connection& db, const std::string& id) { - pqxx::work txn(db); - pqxx::result result = txn.exec_params("SELECT id, email, username, image_url, balance, type FROM users WHERE id = $1", id); - if (result.empty()) throw data_not_found_exception("user doesn't exist"); - const auto& row = result[0]; - UserModel user(row["id"].as(), row["email"].as(), row["username"].as(), row["image_url"].as(), row["balance"].as(), row["type"].as()); - - txn.commit(); - return user; -} - -bool UserModel::delete_by_id(pqxx::connection& db, const std::string& id) { +bool UserModel::delete_user_by_id(pqxx::connection& db, const std::string& id) { try { pqxx::work txn(db); - pqxx::result result = txn.exec_params("DELETE FROM users WHERE id = $1", id); + pqxx::result result = txn.exec_params("DELETE FROM users WHERE id = $1 RETURNING id", id); txn.commit(); - return true; + if (!result.empty() && !result[0][0].is_null()) return true; + + return false; } catch (const std::exception& e) { std::cerr << "Failed to delete user: " << e.what() << std::endl; return false; } } -bool UserModel::update_by_id(pqxx::connection& db, const std::string& id, const std::string username, const std::string email, const std::string password) { +bool UserModel::update_user_by_id(pqxx::connection& db, const std::string& id, const std::string username, const std::string email, const std::string password) { try { pqxx::work txn(db); - if (username != "") { - pqxx::result result = txn.exec_params("UPDATE users SET username = $1 WHERE id = $2", username, id); - } - if (email != "") { - pqxx::result result = txn.exec_params("UPDATE users SET email = $1 WHERE id = $2", email, id); - } - if (password != "") { - pqxx::result result = txn.exec_params("UPDATE users SET password = $1 WHERE id = $2", password, id); - } + if (username != "") pqxx::result result = txn.exec_params("UPDATE users SET username = $1 WHERE id = $2", username, id); + if (email != "") pqxx::result result = txn.exec_params("UPDATE users SET email = $1 WHERE id = $2", email, id); + if (password != "") pqxx::result result = txn.exec_params("UPDATE users SET password = $1 WHERE id = $2", password, id); txn.commit(); return true; } catch (const std::exception& e) { diff --git a/src/routes/auth_routes.cpp b/src/routes/auth_routes.cpp index bef6148..50e3506 100755 --- a/src/routes/auth_routes.cpp +++ b/src/routes/auth_routes.cpp @@ -7,10 +7,7 @@ void initialize_auth_routes(NebyApp& app, pqxx::connection& db) { }); CROW_ROUTE(app, "/api/auth/login") - .methods(crow::HTTPMethod::POST) - ([&db](const crow::request& req, crow::response& res) { + .methods(crow::HTTPMethod::POST)([&db](const crow::request& req, crow::response& res) { AuthController::login_user(db, req, res); }); } - -// ** middleware .CROW_MIDDLEWARES(app, VerifyJWT) diff --git a/src/routes/service_routes.cpp b/src/routes/service_routes.cpp index 5a28293..80a6b55 100755 --- a/src/routes/service_routes.cpp +++ b/src/routes/service_routes.cpp @@ -18,4 +18,4 @@ void initialize_service_routes(NebyApp& app, pqxx::connection& db) { // ** PUT /api/services/:id // ** DELETE /api/services/:id -} \ No newline at end of file +} diff --git a/src/routes/user_routes.cpp b/src/routes/user_routes.cpp index 57ea6f3..4951853 100755 --- a/src/routes/user_routes.cpp +++ b/src/routes/user_routes.cpp @@ -1,4 +1,4 @@ -#include "routes/user_routes.h" +#include void initialize_user_routes(NebyApp& app, pqxx::connection& db) { CROW_ROUTE(app, "/api/users").methods(crow::HTTPMethod::GET).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res) { @@ -6,14 +6,14 @@ void initialize_user_routes(NebyApp& app, pqxx::connection& db) { }); CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::GET).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { - UserController::get_user_by_id(db, req, user_id, res); + UserController::get_user_by_id(db, req, res, user_id); }); CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::DELETE).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { - UserController::delete_user_by_id(db, user_id, res); + UserController::delete_user_by_id(db, res, user_id); }); CROW_ROUTE(app, "/api/users/").methods(crow::HTTPMethod::PUT).CROW_MIDDLEWARES(app, VerifyJWT)([&db](const crow::request& req, crow::response& res, const std::string& user_id) { - UserController::update_user_by_id(db, user_id, req, res); + UserController::update_user_by_id(db, req, res, user_id); }); } diff --git a/src/utils/auth.cpp b/src/utils/auth.cpp index 7da0597..9b9172d 100755 --- a/src/utils/auth.cpp +++ b/src/utils/auth.cpp @@ -46,4 +46,4 @@ std::string get_token_cookie(crow::request& req) { } return ""; -} \ No newline at end of file +} diff --git a/src/utils/common.cpp b/src/utils/common.cpp index a0a45a1..dbde653 100755 --- a/src/utils/common.cpp +++ b/src/utils/common.cpp @@ -1,14 +1,14 @@ -#include "utils/common.h" +#include const std::string Roles::ADMIN = "admin"; const std::string Roles::NEIGHBOR = "neighbor"; -const std::string NotificationStatus::PENDING = "PENDING"; -const std::string NotificationStatus::ACCEPTED = "ACCEPTED"; -const std::string NotificationStatus::REFUSED = "REFUSED"; +const std::string NotificationStatus::PENDING = "pending"; +const std::string NotificationStatus::ACCEPTED = "accepted"; +const std::string NotificationStatus::REFUSED = "refused"; -const std::string ServiceStatus::CLOSED = "CLOSED"; -const std::string ServiceStatus::OPEN = "OPEN"; +const std::string ServiceStatus::CLOSED = "closed"; +const std::string ServiceStatus::OPEN = "open"; -const std::string ServiceType::OFFERED = "OFFERED"; -const std::string ServiceType::REQUESTED = "REQUESTED"; +const std::string ServiceType::OFFERED = "offered"; +const std::string ServiceType::REQUESTED = "requested"; diff --git a/src/utils/errors.cpp b/src/utils/errors.cpp index 8b37087..96943cd 100755 --- a/src/utils/errors.cpp +++ b/src/utils/errors.cpp @@ -2,7 +2,6 @@ data_not_found_exception::data_not_found_exception(const std::string& msg) : _message(msg) {} -const char* data_not_found_exception::what() const noexcept { +const char* data_not_found_exception::what() const noexcept { return _message.c_str(); } - diff --git a/src/utils/user_validations.cpp b/src/utils/user_validations.cpp index d122147..d8736c6 100755 --- a/src/utils/user_validations.cpp +++ b/src/utils/user_validations.cpp @@ -1,4 +1,4 @@ -#include "utils/user_validations.h" +#include bool is_correct_body_login(const crow::request &req, crow::response &res) { try { diff --git a/test/tests/auth_routes_test.cpp b/test/tests/auth_routes_test.cpp index 1a5643e..8c32f87 100755 --- a/test/tests/auth_routes_test.cpp +++ b/test/tests/auth_routes_test.cpp @@ -1,14 +1,12 @@ +#include #include #include - #include // Para std::getenv #include #include #include #include -#include "common.h" - // Declaración de la función limpiarTablaUsers void limpiarTablaUsers() { try { @@ -411,4 +409,4 @@ TEST_F(RegisterGeneralErrors, Community_Not_Exists) { ASSERT_TRUE(json.contains("error")); std::string error_message_username = json["error"]; EXPECT_EQ(error_message_username, "not community exists"); -} \ No newline at end of file +} diff --git a/test/tests/common.h b/test/tests/common.h index fd6ba1f..1a00ca0 100755 --- a/test/tests/common.h +++ b/test/tests/common.h @@ -1,7 +1,6 @@ - int HTTP_PORT = std::stoi(std::getenv("HTTP_PORT")); std::string DB_NAME = std::string(std::getenv("DB_NAME")); std::string DB_USER = std::string(std::getenv("DB_USER")); std::string DB_PASSWORD = std::string(std::getenv("DB_PASSWORD")); std::string DB_HOST = std::string(std::getenv("DB_HOST")); -std::string DB_PORT = std::string(std::getenv("DB_PORT")); \ No newline at end of file +std::string DB_PORT = std::string(std::getenv("DB_PORT")); diff --git a/test/tests/main.cpp b/test/tests/main.cpp index 926358d..a115f6f 100755 --- a/test/tests/main.cpp +++ b/test/tests/main.cpp @@ -1,5 +1,4 @@ #include - #include int main(int argc, char** argv) { diff --git a/test/tests/test_one.cpp b/test/tests/test_one.cpp index 1ed0154..64b146b 100755 --- a/test/tests/test_one.cpp +++ b/test/tests/test_one.cpp @@ -1,12 +1,10 @@ #include #include - #include // Para std::getenv #include #include #include - int sum(int a, int b) { return a + b; } @@ -96,4 +94,4 @@ std::string simulateLoginAndGetToken() { ASSERT_TRUE(user.contains("type")); } } - */ \ No newline at end of file + */ From 83352b26036dc27fbb0419a31f1e0dcde014572e Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Thu, 28 Mar 2024 11:14:13 +0100 Subject: [PATCH 44/48] task #104: unified hot reload scripts --- Dockerfile.dev | 5 ++--- Dockerfile.test | 7 +++---- hot-reload-test.sh | 41 ----------------------------------------- hot-reload.sh | 11 ++++++++++- 4 files changed, 15 insertions(+), 49 deletions(-) delete mode 100755 hot-reload-test.sh diff --git a/Dockerfile.dev b/Dockerfile.dev index 218d98b..39e044c 100755 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,6 +1,6 @@ FROM gcc:latest -RUN apt-get update && apt-get install -y libpqxx-dev libboost-dev cmake inotify-tools git +RUN apt-get update && apt-get install -y libpqxx-dev cmake inotify-tools git WORKDIR /app @@ -22,7 +22,6 @@ RUN git clone https://github.com/arun11299/cpp-jwt.git &&\ make install && \ ldconfig - RUN chmod 777 hot-reload.sh -CMD ["./hot-reload.sh"] +CMD ["./hot-reload.sh", "./backend"] diff --git a/Dockerfile.test b/Dockerfile.test index 800bcec..9d810d4 100755 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,6 +1,6 @@ FROM gcc:latest -RUN apt-get update && apt-get install -y libpqxx-dev cmake inotify-tools git +RUN apt-get update && apt-get install -y libpqxx-dev libboost-dev cmake inotify-tools git WORKDIR /app @@ -12,7 +12,6 @@ RUN cd test && \ git submodule add https://github.com/whoshuu/cpr.git extern/cpr && \ ldconfig +RUN chmod 777 hot-reload.sh -RUN chmod 777 hot-reload-test.sh - -CMD ["./hot-reload-test.sh"] +CMD ["./hot-reload.sh", "./test", "./test/"] diff --git a/hot-reload-test.sh b/hot-reload-test.sh deleted file mode 100755 index f716b72..0000000 --- a/hot-reload-test.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -compile_backend() { - echo "Compiling..." - cmake . - make -} - -run_backend() { - ./test & - BACKEND_PID=$! - echo "Running process with PID: $BACKEND_PID" -} - -monitor_changes() { - echo "Monitoring for changes..." - while true; do - inotifywait -r -e modify,move,create,delete ./ - - kill_backend - compile_backend - run_backend - done -} - -kill_backend() { - if [[ -n $BACKEND_PID ]]; then - echo "Killing process with PID: $BACKEND_PID" - kill $BACKEND_PID - wait $BACKEND_PID 2>/dev/null - fi -} - -main() { - cd test/ - compile_backend - run_backend - monitor_changes -} - -main diff --git a/hot-reload.sh b/hot-reload.sh index e66d00e..51d3db2 100755 --- a/hot-reload.sh +++ b/hot-reload.sh @@ -1,5 +1,10 @@ #!/bin/bash +if [ $# -eq 0 ]; then + echo "Usage: $0 []" + exit 1 +fi + compile_backend() { echo "Compiling..." cmake . @@ -7,7 +12,7 @@ compile_backend() { } run_backend() { - ./backend & + source "$1" & BACKEND_PID=$! echo "Running process with PID: $BACKEND_PID" } @@ -37,4 +42,8 @@ main() { monitor_changes } +if [ -d "$2" ]; then + cd "$2" || exit 1 +fi + main From 218597c9dff7ef68ca8bc0e46f5c368ded9672e0 Mon Sep 17 00:00:00 2001 From: Voooigt Date: Thu, 28 Mar 2024 14:26:40 +0100 Subject: [PATCH 45/48] task #67: validate community name does not exist --- include/models/community_model.h | 3 ++- src/models/community_model.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/models/community_model.h b/include/models/community_model.h index f11a325..6860b48 100755 --- a/include/models/community_model.h +++ b/include/models/community_model.h @@ -21,7 +21,8 @@ class CommunityModel { static std::string generate_community_code(); static CommunityModel create_community(pqxx::connection& db, std::string community_name); - static std::string get_community_id(pqxx::connection &db, std::string community_code); + static std::string get_community_id(pqxx::connection& db, std::string community_code); + static bool comunity_exist(pqxx::connection& db, std::string community_name); }; #endif \ No newline at end of file diff --git a/src/models/community_model.cpp b/src/models/community_model.cpp index 92696b8..d021e9e 100755 --- a/src/models/community_model.cpp +++ b/src/models/community_model.cpp @@ -69,4 +69,20 @@ std::string CommunityModel::get_community_name() const { std::string CommunityModel::get_community_code() const { return _community_code; +} + +bool CommunityModel::comunity_exist(pqxx::connection& db, std::string community_name) { + try { + pqxx::work txn(db); + + pqxx::result result = txn.exec_params("SELECT community_name FROM communities WHERE community_name = $1", community_name); + + bool communityExists = !result.empty() && !result[0][0].is_null(); + + txn.commit(); + + return communityExists; + } catch (const std::exception& e) { + return false; + } } \ No newline at end of file From ead96c343efea6ad9f31a4e60fe03297a6bc19f0 Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Fri, 29 Mar 2024 15:23:32 +0100 Subject: [PATCH 46/48] task #104: updated models and general refactor --- Dockerfile.dev | 2 +- hot-reload.sh | 19 ++-- include/controllers/service_controller.h | 1 + include/models/community_model.h | 22 +++-- include/models/service_model.h | 35 +++++--- include/models/user_model.h | 26 +++--- include/utils/errors.h | 27 ++++++ src/controllers/auth_controller.cpp | 23 +++-- src/controllers/service_controller.cpp | 85 +++++++++--------- src/controllers/user_controller.cpp | 32 +++---- src/models/community_model.cpp | 81 ++++++++--------- src/models/service_model.cpp | 105 +++++++++++++++-------- src/models/user_model.cpp | 82 +++++++++--------- src/utils/errors.cpp | 18 ++++ 14 files changed, 332 insertions(+), 226 deletions(-) diff --git a/Dockerfile.dev b/Dockerfile.dev index 39e044c..d4dd72b 100755 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,6 +1,6 @@ FROM gcc:latest -RUN apt-get update && apt-get install -y libpqxx-dev cmake inotify-tools git +RUN apt-get update && apt-get install -y libpqxx-dev libboost-dev cmake inotify-tools git WORKDIR /app diff --git a/hot-reload.sh b/hot-reload.sh index 51d3db2..f7c9f0c 100755 --- a/hot-reload.sh +++ b/hot-reload.sh @@ -12,7 +12,7 @@ compile_backend() { } run_backend() { - source "$1" & + "$1" & BACKEND_PID=$! echo "Running process with PID: $BACKEND_PID" } @@ -21,10 +21,9 @@ monitor_changes() { echo "Monitoring for changes..." while true; do inotifywait -r -e modify,move,create,delete ./ - kill_backend compile_backend - run_backend + run_backend $1 done } @@ -37,13 +36,13 @@ kill_backend() { } main() { + if [ -d "$2" ]; then + cd "$2" || exit 1 + fi + compile_backend - run_backend - monitor_changes + run_backend $1 + monitor_changes $1 } -if [ -d "$2" ]; then - cd "$2" || exit 1 -fi - -main +main $1 $2 diff --git a/include/controllers/service_controller.h b/include/controllers/service_controller.h index 2bd0f69..7f83356 100755 --- a/include/controllers/service_controller.h +++ b/include/controllers/service_controller.h @@ -9,6 +9,7 @@ // ** custom includes #include +#include #include #include #include diff --git a/include/models/community_model.h b/include/models/community_model.h index 35d6706..551cfa9 100755 --- a/include/models/community_model.h +++ b/include/models/community_model.h @@ -1,24 +1,32 @@ #pragma once +#include #include +#include +#include #include #include class CommunityModel { private: std::string _id; - std::string _community_name; - std::string _community_code; + std::string _name; + std::string _code; + std::string _created_at; + std::string _updated_at; public: - CommunityModel(std::string id, std::string community_name, std::string community_code); + CommunityModel(std::string id, std::string name, std::string code, std::string created_at, std::string updated_at); std::string get_id() const; - std::string get_community_name() const; - std::string get_community_code() const; + std::string get_name() const; + std::string get_code() const; + std::string get_created_at() const; + std::string get_updated_at() const; static std::string generate_community_code(); - static CommunityModel create_community(pqxx::connection& db, std::string community_name); - static std::string get_community_id(pqxx::connection& db, std::string community_code); + static std::unique_ptr create_community(pqxx::connection& db, const std::string& name, bool throw_when_null = false); + static std::unique_ptr get_community_by_id(pqxx::connection& db, const std::string& id, bool throw_when_null = false); + static std::unique_ptr get_community_by_code(pqxx::connection& db, const std::string& code, bool throw_when_null = false); }; diff --git a/include/models/service_model.h b/include/models/service_model.h index 2fda4b0..2741fc3 100755 --- a/include/models/service_model.h +++ b/include/models/service_model.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -9,29 +10,35 @@ class ServiceModel { private: std::string _id; + std::string _community_id; std::string _creator_id; + std::optional _buyer_id; std::string _title; std::string _description; int _price; std::string _status; std::string _type; - std::string _buyer_user_id; + std::optional _image_url; + std::string _created_at; + std::string _updated_at; public: - std::string get_id(); - std::string get_creator_id(); - std::string get_title(); - std::string get_description(); - int get_price(); - std::string get_status(); - std::string get_type(); - std::string get_buyer_user_id(); + ServiceModel(std::string id, std::string community_id, std::string creator_id, std::optional buyer_id, std::string title, std::string description, int price, std::string status, std::string type, std::optional image_url, std::string created_at, std::string updated_at); - ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string type, std::string buyer_user_id = ""); + std::string get_id() const; + std::string get_community_id() const; + std::string get_creator_id() const; + std::optional get_buyer_id() const; + std::string get_title() const; + std::string get_description() const; + int get_price() const; + std::string get_status() const; + std::string get_type() const; + std::optional get_image_url() const; + std::string get_created_at() const; + std::string get_updated_at() const; - static std::unique_ptr create_service(pqxx::connection& db, std::string creator_id, std::string title, std::string description, int price, std::string type, bool isThrow = false); + static std::unique_ptr create_service(pqxx::connection& db, const std::string& community_id, const std::string& creator_id, const std::string& title, const std::string& description, const int price, const std::string& type, const std::optional& image_url, bool isThrow = false); - static std::vector get_services(pqxx::connection& db, std::string status = ""); - - // * static ServiceModel create_notification(pqxx::connection &db, std::string creator_id, std::string title, std::string description, int price); + static std::vector> get_open_services_by_community_id(pqxx::connection& db, const std::string& community_id); }; diff --git a/include/models/user_model.h b/include/models/user_model.h index 124fd0a..87fc6da 100755 --- a/include/models/user_model.h +++ b/include/models/user_model.h @@ -22,25 +22,25 @@ class UserModel { public: UserModel(std::string id, std::string community_id, std::string username, std::string email, std::string type, int balance, std::string created_at, std::string updated_at); - std::string getId(); - std::string getCommunityId(); - std::string getUsername(); - std::string getEmail(); - std::string getType(); - int getBalance(); - std::string getCreatedAt(); - std::string getUpdatedAt(); - - static std::unique_ptr create_user(pqxx::connection& db, const std::string& community_id, const std::string& username, const std::string& email, const std::string& password, const std::string& type, const int balance); + std::string get_id() const; + std::string get_community_id() const; + std::string get_username() const; + std::string get_email() const; + std::string get_type() const; + int get_balance() const; + std::string get_created_at() const; + std::string get_updated_at() const; + + static std::unique_ptr create_user(pqxx::connection& db, const std::string& community_id, const std::string& username, const std::string& email, const std::string& password, const std::string& type, const int balance, bool throw_when_null = false); static std::vector> get_users(pqxx::connection& db); static bool exists_id(pqxx::connection& db, const std::string& id); static bool exists_username(pqxx::connection& db, const std::string& username); static bool exists_email(pqxx::connection& db, const std::string& email); - static std::unique_ptr get_user_by_id(pqxx::connection& db, const std::string& id); - static std::unique_ptr get_user_by_username(pqxx::connection& db, const std::string& username); - static std::unique_ptr get_user_by_email(pqxx::connection& db, const std::string& email); + static std::unique_ptr get_user_by_id(pqxx::connection& db, const std::string& id, bool throw_when_null = false); + static std::unique_ptr get_user_by_username(pqxx::connection& db, const std::string& username, bool throw_when_null = false); + static std::unique_ptr get_user_by_email(pqxx::connection& db, const std::string& email, bool throw_when_null = false); static std::string get_password_by_email(pqxx::connection& db, const std::string& email); static bool delete_user_by_id(pqxx::connection& db, const std::string& id); diff --git a/include/utils/errors.h b/include/utils/errors.h index 182d1d2..5f69b59 100755 --- a/include/utils/errors.h +++ b/include/utils/errors.h @@ -12,3 +12,30 @@ class data_not_found_exception : public std::exception { data_not_found_exception(const std::string& msg); virtual const char* what() const noexcept override; }; + +class creation_exception : public std::exception { + private: + std::string _message; + + public: + creation_exception(const std::string& msg); + virtual const char* what() const noexcept override; +}; + +class deletion_exception : public std::exception { + private: + std::string _message; + + public: + deletion_exception(const std::string& msg); + virtual const char* what() const noexcept override; +}; + +class update_exception : public std::exception { + private: + std::string _message; + + public: + update_exception(const std::string& msg); + virtual const char* what() const noexcept override; +}; diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index c9334b1..2b145b6 100755 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -31,14 +31,19 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re } if (type == Roles::ADMIN) { - CommunityModel community = CommunityModel::create_community(db, body["community_name"].s()); - community_id = community.get_id(); + std::unique_ptr community = CommunityModel::create_community(db, body["community_name"].s()); + if (!community) { + handle_error(res, "internal server error", 500); + return; + } + community_id = community.get()->get_id(); } else if (type == Roles::NEIGHBOR) { - community_id = CommunityModel::get_community_id(db, body["community_code"].s()); - if (community_id == "") { + std::unique_ptr community = CommunityModel::get_community_by_code(db, body["community_code"].s()); + if (!community) { handle_error(res, "community does not exist", 404); return; } + community_id = community.get()->get_id(); } std::unique_ptr user = UserModel::create_user(db, community_id, username, email, hash, type, 0); @@ -48,7 +53,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re return; } - std::string jwtToken = create_token(user.get()->getId(), type); + std::string jwtToken = create_token(user.get()->get_id(), type); int expirationTimeSeconds = 3600; time_t now = time(0); time_t expirationTime = now + expirationTimeSeconds; @@ -67,7 +72,7 @@ void AuthController::register_user(pqxx::connection &db, const crow::request &re res.set_header("Set-Cookie", cookieStream.str()); crow::json::wvalue data({ - {"id", user.get()->getId()}, + {"id", user.get()->get_id()}, }); res.code = 201; @@ -109,7 +114,7 @@ void AuthController::login_user(pqxx::connection &db, const crow::request &req, std::string password = body["password"].s(); if (BCrypt::validatePassword(password, encrypt_password)) { - std::string jwtToken = create_token(user.get()->getId(), user.get()->getType()); + std::string jwtToken = create_token(user.get()->get_id(), user.get()->get_type()); int expirationTimeSeconds = 3600; time_t now = time(0); @@ -130,8 +135,8 @@ void AuthController::login_user(pqxx::connection &db, const crow::request &req, crow::json::wvalue data; data["user"] = { - {"id", user.get()->getId()}, - {"type", user.get()->getType()}}; + {"id", user.get()->get_id()}, + {"type", user.get()->get_type()}}; res.code = 200; res.write(data.dump()); diff --git a/src/controllers/service_controller.cpp b/src/controllers/service_controller.cpp index 3207641..08ddc59 100755 --- a/src/controllers/service_controller.cpp +++ b/src/controllers/service_controller.cpp @@ -12,65 +12,69 @@ void ServiceController::create_service(pqxx::connection &db, const crow::request std::string description = body["description"].s(); int price = body["price"].i(); std::string type = body["type"].s(); + std::string creator_id = body["id"].s(); - std::string service_creator_id = body["id"].s(); + std::unique_ptr user = UserModel::get_user_by_id(db, creator_id); - std::unique_ptr service = ServiceModel::create_service(db, service_creator_id, title, description, price, type); + std::unique_ptr service = ServiceModel::create_service(db, user.get()->get_community_id(), creator_id, title, description, price, type, nullptr); if (!service) { - handle_error(res, "service error controller", 400); + handle_error(res, "internal server error", 500); return; } - crow::json::wvalue data({ - {"type", service.get()->get_type()}, - {"status", service.get()->get_status()}, - {"price", service.get()->get_price()}, - {"creator_id", service.get()->get_creator_id()}, - {"description", service.get()->get_description()}, - {"title", service.get()->get_title()}, - {"id", service.get()->get_id()}, - }); + crow::json::wvalue data( + {{"id", service.get()->get_id()}, + {"community_id", service.get()->get_community_id()}, + {"creator_id", service.get()->get_creator_id()}, + {"title", service.get()->get_title()}, + {"description", service.get()->get_description()}, + {"price", service.get()->get_price()}, + {"status", service.get()->get_status()}, + {"type", service.get()->get_type()}, + {"created_at", service.get()->get_created_at()}, + {"updated_at", service.get()->get_updated_at()}}); + + if (service.get()->get_buyer_id().has_value()) + data["buyer_id"] = service.get()->get_buyer_id().value(); + if (service.get()->get_image_url().has_value()) + data["image_url"] = service.get()->get_image_url().value(); - res.write(data.dump()); res.code = 201; + res.write(data.dump()); res.end(); - } catch (const pqxx::data_exception &e) { - handle_error(res, "invalid properties to create service", 400); - } catch (const std::exception &e) { - handle_error(res, e.what(), 500); + std::cerr << "Error creating service: " << e.what() << std::endl; + handle_error(res, "internal server error", 500); } } void ServiceController::get_services(pqxx::connection &db, const crow::request &req, crow::response &res) { try { - // ** comment to add - auto status = req.url_params.get("status"); + crow::json::rvalue body = crow::json::load(req.body); - std::vector allServices; - if (!status) { - allServices = ServiceModel::get_services(db); + std::unique_ptr user = UserModel::get_user_by_id(db, body["id"].s()); + std::vector> all_services = ServiceModel::get_open_services_by_community_id(db, user.get()->get_community_id()); - } else if (status && (std::string(status) == "open" || std::string(status) == "closed")) { - allServices = ServiceModel::get_services(db, status); - } else { - handle_error(res, "status not valid value", 400); - return; - } - - std::cout << "flag 3" << std::endl; crow::json::wvalue::list services; - for (unsigned int i = 0; i < allServices.size(); i++) { + for (unsigned int i = 0; i < all_services.size(); ++i) { crow::json::wvalue service; - service["id"] = allServices[i].get_id(); - service["creator_id"] = allServices[i].get_creator_id(); - service["title"] = allServices[i].get_title(); - service["description"] = allServices[i].get_description(); - service["price"] = allServices[i].get_price(); - service["status"] = allServices[i].get_status(); - service["type"] = allServices[i].get_type(); - service["buyer_user_id"] = allServices[i].get_buyer_user_id(); + + service["id"] = all_services[i].get()->get_id(); + service["community_id"] = all_services[i].get()->get_community_id(); + service["creator_id"] = all_services[i].get()->get_creator_id(); + if (all_services[i].get()->get_buyer_id().has_value()) + service["buyer_id"] = all_services[i].get()->get_buyer_id().value(); + service["title"] = all_services[i].get()->get_title(); + service["description"] = all_services[i].get()->get_description(); + service["price"] = all_services[i].get()->get_price(); + service["status"] = all_services[i].get()->get_status(); + service["type"] = all_services[i].get()->get_type(); + if (all_services[i].get()->get_image_url().has_value()) + service["image_url"] = all_services[i].get()->get_image_url().value(); + service["created_at"] = all_services[i].get()->get_created_at(); + service["updated_at"] = all_services[i].get()->get_updated_at(); + services.push_back(service); } @@ -80,6 +84,7 @@ void ServiceController::get_services(pqxx::connection &db, const crow::request & res.code = 200; res.end(); } catch (const std::exception &e) { - handle_error(res, e.what(), 500); + std::cerr << "Error getting services: " << e.what() << std::endl; + handle_error(res, "internal server error", 500); } } diff --git a/src/controllers/user_controller.cpp b/src/controllers/user_controller.cpp index fdac7df..75ddd43 100755 --- a/src/controllers/user_controller.cpp +++ b/src/controllers/user_controller.cpp @@ -6,14 +6,14 @@ void UserController::get_users(pqxx::connection &db, const crow::request &req, c crow::json::wvalue::list users; for (unsigned int i = 0; i < allUsers.size(); ++i) { crow::json::wvalue user; - user["id"] = allUsers[i].get()->getId(); - user["community_id"] = allUsers[i].get()->getCommunityId(); - user["username"] = allUsers[i].get()->getUsername(); - user["email"] = allUsers[i].get()->getEmail(); - user["type"] = allUsers[i].get()->getType(); - user["balance"] = allUsers[i].get()->getBalance(); - user["created_at"] = allUsers[i].get()->getCreatedAt(); - user["updated_at"] = allUsers[i].get()->getUpdatedAt(); + user["id"] = allUsers[i].get()->get_id(); + user["community_id"] = allUsers[i].get()->get_community_id(); + user["username"] = allUsers[i].get()->get_username(); + user["email"] = allUsers[i].get()->get_email(); + user["type"] = allUsers[i].get()->get_type(); + user["balance"] = allUsers[i].get()->get_balance(); + user["created_at"] = allUsers[i].get()->get_created_at(); + user["updated_at"] = allUsers[i].get()->get_updated_at(); users.push_back(user); } crow::json::wvalue data{{"users", users}}; @@ -41,14 +41,14 @@ void UserController::get_user_by_id(pqxx::connection &db, const crow::request &r } crow::json::wvalue user_data; - user_data["id"] = user.get()->getId(); - user_data["community_id"] = user.get()->getCommunityId(); - user_data["username"] = user.get()->getUsername(); - user_data["email"] = user.get()->getEmail(); - user_data["type"] = user.get()->getType(); - user_data["balance"] = user.get()->getBalance(); - user_data["created_at"] = user.get()->getCreatedAt(); - user_data["updated_at"] = user.get()->getUpdatedAt(); + user_data["id"] = user.get()->get_id(); + user_data["community_id"] = user.get()->get_community_id(); + user_data["username"] = user.get()->get_username(); + user_data["email"] = user.get()->get_email(); + user_data["type"] = user.get()->get_type(); + user_data["balance"] = user.get()->get_balance(); + user_data["created_at"] = user.get()->get_created_at(); + user_data["updated_at"] = user.get()->get_updated_at(); crow::json::wvalue data{{"user", user_data}}; res.code = 200; diff --git a/src/models/community_model.cpp b/src/models/community_model.cpp index 54807d9..9e7a16c 100755 --- a/src/models/community_model.cpp +++ b/src/models/community_model.cpp @@ -1,7 +1,12 @@ #include -CommunityModel::CommunityModel(std::string id, std::string community_name, std::string community_code) : _id(id), _community_name(community_name), _community_code(community_code) { -} +CommunityModel::CommunityModel(std::string id, std::string name, std::string code, std::string created_at, std::string updated_at) : _id(id), _name(name), _code(code), _created_at(created_at), _updated_at(updated_at) {} + +std::string CommunityModel::get_id() const { return _id; } +std::string CommunityModel::get_name() const { return _name; } +std::string CommunityModel::get_code() const { return _code; } +std::string CommunityModel::get_created_at() const { return _created_at; } +std::string CommunityModel::get_updated_at() const { return _updated_at; } std::string CommunityModel::generate_community_code() { const std::string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -17,55 +22,53 @@ std::string CommunityModel::generate_community_code() { return code; } -CommunityModel CommunityModel::create_community(pqxx::connection& db, std::string community_name) { +std::unique_ptr CommunityModel::create_community(pqxx::connection& db, const std::string& name, bool throw_when_null) { pqxx::work txn(db); - std::string community_code = generate_community_code(); + std::string code = generate_community_code(); - pqxx::result result = txn.exec_params("INSERT INTO communities (community_name, community_code) VALUES ($1, $2) RETURNING id, community_name, community_code", community_name, community_code); + pqxx::result result = txn.exec_params("INSERT INTO communities (name, code) VALUES ($1, $2) RETURNING id, name, code, created_at, updated_at", name, code); txn.commit(); - return CommunityModel( + if (result.empty()) { + if (throw_when_null) + throw creation_exception("community could not be created"); + else + return nullptr; + } + + return std::make_unique( result[0]["id"].as(), - result[0]["community_code"].as(), - result[0]["community_name"].as()); + result[0]["name"].as(), + result[0]["code"].as(), + result[0]["created_at"].as(), + result[0]["updated_at"].as()); } -std::string CommunityModel::get_community_id(pqxx::connection& db, std::string community_code) { - try { - pqxx::work txn(db); - - // Construir la consulta SQL parametrizada para obtener el ID de la comunidad dado su código - std::string query = "SELECT id FROM communities WHERE community_code = $1"; - - // Ejecutar la consulta parametrizada - pqxx::result result = txn.exec_params(query, community_code); - - // Comprobar si se encontró la comunidad - if (!result.empty() && !result[0]["id"].is_null()) { - std::string community_id = result[0]["id"].as(); - txn.commit(); - return community_id; - } else { - // Si no se encuentra la comunidad, devolver un valor nulo o lanzar una excepción según sea necesario - txn.commit(); - return ""; // O cualquier otro valor que desees para indicar que la comunidad no se encontró - } - } catch (const std::exception& e) { - // Manejar cualquier excepción que ocurra durante la ejecución de la consulta - return ""; // O lanzar una excepción adecuada según sea necesario +std::unique_ptr get_community(pqxx::connection& db, const std::string& column, const std::string& value, bool throw_when_null) { + pqxx::work txn(db); + pqxx::result result = txn.exec_params(std::format("SELECT id, name, code, created_at, updated_at FROM communities WHERE {} = $1", column), value); + txn.commit(); + + if (result.empty()) { + if (throw_when_null) + throw data_not_found_exception("community not found"); + else + return nullptr; } -} -std::string CommunityModel::get_id() const { - return _id; + return std::make_unique( + result[0]["id"].as(), + result[0]["name"].as(), + result[0]["code"].as(), + result[0]["created_at"].as(), + result[0]["updated_at"].as()); } -std::string CommunityModel::get_community_name() const { - return _community_name; +std::unique_ptr CommunityModel::get_community_by_id(pqxx::connection& db, const std::string& id, bool throw_when_null) { + return get_community(db, "id", id, throw_when_null); } - -std::string CommunityModel::get_community_code() const { - return _community_code; +std::unique_ptr CommunityModel::get_community_by_code(pqxx::connection& db, const std::string& code, bool throw_when_null) { + return get_community(db, "code", code, throw_when_null); } diff --git a/src/models/service_model.cpp b/src/models/service_model.cpp index 5c17744..44f20ff 100755 --- a/src/models/service_model.cpp +++ b/src/models/service_model.cpp @@ -1,64 +1,95 @@ #include -ServiceModel::ServiceModel(std::string id, std::string creator_id, std::string title, std::string description, int price, std::string status, std::string type, std::string buyer_user_id) : _id(id), _creator_id(creator_id), _title(title), _description(description), _price(price), _status(status), _type(type), _buyer_user_id(buyer_user_id) {} +ServiceModel::ServiceModel(std::string id, std::string community_id, std::string creator_id, std::optional buyer_id, std::string title, std::string description, int price, std::string status, std::string type, std::optional image_url, std::string created_at, std::string updated_at) : _id(id), _community_id(community_id), _creator_id(creator_id), _buyer_id(buyer_id), _title(title), _description(description), _price(price), _status(status), _type(type), _image_url(image_url), _created_at(created_at), _updated_at(updated_at) {} -std::string ServiceModel::get_id() { return _id; } -std::string ServiceModel::get_creator_id() { return _creator_id; } -std::string ServiceModel::get_title() { return _title; } -std::string ServiceModel::get_description() { return _description; } -int ServiceModel::get_price() { return _price; } -std::string ServiceModel::get_status() { return _status; } -std::string ServiceModel::get_type() { return _type; } -std::string ServiceModel::get_buyer_user_id() { return _buyer_user_id; } +std::string ServiceModel::get_id() const { return _id; } +std::string ServiceModel::get_community_id() const { return _community_id; } +std::string ServiceModel::get_creator_id() const { return _creator_id; } +std::optional ServiceModel::get_buyer_id() const { return _buyer_id; } +std::string ServiceModel::get_title() const { return _title; } +std::string ServiceModel::get_description() const { return _description; } +int ServiceModel::get_price() const { return _price; } +std::string ServiceModel::get_status() const { return _status; } +std::string ServiceModel::get_type() const { return _type; } +std::optional ServiceModel::get_image_url() const { return _image_url; } +std::string ServiceModel::get_created_at() const { return _created_at; } +std::string ServiceModel::get_updated_at() const { return _updated_at; } -// * ------------------------------------------------------------------- - -std::unique_ptr ServiceModel::create_service(pqxx::connection& db, std::string creator_id, std::string title, std::string description, int price, std::string type, bool isThrow) { +std::unique_ptr ServiceModel::create_service(pqxx::connection& db, const std::string& community_id, const std::string& creator_id, const std::string& title, const std::string& description, const int price, const std::string& type, const std::optional& image_url, bool isThrow) { pqxx::work txn(db); - pqxx::result result = txn.exec_params( - "INSERT INTO services (creator_id, title, description, price, type) VALUES ($1, $2, $3, $4, $5) RETURNING id", - creator_id, - title, - description, - price, type); + pqxx::result result = txn.exec_params("INSERT INTO services (community_id, creator_id, title, description, price, type) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, community_id, creator_id, buyer_id, title, description, price, status, type, image_url, created_at, updated_at", community_id, creator_id, title, description, price, type); txn.commit(); if (result.empty()) { if (isThrow) - throw data_not_found_exception("error create service model"); - + throw creation_exception("service could not be created"); else return nullptr; } - std::string service_id = result[0]["id"].as(); + std::optional buyer_id_field; + std::optional image_url_field; + if (!result[0]["buyer_id"].is_null()) + buyer_id_field = result[0]["buyer_id"].as(); + else + buyer_id_field = std::nullopt; + if (!result[0]["image_url"].is_null()) + image_url_field = result[0]["image_url"].as(); + else + image_url_field = std::nullopt; - return std::make_unique(service_id, creator_id, title, description, price, "open", type); + return std::make_unique( + result[0]["id"].as(), + result[0]["community_id"].as(), + result[0]["creator_id"].as(), + buyer_id_field, + result[0]["title"].as(), + result[0]["description"].as(), + result[0]["price"].as(), + result[0]["status"].as(), + result[0]["type"].as(), + image_url_field, + result[0]["created_at"].as(), + result[0]["updated_at"].as()); } -std::vector ServiceModel::get_services(pqxx::connection& db, std::string status) { - std::vector all_services; +std::vector> ServiceModel::get_open_services_by_community_id(pqxx::connection& db, const std::string& community_id) { + std::vector> all_services; + pqxx::work txn(db); - pqxx::result result; - std::string query = "SELECT id, creator_id, title, description, price, status, type, buyer_user_id FROM services"; - if (status != "") - query += " WHERE status = '" + status + "'"; + pqxx::result result = txn.exec("SELECT id, community_id, creator_id, buyer_id, title, description, price, status, type, image_url, created_at, updated_at FROM services"); - result = txn.exec(query); + txn.commit(); for (const auto& row : result) { - std::string buyer_user_id; - if (!row["buyer_user_id"].is_null()) { - buyer_user_id = row["buyer_user_id"].as(); - } - ServiceModel service(row["id"].as(), row["creator_id"].as(), row["title"].as(), row["description"].as(), row["price"].as(), row["status"].as(), row["type"].as(), buyer_user_id); + std::optional buyer_id_field; + std::optional image_url_field; + if (!row["buyer_id"].is_null()) + buyer_id_field = row["buyer_id"].as(); + else + buyer_id_field = std::nullopt; + if (!row["image_url"].is_null()) + image_url_field = row["image_url"].as(); + else + image_url_field = std::nullopt; - all_services.push_back(service); + all_services.push_back(std::make_unique( + row["id"].as(), + row["community_id"].as(), + row["creator_id"].as(), + buyer_id_field, + row["title"].as(), + row["description"].as(), + row["price"].as(), + row["status"].as(), + row["type"].as(), + image_url_field, + row["created_at"].as(), + row["updated_at"].as())); } - std::cout << "flag 1 model" << std::endl; - txn.commit(); + return all_services; } diff --git a/src/models/user_model.cpp b/src/models/user_model.cpp index 9edb5eb..3338405 100755 --- a/src/models/user_model.cpp +++ b/src/models/user_model.cpp @@ -2,22 +2,29 @@ UserModel::UserModel(std::string id, std::string community_id, std::string username, std::string email, std::string type, int balance, std::string created_at, std::string updated_at) : _id(id), _community_id(community_id), _username(username), _email(email), _type(type), _balance(balance), _created_at(created_at), _updated_at(updated_at) {} -std::string UserModel::getId() { return _id; } -std::string UserModel::getCommunityId() { return _community_id; } -std::string UserModel::getUsername() { return _username; } -std::string UserModel::getEmail() { return _email; } -std::string UserModel::getType() { return _type; } -int UserModel::getBalance() { return _balance; } -std::string UserModel::getCreatedAt() { return _created_at; } -std::string UserModel::getUpdatedAt() { return _updated_at; } - -std::unique_ptr UserModel::create_user(pqxx::connection& db, const std::string& community_id, const std::string& username, const std::string& email, const std::string& password, const std::string& type, const int balance) { +std::string UserModel::get_id() const { return _id; } +std::string UserModel::get_community_id() const { return _community_id; } +std::string UserModel::get_username() const { return _username; } +std::string UserModel::get_email() const { return _email; } +std::string UserModel::get_type() const { return _type; } +int UserModel::get_balance() const { return _balance; } +std::string UserModel::get_created_at() const { return _created_at; } +std::string UserModel::get_updated_at() const { return _updated_at; } + +std::unique_ptr UserModel::create_user(pqxx::connection& db, const std::string& community_id, const std::string& username, const std::string& email, const std::string& password, const std::string& type, const int balance, bool throw_when_null) { pqxx::work txn(db); pqxx::result result = txn.exec_params("INSERT INTO users (community_id, username, email, password, type, balance) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, community_id, username, email, type, balance, created_at, updated_at", community_id, username, email, password, type, balance); txn.commit(); + if (result.empty()) { + if (throw_when_null) + throw creation_exception("user could not be created"); + else + return nullptr; + } + return std::make_unique( result[0]["id"].as(), result[0]["community_id"].as(), @@ -75,50 +82,45 @@ bool UserModel::exists_email(pqxx::connection& db, const std::string& email) { return exists_user(db, "email", email); } -std::unique_ptr get_user(pqxx::connection& db, const std::string& column, const std::string& value) { - try { - pqxx::work txn(db); - - pqxx::result result = txn.exec_params(std::format("SELECT id, community_id, username, email, type, balance, created_at, updated_at FROM users WHERE {} = $1", column), value); - - txn.commit(); +std::unique_ptr get_user(pqxx::connection& db, const std::string& column, const std::string& value, bool throw_when_null) { + pqxx::work txn(db); + pqxx::result result = txn.exec_params(std::format("SELECT id, community_id, username, email, type, balance, created_at, updated_at FROM users WHERE {} = $1", column), value); + txn.commit(); - if (result.empty()) return nullptr; - - return std::make_unique( - result[0]["id"].as(), - result[0]["community_id"].as(), - result[0]["username"].as(), - result[0]["email"].as(), - result[0]["type"].as(), - result[0]["balance"].as(), - result[0]["created_at"].as(), - result[0]["updated_at"].as()); - } catch (const std::exception& e) { - return nullptr; + if (result.empty()) { + if (throw_when_null) + throw data_not_found_exception("user not found"); + else + return nullptr; } + + return std::make_unique( + result[0]["id"].as(), + result[0]["community_id"].as(), + result[0]["username"].as(), + result[0]["email"].as(), + result[0]["type"].as(), + result[0]["balance"].as(), + result[0]["created_at"].as(), + result[0]["updated_at"].as()); } -std::unique_ptr UserModel::get_user_by_id(pqxx::connection& db, const std::string& id) { - return get_user(db, "id", id); +std::unique_ptr UserModel::get_user_by_id(pqxx::connection& db, const std::string& id, bool throw_when_null) { + return get_user(db, "id", id, throw_when_null); } -std::unique_ptr UserModel::get_user_by_username(pqxx::connection& db, const std::string& username) { - return get_user(db, "username", username); +std::unique_ptr UserModel::get_user_by_username(pqxx::connection& db, const std::string& username, bool throw_when_null) { + return get_user(db, "username", username, throw_when_null); } -std::unique_ptr UserModel::get_user_by_email(pqxx::connection& db, const std::string& email) { - return get_user(db, "email", email); +std::unique_ptr UserModel::get_user_by_email(pqxx::connection& db, const std::string& email, bool throw_when_null) { + return get_user(db, "email", email, throw_when_null); } std::string UserModel::get_password_by_email(pqxx::connection& db, const std::string& email) { try { pqxx::work txn(db); - pqxx::result result = txn.exec_params("SELECT password FROM users WHERE email = $1", email); - txn.commit(); - if (result.empty()) return ""; - return result[0]["password"].as(); } catch (const std::exception& e) { return ""; diff --git a/src/utils/errors.cpp b/src/utils/errors.cpp index 96943cd..3194646 100755 --- a/src/utils/errors.cpp +++ b/src/utils/errors.cpp @@ -5,3 +5,21 @@ data_not_found_exception::data_not_found_exception(const std::string& msg) : _me const char* data_not_found_exception::what() const noexcept { return _message.c_str(); } + +creation_exception::creation_exception(const std::string& msg) : _message(msg) {} + +const char* creation_exception::what() const noexcept { + return _message.c_str(); +} + +deletion_exception::deletion_exception(const std::string& msg) : _message(msg) {} + +const char* deletion_exception::what() const noexcept { + return _message.c_str(); +} + +update_exception::update_exception(const std::string& msg) : _message(msg) {} + +const char* update_exception::what() const noexcept { + return _message.c_str(); +} From 3b9fdb758bb57728115f7919b27977a0ddf684e1 Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Fri, 29 Mar 2024 16:22:25 +0100 Subject: [PATCH 47/48] task #83: method exists community code --- include/models/community_model.h | 1 + src/models/community_model.cpp | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/models/community_model.h b/include/models/community_model.h index c341734..bdeb1e9 100755 --- a/include/models/community_model.h +++ b/include/models/community_model.h @@ -29,6 +29,7 @@ class CommunityModel { static std::unique_ptr create_community(pqxx::connection& db, const std::string& name, bool throw_when_null = false); static bool exists_name(pqxx::connection& db, const std::string& name); + static bool exists_code(pqxx::connection& db, const std::string& code); static std::unique_ptr get_community_by_id(pqxx::connection& db, const std::string& id, bool throw_when_null = false); static std::unique_ptr get_community_by_code(pqxx::connection& db, const std::string& code, bool throw_when_null = false); diff --git a/src/models/community_model.cpp b/src/models/community_model.cpp index 3bb3149..05f59d7 100755 --- a/src/models/community_model.cpp +++ b/src/models/community_model.cpp @@ -52,11 +52,23 @@ bool CommunityModel::exists_name(pqxx::connection& db, const std::string& name) pqxx::result result = txn.exec_params("SELECT community_name FROM communities WHERE community_name = $1", name); - bool communityExists = !result.empty() && !result[0][0].is_null(); + txn.commit(); + + return !result.empty() && !result[0][0].is_null(); + } catch (const std::exception& e) { + return false; + } +} + +bool CommunityModel::exists_code(pqxx::connection& db, const std::string& code) { + try { + pqxx::work txn(db); + + pqxx::result result = txn.exec_params("SELECT community_code FROM communities WHERE community_code = $1", code); txn.commit(); - return communityExists; + return !result.empty() && !result[0][0].is_null(); } catch (const std::exception& e) { return false; } From d438a5267a7b5e03a0034f83902a9f0c58983f99 Mon Sep 17 00:00:00 2001 From: JayexDesigns Date: Fri, 29 Mar 2024 17:11:04 +0100 Subject: [PATCH 48/48] task #105: corrected bug in query of get open services by community id --- src/controllers/auth_controller.cpp | 2 +- src/models/service_model.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/auth_controller.cpp b/src/controllers/auth_controller.cpp index 2b145b6..d4c3d39 100755 --- a/src/controllers/auth_controller.cpp +++ b/src/controllers/auth_controller.cpp @@ -143,7 +143,7 @@ void AuthController::login_user(pqxx::connection &db, const crow::request &req, res.end(); } else { - handle_error(res, "password invalid", 401); + handle_error(res, "password invalid", 400); return; } diff --git a/src/models/service_model.cpp b/src/models/service_model.cpp index 44f20ff..d52981c 100755 --- a/src/models/service_model.cpp +++ b/src/models/service_model.cpp @@ -60,7 +60,7 @@ std::vector> ServiceModel::get_open_services_by_co pqxx::work txn(db); - pqxx::result result = txn.exec("SELECT id, community_id, creator_id, buyer_id, title, description, price, status, type, image_url, created_at, updated_at FROM services"); + pqxx::result result = txn.exec_params("SELECT id, community_id, creator_id, buyer_id, title, description, price, status, type, image_url, created_at, updated_at FROM services WHERE community_id = $1", community_id); txn.commit();