Save messages to database

This commit is contained in:
2026-07-23 14:31:13 +10:00
parent d61946d2ec
commit 1159bdebde
4 changed files with 186 additions and 15 deletions

View File

@@ -1,4 +1,5 @@
#include "db.h"
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <fstream>
@@ -60,6 +61,14 @@ Database::Database(const std::string& path) {
postid INTEGER NOT NULL,
userid INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY,
senderid INTEGER NOT NULL,
recieverid INTEGER NOT NULL,
timestamp INTEGER NOT NULL,
text TEXT NOT NULL
);
)";
char* errmsg = nullptr;
@@ -448,3 +457,69 @@ void Database::invalidateUserSessions(const User& user) {
sqlite3_finalize(stmt);
}
std::vector<Message> Database::getMessages(uint64_t userA, uint64_t userB, uint64_t amount) {
const char* sql = R"(
SELECT * FROM messages
WHERE (senderid = ? AND recieverid = ?) OR (senderid = ? AND recieverid = ?)
ORDER BY timestamp DESC
LIMIT ?;
)";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
}
sqlite3_bind_int64(stmt, 1, userA);
sqlite3_bind_int64(stmt, 2, userB);
sqlite3_bind_int64(stmt, 3, userB);
sqlite3_bind_int64(stmt, 4, userA);
sqlite3_bind_int64(stmt, 5, amount);
std::vector<Message> messages = {};
while (sqlite3_step(stmt) == SQLITE_ROW) {
uint64_t id = sqlite3_column_int64(stmt, 0);
uint64_t senderId = sqlite3_column_int64(stmt, 1);
uint64_t recieverId = sqlite3_column_int64(stmt, 2);
std::time_t timestamp = sqlite3_column_int64(stmt, 3);
const char* textptr = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 4));
std::string text;
if (textptr == NULL) {
text = "";
} else {
text = std::string(textptr);
}
messages.emplace_back(id, senderId, recieverId, text, timestamp);
}
sqlite3_finalize(stmt);
return messages;
}
void Database::addMessage(Message& message) {
const char* sql = R"(
INSERT INTO messages (senderid, recieverid, timestamp, text)
VALUES (?, ?, ?, ?);
)";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
}
sqlite3_bind_int64(stmt, 1, message.sender);
sqlite3_bind_int64(stmt, 2, message.reciever);
sqlite3_bind_int64(stmt, 3, message.timestamp);
sqlite3_bind_text(stmt, 4, message.content.c_str(), -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_DONE) {
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
}
sqlite3_finalize(stmt);
}

View File

@@ -5,6 +5,7 @@
#include <optional>
#include <string>
#include <sqlite3.h>
#include <sys/types.h>
#include <vector>
#include "post.h"
@@ -19,13 +20,33 @@ struct User {
std::string passwordHash = "";
std::string bio = "";
User(uint64_t id, std::string namein, const std::string& passwordHash, const std::string& bio) :
User(uint64_t id, const std::string& namein, const std::string& passwordHash, const std::string& bio) :
id(id), passwordHash(passwordHash), bio(bio), name(namein) {}
User(uint64_t id, std::string namein, const std::string& passwordHash, const std::string& bio, int doSanitize) :
id(id), passwordHash(passwordHash), bio(bio) {
sanitize(namein);
name = namein;
}
};
struct Message {
uint64_t id = 0;
uint64_t sender = 0;
uint64_t reciever = 0;
std::time_t timestamp;
std::string content = "";
Message(uint64_t id, uint64_t sender, uint64_t reciever, const std::string& content, std::time_t timestamp) :
id(id), sender(sender), reciever(reciever), content(content), timestamp(timestamp) {}
Message(uint64_t id, uint64_t sender, uint64_t reciever, std::string contentin, std::time_t timestamp, int doSanitize) :
id(id), sender(sender), reciever(reciever), timestamp(timestamp) {
sanitize(contentin);
content = contentin;
}
};
class Database {
sqlite3* db;
@@ -52,6 +73,10 @@ class Database {
void updateUser(const User& user);
void invalidateUserSessions(const User& user);
std::vector<Message> getMessages(uint64_t userA, uint64_t userB, uint64_t amount);
// modifies the message to have it's id
void addMessage(Message& message);
};
#endif

View File

@@ -1,4 +1,5 @@
#include <cstdint>
#include <ctime>
#include <mutex>
#include <sstream>
#include <stdexcept>
@@ -171,7 +172,7 @@ int main() {
response.set_content("<p>hey you can't have an empty username!!!!1!!!1! >:(</p>", "text/html");
return;
}
User newUser{0, username, bcrypt::generateHash(password), ""};
User newUser{0, username, bcrypt::generateHash(password), "", 1};
database.addUser(newUser);
user = newUser;
}
@@ -483,15 +484,15 @@ int main() {
Json returnData;
returnData["type"] = "ok";
ws.send(returnData.dump());
} else {
Json data;
data["type"] = "error";
data["content"] = "friend not online";
ws.send(data.dump());
connectedFriends.erase(userId);
}
// Store the message in database
{
std::lock_guard<std::mutex> lock(data_mutex);
Message message{0, user->id, userId, content, std::time(nullptr), 1};
database.addMessage(message);
}
// TODO:
// - Store in databse
// - Verify users are friends
} else if (type == 1) { // friend request
@@ -545,6 +546,47 @@ int main() {
}
});
svr.Get("/friends/chatHistory/:userid", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
std::string userIdStr = request.path_params.at("userid");
std::lock_guard<std::mutex> lock(data_mutex);
try {
std::optional<User> user = getLoggedInUser(request, database);
if (!user.has_value()) {
response.status = 401;
response.set_content("your session is invalid", "text/plain");
return;
}
uint64_t friendUserId = std::stoull(userIdStr);
std::vector<Message> messages = database.getMessages(user->id, friendUserId, 100);
// construct json for response
Json list;
for (auto it = messages.rbegin(); it != messages.rend(); ++it) {
Json message;
message["sender"] = it->sender;
message["reciever"] = it->reciever;
message["id"] = it->id;
message["timestamp"] = it->timestamp;
message["content"] = it->content;
list.push_back(message);
}
Json data;
data["messages"] = list;
response.set_content(data.dump(), "application/json");
} catch (const std::runtime_error& e) {
response.status = 500;
response.set_content("<p>there was an error :( it is: " + std::string(e.what()) + "</p>", "text/html");
} catch (const std::exception& e) {
response.status = 500;
response.set_content("<p>there was an error :( it is: " + std::string(e.what()) + "</p>", "text/html");
}
});
// settings endpoints
svr.Post("/settings/changePassword", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {