Cleaned up directory, created documentation and readme

This commit is contained in:
Leonard Lorenz 2020-10-23 00:20:26 +02:00
parent 8a70db9b42
commit d6aee1e99d
16 changed files with 71 additions and 2 deletions

12
site/src/config.rs Normal file
View file

@ -0,0 +1,12 @@
use std::string::String;
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);
}
})
}

53
site/src/db.rs Normal file
View file

@ -0,0 +1,53 @@
mod models;
mod schema;
use crate::config;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use models::*;
fn establish_connection() -> SqliteConnection {
let root_path = config::get_from_env("ROOT_PATH", true);
let db_path = root_path + "/db.sqlite3";
SqliteConnection::establish(&db_path)
.unwrap_or_else(|_| panic!("Error, connection to {} failed.", &db_path))
}
pub fn get_posts() -> std::vec::Vec<Post> {
use crate::db::schema::posts::dsl::*;
let connection = establish_connection();
posts
.filter(published.eq(true))
.order(id.desc())
.limit(5)
.load::<Post>(&connection)
.expect("Error, couldn't load posts.")
}
pub fn get_post_by_id(id: i32) -> Post {
use crate::db::schema::posts::dsl::*;
let connection = establish_connection();
posts
.find(id)
.get_result(&connection)
.expect("Error, couldn't find post")
}
pub fn add_post(title: &str, body: &str) {
use chrono::prelude::*;
use schema::posts;
let connection = establish_connection();
let new_post = NewPost {
title,
body,
published: &true,
publish_date: &Utc::now().naive_utc(),
};
diesel::insert_into(posts::table)
.values(&new_post)
.execute(&connection)
.unwrap_or_else(|_| panic!("Error, couldn't insert new Post."));
}

20
site/src/db/models.rs Normal file
View file

@ -0,0 +1,20 @@
use super::schema::posts;
use serde::{Deserialize, Serialize};
#[derive(Queryable, Serialize, Deserialize)]
pub struct Post {
pub id: i32,
pub title: String,
pub body: String,
pub published: bool,
pub publish_date: chrono::NaiveDateTime,
}
#[derive(Insertable)]
#[table_name = "posts"]
pub struct NewPost<'a> {
pub title: &'a str,
pub body: &'a str,
pub published: &'a bool,
pub publish_date: &'a chrono::NaiveDateTime,
}

9
site/src/db/schema.rs Normal file
View file

@ -0,0 +1,9 @@
table! {
posts (id) {
id -> Integer,
title -> Text,
body -> Text,
published -> Bool,
publish_date -> Timestamp,
}
}

43
site/src/main.rs Normal file
View file

@ -0,0 +1,43 @@
mod config;
mod db;
mod routes;
#[macro_use]
extern crate diesel;
extern crate chrono;
extern crate serde_derive;
extern crate tera;
use actix_files as fs;
use actix_web::{App, HttpServer};
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
let root_path = config::get_from_env("ROOT_PATH", true);
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder
.set_private_key_file(
config::get_from_env("SSL_PATH", true) + "/key.pem",
SslFiletype::PEM,
)
.unwrap();
builder
.set_certificate_chain_file(config::get_from_env("SSL_PATH", true) + "/cert.pem")
.unwrap();
App::new()
//.wrap(middleware::NormalizePath::default())
.service(routes::root)
.service(routes::blog)
.service(routes::blog_permalink)
.service(routes::blog_submit)
.service(routes::blog_new_post)
.service(fs::Files::new("/static", root_path + "/static"))
})
.bind(String::from("localhost:") + &config::get_from_env("BIND_PORT", true))?
.run()
.await
}

92
site/src/routes.rs Normal file
View file

@ -0,0 +1,92 @@
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 tera::{Context, Tera};
#[get("/")]
async fn root() -> impl Responder {
let root_path = config::get_from_env("ROOT_PATH", true);
fs::NamedFile::open(root_path + "/html/index.html")
}
#[get("/blog")]
async fn blog() -> impl Responder {
let root_path = config::get_from_env("ROOT_PATH", true);
let posts = get_posts();
let mut context = Context::new();
context.insert("posts", &posts);
// one-off render blog template with context
let result = Tera::one_off(
&(std::fs::read_to_string(root_path + "/templates/blog.html")
.unwrap_or_else(|e| panic!("Error, couldn't load blog template.\n{}", e))
.as_str()),
&context,
true,
)
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
HttpResponse::Ok().body(result)
}
#[get("/blog/submit")]
async fn blog_submit() -> impl Responder {
let root_path = config::get_from_env("ROOT_PATH", true);
HttpResponse::Ok().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)),
)
}
#[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) => {
let root_path = config::get_from_env("ROOT_PATH", true);
let post = get_post_by_id(i as i32);
let mut context = Context::new();
context.insert("posts", &[post]);
// one-off render blog template with context
let result = Tera::one_off(
&(std::fs::read_to_string(root_path + "/templates/blog.html")
.unwrap_or_else(|e| panic!("Error, couldn't load blog template.\n{}", e))
.as_str()),
&context,
true,
)
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
HttpResponse::Ok().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);
if form.token == token {
add_post(&form.title.as_str(), &form.body.as_str());
println!("New blog post created.");
} else {
println!("Unauthorized new blog post");
}
HttpResponse::MovedPermanently()
.set_header("LOCATION", "/blog")
.finish()
}

9
site/src/schema.rs Normal file
View file

@ -0,0 +1,9 @@
table! {
posts (id) {
id -> Nullable<Integer>,
title -> Text,
body -> Text,
published -> Bool,
publish_date -> Timestamp,
}
}