diff --git a/assets/HypertextPages/list-rooms.nytl.html b/assets/HypertextPages/list-rooms.nytl.html index 4a638ce..ddc4923 100644 --- a/assets/HypertextPages/list-rooms.nytl.html +++ b/assets/HypertextPages/list-rooms.nytl.html @@ -1,6 +1,6 @@ {% ELDEF main JSON pres JSON userinfo %} - + @@ -8,7 +8,7 @@ -{% PUT pass-userinfo userinfo %} +{% PUT pass-pres-userinfo pres userinfo %}

Выберите Чат-Комнату

- + + + + + + + + + + + + + + + + - -
-
-
- × -

Добавить участников

-
-
- -
- -
-
-
-
-
- × -

Вы уверены, что хотите удалить чат?

-
-
- - -
-
-
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% ENDELDEF %} \ No newline at end of file diff --git a/assets/HypertextPages/login.nytl.html b/assets/HypertextPages/login.nytl.html index 700e8b8..674f42b 100644 --- a/assets/HypertextPages/login.nytl.html +++ b/assets/HypertextPages/login.nytl.html @@ -1,27 +1,27 @@ {% ELDEF main JSON pres JSON userinfo %} - - - - - - {% WRITE pres.phr.decl.page-login %} - + + + + + + {% WRITE pres.phr.decl.page-login %} + - + - - {% PUT pass-userinfo userinfo %} -
-

{% WRITE pres.phr.decl.enter %}

-
- -
- -
- -
-
+ +{% PUT pass-pres-userinfo pres userinfo %} +
+

{% WRITE pres.phr.decl.enter %}

+
+ +
+ +
+ +
+
- - + + {% ENDELDEF %} diff --git a/assets/HypertextPages/pass-pres-userinfo.nytl.html b/assets/HypertextPages/pass-pres-userinfo.nytl.html new file mode 100644 index 0000000..8331b75 --- /dev/null +++ b/assets/HypertextPages/pass-pres-userinfo.nytl.html @@ -0,0 +1,6 @@ +{% ELDEF main JSON pres JSON userinfo %} + +{% ENDELDEF %} diff --git a/assets/HypertextPages/pass-userinfo.nytl.html b/assets/HypertextPages/pass-userinfo.nytl.html deleted file mode 100644 index 164366f..0000000 --- a/assets/HypertextPages/pass-userinfo.nytl.html +++ /dev/null @@ -1,5 +0,0 @@ -{% ELDEF main JSON userinfo %} - - - -{% ENDELDEF %} diff --git a/building/main.cpp b/building/main.cpp index 9b03229..7f7cfc5 100644 --- a/building/main.cpp +++ b/building/main.cpp @@ -147,6 +147,10 @@ struct CAWebChat { "find_db.cpp", "sqlite3_wrapper.cpp", "login_cookie.cpp", + "backend_logic/server_data_interact.cpp", + "backend_logic/when_login.cpp", + "backend_logic/when_internalapi_pollevents.cpp", + "backend_logic/when_internalapi_getchatlist.cpp", }; for (std::string& u: T.units) u = "web_chat/iu9_ca_web_chat_lib/" + u; diff --git a/example/config.json b/example/config.json index 2e99947..c88f13f 100644 --- a/example/config.json +++ b/example/config.json @@ -39,6 +39,6 @@ "server": { "workers": 8, "http-listen": ["127.0.0.1:1025"], - "admin-command-listen": ["unix:/run/iu9/iu9cawebchat-ac.sock"] + "admin-command-listen": ["[::1]:1026"] } } diff --git a/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp b/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp index e194e3f..a1be045 100644 --- a/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp +++ b/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp @@ -14,7 +14,7 @@ namespace een9 { return result; } - std::string form_http_server_reponse_header_only(const char* code, + std::string form_http_server_response_header_only(const char* code, const std::vector>& headers) { return form_http_server_response_header(code, headers) + "\r\n"; } @@ -42,13 +42,13 @@ namespace een9 { } std::string form_http_server_response_307(const std::string& Location) { - return form_http_server_reponse_header_only("307", {{"Location", Location}}); + return form_http_server_response_header_only("307", {{"Location", Location}}); } std::string form_http_server_response_307_spec_head(const std::string &Location, const std::vector>& headers) { std::vector> cp = headers; cp.emplace_back("Location", Location); - return form_http_server_reponse_header_only("307", cp); + return form_http_server_response_header_only("307", cp); } } diff --git a/src/http_server/engine_engine_number_9/running_mainloop.cpp b/src/http_server/engine_engine_number_9/running_mainloop.cpp index 1e3911f..0cb1355 100644 --- a/src/http_server/engine_engine_number_9/running_mainloop.cpp +++ b/src/http_server/engine_engine_number_9/running_mainloop.cpp @@ -41,7 +41,6 @@ namespace een9 { } void push_back(SlaveTask task) { - /* Throws a goddamn execption. Because why not. Ofcourse everything has to throw an exception */ /* CLion says. Allocated memory is leaking. YOUR MOTHER IS LEAKING YOU FOOL!! MY CODE IS FINE!! */ QElementHttpConnections* el = new QElementHttpConnections(std::move(task)); /* Exception does not leave queue in incorrect state */ @@ -66,6 +65,15 @@ namespace een9 { sz--; } } + + ~WorkersTaskQueue() { + QElementHttpConnections* cur = first; + while (cur) { + QElementHttpConnections* nxt = cur->nxt; + delete cur; + cur = nxt; + } + } }; struct WorkersEnvCommon { diff --git a/src/http_server/new_york_transit_line/rendering.cpp b/src/http_server/new_york_transit_line/rendering.cpp index 3be9d53..bbdddbc 100644 --- a/src/http_server/new_york_transit_line/rendering.cpp +++ b/src/http_server/new_york_transit_line/rendering.cpp @@ -34,7 +34,7 @@ namespace nytl { } else if (P.isDictionary() && what.isString()) { const std::map& dict_p = P.asDictionary(); const std::string& key_w = what.asString(); - ASSERT(dict_p.count(key_w) == 1, "No such key exception"); + ASSERT(dict_p.count(key_w) == 1, "No such key exception (" + key_w + ")"); result = LocalVarValue{true, "", &dict_p.at(key_w)}; } else THROW("Incorrect type of \"json[json]\" expression. Unallowed signature of [] operator"); @@ -112,15 +112,9 @@ namespace nytl { for (size_t i = 0; i < m; i++) { result += text[i]; if (text[i] == '\n') { - // newlined_somewhere = true; - result.resize(result.size() + wsp_before_newlines, ' '); cur_line_width = wsp_before_newlines; } else { - // if (cur_line_width == 0 && newlined_somewhere) { - // result.resize(result.size() + wsp_before_newlines, ' '); - // cur_line_width = wsp_before_newlines; - // } cur_line_width++; } } @@ -246,7 +240,11 @@ namespace nytl { assert(passed_args.size() == 1); const json::JSON* X = passed_args[0].JSON_subval; assert(X); - if (name == "jesc") { + if (name == "jsinsert") { + std::string pure_json = json::generate_str(*X, json::print_pretty); + rstrip(pure_json); + append(pure_json, result); + } else if (name == "jesc") { std::string escaped_json = escape(json::generate_str(*X, json::print_pretty)); rstrip(escaped_json); append(escaped_json, result); diff --git a/src/http_server/new_york_transit_line/templater.cpp b/src/http_server/new_york_transit_line/templater.cpp index fa613af..b20b365 100644 --- a/src/http_server/new_york_transit_line/templater.cpp +++ b/src/http_server/new_york_transit_line/templater.cpp @@ -111,6 +111,7 @@ namespace nytl { void Templater::update() { elements = { + {"jsinsert", Element{{json::JSON(true)}, true}}, {"jesc", Element{{json::JSON(true)}, true}}, {"jesccomp", Element{{json::JSON(true)}, true}}, /* str2text base element has a dedicated operator - WRITE */ diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp new file mode 100644 index 0000000..f4a604f --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp @@ -0,0 +1,114 @@ +#include "server_data_interact.h" + +#include +#include + +namespace iu9cawebchat { + const char* stringify_user_chat_role(int64_t role) { + if (role == user_chat_role_admin) + return "admin"; + if (role == user_chat_role_regular) + return "regular"; + if (role == user_chat_role_read_only) + return "read-only"; + return "not-a-member"; + } + + int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password) { + SqliteStatement sql_req(conn, + "SELECT `id` FROM `user` WHERE `nickname` = ?1 AND `password` = ?2", + {}, {{1, nickname}, {2, password}}); + fsql_integer_or_null id_col; + int status = sqlite_stmt_step(sql_req, {{0, &id_col}}, {}); + if (status == SQLITE_ROW) { + een9_ASSERT_pl(id_col.exist & id_col.value >= 0); + return id_col.value; + } + return -1; + } + + std::string find_user_name (SqliteConnection& conn, int64_t uid) { + een9_ASSERT(uid >= 0, "Are you crazy?"); + SqliteStatement sql_req(conn, + "SELECT `name` FROM `user` WHERE `id` = ?1", + {{1, uid}}, {}); + fsql_text8_or_null name_col; + int status = sqlite_stmt_step(sql_req, {}, {{0, &name_col}}); + if (status == SQLITE_ROW) { + een9_ASSERT_pl(name_col.exist); + return name_col.value; + } + return ""; + } + + RowUser_Content lookup_user_content(SqliteConnection &conn, int64_t uid) { + een9_ASSERT(uid >= 0, "Are you crazy?"); + SqliteStatement sql_req(conn, + "SELECT `nickname`, `name` FROM `user` WHERE `id` = ?1", + {{1, uid}}, {}); + fsql_text8_or_null nickname_col; + fsql_text8_or_null name_col; + int status = sqlite_stmt_step(sql_req, {}, {{0, &nickname_col}, {1, &name_col}}); + if (status == SQLITE_ROW) { + return {std::move(nickname_col.value), std::move(name_col.value)}; + } + return {}; + } + + RowChat_Content lookup_chat_content(SqliteConnection &conn, int64_t uid) { + een9_ASSERT(uid >= 0, "Are you crazy?"); + SqliteStatement sql_req(conn, + "SELECT `nickname`, `name`, `lastMsgId` FROM `chat` WHERE `id` = ?1", + {{1, uid}}, {}); + fsql_text8_or_null nickname_col; + fsql_text8_or_null name_col; + fsql_integer_or_null last_msg_id_col; + int status = sqlite_stmt_step(sql_req, {{2, &last_msg_id_col}}, {{0, &nickname_col}, {1, &name_col}}); + if (status == SQLITE_ROW) { + return {std::move(nickname_col.value), std::move(name_col.value), + last_msg_id_col.exist ? last_msg_id_col.value : -1}; + } + return {}; + } + + void initial_extraction_of_all_the_useful_info_from_cookies( + SqliteConnection& conn, const een9::ClientRequest& req, + std::vector> &ret_cookies, + std::vector &ret_login_cookies, + json::JSON &ret_userinfo, + int64_t& ret_logged_in_user + ) { + ret_cookies = een9::findAllClientCookies(req.headers); + ret_login_cookies = select_login_cookies(ret_cookies); + ret_logged_in_user = -1; /* Negative means that user is not logged in */ + if (!ret_login_cookies.empty()){ + size_t oldest_ind = select_oldest_login_cookie(ret_login_cookies); + LoginCookie& tried = ret_login_cookies[oldest_ind]; + ret_logged_in_user = find_user_by_credentials(conn, tried.nickname, tried.password); + if (ret_logged_in_user >= 0) { + ret_userinfo["uid"] = json::JSON(ret_logged_in_user); + ret_userinfo["nickname"] = json::JSON(tried.nickname); + ret_userinfo["name"] = json::JSON(find_user_name(conn, ret_logged_in_user)); + } + } + } + + int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId) { + SqliteStatement req(conn, + "SELECT `role` FROM `user_chat_membership` WHERE `userId` = ?1 AND `chatId` = ?2", + {{1, userId}, {2, chatId}}, {}); + fsql_integer_or_null role; + int status = sqlite_stmt_step(req, {{0, &role}}, {}); + if (status == SQLITE_ROW) { + return role.exist ? (int)role.value : user_chat_role_deleted; + } + return user_chat_role_deleted; + } + + std::string RTEE(const std::string& el_name, + const json::JSON& config_presentation, WorkerGuestData& wgd, + const json::JSON& userinfo) { + std::string page = wgd.templater->render(el_name, {&config_presentation, &userinfo}); + return een9::form_http_server_response_200("text/html", page); + } +} diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h new file mode 100644 index 0000000..e6361ac --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h @@ -0,0 +1,79 @@ +#ifndef IU9_CA_WEB_CHAT_LIB_BACKEND_LOGIC_SERVER_DATA_INTERACT_H +#define IU9_CA_WEB_CHAT_LIB_BACKEND_LOGIC_SERVER_DATA_INTERACT_H + +/* This folder covers all code that helps to interact with database and templater, +* or dictates the logic of how this web chat functions */ + +#include +#include +#include "../sqlite3_wrapper.h" +#include "../login_cookie.h" +#include +#include +#include + +namespace iu9cawebchat { + constexpr int64_t user_chat_role_admin = 1; + constexpr int64_t user_chat_role_regular = 2; + constexpr int64_t user_chat_role_read_only = 3; + constexpr int64_t user_chat_role_deleted = 4; + + const char* stringify_user_chat_role(int64_t role); + + struct WorkerGuestData { + /* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */ + std::unique_ptr templater; + std::unique_ptr db; + int debug_trans_op; // todo: delete when debug is over + }; + + int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password); + std::string find_user_name (SqliteConnection& conn, int64_t uid); + + struct RowUser_Content { + std::string nickname; + std::string name; + }; + + struct RowChat_Content { + std::string nickname; + std::string name; + int64_t lastMsgId; // Negative if it does not exist + }; + + RowUser_Content lookup_user_content(SqliteConnection& conn, int64_t uid); + RowChat_Content lookup_chat_content(SqliteConnection& conn, int64_t uid); + + + void initial_extraction_of_all_the_useful_info_from_cookies( + SqliteConnection& conn, const een9::ClientRequest& req, + std::vector>& ret_cookies, + std::vector& ret_login_cookies, + json::JSON& ret_userinfo, + int64_t& ret_logged_in_user + ); + + int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId); + + std::string RTEE(const std::string& el_name, + const json::JSON& config_presentation, WorkerGuestData& wgd, + const json::JSON& userinfo); + + /* ========================== PAGES ================================== */ + std::string when_page_login(WorkerGuestData& wgd, const json::JSON& config_presentation, + const een9::ClientRequest& req, const std::vector& login_cookies, const json::JSON& userinfo); + + + json::JSON internalapi_pollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); + + std::string when_internalapi_pollevents(WorkerGuestData& wgd, + const een9::ClientRequest& req, int64_t uid); + + + json::JSON internalapi_getChatList(SqliteConnection& conn, int64_t uid); + + std::string when_internalapi_getchatlist(WorkerGuestData& wgd, + const een9::ClientRequest& req, int64_t uid); +} + +#endif diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_getchatlist.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_getchatlist.cpp new file mode 100644 index 0000000..ff441fe --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_getchatlist.cpp @@ -0,0 +1,42 @@ +#include "server_data_interact.h" +#include +#include +#include "../str_fields.h" + +namespace iu9cawebchat { + json::JSON internalapi_getChatList(SqliteConnection& conn, int64_t uid) { + json::JSON Recv; + Recv["status"] = json::JSON(0l); + Recv["chats"] = json::JSON(json::array); + std::vector& chats = Recv["chats"].g().asArray(); + SqliteStatement req(conn, + "SELECT `chat`.`id`, `chat`.`nickname`, `chat`.`name`, `chat`.`lastMsgId`, " + "`user_chat_membership`.`role` FROM `chat` " + "RIGHT JOIN `user_chat_membership` ON `chat`.`id` = `user_chat_membership`.`chatId` " + "WHERE `user_chat_membership`.`userId` = ?1 ", {{1, uid}}, {}); + while (true) { + fsql_integer_or_null chat_id; + fsql_text8_or_null chat_nickname, chat_name; + fsql_integer_or_null chat_lastMsgId, role_here; + int status = sqlite_stmt_step(req, {{0, &chat_id}, {3, &chat_lastMsgId}, {4, &role_here}}, + {{1, &chat_nickname}, {2, &chat_name}}); + if (status != SQLITE_ROW) + break; + chats.emplace_back(); + json::JSON& chat = chats.back(); + chat["id"] = json::JSON(chat_id.value); + chat["content"]["nickname"] = json::JSON(chat_nickname.value); + chat["content"]["name"] = json::JSON(chat_name.value); + chat["content"]["lastMsgId"] = json::JSON(chat_lastMsgId.exist ? chat_lastMsgId.value : -1); + chat["content"]["roleHere"] = json::JSON(stringify_user_chat_role(role_here.value)); + } + return Recv; + } + + std::string when_internalapi_getchatlist(WorkerGuestData& wgd, + const een9::ClientRequest& req, int64_t uid) { + const json::JSON& Sent = json::parse_str_flawless(req.body); + std::string result = json::generate_str(internalapi_getChatList(*wgd.db, uid), json::print_pretty); + return een9::form_http_server_response_200("text/json", result); + } +} diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_pollevents.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_pollevents.cpp new file mode 100644 index 0000000..b6af302 --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_pollevents.cpp @@ -0,0 +1,142 @@ +#include "server_data_interact.h" +#include +#include +#include "../str_fields.h" +#include + +namespace iu9cawebchat { + int64_t get_current_history_id_of_chat(SqliteConnection& conn, int64_t chatId) { + SqliteStatement req(conn, "SELECT `it_HistoryId` FROM `chat` WHERE `id` = ?1", {{1, chatId}}, {}); + fsql_integer_or_null HistoryId; + int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {}); + een9_ASSERT_pl(status == SQLITE_ROW); + return HistoryId.value; + } + + int64_t get_current_history_id_of_user_chatList(SqliteConnection& conn, int64_t userId) { + SqliteStatement req(conn, "SELECT `chatList_HistoryId` FROM `user` WHERE `id` = ?1", {{1, userId}}, {}); + fsql_integer_or_null HistoryId; + int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {}); + een9_ASSERT_pl(status == SQLITE_ROW); + return HistoryId.value; + } + + void internalapi_pollEvents_in_chat_collect_membership_events(SqliteConnection& conn, + std::vector& events, int64_t chatId, int64_t LocalHistoryId) { + SqliteStatement membership_changes(conn, + "SELECT `userId`, `role`, FROM `user_chat_membership` WHERE `chatId` = ?1 " + "AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {}); + fsql_integer_or_null ev_userId; + fsql_integer_or_null ev_user_role; + while (true) { + int status = sqlite_stmt_step(membership_changes, + {{0, &ev_userId}, {1, &ev_user_role}}, {}); + if (status != SQLITE_ROW) + break; + events.emplace_back(); + json::JSON& event = events.back(); + event["member"] = json::JSON(ev_userId.value); + if (ev_user_role.value == user_chat_role_deleted) { + event["type"] = json::JSON("removedMember"); + } else { + event["type"] = json::JSON("addedMember"); + RowUser_Content USER = lookup_user_content(conn, ev_userId.value); + event["content"]["name"] = json::JSON(USER.name); + event["content"]["nickname"] = json::JSON(USER.nickname); + event["content"]["role"] = json::JSON(stringify_user_chat_role(ev_user_role.value)); + } + events.push_back(event); + } + } + + void internalapi_pollEvents_in_chat_collect_messages_events(SqliteConnection& conn, + std::vector& events, int64_t chatId, int64_t LocalHistoryId) { + SqliteStatement messages_changes(conn, + "SELECT `id`, `previous`, `senderUserId`, `exists`, `isSystem`, `text` FROM `messages` WHERE " + "WHERE `chatId` = ?1 AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {}); + fsql_integer_or_null ev_msgId, ev_previousMsgId, msgSenderUserId, msgExists, msgIsSystem; + fsql_text8_or_null msgText; + while (true) { + int status = sqlite_stmt_step(messages_changes, + {{0, &ev_msgId}, {1, &ev_previousMsgId}, {2, &msgSenderUserId}, {3, &msgExists}, {4, &msgIsSystem}}, + {{5, &msgText}}); + if (status != SQLITE_ROW) + break; + events.emplace_back(); + json::JSON& event = events.back(); + event["type"] = json::JSON("newMessage"); + event["id"] = json::JSON(ev_msgId.value); + event["previous"] = json::JSON(ev_previousMsgId.value); + event["content"]["sender"] = json::JSON(msgSenderUserId.value); + event["content"]["isSystem"] = json::JSON((bool)msgIsSystem.value); + event["content"]["text"] = json::JSON(msgText.value); + events.push_back(event); + } + } + + void internalapi_pollEvents_in_user_chatList_collect_events(SqliteConnection& conn, + std::vector& events, int64_t userId, int64_t LocalHistoryId) { + SqliteStatement membership_changes(conn, + "SELECT `chatId`, `role` FROM `user_chat_membership` WHERE `userId` = ?1 " + "AND `user_chatList_IncHistoryId` > ?2", {{1, userId}, {2, LocalHistoryId}}); + fsql_integer_or_null ev_chatId, usersRoleHere; + while (true) { + int status = sqlite_stmt_step(membership_changes, {{0, &ev_chatId}, {1, &usersRoleHere}}, {}); + if (status != SQLITE_ROW) + break; + events.emplace_back(); + json::JSON& event = events.back(); + event["id"] = json::JSON(ev_chatId.value); + if (usersRoleHere.value == user_chat_role_deleted) { + event["type"] = json::JSON("removedChat"); + } else { + event["type"] = json::JSON("addedChat"); + RowChat_Content CHAT = lookup_chat_content(conn, ev_chatId.value); + event["content"]["name"] = json::JSON(CHAT.name); + event["content"]["nickname"] = json::JSON(CHAT.nickname); + event["content"]["lastMsgId"] = json::JSON(CHAT.lastMsgId); + event["content"]["roleHere"] = json::JSON(stringify_user_chat_role(usersRoleHere.value)); + } + } + } + + json::JSON internalapi_pollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { + json::JSON Recv; + Recv["status"] = json::JSON(0l); + Recv["update"] = json::JSON(json::array); + const std::vector& req_scope = Sent["scope"].g().asArray(); + std::vector& updated = Recv["update"].g().asArray(); + for (const json::JSON& hist_entity_request: req_scope) { + updated.emplace_back(); + json::JSON& hist_entity_response = updated.back(); + hist_entity_response["type"] = hist_entity_request["type"].g(); + hist_entity_response["events"] = json::JSON(json::array); + std::vector& events = hist_entity_response["events"].g().asArray(); + const int64_t LocalHistoryId = hist_entity_request["LocalHistoryId"].g().asInteger().get_int(); + if (hist_entity_request["type"].g().asString() == "chat") { + int64_t chatId = hist_entity_request["chatId"].g().asInteger().get_int(); + if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) + een9_THROW("internalapi: trying to access chat that user does not belong to"); + hist_entity_response["chatId"] = json::JSON(chatId); + hist_entity_response["HistoryId"] = json::JSON(get_current_history_id_of_chat(conn, chatId)); + /* Two classes of 'real events' can happen to chat: membership table change, message table change */ + /* Here, I collect membership changes (related to this chat) */ + internalapi_pollEvents_in_chat_collect_membership_events(conn, events, chatId, LocalHistoryId); + /* Here, I collect message changes (related to this chat) */ + internalapi_pollEvents_in_chat_collect_messages_events(conn, events, chatId, LocalHistoryId); + } else if (hist_entity_request["type"].g().asString() == "chatlist") { + hist_entity_response["HistotyId"] = json::JSON(get_current_history_id_of_user_chatList(conn, uid)); + internalapi_pollEvents_in_user_chatList_collect_events(conn, events, uid, LocalHistoryId); + } else + een9_THROW("Bad request"); + } + return Recv; + } + + std::string when_internalapi_pollevents(WorkerGuestData& wgd, + const een9::ClientRequest& req, int64_t uid) { + const json::JSON& Sent = json::parse_str_flawless(req.body); + std::string result = json::generate_str(internalapi_pollEvents(*wgd.db, uid, Sent), json::print_pretty); + return een9::form_http_server_response_200("text/json", result); + } +} diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_login.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_login.cpp new file mode 100644 index 0000000..d69516f --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_login.cpp @@ -0,0 +1,38 @@ +#include "server_data_interact.h" +#include +#include +#include "../str_fields.h" + +namespace iu9cawebchat { + std::string when_page_login(WorkerGuestData& wgd, const json::JSON& config_presentation, + const een9::ClientRequest& req, const std::vector& login_cookies, const json::JSON& userinfo) { + if (req.method == "POST") { + std::vector> query = een9::split_html_query(req.body); + int64_t uid = -1; + std::string nickname; + std::string password; + try { + for (const std::pair& cmp: query) { + if (cmp.first == "nickname") + nickname = cmp.second; + if (cmp.first == "password") + password = cmp.second; + } + een9_ASSERT(check_nickname(nickname), "/login/accpet-data rejected impossible nickname"); + een9_ASSERT(check_password(password), "/login/accpet-data rejected impossible password"); + uid = find_user_by_credentials(*wgd.db, nickname, password); + + } catch(const std::exception& e){} + if (uid < 0) { + printf("Redirecting back to /login because of incorrect credentials\n"); + /* todo: Here I need to tell somehow to user (through fancy red box, maybe), that login was incorrect */ + return RTEE("login", config_presentation, wgd, userinfo); + } + std::vector> response_hlines; + LoginCookie new_login_cookie = create_login_cookie(nickname, password); + add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie); + return een9::form_http_server_response_307_spec_head("/", response_hlines); + } + return RTEE("login", config_presentation, wgd, userinfo); + } +} diff --git a/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp b/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp index 2c621b1..d5cd081 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp @@ -35,44 +35,49 @@ namespace iu9cawebchat { * If chat.lastMsgId is NULL, chat is empty * If message.previous is NULL, this message is first in it's chat */ - sqlite_nooutput(conn.hand, "PRAGMA foreign_keys = true"); - sqlite_nooutput(conn.hand, "BEGIN"); - sqlite_nooutput(conn.hand, "CREATE TABLE `nickname` (`it` TEXT PRIMARY KEY NOT NULL) WITHOUT ROWID"); - sqlite_nooutput(conn.hand, "CREATE TABLE `user` (" - "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," - "`name` TEXT NOT NULL," - "`chatList_HistoryId` INTEGER NOT NULL," - "`password` TEXT NOT NULL" - ")"); - sqlite_nooutput(conn.hand, "CREATE TABLE `chat` (" - "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," - "`name` TEXT NOT NULL," - "`it_HistoryId` INTEGER NOT NULL," - "`lastMsgId` INTEGER REFERENCES `message`" - ")"); - sqlite_nooutput(conn.hand, "CREATE TABLE `user_chat_membership` (" - "`userId` INTEGER REFERENCES `user` NOT NULL," - "`chatId` INTEGER REFERENCES `chat` NOT NULL," - "`user_chatList_IncHistoryId` INTEGER NOT NULL," - "`chat_IncHistoryId` INTEGER NOT NULL," - "`role` INTEGER NOT NULL" - ")"); - sqlite_nooutput(conn.hand, "CREATE TABLE `message` (" - "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - "`chatId` INTEGER REFERENCES `chat` NOT NULL," - "`previous` INTEGER REFERENCES `message`," - "`senderUserId` INTEGER REFERENCES `user` NOT NULL," - "`exists` BOOLEAN NOT NULL," - "`isSystem` BOOLEAN NOT NULL," - "`text` TEXT NOT NULL," - "`chat_IncHistoryId` INTEGER NOT NULL" - ")"); - sqlite_nooutput(conn.hand, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}}); - sqlite_nooutput(conn.hand, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES " - "(0, ?1, ?2, 0, ?3)", {}, - {{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}}); - sqlite_nooutput(conn.hand, "END"); + try { + sqlite_nooutput(conn, "PRAGMA foreign_keys = true"); + sqlite_nooutput(conn, "BEGIN"); + sqlite_nooutput(conn, "CREATE TABLE `nickname` (`it` TEXT PRIMARY KEY NOT NULL) WITHOUT ROWID"); + sqlite_nooutput(conn, "CREATE TABLE `user` (" + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," + "`name` TEXT NOT NULL," + "`chatList_HistoryId` INTEGER NOT NULL," + "`password` TEXT NOT NULL" + ")"); + sqlite_nooutput(conn, "CREATE TABLE `chat` (" + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," + "`name` TEXT NOT NULL," + "`it_HistoryId` INTEGER NOT NULL," + "`lastMsgId` INTEGER REFERENCES `message`" + ")"); + sqlite_nooutput(conn, "CREATE TABLE `user_chat_membership` (" + "`userId` INTEGER REFERENCES `user` NOT NULL," + "`chatId` INTEGER REFERENCES `chat` NOT NULL," + "`user_chatList_IncHistoryId` INTEGER NOT NULL," + "`chat_IncHistoryId` INTEGER NOT NULL," + "`role` INTEGER NOT NULL" + ")"); + sqlite_nooutput(conn, "CREATE TABLE `message` (" + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "`chatId` INTEGER REFERENCES `chat` NOT NULL," + "`previous` INTEGER REFERENCES `message`," + "`senderUserId` INTEGER REFERENCES `user` NOT NULL," + "`exists` BOOLEAN NOT NULL," + "`isSystem` BOOLEAN NOT NULL," + "`text` TEXT NOT NULL," + "`chat_IncHistoryId` INTEGER NOT NULL" + ")"); + sqlite_nooutput(conn, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}}); + sqlite_nooutput(conn, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES " + "(0, ?1, ?2, 0, ?3)", {}, + {{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}}); + sqlite_nooutput(conn, "END"); + } catch (const std::exception& e) { + sqlite_nooutput(conn, "ROLLBACK", {}, {}); + throw; + } } } diff --git a/src/web_chat/iu9_ca_web_chat_lib/run.cpp b/src/web_chat/iu9_ca_web_chat_lib/run.cpp index 082c7aa..3cff03a 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/run.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/run.cpp @@ -1,20 +1,29 @@ #include "actions.h" - #include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include "sqlite3_wrapper.h" -#include "str_fields.h" #include "find_db.h" -#include "login_cookie.h" -#include +#include +#include +#include "str_fields.h" + +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include "sqlite3_wrapper.h" +// #include "str_fields.h" +// #include "find_db.h" +// #include "login_cookie.h" +// #include +// #include + +#include "backend_logic/server_data_interact.h" namespace iu9cawebchat { bool termination = false; @@ -45,11 +54,6 @@ namespace iu9cawebchat { int ret = find_db_sqlite_file_path(config, sqlite_db_path); een9_ASSERT(ret == 0, "Can't find database file"); - struct WorkerGuestData { - /* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */ - std::unique_ptr templater; - std::unique_ptr db; - }; std::vector worker_guest_data(slave_number); for (int i = 0; i < slave_number; i++) { worker_guest_data[i].templater = std::make_unique( @@ -63,101 +67,53 @@ namespace iu9cawebchat { (const een9::SlaveTask& task, const een9::ClientRequest& req, een9::worker_id_t worker_id) -> std::string { een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size()); WorkerGuestData& wgd = worker_guest_data[worker_id]; - nytl::Templater& templater = *wgd.templater; een9::StaticAsset sa; - int ret; - auto find_user_by_credentials = [&](const std::string& nickname, const std::string& password) -> int64_t { - SqliteStatement sql_req(*wgd.db, - "SELECT `id` FROM `user` WHERE `nickname` = ?1 AND `password` = ?2", - {}, {{1, nickname}, {2, password}}); - fsql_integer_or_null id_col; - int status = sqlite_stmt_step(sql_req, {{0, &id_col}}, {}); - if (status == SQLITE_ROW) { - een9_ASSERT_pl(id_col.exist & id_col.value >= 0); - return id_col.value; - } - return -1; - }; - auto find_user_name = [&](int64_t uid) -> std::string { - een9_ASSERT(uid >= 0, "Are you crazy?"); - SqliteStatement sql_req(*wgd.db, - "SELECT `name` FROM `user` WHERE `id` = ?1", - {{1, uid}}, {}); - fsql_text8_or_null name_col; - int status = sqlite_stmt_step(sql_req, {}, {{0, &name_col}}); - if (status == SQLITE_ROW) { - een9_ASSERT_pl(name_col.exist); - return name_col.value; - } - return ""; - }; + sqlite_nooutput(*wgd.db, "BEGIN", {}, {}); + struct guard {SqliteConnection& conn; bool rollback = false; ~guard() { + if (rollback) + sqlite_nooutput(conn, "ROLLBACK", {}, {}); + else + sqlite_nooutput(conn, "END", {}, {}); + }} guard_{*wgd.db}; + try { + std::vector> cookies; + std::vector login_cookies; + json::JSON userinfo; + int64_t logged_in_user = -1; + initial_extraction_of_all_the_useful_info_from_cookies(*wgd.db, req, cookies, login_cookies, userinfo, logged_in_user); - std::vector> cookies = een9::findAllClientCookies(req.headers); - std::vector login_cookies = select_login_cookies(cookies); - json::JSON userinfo; - userinfo["uid"] = json::JSON("-1"); - userinfo["nickname"] = json::JSON(""); - userinfo["name"] = json::JSON(""); - int64_t logged_in_user = -1; /* Negative means that user is not logged in */ - if (!login_cookies.empty()){ - size_t oldest_ind = select_oldest_login_cookie(login_cookies); - LoginCookie& tried = login_cookies[oldest_ind]; - logged_in_user = find_user_by_credentials(tried.nickname, tried.password); - if (logged_in_user >= 0) { - userinfo["uid"] = json::JSON(std::to_string(logged_in_user)); - userinfo["nickname"] = json::JSON(tried.nickname); - userinfo["name"] = json::JSON(find_user_name(logged_in_user)); - } - } + std::string result; - auto RTEE = [&](const std::string& el_name) -> std::string { - std::string page = templater.render(el_name, {&config_presentation, &userinfo}); - return een9::form_http_server_response_200("text/html", page); - }; - - if (req.uri_path == "/" || req.uri_path == "/list-rooms") { - printf("DEBUG:::: %d\n", logged_in_user); - if (logged_in_user < 0) - return een9::form_http_server_response_307("/login"); - return RTEE("list-rooms"); - } - if (req.uri_path == "/login") { - if (req.method == "POST") { - std::vector> query = een9::split_html_query(req.body); - std::string nickname; - std::string password; - for (const std::pair& cmp: query) { - if (cmp.first == "nickname") - nickname = cmp.second; - if (cmp.first == "password") - password = cmp.second; - } - een9_ASSERT(check_nickname(nickname), "/login/accpet-data rejected impossible nickname"); - een9_ASSERT(check_password(password), "/login/accpet-data rejected impossible password"); - int64_t uid = find_user_by_credentials(nickname, password); - if (uid < 0) { - /* todo: Here I need to tell somehow to user (through fancy red box, maybe), that login was incorrect */ - return RTEE("login"); - } - std::vector> response_hlines; - LoginCookie new_login_cookie = create_login_cookie(nickname, password); - add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie); - return een9::form_http_server_response_307_spec_head("/", response_hlines); + if (req.uri_path == "/" || req.uri_path == "/list-rooms") { + if (logged_in_user < 0) + result = een9::form_http_server_response_307("/login"); + return RTEE("list-rooms", config_presentation, wgd, userinfo); } - return RTEE("login"); - } - if (req.uri_path == "/chat") { - return RTEE("chat"); - } - if (req.uri_path == "/profile") { - return RTEE("profile"); - } - if (req.uri_path == "/registration") { - return RTEE("registration"); + if (req.uri_path == "/login") { + return when_page_login(wgd, config_presentation, req, login_cookies, userinfo); + } + if (req.uri_path == "/chat") { + return RTEE("chat", config_presentation, wgd, userinfo); + } + if (req.uri_path == "/profile") { + return RTEE("profile", config_presentation, wgd, userinfo); + } + // if (req.uri_path == "/registration") { + // RTEE("registration", config_presentation, wgd, userinfo); + // } + if (req.uri_path == "/internalapi/pollEvents") { + return when_internalapi_pollevents(wgd, req, logged_in_user); + } + if (req.uri_path == "/internalapi/getChatList") { + return when_internalapi_getchatlist(wgd, req, logged_in_user); + } + } catch (const std::exception& e) { + guard_.rollback = true; + throw; } /* Trying to interpret request as asset lookup */ - ret = samI.get_asset(req.uri_path, sa); - if (ret >= 0) { + int rets = samI.get_asset(req.uri_path, sa); + if (rets >= 0) { return een9::form_http_server_response_200(sa.type, sa.content); } return een9::form_http_server_response_404("text/html", "

Not found!

"); @@ -169,26 +125,26 @@ namespace iu9cawebchat { WorkerGuestData& wgd = worker_guest_data[worker_id]; try { if (req == "hello") { - return ":0 omg! hiii!! Hewwou :3 !!!!"; + return ":0 omg! hiii!! Hewwou :3 !!!!\n"; } if (req == "8") { termination = true; - return "Bye"; + return "Bye\n"; } std::string updaterootpw_pref = "updaterootpw"; if (een9::beginsWith(req, "updaterootpw")) { size_t nid = updaterootpw_pref.size(); if (nid >= req.size() || !isSPACE(req[nid])) - return "Bad command syntax. Missing whitespace"; + return "Bad command syntax. Missing whitespace\n"; std::string new_password = req.substr(nid + 1); if (!check_password(new_password)) een9_THROW("Bad password"); - sqlite_nooutput(wgd.db->hand, + sqlite_nooutput(*wgd.db, "UPDATE `user` SET `password` = ?1 WHERE `id` = 0 ", {}, {{1, new_password}}); - return "Successul update"; + return "Successul update\n"; } - return "Incorrect command"; + return "Incorrect command\n"; } catch (std::exception& e) { return std::string("Server error\n") + e.what(); } diff --git a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp index f5a6190..55f150c 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp @@ -13,42 +13,22 @@ namespace iu9cawebchat { } SqliteConnection::~SqliteConnection() { - if (sqlite3_close(hand) != 0) {abort();} + sqlite3_close_v2(hand); } - void sqlite_nooutput(sqlite3* db_hand, const std::string& req_statement, + void sqlite_nooutput(SqliteConnection& conn, const std::string& req_statement, const std::vector>& int64_binds, const std::vector>& text8_binds) { - sqlite3_stmt* stmt_obj = NULL; - int ret = sqlite3_prepare_v2(db_hand, req_statement.c_str(), -1, &stmt_obj, NULL); - if (ret != 0) { - int err_pos = sqlite3_error_offset(db_hand); - een9_THROW("Compilation of request\n" + req_statement + "\nfailed" + - ((err_pos >= 0) ? " with offset " + std::to_string(err_pos) : "")); - } - assert(sqlite3_errcode(db_hand) == SQLITE_OK); - struct Guard1{sqlite3_stmt*& r; ~Guard1(){if (sqlite3_finalize(r) != 0) {abort();}}} guard1{stmt_obj}; - for (const std::pair& bv: int64_binds) { - ret = sqlite3_bind_int64(stmt_obj, bv.first, bv.second); - een9_ASSERT(ret == 0, "sqlite3_bind_int64"); - } - for (const std::pair& bv: text8_binds) { - een9_ASSERT(is_orthodox_string(bv.second), "Can't bind this string to parameter"); - een9_ASSERT(bv.second.size() + 1 < INT_MAX, "Ah, oh, senpai, your string is toooo huge"); - ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_STATIC); - een9_ASSERT(ret == 0, "sqlite3_bind_text"); - } + SqliteStatement stmt(conn, req_statement, int64_binds, text8_binds); + int ret; while (true) { - ret = sqlite3_step(stmt_obj); - if (ret == SQLITE_DONE) - break; + ret = sqlite_stmt_step(stmt, {}, {}); if (ret != SQLITE_ROW) - een9_THROW(std::string("sqlite_row ") + sqlite3_errstr(ret)); - int cc = sqlite3_column_count(stmt_obj); + break; + int cc = sqlite3_column_count(stmt.stmt_obj); std::vector types(cc); - for (int i = 0; i < cc; i++) { - types[i] = sqlite3_column_type(stmt_obj, i); - } + for (int i = 0; i < cc; i++) + types[i] = sqlite3_column_type(stmt.stmt_obj, i); printf("Column: |"); for (int i = 0; i < cc; i++) { switch (types[i]) { @@ -67,18 +47,18 @@ namespace iu9cawebchat { printf("Values: | "); for (int i = 0; i < cc; i++) { if (types[i] == SQLITE_INTEGER) { - printf("%lld | ", sqlite3_column_int64(stmt_obj, i)); + printf("%lld | ", sqlite3_column_int64(stmt.stmt_obj, i)); } else if (types[i] == SQLITE_FLOAT) { - printf("%lf | ", sqlite3_column_double(stmt_obj, i)); + printf("%lf | ", sqlite3_column_double(stmt.stmt_obj, i)); } else if (types[i] == SQLITE_BLOB) { - const void* blob = sqlite3_column_blob(stmt_obj, i); - een9_ASSERT(sqlite3_errcode(db_hand) == SQLITE_OK, "oom in sqlite3_column_blob"); - size_t sz = sqlite3_column_bytes(stmt_obj, i); + const void* blob = sqlite3_column_blob(stmt.stmt_obj, i); + een9_ASSERT(sqlite3_errcode(conn.hand) == SQLITE_OK, "oom in sqlite3_column_blob"); + size_t sz = sqlite3_column_bytes(stmt.stmt_obj, i); printf("Blob of size %lu | ", sz); } else if (types[i] == SQLITE_NULL) { printf("NULL | "); } else { - const unsigned char* text = sqlite3_column_text(stmt_obj, i); + const unsigned char* text = sqlite3_column_text(stmt.stmt_obj, i); een9_ASSERT(text, "oom in sqlite3_column_text"); printf("%s | ", (const char*)text); // todo: print only if string is safe to print @@ -108,7 +88,7 @@ namespace iu9cawebchat { for (const std::pair& bv: text8_binds) { een9_ASSERT(is_orthodox_string(bv.second), "Can't bind this string to parameter"); een9_ASSERT(bv.second.size() + 1 < INT_MAX, "Ah, oh, senpai, your string is toooo huge"); - ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_STATIC); + ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_TRANSIENT); een9_ASSERT(ret == 0, "sqlite3_bind_text"); } } catch (const std::exception& e) { @@ -127,7 +107,8 @@ namespace iu9cawebchat { int ret = sqlite3_step(stmt.stmt_obj); if (ret == SQLITE_DONE) return ret; - een9_ASSERT(ret == SQLITE_ROW, std::string("sqlite3_step ") + sqlite3_errstr(ret)); + if (ret != SQLITE_ROW) + een9_THROW(std::string("sqlite3_step ") + sqlite3_errstr(ret) + " :> " + sqlite3_errmsg(stmt.conn.hand)); int cc = sqlite3_column_count(stmt.stmt_obj); for (auto& resp: ret_of_integer_or_null) { if (resp.first >= cc) diff --git a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h index 620203f..2c880a8 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h +++ b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h @@ -15,9 +15,9 @@ namespace iu9cawebchat { ~SqliteConnection(); }; - void sqlite_nooutput(sqlite3* db_hand, const std::string& req_statement, - const std::vector>& int64_binds= {}, - const std::vector>& text8_binds = {}); + void sqlite_nooutput(SqliteConnection& conn, const std::string& req_statement, + const std::vector>& int64_binds = {}, + const std::vector>& text8_binds = {}); struct fsql_integer_or_null { bool exist; @@ -33,7 +33,7 @@ namespace iu9cawebchat { SqliteConnection& conn; sqlite3_stmt* stmt_obj = NULL; SqliteStatement(SqliteConnection& connection, const std::string& req_statement, - const std::vector>& int64_binds= {}, + const std::vector>& int64_binds = {}, const std::vector>& text8_binds = {}); SqliteStatement(SqliteStatement&) = delete; SqliteStatement& operator=(SqliteStatement&) = delete; diff --git a/src/web_chat/misc_tests/api_test0.cpp b/src/web_chat/misc_tests/api_test0.cpp new file mode 100644 index 0000000..e2d3f29 --- /dev/null +++ b/src/web_chat/misc_tests/api_test0.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +using namespace iu9cawebchat; + +void test(SqliteConnection& conn, int64_t uid){ + json::JSON Recv = internalapi_getChatList(conn, uid); + printf("%s\n", json::generate_str(Recv, json::print_pretty).c_str()); +} + +void test_polling(SqliteConnection& conn, int64_t uid, int64_t LocalHistoryId) { + json::JSON Sent; + Sent["scope"][0]["type"] = json::JSON("chatlist"); + Sent["scope"][0]["LocalHistoryId"] = json::JSON(LocalHistoryId); + + json::JSON Recv = internalapi_pollEvents(conn, uid, Sent); + printf("%s\n", json::generate_str(Recv, json::print_pretty).c_str()); +} + +int main() { + SqliteConnection conn("./iu9-ca-web-chat.db"); + // test(conn, 0); + // test(conn, 1); + // test(conn, 2); + // printf("\n\n ===== Now testing polling of events ===== \n\n"); + test_polling(conn, 1, 0); + test_polling(conn, 1, 1); + test_polling(conn, 1, 2); + return 0; +} diff --git a/src/web_chat/misc_tests/find_credentials_test.cpp b/src/web_chat/misc_tests/find_credentials_test.cpp new file mode 100644 index 0000000..9bf2239 --- /dev/null +++ b/src/web_chat/misc_tests/find_credentials_test.cpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include + +using namespace iu9cawebchat; + +int main() { + SqliteConnection conn("./iu9-ca-web-chat.db"); + for (size_t i = 0; i < 100; i++) { + int64_t uid = find_user_by_credentials(conn, "root", "12345678"); + assert(uid == 0); + } +} \ No newline at end of file