Global configuration, templates load once, render many times

This commit is contained in:
Leonard Lorenz 2020-11-21 20:51:32 +01:00
parent 74ab125243
commit 7becde6dc7
15 changed files with 300 additions and 120 deletions

View file

@ -0,0 +1,30 @@
<!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 }}' blog" />
<meta property="og:description" content="List of all posts" />
<meta property="og:image" content="/static/site-image.png" />
<title> All 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>
<p style="text-align: right"><a href="/blog">Last 5 posts</a></p>
<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>
</div>
</article>
{% endfor %}
</ul>
</body>
</html>

View file

@ -0,0 +1,29 @@
<!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=" {{ post.title }} | {{ username }}' blog" />
<meta property="og:description" content="{{ post.body }}" />
<meta property="og:image" content="/static/site-image.png" />
<title> {{ post.title }} | {{ username }}' 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;">{{ username }}' blog</a></h1>
<p style="text-align: right"><a href="/blog/all">All Posts</a></p>
<ul>
<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>
</ul>
</body>
</html>

View file

@ -0,0 +1,31 @@
<!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 }}' blog" />
<meta property="og:description" content="Last 5 posts" />
<meta property="og:image" content="/static/site-image.png" />
<title> {{ username }}' 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;">{{ username }}' blog</a></h1>
<p style="text-align: right"><a href="/blog/all">All Posts</a></p>
<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

@ -0,0 +1,42 @@
<!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

@ -0,0 +1,22 @@
<!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>

View file

@ -0,0 +1,21 @@
<!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>

View file

@ -0,0 +1,33 @@
<!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>

1
site/Cargo.lock generated
View file

@ -568,6 +568,7 @@ dependencies = [
"chrono", "chrono",
"diesel 1.4.5", "diesel 1.4.5",
"diesel_codegen", "diesel_codegen",
"once_cell",
"serde", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "crablog" name = "crablog"
version = "0.1.0" version = "0.2.0"
authors = ["Leonard Lorenz <dev@leonardlorenz.de>"] authors = ["Leonard Lorenz <dev@leonardlorenz.de>"]
edition = "2018" edition = "2018"
@ -8,16 +8,18 @@ edition = "2018"
[dependencies] [dependencies]
chrono = { version = "*", features = ["serde"] } chrono = { version = "*", features = ["serde"] }
actix-web = "*" actix-web = "3.2.0"
actix-files = "*" actix-files = "0.4.0"
serde = { version = "*", features = ["derive"] } serde = { version = "1.0.117", features = ["derive"] }
serde_json = "*" serde_json = "1.0.59"
serde_derive = "*" serde_derive = "1.0.117"
diesel = { version = "*", default-features = false, features = ["sqlite", "chrono"] } diesel = { version = "1.4.5", default-features = false, features = ["sqlite", "chrono"] }
diesel_codegen = { version = "*", default-features = false } 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"

View file

@ -1,7 +1,8 @@
use crate::db::*; 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 actix_web::{get, http::StatusCode, post, web, web::Form, HttpResponse, Responder};
use serde::Deserialize; use serde::Deserialize;
use super::CONFIG_MAP;
#[derive(Deserialize)] #[derive(Deserialize)]
struct NewPostForm { struct NewPostForm {
@ -17,7 +18,7 @@ struct BlogActionForm {
#[post("/api/blog/create")] #[post("/api/blog/create")]
async fn blog_create_post(form: Form<NewPostForm>) -> impl Responder { 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()); create_post(&form.title.as_str(), replace_newlines(&form.body).as_str());
println!("New blog post created."); println!("New blog post created.");
} else { } else {
@ -35,7 +36,7 @@ async fn blog_edit_post(
form: Form<NewPostForm>, form: Form<NewPostForm>,
) -> impl Responder { ) -> impl Responder {
let (valid, id) = id_valid(post_id); 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( edit_post_by_id(
id as i32, id as i32,
&form.title.as_str(), &form.title.as_str(),
@ -58,7 +59,7 @@ async fn blog_delete_post(
form: Form<BlogActionForm>, form: Form<BlogActionForm>,
) -> impl Responder { ) -> impl Responder {
let (valid, id) = id_valid(post_id); 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); println!("Deleted post: {}", id);
delete_post_by_id(id as i32); delete_post_by_id(id as i32);
} else { } else {
@ -77,7 +78,7 @@ async fn blog_hide_post(
form: Form<BlogActionForm>, form: Form<BlogActionForm>,
) -> impl Responder { ) -> impl Responder {
let (valid, id) = id_valid(post_id); 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); println!("Hid post: {}", id);
hide_post_by_id(id as i32); hide_post_by_id(id as i32);
} else { } else {

View file

@ -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);
}
})
}

View file

@ -1,15 +1,14 @@
mod models; mod models;
mod schema; mod schema;
use crate::config;
use diesel::prelude::*; use diesel::prelude::*;
use diesel::sqlite::SqliteConnection; use diesel::sqlite::SqliteConnection;
use models::*; use models::*;
use std::env;
/// Returns an SqliteConnection if connection successful. /// Returns an SqliteConnection if connection successful.
fn establish_connection() -> SqliteConnection { fn establish_connection() -> SqliteConnection {
let root_path = config::get_from_env("ROOT_PATH", true); let db_path = env::var("ROOT_PATH").unwrap() + "/db.sqlite3";
let db_path = root_path + "/db.sqlite3";
SqliteConnection::establish(&db_path) SqliteConnection::establish(&db_path)
.unwrap_or_else(|_| panic!("Error, connection to {} failed.", &db_path)) .unwrap_or_else(|_| panic!("Error, connection to {} failed.", &db_path))
} }

View file

@ -39,6 +39,7 @@ pub const BLOG: &str = r#"
<body> <body>
<h1><a href="/blog" class="post-link" style="text-decoration:none;color:black;">{{ username }}' blog</a></h1> <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> <ul>
{% for post in posts %} {% for post in posts %}
<article> <article>

View file

@ -1,8 +1,6 @@
mod api; mod api;
mod config;
mod db; mod db;
mod routes; mod routes;
mod html;
#[macro_use] #[macro_use]
extern crate diesel; extern crate diesel;
@ -12,16 +10,33 @@ extern crate tera;
use actix_files as fs; use actix_files as fs;
use actix_web::{App, HttpServer}; 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] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
HttpServer::new(|| { 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() App::new()
.data(tera)
.service(routes::root) .service(routes::root)
.service(routes::blog) .service(routes::blog)
.service(routes::blog_all)
.service(routes::blog_by_id) .service(routes::blog_by_id)
.service(routes::blog_submit) .service(routes::blog_submit)
.service(routes::blog_edit) .service(routes::blog_edit)
@ -31,9 +46,9 @@ async fn main() -> std::io::Result<()> {
.service(api::blog_edit_post) .service(api::blog_edit_post)
.service(api::blog_hide_post) .service(api::blog_hide_post)
.service(api::blog_delete_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() .run()
.await .await
} }

View file

@ -1,18 +1,8 @@
use crate::config;
use crate::db; use crate::db;
use crate::html;
use actix_web::{get, http::StatusCode, web, HttpResponse, Responder}; use actix_web::{get, http::StatusCode, web, HttpResponse, Error, error};
use tera::{Context, Tera}; use tera::Context;
use super::CONFIG_MAP;
/// 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
}
/// tests if the post id is a valid i32 integer bigger than zero /// tests if the post id is a valid i32 integer bigger than zero
/// assert(!(id_valid("2147483648").0)) /// assert(!(id_valid("2147483648").0))
@ -44,107 +34,90 @@ pub fn replace_br_tags(x: &str) -> String {
} }
#[get("/")] #[get("/")]
async fn root() -> impl Responder { async fn root(tmpl: web::Data<tera::Tera>) -> Result<HttpResponse, Error> {
let mut context = Context::new(); 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)); let result = tmpl.render("index.html", &context)
context.insert("email", &config::get_from_env("EMAIL", true)); .map_err(|e| error::ErrorInternalServerError(format!("Template error\n{}", e)))?;
let result = Tera::one_off( Ok(HttpResponse::Ok().content_type("text/html").body(result))
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(tmpl: web::Data<tera::Tera>) -> Result<HttpResponse, Error> {
let posts = db::get_last_five_posts(); let posts = db::get_last_five_posts();
let username = config::get_from_env("USERNAME", true);
let mut context = Context::new(); let mut context = Context::new();
context.insert("posts", &posts); context.insert("posts", &posts);
context.insert("username", &username); context.insert("username", CONFIG_MAP.read().unwrap().get("USERNAME").unwrap());
context.insert("sitetitle", &format!("{}' blog'", &username));
context.insert("sitedescription", &format!("Last 5 posts of {}' blog", &username));
// one-off render blog template with context let result = tmpl.render("blog.html", &context)
let result = Tera::one_off( .map_err(|_| error::ErrorInternalServerError("Template error"))?;
html::BLOG,
&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/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}")] #[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); let (valid, id) = id_valid(post_id);
if valid { if valid {
let post = db::get_post_by_id(id as i32); let post = db::get_post_by_id(id as i32);
let username = config::get_from_env("USERNAME", true);
let mut context = Context::new(); let mut context = Context::new();
context.insert("posts", &[&post]); context.insert("post", &post);
context.insert("username", &username); context.insert("username", CONFIG_MAP.read().unwrap().get("USERNAME").unwrap());
context.insert("sitetitle", &post.title);
context.insert("sitedescription", &post.body);
// one-off render blog template with context let result = tmpl.render("blog-by-id.html", &context)
let result = Tera::one_off( .map_err(|_| error::ErrorInternalServerError("Template error"))?;
html::BLOG,
&context,
false,
)
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
return HttpResponse::Ok().content_type("text/html").body(result); return Ok(HttpResponse::Ok().content_type("text/html").body(result))
} else { } else {
return HttpResponse::new(StatusCode::NOT_FOUND); return Ok(HttpResponse::new(StatusCode::NOT_FOUND))
} }
} }
#[get("/blog/submit")] #[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(); let mut context = Context::new();
context.insert("title", ""); context.insert("title", "");
context.insert("body", ""); context.insert("body", "");
// one-off render blog template with context let result = tmpl.render("submit.html", &context)
let result = Tera::one_off( .map_err(|_| error::ErrorInternalServerError("Template error"))?;
html::SUBMIT,
&context,
false,
)
.unwrap_or_else(|e| panic!("Error, couldn't render blog template.\n{}", e));
HttpResponse::Ok().content_type("text/html").body(result) return Ok(HttpResponse::Ok().content_type("text/html").body(result))
} }
#[get("/blog/edit")] #[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(); let mut context = Context::new();
context.insert("posts", &db::get_all_posts()); 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 = tmpl.render("edit.html", &context)
let result = Tera::one_off( .map_err(|_| error::ErrorInternalServerError("Template error"))?;
html::EDIT,
&context,
false,
)
.unwrap_or_else(|e| panic!("Error, couldn't render submit template.\n{}", e));
return HttpResponse::Ok().content_type("text/html").body(result); Ok(HttpResponse::Ok().content_type("text/html").body(result))
} }
#[get("/blog/edit/{post_id}")] #[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); let (valid, id) = id_valid(post_id);
if valid { if valid {
let mut post = db::get_post_by_id(id as i32); 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("body", &post.body);
context.insert("id", &id); context.insert("id", &id);
// one-off render blog template with context let result = tmpl.render("edit-form.html", &context)
let result = Tera::one_off( .map_err(|_| error::ErrorInternalServerError("Template error"))?;
html::POST_EDIT_FORM,
&context,
false,
)
.unwrap_or_else(|e| panic!("Error, couldn't render submit template.\n{}", e));
return HttpResponse::Ok().content_type("text/html").body(result); Ok(HttpResponse::Ok().content_type("text/html").body(result))
} else { } else {
return HttpResponse::new(StatusCode::UNAUTHORIZED); Ok(HttpResponse::new(StatusCode::UNAUTHORIZED))
} }
} }