Cleaned up directory, created documentation and readme
This commit is contained in:
parent
8a70db9b42
commit
d6aee1e99d
16 changed files with 71 additions and 2 deletions
12
site/src/config.rs
Normal file
12
site/src/config.rs
Normal 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
53
site/src/db.rs
Normal 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
20
site/src/db/models.rs
Normal 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
9
site/src/db/schema.rs
Normal 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
43
site/src/main.rs
Normal 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
92
site/src/routes.rs
Normal 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
9
site/src/schema.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
table! {
|
||||
posts (id) {
|
||||
id -> Nullable<Integer>,
|
||||
title -> Text,
|
||||
body -> Text,
|
||||
published -> Bool,
|
||||
publish_date -> Timestamp,
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue