move templates and static pages into rust source code to ditch i/o on page load

This commit is contained in:
Leonard Lorenz 2020-11-14 22:38:33 +01:00
parent f0f4048835
commit d3a5cac4be
10 changed files with 214 additions and 180 deletions

View file

@ -1,4 +1,4 @@
# My website # crablog
Pure rust. Built with actix, diesel, tera, serde and sqlite3. Pure rust. Built with actix, diesel, tera, serde and sqlite3.
Environment variables are documented in [variables](./doc/environment.md) Environment variables are documented in [variables.md](./doc/environment.md)

View file

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>mtrx' site</title>
<link rel="stylesheet" href="/static/css/index.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<h1>Hi, I'm mtrx.</h1>
<p>
I have a <a href="/blog">blog.</a><br>
This site is 100% <a href="https://github.com/leonardlorenz/website">open source</a>.<br>
If you have questions or input for me please send me an E-Mail to me[at]mtrx.tech
</p>
</body>
</html>

View file

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>mtrx' blog</title>
<link rel="stylesheet" href="/static/css/blog.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<h1><a href="/blog" class="post-link" style="text-decoration:none;color:black;">mtrx' blog</a></h1>
<ul>
{% for post in posts %}
<article>
<a href="/blog/id/{{ post.id }}" class="post-link">[link]</a>
<div class="post-content">
<h2 class="post-title">{{ post.title }}</h2>
<sub class="post-publish-date"> {{ post.publish_date | date(format="%Y-%m-%d at %H:%M") }}</sub>
<p class="post-body">{{ post.body }}</p>
</div>
</article>
{% endfor %}
</ul>
</body>
</html>

View file

@ -1,19 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>mtrx' blog</title>
<link rel="stylesheet" href="/static/css/blog.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<h1><a href="/blog" class="post-link" style="text-decoration:none;color:black;">mtrx' blog</a></h1>
<h2>Edit posts</h2>
<ul style="list-style: none;">
{% for post in posts %}
<li><a href="/blog/edit/{{ post.id }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
</body>
</html>

View file

@ -1,39 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Post</title>
<link rel="stylesheet" href="/static/css/blog.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<div id="cookie-block" hidden>
<p>Please set your token cookie first.</p>
<input id="set-token" type="text" name="set-token">
<button onclick="setTokenCookie()">Set Token Cookie</button>
</div>
<button onclick="clearTokenCookie()">Clear Token Cookie</button>
<form id="submit-form" action="/api/blog/posts/edit/{{ id }}" method=POST>
<input class="token" type="text" name="token" hidden>
<label for="title">Title</label>
<textarea id="submit-title" type="text" name="title">{{ title }}</textarea>
<br>
<label for="submit-body">Content</label>
<textarea id="submit-body" type="text" name="body">{{ body }}</textarea>
<br>
<button id="submit-button" type="submit">Edit Post</button>
</form>
<form action="/api/blog/posts/hide/{{ id }}" method="POST">
<input class="token" type="text" name="token" hidden>
<button type="submit">Hide Post</button>
</form>
<form action="/api/blog/posts/delete/{{ id }}" method="POST">
<input class="token" type="text" name="token" hidden>
<button type="submit">Delete Post</button>
</form>
<script src="/static/js/blog.js"></script>
</body>
</html>

View file

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Submit Post</title>
<link rel="stylesheet" href="/static/css/blog.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<div id="cookie-block" hidden>
<p>Please set your token cookie first.</p>
<input id="set-token" type="text" name="set-token">
<button onclick="setTokenCookie()">Set Token Cookie</button>
</div>
<button onclick="clearTokenCookie()">Clear Token Cookie</button>
<form id="submit-form" action="/api/blog/create" method=POST>
<input class="token" type="text" name="token" hidden>
<label for="title">Title</label>
<textarea id="submit-title" type="text" name="title">{{ title }}</textarea>
<br>
<label for="submit-body">Content</label>
<textarea id="submit-body" type="text" name="body">{{ body }}</textarea>
<br>
<button id="submit-button" type="submit">Submit</button>
</form>
<script src="/static/js/blog.js"></script>
</body>
</html>

View file

@ -1,5 +1,8 @@
# Environment Variables # Environment Variables
- SUBMIT_TOKEN: Alphanumeric string to be used for creating blog posts
- ROOT_PATH: path where html, static and database reside - ROOT_PATH: path where html, static and database reside
- BIND_PORT: port to bind to - BIND_PORT: port to bind to
- DATABASE_URL: path to the sqlite3 database
- USERNAME: who does this site belong to?
- EMAIL: email to contact the site owner
- SUBMIT_TOKEN: Alphanumeric string to be used for creating blog posts

162
site/src/html.rs Normal file
View file

@ -0,0 +1,162 @@
pub const INDEX: &str = r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="{{ username }}' site'" />
<meta property="og:image" content="/static/site-image.png" />
<title>{{ username }}' site</title>
<link rel="stylesheet" href="/static/css/index.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<h1>Hi, I'm {{ username }}</h1>
<p>
I have a <a href="/blog">blog.</a><br>
If you have questions or input for me please send me an E-Mail to {{ email }}
</p>
</body>
</html>
"#;
pub const BLOG: &str = r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="{{ sitetitle }}" />
<meta property="og:image" content="/static/site-image.png" />
<title> {{ sitetitle }} </title>
<link rel="stylesheet" href="/static/css/blog.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<h1><a href="/blog" class="post-link" style="text-decoration:none;color:black;">{{ username }}' blog</a></h1>
<ul>
{% for post in posts %}
<article>
<a href="/blog/id/{{ post.id }}" class="post-link">[link]</a>
<div class="post-content">
<h2 class="post-title">{{ post.title }}</h2>
<sub class="post-publish-date"> {{ post.publish_date | date(format="%Y-%m-%d at %H:%M") }}</sub>
<p class="post-body">{{ post.body }}</p>
</div>
</article>
{% endfor %}
</ul>
</body>
</html>
"#;
pub const SUBMIT: &str = r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="Submit post" />
<meta property="og:image" content="/static/site-image.png" />
<title>Submit post</title>
<link rel="stylesheet" href="/static/css/blog.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<div id="cookie-block" hidden>
<p>Please set your token cookie first.</p>
<input id="set-token" type="text" name="set-token">
<button onclick="setTokenCookie()">Set Token Cookie</button>
</div>
<button onclick="clearTokenCookie()">Clear Token Cookie</button>
<form id="submit-form" action="/api/blog/create" method=POST>
<input class="token" type="text" name="token" hidden>
<label for="title">Title</label>
<textarea id="submit-title" type="text" name="title">{{ title }}</textarea>
<br>
<label for="submit-body">Content</label>
<textarea id="submit-body" type="text" name="body">{{ body }}</textarea>
<br>
<button id="submit-button" type="submit">Submit</button>
</form>
<script src="/static/js/blog.js"></script>
</body>
</html>
"#;
pub const EDIT: &str = r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="Edit posts..." />
<meta property="og:image" content="/static/site-image.png" />
<title>Edit posts...</title>
<link rel="stylesheet" href="/static/css/blog.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<h1><a href="/blog" class="post-link" style="text-decoration:none;color:black;">{{ username }}' blog</a></h1>
<h2>Edit posts</h2>
<ul style="list-style: none;">
{% for post in posts %}
<li><a href="/blog/edit/{{ post.id }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
</body>
</html>
"#;
pub const POST_EDIT_FORM: &str = r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="Edit '{{ title }}'"/>
<meta property="og:image" content="/static/site-image.png" />
<title>Edit '{{ title }}'</title>
<link rel="stylesheet" href="/static/css/blog.css">
<link rel="shortcut icon" type="image/jpg" href="/static/favicon.ico"/>
</head>
<body>
<div id="cookie-block" hidden>
<p>Please set your token cookie first.</p>
<input id="set-token" type="text" name="set-token">
<button onclick="setTokenCookie()">Set Token Cookie</button>
</div>
<button onclick="clearTokenCookie()">Clear Token Cookie</button>
<form id="submit-form" action="/api/blog/posts/edit/{{ id }}" method=POST>
<input class="token" type="text" name="token" hidden>
<label for="title">Title</label>
<textarea id="submit-title" type="text" name="title">{{ title }}</textarea>
<br>
<label for="submit-body">Content</label>
<textarea id="submit-body" type="text" name="body">{{ body }}</textarea>
<br>
<button id="submit-button" type="submit">Edit post</button>
</form>
<form action="/api/blog/posts/hide/{{ id }}" method="POST">
<input class="token" type="text" name="token" hidden>
<button type="submit">Hide post</button>
</form>
<form action="/api/blog/posts/delete/{{ id }}" method="POST">
<input class="token" type="text" name="token" hidden>
<button type="submit">Delete post</button>
</form>
<script src="/static/js/blog.js"></script>
</body>
</html>
"#;

View file

@ -2,6 +2,7 @@ mod api;
mod config; mod config;
mod db; mod db;
mod routes; mod routes;
mod html;
#[macro_use] #[macro_use]
extern crate diesel; extern crate diesel;

View file

@ -1,7 +1,7 @@
use crate::config; use crate::config;
use crate::db; use crate::db;
use crate::html;
use actix_files as fs;
use actix_web::{get, http::StatusCode, web, HttpResponse, Responder}; use actix_web::{get, http::StatusCode, web, HttpResponse, Responder};
use tera::{Context, Tera}; use tera::{Context, Tera};
@ -45,48 +45,38 @@ pub fn replace_br_tags(x: &str) -> String {
#[get("/")] #[get("/")]
async fn root() -> impl Responder { async fn root() -> impl Responder {
let root_path = config::get_from_env("ROOT_PATH", true); let mut context = Context::new();
fs::NamedFile::open(root_path + "/html/index.html")
context.insert("username", &config::get_from_env("USERNAME", true));
context.insert("email", &config::get_from_env("EMAIL", true));
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)
} }
#[get("/blog")] #[get("/blog")]
async fn blog() -> impl Responder { async fn blog() -> impl Responder {
let root_path = config::get_from_env("ROOT_PATH", true);
let posts = db::get_last_five_posts(); let posts = db::get_last_five_posts();
let mut context = Context::new(); let mut context = Context::new();
context.insert("posts", &posts); context.insert("posts", &posts);
context.insert("username", &(config::get_from_env("USERNAME", true)));
context.insert("sitetitle", &(config::get_from_env("USERNAME", true) + "' blog"));
// one-off render blog template with context // one-off render blog template with context
let result = Tera::one_off( let result = Tera::one_off(
&(std::fs::read_to_string(root_path + "/templates/blog.html") html::BLOG,
.unwrap_or_else(|e| panic!("Error, couldn't load blog template.\n{}", e))
.as_str()),
&context, &context,
false, false,
) )
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e)); .unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
HttpResponse::Ok().content_type("text/html").body(result)
}
#[get("/blog/submit")]
async fn blog_submit() -> impl Responder {
let root_path = config::get_from_env("ROOT_PATH", true);
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) HttpResponse::Ok().content_type("text/html").body(result)
} }
@ -94,40 +84,53 @@ async fn blog_submit() -> impl Responder {
async fn blog_by_id(web::Path(post_id): web::Path<std::string::String>) -> impl Responder { async fn blog_by_id(web::Path(post_id): web::Path<std::string::String>) -> impl Responder {
let (valid, id) = id_valid(post_id); let (valid, id) = id_valid(post_id);
if valid { if valid {
let root_path = config::get_from_env("ROOT_PATH", true);
let post = db::get_post_by_id(id as i32); let post = db::get_post_by_id(id as i32);
let mut context = Context::new(); let mut context = Context::new();
context.insert("posts", &[post]); context.insert("posts", &[&post]);
context.insert("username", &(config::get_from_env("USERNAME", true)));
context.insert("sitetitle", &post.title);
// one-off render blog template with context // one-off render blog template with context
let result = Tera::one_off( let result = Tera::one_off(
&(std::fs::read_to_string(root_path + "/templates/blog.html") html::BLOG,
.unwrap_or_else(|e| panic!("Error, couldn't load blog template.\n{}", e))
.as_str()),
&context, &context,
false, false,
) )
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e)); .unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
return HttpResponse::Ok().content_type("text/html").body(result); return HttpResponse::Ok().content_type("text/html").body(result);
} else { } else {
return HttpResponse::new(StatusCode::NOT_FOUND); return HttpResponse::new(StatusCode::NOT_FOUND);
} }
} }
#[get("/blog/edit")] #[get("/blog/submit")]
async fn blog_edit() -> impl Responder { async fn blog_submit() -> impl Responder {
let root_path = config::get_from_env("ROOT_PATH", true);
let mut context = Context::new(); let mut context = Context::new();
context.insert("posts", &db::get_all_posts()); context.insert("title", "");
context.insert("body", "");
// one-off render blog template with context // one-off render blog template with context
let result = Tera::one_off( let result = Tera::one_off(
&(std::fs::read_to_string(root_path + "/templates/edit.html") html::SUBMIT,
.unwrap_or_else(|e| panic!("Error, couldn't load edit template.\n{}", e)) &context,
.as_str()), false,
)
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
HttpResponse::Ok().content_type("text/html").body(result)
}
#[get("/blog/edit")]
async fn blog_edit() -> impl Responder {
let mut context = Context::new();
context.insert("posts", &db::get_all_posts());
context.insert("username", &config::get_from_env("USERNAME", true));
// one-off render blog template with context
let result = Tera::one_off(
html::EDIT,
&context, &context,
false, false,
) )
@ -140,8 +143,6 @@ async fn blog_edit() -> impl Responder {
async fn blog_edit_by_id(web::Path(post_id): web::Path<std::string::String>) -> impl Responder { async fn blog_edit_by_id(web::Path(post_id): web::Path<std::string::String>) -> impl Responder {
let (valid, id) = id_valid(post_id); let (valid, id) = id_valid(post_id);
if valid { if valid {
let root_path = config::get_from_env("ROOT_PATH", true);
let mut post = db::get_post_by_id(id as i32); let mut post = db::get_post_by_id(id as i32);
post.title = replace_br_tags(&post.title); post.title = replace_br_tags(&post.title);
@ -154,9 +155,7 @@ async fn blog_edit_by_id(web::Path(post_id): web::Path<std::string::String>) ->
// one-off render blog template with context // one-off render blog template with context
let result = Tera::one_off( let result = Tera::one_off(
&(std::fs::read_to_string(root_path + "/templates/post-edit.html") html::POST_EDIT_FORM,
.unwrap_or_else(|e| panic!("Error, couldn't load blog template.\n{}", e))
.as_str()),
&context, &context,
false, false,
) )