remove tera dependency and html rendering code.

This commit is contained in:
2021-08-12 15:48:02 +02:00
committed by dietrich
parent dd113c3548
commit 06c2fd18bd
4 changed files with 60 additions and 778 deletions
-2
View File
@@ -32,7 +32,6 @@ qrcode = "0.12"
rand = "0.8"
rpassword = "5.0"
serde = "1.0"
tera = "1.6"
thiserror = "1.0"
tracing-actix-web = "0.2.1"
tracing-bunyan-formatter = "0.2.0"
@@ -47,7 +46,6 @@ features = ["serde"]
version = "0.4"
[dependencies.fluent-templates]
features = ["tera"]
version = "0.6"
[dependencies.sqlx]
+3 -118
View File
@@ -9,14 +9,12 @@ use actix_files::Files;
use actix_identity::{CookieIdentityPolicy, IdentityService};
use actix_web::HttpResponse;
use actix_web::{web, App, HttpServer};
use fluent_templates::{static_loader, FluentLoader};
use fluent_templates::static_loader;
use qrcode::types::QrError;
use shared::datatypes::Secret;
use sqlx::{Pool, Sqlite};
use std::{fmt::Display, path::PathBuf, str::FromStr};
use tera::Tera;
use thiserror::Error;
use tracing::instrument;
use tracing::{error, info, trace};
use tracing_actix_web::TracingLogger;
@@ -30,8 +28,6 @@ pub enum ServerError {
DatabaseMigration(#[from] sqlx::migrate::MigrateError),
#[error("The environment file could not be read")]
Environment(#[from] std::env::VarError),
#[error("The templates could not be rendered correctly: {0}")]
Template(#[from] tera::Error),
#[error("The qr-code could not be generated: {0}")]
Qr(#[from] QrError),
#[error("Some error happened during input and output: {0}")]
@@ -98,13 +94,6 @@ impl actix_web::error::ResponseError for ServerError {
"This Server is not properly configured, if you are the admin look into the installation- or update instructions!",
))
}
Self::Template(e) => {
eprintln!("Template Error happened: {:?}", e);
HttpResponse::InternalServerError().body(&Self::render_error(
"Server Error",
"The templates could not be rendered.",
))
}
Self::Qr(e) => {
eprintln!("QR Error happened: {:?}", e);
HttpResponse::InternalServerError().body(&Self::render_error(
@@ -206,51 +195,6 @@ static_loader! {
};
}
#[instrument]
fn build_tera() -> Result<Tera, ServerError> {
let mut tera = Tera::default();
// Add translation support
tera.register_function("fluent", FluentLoader::new(&*LOCALES));
tera.add_raw_templates(vec![
("admin.html", include_str!("../templates/admin.html")),
("base.html", include_str!("../templates/base.html")),
(
"edit_link.html",
include_str!("../templates/edit_link.html"),
),
(
"edit_profile.html",
include_str!("../templates/edit_profile.html"),
),
(
"index_users.html",
include_str!("../templates/index_users.html"),
),
("index.html", include_str!("../templates/index.html")),
("login.html", include_str!("../templates/login.html")),
(
"not_found.html",
include_str!("../templates/not_found.html"),
),
("signup.html", include_str!("../templates/signup.html")),
(
"submission.html",
include_str!("../templates/submission.html"),
),
(
"view_link.html",
include_str!("../templates/view_link.html"),
),
(
"view_profile.html",
include_str!("../templates/view_profile.html"),
),
])?;
Ok(tera)
}
/// Launch the pslink-webservice
///
/// # Errors
@@ -270,7 +214,6 @@ pub async fn webservice(
"If the public url is set up correctly it should be accessible via: {}://{}/admin/login/",
&server_config.protocol, &server_config.public_url
);
let tera = build_tera().expect("Failed to build Templates");
trace!("The tera templates are ready");
let server = HttpServer::new(move || {
@@ -283,7 +226,6 @@ pub async fn webservice(
.name("auth-cookie")
.secure(true),
))
.data(tera.clone())
.service(actix_web_static_files::ResourceFiles::new(
"/static", generated,
))
@@ -292,60 +234,6 @@ pub async fn webservice(
// admin block
.service(
web::scope("/admin")
// list all links
.route("/index/", web::get().to(views::index))
// invite users
.route("/signup/", web::get().to(views::signup))
.route("/signup/", web::post().to(views::process_signup))
// logout
.route("/logout/", web::to(views::logout))
// submit a new url for shortening
.route("/submit/", web::get().to(views::create_link))
.route("/submit/", web::post().to(views::process_link_creation))
// view an existing url
.service(
web::scope("/view")
.service(
web::scope("/link")
.route("/{redirect_id}", web::get().to(views::view_link))
.route("/", web::get().to(views::view_link_empty)),
)
.service(
web::scope("/profile")
.route("/{user_id}", web::get().to(views::view_profile)),
)
.route("/users/", web::get().to(views::index_users)),
)
.service(
web::scope("/edit")
.service(
web::scope("/link")
.route("/{redirect_id}", web::get().to(views::edit_link))
.route(
"/{redirect_id}",
web::post().to(views::process_link_edit),
),
)
.service(
web::scope("/profile")
.route("/{user_id}", web::get().to(views::edit_profile))
.route(
"/{user_id}",
web::post().to(views::process_edit_profile),
),
)
.route("/set_admin/{user_id}", web::get().to(views::toggle_admin))
.route(
"/set_language/{language}",
web::get().to(views::set_language),
),
)
.service(
web::scope("/delete").service(
web::scope("/link")
.route("/{redirect_id}", web::get().to(views::process_link_delete)),
),
)
.service(
web::scope("/download")
.route("/png/{redirect_id}", web::get().to(views::download_png)),
@@ -357,7 +245,6 @@ pub async fn webservice(
"/create_link/",
web::post().to(views::process_create_link_json),
)
.route("/get_qr_code/", web::post().to(views::get_qr_code_json))
.route(
"/edit_link/",
web::post().to(views::process_update_link_json),
@@ -380,11 +267,9 @@ pub async fn webservice(
web::post().to(views::get_logged_user_json),
)
.route("/login_user/", web::post().to(views::process_login_json)),
)
// login to the admin area
.route("/login/", web::get().to(views::login))
.route("/login/", web::post().to(views::process_login)),
),
)
// Serve the Wasm App for the admin interface.
.service(
web::scope("/app")
.service(Files::new("/pkg", "./app/pkg"))
+20 -393
View File
@@ -12,18 +12,15 @@ use fluent_langneg::{
};
use fluent_templates::LanguageIdentifier;
use image::{DynamicImage, ImageOutputFormat, Luma};
use qrcode::{render::svg, QrCode};
use qrcode::QrCode;
use queries::{authenticate, Role};
use shared::apirequests::{
general::{Message, Status},
links::{LinkDelta, LinkRequestForm, QrCodeRequest, SvgQrCodeResponse},
links::{LinkDelta, LinkRequestForm},
users::{LoginUser, UserDelta, UserRequestForm},
};
use tera::{Context, Tera};
use tracing::{error, info, instrument, warn};
use crate::forms::LinkForm;
use crate::models::NewUser;
use crate::queries;
use crate::ServerError;
@@ -94,30 +91,6 @@ pub async fn wasm_app(config: web::Data<crate::ServerConfig>) -> Result<HttpResp
))
}
/// Show the list of all available links if a user is authenticated
#[instrument(skip(id, tera))]
pub async fn index(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
id: Identity,
) -> Result<HttpResponse, ServerError> {
match queries::list_all_allowed(&id, &config, LinkRequestForm::default()).await {
Ok(links) => {
let mut data = Context::new();
data.insert("user", &links.user);
data.insert("title", &format!("Links der {}", &config.brand_name,));
data.insert("links_per_users", &links.list);
let rendered = tera.render("index.html", &data)?;
Ok(HttpResponse::Ok().body(rendered))
}
Err(e) => {
error!("Failed to get the links: {:?}", e);
Ok(redirect_builder("/admin/login/"))
}
}
}
#[instrument(skip(id))]
pub async fn index_json(
config: web::Data<crate::ServerConfig>,
@@ -135,25 +108,6 @@ pub async fn index_json(
}
}
/// Show the list of all available links if a user is authenticated
#[instrument(skip(id, tera))]
pub async fn index_users(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
id: Identity,
) -> Result<HttpResponse, ServerError> {
if let Ok(users) = queries::list_users(&id, &config, UserRequestForm::default()).await {
let mut data = Context::new();
data.insert("user", &users.user);
data.insert("title", &format!("Benutzer der {}", &config.brand_name,));
data.insert("users", &users.list);
let rendered = tera.render("index_users.html", &data)?;
Ok(HttpResponse::Ok().body(rendered))
} else {
Ok(redirect_builder("/admin/login"))
}
}
#[instrument(skip(id))]
pub async fn index_users_json(
config: web::Data<crate::ServerConfig>,
@@ -179,149 +133,6 @@ pub async fn get_logged_user_json(
}
}
#[instrument(skip(id, tera))]
pub async fn view_link_empty(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
id: Identity,
) -> Result<HttpResponse, ServerError> {
view_link(tera, config, id, web::Path::from("".to_owned())).await
}
pub async fn get_qr_code_json(
config: web::Data<crate::ServerConfig>,
qr_request: web::Json<QrCodeRequest>,
id: Identity,
) -> Result<HttpResponse, ServerError> {
if let Ok(link) = queries::get_link(&id, &qr_request.link_id, &config).await {
let host = config.public_url.to_string();
let qr = QrCode::with_error_correction_level(
&format!("http://{}/{}", &host, &link.item.code),
qrcode::EcLevel::L,
)?;
let svg = qr
.render()
.min_dimensions(100, 100)
.dark_color(svg::Color("#000000"))
.light_color(svg::Color("#ffffff"))
.build();
Ok(HttpResponse::Ok().json2(&SvgQrCodeResponse { svg }))
} else {
Ok(redirect_builder("/admin/login/"))
}
}
#[instrument(skip(id, tera))]
pub async fn view_link(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
id: Identity,
link_id: web::Path<String>,
) -> Result<HttpResponse, ServerError> {
if let Ok(link) = queries::get_link(&id, &link_id.0, &config).await {
let host = config.public_url.to_string();
let protocol = config.protocol.to_string();
let qr = QrCode::with_error_correction_level(
&format!("http://{}/{}", &host, &link.item.code),
qrcode::EcLevel::L,
)?;
let svg = qr
.render()
.min_dimensions(100, 100)
.dark_color(svg::Color("#000000"))
.light_color(svg::Color("#ffffff"))
.build();
let mut data = Context::new();
data.insert("user", &link.user);
data.insert(
"title",
&format!("Links {} der {}", &link.item.code, &config.brand_name,),
);
data.insert("link", &link.item);
data.insert("qr", &svg);
data.insert("host", &host);
data.insert("protocol", &protocol);
let rendered = tera.render("view_link.html", &data)?;
Ok(HttpResponse::Ok().body(rendered))
} else {
Ok(redirect_builder("/admin/login/"))
}
}
#[instrument(skip(id, tera))]
pub async fn view_profile(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
id: Identity,
user_id: web::Path<String>,
) -> Result<HttpResponse, ServerError> {
info!("Viewing Profile!");
if let Ok(query) = queries::get_user(&id, &user_id.0, &config).await {
let mut data = Context::new();
data.insert("user", &query.user);
data.insert(
"title",
&format!(
"Benutzer {} der {}",
&query.item.username, &config.brand_name,
),
);
data.insert("viewed_user", &query.item);
let rendered = tera.render("view_profile.html", &data)?;
Ok(HttpResponse::Ok().body(rendered))
} else {
// Parsing error -- do something else
Ok(redirect_builder("/admin/login/"))
}
}
#[instrument(skip(id, tera))]
pub async fn edit_profile(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
id: Identity,
user_id: web::Path<String>,
) -> Result<HttpResponse, ServerError> {
info!("Editing Profile!");
if let Ok(query) = queries::get_user(&id, &user_id.0, &config).await {
let mut data = Context::new();
data.insert("user", &query.user);
data.insert(
"title",
&format!(
"Benutzer {} der {}",
&query.user.username, &config.brand_name,
),
);
data.insert("user", &query.user);
let rendered = tera.render("edit_profile.html", &data)?;
Ok(HttpResponse::Ok().body(rendered))
} else {
Ok(redirect_builder("/admin/login/"))
}
}
#[instrument(skip(id))]
pub async fn process_edit_profile(
data: web::Form<NewUser>,
config: web::Data<crate::ServerConfig>,
id: Identity,
user_id: web::Path<String>,
) -> Result<HttpResponse, ServerError> {
let query = queries::update_user(&id, &user_id.0, &config, &data).await?;
Ok(redirect_builder(&format!(
"admin/view/profile/{}",
query.user.username
)))
}
#[instrument(skip(id))]
pub async fn download_png(
id: Identity,
@@ -347,42 +158,6 @@ pub async fn download_png(
}
}
#[instrument(skip(id, tera))]
pub async fn signup(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
id: Identity,
) -> Result<HttpResponse, ServerError> {
match queries::authenticate(&id, &config).await? {
queries::Role::Admin { user } => {
let mut data = Context::new();
data.insert("title", "Ein Benutzerkonto erstellen");
data.insert("user", &user);
let rendered = tera.render("signup.html", &data)?;
Ok(HttpResponse::Ok().body(rendered))
}
queries::Role::Regular { .. }
| queries::Role::NotAuthenticated
| queries::Role::Disabled => Ok(redirect_builder("/admin/login/")),
}
}
#[instrument(skip(id))]
pub async fn process_signup(
data: web::Form<NewUser>,
config: web::Data<crate::ServerConfig>,
id: Identity,
) -> Result<HttpResponse, ServerError> {
info!("Creating a User: {:?}", &data);
match queries::create_user(&id, &data, &config).await {
Ok(item) => {
Ok(HttpResponse::Ok().body(format!("Successfully saved user: {}", item.item.username)))
}
Err(e) => Err(e),
}
}
#[instrument(skip(id))]
pub async fn process_create_user_json(
config: web::Data<crate::ServerConfig>,
@@ -436,81 +211,6 @@ pub async fn set_language(
Ok(redirect_builder("/admin/index/"))
}
#[instrument(skip(tera, id))]
pub async fn login(
tera: web::Data<Tera>,
id: Identity,
config: web::Data<crate::ServerConfig>,
req: HttpRequest,
) -> Result<HttpResponse, ServerError> {
let language_code = detect_language(&req).unwrap_or_else(|_| "en".to_string());
info!("Detected languagecode: {}", &language_code);
let mut data = Context::new();
data.insert("title", "Login");
data.insert("language", &language_code);
if id.identity().is_some() {
if let Ok(r) = authenticate(&id, &config).await {
match r {
Role::Admin { user } | Role::Regular { user } => {
info!(
"This user ({}) is already logged in redirecting to /admin/index/",
user.username
);
return Ok(redirect_builder("/admin/index/"));
}
Role::Disabled | Role::NotAuthenticated => (),
}
}
warn!("Invalid user session. The user might be deleted or something tampered with the cookies.");
id.forget();
}
let rendered = tera.render("login.html", &data)?;
Ok(HttpResponse::Ok().body(rendered))
}
#[instrument(skip(id))]
pub async fn process_login(
data: web::Form<LoginUser>,
config: web::Data<crate::ServerConfig>,
id: Identity,
) -> Result<HttpResponse, ServerError> {
let user = queries::get_user_by_name(&data.username, &config).await;
match user {
Ok(u) => {
if let Some(hash) = u.password.secret {
let secret = &config.secret;
let valid = Verifier::default()
.with_hash(hash)
.with_password(&data.password)
.with_secret_key(secret.secret.as_ref().expect("No secret available"))
.verify()?;
if valid {
info!("Log-in of user: {}", &u.username);
let session_token = u.username;
id.remember(session_token);
Ok(redirect_builder("/admin/index/"))
} else {
Ok(redirect_builder("/admin/login/"))
}
} else {
Ok(HttpResponse::Unauthorized().json2(&Status::Error(Message {
message: "Failed to Login".to_string(),
})))
}
}
Err(e) => {
info!("Failed to login: {}", e);
Ok(HttpResponse::Unauthorized().json2(&Status::Error(Message {
message: "Failed to Login".to_string(),
})))
}
}
}
#[instrument(skip(id))]
pub async fn process_login_json(
data: web::Json<LoginUser>,
@@ -559,16 +259,8 @@ pub async fn process_login_json(
}
}
#[instrument(skip(id))]
pub async fn logout(id: Identity) -> Result<HttpResponse, ServerError> {
info!("Logging out the user");
id.forget();
Ok(redirect_builder("/admin/login/"))
}
#[instrument(skip(tera))]
#[instrument()]
pub async fn redirect(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
data: web::Path<String>,
req: HttpRequest,
@@ -586,12 +278,23 @@ pub async fn redirect(
"Link was not found: http://{}/{} \n {}",
&config.public_url, &data.0, e
);
let mut data = Context::new();
data.insert("title", "Wurde gel\u{f6}scht");
let language = detect_language(&req).unwrap_or_else(|_| "en".to_string());
data.insert("language", &language);
let rendered = tera.render("not_found.html", &data)?;
Ok(HttpResponse::NotFound().body(rendered))
Ok(HttpResponse::NotFound().body(
r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<meta name="author" content="Franz Dietrich">
<meta http-equiv="robots" content="[noindex|nofollow]">
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="content">
This link was either deleted or does not exist.
</div>
</body>
</html>"#,
))
}
Err(e) => Err(e),
}
@@ -634,82 +337,6 @@ pub async fn process_update_link_json(
}
}
#[instrument(skip(id))]
pub async fn create_link(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
id: Identity,
) -> Result<HttpResponse, ServerError> {
match queries::authenticate(&id, &config).await? {
queries::Role::Admin { user } | queries::Role::Regular { user } => {
let mut data = Context::new();
data.insert("title", "Einen Kurzlink erstellen");
data.insert("user", &user);
let rendered = tera.render("submission.html", &data)?;
Ok(HttpResponse::Ok().body(rendered))
}
queries::Role::NotAuthenticated | queries::Role::Disabled => {
Ok(redirect_builder("/admin/login/"))
}
}
}
#[instrument(skip(id))]
pub async fn process_link_creation(
data: web::Form<LinkForm>,
config: web::Data<crate::ServerConfig>,
id: Identity,
) -> Result<HttpResponse, ServerError> {
let new_link = queries::create_link(&id, data, &config).await?;
Ok(redirect_builder(&format!(
"/admin/view/link/{}",
new_link.item.code
)))
}
#[instrument(skip(id))]
pub async fn edit_link(
tera: web::Data<Tera>,
config: web::Data<crate::ServerConfig>,
id: Identity,
link_id: web::Path<String>,
) -> Result<HttpResponse, ServerError> {
if let Ok(query) = queries::get_link(&id, &link_id.0, &config).await {
let mut data = Context::new();
data.insert("title", "Submit a Post");
data.insert("link", &query.item);
data.insert("user", &query.user);
let rendered = tera.render("edit_link.html", &data)?;
return Ok(HttpResponse::Ok().body(rendered));
}
Ok(redirect_builder("/admin/login/"))
}
pub async fn process_link_edit(
data: web::Form<LinkForm>,
config: web::Data<crate::ServerConfig>,
id: Identity,
link_code: web::Path<String>,
) -> Result<HttpResponse, ServerError> {
match queries::update_link(&id, &link_code.0, data, &config).await {
Ok(query) => Ok(redirect_builder(&format!(
"/admin/view/link/{}",
&query.item.code
))),
Err(e) => Err(e),
}
}
#[instrument(skip(id))]
pub async fn process_link_delete(
id: Identity,
config: web::Data<crate::ServerConfig>,
link_code: web::Path<String>,
) -> Result<HttpResponse, ServerError> {
queries::delete_link(&id, &link_code.0, &config).await?;
Ok(redirect_builder("/admin/login/"))
}
#[instrument(skip(id))]
pub async fn process_delete_link_json(
id: Identity,