fix rerendering logout button, console page

This commit is contained in:
Tyler Murphy 2023-01-28 02:51:34 -05:00
parent 8536e41c64
commit c01b8b8c90
21 changed files with 373 additions and 95 deletions

7
Cargo.lock generated
View file

@ -381,6 +381,12 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.139" version = "0.2.139"
@ -1051,6 +1057,7 @@ version = "0.0.1"
dependencies = [ dependencies = [
"axum", "axum",
"bytes", "bytes",
"lazy_static",
"rand", "rand",
"rusqlite", "rusqlite",
"serde", "serde",

View file

@ -11,7 +11,8 @@ tower-cookies = "0.8.0"
tower = "0.4.13" tower = "0.4.13"
bytes = "1.3.0" bytes = "1.3.0"
serde = { version = "1.0.152", features = ["derive"] } serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0" serde_json = { version = "1.0", features = ["std"] }
rusqlite = { version = "0.28.0", features = ["bundled"] } rusqlite = { version = "0.28.0", features = ["bundled"] }
rand = "0.8.5" rand = "0.8.5"
time = "0.3.17" time = "0.3.17"
lazy_static = "1.4.0"

View file

@ -2,8 +2,6 @@ body {
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: #181818; background-color: #181818;
display: flex;
flex-direction: column-reverse;
} }
@font-face { @font-face {
@ -30,20 +28,20 @@ span {
margin-right: 10px; margin-right: 10px;
} }
.body span {
margin-right: 0;
}
.json span { .json span {
display: inline; display: inline;
margin: 0; margin: 0;
} }
.key {
color: white;
}
.value { .value {
color: white; color: white;
} }
.boolean { .bool {
color: aqua; color: aqua;
} }
@ -58,3 +56,7 @@ span {
.string { .string {
color: #4ae04a color: #4ae04a
} }
.key .string {
color: white;
}

View file

@ -62,6 +62,7 @@ body {
height: 3em; height: 3em;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
} }
.profilebuttons button { .profilebuttons button {
@ -74,6 +75,7 @@ body {
justify-content: center; justify-content: center;
color: #606770; color: #606770;
cursor: pointer; cursor: pointer;
flex: 0;
} }
.profilebuttons button:hover { .profilebuttons button:hover {
@ -119,3 +121,8 @@ body {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
} }
.logout {
flex: 1;
/* align-self: flex-end !important; */
}

View file

@ -30,6 +30,10 @@ const register = async (firstname, lastname, email, password, gender, day, month
return await request('/auth/register', {firstname, lastname, email, password, gender, day, month, year}) return await request('/auth/register', {firstname, lastname, email, password, gender, day, month, year})
} }
const logout = async () => {
return await request('/auth/logout', {})
}
const loadpostspage = async (page) => { const loadpostspage = async (page) => {
return await request('/posts/page', {page}) return await request('/posts/page', {page})
} }

View file

@ -1,12 +1,5 @@
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
function parseDate(date) {
return months[date.getUTCMonth()] + ' ' + date.getUTCDate() + ', ' + date.getUTCFullYear() + ' ' + date.toLocaleTimeString();
}
function parseComment(comment) { function parseComment(comment) {
const author = data.users[comment[0]] let author = data.users[comment[0]]
if (author === undefined) { if (author === undefined) {
author = {} author = {}
} }
@ -25,14 +18,14 @@ function parseComment(comment) {
} }
function parsePost(post) { function parsePost(post) {
const author = data.users[post.user_id] let author = data.users[post.user_id]
if (author === undefined) { if (author === undefined) {
author = {} author = {}
} }
const html = ` const html = `
<div class="post" postid=${post.post_id}> <div class="post" postid=${post.post_id}>
<div class="postheader"> <div class="postheader">
<a class="pfp"> <a class="pfp" href=/profile?id=${author.user_id}>
</a> </a>
<div class="postname"> <div class="postname">
@ -217,8 +210,8 @@ async function load() {
batch.push(post.user_id) batch.push(post.user_id)
} }
const users = (await loadusers(batch)).json const users = (await loadusers(batch)).json
for (const id in users) { for (const user of users) {
data.users[id] = users[id] data.users[user.user_id] = user
} }
render() render()
} }

View file

@ -20,7 +20,7 @@ async function onregister() {
const year = document.getElementById('year').value const year = document.getElementById('year').value
const gender = document.querySelector('input[name="gender"]:checked').value const gender = document.querySelector('input[name="gender"]:checked').value
const response = await register(first, last, email, pass, gender, parseInt(day), parseInt(month), parseInt(year)) const response = await register(first, last, email, pass, gender, parseInt(day), parseInt(month), parseInt(year))
if (response.status !== 200) { if (response.status !== 201) {
const error = document.getElementsByClassName('error')[1] const error = document.getElementsByClassName('error')[1]
error.innerHTML = response.msg error.innerHTML = response.msg
} else { } else {

View file

@ -20,3 +20,24 @@ function remove(id) {
old.remove() old.remove()
} }
} }
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
function parseMonth(month) {
if (month > -1 && month < 26) {
return months[month]
} else {
let first = letters[month%26].toUpperCase()
let middle = letters[month*13%26]
let last = letters[month*50%26]
return first + middle + last
}
}
function parseDate(date) {
return parseMonth(date.getUTCMonth()) + ' ' + date.getUTCDate() + ', ' + date.getUTCFullYear() + ' ' + date.toLocaleTimeString();
}

View file

@ -1,10 +1,3 @@
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
function parseDate(date) {
return months[date.getUTCMonth()] + ' ' + date.getUTCDate() + ', ' + date.getUTCFullYear() + ' ' + date.toLocaleTimeString();
}
function parseUser(user) { function parseUser(user) {
const html = ` const html = `
<a class="person" href="/profile?id=${user.id}"> <a class="person" href="/profile?id=${user.id}">
@ -15,7 +8,7 @@ function parseUser(user) {
<span class="bold ltext">${user.firstname + ' ' + user.lastname}</span> <span class="bold ltext">${user.firstname + ' ' + user.lastname}</span>
<span class="gtext">Joined ${parseDate(new Date(user.date))}</span> <span class="gtext">Joined ${parseDate(new Date(user.date))}</span>
<span class="gtext">Gender: ${user.gender}</span> <span class="gtext">Gender: ${user.gender}</span>
<span class="gtext">Birthday: ${months[user.month] + ' ' + user.day + ', ' + user.year}</span> <span class="gtext">Birthday: ${parseMonth(user.month) + ' ' + user.day + ', ' + user.year}</span>
<span class="gtext" style="margin-bottom: -100px;">User ID: ${user.user_id}</span> <span class="gtext" style="margin-bottom: -100px;">User ID: ${user.user_id}</span>
</div> </div>
</a> </a>

View file

@ -23,6 +23,8 @@ function render() {
<button class="${posts ? '' : 'selected'}" onclick="posts = false; render()"> <button class="${posts ? '' : 'selected'}" onclick="posts = false; render()">
About About
</button> </button>
<div style="flex: 20"></div>
${ isself ? `<button class="logout" onclick="logout_button()">Logout</button>` : ''}
</div> </div>
</div> </div>
` `
@ -44,7 +46,7 @@ function render() {
<span class="gtext bold">Name: ${data.user.firstname + ' ' + data.user.lastname}</span> <span class="gtext bold">Name: ${data.user.firstname + ' ' + data.user.lastname}</span>
<span class="gtext bold">Email: ${data.user.email}</span> <span class="gtext bold">Email: ${data.user.email}</span>
<span class="gtext bold">Gender: ${data.user.gender}</span> <span class="gtext bold">Gender: ${data.user.gender}</span>
<span class="gtext bold">Birthday: ${months[data.user.month] + ' ' + data.user.day + ', ' + data.user.year}</span> <span class="gtext bold">Birthday: ${parseMonth(data.user.month) + ' ' + data.user.day + ', ' + data.user.year}</span>
<span class="gtext bold">User ID: ${data.user.user_id}</span> <span class="gtext bold">User ID: ${data.user.user_id}</span>
</div> </div>
</div> </div>
@ -53,7 +55,14 @@ function render() {
add(about, 'about') add(about, 'about')
} }
async function logout_button() {
const response = await logout()
if (response.status != 200) return;
location.href = '/login'
}
var posts = true var posts = true
var isself = false
async function load() { async function load() {
header(false, false) header(false, false)
@ -63,7 +72,18 @@ async function load() {
params[key] = value params[key] = value
} }
const id = params.id !== undefined && !isNaN(params.id) ? parseInt(params.id) : (await loadself()).json.user_id let self = (await loadself()).json;
let id;
if (params.id !== undefined && !isNaN(params.id)) {
id = parseInt(params.id);
} else {
id = self.user_id
}
isself = id === self.user_id
const posts = (await loadusersposts(id)).json const posts = (await loadusersposts(id)).json
data.posts.push(... posts) data.posts.push(... posts)
const batch = [id] const batch = [id]

View file

@ -3,7 +3,7 @@ use serde::Deserialize;
use time::{OffsetDateTime, Duration}; use time::{OffsetDateTime, Duration};
use tower_cookies::{Cookies, Cookie}; use tower_cookies::{Cookies, Cookie};
use crate::types::{user::User, response::ResponseCode, session::Session, extract::{Json, AuthorizedUser, Check, CheckResult}}; use crate::types::{user::User, response::ResponseCode, session::Session, extract::{Json, AuthorizedUser, Check, CheckResult, Log}};
#[derive(Deserialize)] #[derive(Deserialize)]
struct RegistrationRequet { struct RegistrationRequet {
@ -55,7 +55,7 @@ async fn register(cookies: Cookies, Json(body): Json<RegistrationRequet>) -> Res
cookies.add(cookie); cookies.add(cookie);
ResponseCode::Created.msg("Successfully created new user") ResponseCode::Created.text("Successfully created new user")
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -73,11 +73,11 @@ impl Check for LoginRequest {
async fn login(cookies: Cookies, Json(body): Json<LoginRequest>) -> Response { async fn login(cookies: Cookies, Json(body): Json<LoginRequest>) -> Response {
let Ok(user) = User::from_email(&body.email) else { let Ok(user) = User::from_email(&body.email) else {
return ResponseCode::BadRequest.msg("Email is not registered") return ResponseCode::BadRequest.text("Email is not registered")
}; };
if user.password != body.password { if user.password != body.password {
return ResponseCode::BadRequest.msg("Password is not correct") return ResponseCode::BadRequest.text("Password is not correct")
} }
let session = match Session::new(user.user_id) { let session = match Session::new(user.user_id) {
@ -96,10 +96,10 @@ async fn login(cookies: Cookies, Json(body): Json<LoginRequest>) -> Response {
cookies.add(cookie); cookies.add(cookie);
ResponseCode::Success.msg("Successfully logged in") ResponseCode::Success.text("Successfully logged in")
} }
async fn logout(cookies: Cookies, AuthorizedUser(user): AuthorizedUser) -> Response { async fn logout(cookies: Cookies, AuthorizedUser(user): AuthorizedUser, _: Log) -> Response {
cookies.remove(Cookie::new("auth", "")); cookies.remove(Cookie::new("auth", ""));
@ -107,7 +107,7 @@ async fn logout(cookies: Cookies, AuthorizedUser(user): AuthorizedUser) -> Respo
return err return err
} }
ResponseCode::Success.msg("Successfully logged out") ResponseCode::Success.text("Successfully logged out")
} }
pub fn router() -> Router { pub fn router() -> Router {

View file

@ -1,6 +1,6 @@
use axum::{Router, response::{Response, Redirect, IntoResponse}, routing::get}; use axum::{Router, response::{Response, Redirect, IntoResponse}, routing::get};
use crate::types::{extract::AuthorizedUser, response::ResponseCode}; use crate::{types::{extract::AuthorizedUser, response::ResponseCode}, console};
async fn root(user: Option<AuthorizedUser>) -> Response { async fn root(user: Option<AuthorizedUser>) -> Response {
if user.is_some() { if user.is_some() {
@ -42,8 +42,12 @@ async fn profile(user: Option<AuthorizedUser>) -> Response {
} }
} }
async fn console() -> Response {
console::generate().await
}
async fn wordpress() -> Response { async fn wordpress() -> Response {
ResponseCode::ImATeapot.msg("Hello i am a teapot owo") ResponseCode::ImATeapot.text("Hello i am a teapot owo")
} }
pub fn router() -> Router { pub fn router() -> Router {
@ -53,5 +57,6 @@ pub fn router() -> Router {
.route("/home", get(home)) .route("/home", get(home))
.route("/people", get(people)) .route("/people", get(people))
.route("/profile", get(profile)) .route("/profile", get(profile))
.route("/console", get(console))
.route("/wp-admin", get(wordpress)) .route("/wp-admin", get(wordpress))
} }

View file

@ -19,11 +19,11 @@ impl Check for PostCreateRequest {
async fn create(AuthorizedUser(user): AuthorizedUser, Json(body): Json<PostCreateRequest>) -> Response { async fn create(AuthorizedUser(user): AuthorizedUser, Json(body): Json<PostCreateRequest>) -> Response {
let Ok(post) = Post::new(user.user_id, body.content) else { let Ok(post) = Post::new(user.user_id, body.content) else {
return ResponseCode::InternalServerError.msg("Failed to create post") return ResponseCode::InternalServerError.text("Failed to create post")
}; };
let Ok(json) = serde_json::to_string(&post) else { let Ok(json) = serde_json::to_string(&post) else {
return ResponseCode::InternalServerError.msg("Failed to create post") return ResponseCode::InternalServerError.text("Failed to create post")
}; };
ResponseCode::Created.json(&json) ResponseCode::Created.json(&json)
@ -43,11 +43,11 @@ impl Check for PostPageRequest {
async fn page(AuthorizedUser(_user): AuthorizedUser, Json(body): Json<PostPageRequest>) -> Response { async fn page(AuthorizedUser(_user): AuthorizedUser, Json(body): Json<PostPageRequest>) -> Response {
let Ok(posts) = Post::from_post_page(body.page) else { let Ok(posts) = Post::from_post_page(body.page) else {
return ResponseCode::InternalServerError.msg("Failed to fetch posts") return ResponseCode::InternalServerError.text("Failed to fetch posts")
}; };
let Ok(json) = serde_json::to_string(&posts) else { let Ok(json) = serde_json::to_string(&posts) else {
return ResponseCode::InternalServerError.msg("Failed to fetch posts") return ResponseCode::InternalServerError.text("Failed to fetch posts")
}; };
ResponseCode::Success.json(&json) ResponseCode::Success.json(&json)
@ -67,11 +67,11 @@ impl Check for UsersPostsRequest {
async fn user(AuthorizedUser(_user): AuthorizedUser, Json(body): Json<UsersPostsRequest>) -> Response { async fn user(AuthorizedUser(_user): AuthorizedUser, Json(body): Json<UsersPostsRequest>) -> Response {
let Ok(posts) = Post::from_user_id(body.user_id) else { let Ok(posts) = Post::from_user_id(body.user_id) else {
return ResponseCode::InternalServerError.msg("Failed to fetch posts") return ResponseCode::InternalServerError.text("Failed to fetch posts")
}; };
let Ok(json) = serde_json::to_string(&posts) else { let Ok(json) = serde_json::to_string(&posts) else {
return ResponseCode::InternalServerError.msg("Failed to fetch posts") return ResponseCode::InternalServerError.text("Failed to fetch posts")
}; };
ResponseCode::Success.json(&json) ResponseCode::Success.json(&json)
@ -93,14 +93,14 @@ impl Check for PostCommentRequest {
async fn comment(AuthorizedUser(user): AuthorizedUser, Json(body): Json<PostCommentRequest>) -> Response { async fn comment(AuthorizedUser(user): AuthorizedUser, Json(body): Json<PostCommentRequest>) -> Response {
let Ok(mut post) = Post::from_post_id(body.post_id) else { let Ok(mut post) = Post::from_post_id(body.post_id) else {
return ResponseCode::InternalServerError.msg("Failed to fetch posts") return ResponseCode::InternalServerError.text("Failed to fetch posts")
}; };
if let Err(err) = post.comment(user.user_id, body.content) { if let Err(err) = post.comment(user.user_id, body.content) {
return err; return err;
} }
ResponseCode::Success.msg("Successfully commented on post") ResponseCode::Success.text("Successfully commented on post")
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -118,14 +118,14 @@ impl Check for PostLikeRequest {
async fn like(AuthorizedUser(user): AuthorizedUser, Json(body): Json<PostLikeRequest>) -> Response { async fn like(AuthorizedUser(user): AuthorizedUser, Json(body): Json<PostLikeRequest>) -> Response {
let Ok(mut post) = Post::from_post_id(body.post_id) else { let Ok(mut post) = Post::from_post_id(body.post_id) else {
return ResponseCode::InternalServerError.msg("Failed to fetch posts") return ResponseCode::InternalServerError.text("Failed to fetch posts")
}; };
if let Err(err) = post.like(user.user_id, body.state) { if let Err(err) = post.like(user.user_id, body.state) {
return err; return err;
} }
ResponseCode::Success.msg("Successfully changed like status on post") ResponseCode::Success.text("Successfully changed like status on post")
} }
pub fn router() -> Router { pub fn router() -> Router {

View file

@ -17,7 +17,7 @@ async fn load_batch(AuthorizedUser(_user): AuthorizedUser, Json(body): Json<User
let users = User::from_user_ids(body.ids); let users = User::from_user_ids(body.ids);
let Ok(json) = serde_json::to_string(&users) else { let Ok(json) = serde_json::to_string(&users) else {
return ResponseCode::InternalServerError.msg("Failed to fetch users") return ResponseCode::InternalServerError.text("Failed to fetch users")
}; };
ResponseCode::Success.json(&json) ResponseCode::Success.json(&json)
@ -37,11 +37,11 @@ impl Check for UserPageReqiest {
async fn load_page(AuthorizedUser(_user): AuthorizedUser, Json(body): Json<UserPageReqiest>) -> Response { async fn load_page(AuthorizedUser(_user): AuthorizedUser, Json(body): Json<UserPageReqiest>) -> Response {
let Ok(users) = User::from_user_page(body.page) else { let Ok(users) = User::from_user_page(body.page) else {
return ResponseCode::InternalServerError.msg("Failed to fetch users") return ResponseCode::InternalServerError.text("Failed to fetch users")
}; };
let Ok(json) = serde_json::to_string(&users) else { let Ok(json) = serde_json::to_string(&users) else {
return ResponseCode::InternalServerError.msg("Failed to fetch users") return ResponseCode::InternalServerError.text("Failed to fetch users")
}; };
ResponseCode::Success.json(&json) ResponseCode::Success.json(&json)
@ -50,7 +50,7 @@ async fn load_page(AuthorizedUser(_user): AuthorizedUser, Json(body): Json<UserP
async fn load_self(AuthorizedUser(user): AuthorizedUser) -> Response { async fn load_self(AuthorizedUser(user): AuthorizedUser) -> Response {
let Ok(json) = serde_json::to_string(&user) else { let Ok(json) = serde_json::to_string(&user) else {
return ResponseCode::InternalServerError.msg("Failed to fetch user") return ResponseCode::InternalServerError.text("Failed to fetch user")
}; };
ResponseCode::Success.json(&json) ResponseCode::Success.json(&json)

View file

@ -1,13 +1,195 @@
use std::net::IpAddr; use std::{net::IpAddr, collections::VecDeque, io, };
use axum::http::{Method, Uri}; use axum::{http::{Method, Uri}, response::Response};
use lazy_static::lazy_static;
use serde::Serialize;
use serde_json::{ser::Formatter, Value};
use tokio::sync::Mutex;
pub async fn log(ip: &IpAddr, method: &Method, uri: &Uri, path: Option<&str>, body: Option<&str>) { use crate::types::response::ResponseCode;
if path.is_some() && body.is_some() { struct LogMessage {
println!("{} {} {}{} {}", ip, method, path.unwrap(), uri, body.unwrap()); ip: IpAddr,
method: Method,
uri: Uri,
path: String,
body: String
}
impl ToString for LogMessage {
fn to_string(&self) -> String {
let mut ip = self.ip.to_string();
if ip.contains("::ffff:") {
ip = ip.as_str()[7..].to_string()
}
let color = match self.method {
Method::GET => "#3fe04f",
Method::POST => "#853fe0",
Method::PATCH => "#e0773f",
Method::PUT => "#e0cb3f",
Method::HEAD => "#3f75e0",
Method::DELETE => "#e04c3f",
Method::CONNECT => "#3fe0ad",
Method::TRACE => "#e03fc5",
Method::OPTIONS => "#423fe0",
_ => "white"
};
format!("<div><span class='ip'>{}</span> <span class='method' style='color: {};'>{}</span> <span class='path'>{}{}</span> <span class='body'>{}</span></div>", ip, color, self.method, self.path, self.uri, self.body)
}
}
lazy_static! {
static ref LOG: Mutex<VecDeque<LogMessage>> = Mutex::new(VecDeque::with_capacity(200));
}
pub async fn log(ip: IpAddr, method: Method, uri: Uri, path: Option<String>, body: Option<String>) {
if uri.to_string().starts_with("/console") { return; }
let path = path.unwrap_or_default();
let body = body.unwrap_or_default();
println!("{} {} {}{} {}", &ip, &method, &path, &uri, &body);
let message = LogMessage {
ip: ip,
method: method,
uri: uri,
path: path,
body: beautify(body)
};
let mut lock = LOG.lock().await;
if lock.len() > 200 {
lock.pop_back();
}
lock.push_front(message);
}
struct HtmlFormatter;
impl Formatter for HtmlFormatter {
fn write_null<W>(&mut self, writer: &mut W) -> io::Result<()> where W: ?Sized + io::Write {
writer.write_all(b"<span class='null'>null</span>")
}
fn write_bool<W>(&mut self, writer: &mut W, value: bool) -> io::Result<()> where W: ?Sized + io::Write {
let s = if value {
b"<span class='bool'>true</span>" as &[u8]
} else { } else {
println!("{} {} {}", ip, method, uri); b"<span class='bool'>false</span>" as &[u8]
};
writer.write_all(s)
} }
fn write_i8<W>(&mut self, writer: &mut W, value: i8) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn write_i16<W>(&mut self, writer: &mut W, value: i16) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn write_i32<W>(&mut self, writer: &mut W, value: i32) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn write_i64<W>(&mut self, writer: &mut W, value: i64) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn write_u8<W>(&mut self, writer: &mut W, value: u8) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn write_u16<W>(&mut self, writer: &mut W, value: u16) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn write_u32<W>(&mut self, writer: &mut W, value: u32) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn write_u64<W>(&mut self, writer: &mut W, value: u64) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn write_f32<W>(&mut self, writer: &mut W, value: f32) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn write_f64<W>(&mut self, writer: &mut W, value: f64) -> io::Result<()> where W: ?Sized + io::Write {
let buff = format!("<span class='number'>{}</span>", value);
writer.write_all(buff.as_bytes())
}
fn begin_string<W>(&mut self, writer: &mut W) -> io::Result<()> where W: ?Sized + io::Write {
writer.write_all(b"<span class='string'>\"")
}
fn end_string<W>(&mut self, writer: &mut W) -> io::Result<()> where W: ?Sized + io::Write {
writer.write_all(b"\"</span>")
}
fn begin_object_key<W>(&mut self, writer: &mut W, first: bool) -> io::Result<()> where W: ?Sized + io::Write {
if first {
writer.write_all(b"<span class='key'>")
} else {
writer.write_all(b"<span class='key'>,")
}
}
fn end_object_key<W>(&mut self, writer: &mut W) -> io::Result<()> where W: ?Sized + io::Write {
writer.write_all(b"</span>")
}
} }
fn beautify(body: String) -> String {
if body.len() < 1 {
return "".to_string()
}
let Ok(mut json) = serde_json::from_str::<Value>(&body) else {
return body
};
if json["password"].is_string() {
json["password"] = Value::String("********".to_owned());
}
let mut writer: Vec<u8> = Vec::with_capacity(128);
let mut serializer = serde_json::Serializer::with_formatter(&mut writer, HtmlFormatter);
if let Err(_) = json.serialize(&mut serializer) {
return body
}
String::from_utf8_lossy(&writer).to_string()
}
pub async fn generate() -> Response {
let lock = LOG.lock().await;
let mut html = r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="5">
<link rel="stylesheet" href="css/console.css">
<title>XSSBook - Console</title>
</head>
<body>
"#.to_string();
for message in lock.iter() {
html.push_str(&message.to_string());
}
html.push_str("</body></html>");
ResponseCode::Success.html(&html)
}

View file

@ -25,7 +25,7 @@ async fn log<B>(mut req: Request<B>, next: Next<B>) -> Response where
return next.run(req).await return next.run(req).await
}; };
console::log(&info.ip(), req.method(), req.uri(), None, None).await; console::log(info.ip().clone(), req.method().clone(), req.uri().clone(), None, None).await;
return next.run(req).await return next.run(req).await
} }

View file

@ -15,25 +15,60 @@ impl<S> FromRequestParts<S> for AuthorizedUser where S: Send + Sync {
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self> { async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self> {
let Ok(Some(cookies)) = Option::<TypedHeader<Cookie>>::from_request_parts(parts, state).await else { let Ok(Some(cookies)) = Option::<TypedHeader<Cookie>>::from_request_parts(parts, state).await else {
return Err(ResponseCode::Forbidden.msg("No cookies provided")) return Err(ResponseCode::Forbidden.text("No cookies provided"))
}; };
let Some(token) = cookies.get("auth") else { let Some(token) = cookies.get("auth") else {
return Err(ResponseCode::Forbidden.msg("No auth token provided")) return Err(ResponseCode::Forbidden.text("No auth token provided"))
}; };
let Ok(session) = Session::from_token(&token) else { let Ok(session) = Session::from_token(&token) else {
return Err(ResponseCode::Unauthorized.msg("Auth token invalid")) return Err(ResponseCode::Unauthorized.text("Auth token invalid"))
}; };
let Ok(user) = User::from_user_id(session.user_id, true) else { let Ok(user) = User::from_user_id(session.user_id, true) else {
return Err(ResponseCode::InternalServerError.msg("Valid token but no valid user")) return Err(ResponseCode::InternalServerError.text("Valid token but no valid user"))
}; };
Ok(AuthorizedUser(user)) Ok(AuthorizedUser(user))
} }
} }
pub struct Log;
#[async_trait]
impl<S, B> FromRequest<S, B> for Log where
B: HttpBody + Sync + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(mut req: Request<B>, state: &S) -> Result<Self> {
let Ok(ConnectInfo(info)) = req.extract_parts::<ConnectInfo<SocketAddr>>().await else {
return Ok(Log)
};
let method = req.method().clone();
let path = req.extensions().get::<RouterURI>().unwrap().0;
let uri = req.uri().clone();
let Ok(bytes) = Bytes::from_request(req, state).await else {
console::log(info.ip().clone(), method.clone(), uri.clone(), Some(path.to_string()), None).await;
return Ok(Log)
};
let Ok(body) = String::from_utf8(bytes.bytes().flatten().collect()) else {
console::log(info.ip().clone(), method.clone(), uri.clone(), Some(path.to_string()), None).await;
return Ok(Log)
};
console::log(info.ip().clone(), method.clone(), uri.clone(), Some(path.to_string()), Some(body.to_string())).await;
Ok(Log)
}
}
pub struct Json<T>(pub T); pub struct Json<T>(pub T);
#[async_trait] #[async_trait]
@ -49,28 +84,28 @@ impl<T, S, B> FromRequest<S, B> for Json<T> where
async fn from_request(mut req: Request<B>, state: &S) -> Result<Self> { async fn from_request(mut req: Request<B>, state: &S) -> Result<Self> {
let Ok(ConnectInfo(info)) = req.extract_parts::<ConnectInfo<SocketAddr>>().await else { let Ok(ConnectInfo(info)) = req.extract_parts::<ConnectInfo<SocketAddr>>().await else {
return Err(ResponseCode::InternalServerError.msg("Failed to read connection info")); return Err(ResponseCode::InternalServerError.text("Failed to read connection info"));
}; };
let method = req.method().clone(); let method = req.method().clone();
let path = req.extensions().get::<RouterURI>().unwrap().0; let path = req.extensions().get::<RouterURI>().unwrap().0;
let uri = req.uri().clone(); let uri = req.uri().clone();
let Ok(bytes) = Bytes::from_request(req, state).await else { let Ok(bytes) = Bytes::from_request(req, state).await else {
return Err(ResponseCode::InternalServerError.msg("Failed to read request body")); return Err(ResponseCode::InternalServerError.text("Failed to read request body"));
}; };
let Ok(body) = String::from_utf8(bytes.bytes().flatten().collect()) else { let Ok(body) = String::from_utf8(bytes.bytes().flatten().collect()) else {
return Err(ResponseCode::BadRequest.msg("Invalid utf8 body")) return Err(ResponseCode::BadRequest.text("Invalid utf8 body"))
}; };
console::log(&info.ip(), &method, &uri, Some(path), Some(&body)).await; console::log(info.ip().clone(), method.clone(), uri.clone(), Some(path.to_string()), Some(body.to_string())).await;
let Ok(value) = serde_json::from_str::<T>(&body) else { let Ok(value) = serde_json::from_str::<T>(&body) else {
return Err(ResponseCode::BadRequest.msg("Invalid request body")) return Err(ResponseCode::BadRequest.text("Invalid request body"))
}; };
if let Err(msg) = value.check() { if let Err(msg) = value.check() {
return Err(ResponseCode::BadRequest.msg(&msg)); return Err(ResponseCode::BadRequest.text(&msg));
} }
Ok(Json(value)) Ok(Json(value))

View file

@ -18,7 +18,7 @@ impl Post {
pub fn from_post_id(post_id: u64) -> Result<Self> { pub fn from_post_id(post_id: u64) -> Result<Self> {
let Ok(Some(post)) = database::posts::get_post(post_id) else { let Ok(Some(post)) = database::posts::get_post(post_id) else {
return Err(ResponseCode::BadRequest.msg("Post does not exist")) return Err(ResponseCode::BadRequest.text("Post does not exist"))
}; };
Ok(post) Ok(post)
@ -35,21 +35,21 @@ impl Post {
pub fn from_post_page(page: u64) -> Result<Vec<Self>> { pub fn from_post_page(page: u64) -> Result<Vec<Self>> {
let Ok(posts) = database::posts::get_post_page(page) else { let Ok(posts) = database::posts::get_post_page(page) else {
return Err(ResponseCode::BadRequest.msg("Failed to fetch posts")) return Err(ResponseCode::BadRequest.text("Failed to fetch posts"))
}; };
Ok(posts) Ok(posts)
} }
pub fn from_user_id(user_id: u64) -> Result<Vec<Self>> { pub fn from_user_id(user_id: u64) -> Result<Vec<Self>> {
let Ok(posts) = database::posts::get_users_posts(user_id) else { let Ok(posts) = database::posts::get_users_posts(user_id) else {
return Err(ResponseCode::BadRequest.msg("Failed to fetch posts")) return Err(ResponseCode::BadRequest.text("Failed to fetch posts"))
}; };
Ok(posts) Ok(posts)
} }
pub fn new(user_id: u64, content: String) -> Result<Self> { pub fn new(user_id: u64, content: String) -> Result<Self> {
let Ok(post) = database::posts::add_post(user_id, &content) else { let Ok(post) = database::posts::add_post(user_id, &content) else {
return Err(ResponseCode::InternalServerError.msg("Failed to create post")) return Err(ResponseCode::InternalServerError.text("Failed to create post"))
}; };
Ok(post) Ok(post)
@ -59,7 +59,7 @@ impl Post {
self.comments.push((user_id, content)); self.comments.push((user_id, content));
if database::posts::update_post(self.post_id, &self.likes, &self.comments).is_err() { if database::posts::update_post(self.post_id, &self.likes, &self.comments).is_err() {
return Err(ResponseCode::InternalServerError.msg("Failed to comment on post")) return Err(ResponseCode::InternalServerError.text("Failed to comment on post"))
} }
Ok(()) Ok(())
@ -74,7 +74,7 @@ impl Post {
} }
if database::posts::update_post(self.post_id, &self.likes, &self.comments).is_err() { if database::posts::update_post(self.post_id, &self.likes, &self.comments).is_err() {
return Err(ResponseCode::InternalServerError.msg("Failed to comment on post")) return Err(ResponseCode::InternalServerError.text("Failed to comment on post"))
} }
Ok(()) Ok(())

View file

@ -28,7 +28,7 @@ impl ResponseCode {
} }
} }
pub fn msg(self, msg: &str) -> Response { pub fn text(self, msg: &str) -> Response {
(self.code(), msg.to_owned()).into_response() (self.code(), msg.to_owned()).into_response()
} }
@ -40,17 +40,25 @@ impl ResponseCode {
res res
} }
pub fn html(self, json: &str) -> Response {
let mut res = (self.code(), json.to_owned()).into_response();
res.headers_mut().insert(
HeaderName::from_static("content-type"), HeaderValue::from_static("text/html"),
);
res
}
pub async fn file(self, path: &str) -> Result<Response> { pub async fn file(self, path: &str) -> Result<Response> {
if path.chars().position(|c| c == '.' ).is_none() { if path.chars().position(|c| c == '.' ).is_none() {
return Err(ResponseCode::BadRequest.msg("Folders cannot be served")); return Err(ResponseCode::BadRequest.text("Folders cannot be served"));
} }
let path = format!("public{}", path); let path = format!("public{}", path);
let svc = ServeFile::new(path); let svc = ServeFile::new(path);
let Ok(mut res) = svc.oneshot(Request::new(Body::empty())).await else { let Ok(mut res) = svc.oneshot(Request::new(Body::empty())).await else {
return Err(ResponseCode::InternalServerError.msg("Error wile fetching file")); return Err(ResponseCode::InternalServerError.text("Error wile fetching file"));
}; };
if res.status() != StatusCode::OK { if res.status() != StatusCode::OK {
return Err(ResponseCode::NotFound.msg("File not found")); return Err(ResponseCode::NotFound.text("File not found"));
} }
*res.status_mut() = self.code(); *res.status_mut() = self.code();
Ok(res.into_response()) Ok(res.into_response())

View file

@ -14,7 +14,7 @@ impl Session {
pub fn from_token(token: &str) -> Result<Self> { pub fn from_token(token: &str) -> Result<Self> {
let Ok(Some(session)) = database::sessions::get_session(token) else { let Ok(Some(session)) = database::sessions::get_session(token) else {
return Err(ResponseCode::BadRequest.msg("Invalid auth token")); return Err(ResponseCode::BadRequest.text("Invalid auth token"));
}; };
Ok(session) Ok(session)
@ -23,14 +23,14 @@ impl Session {
pub fn new(user_id: u64) -> Result<Self> { pub fn new(user_id: u64) -> Result<Self> {
let token: String = rand::thread_rng().sample_iter(&Alphanumeric).take(32).map(char::from).collect(); let token: String = rand::thread_rng().sample_iter(&Alphanumeric).take(32).map(char::from).collect();
match database::sessions::set_session(user_id, &token) { match database::sessions::set_session(user_id, &token) {
Err(_) => return Err(ResponseCode::BadRequest.msg("Failed to create session")), Err(_) => return Err(ResponseCode::BadRequest.text("Failed to create session")),
Ok(_) => return Ok(Session {user_id, token}) Ok(_) => return Ok(Session {user_id, token})
}; };
} }
pub fn delete(user_id: u64) -> Result<()> { pub fn delete(user_id: u64) -> Result<()> {
if let Err(_) = database::sessions::delete_session(user_id) { if let Err(_) = database::sessions::delete_session(user_id) {
return Err(ResponseCode::InternalServerError.msg("Failed to logout")); return Err(ResponseCode::InternalServerError.text("Failed to logout"));
}; };
Ok(()) Ok(())
} }

View file

@ -22,7 +22,7 @@ impl User {
pub fn from_user_id(user_id: u64, hide_password: bool) -> Result<Self> { pub fn from_user_id(user_id: u64, hide_password: bool) -> Result<Self> {
let Ok(Some(user)) = database::users::get_user_by_id(user_id, hide_password) else { let Ok(Some(user)) = database::users::get_user_by_id(user_id, hide_password) else {
return Err(ResponseCode::BadRequest.msg("User does not exist")) return Err(ResponseCode::BadRequest.text("User does not exist"))
}; };
Ok(user) Ok(user)
@ -39,14 +39,14 @@ impl User {
pub fn from_user_page(page: u64) -> Result<Vec<Self>> { pub fn from_user_page(page: u64) -> Result<Vec<Self>> {
let Ok(users) = database::users::get_user_page(page, true) else { let Ok(users) = database::users::get_user_page(page, true) else {
return Err(ResponseCode::BadRequest.msg("Failed to fetch users")) return Err(ResponseCode::BadRequest.text("Failed to fetch users"))
}; };
Ok(users) Ok(users)
} }
pub fn from_email(email: &str) -> Result<Self> { pub fn from_email(email: &str) -> Result<Self> {
let Ok(Some(user)) = database::users::get_user_by_email(email, false) else { let Ok(Some(user)) = database::users::get_user_by_email(email, false) else {
return Err(ResponseCode::BadRequest.msg("User does not exist")) return Err(ResponseCode::BadRequest.text("User does not exist"))
}; };
Ok(user) Ok(user)
@ -54,7 +54,7 @@ impl User {
pub fn from_password(password: &str) -> Result<Self> { pub fn from_password(password: &str) -> Result<Self> {
let Ok(Some(user)) = database::users::get_user_by_password(password, true) else { let Ok(Some(user)) = database::users::get_user_by_password(password, true) else {
return Err(ResponseCode::BadRequest.msg("User does not exist")) return Err(ResponseCode::BadRequest.text("User does not exist"))
}; };
Ok(user) Ok(user)
@ -62,15 +62,15 @@ impl User {
pub fn new(firstname: String, lastname: String, email: String, password: String, gender: String, day: u8, month: u8, year: u32) -> Result<Self> { pub fn new(firstname: String, lastname: String, email: String, password: String, gender: String, day: u8, month: u8, year: u32) -> Result<Self> {
if let Ok(_) = User::from_email(&email) { if let Ok(_) = User::from_email(&email) {
return Err(ResponseCode::BadRequest.msg(&format!("Email is already in use by {}", &email))) return Err(ResponseCode::BadRequest.text(&format!("Email is already in use by {}", &email)))
} }
if let Ok(user) = User::from_password(&password) { if let Ok(user) = User::from_password(&password) {
return Err(ResponseCode::BadRequest.msg(&format!("Password is already in use by {}", user.email))) return Err(ResponseCode::BadRequest.text(&format!("Password is already in use by {}", user.email)))
} }
let Ok(user) = database::users::add_user(&firstname, &lastname, &email, &password, &gender, day, month, year) else { let Ok(user) = database::users::add_user(&firstname, &lastname, &email, &password, &gender, day, month, year) else {
return Err(ResponseCode::InternalServerError.msg("Failed to create new uesr")) return Err(ResponseCode::InternalServerError.text("Failed to create new uesr"))
}; };
Ok(user) Ok(user)