mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-25 22:11:10 +00:00
working login and logout
This commit is contained in:
parent
f41cc01ad3
commit
469ca6f718
9 changed files with 35 additions and 55 deletions
|
@ -57,7 +57,7 @@ uuid = { version = "1.6.1", features = ["serde"] }
|
||||||
tower-http = { version = "0.4.0", features = ["cors", "fs"], optional = true }
|
tower-http = { version = "0.4.0", features = ["cors", "fs"], optional = true }
|
||||||
serde = { version = "1.0.192", features = ["derive"] }
|
serde = { version = "1.0.192", features = ["derive"] }
|
||||||
url = { version = "2.4.1", features = ["serde"] }
|
url = { version = "2.4.1", features = ["serde"] }
|
||||||
reqwest = { version = "0.11.22", features = ["json"] }
|
reqwest = { version = "0.11.22", features = ["json", "cookies"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
|
@ -68,7 +68,6 @@ time = "0.3.31"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
reqwest = { version = "0.11.22", features = ["cookies"] }
|
|
||||||
|
|
||||||
[package.metadata.leptos]
|
[package.metadata.leptos]
|
||||||
output-name = "ibis"
|
output-name = "ibis"
|
||||||
|
|
|
@ -3,4 +3,4 @@ filehash = false
|
||||||
target = "assets/index.html"
|
target = "assets/index.html"
|
||||||
|
|
||||||
[[proxy]]
|
[[proxy]]
|
||||||
backend = "http://[::1]:8131"
|
backend = "http://127.0.0.1:8131"
|
||||||
|
|
|
@ -91,6 +91,7 @@ fn create_cookie(jwt: String, data: &Data<MyDataHandle>) -> Cookie<'static> {
|
||||||
.same_site(SameSite::Strict)
|
.same_site(SameSite::Strict)
|
||||||
.path("/")
|
.path("/")
|
||||||
.http_only(true)
|
.http_only(true)
|
||||||
|
.secure(true)
|
||||||
.expires(Expiration::DateTime(
|
.expires(Expiration::DateTime(
|
||||||
OffsetDateTime::now_utc() + Duration::weeks(52),
|
OffsetDateTime::now_utc() + Duration::weeks(52),
|
||||||
))
|
))
|
||||||
|
|
|
@ -6,13 +6,10 @@ use crate::common::{DbArticle, GetArticleData};
|
||||||
use crate::common::{DbInstance, FollowInstance, InstanceView, SearchArticleData};
|
use crate::common::{DbInstance, FollowInstance, InstanceView, SearchArticleData};
|
||||||
use crate::frontend::error::MyResult;
|
use crate::frontend::error::MyResult;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use reqwest::{Client, RequestBuilder, StatusCode};
|
use reqwest::{Client, RequestBuilder, StatusCode};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub static CLIENT: Lazy<Client> = Lazy::new(Client::new);
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ApiClient {
|
pub struct ApiClient {
|
||||||
client: Client,
|
client: Client,
|
||||||
|
@ -116,8 +113,9 @@ impl ApiClient {
|
||||||
let resolve_form = ResolveObject {
|
let resolve_form = ResolveObject {
|
||||||
id: Url::parse(&format!("http://{}", follow_instance))?,
|
id: Url::parse(&format!("http://{}", follow_instance))?,
|
||||||
};
|
};
|
||||||
let instance_resolved: DbInstance =
|
let instance_resolved: DbInstance = self
|
||||||
get_query(&self.hostname, "instance/resolve", Some(resolve_form)).await?;
|
.get_query("instance/resolve", Some(resolve_form))
|
||||||
|
.await?;
|
||||||
|
|
||||||
// send follow
|
// send follow
|
||||||
let follow_form = FollowInstance {
|
let follow_form = FollowInstance {
|
||||||
|
@ -170,35 +168,29 @@ impl ApiClient {
|
||||||
|
|
||||||
pub async fn resolve_article(&self, id: Url) -> MyResult<ArticleView> {
|
pub async fn resolve_article(&self, id: Url) -> MyResult<ArticleView> {
|
||||||
let resolve_object = ResolveObject { id };
|
let resolve_object = ResolveObject { id };
|
||||||
get_query(&self.hostname, "article/resolve", Some(resolve_object)).await
|
self.get_query("article/resolve", Some(resolve_object))
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn resolve_instance(&self, id: Url) -> MyResult<DbInstance> {
|
pub async fn resolve_instance(&self, id: Url) -> MyResult<DbInstance> {
|
||||||
let resolve_object = ResolveObject { id };
|
let resolve_object = ResolveObject { id };
|
||||||
get_query(&self.hostname, "instance/resolve", Some(resolve_object)).await
|
self.get_query("instance/resolve", Some(resolve_object))
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_query<T, R>(hostname: &str, endpoint: &str, query: Option<R>) -> MyResult<T>
|
async fn handle_json_res<T>(#[allow(unused_mut)] mut req: RequestBuilder) -> MyResult<T>
|
||||||
where
|
where
|
||||||
T: for<'de> Deserialize<'de>,
|
T: for<'de> Deserialize<'de>,
|
||||||
R: Serialize,
|
|
||||||
{
|
{
|
||||||
let mut req = CLIENT.get(format!("http://{}/api/v1/{}", hostname, endpoint));
|
#[cfg(not(feature = "ssr"))]
|
||||||
if let Some(query) = query {
|
{
|
||||||
req = req.query(&query);
|
req = req.fetch_credentials_include();
|
||||||
}
|
}
|
||||||
handle_json_res::<T>(req).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_json_res<T>(req: RequestBuilder) -> MyResult<T>
|
|
||||||
where
|
|
||||||
T: for<'de> Deserialize<'de>,
|
|
||||||
{
|
|
||||||
let res = req.send().await?;
|
let res = req.send().await?;
|
||||||
let status = res.status();
|
let status = res.status();
|
||||||
let text = res.text().await?;
|
let text = res.text().await?;
|
||||||
if status == reqwest::StatusCode::OK {
|
if status == StatusCode::OK {
|
||||||
Ok(serde_json::from_str(&text).map_err(|e| anyhow!("Json error on {text}: {e}"))?)
|
Ok(serde_json::from_str(&text).map_err(|e| anyhow!("Json error on {text}: {e}"))?)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("API error: {text}").into())
|
Err(anyhow!("API error: {text}").into())
|
||||||
|
|
|
@ -43,10 +43,9 @@ impl GlobalState {
|
||||||
create_local_resource(
|
create_local_resource(
|
||||||
move || (),
|
move || (),
|
||||||
|_| async move {
|
|_| async move {
|
||||||
if let Ok(my_profile) = GlobalState::api_client().my_profile().await {
|
let my_profile = GlobalState::api_client().my_profile().await.ok();
|
||||||
expect_context::<RwSignal<GlobalState>>()
|
expect_context::<RwSignal<GlobalState>>()
|
||||||
.update(|state| state.my_profile = Some(my_profile.clone()))
|
.update(|state| state.my_profile = my_profile.clone());
|
||||||
};
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -54,7 +53,7 @@ impl GlobalState {
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn App() -> impl IntoView {
|
pub fn App() -> impl IntoView {
|
||||||
let backend_hostname = "localhost:8080".to_string();
|
let backend_hostname = "127.0.0.1:8080".to_string();
|
||||||
|
|
||||||
provide_meta_context();
|
provide_meta_context();
|
||||||
let backend_hostname = GlobalState {
|
let backend_hostname = GlobalState {
|
||||||
|
|
|
@ -6,6 +6,12 @@ use leptos_router::*;
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Nav() -> impl IntoView {
|
pub fn Nav() -> impl IntoView {
|
||||||
let global_state = use_context::<RwSignal<GlobalState>>().unwrap();
|
let global_state = use_context::<RwSignal<GlobalState>>().unwrap();
|
||||||
|
let logout_action = create_action(move |_| async move {
|
||||||
|
GlobalState::api_client().logout().await.unwrap();
|
||||||
|
expect_context::<RwSignal<GlobalState>>()
|
||||||
|
.get_untracked()
|
||||||
|
.update_my_profile();
|
||||||
|
});
|
||||||
view! {
|
view! {
|
||||||
<nav class="inner">
|
<nav class="inner">
|
||||||
<li>
|
<li>
|
||||||
|
@ -19,11 +25,7 @@ pub fn Nav() -> impl IntoView {
|
||||||
{
|
{
|
||||||
move || global_state.with(|state| state.my_profile.clone().unwrap().person.username)
|
move || global_state.with(|state| state.my_profile.clone().unwrap().person.username)
|
||||||
}
|
}
|
||||||
<button on:click=move |_| {
|
<button on:click=move |_| logout_action.dispatch(())>
|
||||||
// TODO: not executed
|
|
||||||
dbg!(1);
|
|
||||||
do_logout()
|
|
||||||
}>
|
|
||||||
Logout
|
Logout
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
|
@ -40,14 +42,3 @@ pub fn Nav() -> impl IntoView {
|
||||||
</nav>
|
</nav>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_logout() {
|
|
||||||
dbg!("do logout");
|
|
||||||
create_action(move |()| async move {
|
|
||||||
dbg!("run logout action");
|
|
||||||
GlobalState::api_client().logout().await.unwrap();
|
|
||||||
expect_context::<RwSignal<GlobalState>>()
|
|
||||||
.get()
|
|
||||||
.update_my_profile();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,12 +26,19 @@ pub fn Article() -> impl IntoView {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let global_state = use_context::<RwSignal<GlobalState>>().unwrap();
|
||||||
|
let (count, set_count) = create_signal(0);
|
||||||
view! {
|
view! {
|
||||||
<Suspense fallback=|| view! { "Loading..." }>
|
<Suspense fallback=|| view! { "Loading..." }>
|
||||||
{move || article.get().map(|article|
|
{move || article.get().map(|article|
|
||||||
view! {
|
view! {
|
||||||
<div class="item-view">
|
<div class="item-view">
|
||||||
<h1>{article.article.title}</h1>
|
<h1>{article.article.title}</h1>
|
||||||
|
<Show when=move || global_state.with(|state| state.my_profile.is_some())>
|
||||||
|
<button on:click=move |_| {
|
||||||
|
set_count.update(|n| *n += 1);
|
||||||
|
}>Edit {move || count.get()}</button>
|
||||||
|
</Show>
|
||||||
<div>{article.article.text}</div>
|
<div>{article.article.text}</div>
|
||||||
</div>
|
</div>
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -8,7 +8,7 @@ pub async fn main() -> ibis_lib::backend::error::MyResult<()> {
|
||||||
.filter_module("ibis", LevelFilter::Info)
|
.filter_module("ibis", LevelFilter::Info)
|
||||||
.init();
|
.init();
|
||||||
let database_url = "postgres://ibis:password@localhost:5432/ibis";
|
let database_url = "postgres://ibis:password@localhost:5432/ibis";
|
||||||
ibis_lib::backend::start("localhost:8131", database_url).await?;
|
ibis_lib::backend::start("127.0.0.1:8131", database_url).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,16 +115,7 @@ impl IbisInstance {
|
||||||
username: username.to_string(),
|
username: username.to_string(),
|
||||||
password: "hunter2".to_string(),
|
password: "hunter2".to_string(),
|
||||||
};
|
};
|
||||||
// use a separate http client for each backend instance, with cookie store for auth
|
let client = ClientBuilder::new().cookie_store(true).build().unwrap();
|
||||||
// how to pass the client/hostname to api client methods?
|
|
||||||
// probably create a struct ApiClient(hostname, client) with all api methods in impl
|
|
||||||
// TODO: seems that cookie isnt being stored? or maybe wrong hostname?
|
|
||||||
let jar = Arc::new(Jar::default());
|
|
||||||
let client = ClientBuilder::new()
|
|
||||||
.cookie_store(true)
|
|
||||||
.cookie_provider(jar.clone())
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
let api_client = ApiClient::new(client, hostname.clone());
|
let api_client = ApiClient::new(client, hostname.clone());
|
||||||
api_client.register(form).await.unwrap();
|
api_client.register(form).await.unwrap();
|
||||||
Self {
|
Self {
|
||||||
|
|
Loading…
Reference in a new issue