Compare commits
No commits in common. "a9cbf77eaa257d691b3e617944ae8cf0d56d9691" and "518dd26b0f5bb8851e7b96b5693a65ceb795ae72" have entirely different histories.
a9cbf77eaa
...
518dd26b0f
|
@ -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.
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -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()
|
||||||
|
|
|
@ -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 {
|
||||||
true => match Image::get_by_username(&mut db, &username).await {
|
Some(t) => match User::get_by_token(&mut db, t).await {
|
||||||
Ok(images) => status::Custom(
|
Some(user) => match user.admin || user.username == username {
|
||||||
Status::Ok,
|
true => match Image::get_by_username(&mut db, &username).await {
|
||||||
Ok(Json::from(
|
Ok(images) => 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:?}");
|
Err("Couldn't get that user's images".to_string())
|
||||||
status::Custom(
|
}
|
||||||
Status::NotFound,
|
},
|
||||||
Err("Couldn't get that user's images".to_string()),
|
false => Err("You don't have permission to do this".to_string()),
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
false => status::Custom(
|
None => Err("Invalid login token".to_string()),
|
||||||
Status::Unauthorized,
|
|
||||||
Err("You don't have permission to do this".to_string()),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
LoginStatus::InvalidToken => status::Custom(
|
None => Err("Not logged in".to_string()),
|
||||||
Status::Unauthorized,
|
|
||||||
Err("Invalid login token.".to_string()),
|
|
||||||
),
|
|
||||||
LoginStatus::NotLoggedIn => {
|
|
||||||
status::Custom(Status::Unauthorized, Err("Not logged in.".to_string()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
// if post with same text id exists, fail
|
||||||
true => {
|
// make sure user has perms to do this first tho
|
||||||
// if post with same text id exists, fail
|
// and uhhhhh
|
||||||
match Post::get_by_text_id(&mut db, &info.text_id).await {
|
// yeah thats it idk
|
||||||
Some(_) => status::Custom(
|
// TODO: implement all that
|
||||||
Status::Forbidden,
|
status::Custom(Status::NotImplemented, "Not implemented yet".to_string())
|
||||||
"A post already exists with this text id.".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()))
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue