Fiexs bugs caused by misuse if sqlite3, fixed very old memory leak, added separate .cpp files for each request type, added raw login page, did some refactoring, added functions for http redirection (307), /internalapi/pollEvents is done, /internalapi/getChatList is done

This commit is contained in:
Андреев Григорий 2024-08-25 13:06:26 +03:00
parent a6f4bd6c88
commit 799e156f88
21 changed files with 693 additions and 279 deletions

View File

@ -1,6 +1,6 @@
{% ELDEF main JSON pres JSON userinfo %}
<!DOCTYPE html>
<html lang="ru">
<html lang="{% WRITE pres.lang %}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -8,7 +8,7 @@
<link rel="stylesheet" href="/assets/css/list-rooms.css">
</head>
<body>
{% PUT pass-userinfo userinfo %}
{% PUT pass-pres-userinfo pres userinfo %}
<div class="container">
<h1 style="color: white;">Выберите Чат-Комнату</h1>
<ul class="room-list">
@ -18,51 +18,51 @@
</div>
<!-- Модальное окно для создания комнаты -->
<div id="createRoomModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close" onclick="closeCreateRoomModal()">&times;</span>
<h2>Создать Комнату</h2>
</div>
<div class="modal-body">
<input type="text" id="newRoomName" placeholder="Название комнаты">
<input type="password" id="newRoomNickname" placeholder="Никнейм комнаты">
</div>
<div id="error"></div>
<div class="modal-footer">
<button class="join-button" onclick="createRoom()">Создать</button>
</div>
</div>
</div>
<!--<div id="createRoomModal" class="modal">-->
<!-- <div class="modal-content">-->
<!-- <div class="modal-header">-->
<!-- <span class="close" onclick="closeCreateRoomModal()">&times;</span>-->
<!-- <h2>Создать Комнату</h2>-->
<!-- </div>-->
<!-- <div class="modal-body">-->
<!-- <input type="text" id="newRoomName" placeholder="Название комнаты">-->
<!-- <input type="password" id="newRoomNickname" placeholder="Никнейм комнаты">-->
<!-- </div>-->
<!-- <div id="error"></div>-->
<!-- <div class="modal-footer">-->
<!-- <button class="join-button" onclick="createRoom()">Создать</button>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
<!-- Модальное окно для добавления участников -->
<div class="overlay" id="add_members">
<div class="add-members">
<div class="add-members-header">
<span class="close" onclick="closeAdd()">&times;</span>
<h2>Добавить участников</h2>
</div>
<div class="add-members-body">
<input type="text" id="newMemberLogin" placeholder="Логин пользователя">
</div>
<div class="add-members-footer">
<button class="add-member-button" onclick="addMember()">Добавить</button>
</div>
</div>
</div>
<div class="overlay" id="delete-chat">
<div class="delete-chat">
<div class="delete-chat-header">
<span class="delete-close" onclick="closeConfirm()">&times;</span>
<h2>Вы уверены, что хотите удалить чат?</h2>
</div>
<div class="delete-chat-body">
<button class="confirm" onclick="deleteChat()">Да</button>
<button class="cancel" onclick="closeConfirm()">Нет</button>
</div>
</div>
</div>
<!--<script src="/assets/js/list-rooms.js"></script>-->
<!--&lt;!&ndash; Модальное окно для добавления участников &ndash;&gt;-->
<!--<div class="overlay" id="add_members">-->
<!-- <div class="add-members">-->
<!-- <div class="add-members-header">-->
<!-- <span class="close" onclick="closeAdd()">&times;</span>-->
<!-- <h2>Добавить участников</h2>-->
<!-- </div>-->
<!-- <div class="add-members-body">-->
<!-- <input type="text" id="newMemberLogin" placeholder="Логин пользователя">-->
<!-- </div>-->
<!-- <div class="add-members-footer">-->
<!-- <button class="add-member-button" onclick="addMember()">Добавить</button>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
<!--<div class="overlay" id="delete-chat">-->
<!-- <div class="delete-chat">-->
<!-- <div class="delete-chat-header">-->
<!-- <span class="delete-close" onclick="closeConfirm()">&times;</span>-->
<!-- <h2>Вы уверены, что хотите удалить чат?</h2>-->
<!-- </div>-->
<!-- <div class="delete-chat-body">-->
<!-- <button class="confirm" onclick="deleteChat()">Да</button>-->
<!-- <button class="cancel" onclick="closeConfirm()">Нет</button>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
<script src="/assets/js/list-rooms.js"></script>
</body>
</html>
{% ENDELDEF %}

View File

@ -1,17 +1,17 @@
{% ELDEF main JSON pres JSON userinfo %}
<!DOCTYPE html>
<html lang="{% WRITE pres.lang %}">
<head>
<!DOCTYPE html>
<html lang="{% WRITE pres.lang %}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% WRITE pres.phr.decl.page-login %}</title>
<link rel="stylesheet" href="/assets/css/login.css">
</head>
</head>
<body>
{% PUT pass-userinfo userinfo %}
<div class="form-container">
<body>
{% PUT pass-pres-userinfo pres userinfo %}
<div class="form-container">
<h1 class="hide-cursor no-select">{% WRITE pres.phr.decl.enter %}</h1>
<form action="/login" method="post" enctype="application/x-www-form-urlencoded">
<label for="nickname">{% WRITE pres.phr.decl.nickname %}</label>
@ -20,8 +20,8 @@
<input type="password" name="password" id="password"><br>
<button type="submit" class="hide-cursor no-select">{% WRITE pres.phr.act.enter %}</button>
</form>
</div>
</div>
</body>
</html>
</body>
</html>
{% ENDELDEF %}

View File

@ -0,0 +1,6 @@
{% ELDEF main JSON pres JSON userinfo %}
<script>
let pres = {% PUT jsinsert pres %};
let userinfo = {% PUT jsinsert userinfo %};
</script>
{% ENDELDEF %}

View File

@ -1,5 +0,0 @@
{% ELDEF main JSON userinfo %}
<input type="hidden" id="hidden_field_uid" name="uid" value="{% WRITE userinfo.uid %}">
<input type="hidden" id="hidden_field_nickname" name="nickname" value="{% WRITE userinfo.nickname %}">
<input type="hidden" id="hidden_field_name" name="name" value="{% WRITE userinfo.name %}">
{% ENDELDEF %}

View File

@ -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;

View File

@ -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"]
}
}

View File

@ -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<std::pair<std::string, std::string>>& 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<std::pair<std::string, std::string>>& headers) {
std::vector<std::pair<std::string, std::string>> 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);
}
}

View File

@ -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 {

View File

@ -34,7 +34,7 @@ namespace nytl {
} else if (P.isDictionary() && what.isString()) {
const std::map<std::string, json::JSON>& 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);

View File

@ -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 */

View File

@ -0,0 +1,114 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
#include <engine_engine_number_9/http_structures/cookies.h>
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<std::pair<std::string, std::string>> &ret_cookies,
std::vector<LoginCookie> &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);
}
}

View File

@ -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 <memory>
#include <new_york_transit_line/templater.h>
#include "../sqlite3_wrapper.h"
#include "../login_cookie.h"
#include <engine_engine_number_9/http_structures/client_request.h>
#include <engine_engine_number_9/http_structures/response_gen.h>
#include <jsonincpp/string_representation.h>
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<nytl::Templater> templater;
std::unique_ptr<SqliteConnection> 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<std::pair<std::string, std::string>>& ret_cookies,
std::vector<LoginCookie>& 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<LoginCookie>& 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

View File

@ -0,0 +1,42 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
#include <engine_engine_number_9/baza_throw.h>
#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<json::JSON>& 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);
}
}

View File

@ -0,0 +1,142 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
#include <engine_engine_number_9/baza_throw.h>
#include "../str_fields.h"
#include <engine_engine_number_9/http_structures/response_gen.h>
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<json::JSON>& 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<json::JSON>& 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<json::JSON>& 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<json::JSON>& req_scope = Sent["scope"].g().asArray();
std::vector<json::JSON>& 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<json::JSON>& 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);
}
}

View File

@ -0,0 +1,38 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
#include <engine_engine_number_9/baza_throw.h>
#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<LoginCookie>& login_cookies, const json::JSON& userinfo) {
if (req.method == "POST") {
std::vector<std::pair<std::string, std::string>> query = een9::split_html_query(req.body);
int64_t uid = -1;
std::string nickname;
std::string password;
try {
for (const std::pair<std::string, std::string>& 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<std::pair<std::string, std::string>> 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);
}
}

View File

@ -35,31 +35,32 @@ 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` ("
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.hand, "CREATE TABLE `chat` ("
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.hand, "CREATE TABLE `user_chat_membership` ("
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.hand, "CREATE TABLE `message` ("
sqlite_nooutput(conn, "CREATE TABLE `message` ("
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
"`chatId` INTEGER REFERENCES `chat` NOT NULL,"
"`previous` INTEGER REFERENCES `message`,"
@ -69,10 +70,14 @@ namespace iu9cawebchat {
"`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 "
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.hand, "END");
sqlite_nooutput(conn, "END");
} catch (const std::exception& e) {
sqlite_nooutput(conn, "ROLLBACK", {}, {});
throw;
}
}
}

View File

@ -1,20 +1,29 @@
#include "actions.h"
#include <engine_engine_number_9/baza_throw.h>
#include <engine_engine_number_9/running_mainloop.h>
#include <engine_engine_number_9/http_structures/response_gen.h>
#include <signal.h>
#include <engine_engine_number_9/os_utils.h>
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
#include <assert.h>
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
#include <new_york_transit_line/templater.h>
#include <sqlite3.h>
#include <engine_engine_number_9/socket_address.h>
#include "sqlite3_wrapper.h"
#include "str_fields.h"
#include "find_db.h"
#include "login_cookie.h"
#include <engine_engine_number_9/http_structures/cookies.h>
#include <engine_engine_number_9/running_mainloop.h>
#include <signal.h>
#include "str_fields.h"
// #include <engine_engine_number_9/baza_throw.h>
// #include <engine_engine_number_9/running_mainloop.h>
// #include <engine_engine_number_9/http_structures/response_gen.h>
// #include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
// #include <assert.h>
// #include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
// #include <new_york_transit_line/templater.h>
// #include <sqlite3.h>
// #include <engine_engine_number_9/socket_address.h>
// #include "sqlite3_wrapper.h"
// #include "str_fields.h"
// #include "find_db.h"
// #include "login_cookie.h"
// #include <engine_engine_number_9/http_structures/cookies.h>
// #include <jsonincpp/string_representation.h>
#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<nytl::Templater> templater;
std::unique_ptr<SqliteConnection> db;
};
std::vector<WorkerGuestData> worker_guest_data(slave_number);
for (int i = 0; i < slave_number; i++) {
worker_guest_data[i].templater = std::make_unique<nytl::Templater>(
@ -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 "";
};
std::vector<std::pair<std::string, std::string>> cookies = een9::findAllClientCookies(req.headers);
std::vector<LoginCookie> login_cookies = select_login_cookies(cookies);
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<std::pair<std::string, std::string>> cookies;
std::vector<LoginCookie> login_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));
}
}
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);
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);
};
std::string result;
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");
result = een9::form_http_server_response_307("/login");
return RTEE("list-rooms", config_presentation, wgd, userinfo);
}
if (req.uri_path == "/login") {
if (req.method == "POST") {
std::vector<std::pair<std::string, std::string>> query = een9::split_html_query(req.body);
std::string nickname;
std::string password;
for (const std::pair<std::string, std::string>& 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<std::pair<std::string, std::string>> 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");
return when_page_login(wgd, config_presentation, req, login_cookies, userinfo);
}
if (req.uri_path == "/chat") {
return RTEE("chat");
return RTEE("chat", config_presentation, wgd, userinfo);
}
if (req.uri_path == "/profile") {
return RTEE("profile");
return RTEE("profile", config_presentation, wgd, userinfo);
}
if (req.uri_path == "/registration") {
return RTEE("registration");
// 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", "<h1> Not found! </h1>");
@ -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();
}

View File

@ -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<std::pair<int, int64_t>>& int64_binds,
const std::vector<std::pair<int, std::string>>& 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<int, int64_t>& bv: int64_binds) {
ret = sqlite3_bind_int64(stmt_obj, bv.first, bv.second);
een9_ASSERT(ret == 0, "sqlite3_bind_int64");
}
for (const std::pair<int, std::string>& 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<int> 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<int, std::string>& 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)

View File

@ -15,8 +15,8 @@ namespace iu9cawebchat {
~SqliteConnection();
};
void sqlite_nooutput(sqlite3* db_hand, const std::string& req_statement,
const std::vector<std::pair<int, int64_t>>& int64_binds= {},
void sqlite_nooutput(SqliteConnection& conn, const std::string& req_statement,
const std::vector<std::pair<int, int64_t>>& int64_binds = {},
const std::vector<std::pair<int, std::string>>& text8_binds = {});
struct fsql_integer_or_null {
@ -33,7 +33,7 @@ namespace iu9cawebchat {
SqliteConnection& conn;
sqlite3_stmt* stmt_obj = NULL;
SqliteStatement(SqliteConnection& connection, const std::string& req_statement,
const std::vector<std::pair<int, int64_t>>& int64_binds= {},
const std::vector<std::pair<int, int64_t>>& int64_binds = {},
const std::vector<std::pair<int, std::string>>& text8_binds = {});
SqliteStatement(SqliteStatement&) = delete;
SqliteStatement& operator=(SqliteStatement&) = delete;

View File

@ -0,0 +1,31 @@
#include <iu9_ca_web_chat_lib/backend_logic/server_data_interact.h>
#include <iu9_ca_web_chat_lib/sqlite3_wrapper.h>
#include <jsonincpp/string_representation.h>
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;
}

View File

@ -0,0 +1,14 @@
#include <iu9_ca_web_chat_lib/backend_logic/server_data_interact.h>
#include <iu9_ca_web_chat_lib/sqlite3_wrapper.h>
#include <jsonincpp/string_representation.h>
#include <assert.h>
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);
}
}