Added hiding, deleting, editing posts
This commit is contained in:
parent
22846c85b3
commit
4cbb673f15
4 changed files with 243 additions and 61 deletions
97
site/src/api.rs
Normal file
97
site/src/api.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use crate::db::*;
|
||||
use crate::routes::{authorized, id_valid, replace_newlines};
|
||||
use actix_web::{get, http::StatusCode, post, web, web::Form, HttpResponse, Responder};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct NewPostForm {
|
||||
title: String,
|
||||
body: String,
|
||||
token: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct BlogActionForm {
|
||||
token: String,
|
||||
}
|
||||
|
||||
#[post("/api/blog/create")]
|
||||
async fn blog_create_post(form: Form<NewPostForm>) -> impl Responder {
|
||||
if authorized(&form.token) {
|
||||
create_post(&form.title.as_str(), replace_newlines(&form.body).as_str());
|
||||
println!("New blog post created.");
|
||||
} else {
|
||||
println!("Unauthorized new blog post");
|
||||
}
|
||||
|
||||
HttpResponse::MovedPermanently()
|
||||
.set_header("LOCATION", "/blog")
|
||||
.finish()
|
||||
}
|
||||
|
||||
#[post("/api/blog/posts/edit/{post_id}")]
|
||||
async fn blog_edit_post(
|
||||
web::Path(post_id): web::Path<std::string::String>,
|
||||
form: Form<NewPostForm>,
|
||||
) -> impl Responder {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid && authorized(&form.token) {
|
||||
edit_post_by_id(
|
||||
id as i32,
|
||||
&form.title.as_str(),
|
||||
replace_newlines(&form.body).as_str(),
|
||||
);
|
||||
println!("Edited post: {}", id);
|
||||
} else {
|
||||
println!("Unauthorized blog post edit.");
|
||||
return HttpResponse::new(StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
return HttpResponse::MovedPermanently()
|
||||
.set_header("LOCATION", "/blog")
|
||||
.finish();
|
||||
}
|
||||
|
||||
#[post("/api/blog/posts/delete/{post_id}")]
|
||||
async fn blog_delete_post(
|
||||
web::Path(post_id): web::Path<std::string::String>,
|
||||
form: Form<BlogActionForm>,
|
||||
) -> impl Responder {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid && authorized(&form.token) {
|
||||
println!("Deleted post: {}", id);
|
||||
delete_post_by_id(id as i32);
|
||||
} else {
|
||||
println!("Unauthorized blog post deletion.");
|
||||
return HttpResponse::new(StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
return HttpResponse::MovedPermanently()
|
||||
.set_header("LOCATION", "/blog")
|
||||
.finish();
|
||||
}
|
||||
|
||||
#[post("/api/blog/posts/hide/{post_id}")]
|
||||
async fn blog_hide_post(
|
||||
web::Path(post_id): web::Path<std::string::String>,
|
||||
form: Form<BlogActionForm>,
|
||||
) -> impl Responder {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid && authorized(&form.token) {
|
||||
println!("Hid post: {}", id);
|
||||
hide_post_by_id(id as i32);
|
||||
} else {
|
||||
println!("Unauthorized blog post hiding.");
|
||||
return HttpResponse::new(StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
return HttpResponse::MovedPermanently()
|
||||
.set_header("LOCATION", "/blog")
|
||||
.finish();
|
||||
}
|
||||
|
||||
#[get("/api/blog/posts")]
|
||||
async fn blog_get_posts_json() -> impl Responder {
|
||||
let posts = get_posts();
|
||||
HttpResponse::Ok().json(posts)
|
||||
}
|
|
@ -14,7 +14,7 @@ fn establish_connection() -> SqliteConnection {
|
|||
}
|
||||
|
||||
pub fn get_posts() -> std::vec::Vec<Post> {
|
||||
use crate::db::schema::posts::dsl::*;
|
||||
use schema::posts::dsl::*;
|
||||
let connection = establish_connection();
|
||||
posts
|
||||
.filter(published.eq(true))
|
||||
|
@ -24,16 +24,16 @@ pub fn get_posts() -> std::vec::Vec<Post> {
|
|||
.expect("Error, couldn't load posts.")
|
||||
}
|
||||
|
||||
pub fn get_post_by_id(_id: i32) -> Post {
|
||||
use crate::db::schema::posts::dsl::*;
|
||||
pub fn get_post_by_id(post_id: i32) -> Post {
|
||||
use schema::posts::dsl::*;
|
||||
let connection = establish_connection();
|
||||
posts
|
||||
.find(_id)
|
||||
.find(post_id)
|
||||
.get_result(&connection)
|
||||
.expect("Error, couldn't find post")
|
||||
.expect("Error, couldn't find post.")
|
||||
}
|
||||
|
||||
pub fn add_post(title: &str, body: &str) {
|
||||
pub fn create_post(title: &str, body: &str) {
|
||||
use chrono::prelude::*;
|
||||
use schema::posts;
|
||||
|
||||
|
@ -51,3 +51,34 @@ pub fn add_post(title: &str, body: &str) {
|
|||
.execute(&connection)
|
||||
.unwrap_or_else(|_| panic!("Error, couldn't insert new Post."));
|
||||
}
|
||||
|
||||
pub fn edit_post_by_id(post_id: i32, new_title: &str, new_body: &str) {
|
||||
use schema::posts::dsl::*;
|
||||
let connection = establish_connection();
|
||||
|
||||
diesel::update(posts)
|
||||
.filter(id.eq(post_id))
|
||||
.set((title.eq(new_title), body.eq(new_body)))
|
||||
.execute(&connection)
|
||||
.expect("Error, couldn't update post.");
|
||||
}
|
||||
|
||||
pub fn delete_post_by_id(post_id: i32) {
|
||||
use schema::posts::dsl::*;
|
||||
let connection = establish_connection();
|
||||
|
||||
diesel::delete(posts.filter(id.eq(post_id)))
|
||||
.execute(&connection)
|
||||
.expect("Error, couldn't update post.");
|
||||
}
|
||||
|
||||
pub fn hide_post_by_id(post_id: i32) {
|
||||
use schema::posts::dsl::*;
|
||||
let connection = establish_connection();
|
||||
|
||||
diesel::update(posts)
|
||||
.filter(id.eq(post_id))
|
||||
.set(published.eq(false))
|
||||
.execute(&connection)
|
||||
.expect("Error, couldn't update post.");
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
mod api;
|
||||
mod config;
|
||||
mod db;
|
||||
mod routes;
|
||||
|
@ -18,12 +19,17 @@ async fn main() -> std::io::Result<()> {
|
|||
let root_path = get_from_env("ROOT_PATH", true);
|
||||
|
||||
App::new()
|
||||
//.wrap(middleware::NormalizePath::default())
|
||||
.service(routes::root)
|
||||
.service(routes::blog)
|
||||
.service(routes::blog_permalink)
|
||||
.service(routes::blog_by_id)
|
||||
.service(routes::blog_submit)
|
||||
.service(routes::blog_new_post)
|
||||
.service(routes::blog_edit)
|
||||
.service(routes::blog_edit_by_id)
|
||||
.service(api::blog_get_posts_json)
|
||||
.service(api::blog_create_post)
|
||||
.service(api::blog_edit_post)
|
||||
.service(api::blog_hide_post)
|
||||
.service(api::blog_delete_post)
|
||||
.service(fs::Files::new("/static", root_path + "/static"))
|
||||
})
|
||||
.bind(String::from("localhost:") + &get_from_env("BIND_PORT", true))?
|
||||
|
|
|
@ -2,10 +2,38 @@ use crate::config;
|
|||
use crate::db::*;
|
||||
|
||||
use actix_files as fs;
|
||||
use actix_web::{get, http::StatusCode, post, web, web::Form, HttpResponse, Responder};
|
||||
use serde::Deserialize;
|
||||
use actix_web::{get, http::StatusCode, web, HttpResponse, Responder};
|
||||
use tera::{Context, Tera};
|
||||
|
||||
pub fn authorized(form_token: &str) -> bool {
|
||||
let token = config::get_from_env("SUBMIT_TOKEN", true);
|
||||
if token == form_token {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn id_valid(post_id: String) -> (bool, i32) {
|
||||
match post_id.parse::<i32>() {
|
||||
Err(_) => (false, 0),
|
||||
Ok(id) => {
|
||||
if id < 1 {
|
||||
(false, id)
|
||||
} else {
|
||||
(true, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_newlines(x: &String) -> String {
|
||||
x.replace("\n", "<br>")
|
||||
}
|
||||
|
||||
pub fn replace_br_tags(x: &String) -> String {
|
||||
x.replace("<br>", "\n")
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn root() -> impl Responder {
|
||||
let root_path = config::get_from_env("ROOT_PATH", true);
|
||||
|
@ -36,23 +64,30 @@ async fn blog() -> impl Responder {
|
|||
#[get("/blog/submit")]
|
||||
async fn blog_submit() -> impl Responder {
|
||||
let root_path = config::get_from_env("ROOT_PATH", true);
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html")
|
||||
.set_header("SameSite", "secure")
|
||||
.body(
|
||||
std::fs::read_to_string(root_path + "/html/submit.html")
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't load submit html file.\n{}", e)),
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("title", "");
|
||||
context.insert("body", "");
|
||||
|
||||
// one-off render blog template with context
|
||||
let result = Tera::one_off(
|
||||
&(std::fs::read_to_string(root_path + "/templates/post-submit.html")
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't load blog template.\n{}", e))
|
||||
.as_str()),
|
||||
&context,
|
||||
false,
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
|
||||
HttpResponse::Ok().content_type("text/html").body(result)
|
||||
}
|
||||
|
||||
#[get("/blog/id/{post_id}")]
|
||||
async fn blog_permalink(web::Path(post_id): web::Path<std::string::String>) -> impl Responder {
|
||||
match post_id.parse::<u32>() {
|
||||
Err(_) => HttpResponse::new(StatusCode::NOT_FOUND),
|
||||
Ok(i) => {
|
||||
async fn blog_by_id(web::Path(post_id): web::Path<std::string::String>) -> impl Responder {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid {
|
||||
let root_path = config::get_from_env("ROOT_PATH", true);
|
||||
|
||||
let post = get_post_by_id(i as i32);
|
||||
let post = get_post_by_id(id as i32);
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("posts", &[post]);
|
||||
|
@ -66,32 +101,45 @@ async fn blog_permalink(web::Path(post_id): web::Path<std::string::String>) -> i
|
|||
false,
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
|
||||
HttpResponse::Ok().content_type("text/html").body(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct NewPostForm {
|
||||
title: String,
|
||||
body: String,
|
||||
token: String,
|
||||
}
|
||||
|
||||
#[post("/blog/posts/new")]
|
||||
async fn blog_new_post(form: Form<NewPostForm>) -> impl Responder {
|
||||
let token = config::get_from_env("SUBMIT_TOKEN", true);
|
||||
|
||||
let replace_newlines = |x: &String| x.replace("\n", "<br>");
|
||||
|
||||
if form.token == token {
|
||||
add_post(&form.title.as_str(), replace_newlines(&form.body).as_str());
|
||||
println!("New blog post created.");
|
||||
return HttpResponse::Ok().content_type("text/html").body(result);
|
||||
} else {
|
||||
println!("Unauthorized new blog post");
|
||||
return HttpResponse::new(StatusCode::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/blog/edit")]
|
||||
async fn blog_edit() -> impl Responder {
|
||||
"edit"
|
||||
}
|
||||
|
||||
#[get("/blog/edit/{post_id}")]
|
||||
async fn blog_edit_by_id(web::Path(post_id): web::Path<std::string::String>) -> impl Responder {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid {
|
||||
let root_path = config::get_from_env("ROOT_PATH", true);
|
||||
|
||||
let mut post = get_post_by_id(id as i32);
|
||||
|
||||
post.title = replace_br_tags(&post.title);
|
||||
post.body = replace_br_tags(&post.body);
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("title", &post.title);
|
||||
context.insert("body", &post.body);
|
||||
context.insert("id", &id);
|
||||
|
||||
// one-off render blog template with context
|
||||
let result = Tera::one_off(
|
||||
&(std::fs::read_to_string(root_path + "/templates/post-edit.html")
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't load blog template.\n{}", e))
|
||||
.as_str()),
|
||||
&context,
|
||||
false,
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't render submit template.\n{}", e));
|
||||
|
||||
return HttpResponse::Ok().content_type("text/html").body(result);
|
||||
} else {
|
||||
return HttpResponse::new(StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
HttpResponse::MovedPermanently()
|
||||
.set_header("LOCATION", "/blog")
|
||||
.finish()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue