diff --git a/content/templates/blog-all-posts.html b/content/templates/blog-all-posts.html
new file mode 100644
index 0000000..796b045
--- /dev/null
+++ b/content/templates/blog-all-posts.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+ All posts
+
+
+
+
+
+
+ Last 5 posts
+
+
+
diff --git a/content/templates/blog-by-id.html b/content/templates/blog-by-id.html
new file mode 100644
index 0000000..1de1257
--- /dev/null
+++ b/content/templates/blog-by-id.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+ {{ post.title }} | {{ username }}' blog
+
+
+
+
+
+
+ All Posts
+
+
+ [link]
+
+
{{ post.title }}
+
{{ post.publish_date | date(format="%Y-%m-%d at %H:%M") }}
+
{{ post.body }}
+
+
+
+
+
diff --git a/content/templates/blog.html b/content/templates/blog.html
new file mode 100644
index 0000000..b37aecb
--- /dev/null
+++ b/content/templates/blog.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+ {{ username }}' blog
+
+
+
+
+
+
+ All Posts
+
+ {% for post in posts %}
+
+ [link]
+
+
{{ post.title }}
+
{{ post.publish_date | date(format="%Y-%m-%d at %H:%M") }}
+
{{ post.body }}
+
+
+ {% endfor %}
+
+
+
diff --git a/content/templates/edit-form.html b/content/templates/edit-form.html
new file mode 100644
index 0000000..5215c94
--- /dev/null
+++ b/content/templates/edit-form.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+ Edit '{{ title }}'
+
+
+
+
+
+
Please set your token cookie first.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/content/templates/edit.html b/content/templates/edit.html
new file mode 100644
index 0000000..8a02d1a
--- /dev/null
+++ b/content/templates/edit.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+ Edit posts...
+
+
+
+
+
+ Edit posts
+
+
+
diff --git a/content/templates/index.html b/content/templates/index.html
new file mode 100644
index 0000000..a0b9750
--- /dev/null
+++ b/content/templates/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+ {{ username }}' site
+
+
+
+
+
+ Hi, I'm {{ username }}
+
+ I have a blog.
+ If you have questions or input for me please send me an E-Mail to {{ email }}
+
+
+
diff --git a/content/templates/submit.html b/content/templates/submit.html
new file mode 100644
index 0000000..449c02e
--- /dev/null
+++ b/content/templates/submit.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+ Submit post
+
+
+
+
+
+
Please set your token cookie first.
+
+
+
+
+
+
+
+
+
diff --git a/site/Cargo.lock b/site/Cargo.lock
index 168f2a8..19e7678 100644
--- a/site/Cargo.lock
+++ b/site/Cargo.lock
@@ -568,6 +568,7 @@ dependencies = [
"chrono",
"diesel 1.4.5",
"diesel_codegen",
+ "once_cell",
"serde",
"serde_derive",
"serde_json",
diff --git a/site/Cargo.toml b/site/Cargo.toml
index acc67a0..32a2b8a 100644
--- a/site/Cargo.toml
+++ b/site/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "crablog"
-version = "0.1.0"
+version = "0.2.0"
authors = ["Leonard Lorenz "]
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"
diff --git a/site/src/api.rs b/site/src/api.rs
index 65e5307..db074a4 100644
--- a/site/src/api.rs
+++ b/site/src/api.rs
@@ -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) -> 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,
) -> 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,
) -> 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,
) -> 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 {
diff --git a/site/src/config.rs b/site/src/config.rs
deleted file mode 100644
index a713a76..0000000
--- a/site/src/config.rs
+++ /dev/null
@@ -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);
- }
- })
-}
diff --git a/site/src/db.rs b/site/src/db.rs
index b87206c..a6484a7 100644
--- a/site/src/db.rs
+++ b/site/src/db.rs
@@ -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))
}
diff --git a/site/src/html.rs b/site/src/html.rs
index d89c5ba..0af4976 100644
--- a/site/src/html.rs
+++ b/site/src/html.rs
@@ -39,6 +39,7 @@ pub const BLOG: &str = r#"
+ All Posts
{% for post in posts %}
diff --git a/site/src/main.rs b/site/src/main.rs
index 99bf263..9e13e06 100644
--- a/site/src/main.rs
+++ b/site/src/main.rs
@@ -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>> = Lazy::new(|| {
+ let mut config: HashMap = 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
}
diff --git a/site/src/routes.rs b/site/src/routes.rs
index 2346c2b..f3cf216 100644
--- a/site/src/routes.rs
+++ b/site/src/routes.rs
@@ -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) -> Result {
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) -> Result {
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) -> Result {
+ 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) -> impl Responder {
+async fn blog_by_id(tmpl: web::Data, web::Path(post_id): web::Path) -> Result {
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) -> Result {
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) -> Result {
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) -> impl Responder {
+async fn blog_edit_by_id(tmpl: web::Data, web::Path(post_id): web::Path) -> Result {
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) ->
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))
}
}