add GET /api/posts/<id>, returns json post data
This commit is contained in:
parent
a9cbf77eaa
commit
d3d2510f7a
|
@ -3,3 +3,4 @@ db/password.txt
|
||||||
.env
|
.env
|
||||||
images/*.png
|
images/*.png
|
||||||
tmpimages/*.png
|
tmpimages/*.png
|
||||||
|
rustc-*.txt
|
||||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -120,7 +120,7 @@ async fn migrate(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
let _ = conn
|
let _ = conn
|
||||||
.execute(sqlx::query(
|
.execute(sqlx::query(
|
||||||
"ALTER TABLE posts
|
"ALTER TABLE posts
|
||||||
ADD COLUMN IF NOT EXISTS author STRING NOT NULL",
|
ADD COLUMN IF NOT EXISTS author TEXT NOT NULL",
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
let _ = conn
|
let _ = conn
|
||||||
|
@ -129,6 +129,12 @@ async fn migrate(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
ADD COLUMN IF NOT EXISTS timestamp TIMESTAMP DEFAULT localtimestamp NOT NULL",
|
ADD COLUMN IF NOT EXISTS timestamp TIMESTAMP DEFAULT localtimestamp NOT NULL",
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
let _ = conn
|
||||||
|
.execute(sqlx::query(
|
||||||
|
"ALTER TABLE posts
|
||||||
|
ADD COLUMN IF NOT EXISTS last_edited_timestamp TIMESTAMP",
|
||||||
|
))
|
||||||
|
.await;
|
||||||
rocket
|
rocket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +182,7 @@ async fn main() {
|
||||||
)
|
)
|
||||||
.mount(
|
.mount(
|
||||||
"/api",
|
"/api",
|
||||||
routes![routes::users::api_perms, routes::posts::get_post],
|
routes![routes::users::api_perms, routes::posts::get_post, routes::posts::update_post],
|
||||||
)
|
)
|
||||||
.mount("/css", FileServer::from("/srv/web/css"))
|
.mount("/css", FileServer::from("/srv/web/css"))
|
||||||
.register("/", catchers![default_catcher])
|
.register("/", catchers![default_catcher])
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* 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
|
* TODO PATCH /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
|
* TODO DELETE /posts/<id>: you can figure out what this one does
|
||||||
*
|
*
|
||||||
|
@ -15,10 +15,10 @@ use rocket::http::Status;
|
||||||
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::serde::Serialize;
|
||||||
|
use rocket::{get, patch, post};
|
||||||
use rocket_db_pools::Connection;
|
use rocket_db_pools::Connection;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use rocket::serde::Serialize;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
|
@ -26,7 +26,7 @@ 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,
|
pub auto_publish: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/posts", data = "<info>")]
|
#[post("/posts", data = "<info>")]
|
||||||
|
@ -40,25 +40,28 @@ pub async fn create(
|
||||||
match user.make_posts || user.admin {
|
match user.make_posts || user.admin {
|
||||||
true => {
|
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 {
|
match Post::get_by_text_id(&mut db, &info.text_id, false).await {
|
||||||
Some(_) => status::Custom(
|
Some(_) => status::Custom(
|
||||||
Status::Forbidden,
|
Status::Forbidden,
|
||||||
"A post already exists with this text id.".to_string(),
|
"A post already exists with this text id.".to_string(),
|
||||||
),
|
),
|
||||||
None => {
|
None => {
|
||||||
// create post
|
// create post
|
||||||
|
let uuid = Uuid::new_v4().to_string();
|
||||||
match Post::create(
|
match Post::create(
|
||||||
&mut db,
|
&mut db,
|
||||||
&info.title,
|
&info.title,
|
||||||
&info.body,
|
&info.body,
|
||||||
info.auto_pubilsh,
|
info.auto_publish,
|
||||||
Uuid::new_v4().to_string(),
|
&uuid,
|
||||||
&info.text_id,
|
&info.text_id,
|
||||||
user.username,
|
user.username,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Some(_) => status::Custom(Status::Ok, "Created.".to_string()),
|
Some(_) => {
|
||||||
|
status::Custom(Status::Ok, format!("Created with uuid {uuid}"))
|
||||||
|
}
|
||||||
None => status::Custom(
|
None => status::Custom(
|
||||||
Status::InternalServerError,
|
Status::InternalServerError,
|
||||||
"Couldn't create post.".to_string(),
|
"Couldn't create post.".to_string(),
|
||||||
|
@ -90,13 +93,65 @@ pub struct ApiPost {
|
||||||
body: String,
|
body: String,
|
||||||
timestamp: String,
|
timestamp: String,
|
||||||
uuid: String,
|
uuid: String,
|
||||||
published: bool
|
// published: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/posts/<id>")]
|
#[get("/posts/<id>")]
|
||||||
pub async fn get_post(mut db: Connection<Db>, id: String) -> status::Custom<Result<Json<ApiPost>, String>> {
|
pub async fn get_post(
|
||||||
// get post by uuid
|
mut db: Connection<Db>,
|
||||||
// if none, get post by text id
|
id: String,
|
||||||
// if none, return none
|
) -> status::Custom<Result<Json<ApiPost>, String>> {
|
||||||
status::Custom(Status::NotImplemented, Err("Not implemented yet.".to_string()))
|
// get uuid
|
||||||
|
match match Post::get_by_uuid(&mut db, &id, false).await {
|
||||||
|
Some(p) => Some(p),
|
||||||
|
None => match Post::get_by_text_id(&mut db, &id, false).await {
|
||||||
|
Some(p) => Some(p),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
Some(p) => {
|
||||||
|
if p.published {
|
||||||
|
status::Custom(
|
||||||
|
Status::Ok,
|
||||||
|
Ok(Json(ApiPost {
|
||||||
|
title: p.title,
|
||||||
|
author: p.author,
|
||||||
|
text_id: p.text_id,
|
||||||
|
body: p.body,
|
||||||
|
uuid: p.uuid,
|
||||||
|
timestamp: p.timestamp.to_string(),
|
||||||
|
// published: p.published,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
status::Custom(Status::NotFound, Err("Post doesn't exist".to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => status::Custom(Status::NotFound, Err("Post doesn't exist".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct PostUpdateNew {
|
||||||
|
pub title: String,
|
||||||
|
pub text_id: String,
|
||||||
|
pub body: String,
|
||||||
|
pub published: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct PostUpdateInfo {
|
||||||
|
pub uuid: String,
|
||||||
|
pub new: PostUpdateNew,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[patch /* because of course it's patch and not update */ ("/posts/<id>", data = "<info>")]
|
||||||
|
pub async fn update_post(
|
||||||
|
mut db: Connection<Db>,
|
||||||
|
id: String,
|
||||||
|
info: Json<PostUpdateInfo>,
|
||||||
|
) -> status::Custom<&'static str> {
|
||||||
|
// TODO
|
||||||
|
status::Custom(Status::NotImplemented, "Not implemented yet.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub struct Post {
|
||||||
pub body: String,
|
pub body: String,
|
||||||
pub published: bool,
|
pub published: bool,
|
||||||
pub timestamp: NaiveDateTime,
|
pub timestamp: NaiveDateTime,
|
||||||
|
pub author: String,
|
||||||
}
|
}
|
||||||
impl Post {
|
impl Post {
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
|
@ -21,9 +22,9 @@ impl Post {
|
||||||
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
|
author: String,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
match db
|
match db
|
||||||
.execute(
|
.execute(
|
||||||
|
@ -49,7 +50,7 @@ impl Post {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get(mut db: Connection<Db>, id: i32) -> Post {
|
pub async fn get(db: &mut Connection<Db>, id: i32) -> Post {
|
||||||
let res = db
|
let res = db
|
||||||
.fetch_one(sqlx::query("SELECT * FROM posts WHERE id = $1;").bind(id))
|
.fetch_one(sqlx::query("SELECT * FROM posts WHERE id = $1;").bind(id))
|
||||||
.await
|
.await
|
||||||
|
@ -62,14 +63,19 @@ impl Post {
|
||||||
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"),
|
||||||
|
author: res.get::<String, _>("author"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_by_uuid(mut db: Connection<Db>, uuid: String) -> Post {
|
pub async fn get_by_uuid(
|
||||||
let res = db
|
db: &mut Connection<Db>,
|
||||||
|
uuid: &String,
|
||||||
|
log_errors: bool,
|
||||||
|
) -> Option<Post> {
|
||||||
|
match db
|
||||||
.fetch_one(sqlx::query("SELECT * FROM posts WHERE uuid = $1;").bind(uuid))
|
.fetch_one(sqlx::query("SELECT * FROM posts WHERE uuid = $1;").bind(uuid))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
{
|
||||||
Post {
|
Ok(res) => Some(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"),
|
||||||
|
@ -77,9 +83,21 @@ impl Post {
|
||||||
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"),
|
||||||
|
author: res.get::<String, _>("author"),
|
||||||
|
}),
|
||||||
|
Err(why) => {
|
||||||
|
if log_errors {
|
||||||
|
eprintln!("{why:?}")
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_by_text_id(db: &mut Connection<Db>, text_id: &String) -> Option<Post> {
|
}
|
||||||
|
pub async fn get_by_text_id(
|
||||||
|
db: &mut Connection<Db>,
|
||||||
|
text_id: &String,
|
||||||
|
log_errors: bool,
|
||||||
|
) -> Option<Post> {
|
||||||
match db
|
match 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
|
||||||
|
@ -92,9 +110,12 @@ impl Post {
|
||||||
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"),
|
||||||
|
author: res.get::<String, _>("author"),
|
||||||
}),
|
}),
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
eprintln!("{why:?}");
|
if log_errors {
|
||||||
|
eprintln!("{why:?}")
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue