Compare commits

..

No commits in common. "a9cbf77eaa257d691b3e617944ae8cf0d56d9691" and "518dd26b0f5bb8851e7b96b5693a65ceb795ae72" have entirely different histories.

5 changed files with 64 additions and 130 deletions

14
imagesplan.txt Normal file
View File

@ -0,0 +1,14 @@
GET /images/uuid.filetype DONE
-> returns the image with the correct filetype - uuid.png would return the image in png format, etc
GET /images/by-user/username DONE
-> gets all of the images made by {username}, if you are {username}, or if you have admin.
POST /images/create {image: "image data"} DONE
-> returns the uuid of the image, which it saves to the folder and database
DELETE /images/uuid DONE
-> if you're the owner of the image or an admin, deletes the image. returns basic success/faliure
images are stored in /images/uuid.png. Image::get_by_uuid(uuid) just gets the image from the folder, verifies that it is an image, and returns it. Image::get_by_username(username) gets all database images from the database and returns the uuids. the client is responsible for getting the images.

View File

@ -117,12 +117,6 @@ async fn migrate(rocket: Rocket<Build>) -> Rocket<Build> {
ADD COLUMN IF NOT EXISTS text_id TEXT NOT NULL UNIQUE", ADD COLUMN IF NOT EXISTS text_id TEXT NOT NULL UNIQUE",
)) ))
.await; .await;
let _ = conn
.execute(sqlx::query(
"ALTER TABLE posts
ADD COLUMN IF NOT EXISTS author STRING NOT NULL",
))
.await;
let _ = conn let _ = conn
.execute(sqlx::query( .execute(sqlx::query(
"ALTER TABLE posts "ALTER TABLE posts
@ -174,10 +168,7 @@ async fn main() {
routes::posts::create, routes::posts::create,
], ],
) )
.mount( .mount("/api", routes![routes::users::api_perms])
"/api",
routes![routes::users::api_perms, routes::posts::get_post],
)
.mount("/css", FileServer::from("/srv/web/css")) .mount("/css", FileServer::from("/srv/web/css"))
.register("/", catchers![default_catcher]) .register("/", catchers![default_catcher])
.launch() .launch()

View File

@ -181,35 +181,24 @@ pub async fn get_images_by_username(
mut db: Connection<Db>, mut db: Connection<Db>,
cookies: &CookieJar<'_>, cookies: &CookieJar<'_>,
username: String, username: String,
) -> status::Custom<Result<Json<Vec<String>>, String>> { ) -> Result<Json<Vec<String>>, String> {
match User::login_status(&mut db, cookies).await { let token = cookies.get_private("token");
LoginStatus::LoggedIn(user) => match user.admin || user.username == username { match token {
Some(t) => match User::get_by_token(&mut db, t).await {
Some(user) => match user.admin || user.username == username {
true => match Image::get_by_username(&mut db, &username).await { true => match Image::get_by_username(&mut db, &username).await {
Ok(images) => status::Custom( Ok(images) => Ok(Json::from(
Status::Ok,
Ok(Json::from(
images.into_iter().map(|i| i.uuid).collect::<Vec<String>>(), images.into_iter().map(|i| i.uuid).collect::<Vec<String>>(),
)), )),
),
Err(why) => { Err(why) => {
eprintln!("{why:?}"); eprintln!("{why:?}");
status::Custom( Err("Couldn't get that user's images".to_string())
Status::NotFound,
Err("Couldn't get that user's images".to_string()),
)
} }
}, },
false => status::Custom( false => Err("You don't have permission to do this".to_string()),
Status::Unauthorized,
Err("You don't have permission to do this".to_string()),
),
}, },
LoginStatus::InvalidToken => status::Custom( None => Err("Invalid login token".to_string()),
Status::Unauthorized, },
Err("Invalid login token.".to_string()), None => Err("Not logged in".to_string()),
),
LoginStatus::NotLoggedIn => {
status::Custom(Status::Unauthorized, Err("Not logged in.".to_string()))
}
} }
} }

View File

@ -1,24 +1,21 @@
/* /*
* POST /posts: uses json request body to get the post info and, y'know, create it * POST /posts: uses json request body to get the post info and, y'know, create it
* TODO GET /posts/<id>: figures out what type of id <id> is, gets post via that, returns it * GET /posts/<id>: figures out what type of id <id> is, gets post via that, returns it
* TODO UPDATE /posts/<id>: figures out what type of id <id> is, uses json request body to update * UPDATE /posts/<id>: figures out what type of id <id> is, uses json request body to update
* specific data about the post * specific data about the post
* TODO DELETE /posts/<id>: you can figure out what this one does * DELETE /posts/<id>: you can figure out what this one does
* *
* TODO GET /posts: gets all posts, maybe json request body for args? * GET /posts: gets all posts, maybe json request body for args?
*/ */
use crate::tables::posts::Post;
use crate::tables::{users::LoginStatus, users::User, Db}; use crate::tables::{users::LoginStatus, users::User, Db};
use rocket::http::CookieJar; use rocket::http::CookieJar;
use rocket::http::Status; use rocket::http::Status;
use rocket::post;
use rocket::response::status; use rocket::response::status;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use rocket::serde::Deserialize; use rocket::serde::Deserialize;
use rocket::{get, post};
use rocket_db_pools::Connection; use rocket_db_pools::Connection;
use uuid::Uuid;
use rocket::serde::Serialize;
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
@ -26,7 +23,6 @@ pub struct PostCreateInfo {
pub title: String, pub title: String,
pub text_id: String, pub text_id: String,
pub body: String, pub body: String,
pub auto_pubilsh: bool,
} }
#[post("/posts", data = "<info>")] #[post("/posts", data = "<info>")]
@ -37,41 +33,12 @@ pub async fn create(
) -> status::Custom<String> { ) -> status::Custom<String> {
match User::login_status(&mut db, cookies).await { match User::login_status(&mut db, cookies).await {
LoginStatus::LoggedIn(user) => { LoginStatus::LoggedIn(user) => {
match user.make_posts || user.admin {
true => {
// if post with same text id exists, fail // if post with same text id exists, fail
match Post::get_by_text_id(&mut db, &info.text_id).await { // make sure user has perms to do this first tho
Some(_) => status::Custom( // and uhhhhh
Status::Forbidden, // yeah thats it idk
"A post already exists with this text id.".to_string(), // TODO: implement all that
), status::Custom(Status::NotImplemented, "Not implemented yet".to_string())
None => {
// create post
match Post::create(
&mut db,
&info.title,
&info.body,
info.auto_pubilsh,
Uuid::new_v4().to_string(),
&info.text_id,
user.username,
)
.await
{
Some(_) => status::Custom(Status::Ok, "Created.".to_string()),
None => status::Custom(
Status::InternalServerError,
"Couldn't create post.".to_string(),
),
}
}
}
}
false => status::Custom(
Status::Unauthorized,
"You don't have the permissions to do this".to_string(),
),
}
} }
LoginStatus::InvalidToken => { LoginStatus::InvalidToken => {
status::Custom(Status::Unauthorized, "Invalid login token".to_string()) status::Custom(Status::Unauthorized, "Invalid login token".to_string())
@ -81,22 +48,3 @@ pub async fn create(
} }
} }
} }
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct ApiPost {
title: String,
author: String,
text_id: String,
body: String,
timestamp: String,
uuid: String,
published: bool
}
#[get("/posts/<id>")]
pub async fn get_post(mut db: Connection<Db>, id: String) -> status::Custom<Result<Json<ApiPost>, String>> {
// get post by uuid
// if none, get post by text id
// if none, return none
status::Custom(Status::NotImplemented, Err("Not implemented yet.".to_string()))
}

View File

@ -17,35 +17,32 @@ pub struct Post {
} }
impl Post { impl Post {
pub async fn create( pub async fn create(
db: &mut Connection<Db>, mut db: Connection<Db>,
title: &String, /*ex: Why Trans People Deserve All Your Money*/ title: String, /*ex: Why Trans People Deserve All Your Money*/
body: &String, /*ex: # Because we're cooler than you \n\n![trans flag image](https://sadlynotsappho.dev/pfp.png)*/ body: String, /*ex: # Because we're cooler than you \n\n![trans flag image](https://sadlynotsappho.dev/pfp.png)*/
published: bool, published: bool,
uuid: String, uuid: String,
text_id: &String, /*ex: why-trans-people-deserve-all-your-money */ text_id: String, /*ex: why-trans-people-deserve-all-your-money */
author: String ) {
) -> Option<()> {
match db match db
.execute( .fetch_all(
sqlx::query( sqlx::query(
r#" r#"
INSERT INTO posts (title, body, published, uuid, text_id, author) INSERT INTO posts (title, body, published, uuid, text_id)
VALUES ($1, $2, $3, $4, $5, $6); VALUES ($1, $2, $3, $4, $5);
"#, "#,
) )
.bind(title) .bind(title)
.bind(body) .bind(body)
.bind(published) .bind(published)
.bind(uuid) .bind(uuid)
.bind(text_id) .bind(text_id),
.bind(author),
) )
.await .await
{ {
Ok(_) => Some(()), Ok(_) => (),
Err(why) => { Err(why) => {
eprintln!("Couldn't create database entry: {why:?}"); eprintln!("Couldn't create database entry: {why:?}");
None
} }
} }
} }
@ -61,7 +58,7 @@ impl Post {
title: res.get::<String, _>("title"), title: res.get::<String, _>("title"),
body: res.get::<String, _>("body"), body: res.get::<String, _>("body"),
published: res.get::<bool, _>("published"), published: res.get::<bool, _>("published"),
timestamp: res.get::<NaiveDateTime, _>("timestamp"), timestamp: res.get::<NaiveDateTime, _>("timestamp")
} }
} }
pub async fn get_by_uuid(mut db: Connection<Db>, uuid: String) -> Post { pub async fn get_by_uuid(mut db: Connection<Db>, uuid: String) -> Post {
@ -76,27 +73,22 @@ impl Post {
title: res.get::<String, _>("title"), title: res.get::<String, _>("title"),
body: res.get::<String, _>("body"), body: res.get::<String, _>("body"),
published: res.get::<bool, _>("published"), published: res.get::<bool, _>("published"),
timestamp: res.get::<NaiveDateTime, _>("timestamp"), timestamp: res.get::<NaiveDateTime, _>("timestamp")
} }
} }
pub async fn get_by_text_id(db: &mut Connection<Db>, text_id: &String) -> Option<Post> { pub async fn get_by_text_id(mut db: Connection<Db>, text_id: String) -> Post {
match db let res = db
.fetch_one(sqlx::query("SELECT * FROM posts WHERE text_id = $1;").bind(text_id)) .fetch_one(sqlx::query("SELECT * FROM posts WHERE text_id = $1;").bind(text_id))
.await .await
{ .unwrap();
Ok(res) => Some(Post { Post {
id: res.get::<i32, _>("id"), id: res.get::<i32, _>("id"),
uuid: res.get::<String, _>("uuid"), uuid: res.get::<String, _>("uuid"),
text_id: res.get::<String, _>("text_id"), text_id: res.get::<String, _>("text_id"),
title: res.get::<String, _>("title"), title: res.get::<String, _>("title"),
body: res.get::<String, _>("body"), body: res.get::<String, _>("body"),
published: res.get::<bool, _>("published"), published: res.get::<bool, _>("published"),
timestamp: res.get::<NaiveDateTime, _>("timestamp"), timestamp: res.get::<NaiveDateTime, _>("timestamp")
}),
Err(why) => {
eprintln!("{why:?}");
None
}
} }
} }