diff --git a/Cargo.lock b/Cargo.lock index f275ca6..b490d6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -381,6 +381,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.139" @@ -1051,6 +1057,7 @@ version = "0.0.1" dependencies = [ "axum", "bytes", + "lazy_static", "rand", "rusqlite", "serde", diff --git a/Cargo.toml b/Cargo.toml index 1d3a937..e81c936 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,8 @@ tower-cookies = "0.8.0" tower = "0.4.13" bytes = "1.3.0" 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"] } rand = "0.8.5" -time = "0.3.17" \ No newline at end of file +time = "0.3.17" +lazy_static = "1.4.0" \ No newline at end of file diff --git a/public/css/console.css b/public/css/console.css index bc07969..e3fde6a 100644 --- a/public/css/console.css +++ b/public/css/console.css @@ -2,8 +2,6 @@ body { margin: 0; padding: 0; background-color: #181818; - display: flex; - flex-direction: column-reverse; } @font-face { @@ -30,20 +28,20 @@ span { margin-right: 10px; } +.body span { + margin-right: 0; +} + .json span { display: inline; margin: 0; } -.key { - color: white; -} - .value { color: white; } -.boolean { +.bool { color: aqua; } @@ -57,4 +55,8 @@ span { .string { color: #4ae04a +} + +.key .string { + color: white; } \ No newline at end of file diff --git a/public/css/profile.css b/public/css/profile.css index 4c5ae10..467c756 100644 --- a/public/css/profile.css +++ b/public/css/profile.css @@ -62,6 +62,7 @@ body { height: 3em; display: flex; align-items: center; + justify-content: space-between; } .profilebuttons button { @@ -74,6 +75,7 @@ body { justify-content: center; color: #606770; cursor: pointer; + flex: 0; } .profilebuttons button:hover { @@ -118,4 +120,9 @@ body { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; +} + +.logout { + flex: 1; + /* align-self: flex-end !important; */ } \ No newline at end of file diff --git a/public/js/api.js b/public/js/api.js index 07769f6..77adff7 100644 --- a/public/js/api.js +++ b/public/js/api.js @@ -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}) } +const logout = async () => { + return await request('/auth/logout', {}) +} + const loadpostspage = async (page) => { return await request('/posts/page', {page}) } diff --git a/public/js/home.js b/public/js/home.js index 23f0d01..688cbf5 100644 --- a/public/js/home.js +++ b/public/js/home.js @@ -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) { - const author = data.users[comment[0]] + let author = data.users[comment[0]] if (author === undefined) { author = {} } @@ -25,14 +18,14 @@ function parseComment(comment) { } function parsePost(post) { - const author = data.users[post.user_id] + let author = data.users[post.user_id] if (author === undefined) { author = {} } const html = `
- +
@@ -217,8 +210,8 @@ async function load() { batch.push(post.user_id) } const users = (await loadusers(batch)).json - for (const id in users) { - data.users[id] = users[id] + for (const user of users) { + data.users[user.user_id] = user } render() } diff --git a/public/js/login.js b/public/js/login.js index f65808b..0d9feb8 100644 --- a/public/js/login.js +++ b/public/js/login.js @@ -20,7 +20,7 @@ async function onregister() { const year = document.getElementById('year').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)) - if (response.status !== 200) { + if (response.status !== 201) { const error = document.getElementsByClassName('error')[1] error.innerHTML = response.msg } else { diff --git a/public/js/main.js b/public/js/main.js index 0003c0d..06736ee 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -19,4 +19,25 @@ function remove(id) { if (old !== null) { 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(); } \ No newline at end of file diff --git a/public/js/people.js b/public/js/people.js index ddd1875..e9f5db6 100644 --- a/public/js/people.js +++ b/public/js/people.js @@ -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) { const html = ` @@ -15,7 +8,7 @@ function parseUser(user) { ${user.firstname + ' ' + user.lastname} Joined ${parseDate(new Date(user.date))} Gender: ${user.gender} - Birthday: ${months[user.month] + ' ' + user.day + ', ' + user.year} + Birthday: ${parseMonth(user.month) + ' ' + user.day + ', ' + user.year} User ID: ${user.user_id}
diff --git a/public/js/profile.js b/public/js/profile.js index 79dbe2f..1f72b17 100644 --- a/public/js/profile.js +++ b/public/js/profile.js @@ -23,6 +23,8 @@ function render() { +
+ ${ isself ? `` : ''}
` @@ -44,7 +46,7 @@ function render() { Name: ${data.user.firstname + ' ' + data.user.lastname} Email: ${data.user.email} Gender: ${data.user.gender} - Birthday: ${months[data.user.month] + ' ' + data.user.day + ', ' + data.user.year} + Birthday: ${parseMonth(data.user.month) + ' ' + data.user.day + ', ' + data.user.year} User ID: ${data.user.user_id} @@ -53,7 +55,14 @@ function render() { add(about, 'about') } +async function logout_button() { + const response = await logout() + if (response.status != 200) return; + location.href = '/login' +} + var posts = true +var isself = false async function load() { header(false, false) @@ -63,7 +72,18 @@ async function load() { 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 data.posts.push(... posts) const batch = [id] diff --git a/src/api/auth.rs b/src/api/auth.rs index 54c4e06..253e982 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -3,7 +3,7 @@ use serde::Deserialize; use time::{OffsetDateTime, Duration}; 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)] struct RegistrationRequet { @@ -55,7 +55,7 @@ async fn register(cookies: Cookies, Json(body): Json) -> Res cookies.add(cookie); - ResponseCode::Created.msg("Successfully created new user") + ResponseCode::Created.text("Successfully created new user") } #[derive(Deserialize)] @@ -73,11 +73,11 @@ impl Check for LoginRequest { async fn login(cookies: Cookies, Json(body): Json) -> Response { 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 { - return ResponseCode::BadRequest.msg("Password is not correct") + return ResponseCode::BadRequest.text("Password is not correct") } let session = match Session::new(user.user_id) { @@ -96,10 +96,10 @@ async fn login(cookies: Cookies, Json(body): Json) -> Response { 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", "")); @@ -107,7 +107,7 @@ async fn logout(cookies: Cookies, AuthorizedUser(user): AuthorizedUser) -> Respo return err } - ResponseCode::Success.msg("Successfully logged out") + ResponseCode::Success.text("Successfully logged out") } pub fn router() -> Router { diff --git a/src/api/pages.rs b/src/api/pages.rs index 4701795..b2bef82 100644 --- a/src/api/pages.rs +++ b/src/api/pages.rs @@ -1,6 +1,6 @@ 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) -> Response { if user.is_some() { @@ -42,8 +42,12 @@ async fn profile(user: Option) -> Response { } } +async fn console() -> Response { + console::generate().await +} + 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 { @@ -53,5 +57,6 @@ pub fn router() -> Router { .route("/home", get(home)) .route("/people", get(people)) .route("/profile", get(profile)) + .route("/console", get(console)) .route("/wp-admin", get(wordpress)) } \ No newline at end of file diff --git a/src/api/posts.rs b/src/api/posts.rs index 6830c1a..fda1fb1 100644 --- a/src/api/posts.rs +++ b/src/api/posts.rs @@ -19,11 +19,11 @@ impl Check for PostCreateRequest { async fn create(AuthorizedUser(user): AuthorizedUser, Json(body): Json) -> Response { 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 { - return ResponseCode::InternalServerError.msg("Failed to create post") + return ResponseCode::InternalServerError.text("Failed to create post") }; ResponseCode::Created.json(&json) @@ -43,11 +43,11 @@ impl Check for PostPageRequest { async fn page(AuthorizedUser(_user): AuthorizedUser, Json(body): Json) -> Response { 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 { - return ResponseCode::InternalServerError.msg("Failed to fetch posts") + return ResponseCode::InternalServerError.text("Failed to fetch posts") }; ResponseCode::Success.json(&json) @@ -67,11 +67,11 @@ impl Check for UsersPostsRequest { async fn user(AuthorizedUser(_user): AuthorizedUser, Json(body): Json) -> Response { 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 { - return ResponseCode::InternalServerError.msg("Failed to fetch posts") + return ResponseCode::InternalServerError.text("Failed to fetch posts") }; ResponseCode::Success.json(&json) @@ -93,14 +93,14 @@ impl Check for PostCommentRequest { async fn comment(AuthorizedUser(user): AuthorizedUser, Json(body): Json) -> Response { 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) { return err; } - ResponseCode::Success.msg("Successfully commented on post") + ResponseCode::Success.text("Successfully commented on post") } #[derive(Deserialize)] @@ -118,14 +118,14 @@ impl Check for PostLikeRequest { async fn like(AuthorizedUser(user): AuthorizedUser, Json(body): Json) -> Response { 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) { return err; } - ResponseCode::Success.msg("Successfully changed like status on post") + ResponseCode::Success.text("Successfully changed like status on post") } pub fn router() -> Router { diff --git a/src/api/users.rs b/src/api/users.rs index 45ed195..7bea200 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -17,7 +17,7 @@ async fn load_batch(AuthorizedUser(_user): AuthorizedUser, Json(body): Json) -> Response { 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 { - return ResponseCode::InternalServerError.msg("Failed to fetch users") + return ResponseCode::InternalServerError.text("Failed to fetch users") }; ResponseCode::Success.json(&json) @@ -50,7 +50,7 @@ async fn load_page(AuthorizedUser(_user): AuthorizedUser, Json(body): Json Response { 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) diff --git a/src/console.rs b/src/console.rs index 5a3c60b..660fb25 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,13 +1,195 @@ -use std::net::IpAddr; -use axum::http::{Method, Uri}; +use std::{net::IpAddr, collections::VecDeque, io, }; +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>) { - - if path.is_some() && body.is_some() { - println!("{} {} {}{} {}", ip, method, path.unwrap(), uri, body.unwrap()); - } else { - println!("{} {} {}", ip, method, uri); +use crate::types::response::ResponseCode; + +struct LogMessage { + 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!("
{} {} {}{} {}
", ip, color, self.method, self.path, self.uri, self.body) } - +} +lazy_static! { + static ref LOG: Mutex> = Mutex::new(VecDeque::with_capacity(200)); +} + +pub async fn log(ip: IpAddr, method: Method, uri: Uri, path: Option, body: Option) { + + 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(&mut self, writer: &mut W) -> io::Result<()> where W: ?Sized + io::Write { + writer.write_all(b"null") + } + + fn write_bool(&mut self, writer: &mut W, value: bool) -> io::Result<()> where W: ?Sized + io::Write { + let s = if value { + b"true" as &[u8] + } else { + b"false" as &[u8] + }; + writer.write_all(s) + } + + fn write_i8(&mut self, writer: &mut W, value: i8) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn write_i16(&mut self, writer: &mut W, value: i16) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn write_i32(&mut self, writer: &mut W, value: i32) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn write_i64(&mut self, writer: &mut W, value: i64) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn write_u8(&mut self, writer: &mut W, value: u8) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn write_u16(&mut self, writer: &mut W, value: u16) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn write_u32(&mut self, writer: &mut W, value: u32) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn write_u64(&mut self, writer: &mut W, value: u64) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn write_f32(&mut self, writer: &mut W, value: f32) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn write_f64(&mut self, writer: &mut W, value: f64) -> io::Result<()> where W: ?Sized + io::Write { + let buff = format!("{}", value); + writer.write_all(buff.as_bytes()) + } + + fn begin_string(&mut self, writer: &mut W) -> io::Result<()> where W: ?Sized + io::Write { + writer.write_all(b"\"") + } + + fn end_string(&mut self, writer: &mut W) -> io::Result<()> where W: ?Sized + io::Write { + writer.write_all(b"\"") + } + + fn begin_object_key(&mut self, writer: &mut W, first: bool) -> io::Result<()> where W: ?Sized + io::Write { + if first { + writer.write_all(b"") + } else { + writer.write_all(b",") + } + } + + fn end_object_key(&mut self, writer: &mut W) -> io::Result<()> where W: ?Sized + io::Write { + writer.write_all(b"") + } + +} + +fn beautify(body: String) -> String { + if body.len() < 1 { + return "".to_string() + } + let Ok(mut json) = serde_json::from_str::(&body) else { + return body + }; + if json["password"].is_string() { + json["password"] = Value::String("********".to_owned()); + } + let mut writer: Vec = 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#" + + + + + + XSSBook - Console + + + "#.to_string(); + + for message in lock.iter() { + html.push_str(&message.to_string()); + } + + html.push_str(""); + + ResponseCode::Success.html(&html) } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 64abf68..54c73c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ async fn log(mut req: Request, next: Next) -> Response where 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 } diff --git a/src/types/extract.rs b/src/types/extract.rs index 1379828..399fe67 100644 --- a/src/types/extract.rs +++ b/src/types/extract.rs @@ -15,25 +15,60 @@ impl FromRequestParts for AuthorizedUser where S: Send + Sync { async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { let Ok(Some(cookies)) = Option::>::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 { - 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 { - 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 { - 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)) } } +pub struct Log; +#[async_trait] +impl FromRequest for Log where + B: HttpBody + Sync + Send + 'static, + B::Data: Send, + B::Error: Into, + S: Send + Sync, +{ + type Rejection = Response; + + async fn from_request(mut req: Request, state: &S) -> Result { + + let Ok(ConnectInfo(info)) = req.extract_parts::>().await else { + return Ok(Log) + }; + let method = req.method().clone(); + let path = req.extensions().get::().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(pub T); #[async_trait] @@ -49,28 +84,28 @@ impl FromRequest for Json where async fn from_request(mut req: Request, state: &S) -> Result { let Ok(ConnectInfo(info)) = req.extract_parts::>().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 path = req.extensions().get::().unwrap().0; let uri = req.uri().clone(); 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 { - 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::(&body) else { - return Err(ResponseCode::BadRequest.msg("Invalid request body")) + return Err(ResponseCode::BadRequest.text("Invalid request body")) }; if let Err(msg) = value.check() { - return Err(ResponseCode::BadRequest.msg(&msg)); + return Err(ResponseCode::BadRequest.text(&msg)); } Ok(Json(value)) diff --git a/src/types/post.rs b/src/types/post.rs index 94f0a9e..7805a4e 100644 --- a/src/types/post.rs +++ b/src/types/post.rs @@ -18,7 +18,7 @@ impl Post { pub fn from_post_id(post_id: u64) -> Result { 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) @@ -35,21 +35,21 @@ impl Post { pub fn from_post_page(page: u64) -> Result> { 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) } pub fn from_user_id(user_id: u64) -> Result> { 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) } pub fn new(user_id: u64, content: String) -> Result { 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) @@ -59,7 +59,7 @@ impl Post { self.comments.push((user_id, content)); 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(()) @@ -74,7 +74,7 @@ impl Post { } 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(()) diff --git a/src/types/response.rs b/src/types/response.rs index bea3406..72c1334 100644 --- a/src/types/response.rs +++ b/src/types/response.rs @@ -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() } @@ -40,17 +40,25 @@ impl ResponseCode { 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 { 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 svc = ServeFile::new(path); 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 { - return Err(ResponseCode::NotFound.msg("File not found")); + return Err(ResponseCode::NotFound.text("File not found")); } *res.status_mut() = self.code(); Ok(res.into_response()) diff --git a/src/types/session.rs b/src/types/session.rs index 8064fb1..9b949be 100644 --- a/src/types/session.rs +++ b/src/types/session.rs @@ -14,7 +14,7 @@ impl Session { pub fn from_token(token: &str) -> Result { 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) @@ -23,14 +23,14 @@ impl Session { pub fn new(user_id: u64) -> Result { let token: String = rand::thread_rng().sample_iter(&Alphanumeric).take(32).map(char::from).collect(); 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}) }; } pub fn delete(user_id: u64) -> Result<()> { 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(()) } diff --git a/src/types/user.rs b/src/types/user.rs index 1213a75..56ef467 100644 --- a/src/types/user.rs +++ b/src/types/user.rs @@ -22,7 +22,7 @@ impl User { pub fn from_user_id(user_id: u64, hide_password: bool) -> Result { 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) @@ -39,14 +39,14 @@ impl User { pub fn from_user_page(page: u64) -> Result> { 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) } pub fn from_email(email: &str) -> Result { 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) @@ -54,7 +54,7 @@ impl User { pub fn from_password(password: &str) -> Result { 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) @@ -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 { 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) { - 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 { - return Err(ResponseCode::InternalServerError.msg("Failed to create new uesr")) + return Err(ResponseCode::InternalServerError.text("Failed to create new uesr")) }; Ok(user)