diff --git a/admin_app/auth/index.html b/admin_app/auth/index.html new file mode 100644 index 0000000000000000000000000000000000000000..b40b6eafc63919da11b5e9ad889a43d0966c5945 --- /dev/null +++ b/admin_app/auth/index.html @@ -0,0 +1,15 @@ +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Krustacea | Admin dashboard</title> +</head> + +<body> + ADMIN Dahsboard +</body> +<script src='/assets/default/admin.js'></script> + +</html> \ No newline at end of file diff --git a/admin_app/login/index.html b/admin_app/login/index.html new file mode 100644 index 0000000000000000000000000000000000000000..8ebf4040b0991f6c4f24d84be47652aa5e6dbcbf --- /dev/null +++ b/admin_app/login/index.html @@ -0,0 +1,26 @@ +<html lang='en' prefix='og: https://ogp.me/ns#'> + +<head> + <meta charset='UTF-8'> + <meta http-equiv='X-UA-Compatible' content='IE=edge'> + <meta name='viewport' content='width=device-width, initial-scale=1.0'> + <title>Krustacea - Admin Login</title> + <link rel='stylesheet' href='/assets/default/admin.css'> +</head> + +<body> + <form id='admin-login-form'> + <div> + <label for='username'>Admin Id</label> + <input type='text' name='username' /> + </div> + <div> + <label for='password'>Password</label> + <input type='password' name='password' /> + </div> + <input type='submit' /> + </form> +</body> +<script src='/assets/default/admin.js'></script> + +</html> \ No newline at end of file diff --git a/default_static/admin.js b/default_static/admin.js index 065f3cd0d05b7661f0b25a4b30f2910918436262..0b482136fb0f42419a456398d53579c2f885f08d 100644 --- a/default_static/admin.js +++ b/default_static/admin.js @@ -3,7 +3,7 @@ document.getElementById('admin-login-form').onsubmit = function (e) { fetch('/admin/login', { method: 'POST', body: new URLSearchParams(new FormData(e.target)) }).then(res => { if (res.status >= 200 && res.status < 400) { console.log(res) - window.location = '/admin/auth/workspace'; + window.location = '/admin_app/auth/'; } }).catch(err => console.log(err)) diff --git a/default_views/401.html b/default_views/401.html new file mode 100644 index 0000000000000000000000000000000000000000..529b461607f06cf0de275cbcc6517b59319b5577 --- /dev/null +++ b/default_views/401.html @@ -0,0 +1,18 @@ +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Krustacea | Unauthorized</title> +</head> + +<body> + <h1>Error 401 - Unauthorized</h1> + <p> + Please <a href='/admin_app/login/'>login</a> before accessing this page. + </p> + +</body> + +</html> \ No newline at end of file diff --git a/default_views/404.html b/default_views/404.html new file mode 100644 index 0000000000000000000000000000000000000000..25aad06c2825677378176a94e58df2739f3554ae --- /dev/null +++ b/default_views/404.html @@ -0,0 +1,15 @@ +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Krustacea | Not Found</title> +</head> + +<body> + <h1>Page Not Found</h1> + <a href="/">Back to home</a> +</body> + +</html> \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 66b5818fc0ce8fddcf3f7d896547cde3fc62bd90..57fe5376ddac312497ab4b38d36eebf2e6a00835 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,6 +43,7 @@ async fn main() -> std::io::Result<()> { let srv_conf = tls_config(&app_state.config); let dir = website.static_files_manager.dir.clone(); + let auth_service = std::sync::Arc::new(AuthService {}); HttpServer::new(move || { App::new() @@ -51,14 +52,22 @@ async fn main() -> std::io::Result<()> { .wrap(RedirectHttps::default().to_port(port_tls)) .app_data(web::Data::new(RwLock::new(app_state.clone()))) .app_data(web::Data::new(RwLock::new(website.clone()))) + .service( + web::scope("/admin_app") + .service( + web::scope("/auth").wrap(auth_service.clone()).service( + Files::new("/", &dir.join("admin_app").join("auth")) + .index_file("index.html"), + ), + ) + .service(Files::new("/", &dir.join("admin_app")).index_file("index.html")), + ) .service( web::scope("/admin") - .service(service::admin_login) .service(service::admin_authenticate) .service( web::scope("/auth") - .wrap(AuthService {}) - .service(service::admin_workspace) + .wrap(auth_service.clone()) .service(service::add_page) .service(service::get_page_data) .service(service::update_page) @@ -72,6 +81,7 @@ async fn main() -> std::io::Result<()> { ) .service(service::files::favicon) .service(Files::new("/", &dir).index_file("index.html")) + .default_service(web::to(service::not_found)) }) .bind(format!("{}:{}", host, port))? .bind_rustls(format!("{}:{}", host, port_tls), srv_conf)? diff --git a/src/middleware/authentication.rs b/src/middleware/authentication.rs index faf95886e7b7f7224395b9d01ad137cc0c3976ba..66473fdb3149c0d16e587e128ad1b6ab4be7fdf9 100644 --- a/src/middleware/authentication.rs +++ b/src/middleware/authentication.rs @@ -1,4 +1,4 @@ -use crate::{app::AdminAuthToken, AppState}; +use crate::{app::AdminAuthToken, website::WebSite, AppState}; use actix_web::{ body::{EitherBody, MessageBody}, dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, @@ -66,9 +66,19 @@ where Box::pin(async move { let mut req = req; if let false = authenticate(&mut req, &token).await { + let f401 = req + .app_data::<web::Data<RwLock<WebSite>>>() + .expect("Failed to extract WebSite from ServiceRequest") + .read() + .expect("Failed to read WebSite") + .static_files_manager + .dir + .join("default_views") + .join("401.html"); + return Ok(req.into_response( HttpResponse::Unauthorized() - .body("<html><body>Error 401 - Unauthorized - Please go to <a href='/admin/login'>login page</a>.</body></html>") // TODO a proper 401 view ? + .body(std::fs::read_to_string(f401)?) .map_into_right_body(), )); } diff --git a/src/service/admin.rs b/src/service/admin.rs index 76fe7a2f1dd1b822d826b811891e284134f4b2f3..325278254a886bc51921bb3cc4238a1f3b3f3a4d 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1,17 +1,11 @@ use crate::AppState; use actix_web::{ - get, post, + post, web::{Data, Form}, HttpRequest, HttpResponse, }; use std::sync::RwLock; -#[get("/workspace")] -async fn admin_workspace() -> HttpResponse { - // TODO return admin static web view with js application - actix_web::HttpResponse::Ok().body("Welcome Admin") -} - #[derive(serde::Deserialize)] struct Credentials { username: String, @@ -55,36 +49,3 @@ async fn admin_authenticate( } } } - -#[get("/login")] -pub async fn admin_login() -> HttpResponse { - // TODO create a module with built-in admin static views - HttpResponse::Ok().body( - " -<html lang='en' prefix='og: https://ogp.me/ns#'> -<head> - <meta charset='UTF-8'> - <meta http-equiv='X-UA-Compatible' content='IE=edge'> - <meta name='viewport' content='width=device-width, initial-scale=1.0'> - <title>Krutacea - Admin Login</title> - <link rel='stylesheet' href='/assets/default/admin.css'> -</head> - -<body> - <form id='admin-login-form'> - <div> - <label for='username'>Admin Id</label> - <input type='text' name='username'/> - </div> - <div> - <label for='password'>Password</label> - <input type='password' name='password' /> - </div> - <input type='submit' /> - </form> -</body> -<script src='/assets/default/admin.js'></script> -</html> -", - ) -} diff --git a/src/service/default.rs b/src/service/default.rs new file mode 100644 index 0000000000000000000000000000000000000000..ccab01c06da8edc5a519c00eb587be56d9dfcc94 --- /dev/null +++ b/src/service/default.rs @@ -0,0 +1,15 @@ +use crate::website::WebSite; +use actix_web::{web::Data, HttpResponse, Responder}; +use std::sync::RwLock; + +pub async fn not_found(website: Data<RwLock<WebSite>>) -> impl Responder { + let f404 = website + .read() + .expect("Failed to read WebSite") + .static_files_manager + .dir + .join("default_views") + .join("404.html"); + + HttpResponse::NotFound().body(std::fs::read_to_string(f404).unwrap()) +} diff --git a/src/service/files.rs b/src/service/files.rs index e55c419be941e3904cf66f8824b1024041dd8a83..a46ed06c6e2697bb95011e50f37e9be617f0049a 100644 --- a/src/service/files.rs +++ b/src/service/files.rs @@ -22,6 +22,7 @@ pub async fn favicon(website: web::Data<RwLock<WebSite>>) -> impl Responder { .unwrap() .static_files_manager .dir + .join("assets") .join("default") .join("favicon.ico"), ) diff --git a/src/service/mod.rs b/src/service/mod.rs index 18a7bf114a2cfc700b076b7cf847efb25c7f166d..57f77d23da26f868a7333ebb7f1ca86179fc729c 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -1,5 +1,7 @@ mod admin; +mod default; pub mod files; mod website; pub use admin::*; +pub use default::*; pub use website::*; diff --git a/src/static_files/static_files.rs b/src/static_files/static_files.rs index ae8c6e66d271503621f5662116db1fd58e8707e5..6f3d3dfa78cc2c19ab8a98d5692de738fe81837a 100644 --- a/src/static_files/static_files.rs +++ b/src/static_files/static_files.rs @@ -59,7 +59,21 @@ impl StaticFilesManager { if let Err(err) = Self::copy_default_files(&dir) { return Err(format!( - "Error copying StaticFilesManager default assets directory- {}", + "Error copying StaticFilesManager default assets directory - {}", + err + )); + } + + if let Err(err) = Self::copy_admin_web_app(&dir) { + return Err(format!( + "Error copying StaticFilesManager admin_web_app directory - {}", + err + )); + } + + if let Err(err) = Self::copy_default_views(&dir) { + return Err(format!( + "Error copying StaticFilesManager defult_views directory - {}", err )); } @@ -97,6 +111,32 @@ impl StaticFilesManager { } } + fn copy_admin_web_app(static_dir: &PathBuf) -> Result<(), String> { + let local_admin_app_dir = std::env::current_dir().unwrap().join("admin_app"); + let dest = static_dir.join("admin_app"); + let mut cpy_options = fs_extra::dir::CopyOptions::new(); + cpy_options.content_only = true; + cpy_options.overwrite = true; + + match fs_extra::dir::copy(local_admin_app_dir, dest, &cpy_options) { + Err(err) => Err(format!("{}", err)), + Ok(_) => Ok(()), + } + } + + fn copy_default_views(static_dir: &PathBuf) -> Result<(), String> { + let local_admin_app_dir = std::env::current_dir().unwrap().join("default_views"); + let dest = static_dir.join("default_views"); + let mut cpy_options = fs_extra::dir::CopyOptions::new(); + cpy_options.content_only = true; + cpy_options.overwrite = true; + + match fs_extra::dir::copy(local_admin_app_dir, dest, &cpy_options) { + Err(err) => Err(format!("{}", err)), + Ok(_) => Ok(()), + } + } + fn rec_read_dir(&mut self, root: &Path, strip_from: &Path) { for entry in root.read_dir().unwrap() { if let Ok(entry) = entry {