2024-04-17 22:07:47 +02:00
|
|
|
use leptos::*;
|
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
|
2024-04-17 23:07:56 +02:00
|
|
|
pub trait AuthToken {
|
|
|
|
fn present(&self) -> bool;
|
|
|
|
fn token(&self) -> String;
|
2024-05-02 02:07:11 +02:00
|
|
|
fn user_id(&self) -> String;
|
2024-05-01 18:22:25 +02:00
|
|
|
fn username(&self) -> String;
|
|
|
|
fn outbox(&self) -> String;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub struct Auth {
|
|
|
|
pub token: Signal<Option<String>>,
|
2024-05-02 02:07:11 +02:00
|
|
|
pub userid: Signal<Option<String>>,
|
2024-04-17 23:07:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-04-17 22:07:47 +02:00
|
|
|
#[component]
|
|
|
|
pub fn LoginBox(
|
|
|
|
token_tx: WriteSignal<Option<String>>,
|
2024-05-02 02:07:11 +02:00
|
|
|
userid_tx: WriteSignal<Option<String>>,
|
2024-04-17 22:07:47 +02:00
|
|
|
home_tl: Timeline,
|
|
|
|
server_tl: Timeline,
|
|
|
|
) -> impl IntoView {
|
2024-05-01 18:22:25 +02:00
|
|
|
let auth = use_context::<Auth>().expect("missing auth context");
|
2024-04-17 22:07:47 +02:00
|
|
|
let username_ref: NodeRef<html::Input> = create_node_ref();
|
|
|
|
let password_ref: NodeRef<html::Input> = create_node_ref();
|
|
|
|
view! {
|
|
|
|
<div>
|
2024-05-01 18:22:25 +02:00
|
|
|
<div class="w-100" class:hidden=move || !auth.present() >
|
|
|
|
"hi "<a href={move || Uri::web(FetchKind::User, &auth.username() )} >{move || auth.username() }</a>
|
2024-04-17 22:07:47 +02:00
|
|
|
<input style="float:right" type="submit" value="logout" on:click=move |_| {
|
|
|
|
token_tx.set(None);
|
|
|
|
home_tl.reset(format!("{URL_BASE}/outbox/page"));
|
|
|
|
server_tl.reset(format!("{URL_BASE}/inbox/page"));
|
|
|
|
spawn_local(async move {
|
2024-05-01 18:22:25 +02:00
|
|
|
if let Err(e) = server_tl.more(auth).await {
|
2024-04-17 22:07:47 +02:00
|
|
|
logging::error!("failed refreshing server timeline: {e}");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} />
|
|
|
|
</div>
|
2024-05-01 18:22:25 +02:00
|
|
|
<div class:hidden=move || auth.present() >
|
2024-04-21 23:41:30 +02:00
|
|
|
<form on:submit=move|ev| {
|
|
|
|
ev.prevent_default();
|
2024-04-17 22:07:47 +02:00
|
|
|
logging::log!("logging in...");
|
|
|
|
let email = username_ref.get().map(|x| x.value()).unwrap_or("".into());
|
|
|
|
let password = password_ref.get().map(|x| x.value()).unwrap_or("".into());
|
|
|
|
spawn_local(async move {
|
2024-04-22 03:15:41 +02:00
|
|
|
let Ok(res) = reqwest::Client::new()
|
2024-04-17 22:07:47 +02:00
|
|
|
.post(format!("{URL_BASE}/auth"))
|
|
|
|
.json(&LoginForm { email, password })
|
|
|
|
.send()
|
2024-04-22 03:15:41 +02:00
|
|
|
.await
|
|
|
|
else { if let Some(rf) = password_ref.get() { rf.set_value("") }; return };
|
2024-05-01 18:22:25 +02:00
|
|
|
let Ok(auth_response) = res
|
2024-04-17 22:07:47 +02:00
|
|
|
.json::<AuthResponse>()
|
2024-04-22 03:15:41 +02:00
|
|
|
.await
|
|
|
|
else { if let Some(rf) = password_ref.get() { rf.set_value("") }; return };
|
2024-05-01 18:22:25 +02:00
|
|
|
logging::log!("logged in until {}", auth_response.expires);
|
2024-04-18 05:00:26 +02:00
|
|
|
// update our username and token cookies
|
2024-05-01 18:22:25 +02:00
|
|
|
let username = auth_response.user.split('/').last().unwrap_or_default().to_string();
|
2024-05-02 02:07:11 +02:00
|
|
|
userid_tx.set(Some(auth_response.user));
|
2024-05-01 18:22:25 +02:00
|
|
|
token_tx.set(Some(auth_response.token));
|
2024-04-17 22:07:47 +02:00
|
|
|
// reset home feed and point it to our user's inbox
|
|
|
|
home_tl.reset(format!("{URL_BASE}/users/{}/inbox/page", username));
|
|
|
|
spawn_local(async move {
|
2024-05-01 18:22:25 +02:00
|
|
|
if let Err(e) = home_tl.more(auth).await {
|
2024-04-17 22:07:47 +02:00
|
|
|
tracing::error!("failed refreshing home timeline: {e}");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// reset server feed: there may be more content now that we're authed
|
|
|
|
server_tl.reset(format!("{URL_BASE}/inbox/page"));
|
|
|
|
spawn_local(async move {
|
2024-05-01 18:22:25 +02:00
|
|
|
if let Err(e) = server_tl.more(auth).await {
|
2024-04-17 22:07:47 +02:00
|
|
|
tracing::error!("failed refreshing server timeline: {e}");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2024-04-21 23:41:30 +02:00
|
|
|
} >
|
|
|
|
<input class="w-100" type="text" node_ref=username_ref placeholder="username" />
|
2024-04-22 03:08:32 +02:00
|
|
|
<input class="w-100" type="password" node_ref=password_ref placeholder="password" />
|
2024-04-21 23:41:30 +02:00
|
|
|
<input class="w-100" type="submit" value="login" />
|
|
|
|
</form>
|
2024-04-17 22:07:47 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, serde::Serialize)]
|
|
|
|
struct LoginForm {
|
|
|
|
email: String,
|
|
|
|
password: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, serde::Deserialize)]
|
|
|
|
struct AuthResponse {
|
|
|
|
token: String,
|
|
|
|
user: String,
|
|
|
|
expires: chrono::DateTime<chrono::Utc>,
|
|
|
|
}
|
|
|
|
|
2024-05-01 18:22:25 +02:00
|
|
|
impl AuthToken for Auth {
|
2024-04-17 22:07:47 +02:00
|
|
|
fn token(&self) -> String {
|
2024-05-01 18:22:25 +02:00
|
|
|
self.token.get().unwrap_or_default()
|
|
|
|
}
|
2024-05-02 02:07:11 +02:00
|
|
|
|
|
|
|
fn user_id(&self) -> String {
|
|
|
|
self.userid.get().unwrap_or_default()
|
|
|
|
}
|
2024-05-01 18:22:25 +02:00
|
|
|
|
|
|
|
fn username(&self) -> String {
|
2024-05-02 02:07:11 +02:00
|
|
|
// TODO maybe cache this?? how often do i need it?
|
|
|
|
self.userid.get()
|
|
|
|
.unwrap_or_default()
|
|
|
|
.split('/')
|
|
|
|
.last()
|
|
|
|
.unwrap_or_default()
|
|
|
|
.to_string()
|
2024-04-17 22:07:47 +02:00
|
|
|
}
|
2024-05-01 18:22:25 +02:00
|
|
|
|
2024-04-17 22:07:47 +02:00
|
|
|
fn present(&self) -> bool {
|
2024-05-01 18:22:25 +02:00
|
|
|
self.token.get().map_or(false, |x| !x.is_empty())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn outbox(&self) -> String {
|
2024-05-02 02:07:11 +02:00
|
|
|
format!("{URL_BASE}/users/{}/outbox", self.username())
|
2024-04-17 22:07:47 +02:00
|
|
|
}
|
|
|
|
}
|