From 467f7c56b486925cd452200ca2f91e0554fcaf1a Mon Sep 17 00:00:00 2001 From: thevickypedia Date: Fri, 16 Feb 2024 19:18:26 -0600 Subject: [PATCH] Use `PathBuf` to load all HTML files in Jinja environment Pass Jinja template as Mutex via app data to all endpoints --- Cargo.toml | 2 +- src/constant.rs | 37 --------------------- src/jinja.rs | 40 +++++++++++++++++++++++ src/lib.rs | 7 ++-- src/routes/auth.rs | 27 ++++++++------- src/routes/basics.rs | 7 ++-- src/routes/video.rs | 7 ++-- src/template.rs | 37 --------------------- src/templates/{land.html => landing.html} | 0 src/templates/{list.html => listing.html} | 0 10 files changed, 68 insertions(+), 96 deletions(-) create mode 100644 src/jinja.rs delete mode 100644 src/template.rs rename src/templates/{land.html => landing.html} (100%) rename src/templates/{list.html => listing.html} (100%) diff --git a/Cargo.toml b/Cargo.toml index 0cbdd15..8d3e1b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ base64 = "0.21.7" sha2 = "0.10.8" rand = "0.8.5" fernet = "0.2.1" -minijinja = "1.0.12" +minijinja = { version = "1.0.12", features = ["loader"] } pyo3 = { version = "0.20.2", features = ["auto-initialize"] } url = "2.5.0" itertools = "0.12.1" diff --git a/src/constant.rs b/src/constant.rs index b7ffa33..d93a14a 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -4,9 +4,6 @@ use std::sync::Mutex; use fernet::Fernet; use lazy_static::lazy_static; -use minijinja::Environment; - -use crate::template; pub fn get_binary() -> String { let binary = env::args().next().unwrap(); @@ -65,40 +62,6 @@ fn generate_key() -> String { Fernet::generate_key() } -// fn get_landing(filename: &str) -> String { -// let filepath = Path::new(env!("CARGO_MANIFEST_DIR")) -// .join("src") -// .join("templates") -// .join(filename) -// .into_os_string().into_string().unwrap(); -// std::fs::read_to_string(&filepath).unwrap_or_else(|_| String::new()) -// } -// -// pub fn jinja_template() -> Mutex> { -// let mut env = Environment::new(); -// for html in ["landing", "listing", "logout", "session", "unauthorized"] { -// let extract = Path::new(env!("CARGO_MANIFEST_DIR")) -// .join("src") -// .join("templates") -// .join(format!("{}.html", html)) -// .into_os_string().into_string().unwrap(); -// env.add_template(&html, &std::fs::read_to_string(&extract).unwrap_or_else(|_| String::new())).unwrap(); -// } -// let mutex = Mutex::new(env.to_owned()); -// mutex -// } - -lazy_static! { - pub static ref ENV: Mutex> = Mutex::new({ - let mut env = Environment::new(); - env.add_template("landing", template::LANDING).unwrap(); - env.add_template("listing", template::LISTING).unwrap(); - env.add_template("logout", template::LOGOUT).unwrap(); - env.add_template("session", template::SESSION).unwrap(); - env - }); -} - lazy_static::lazy_static! { pub static ref HOST_SERVE: Mutex> = Mutex::new(HashMap::new()); } diff --git a/src/jinja.rs b/src/jinja.rs new file mode 100644 index 0000000..99af03e --- /dev/null +++ b/src/jinja.rs @@ -0,0 +1,40 @@ +use std::path::Path; +use std::sync::{Arc, Mutex}; + +/// Read the content of each file and return it as a String. +/// +/// # Arguments +/// +/// * `filename` - Filename that has to be read. +/// +/// # Returns +/// +/// String representation of the file content. +pub fn get_content(filename: &str) -> String { + let filepath = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("src") + .join("templates") + .join(format!("{}.html", filename)) + .to_string_lossy() + .to_string(); + std::fs::read_to_string(&filepath).unwrap_or_else(|_| String::new()) +} + +/// Reads all the HTML files in templates directory and loads the content into a Jinja Environment +/// +/// # Rendered files +/// - Index page template that is served as HTML response for the root endpoint. +/// - Landing page template that is served as HTML response while streaming videos. +/// - Listing page template that is served as HTML response after successful authentication. +/// - Logout page template that is served as HTML response when the user decides to end the session. +/// - Session page template that is served as HTML response when invalid/expired session tokens are received. +/// - Unauthorized page template that is served as HTML response after failed authentication. +pub fn environment() -> Arc>> { + let mut env = minijinja::Environment::new(); + for html in ["landing", "listing", "logout", "session"] { + let content = get_content(&html); + env.add_template_owned(html, content).unwrap(); + } + let mutex = Mutex::new(env.to_owned()); + Arc::new(mutex) +} diff --git a/src/lib.rs b/src/lib.rs index 86b4ceb..9bba224 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ use rand::prelude::SliceRandom; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; mod squire; -mod template; +mod jinja; mod constant; mod routes; @@ -39,8 +39,10 @@ pub async fn start() -> io::Result<()> { println!("{}", arts.choose(&mut rand::thread_rng()).unwrap()); let config = squire::startup::get_config(args); + let template = jinja::environment(); // Create a dedicated clone, since it will be used within closure let config_clone = config.clone(); + let template_clone = template.clone(); let host = format!("{}:{}", config.video_host, config.video_port); log::info!("{} [workers:{}] running on http://{} (Press CTRL+C to quit)", cargo.pkg_name, config.workers, host); @@ -51,8 +53,9 @@ pub async fn start() -> io::Result<()> { */ let application = move || { App::new() // Creates a new Actix web application - .wrap(squire::middleware::get_cors(config_clone.website.clone())) .app_data(web::Data::new(config_clone.clone())) + .app_data(web::Data::new(template_clone.clone())) + .wrap(squire::middleware::get_cors(config_clone.website.clone())) .wrap(middleware::Logger::default()) // Adds a default logger middleware to the application .service(routes::basics::health) // Registers a service for handling requests .service(routes::basics::root) diff --git a/src/routes/auth.rs b/src/routes/auth.rs index b2412a6..f6fc677 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -1,14 +1,14 @@ -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use actix_web::{HttpRequest, HttpResponse, web}; use actix_web::cookie::Cookie; use actix_web::cookie::time::{Duration, OffsetDateTime}; use actix_web::http::StatusCode; use itertools::Itertools; -use minijinja::context; +use minijinja::{context, Environment}; use serde::Serialize; -use crate::{constant, routes, squire, template}; +use crate::{constant, routes, squire, jinja}; use crate::routes::authenticator::AuthToken; /// Struct for representing a JSON Response with a redirect URL. @@ -28,7 +28,7 @@ pub struct DetailError { /// # Arguments /// /// * `config` - Configuration data for the application. -/// * `request`: Actix HttpRequest containing information about the incoming request. +/// * `request` - Actix HttpRequest containing information about the incoming request. #[post("/login")] pub async fn login(config: web::Data>, request: HttpRequest) -> HttpResponse { let verified = routes::authenticator::verify_login(&request, &config); @@ -67,12 +67,13 @@ pub async fn login(config: web::Data>, request: Ht /// # Arguments /// /// * `config` - Configuration data for the application. -/// * `request`: Actix HttpRequest containing information about the incoming request. +/// * `request` - Actix HttpRequest containing information about the incoming request. #[get("/logout")] pub async fn logout(config: web::Data>, + environment: web::Data>>>, request: HttpRequest) -> HttpResponse { let host = request.connection_info().host().to_owned(); - let template = constant::ENV.lock().unwrap(); + let template = environment.lock().unwrap(); let logout_template = template.get_template("logout").unwrap(); let mut response = HttpResponse::build(StatusCode::OK); response.content_type("text/html; charset=utf-8"); @@ -115,9 +116,10 @@ pub async fn logout(config: web::Data>, /// # Arguments /// /// * `config` - Configuration data for the application. -/// * `request`: Actix HttpRequest containing information about the incoming request. +/// * `request` - Actix HttpRequest containing information about the incoming request. #[get("/home")] pub async fn home(config: web::Data>, + environment: web::Data>>>, request: HttpRequest) -> HttpResponse { let auth_response = routes::authenticator::verify_token(&request, &config); if !auth_response.ok { @@ -137,7 +139,7 @@ pub async fn home(config: web::Data>, }; let args = (config.video_source.to_string_lossy().to_string(), file_format.unwrap()); let listing_page = squire::fileio::get_all_stream_content(args); - let template = constant::ENV.lock().unwrap(); + let template = environment.lock().unwrap(); let listing = template.get_template("listing").unwrap(); return HttpResponse::build(StatusCode::OK) @@ -151,12 +153,13 @@ pub async fn home(config: web::Data>, /// /// # Arguments /// -/// * `request`: Actix HttpRequest containing information about the incoming request. +/// * `request` - Actix HttpRequest containing information about the incoming request. #[get("/error")] -pub async fn error(request: HttpRequest) -> HttpResponse { +pub async fn error(environment: web::Data>>>, + request: HttpRequest) -> HttpResponse { if let Some(detail) = request.cookie("detail") { log::info!("Error response for /error: {}", detail.value()); - let template = constant::ENV.lock().unwrap(); + let template = environment.lock().unwrap(); let session = template.get_template("session").unwrap(); return HttpResponse::build(StatusCode::OK) .content_type("text/html; charset=utf-8") @@ -166,7 +169,7 @@ pub async fn error(request: HttpRequest) -> HttpResponse { log::info!("Sending unauthorized response for /error"); return HttpResponse::build(StatusCode::OK) .content_type("text/html; charset=utf-8") - .body(template::UNAUTHORIZED); + .body(jinja::get_content("unauthorized")); } /// Constructs an `HttpResponse` for failed `session_token` verification. diff --git a/src/routes/basics.rs b/src/routes/basics.rs index 7e96c70..6e77da1 100644 --- a/src/routes/basics.rs +++ b/src/routes/basics.rs @@ -1,7 +1,7 @@ use actix_web::{HttpRequest, HttpResponse}; use actix_web::http::StatusCode; -use crate::{squire, template}; +use crate::{squire, jinja}; /// Handles the health endpoint, returning a JSON response indicating the server is healthy. /// @@ -24,8 +24,7 @@ pub async fn health() -> HttpResponse { /// /// # Returns /// -/// Returns an `HttpResponse` with a status of 200 (OK), content type "text/html; charset=utf-8", -/// and the body containing HTML content from the `template::INDEX`. +/// Returns an `HttpResponse` with the index page as its body. #[get("/")] pub async fn root(request: HttpRequest) -> HttpResponse { // Log the connection using the squire::logger::log_connection function. @@ -33,5 +32,5 @@ pub async fn root(request: HttpRequest) -> HttpResponse { HttpResponse::build(StatusCode::OK) .content_type("text/html; charset=utf-8") - .body(template::INDEX) + .body(jinja::get_content("index")) } diff --git a/src/routes/video.rs b/src/routes/video.rs index ca35ee9..d56753f 100644 --- a/src/routes/video.rs +++ b/src/routes/video.rs @@ -1,10 +1,10 @@ use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use actix_web::{HttpRequest, HttpResponse, web}; use actix_web::http::StatusCode; use itertools::Itertools; -use minijinja::context; +use minijinja::{context, Environment}; use serde::Deserialize; use url::form_urlencoded; @@ -113,6 +113,7 @@ pub async fn track(config: web::Data>, /// Returns an `HttpResponse` containing the video content or directory listing, or an error response. #[get("/stream/{video_path:.*}")] pub async fn stream(config: web::Data>, + environment: web::Data>>>, request: HttpRequest, video_path: web::Path) -> HttpResponse { let auth_response = routes::authenticator::verify_token(&request, &config); if !auth_response.ok { @@ -131,7 +132,7 @@ pub async fn stream(config: web::Data>, // True path of the video file as a String let __target_str = __target.to_string_lossy().to_string(); let __filename = __target.file_name().unwrap().to_string_lossy().to_string(); - let template = constant::ENV.lock().unwrap(); + let template = environment.lock().unwrap(); if __target.is_file() { let landing = template.get_template("landing").unwrap(); let default_values = squire::settings::default_file_formats(); diff --git a/src/template.rs b/src/template.rs deleted file mode 100644 index f5c3339..0000000 --- a/src/template.rs +++ /dev/null @@ -1,37 +0,0 @@ -// todo: Convert all this to PathBuff, and fix inconsistencies in usage -/// Index page template that is served as HTML response for the root endpoint. -pub static INDEX: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/templates/index.html")); - -/// Landing page template that is served as HTML response while streaming videos. -pub static LANDING: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/templates/land.html")); - -/// Listing page template that is served as HTML response after successful authentication. -pub static LISTING: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/templates/list.html")); - -/// Logout page template that is served as HTML response when the user decides to end the session. -pub static LOGOUT: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/templates/logout.html")); - -/// Session page template that is served as HTML response when invalid/expired session tokens are received. -pub static SESSION: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/templates/session.html")); - -/// Unauthorized page template that is served as HTML response after failed authentication. -pub static UNAUTHORIZED: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/templates/unauthorized.html")); - -// fn template_path(filename: &'static str) -> String { -// PathBuf::new() -// .join(env!("CARGO_MANIFEST_DIR")) -// .join("src") -// .join("templates") -// .join(format!("{}.html", filename)) -// .to_string_lossy() -// .to_string() -// } -// -// pub fn jinja_template() -> Arc>> { -// let mut env = minijinja::Environment::new(); -// for html in ["landing", "listing", "logout", "session", "unauthorized"] { -// env.add_template(&html, &template_path(html)).unwrap(); -// } -// let mutex = Mutex::new(env.to_owned()); -// Arc::new(mutex) -// } diff --git a/src/templates/land.html b/src/templates/landing.html similarity index 100% rename from src/templates/land.html rename to src/templates/landing.html diff --git a/src/templates/list.html b/src/templates/listing.html similarity index 100% rename from src/templates/list.html rename to src/templates/listing.html