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 {