Added auth all across admin
This commit is contained in:
parent
7177823b14
commit
5acf61c4f8
@ -16,16 +16,26 @@ use jl_types::{
|
||||
project::{NewProjectPayload, UpdateProjectPayload},
|
||||
unit::{UpdateUnitPayload, NewUnitPayload},
|
||||
},
|
||||
project_card::ProjectCardDto,
|
||||
project_card::ProjectCardDto, auth::AuthDto,
|
||||
},
|
||||
};
|
||||
use reqwest::Method;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::utils::admin_panel;
|
||||
|
||||
use super::base::{perform_multipart_request_without_client, perform_request_without_client};
|
||||
|
||||
const BASE_URL: &str = "http://localhost:8095/";
|
||||
|
||||
pub fn get_auth_header() -> (String, String) {
|
||||
let token = admin_panel::get_admin_token_from_storage();
|
||||
match token {
|
||||
Some(token) => (String::from("auth_token"), token),
|
||||
None => (String::from("noauth"), String::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_all_cities() -> Result<HashSet<String>, err::Error> {
|
||||
perform_request_without_client::<String, HashSet<String>>(
|
||||
BASE_URL.into(),
|
||||
@ -145,7 +155,7 @@ pub async fn get_all_page_visits_count() -> Result<Count, err::Error> {
|
||||
format!("admin/visits/count"),
|
||||
None,
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -158,7 +168,7 @@ pub async fn get_all_contacts_count() -> Result<Count, err::Error> {
|
||||
format!("admin/contacts/count"),
|
||||
None,
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -171,7 +181,7 @@ pub async fn get_all_contacts() -> Result<Vec<Contact>, err::Error> {
|
||||
format!("admin/contacts"),
|
||||
None,
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -213,7 +223,7 @@ pub async fn create_new_agent(agent: NewAgentPayload) -> Result<Agent, err::Erro
|
||||
format!("admin/agent"),
|
||||
Some(agent),
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -226,7 +236,7 @@ pub async fn create_new_unit(unit: NewUnitPayload) -> Result<Unit, err::Error> {
|
||||
format!("admin/unit"),
|
||||
Some(unit),
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -239,7 +249,7 @@ pub async fn create_new_project(project: NewProjectPayload) -> Result<Project, e
|
||||
format!("admin/project"),
|
||||
Some(project),
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -252,7 +262,7 @@ pub async fn create_location(location: NewLocationPayload) -> Result<Location, e
|
||||
format!("admin/location"),
|
||||
Some(location),
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -264,7 +274,7 @@ pub async fn update_agent(agent: UpdateAgentPayload) -> Result<Agent, err::Error
|
||||
format!("admin/agent"),
|
||||
Some(agent),
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -277,7 +287,7 @@ pub async fn update_project(project: UpdateProjectPayload) -> Result<Project, er
|
||||
format!("admin/project"),
|
||||
Some(project),
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -290,7 +300,7 @@ pub async fn update_unit(unit: UpdateUnitPayload) -> Result<Unit, err::Error> {
|
||||
format!("admin/unit"),
|
||||
Some(unit),
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -303,7 +313,7 @@ pub async fn delete_project(project_id: &Uuid) -> Result<(), err::Error> {
|
||||
format!("admin/project/{project_id}"),
|
||||
None,
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -316,7 +326,7 @@ pub async fn delete_agent(agent_id: &Uuid) -> Result<(), err::Error> {
|
||||
format!("admin/agent/{agent_id}"),
|
||||
None,
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -329,7 +339,7 @@ pub async fn delete_unit(unit_id: &Uuid) -> Result<(), err::Error> {
|
||||
format!("admin/unit/{unit_id}"),
|
||||
None,
|
||||
200,
|
||||
Vec::new(),
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -342,6 +352,19 @@ pub async fn upload_image(item: Item, body: Vec<u8>) -> Result<String, err::Erro
|
||||
format!("admin/images/{item}"),
|
||||
body,
|
||||
200,
|
||||
Vec::from([get_auth_header()]),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn authenticate(auth_dto: AuthDto) -> Result<String, err::Error> {
|
||||
perform_request_without_client(
|
||||
BASE_URL.into(),
|
||||
Method::POST,
|
||||
format!("admin/auth"),
|
||||
Some(auth_dto),
|
||||
200,
|
||||
Vec::new(),
|
||||
None,
|
||||
)
|
||||
|
@ -1,15 +1,19 @@
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::use_navigator;
|
||||
|
||||
use crate::{
|
||||
api::backend::get_all_agents,
|
||||
components::{
|
||||
admin_agent::AdminAgent, admin_nav_bar::AdminNavigationBar, new_widget::NewThingWidget,
|
||||
},
|
||||
pages::admin::edit::EditItem,
|
||||
pages::admin::edit::EditItem, auth_present,
|
||||
};
|
||||
|
||||
#[function_component(AdminAgents)]
|
||||
pub fn admin_agents() -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
auth_present!(navigator);
|
||||
|
||||
let agents = use_state(|| Vec::new());
|
||||
|
||||
use_state(|| {
|
||||
|
@ -1,9 +1,12 @@
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::use_navigator;
|
||||
|
||||
use crate::{api::backend::get_all_contacts, components::admin_nav_bar::AdminNavigationBar};
|
||||
use crate::{api::backend::get_all_contacts, components::admin_nav_bar::AdminNavigationBar, auth_present};
|
||||
|
||||
#[function_component(AdminContacts)]
|
||||
pub fn admin_contacts() -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
auth_present!(navigator);
|
||||
let contacts = use_state(|| Vec::new());
|
||||
|
||||
use_state(|| {
|
||||
|
@ -7,13 +7,14 @@ use yew_router::prelude::use_navigator;
|
||||
use crate::{
|
||||
api::backend::{get_agent_with_id, get_project_listing},
|
||||
components::admin_nav_bar::AdminNavigationBar,
|
||||
pages::admin::fields::{agent::AgentFields, project::ProjectFields, unit::UnitFields},
|
||||
pages::admin::fields::{agent::AgentFields, project::ProjectFields, unit::UnitFields}, auth_present,
|
||||
};
|
||||
|
||||
/// All of the editing actions of the admin panel will lead to here. This should take an id of anything. A unit, a project, an agent. And its corresponding ID.
|
||||
#[function_component(AdminEditPage)]
|
||||
pub fn edit_page(props: &AdminEditPageProps) -> Html {
|
||||
let _navigator = use_navigator().unwrap();
|
||||
let navigator = use_navigator().unwrap();
|
||||
auth_present!(navigator);
|
||||
let listing = use_state(|| None);
|
||||
let agent = use_state(|| None);
|
||||
use_state(|| {
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||
dropdown::DropDown, single_media_picker::SingleMediaPicker, textfield::TextField,
|
||||
},
|
||||
pages::admin::edit::EditType,
|
||||
routes::main_router::Route,
|
||||
routes::main_router::Route, auth_present,
|
||||
};
|
||||
|
||||
#[derive(Properties, PartialEq, Clone)]
|
||||
@ -23,6 +23,7 @@ pub struct AgentFieldsProps {
|
||||
#[function_component(AgentFields)]
|
||||
pub fn agent_fields(props: &AgentFieldsProps) -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
auth_present!(navigator);
|
||||
let user_typed = use_state(|| false);
|
||||
let agent_opt = props.agent.clone();
|
||||
|
||||
|
@ -32,7 +32,7 @@ use crate::{
|
||||
edit::{EditItem, EditType},
|
||||
units::AdminUnits,
|
||||
},
|
||||
routes::main_router::Route,
|
||||
routes::main_router::Route, auth_present,
|
||||
};
|
||||
|
||||
#[derive(Properties, PartialEq, Clone)]
|
||||
@ -44,6 +44,7 @@ pub struct ProjectFieldsProps {
|
||||
#[function_component(ProjectFields)]
|
||||
pub fn generate_fields_for_project(props: &ProjectFieldsProps) -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
auth_present!(navigator);
|
||||
let user_typed = use_state(|| false);
|
||||
let listing_opt = props.listing.clone();
|
||||
|
||||
|
@ -5,7 +5,7 @@ use yew_router::prelude::use_navigator;
|
||||
|
||||
use crate::{
|
||||
components::{dropdown::DropDown, number_textfield::NumberTextField, media_picker::MediaPicker, textfield::{TextField, TextFieldType}},
|
||||
pages::admin::edit::EditType, api::backend::{get_unit_with_id, create_new_unit, update_unit}, routes::main_router::Route,
|
||||
pages::admin::edit::EditType, api::backend::{get_unit_with_id, create_new_unit, update_unit}, routes::main_router::Route, auth_present,
|
||||
};
|
||||
|
||||
#[derive(Properties, PartialEq, Clone)]
|
||||
@ -38,6 +38,7 @@ pub fn unit_fields(props: &UnitFieldsProps) -> Html {
|
||||
|
||||
|
||||
let navigator = use_navigator().unwrap();
|
||||
auth_present!(navigator);
|
||||
let user_typed = use_state(|| false);
|
||||
let edit_type = props.edittype.clone();
|
||||
let project_id = props.projectid.clone();
|
||||
|
@ -1,15 +1,23 @@
|
||||
use jl_types::dto::auth::AuthDto;
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::use_navigator;
|
||||
|
||||
use crate::{routes::main_router::Route, utils::input::get_value_from_input_event};
|
||||
use crate::{routes::main_router::Route, utils::{input::get_value_from_input_event, admin_panel::{self, save_admin_token}}, api::backend::authenticate};
|
||||
|
||||
#[function_component(AdminLoginPage)]
|
||||
pub fn login_page() -> Html {
|
||||
// TODO: If logged in go to start
|
||||
let navigator = use_navigator().unwrap();
|
||||
let error = use_state(|| false);
|
||||
let username = use_state(|| String::new());
|
||||
let password = use_state(|| String::new());
|
||||
let saved_token = use_state(|| admin_panel::get_admin_token_from_storage());
|
||||
|
||||
match (*saved_token).clone() {
|
||||
Some(_) => {
|
||||
navigator.push(&Route::AdminStart);
|
||||
},
|
||||
None => {}
|
||||
};
|
||||
|
||||
let on_username_input_changed = {
|
||||
let username = username.clone();
|
||||
@ -25,14 +33,24 @@ pub fn login_page() -> Html {
|
||||
};
|
||||
let onclick = {
|
||||
let error = error.clone();
|
||||
|
||||
let username = username.clone();
|
||||
let password = password.clone();
|
||||
Callback::from(move |_| {
|
||||
let error = error.clone();
|
||||
let navigator = navigator.clone();
|
||||
let username = username.clone();
|
||||
let password = password.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
error.set(false);
|
||||
// Call backend login
|
||||
navigator.push(&Route::AdminStart);
|
||||
match authenticate(AuthDto { email: (*username).clone(), password: (*password).clone() }).await {
|
||||
Ok(token) => {
|
||||
save_admin_token(token);
|
||||
navigator.push(&Route::AdminStart);
|
||||
},
|
||||
Err(error_message) => {
|
||||
log::error!("Incorrect user/pass...: {error_message}");
|
||||
error.set(true);
|
||||
}
|
||||
};
|
||||
});
|
||||
})
|
||||
};
|
||||
|
@ -1,20 +1,24 @@
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::use_navigator;
|
||||
|
||||
use crate::{
|
||||
api::backend::get_all_projects_with_filters_paged,
|
||||
components::{
|
||||
admin_nav_bar::AdminNavigationBar, admin_project::AdminProject, new_widget::NewThingWidget,
|
||||
},
|
||||
pages::admin::edit::EditItem,
|
||||
pages::admin::edit::EditItem, authed_call, auth_present,
|
||||
};
|
||||
|
||||
#[function_component(AdminProjects)]
|
||||
pub fn admin_projects() -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
auth_present!(navigator);
|
||||
let projects = use_state(|| Vec::new());
|
||||
use_state(|| {
|
||||
let navigator = navigator.clone();
|
||||
let projects_handle = projects.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match get_all_projects_with_filters_paged(&1, Vec::new()).await {
|
||||
match authed_call!(get_all_projects_with_filters_paged(&1, Vec::new()), navigator) {
|
||||
Ok(projects) => {
|
||||
projects_handle.set(projects);
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::use_navigator;
|
||||
|
||||
use crate::components::admin_nav_bar::AdminNavigationBar;
|
||||
use crate::{components::admin_nav_bar::AdminNavigationBar, auth_present};
|
||||
|
||||
#[function_component(AdminStart)]
|
||||
pub fn admin_start() -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
auth_present!(navigator);
|
||||
html! {
|
||||
<>
|
||||
<AdminNavigationBar/>
|
||||
|
@ -1,10 +1,13 @@
|
||||
use jl_types::domain::unit::Unit;
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::use_navigator;
|
||||
|
||||
use crate::components::admin_unit::AdminUnit;
|
||||
use crate::{components::admin_unit::AdminUnit, auth_present};
|
||||
|
||||
#[function_component(AdminUnits)]
|
||||
pub fn admin_units(props: &AdminUnitProps) -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
auth_present!(navigator);
|
||||
let units_handle = props.units.clone();
|
||||
html! {
|
||||
<div class={"admin-start-container"} style={"min-height: 10vh; margin-top: 10vh;"}>
|
||||
|
@ -1,13 +1,35 @@
|
||||
use super::storage;
|
||||
|
||||
pub fn get_admin_token_from_storage() -> Option<String> {
|
||||
match storage::get_from_local_storage(storage::StorageKey::AdminUser) {
|
||||
match storage::get_from_local_storage(storage::StorageKey::AdminToken) {
|
||||
Ok(opt) => opt,
|
||||
Err(_) => {
|
||||
log::error!(
|
||||
"No user stored when attempting to use admin panel. Redirect to admin panel."
|
||||
"No user stored when attempting to use admin panel. Redirect to admin authentication."
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_admin_token(token: String) {
|
||||
match storage::store_in_local_storage(storage::StorageKey::AdminToken, &token) {
|
||||
Ok(_) => {},
|
||||
Err(_) => {
|
||||
log::error!(
|
||||
"Something wrong happened while storing token."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_admin_token() {
|
||||
match storage::delete_from_local_storage(storage::StorageKey::AdminToken) {
|
||||
Ok(_) => {},
|
||||
Err(_) => {
|
||||
log::error!(
|
||||
"Something wrong happened while deleting token."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
35
src/utils/macros.rs
Normal file
35
src/utils/macros.rs
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
/// This macro calls the api call you give it, then if it's a 401 unauthorized it will navigate you to the admin panel login.
|
||||
#[macro_export]
|
||||
macro_rules! authed_call {
|
||||
($e:expr, $navigator:expr) => {
|
||||
match $e.await {
|
||||
Ok(value) => Ok(value),
|
||||
Err(error) => {
|
||||
log::error!("Error happened inside auth call. {error}");
|
||||
match error.clone() {
|
||||
err::Error::UnexpectedStatusCode(_, actual, _) => {
|
||||
if actual == 401 {
|
||||
$navigator.push(&crate::routes::main_router::Route::Admin);
|
||||
}
|
||||
Err(error)
|
||||
},
|
||||
_ => Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This macro checks if there's an authtoken present in localstorage. If not, send user back to login.
|
||||
#[macro_export]
|
||||
macro_rules! auth_present {
|
||||
($navigator:expr) => {
|
||||
match crate::utils::admin_panel::get_admin_token_from_storage() {
|
||||
Some(_) => {},
|
||||
None => $navigator.push(&crate::routes::main_router::Route::Admin),
|
||||
};
|
||||
|
||||
};
|
||||
}
|
@ -2,3 +2,4 @@ pub mod admin_panel;
|
||||
pub mod get_value;
|
||||
pub mod input;
|
||||
pub mod storage;
|
||||
pub mod macros;
|
@ -5,13 +5,14 @@ use web_sys::window;
|
||||
|
||||
pub enum StorageKey {
|
||||
AgentShortcode,
|
||||
AdminUser,
|
||||
AdminToken,
|
||||
|
||||
}
|
||||
impl Display for StorageKey {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
StorageKey::AgentShortcode => write!(f, "agentshortcode"),
|
||||
StorageKey::AdminUser => write!(f, "adminuser"),
|
||||
StorageKey::AdminToken => write!(f, "adminuser"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,3 +54,19 @@ pub fn get_from_local_storage(key: StorageKey) -> Result<Option<String>, Error>
|
||||
None => Err(Error::IO(MessageResource::new_from_string(format!("Error accessing Window object.")))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_from_local_storage(key: StorageKey) -> Result<(), Error> {
|
||||
match window() {
|
||||
Some(window) => match window.local_storage() {
|
||||
Ok(local_storage_opt) => match local_storage_opt {
|
||||
Some(local_storage) => match local_storage.remove_item(key.to_string().as_str()) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(js_err) => Err(Error::IO(MessageResource::new_from_string(format!("JsValue: {:#?}", js_err.as_string())))),
|
||||
},
|
||||
None => Err(Error::IO(MessageResource::new_from_str("Error accessing local storage instance from window object. Result was fine, option came back as none."))),
|
||||
},
|
||||
Err(e) => Err(Error::IO(MessageResource::new_from_string(format!("Error accessing local storage instance from window object. Resulting error: {:#?}", e.as_string())))),
|
||||
},
|
||||
None => Err(Error::IO(MessageResource::new_from_string(format!("Error accessing Window object.")))),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user