forked from chookspace/chookchat
Compare commits
10 Commits
reallynnir
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e99d312937 | |||
| dd7e1f8981 | |||
| 0965749fc8 | |||
| 3a63c68c2d | |||
| 09ec5a33ec | |||
| 5d5982c2dc | |||
| 7e9271db72 | |||
| c6a0e91e7e | |||
| 720a101468 | |||
| a7dbe2fca3 |
1
build.sh
1
build.sh
@@ -7,6 +7,7 @@ bin2cpp --file=client/header_bottom.html --output=server/src/generated
|
|||||||
|
|
||||||
bin2cpp --file=client/footer.html --output=server/src/generated
|
bin2cpp --file=client/footer.html --output=server/src/generated
|
||||||
bin2cpp --file=client/login.html --output=server/src/generated
|
bin2cpp --file=client/login.html --output=server/src/generated
|
||||||
|
bin2cpp --file=client/settings.html --output=server/src/generated
|
||||||
bin2cpp --file=client/e404.html --output=server/src/generated
|
bin2cpp --file=client/e404.html --output=server/src/generated
|
||||||
bin2cpp --file=client/style.css --output=server/src/generated
|
bin2cpp --file=client/style.css --output=server/src/generated
|
||||||
bin2cpp --file=client/script.js --output=server/src/generated
|
bin2cpp --file=client/script.js --output=server/src/generated
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">home</a></li>
|
|
||||||
<li><a id="loginLink" href="/login">login</a></li>
|
<li><a id="loginLink" href="/login">login</a></li>
|
||||||
|
<li><a href="/settings">settings</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="postbox">
|
<div class="postbox" id="postbox">
|
||||||
<form action="/make_post" method="post">
|
<form id="postbox-form" action="/make_post" method="post">
|
||||||
<div>
|
<div>
|
||||||
<label for="username">Username:</label>
|
<label for="username">Username:</label>
|
||||||
<input name="username" id="username" />
|
<input name="username" id="username" />
|
||||||
@@ -35,6 +35,3 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div id="posts">
|
<div id="posts">
|
||||||
<div id="posts-header">
|
|
||||||
<h1>posts</h1>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">home</a></li>
|
|
||||||
<li><a id="loginLink" href="/login">login</a></li>
|
<li><a id="loginLink" href="/login">login</a></li>
|
||||||
|
<li><a href="/settings">settings</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="login" id="posts">
|
<div class="login" id="posts">
|
||||||
|
|||||||
174
client/script.js
174
client/script.js
@@ -8,7 +8,7 @@ async function like(id) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result.status === 401) {
|
if (result.status === 401) {
|
||||||
alert("you need to login before you like a post");
|
window.location.href = "/login";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,6 +18,25 @@ async function like(id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function post() {
|
||||||
|
const postbox = document.getElementById('newpost');
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('post', postbox.value);
|
||||||
|
|
||||||
|
const result = await fetch('/post', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.status < 200 || result.status >= 400) {
|
||||||
|
alert("you have been silenced by the server and your post did not go through");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = "/";
|
||||||
|
}
|
||||||
|
|
||||||
async function register() {
|
async function register() {
|
||||||
const usernameBox = document.getElementById('username');
|
const usernameBox = document.getElementById('username');
|
||||||
const passwordBox = document.getElementById('password');
|
const passwordBox = document.getElementById('password');
|
||||||
@@ -32,7 +51,7 @@ async function register() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result.status < 200 || result.status >= 400) {
|
if (result.status < 200 || result.status >= 400) {
|
||||||
alert("fard");
|
alert(await result.text());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +67,6 @@ async function login() {
|
|||||||
formData.append('username', usernameBox.value);
|
formData.append('username', usernameBox.value);
|
||||||
formData.append('password', passwordBox.value);
|
formData.append('password', passwordBox.value);
|
||||||
|
|
||||||
console.log(formData);
|
|
||||||
|
|
||||||
const result = await fetch('/login', {
|
const result = await fetch('/login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -57,10 +75,156 @@ async function login() {
|
|||||||
|
|
||||||
if (result.status < 200 || result.status >= 400) {
|
if (result.status < 200 || result.status >= 400) {
|
||||||
alert("for some reason the server hates you and didn't let you log into your account");
|
alert("for some reason the server hates you and didn't let you log into your account");
|
||||||
console.log(result.status);
|
|
||||||
console.log(result.content);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkLoginStatus() {
|
||||||
|
const result = await fetch('/me');
|
||||||
|
|
||||||
|
if (result.headers.get('X-Logged-In') !== 'true') {
|
||||||
|
return {
|
||||||
|
username: "",
|
||||||
|
bio: ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = {
|
||||||
|
username: result.headers.get('X-Username'),
|
||||||
|
bio: result.headers.get('X-Bio')
|
||||||
|
};
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
|
if (window.fetch) {
|
||||||
|
const userStatus = await checkLoginStatus();
|
||||||
|
|
||||||
|
// update nav link / redirect regardless of whether a postbox exists
|
||||||
|
const loginLink = document.getElementById("loginLink");
|
||||||
|
if (userStatus.username === "") {
|
||||||
|
if (window.location.pathname === "/settings") {
|
||||||
|
window.location.href = "/login";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (loginLink) {
|
||||||
|
loginLink.textContent = "hello, " + userStatus.username + "!";
|
||||||
|
loginLink.href = "/profile/" + userStatus.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userStatus.username !== "" && window.location.pathname === "/settings") {
|
||||||
|
// fill bio box with the bio
|
||||||
|
const textarea = document.getElementById("change-bio");
|
||||||
|
textarea.value = userStatus.bio;
|
||||||
|
}
|
||||||
|
|
||||||
|
const postbox = document.getElementById("postbox");
|
||||||
|
if (!postbox) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// postbox-specific setup stays here
|
||||||
|
if (userStatus.username === "") {
|
||||||
|
const newElement = document.createElement("p");
|
||||||
|
newElement.textContent = "Welcome to Chookchat!\nTo get posting, click 'login' to log in or create an account.\nEnjoy your stay!";
|
||||||
|
postbox.appendChild(newElement);
|
||||||
|
} else {
|
||||||
|
const helperText = document.createElement("p");
|
||||||
|
helperText.textContent = "You're posting as " + userStatus.username;
|
||||||
|
postbox.appendChild(helperText);
|
||||||
|
|
||||||
|
const textArea = document.createElement("textarea");
|
||||||
|
textArea.id = "newpost";
|
||||||
|
postbox.appendChild(textArea);
|
||||||
|
|
||||||
|
const postButton = document.createElement("button");
|
||||||
|
postButton.textContent = "Post";
|
||||||
|
postButton.id = "postbutton";
|
||||||
|
postButton.onclick = post;
|
||||||
|
postbox.appendChild(postButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = document.getElementById("postbox-form");
|
||||||
|
if (!form) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
form.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function settingsChangePassword() {
|
||||||
|
const oldpassword = document.getElementById("change-password-oldpassword");
|
||||||
|
const newpassword = document.getElementById("change-password");
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('oldpassword', oldpassword.value);
|
||||||
|
formData.append('newpassword', newpassword.value);
|
||||||
|
|
||||||
|
const result = await fetch('/settings/changePassword', {
|
||||||
|
method: "POST",
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.status < 200 || result.status >= 400) {
|
||||||
|
alert("for whatever reason your password wasn't changed");
|
||||||
|
} else {
|
||||||
|
alert("your password was changed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function settingsChangeUsername() {
|
||||||
|
const password = document.getElementById("change-username-password");
|
||||||
|
const newusername = document.getElementById("change-username");
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('password', password.value);
|
||||||
|
formData.append('newusername', newusername.value);
|
||||||
|
|
||||||
|
const result = await fetch('/settings/changeUsername', {
|
||||||
|
method: "POST",
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.status < 200 || result.status >= 400) {
|
||||||
|
alert("for whatever reason your username wasn't changed");
|
||||||
|
} else {
|
||||||
|
alert("your username was changed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function settingsChangeBio() {
|
||||||
|
const bio = document.getElementById("change-bio");
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('bio', bio.value);
|
||||||
|
|
||||||
|
const result = await fetch('/settings/changeBio', {
|
||||||
|
method: "POST",
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.status < 200 || result.status >= 400) {
|
||||||
|
alert("for whatever reason your bio wasn't changed");
|
||||||
|
} else {
|
||||||
|
alert("your bio was changed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function invalidateAllSessions() {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('a', 'a');
|
||||||
|
|
||||||
|
const result = await fetch('/settings/invalidateAllSessions', {
|
||||||
|
method: "POST",
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.status < 200 || result.status >= 400) {
|
||||||
|
alert("for whatever reason your sessions weren't invalidated");
|
||||||
|
} else {
|
||||||
|
alert("your sessions were invalidated!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
61
client/settings.html
Normal file
61
client/settings.html
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>posts</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script src="/script.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<div id="brand">
|
||||||
|
<h2><a href="/">chookchat</a></h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a id="loginLink" href="/login">login</a></li>
|
||||||
|
<li><a href="/settings">settings</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div id="posts">
|
||||||
|
<div id="posts-header">
|
||||||
|
<h1>settings</h1>
|
||||||
|
</div>
|
||||||
|
<div id="password-settings">
|
||||||
|
<h3>password</h3>
|
||||||
|
<p>Enter a new password in the box, and click 'change password' to set a new password</p>
|
||||||
|
<label for="change-password-oldpassword">Old password:</label>
|
||||||
|
<input id="change-password-oldpassword" type="password"></input>
|
||||||
|
<br>
|
||||||
|
<label for="change-password">New password:</label>
|
||||||
|
<input id="change-password" type="password"></input>
|
||||||
|
<br>
|
||||||
|
<button onclick="settingsChangePassword()">Change Password</button>
|
||||||
|
</div>
|
||||||
|
<div id="invalidate-sessions">
|
||||||
|
<h3>invalidate sessions</h3>
|
||||||
|
<p>this button will invalidate all your sessions, and you will have to log in again on all your devices.</p>
|
||||||
|
<button onclick="invalidateAllSessions()">Invalidate All Sessions</button>
|
||||||
|
</div>
|
||||||
|
<div id="username-settings">
|
||||||
|
<h3>username</h3>
|
||||||
|
<p>Enter a new username in the box, and click 'set username' to set a new username</p>
|
||||||
|
<p>Note: any links to your profile will break! However, any links to your posts will remain.</p>
|
||||||
|
<label for="change-username">New username:</label>
|
||||||
|
<input id="change-username"></input>
|
||||||
|
<br>
|
||||||
|
<label for="change-username-password">Password:</label>
|
||||||
|
<input id="change-username-password" type="password"></input>
|
||||||
|
<br>
|
||||||
|
<button onclick="settingsChangeUsername()">Change Username</button>
|
||||||
|
</div>
|
||||||
|
<div id="bio-settings">
|
||||||
|
<h3>bio</h3>
|
||||||
|
<p>set a new bio here!</p>
|
||||||
|
<textarea id="change-bio"></textarea>
|
||||||
|
<br>
|
||||||
|
<button onclick="settingsChangeBio()">Change Bio</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -62,6 +62,11 @@ img {
|
|||||||
gap: 5px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: lime;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
background: rgb(25, 25, 25);
|
background: rgb(25, 25, 25);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ sources = [
|
|||||||
'src/generated/header_bottom.cpp',
|
'src/generated/header_bottom.cpp',
|
||||||
'src/generated/footer.cpp',
|
'src/generated/footer.cpp',
|
||||||
'src/generated/login.cpp',
|
'src/generated/login.cpp',
|
||||||
|
'src/generated/settings.cpp',
|
||||||
'src/generated/style.cpp',
|
'src/generated/style.cpp',
|
||||||
'src/generated/script.cpp',
|
'src/generated/script.cpp',
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <ostream>
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -37,7 +38,8 @@ Database::Database(const std::string& path) {
|
|||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
password TEXT NOT NULL
|
password TEXT NOT NULL,
|
||||||
|
bio TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS posts (
|
CREATE TABLE IF NOT EXISTS posts (
|
||||||
@@ -53,6 +55,11 @@ Database::Database(const std::string& path) {
|
|||||||
userid INTEGER NOT NULL,
|
userid INTEGER NOT NULL,
|
||||||
expiry INTEGER NOT NULL
|
expiry INTEGER NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS likes (
|
||||||
|
postid INTEGER NOT NULL,
|
||||||
|
userid INTEGER NOT NULL
|
||||||
|
);
|
||||||
)";
|
)";
|
||||||
|
|
||||||
char* errmsg = nullptr;
|
char* errmsg = nullptr;
|
||||||
@@ -100,7 +107,7 @@ std::optional<Post> Database::getPost(uint64_t id) {
|
|||||||
|
|
||||||
std::vector<Post> Database::getUserPosts(uint64_t userId) {
|
std::vector<Post> Database::getUserPosts(uint64_t userId) {
|
||||||
std::stringstream sql;
|
std::stringstream sql;
|
||||||
sql << "SELECT * FROM posts WHERE userid = " << userId << ";";
|
sql << "SELECT * FROM posts WHERE userid = " << userId << " ORDER BY timestamp DESC LIMIT 100;";
|
||||||
std::string sqlstr = sql.str();
|
std::string sqlstr = sql.str();
|
||||||
|
|
||||||
sqlite3_stmt* stmt;
|
sqlite3_stmt* stmt;
|
||||||
@@ -183,13 +190,9 @@ void Database::addPost(const Post& post) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Database::addLike(uint64_t postId, uint64_t userId) {
|
void Database::addLike(uint64_t postId, uint64_t userId) {
|
||||||
|
// check if the post has already been liked
|
||||||
std::stringstream sql;
|
std::stringstream sql;
|
||||||
sql << R"(
|
sql << "SELECT * FROM likes WHERE postid = " << postId << " AND userId = " << userId << ";";
|
||||||
UPDATE posts
|
|
||||||
SET likes = COALESCE(likes, 0) + 1
|
|
||||||
WHERE id =
|
|
||||||
)";
|
|
||||||
sql << postId << ";";
|
|
||||||
std::string sqlstr = sql.str();
|
std::string sqlstr = sql.str();
|
||||||
|
|
||||||
sqlite3_stmt* stmt;
|
sqlite3_stmt* stmt;
|
||||||
@@ -197,12 +200,67 @@ void Database::addLike(uint64_t postId, uint64_t userId) {
|
|||||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
// the user has UNLIKED something??? impossible
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
// get rid of their like, how sad
|
||||||
|
sql.str("");
|
||||||
|
sql.clear();
|
||||||
|
sql << "UPDATE posts SET likes = COALESCE(likes, 1) - 1 WHERE id = " << postId << ";";
|
||||||
|
sqlstr = sql.str();
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(db, sqlstr.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||||
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
|
}
|
||||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
// remove like from the likes table :(
|
||||||
|
sql.str("");
|
||||||
|
sql.clear();
|
||||||
|
sql << "DELETE FROM likes WHERE postid = " << postId << " AND userid = " << userId << ";";
|
||||||
|
sqlstr = sql.str();
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(db, sqlstr.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||||
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
|
}
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
// add like to the post
|
||||||
|
sql.str("");
|
||||||
|
sql.clear();
|
||||||
|
sql << "UPDATE posts SET likes = COALESCE(likes, 0) + 1 WHERE id = " << postId << ";";
|
||||||
|
sqlstr = sql.str();
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(db, sqlstr.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||||
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
|
}
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
// add like to the likes table
|
||||||
|
sql.str("");
|
||||||
|
sql.clear();
|
||||||
|
sql << "INSERT INTO likes (postid, userid) VALUES (" << postId << ", " << userId << ");";
|
||||||
|
sqlstr = sql.str();
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(db, sqlstr.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||||
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
|
}
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -226,10 +284,17 @@ std::optional<User> Database::getUser(uint64_t id) {
|
|||||||
|
|
||||||
std::string name{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))};
|
std::string name{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))};
|
||||||
std::string password{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))};
|
std::string password{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))};
|
||||||
|
const char* bioText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
|
||||||
|
std::string bio;
|
||||||
|
if (bioText == NULL) {
|
||||||
|
bio = "";
|
||||||
|
} else {
|
||||||
|
bio = std::string(bioText);
|
||||||
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
return User(id, name, password);
|
return User(id, name, password, bio);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<User> Database::getUserByName(const std::string& name) {
|
std::optional<User> Database::getUserByName(const std::string& name) {
|
||||||
@@ -250,9 +315,16 @@ std::optional<User> Database::getUserByName(const std::string& name) {
|
|||||||
|
|
||||||
uint64_t id = sqlite3_column_int64(stmt, 0);
|
uint64_t id = sqlite3_column_int64(stmt, 0);
|
||||||
std::string password{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))};
|
std::string password{reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))};
|
||||||
|
const char* bioText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
|
||||||
|
std::string bio;
|
||||||
|
if (bioText == NULL) {
|
||||||
|
bio = "";
|
||||||
|
} else {
|
||||||
|
bio = std::string(bioText);
|
||||||
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
return User(id, name, password);
|
return User(id, name, password, bio);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> Database::createNewToken(uint64_t id) {
|
std::optional<std::string> Database::createNewToken(uint64_t id) {
|
||||||
@@ -331,3 +403,48 @@ void Database::addUser(User& user) {
|
|||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Database::updateUser(const User& user) {
|
||||||
|
const char* sql = R"(
|
||||||
|
UPDATE users
|
||||||
|
SET
|
||||||
|
name = ?,
|
||||||
|
bio = ?,
|
||||||
|
password = ?
|
||||||
|
WHERE id = ?;
|
||||||
|
)";
|
||||||
|
|
||||||
|
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.bio.c_str(), -1, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_text(stmt, 3, user.passwordHash.c_str(), -1, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_int64(stmt, 4, user.id);
|
||||||
|
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database::invalidateUserSessions(const User& user) {
|
||||||
|
const char* sql = R"(
|
||||||
|
DELETE FROM sessions WHERE userid = ?;
|
||||||
|
)";
|
||||||
|
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, user.id);
|
||||||
|
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ struct User {
|
|||||||
uint64_t id = 0;
|
uint64_t id = 0;
|
||||||
std::string name = "";
|
std::string name = "";
|
||||||
std::string passwordHash = "";
|
std::string passwordHash = "";
|
||||||
|
std::string bio = "";
|
||||||
|
|
||||||
User(uint64_t id, std::string namein, const std::string& passwordHash) :
|
User(uint64_t id, std::string namein, const std::string& passwordHash, const std::string& bio) :
|
||||||
id(id), passwordHash(passwordHash) {
|
id(id), passwordHash(passwordHash), bio(bio) {
|
||||||
sanitize(namein);
|
sanitize(namein);
|
||||||
name = namein;
|
name = namein;
|
||||||
}
|
}
|
||||||
@@ -48,6 +49,8 @@ class Database {
|
|||||||
|
|
||||||
// this will modify the user to have their user ID
|
// this will modify the user to have their user ID
|
||||||
void addUser(User& user);
|
void addUser(User& user);
|
||||||
|
void updateUser(const User& user);
|
||||||
|
void invalidateUserSessions(const User& user);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "generated/header_bottom.h"
|
#include "generated/header_bottom.h"
|
||||||
#include "generated/footer.h"
|
#include "generated/footer.h"
|
||||||
#include "generated/login.h"
|
#include "generated/login.h"
|
||||||
|
#include "generated/settings.h"
|
||||||
#include "generated/style.h"
|
#include "generated/style.h"
|
||||||
#include "generated/script.h"
|
#include "generated/script.h"
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ int main() {
|
|||||||
const bin2cpp::File& headerBottomFile = bin2cpp::getHeader_bottomHtmlFile();
|
const bin2cpp::File& headerBottomFile = bin2cpp::getHeader_bottomHtmlFile();
|
||||||
const bin2cpp::File& footerfile = bin2cpp::getFooterHtmlFile();
|
const bin2cpp::File& footerfile = bin2cpp::getFooterHtmlFile();
|
||||||
const bin2cpp::File& loginfile = bin2cpp::getLoginHtmlFile();
|
const bin2cpp::File& loginfile = bin2cpp::getLoginHtmlFile();
|
||||||
|
const bin2cpp::File& settingsfile = bin2cpp::getSettingsHtmlFile();
|
||||||
const bin2cpp::File& e404file = bin2cpp::getE404HtmlFile();
|
const bin2cpp::File& e404file = bin2cpp::getE404HtmlFile();
|
||||||
const bin2cpp::File& stylefile = bin2cpp::getStyleCssFile();
|
const bin2cpp::File& stylefile = bin2cpp::getStyleCssFile();
|
||||||
const bin2cpp::File& scriptfile = bin2cpp::getScriptJsFile();
|
const bin2cpp::File& scriptfile = bin2cpp::getScriptJsFile();
|
||||||
@@ -52,6 +54,7 @@ int main() {
|
|||||||
std::string headerBottom{headerBottomFile.getBuffer(), headerBottomFile.getSize()};
|
std::string headerBottom{headerBottomFile.getBuffer(), headerBottomFile.getSize()};
|
||||||
std::string footer{footerfile.getBuffer(), footerfile.getSize()};
|
std::string footer{footerfile.getBuffer(), footerfile.getSize()};
|
||||||
std::string login{loginfile.getBuffer(), loginfile.getSize()};
|
std::string login{loginfile.getBuffer(), loginfile.getSize()};
|
||||||
|
std::string settings{settingsfile.getBuffer(), settingsfile.getSize()};
|
||||||
std::string e404{e404file.getBuffer(), e404file.getSize()};
|
std::string e404{e404file.getBuffer(), e404file.getSize()};
|
||||||
std::string style{stylefile.getBuffer(), stylefile.getSize()};
|
std::string style{stylefile.getBuffer(), stylefile.getSize()};
|
||||||
std::string script{scriptfile.getBuffer(), scriptfile.getSize()};
|
std::string script{scriptfile.getBuffer(), scriptfile.getSize()};
|
||||||
@@ -67,7 +70,7 @@ int main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
svr.Get("/script.js", [&script](const httplib::Request& request, httplib::Response& response) {
|
svr.Get("/script.js", [&script](const httplib::Request& request, httplib::Response& response) {
|
||||||
response.set_content(script, "text/css");
|
response.set_content(script, "text/javascript");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.Get("/login", [&login](const httplib::Request& request, httplib::Response& response) {
|
svr.Get("/login", [&login](const httplib::Request& request, httplib::Response& response) {
|
||||||
@@ -78,6 +81,14 @@ int main() {
|
|||||||
response.set_content(login, "text/html");
|
response.set_content(login, "text/html");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
svr.Get("/settings", [&settings](const httplib::Request& request, httplib::Response& response) {
|
||||||
|
response.set_content(settings, "text/html");
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.Get("/settings.html", [&settings](const httplib::Request& request, httplib::Response& response) {
|
||||||
|
response.set_content(settings, "text/html");
|
||||||
|
});
|
||||||
|
|
||||||
svr.Post("/login", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
svr.Post("/login", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
std::string username = request.form.get_field("username");
|
std::string username = request.form.get_field("username");
|
||||||
std::string password = request.form.get_field("password");
|
std::string password = request.form.get_field("password");
|
||||||
@@ -90,17 +101,56 @@ int main() {
|
|||||||
if (user.has_value()) {
|
if (user.has_value()) {
|
||||||
if (!bcrypt::validatePassword(password, user->passwordHash)) {
|
if (!bcrypt::validatePassword(password, user->passwordHash)) {
|
||||||
response.status = 401;
|
response.status = 401;
|
||||||
response.set_content("<p>wrong password lmao</p><img src="https://media.tenor.com/wWX7upr7SvwAAAAM/byuntear-cat.gif" alt="your stupid lol">", "text/html");
|
response.set_content("<p>wrong password lmao</p><img src='https://media.tenor.com/wWX7upr7SvwAAAAM/byuntear-cat.gif' alt='your stupid lol'>", "text/html");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
response.status = 400;
|
||||||
|
response.set_content("<p>that username doesn't exist</p>", "text/html");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> token = database.createNewToken(user->id);
|
||||||
|
if (!token.has_value()) {
|
||||||
|
response.status = 500;
|
||||||
|
response.set_content("<p>couldn't create a session, sorry</p>", "text/html");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpOnly so script.js can't read/leak it, SameSite=Lax so it
|
||||||
|
// isn't sent on cross-site POSTs (basic CSRF mitigation),
|
||||||
|
// Max-Age matches the 30 day expiry stored in the DB
|
||||||
|
response.set_header(
|
||||||
|
"Set-Cookie",
|
||||||
|
"session=" + *token + "; Path=/; HttpOnly; SameSite=Lax; Max-Age=2592000"
|
||||||
|
);
|
||||||
|
response.set_redirect("/");
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.Post("/register", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
|
std::string username = request.form.get_field("username");
|
||||||
|
std::string password = request.form.get_field("password");
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex);
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::optional<User> user = database.getUserByName(username);
|
||||||
|
|
||||||
|
if (user.has_value()) {
|
||||||
|
response.status = 400;
|
||||||
|
response.set_content("<p>that username already exists</p>", "text/html");
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (username.empty()) {
|
if (username.empty()) {
|
||||||
response.status = 400;
|
response.status = 400;
|
||||||
response.set_content("<p>hey you can't have an empty username!!!!1!!!1! >:(</p>", "text/html");
|
response.set_content("<p>hey you can't have an empty username!!!!1!!!1! >:(</p>", "text/html");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// register on the fly, same as make_post currently does
|
User newUser{0, username, bcrypt::generateHash(password), ""};
|
||||||
User newUser{0, username, bcrypt::generateHash(password)};
|
|
||||||
database.addUser(newUser);
|
database.addUser(newUser);
|
||||||
user = newUser;
|
user = newUser;
|
||||||
}
|
}
|
||||||
@@ -130,6 +180,7 @@ int main() {
|
|||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << headerTop << "<meta content='Chookchat' property='og:title' /><meta content='See posts from cool people' property='og:description' />" << headerBottom;
|
ss << headerTop << "<meta content='Chookchat' property='og:title' /><meta content='See posts from cool people' property='og:description' />" << headerBottom;
|
||||||
|
ss << "<div id='posts-header'><h1>posts</h1></div>";
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(data_mutex);
|
std::lock_guard<std::mutex> lock(data_mutex);
|
||||||
|
|
||||||
@@ -147,11 +198,15 @@ int main() {
|
|||||||
} catch (const std::runtime_error& e) {
|
} catch (const std::runtime_error& e) {
|
||||||
response.status = 500;
|
response.status = 500;
|
||||||
response.set_content("<p>there was an error :( it is: " + std::string(e.what()) + "</p>", "text/html");
|
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");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.Get("/posts/:id", [&headerTop, &headerBottom, &footer, &e404, &database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
svr.Get("/posts/:id", [&headerTop, &headerBottom, &footer, &e404, &database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
std::string postId = request.path_params.at("id");
|
std::string postId = request.path_params.at("id");
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex);
|
||||||
try {
|
try {
|
||||||
uint64_t postIdNum = std::stoll(postId);
|
uint64_t postIdNum = std::stoll(postId);
|
||||||
std::optional<Post> post = database.getPost(postIdNum);
|
std::optional<Post> post = database.getPost(postIdNum);
|
||||||
@@ -174,6 +229,38 @@ int main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
svr.Get("/profile/:username", [&headerTop, &headerBottom, &footer, &e404, &database, &data_mutex](const httplib::Request& request, httplib::Response& response){
|
||||||
|
std::string username = request.path_params.at("username");
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex);
|
||||||
|
try {
|
||||||
|
std::optional<User> user = database.getUserByName(username);
|
||||||
|
if (!user.has_value()) {
|
||||||
|
response.status = 404;
|
||||||
|
response.set_content(e404, "text/html");
|
||||||
|
}
|
||||||
|
std::vector<Post> posts = database.getUserPosts(user->id);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << headerTop << "<meta content='See posts from " << username << " on Chookchat' property='og:title' />" << headerBottom;
|
||||||
|
ss << "<div id='posts-header'><h1>" << username << "</h1></div>";
|
||||||
|
ss << "<p>" << user->bio << "</p>";
|
||||||
|
for (const auto& post : posts) {
|
||||||
|
ss << post.genHtml();
|
||||||
|
}
|
||||||
|
ss << footer;
|
||||||
|
|
||||||
|
response.set_content(ss.str(), "text/html");
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// endpoint to be used by HTML forms
|
||||||
svr.Post("/make_post", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
svr.Post("/make_post", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
|
|
||||||
std::string username = request.get_param_value("username");
|
std::string username = request.get_param_value("username");
|
||||||
@@ -198,7 +285,7 @@ int main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// create user
|
// create user
|
||||||
User newUser{0, username, bcrypt::generateHash(password)};
|
User newUser{0, username, bcrypt::generateHash(password), ""};
|
||||||
database.addUser(newUser);
|
database.addUser(newUser);
|
||||||
userId = newUser.id;
|
userId = newUser.id;
|
||||||
}
|
}
|
||||||
@@ -213,16 +300,179 @@ int main() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// endpoint to be used in Javascript
|
||||||
|
svr.Post("/post", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
|
try {
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex);
|
||||||
|
std::string post = request.form.get_field("post");
|
||||||
|
|
||||||
|
std::optional<User> user = getLoggedInUser(request, database);
|
||||||
|
if (!user.has_value()) {
|
||||||
|
response.status = 401;
|
||||||
|
response.set_content("<p>you're not logged in, so you can't post</p>", "text/html");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
database.addPost(Post(post, user->id));
|
||||||
|
response.status = 200;
|
||||||
|
response.set_content("OK", "text/text");
|
||||||
|
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
svr.Post("/like", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
svr.Post("/like", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
try {
|
try {
|
||||||
std::string username = request.form.get_field("username");
|
|
||||||
std::string password = request.form.get_field("password");
|
|
||||||
std::string post = request.form.get_field("post");
|
std::string post = request.form.get_field("post");
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex);
|
||||||
|
|
||||||
|
std::optional<User> user = getLoggedInUser(request, database);
|
||||||
|
if (!user.has_value()) {
|
||||||
|
response.status = 401;
|
||||||
|
response.set_content("i dunno that user", "text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t postNum = std::stoull(post);
|
||||||
|
database.addLike(postNum, user->id);
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.Get("/me", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex);
|
||||||
|
try {
|
||||||
|
std::optional<User> user = getLoggedInUser(request, database);
|
||||||
|
response.set_header("Cache-Control", "no-store");
|
||||||
|
|
||||||
|
if (!user.has_value()) {
|
||||||
|
response.set_header("X-Logged-In", "false");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.set_header("X-Logged-In", "true");
|
||||||
|
response.set_header("X-Username", user->name);
|
||||||
|
response.set_header("X-Bio", user->bio);
|
||||||
|
} 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) {
|
||||||
|
std::string oldpassword = request.form.get_field("oldpassword");
|
||||||
|
std::string newpassword = request.form.get_field("newpassword");
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(data_mutex);
|
std::lock_guard<std::mutex> lock(data_mutex);
|
||||||
|
|
||||||
uint64_t postNum = std::stoull(post);
|
try {
|
||||||
database.addLike(postNum, 0);
|
std::optional<User> user = getLoggedInUser(request, database);
|
||||||
|
if (!user.has_value()) {
|
||||||
|
response.status = 401;
|
||||||
|
response.set_content("your session is invalid", "text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bcrypt::validatePassword(oldpassword, user->passwordHash)) {
|
||||||
|
response.status = 400;
|
||||||
|
response.set_content("invalid password", "text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user->passwordHash = bcrypt::generateHash(newpassword);
|
||||||
|
database.updateUser(*user);
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.Post("/settings/changeUsername", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
|
std::string password = request.form.get_field("password");
|
||||||
|
std::string newusername = request.form.get_field("newusername");
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bcrypt::validatePassword(password, user->passwordHash)) {
|
||||||
|
response.status = 400;
|
||||||
|
response.set_content("invalid password", "text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user->name = newusername;
|
||||||
|
database.updateUser(*user);
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.Post("/settings/changeBio", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
|
std::string bio = request.form.get_field("bio");
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
user->bio = bio;
|
||||||
|
database.updateUser(*user);
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.Post("/settings/invalidateAllSessions", [&database, &data_mutex](const httplib::Request& request, httplib::Response& response) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
database.invalidateUserSessions(*user);
|
||||||
} catch (const std::runtime_error& e) {
|
} catch (const std::runtime_error& e) {
|
||||||
response.status = 500;
|
response.status = 500;
|
||||||
response.set_content("<p>there was an error :( it is: " + std::string(e.what()) + "</p>", "text/html");
|
response.set_content("<p>there was an error :( it is: " + std::string(e.what()) + "</p>", "text/html");
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ std::string Post::genHtml() const {
|
|||||||
ts = *localtime(&time);
|
ts = *localtime(&time);
|
||||||
strftime(buf, sizeof(buf), "%a %Y-%m-%d %H:%M:%S %Z", &ts);
|
strftime(buf, sizeof(buf), "%a %Y-%m-%d %H:%M:%S %Z", &ts);
|
||||||
|
|
||||||
ss << "<p class='postinfo'><i>" << user << "</i> posted this at " << buf << "</p>\n";
|
ss << "<p class='postinfo'><i><a href='/profile/" << user << "'>" << user << "</a></i> posted this at " << buf << "</p>\n";
|
||||||
|
|
||||||
ss << content;
|
ss << content;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user