yay initial commit
This commit is contained in:
229
server/src/db.cpp
Normal file
229
server/src/db.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#include "db.h"
|
||||
#include <iostream>
|
||||
#include <sqlite3.h>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
Database::Database(const std::string& path) {
|
||||
// init db
|
||||
if (sqlite3_open(path.c_str(), &db) != SQLITE_OK) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
// create tables in case they don't exist
|
||||
const char* sql = R"(
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
password TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS posts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
userid INTEGER NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
timestamp INTEGER NOT NULL,
|
||||
likes INTEGER
|
||||
);
|
||||
)";
|
||||
|
||||
char* errmsg = nullptr;
|
||||
|
||||
if (sqlite3_exec(db, sql, nullptr, nullptr, &errmsg) != SQLITE_OK) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(errmsg));
|
||||
}
|
||||
}
|
||||
|
||||
Database::~Database() {
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
std::optional<Post> Database::getPost(uint64_t id) {
|
||||
std::stringstream sql;
|
||||
sql << "SELECT * FROM posts WHERE id = " << id << ";";
|
||||
std::string sqlstr = sql.str();
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
if (sqlite3_prepare_v2(db, sqlstr.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
// we only want the first one (there's probably only one)
|
||||
// if there is none, return nothing
|
||||
if (sqlite3_step(stmt) == SQLITE_DONE) {
|
||||
return {};
|
||||
}
|
||||
int64_t userid = sqlite3_column_int64(stmt, 1);
|
||||
std::string content{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))};
|
||||
int64_t timestamp = sqlite3_column_int64(stmt, 3);
|
||||
int64_t likes = sqlite3_column_int64(stmt, 4);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// get user info from post
|
||||
std::optional<User> user = getUser(userid);
|
||||
if (user.has_value()) {
|
||||
return Post(id, content, user->name, timestamp, likes);
|
||||
} else {
|
||||
throw std::runtime_error("post has invalid user ID attached (this is weird)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<Post> Database::getUserPosts(uint64_t userId) {
|
||||
std::stringstream sql;
|
||||
sql << "SELECT * FROM posts WHERE userid = " << userId << ";";
|
||||
std::string sqlstr = sql.str();
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
if (sqlite3_prepare_v2(db, sqlstr.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
// get user info from post
|
||||
std::optional<User> user = getUser(userId);
|
||||
if (!user.has_value()) {
|
||||
throw std::runtime_error("user does not exist");
|
||||
}
|
||||
|
||||
std::vector<Post> posts = {};
|
||||
|
||||
while (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||
int64_t postid = sqlite3_column_int64(stmt, 0);
|
||||
std::string content{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))};
|
||||
int64_t timestamp = sqlite3_column_int64(stmt, 3);
|
||||
int64_t likes = sqlite3_column_int64(stmt, 4);
|
||||
|
||||
posts.emplace_back(postid, content, user->name, timestamp, likes);
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
return posts;
|
||||
}
|
||||
|
||||
std::vector<Post> Database::getTopPosts(uint64_t amount) {
|
||||
std::stringstream sql;
|
||||
sql << "SELECT * FROM posts ORDER BY timestamp DESC LIMIT " << amount << ";";
|
||||
std::string sqlstr = sql.str();
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
if (sqlite3_prepare_v2(db, sqlstr.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
std::vector<Post> posts = {};
|
||||
while (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||
int64_t postid = sqlite3_column_int64(stmt, 0);
|
||||
int64_t userid = sqlite3_column_int64(stmt, 1);
|
||||
std::string content{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))};
|
||||
int64_t timestamp = sqlite3_column_int64(stmt, 3);
|
||||
int64_t likes = sqlite3_column_int64(stmt, 4);
|
||||
|
||||
std::cout << userid << std::endl;
|
||||
|
||||
// get username from id
|
||||
std::optional<User> user = getUser(userid);
|
||||
if (!user.has_value()) {
|
||||
throw std::runtime_error("post has invalid user ID attached (this is weird)");
|
||||
}
|
||||
|
||||
posts.emplace_back(postid, content, user->name, timestamp, likes);
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
return posts;
|
||||
|
||||
}
|
||||
|
||||
void Database::addPost(const Post& post) {
|
||||
const char* sql = R"(
|
||||
INSERT INTO posts (userid, content, timestamp)
|
||||
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, post.userId);
|
||||
sqlite3_bind_text(stmt, 2, post.content.c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 3, post.time);
|
||||
|
||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::optional<User> Database::getUser(uint64_t id) {
|
||||
std::stringstream sql;
|
||||
sql << "SELECT * FROM users WHERE id = " << id << ";";
|
||||
std::string sqlstr = sql.str();
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
if (sqlite3_prepare_v2(db, sqlstr.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
// we only want the first one (there's probably only one)
|
||||
// if there is none, return nothing
|
||||
if (sqlite3_step(stmt) == SQLITE_DONE) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string name{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))};
|
||||
std::string password{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))};
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return User(id, name, password);
|
||||
}
|
||||
|
||||
std::optional<User> Database::getUserByName(const std::string& name) {
|
||||
std::string sql = "SELECT * FROM users WHERE name = ?;";
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
// we only want the first one (there's probably only one)
|
||||
// if there is none, return nothing
|
||||
if (sqlite3_step(stmt) == SQLITE_DONE) {
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t id = sqlite3_column_int64(stmt, 0);
|
||||
std::string password{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))};
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
return User(id, name, password);
|
||||
}
|
||||
|
||||
void Database::addUser(User& user) {
|
||||
const char* sql = R"(
|
||||
INSERT INTO users (name, password)
|
||||
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_text(stmt, 1, user.name.c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, user.passwordHash.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
user.id = sqlite3_last_insert_rowid(db);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
Reference in New Issue
Block a user