Global configuration, templates load once, render many times
This commit is contained in:
parent
74ab125243
commit
7becde6dc7
15 changed files with 300 additions and 120 deletions
1
site/Cargo.lock
generated
1
site/Cargo.lock
generated
|
@ -568,6 +568,7 @@ dependencies = [
|
|||
"chrono",
|
||||
"diesel 1.4.5",
|
||||
"diesel_codegen",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "crablog"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
authors = ["Leonard Lorenz <dev@leonardlorenz.de>"]
|
||||
edition = "2018"
|
||||
|
||||
|
@ -8,16 +8,18 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
chrono = { version = "*", features = ["serde"] }
|
||||
actix-web = "*"
|
||||
actix-files = "*"
|
||||
actix-web = "3.2.0"
|
||||
actix-files = "0.4.0"
|
||||
|
||||
serde = { version = "*", features = ["derive"] }
|
||||
serde_json = "*"
|
||||
serde_derive = "*"
|
||||
serde = { version = "1.0.117", features = ["derive"] }
|
||||
serde_json = "1.0.59"
|
||||
serde_derive = "1.0.117"
|
||||
|
||||
diesel = { version = "*", default-features = false, features = ["sqlite", "chrono"] }
|
||||
diesel_codegen = { version = "*", default-features = false }
|
||||
diesel = { version = "1.4.5", default-features = false, features = ["sqlite", "chrono"] }
|
||||
diesel_codegen = { version = "0.16.1", default-features = false }
|
||||
|
||||
uuid = { version = "*", features = ["serde", "v5"] }
|
||||
uuid = { version = "0.8.1", features = ["serde", "v5"] }
|
||||
|
||||
tera = "*"
|
||||
tera = "1.5.0"
|
||||
|
||||
once_cell = "1.5.2"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::db::*;
|
||||
use crate::routes::{authorized, id_valid, replace_newlines};
|
||||
use crate::routes::{id_valid, replace_newlines};
|
||||
use actix_web::{get, http::StatusCode, post, web, web::Form, HttpResponse, Responder};
|
||||
use serde::Deserialize;
|
||||
use super::CONFIG_MAP;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct NewPostForm {
|
||||
|
@ -17,7 +18,7 @@ struct BlogActionForm {
|
|||
|
||||
#[post("/api/blog/create")]
|
||||
async fn blog_create_post(form: Form<NewPostForm>) -> impl Responder {
|
||||
if authorized(&form.token) {
|
||||
if *CONFIG_MAP.read().unwrap().get("SUBMIT_TOKEN").unwrap() == form.token {
|
||||
create_post(&form.title.as_str(), replace_newlines(&form.body).as_str());
|
||||
println!("New blog post created.");
|
||||
} else {
|
||||
|
@ -35,7 +36,7 @@ async fn blog_edit_post(
|
|||
form: Form<NewPostForm>,
|
||||
) -> impl Responder {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid && authorized(&form.token) {
|
||||
if valid && *CONFIG_MAP.read().unwrap().get("AUTH_TOKEN").unwrap() == form.token {
|
||||
edit_post_by_id(
|
||||
id as i32,
|
||||
&form.title.as_str(),
|
||||
|
@ -58,7 +59,7 @@ async fn blog_delete_post(
|
|||
form: Form<BlogActionForm>,
|
||||
) -> impl Responder {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid && authorized(&form.token) {
|
||||
if valid && *CONFIG_MAP.read().unwrap().get("AUTH_TOKEN").unwrap() == form.token {
|
||||
println!("Deleted post: {}", id);
|
||||
delete_post_by_id(id as i32);
|
||||
} else {
|
||||
|
@ -77,7 +78,7 @@ async fn blog_hide_post(
|
|||
form: Form<BlogActionForm>,
|
||||
) -> impl Responder {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid && authorized(&form.token) {
|
||||
if valid && *CONFIG_MAP.read().unwrap().get("AUTH_TOKEN").unwrap() == form.token {
|
||||
println!("Hid post: {}", id);
|
||||
hide_post_by_id(id as i32);
|
||||
} else {
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
use std::string::String;
|
||||
|
||||
/// gets a value from an environment variable and returns it.
|
||||
/// if this call was mandatory and it couldn't get a value, it will exit
|
||||
/// the program and write an error message.
|
||||
pub fn get_from_env(variable: &str, mandatory: bool) -> String {
|
||||
std::env::var(variable).unwrap_or_else(|_| {
|
||||
if mandatory {
|
||||
println!("Error, couldn't read environment variable: {}", variable);
|
||||
std::process::exit(1);
|
||||
} else {
|
||||
panic!("Error, couldn't read environment variable: {}", variable);
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,15 +1,14 @@
|
|||
mod models;
|
||||
mod schema;
|
||||
|
||||
use crate::config;
|
||||
use diesel::prelude::*;
|
||||
use diesel::sqlite::SqliteConnection;
|
||||
use models::*;
|
||||
use std::env;
|
||||
|
||||
/// Returns an SqliteConnection if connection successful.
|
||||
fn establish_connection() -> SqliteConnection {
|
||||
let root_path = config::get_from_env("ROOT_PATH", true);
|
||||
let db_path = root_path + "/db.sqlite3";
|
||||
let db_path = env::var("ROOT_PATH").unwrap() + "/db.sqlite3";
|
||||
SqliteConnection::establish(&db_path)
|
||||
.unwrap_or_else(|_| panic!("Error, connection to {} failed.", &db_path))
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ pub const BLOG: &str = r#"
|
|||
|
||||
<body>
|
||||
<h1><a href="/blog" class="post-link" style="text-decoration:none;color:black;">{{ username }}' blog</a></h1>
|
||||
<p style="text-align: right">All Posts</p>
|
||||
<ul>
|
||||
{% for post in posts %}
|
||||
<article>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
mod api;
|
||||
mod config;
|
||||
mod db;
|
||||
mod routes;
|
||||
mod html;
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
@ -12,16 +10,33 @@ extern crate tera;
|
|||
|
||||
use actix_files as fs;
|
||||
use actix_web::{App, HttpServer};
|
||||
use config::get_from_env;
|
||||
use tera::Tera;
|
||||
use std::{env, sync::RwLock, collections::HashMap};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub static CONFIG_MAP: Lazy<RwLock<HashMap<String, String>>> = Lazy::new(|| {
|
||||
let mut config: HashMap<String, String> = HashMap::new();
|
||||
config.insert(String::from("SUBMIT_TOKEN"), env::var("SUBMIT_TOKEN").expect("SUBMIT_TOKEN variable not set."));
|
||||
config.insert(String::from("ROOT_PATH"), env::var("ROOT_PATH").expect("ROOT_PATH variable not set."));
|
||||
config.insert(String::from("USERNAME"), env::var("USERNAME").expect("USERNAME variable not set."));
|
||||
config.insert(String::from("EMAIL"), env::var("EMAIL").expect("EMAIL variable not set."));
|
||||
config.insert(String::from("BIND_PORT"), env::var("BIND_PORT").expect("BIND_PORT variable not set."));
|
||||
RwLock::new(config)
|
||||
});
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
|
||||
HttpServer::new(|| {
|
||||
let root_path = get_from_env("ROOT_PATH", true);
|
||||
|
||||
let tera =
|
||||
Tera::new(format!("{}{}", CONFIG_MAP.read().unwrap().get("ROOT_PATH").unwrap(), "/templates/*").as_str()).unwrap();
|
||||
|
||||
App::new()
|
||||
.data(tera)
|
||||
.service(routes::root)
|
||||
.service(routes::blog)
|
||||
.service(routes::blog_all)
|
||||
.service(routes::blog_by_id)
|
||||
.service(routes::blog_submit)
|
||||
.service(routes::blog_edit)
|
||||
|
@ -31,9 +46,9 @@ async fn main() -> std::io::Result<()> {
|
|||
.service(api::blog_edit_post)
|
||||
.service(api::blog_hide_post)
|
||||
.service(api::blog_delete_post)
|
||||
.service(fs::Files::new("/static", root_path + "/static"))
|
||||
.service(fs::Files::new("/static", "../content/static"))
|
||||
})
|
||||
.bind("0.0.0.0:8000")?
|
||||
.bind(format!("0.0.0.0:{}", CONFIG_MAP.read().unwrap().get("BIND_PORT").unwrap()))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
use crate::config;
|
||||
use crate::db;
|
||||
use crate::html;
|
||||
|
||||
use actix_web::{get, http::StatusCode, web, HttpResponse, Responder};
|
||||
use tera::{Context, Tera};
|
||||
|
||||
/// authorizes a request by comparing it to the SUBMIT_TOKEN environment variable
|
||||
pub fn authorized(form_token: &str) -> bool {
|
||||
let token = config::get_from_env("SUBMIT_TOKEN", true);
|
||||
if token == form_token {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
use actix_web::{get, http::StatusCode, web, HttpResponse, Error, error};
|
||||
use tera::Context;
|
||||
use super::CONFIG_MAP;
|
||||
|
||||
/// tests if the post id is a valid i32 integer bigger than zero
|
||||
/// assert(!(id_valid("2147483648").0))
|
||||
|
@ -44,107 +34,90 @@ pub fn replace_br_tags(x: &str) -> String {
|
|||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn root() -> impl Responder {
|
||||
async fn root(tmpl: web::Data<tera::Tera>) -> Result<HttpResponse, Error> {
|
||||
let mut context = Context::new();
|
||||
context.insert("username", CONFIG_MAP.read().unwrap().get("USERNAME").unwrap());
|
||||
context.insert("email", CONFIG_MAP.read().unwrap().get("EMAIL").unwrap());
|
||||
|
||||
context.insert("username", &config::get_from_env("USERNAME", true));
|
||||
context.insert("email", &config::get_from_env("EMAIL", true));
|
||||
let result = tmpl.render("index.html", &context)
|
||||
.map_err(|e| error::ErrorInternalServerError(format!("Template error\n{}", e)))?;
|
||||
|
||||
let result = Tera::one_off(
|
||||
html::INDEX,
|
||||
&context,
|
||||
false,
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
|
||||
|
||||
HttpResponse::Ok().content_type("text/html").body(result)
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(result))
|
||||
}
|
||||
|
||||
#[get("/blog")]
|
||||
async fn blog() -> impl Responder {
|
||||
async fn blog(tmpl: web::Data<tera::Tera>) -> Result<HttpResponse, Error> {
|
||||
let posts = db::get_last_five_posts();
|
||||
let username = config::get_from_env("USERNAME", true);
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("posts", &posts);
|
||||
context.insert("username", &username);
|
||||
context.insert("sitetitle", &format!("{}' blog'", &username));
|
||||
context.insert("sitedescription", &format!("Last 5 posts of {}' blog", &username));
|
||||
context.insert("username", CONFIG_MAP.read().unwrap().get("USERNAME").unwrap());
|
||||
|
||||
// one-off render blog template with context
|
||||
let result = Tera::one_off(
|
||||
html::BLOG,
|
||||
&context,
|
||||
false,
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
|
||||
let result = tmpl.render("blog.html", &context)
|
||||
.map_err(|_| error::ErrorInternalServerError("Template error"))?;
|
||||
|
||||
HttpResponse::Ok().content_type("text/html").body(result)
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(result))
|
||||
}
|
||||
|
||||
#[get("/blog/all")]
|
||||
async fn blog_all(tmpl: web::Data<tera::Tera>) -> Result<HttpResponse, Error> {
|
||||
let posts = db::get_all_posts();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("posts", &posts);
|
||||
context.insert("username", CONFIG_MAP.read().unwrap().get("USERNAME").unwrap());
|
||||
|
||||
let result = tmpl.render("blog-all-posts.html", &context)
|
||||
.map_err(|_| error::ErrorInternalServerError("Template error"))?;
|
||||
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(result))
|
||||
}
|
||||
|
||||
#[get("/blog/id/{post_id}")]
|
||||
async fn blog_by_id(web::Path(post_id): web::Path<std::string::String>) -> impl Responder {
|
||||
async fn blog_by_id(tmpl: web::Data<tera::Tera>, web::Path(post_id): web::Path<std::string::String>) -> Result<HttpResponse, Error> {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid {
|
||||
let post = db::get_post_by_id(id as i32);
|
||||
let username = config::get_from_env("USERNAME", true);
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("posts", &[&post]);
|
||||
context.insert("username", &username);
|
||||
context.insert("sitetitle", &post.title);
|
||||
context.insert("sitedescription", &post.body);
|
||||
context.insert("post", &post);
|
||||
context.insert("username", CONFIG_MAP.read().unwrap().get("USERNAME").unwrap());
|
||||
|
||||
// one-off render blog template with context
|
||||
let result = Tera::one_off(
|
||||
html::BLOG,
|
||||
&context,
|
||||
false,
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
|
||||
let result = tmpl.render("blog-by-id.html", &context)
|
||||
.map_err(|_| error::ErrorInternalServerError("Template error"))?;
|
||||
|
||||
return HttpResponse::Ok().content_type("text/html").body(result);
|
||||
return Ok(HttpResponse::Ok().content_type("text/html").body(result))
|
||||
} else {
|
||||
return HttpResponse::new(StatusCode::NOT_FOUND);
|
||||
return Ok(HttpResponse::new(StatusCode::NOT_FOUND))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/blog/submit")]
|
||||
async fn blog_submit() -> impl Responder {
|
||||
async fn blog_submit(tmpl: web::Data<tera::Tera>) -> Result<HttpResponse, Error> {
|
||||
let mut context = Context::new();
|
||||
context.insert("title", "");
|
||||
context.insert("body", "");
|
||||
|
||||
// one-off render blog template with context
|
||||
let result = Tera::one_off(
|
||||
html::SUBMIT,
|
||||
&context,
|
||||
false,
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
|
||||
let result = tmpl.render("submit.html", &context)
|
||||
.map_err(|_| error::ErrorInternalServerError("Template error"))?;
|
||||
|
||||
HttpResponse::Ok().content_type("text/html").body(result)
|
||||
return Ok(HttpResponse::Ok().content_type("text/html").body(result))
|
||||
}
|
||||
|
||||
#[get("/blog/edit")]
|
||||
async fn blog_edit() -> impl Responder {
|
||||
async fn blog_edit(tmpl: web::Data<tera::Tera>) -> Result<HttpResponse, Error> {
|
||||
let mut context = Context::new();
|
||||
context.insert("posts", &db::get_all_posts());
|
||||
context.insert("username", &config::get_from_env("USERNAME", true));
|
||||
context.insert("username", CONFIG_MAP.read().unwrap().get("USERNAME").unwrap());
|
||||
|
||||
// one-off render blog template with context
|
||||
let result = Tera::one_off(
|
||||
html::EDIT,
|
||||
&context,
|
||||
false,
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't render submit template.\n{}", e));
|
||||
let result = tmpl.render("edit.html", &context)
|
||||
.map_err(|_| error::ErrorInternalServerError("Template error"))?;
|
||||
|
||||
return HttpResponse::Ok().content_type("text/html").body(result);
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(result))
|
||||
}
|
||||
|
||||
#[get("/blog/edit/{post_id}")]
|
||||
async fn blog_edit_by_id(web::Path(post_id): web::Path<std::string::String>) -> impl Responder {
|
||||
async fn blog_edit_by_id(tmpl: web::Data<tera::Tera>, web::Path(post_id): web::Path<std::string::String>) -> Result<HttpResponse, Error> {
|
||||
let (valid, id) = id_valid(post_id);
|
||||
if valid {
|
||||
let mut post = db::get_post_by_id(id as i32);
|
||||
|
@ -157,16 +130,11 @@ async fn blog_edit_by_id(web::Path(post_id): web::Path<std::string::String>) ->
|
|||
context.insert("body", &post.body);
|
||||
context.insert("id", &id);
|
||||
|
||||
// one-off render blog template with context
|
||||
let result = Tera::one_off(
|
||||
html::POST_EDIT_FORM,
|
||||
&context,
|
||||
false,
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Error, couldn't render submit template.\n{}", e));
|
||||
let result = tmpl.render("edit-form.html", &context)
|
||||
.map_err(|_| error::ErrorInternalServerError("Template error"))?;
|
||||
|
||||
return HttpResponse::Ok().content_type("text/html").body(result);
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(result))
|
||||
} else {
|
||||
return HttpResponse::new(StatusCode::UNAUTHORIZED);
|
||||
Ok(HttpResponse::new(StatusCode::UNAUTHORIZED))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue