summaryrefslogtreecommitdiff
path: root/src/types
diff options
context:
space:
mode:
authorTyler Murphy <tylermurphy534@gmail.com>2023-01-26 17:29:16 -0500
committerTyler Murphy <tylermurphy534@gmail.com>2023-01-26 17:29:16 -0500
commit88209d88236c3d865a9f5174a0dced31920859bf (patch)
tree89a9985927393005cf632950b585a6a227b1c679 /src/types
downloadxssbook-88209d88236c3d865a9f5174a0dced31920859bf.tar.gz
xssbook-88209d88236c3d865a9f5174a0dced31920859bf.tar.bz2
xssbook-88209d88236c3d865a9f5174a0dced31920859bf.zip
i did things
Diffstat (limited to 'src/types')
-rw-r--r--src/types/extract.rs65
-rw-r--r--src/types/mod.rs5
-rw-r--r--src/types/post.rs83
-rw-r--r--src/types/response.rs60
-rw-r--r--src/types/session.rs38
-rw-r--r--src/types/user.rs79
6 files changed, 330 insertions, 0 deletions
diff --git a/src/types/extract.rs b/src/types/extract.rs
new file mode 100644
index 0000000..6518ca1
--- /dev/null
+++ b/src/types/extract.rs
@@ -0,0 +1,65 @@
+use std::io::Read;
+
+use axum::{extract::{FromRequestParts, FromRequest}, async_trait, response::Response, http::{request::Parts, Request}, TypedHeader, headers::Cookie, body::HttpBody, BoxError};
+use bytes::Bytes;
+use serde::de::DeserializeOwned;
+
+use crate::types::{user::User, response::{ResponseCode, Result}, session::Session};
+
+pub struct AuthorizedUser(pub User);
+
+#[async_trait]
+impl<S> FromRequestParts<S> for AuthorizedUser where S: Send + Sync {
+ type Rejection = Response;
+
+ 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 {
+ return Err(ResponseCode::Forbidden.msg("No cookies provided"))
+ };
+
+ let Some(token) = cookies.get("auth") else {
+ return Err(ResponseCode::Forbidden.msg("No auth token provided"))
+ };
+
+ let Ok(session) = Session::from_token(&token) else {
+ return Err(ResponseCode::Unauthorized.msg("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"))
+ };
+
+ Ok(AuthorizedUser(user))
+ }
+}
+
+pub struct Json<T>(pub T);
+
+#[async_trait]
+impl<T, S, B> FromRequest<S, B> for Json<T> where
+ T: DeserializeOwned,
+ B: HttpBody + Send + 'static,
+ B::Data: Send,
+ B::Error: Into<BoxError>,
+ S: Send + Sync,
+{
+ type Rejection = Response;
+
+ async fn from_request(req: Request<B>, state: &S) -> Result<Self> {
+
+ let Ok(bytes) = Bytes::from_request(req, state).await else {
+ return Err(ResponseCode::InternalServerError.msg("Failed to read request body"));
+ };
+
+ let Ok(string) = String::from_utf8(bytes.bytes().flatten().collect()) else {
+ return Err(ResponseCode::BadRequest.msg("Invalid utf8 body"))
+ };
+
+ let Ok(value) = serde_json::from_str(&string) else {
+ return Err(ResponseCode::BadRequest.msg("Invalid request body"))
+ };
+
+ Ok(Json(value))
+ }
+} \ No newline at end of file
diff --git a/src/types/mod.rs b/src/types/mod.rs
new file mode 100644
index 0000000..089885e
--- /dev/null
+++ b/src/types/mod.rs
@@ -0,0 +1,5 @@
+pub mod user;
+pub mod post;
+pub mod session;
+pub mod extract;
+pub mod response; \ No newline at end of file
diff --git a/src/types/post.rs b/src/types/post.rs
new file mode 100644
index 0000000..94f0a9e
--- /dev/null
+++ b/src/types/post.rs
@@ -0,0 +1,83 @@
+use std::collections::HashSet;
+use serde::Serialize;
+
+use crate::database;
+use crate::types::response::{Result, ResponseCode};
+
+#[derive(Serialize)]
+pub struct Post {
+ pub post_id: u64,
+ pub user_id: u64,
+ pub content: String,
+ pub likes: HashSet<u64>,
+ pub comments: Vec<(u64, String)>,
+ pub date: u64
+}
+
+impl Post {
+
+ pub fn from_post_id(post_id: u64) -> Result<Self> {
+ let Ok(Some(post)) = database::posts::get_post(post_id) else {
+ return Err(ResponseCode::BadRequest.msg("Post does not exist"))
+ };
+
+ Ok(post)
+ }
+
+ // pub fn from_post_ids(post_ids: Vec<u64>) -> Vec<Self> {
+ // post_ids.iter().map(|id| {
+ // let Ok(post) = Post::from_post_id(*id) else {
+ // return None;
+ // };
+ // Some(post)
+ // }).flatten().collect()
+ // }
+
+ pub fn from_post_page(page: u64) -> Result<Vec<Self>> {
+ let Ok(posts) = database::posts::get_post_page(page) else {
+ return Err(ResponseCode::BadRequest.msg("Failed to fetch posts"))
+ };
+ Ok(posts)
+ }
+
+ pub fn from_user_id(user_id: u64) -> Result<Vec<Self>> {
+ let Ok(posts) = database::posts::get_users_posts(user_id) else {
+ return Err(ResponseCode::BadRequest.msg("Failed to fetch posts"))
+ };
+ Ok(posts)
+ }
+
+ pub fn new(user_id: u64, content: String) -> Result<Self> {
+ let Ok(post) = database::posts::add_post(user_id, &content) else {
+ return Err(ResponseCode::InternalServerError.msg("Failed to create post"))
+ };
+
+ Ok(post)
+ }
+
+ pub fn comment(&mut self, user_id: u64, content: String) -> Result<()> {
+ 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"))
+ }
+
+ Ok(())
+ }
+
+ pub fn like(&mut self, user_id: u64, state: bool) -> Result<()> {
+
+ if state {
+ self.likes.insert(user_id);
+ } else {
+ self.likes.remove(&user_id);
+ }
+
+ if database::posts::update_post(self.post_id, &self.likes, &self.comments).is_err() {
+ return Err(ResponseCode::InternalServerError.msg("Failed to comment on post"))
+ }
+
+ Ok(())
+ }
+
+} \ No newline at end of file
diff --git a/src/types/response.rs b/src/types/response.rs
new file mode 100644
index 0000000..bea3406
--- /dev/null
+++ b/src/types/response.rs
@@ -0,0 +1,60 @@
+use axum::{response::{IntoResponse, Response}, http::{StatusCode, Request, HeaderValue}, body::Body, headers::HeaderName};
+use tower::ServiceExt;
+use tower_http::services::ServeFile;
+
+#[derive(Debug)]
+pub enum ResponseCode {
+ Success,
+ Created,
+ BadRequest,
+ Unauthorized,
+ Forbidden,
+ NotFound,
+ ImATeapot,
+ InternalServerError
+}
+
+impl ResponseCode {
+ pub fn code(self) -> StatusCode {
+ match self {
+ Self::Success => StatusCode::OK,
+ Self::Created => StatusCode::CREATED,
+ Self::BadRequest => StatusCode::BAD_REQUEST,
+ Self::Unauthorized => StatusCode::UNAUTHORIZED,
+ Self::Forbidden => StatusCode::FORBIDDEN,
+ Self::NotFound => StatusCode::NOT_FOUND,
+ Self::ImATeapot => StatusCode::IM_A_TEAPOT,
+ Self::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR
+ }
+ }
+
+ pub fn msg(self, msg: &str) -> Response {
+ (self.code(), msg.to_owned()).into_response()
+ }
+
+ pub fn json(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("application/json"),
+ );
+ res
+ }
+
+ pub async fn file(self, path: &str) -> Result<Response> {
+ if path.chars().position(|c| c == '.' ).is_none() {
+ return Err(ResponseCode::BadRequest.msg("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"));
+ };
+ if res.status() != StatusCode::OK {
+ return Err(ResponseCode::NotFound.msg("File not found"));
+ }
+ *res.status_mut() = self.code();
+ Ok(res.into_response())
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Response>; \ No newline at end of file
diff --git a/src/types/session.rs b/src/types/session.rs
new file mode 100644
index 0000000..8064fb1
--- /dev/null
+++ b/src/types/session.rs
@@ -0,0 +1,38 @@
+use rand::{distributions::Alphanumeric, Rng};
+use serde::Serialize;
+
+use crate::database;
+use crate::types::response::{Result, ResponseCode};
+
+#[derive(Serialize)]
+pub struct Session {
+ pub user_id: u64,
+ pub token: String
+}
+
+impl Session {
+
+ pub fn from_token(token: &str) -> Result<Self> {
+ let Ok(Some(session)) = database::sessions::get_session(token) else {
+ return Err(ResponseCode::BadRequest.msg("Invalid auth token"));
+ };
+
+ Ok(session)
+ }
+
+ pub fn new(user_id: u64) -> Result<Self> {
+ 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")),
+ 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"));
+ };
+ Ok(())
+ }
+
+} \ No newline at end of file
diff --git a/src/types/user.rs b/src/types/user.rs
new file mode 100644
index 0000000..1213a75
--- /dev/null
+++ b/src/types/user.rs
@@ -0,0 +1,79 @@
+use serde::{Serialize, Deserialize};
+
+use crate::database;
+use crate::types::response::{Result, ResponseCode};
+
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct User {
+ pub user_id: u64,
+ pub firstname: String,
+ pub lastname: String,
+ pub email: String,
+ pub password: String,
+ pub gender: String,
+ pub date: u64,
+ pub day: u8,
+ pub month: u8,
+ pub year: u32,
+}
+
+impl User {
+
+ 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 {
+ return Err(ResponseCode::BadRequest.msg("User does not exist"))
+ };
+
+ Ok(user)
+ }
+
+ pub fn from_user_ids(user_ids: Vec<u64>) -> Vec<Self> {
+ user_ids.iter().map(|user_id| {
+ let Ok(Some(user)) = database::users::get_user_by_id(*user_id, true) else {
+ return None;
+ };
+ Some(user)
+ }).flatten().collect()
+ }
+
+ pub fn from_user_page(page: u64) -> Result<Vec<Self>> {
+ let Ok(users) = database::users::get_user_page(page, true) else {
+ return Err(ResponseCode::BadRequest.msg("Failed to fetch users"))
+ };
+ Ok(users)
+ }
+
+ pub fn from_email(email: &str) -> Result<Self> {
+ let Ok(Some(user)) = database::users::get_user_by_email(email, false) else {
+ return Err(ResponseCode::BadRequest.msg("User does not exist"))
+ };
+
+ Ok(user)
+ }
+
+ pub fn from_password(password: &str) -> Result<Self> {
+ let Ok(Some(user)) = database::users::get_user_by_password(password, true) else {
+ return Err(ResponseCode::BadRequest.msg("User does not exist"))
+ };
+
+ Ok(user)
+ }
+
+ 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) {
+ return Err(ResponseCode::BadRequest.msg(&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)))
+ }
+
+ 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"))
+ };
+
+ Ok(user)
+ }
+
+} \ No newline at end of file