2022-05-15 10:30:15 +02:00
|
|
|
use chrono::{NaiveDateTime, TimeZone};
|
|
|
|
use model::{AccessTokenString, RefreshTokenString};
|
|
|
|
use seed::prelude::*;
|
|
|
|
|
2022-05-15 14:05:26 +02:00
|
|
|
use crate::pages::AdminPage;
|
2022-05-15 10:30:15 +02:00
|
|
|
use crate::shared::notification::NotificationMsg;
|
2022-05-16 16:08:14 +02:00
|
|
|
use crate::{Model, Msg, Page, PublicPage};
|
2022-05-15 10:30:15 +02:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum SessionMsg {
|
|
|
|
SessionReceived {
|
|
|
|
access_token: AccessTokenString,
|
|
|
|
refresh_token: RefreshTokenString,
|
|
|
|
exp: NaiveDateTime,
|
|
|
|
},
|
|
|
|
SessionExpired,
|
|
|
|
RefreshToken(RefreshTokenString),
|
|
|
|
TokenRefreshed(crate::api::NetRes<model::api::SessionOutput>),
|
|
|
|
CheckSession,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn init(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|
|
|
orders.stream(streams::interval(500, || {
|
|
|
|
Msg::Session(SessionMsg::CheckSession)
|
|
|
|
}));
|
|
|
|
model.shared.access_token = LocalStorage::get::<_, String>("at")
|
|
|
|
.ok()
|
|
|
|
.map(model::AccessTokenString::new);
|
|
|
|
model.shared.refresh_token = LocalStorage::get::<_, String>("rt")
|
|
|
|
.ok()
|
|
|
|
.map(model::RefreshTokenString::new);
|
|
|
|
model.shared.exp = LocalStorage::get::<_, String>("exp").ok().and_then(|s| {
|
|
|
|
chrono::DateTime::parse_from_rfc3339(&s)
|
|
|
|
.ok()
|
|
|
|
.map(|t| t.naive_utc())
|
2022-05-15 14:05:26 +02:00
|
|
|
});
|
|
|
|
redirect_on_session(model, orders);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn redirect_on_session(model: &Model, orders: &mut impl Orders<Msg>) {
|
2022-05-16 20:29:48 +02:00
|
|
|
// seed::log!(&model.page, model.shared.me.is_some());
|
2022-05-15 14:05:26 +02:00
|
|
|
match &model.page {
|
|
|
|
Page::Admin(admin) => match admin {
|
2022-05-16 20:29:48 +02:00
|
|
|
AdminPage::Landing(_) => {}
|
2022-05-15 14:05:26 +02:00
|
|
|
AdminPage::Dashboard => {}
|
|
|
|
AdminPage::Products => {}
|
|
|
|
AdminPage::Product => {}
|
|
|
|
},
|
|
|
|
Page::Public(public) if model.shared.me.is_some() => match public {
|
|
|
|
PublicPage::SignUp(_) | PublicPage::SignIn(_) => {
|
|
|
|
let url = model.url.clone().set_path(&[] as &[&str]);
|
|
|
|
url.go_and_push();
|
|
|
|
orders.send_msg(crate::Msg::UrlChanged(subs::UrlChanged(url)));
|
|
|
|
orders.force_render_now();
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
},
|
|
|
|
Page::Public(public) => match public {
|
|
|
|
PublicPage::Listing(_) => {}
|
|
|
|
PublicPage::Product(_) => {}
|
|
|
|
PublicPage::SignIn(_) => {}
|
|
|
|
PublicPage::SignUp(_) => {}
|
2022-05-16 16:08:14 +02:00
|
|
|
PublicPage::ShoppingCart(_) => {}
|
2022-05-18 15:40:50 +02:00
|
|
|
PublicPage::Checkout(_) => {}
|
2022-05-15 14:05:26 +02:00
|
|
|
},
|
|
|
|
}
|
2022-05-15 10:30:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn update(msg: SessionMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|
|
|
match msg {
|
|
|
|
SessionMsg::SessionReceived {
|
|
|
|
access_token,
|
|
|
|
refresh_token,
|
|
|
|
exp,
|
|
|
|
} => {
|
|
|
|
LocalStorage::insert("at", access_token.as_str()).ok();
|
|
|
|
LocalStorage::insert("rt", refresh_token.as_str()).ok();
|
|
|
|
let encoded = {
|
|
|
|
let l: chrono::DateTime<chrono::Local> =
|
|
|
|
chrono::Local.from_local_datetime(&exp).unwrap();
|
|
|
|
l.to_rfc3339()
|
|
|
|
};
|
|
|
|
LocalStorage::insert("exp", &encoded).ok();
|
|
|
|
|
|
|
|
model.shared.access_token = Some(access_token);
|
|
|
|
model.shared.refresh_token = Some(refresh_token);
|
|
|
|
model.shared.exp = Some(exp);
|
|
|
|
}
|
|
|
|
SessionMsg::SessionExpired => {
|
|
|
|
LocalStorage::remove("at").ok();
|
|
|
|
LocalStorage::remove("rt").ok();
|
|
|
|
LocalStorage::remove("exp").ok();
|
|
|
|
|
|
|
|
orders.force_render_now();
|
|
|
|
}
|
|
|
|
SessionMsg::CheckSession => {
|
|
|
|
orders.skip();
|
|
|
|
if model.shared.refresh_token.is_none() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if let Some(exp) = model.shared.exp {
|
|
|
|
if exp > chrono::Utc::now().naive_utc() - chrono::Duration::seconds(1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if let Some(token) = model.shared.refresh_token.take() {
|
|
|
|
orders
|
|
|
|
.skip()
|
|
|
|
.send_msg(Msg::Session(SessionMsg::RefreshToken(token)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SessionMsg::RefreshToken(token) => {
|
|
|
|
orders.skip().perform_cmd(async {
|
|
|
|
Msg::Session(SessionMsg::TokenRefreshed(
|
|
|
|
crate::api::public::refresh_token(token).await,
|
|
|
|
))
|
|
|
|
});
|
|
|
|
}
|
|
|
|
SessionMsg::TokenRefreshed(crate::api::NetRes::Success(model::api::SessionOutput {
|
|
|
|
access_token,
|
|
|
|
refresh_token,
|
|
|
|
exp,
|
2022-05-17 16:04:29 +02:00
|
|
|
role: _,
|
2022-05-15 10:30:15 +02:00
|
|
|
})) => {
|
|
|
|
orders
|
|
|
|
.skip()
|
|
|
|
.send_msg(Msg::Session(SessionMsg::SessionReceived {
|
|
|
|
access_token,
|
|
|
|
refresh_token,
|
|
|
|
exp,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
SessionMsg::TokenRefreshed(crate::api::NetRes::Error(model::api::Failure { errors })) => {
|
|
|
|
errors.into_iter().for_each(|msg| {
|
|
|
|
orders
|
|
|
|
.skip()
|
2022-05-15 14:05:26 +02:00
|
|
|
.send_msg(Msg::Shared(crate::shared::SharedMsg::Notification(
|
2022-05-15 10:30:15 +02:00
|
|
|
NotificationMsg::Error(msg),
|
|
|
|
)));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
SessionMsg::TokenRefreshed(crate::api::NetRes::Http(e)) => match e {
|
|
|
|
FetchError::JsonError(json) => {
|
|
|
|
seed::error!("invalid data in response", json);
|
|
|
|
}
|
|
|
|
FetchError::DomException(dom) => {
|
|
|
|
seed::error!("dom", dom);
|
|
|
|
}
|
|
|
|
FetchError::PromiseError(res) => {
|
|
|
|
seed::error!("promise", res);
|
|
|
|
}
|
|
|
|
FetchError::NetworkError(net) => {
|
|
|
|
seed::error!("net", net);
|
|
|
|
}
|
|
|
|
FetchError::RequestError(e) => {
|
2022-05-16 20:29:48 +02:00
|
|
|
seed::error!("request error", e);
|
2022-05-15 10:30:15 +02:00
|
|
|
}
|
|
|
|
FetchError::StatusError(status) => match status.code {
|
|
|
|
401 | 403 => {
|
|
|
|
orders
|
|
|
|
.skip()
|
|
|
|
.send_msg(Msg::Session(SessionMsg::SessionExpired));
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
orders
|
|
|
|
.skip()
|
2022-05-15 14:05:26 +02:00
|
|
|
.send_msg(Msg::Shared(crate::shared::SharedMsg::Notification(
|
2022-05-15 10:30:15 +02:00
|
|
|
NotificationMsg::Error("Request failed".into()),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|