Refactor authentication from database to direct CalDAV authentication
Major architectural change to simplify authentication by authenticating directly against CalDAV servers instead of maintaining a local user database. Backend changes: - Remove SQLite database dependencies and user storage - Refactor AuthService to authenticate directly against CalDAV servers - Update JWT tokens to store CalDAV server info instead of user IDs - Implement proper CalDAV calendar discovery with XML parsing - Fix URL construction for CalDAV REPORT requests - Add comprehensive debug logging for authentication flow Frontend changes: - Add server URL input field to login form - Remove registration functionality entirely - Update calendar service to pass CalDAV passwords via headers - Store CalDAV credentials in localStorage for API calls Key improvements: - Simplified architecture eliminates database complexity - Direct CalDAV authentication ensures credentials always work - Proper calendar discovery automatically finds user calendars - Robust error handling and debug logging for troubleshooting 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										38
									
								
								src/app.rs
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								src/app.rs
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| use yew::prelude::*; | ||||
| use yew_router::prelude::*; | ||||
| use gloo_storage::{LocalStorage, Storage}; | ||||
| use crate::components::{Login, Register, Calendar}; | ||||
| use crate::components::{Login, Calendar}; | ||||
| use crate::services::{CalendarService, CalendarEvent}; | ||||
| use std::collections::HashMap; | ||||
| use chrono::{Local, NaiveDate, Datelike}; | ||||
| @@ -12,8 +12,6 @@ enum Route { | ||||
|     Home, | ||||
|     #[at("/login")] | ||||
|     Login, | ||||
|     #[at("/register")] | ||||
|     Register, | ||||
|     #[at("/calendar")] | ||||
|     Calendar, | ||||
| } | ||||
| @@ -56,7 +54,6 @@ pub fn App() -> Html { | ||||
|                             html! { | ||||
|                                 <nav> | ||||
|                                     <Link<Route> to={Route::Login}>{"Login"}</Link<Route>> | ||||
|                                     <Link<Route> to={Route::Register}>{"Register"}</Link<Route>> | ||||
|                                 </nav> | ||||
|                             } | ||||
|                         } | ||||
| @@ -83,13 +80,6 @@ pub fn App() -> Html { | ||||
|                                     html! { <Login {on_login} /> } | ||||
|                                 } | ||||
|                             } | ||||
|                             Route::Register => { | ||||
|                                 if auth_token.is_some() { | ||||
|                                     html! { <Redirect<Route> to={Route::Calendar}/> } | ||||
|                                 } else { | ||||
|                                     html! { <Register on_register={on_login.clone()} /> } | ||||
|                                 } | ||||
|                             } | ||||
|                             Route::Calendar => { | ||||
|                                 if auth_token.is_some() { | ||||
|                                     html! { <CalendarView /> } | ||||
| @@ -136,7 +126,18 @@ fn CalendarView() -> Html { | ||||
|                 wasm_bindgen_futures::spawn_local(async move { | ||||
|                     let calendar_service = CalendarService::new(); | ||||
|                      | ||||
|                     match calendar_service.refresh_event(&token, &uid).await { | ||||
|                     // Get password from stored credentials | ||||
|                     let password = if let Ok(credentials_str) = LocalStorage::get::<String>("caldav_credentials") { | ||||
|                         if let Ok(credentials) = serde_json::from_str::<serde_json::Value>(&credentials_str) { | ||||
|                             credentials["password"].as_str().unwrap_or("").to_string() | ||||
|                         } else { | ||||
|                             String::new() | ||||
|                         } | ||||
|                     } else { | ||||
|                         String::new() | ||||
|                     }; | ||||
|                      | ||||
|                     match calendar_service.refresh_event(&token, &password, &uid).await { | ||||
|                         Ok(Some(refreshed_event)) => { | ||||
|                             // If this is a recurring event, we need to regenerate all occurrences | ||||
|                             let mut updated_events = (*events).clone(); | ||||
| @@ -203,7 +204,18 @@ fn CalendarView() -> Html { | ||||
|                 wasm_bindgen_futures::spawn_local(async move { | ||||
|                     let calendar_service = CalendarService::new(); | ||||
|                      | ||||
|                     match calendar_service.fetch_events_for_month(&token, current_year, current_month).await { | ||||
|                     // Get password from stored credentials | ||||
|                     let password = if let Ok(credentials_str) = LocalStorage::get::<String>("caldav_credentials") { | ||||
|                         if let Ok(credentials) = serde_json::from_str::<serde_json::Value>(&credentials_str) { | ||||
|                             credentials["password"].as_str().unwrap_or("").to_string() | ||||
|                         } else { | ||||
|                             String::new() | ||||
|                         } | ||||
|                     } else { | ||||
|                         String::new() | ||||
|                     }; | ||||
|                      | ||||
|                     match calendar_service.fetch_events_for_month(&token, &password, current_year, current_month).await { | ||||
|                         Ok(calendar_events) => { | ||||
|                             let grouped_events = CalendarService::group_events_by_date(calendar_events); | ||||
|                             events.set(grouped_events); | ||||
|   | ||||
							
								
								
									
										33
									
								
								src/auth.rs
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								src/auth.rs
									
									
									
									
									
								
							| @@ -4,29 +4,9 @@ use wasm_bindgen::JsCast; | ||||
| use wasm_bindgen_futures::JsFuture; | ||||
| use web_sys::{Request, RequestInit, RequestMode, Response}; | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||||
| pub struct User { | ||||
|     pub id: String, | ||||
|     pub username: String, | ||||
|     pub email: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||||
| pub struct UserInfo { | ||||
|     pub id: String, | ||||
|     pub username: String, | ||||
|     pub email: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct RegisterRequest { | ||||
|     pub username: String, | ||||
|     pub email: String, | ||||
|     pub password: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct LoginRequest { | ||||
| pub struct CalDAVLoginRequest { | ||||
|     pub server_url: String, | ||||
|     pub username: String, | ||||
|     pub password: String, | ||||
| } | ||||
| @@ -34,7 +14,8 @@ pub struct LoginRequest { | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct AuthResponse { | ||||
|     pub token: String, | ||||
|     pub user: UserInfo, | ||||
|     pub username: String, | ||||
|     pub server_url: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| @@ -57,11 +38,7 @@ impl AuthService { | ||||
|         Self { base_url } | ||||
|     } | ||||
|  | ||||
|     pub async fn register(&self, request: RegisterRequest) -> Result<AuthResponse, String> { | ||||
|         self.post_json("/auth/register", &request).await | ||||
|     } | ||||
|      | ||||
|     pub async fn login(&self, request: LoginRequest) -> Result<AuthResponse, String> { | ||||
|     pub async fn login(&self, request: CalDAVLoginRequest) -> Result<AuthResponse, String> { | ||||
|         self.post_json("/auth/login", &request).await | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -9,14 +9,24 @@ pub struct LoginProps { | ||||
|  | ||||
| #[function_component] | ||||
| pub fn Login(props: &LoginProps) -> Html { | ||||
|     let server_url = use_state(String::new); | ||||
|     let username = use_state(String::new); | ||||
|     let password = use_state(String::new); | ||||
|     let error_message = use_state(|| Option::<String>::None); | ||||
|     let is_loading = use_state(|| false); | ||||
|  | ||||
|     let server_url_ref = use_node_ref(); | ||||
|     let username_ref = use_node_ref(); | ||||
|     let password_ref = use_node_ref(); | ||||
|  | ||||
|     let on_server_url_change = { | ||||
|         let server_url = server_url.clone(); | ||||
|         Callback::from(move |e: Event| { | ||||
|             let target = e.target_unchecked_into::<HtmlInputElement>(); | ||||
|             server_url.set(target.value()); | ||||
|         }) | ||||
|     }; | ||||
|  | ||||
|     let on_username_change = { | ||||
|         let username = username.clone(); | ||||
|         Callback::from(move |e: Event| { | ||||
| @@ -34,6 +44,7 @@ pub fn Login(props: &LoginProps) -> Html { | ||||
|     }; | ||||
|  | ||||
|     let on_submit = { | ||||
|         let server_url = server_url.clone(); | ||||
|         let username = username.clone(); | ||||
|         let password = password.clone(); | ||||
|         let error_message = error_message.clone(); | ||||
| @@ -43,6 +54,7 @@ pub fn Login(props: &LoginProps) -> Html { | ||||
|         Callback::from(move |e: SubmitEvent| { | ||||
|             e.prevent_default(); | ||||
|              | ||||
|             let server_url = (*server_url).clone(); | ||||
|             let username = (*username).clone(); | ||||
|             let password = (*password).clone(); | ||||
|             let error_message = error_message.clone(); | ||||
| @@ -50,7 +62,7 @@ pub fn Login(props: &LoginProps) -> Html { | ||||
|             let on_login = on_login.clone(); | ||||
|  | ||||
|             // Basic client-side validation | ||||
|             if username.trim().is_empty() || password.is_empty() { | ||||
|             if server_url.trim().is_empty() || username.trim().is_empty() || password.is_empty() { | ||||
|                 error_message.set(Some("Please fill in all fields".to_string())); | ||||
|                 return; | ||||
|             } | ||||
| @@ -59,19 +71,27 @@ pub fn Login(props: &LoginProps) -> Html { | ||||
|             error_message.set(None); | ||||
|  | ||||
|             wasm_bindgen_futures::spawn_local(async move { | ||||
|                 match perform_login(username, password).await { | ||||
|                     Ok(token) => { | ||||
|                         // Store token in local storage | ||||
|                 web_sys::console::log_1(&"🚀 Starting login process...".into()); | ||||
|                 match perform_login(server_url.clone(), username.clone(), password.clone()).await { | ||||
|                     Ok((token, credentials)) => { | ||||
|                         web_sys::console::log_1(&"✅ Login successful!".into()); | ||||
|                         // Store token and credentials in local storage | ||||
|                         if let Err(_) = LocalStorage::set("auth_token", &token) { | ||||
|                             error_message.set(Some("Failed to store authentication token".to_string())); | ||||
|                             is_loading.set(false); | ||||
|                             return; | ||||
|                         } | ||||
|                         if let Err(_) = LocalStorage::set("caldav_credentials", &credentials) { | ||||
|                             error_message.set(Some("Failed to store credentials".to_string())); | ||||
|                             is_loading.set(false); | ||||
|                             return; | ||||
|                         } | ||||
|                          | ||||
|                         is_loading.set(false); | ||||
|                         on_login.emit(token); | ||||
|                     } | ||||
|                     Err(err) => { | ||||
|                         web_sys::console::log_1(&format!("❌ Login failed: {}", err).into()); | ||||
|                         error_message.set(Some(err)); | ||||
|                         is_loading.set(false); | ||||
|                     } | ||||
| @@ -83,8 +103,21 @@ pub fn Login(props: &LoginProps) -> Html { | ||||
|     html! { | ||||
|         <div class="login-container"> | ||||
|             <div class="login-form"> | ||||
|                 <h2>{"Sign In"}</h2> | ||||
|                 <h2>{"Sign In to CalDAV"}</h2> | ||||
|                 <form onsubmit={on_submit}> | ||||
|                     <div class="form-group"> | ||||
|                         <label for="server_url">{"CalDAV Server URL"}</label> | ||||
|                         <input | ||||
|                             ref={server_url_ref} | ||||
|                             type="text" | ||||
|                             id="server_url" | ||||
|                             placeholder="https://your-caldav-server.com/dav/" | ||||
|                             value={(*server_url).clone()} | ||||
|                             onchange={on_server_url_change} | ||||
|                             disabled={*is_loading} | ||||
|                         /> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="form-group"> | ||||
|                         <label for="username">{"Username"}</label> | ||||
|                         <input | ||||
| @@ -131,22 +164,43 @@ pub fn Login(props: &LoginProps) -> Html { | ||||
|                 </form> | ||||
|  | ||||
|                 <div class="auth-links"> | ||||
|                     <p>{"Don't have an account? "}<a href="/register">{"Sign up here"}</a></p> | ||||
|                     <p>{"Enter your CalDAV server credentials to connect to your calendar"}</p> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Perform login using the auth service | ||||
| async fn perform_login(username: String, password: String) -> Result<String, String> { | ||||
|     use crate::auth::{AuthService, LoginRequest}; | ||||
| /// Perform login using the CalDAV auth service | ||||
| async fn perform_login(server_url: String, username: String, password: String) -> Result<(String, String), String> { | ||||
|     use crate::auth::{AuthService, CalDAVLoginRequest}; | ||||
|     use serde_json; | ||||
|      | ||||
|     web_sys::console::log_1(&format!("📡 Creating auth service and request...").into()); | ||||
|      | ||||
|     let auth_service = AuthService::new(); | ||||
|     let request = LoginRequest { username, password }; | ||||
|     let request = CalDAVLoginRequest {  | ||||
|         server_url: server_url.clone(),  | ||||
|         username: username.clone(),  | ||||
|         password: password.clone()  | ||||
|     }; | ||||
|      | ||||
|     web_sys::console::log_1(&format!("🚀 Sending login request to backend...").into()); | ||||
|      | ||||
|     match auth_service.login(request).await { | ||||
|         Ok(response) => Ok(response.token), | ||||
|         Err(err) => Err(err), | ||||
|         Ok(response) => { | ||||
|             web_sys::console::log_1(&format!("✅ Backend responded successfully").into()); | ||||
|             // Create credentials object to store | ||||
|             let credentials = serde_json::json!({ | ||||
|                 "server_url": server_url, | ||||
|                 "username": username, | ||||
|                 "password": password | ||||
|             }); | ||||
|             Ok((response.token, credentials.to_string())) | ||||
|         }, | ||||
|         Err(err) => { | ||||
|             web_sys::console::log_1(&format!("❌ Backend error: {}", err).into()); | ||||
|             Err(err) | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| @@ -1,9 +1,7 @@ | ||||
| pub mod login; | ||||
| pub mod register; | ||||
| pub mod calendar; | ||||
| pub mod event_modal; | ||||
|  | ||||
| pub use login::Login; | ||||
| pub use register::Register; | ||||
| pub use calendar::Calendar; | ||||
| pub use event_modal::EventModal; | ||||
| @@ -1,235 +0,0 @@ | ||||
| use yew::prelude::*; | ||||
| use web_sys::HtmlInputElement; | ||||
| use gloo_storage::{LocalStorage, Storage}; | ||||
|  | ||||
| #[derive(Properties, PartialEq)] | ||||
| pub struct RegisterProps { | ||||
|     pub on_register: Callback<String>, // Callback with JWT token | ||||
| } | ||||
|  | ||||
| #[function_component] | ||||
| pub fn Register(props: &RegisterProps) -> Html { | ||||
|     let username = use_state(String::new); | ||||
|     let email = use_state(String::new); | ||||
|     let password = use_state(String::new); | ||||
|     let confirm_password = use_state(String::new); | ||||
|     let error_message = use_state(|| Option::<String>::None); | ||||
|     let is_loading = use_state(|| false); | ||||
|  | ||||
|     let username_ref = use_node_ref(); | ||||
|     let email_ref = use_node_ref(); | ||||
|     let password_ref = use_node_ref(); | ||||
|     let confirm_password_ref = use_node_ref(); | ||||
|  | ||||
|     let on_username_change = { | ||||
|         let username = username.clone(); | ||||
|         Callback::from(move |e: Event| { | ||||
|             let target = e.target_unchecked_into::<HtmlInputElement>(); | ||||
|             username.set(target.value()); | ||||
|         }) | ||||
|     }; | ||||
|  | ||||
|     let on_email_change = { | ||||
|         let email = email.clone(); | ||||
|         Callback::from(move |e: Event| { | ||||
|             let target = e.target_unchecked_into::<HtmlInputElement>(); | ||||
|             email.set(target.value()); | ||||
|         }) | ||||
|     }; | ||||
|  | ||||
|     let on_password_change = { | ||||
|         let password = password.clone(); | ||||
|         Callback::from(move |e: Event| { | ||||
|             let target = e.target_unchecked_into::<HtmlInputElement>(); | ||||
|             password.set(target.value()); | ||||
|         }) | ||||
|     }; | ||||
|  | ||||
|     let on_confirm_password_change = { | ||||
|         let confirm_password = confirm_password.clone(); | ||||
|         Callback::from(move |e: Event| { | ||||
|             let target = e.target_unchecked_into::<HtmlInputElement>(); | ||||
|             confirm_password.set(target.value()); | ||||
|         }) | ||||
|     }; | ||||
|  | ||||
|     let on_submit = { | ||||
|         let username = username.clone(); | ||||
|         let email = email.clone(); | ||||
|         let password = password.clone(); | ||||
|         let confirm_password = confirm_password.clone(); | ||||
|         let error_message = error_message.clone(); | ||||
|         let is_loading = is_loading.clone(); | ||||
|         let on_register = props.on_register.clone(); | ||||
|  | ||||
|         Callback::from(move |e: SubmitEvent| { | ||||
|             e.prevent_default(); | ||||
|              | ||||
|             let username = (*username).clone(); | ||||
|             let email = (*email).clone(); | ||||
|             let password = (*password).clone(); | ||||
|             let confirm_password = (*confirm_password).clone(); | ||||
|             let error_message = error_message.clone(); | ||||
|             let is_loading = is_loading.clone(); | ||||
|             let on_register = on_register.clone(); | ||||
|  | ||||
|             // Client-side validation | ||||
|             if let Err(validation_error) = validate_registration(&username, &email, &password, &confirm_password) { | ||||
|                 error_message.set(Some(validation_error)); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             is_loading.set(true); | ||||
|             error_message.set(None); | ||||
|  | ||||
|             wasm_bindgen_futures::spawn_local(async move { | ||||
|                 match perform_registration(username, email, password).await { | ||||
|                     Ok(token) => { | ||||
|                         // Store token in local storage | ||||
|                         if let Err(_) = LocalStorage::set("auth_token", &token) { | ||||
|                             error_message.set(Some("Failed to store authentication token".to_string())); | ||||
|                             is_loading.set(false); | ||||
|                             return; | ||||
|                         } | ||||
|                          | ||||
|                         is_loading.set(false); | ||||
|                         on_register.emit(token); | ||||
|                     } | ||||
|                     Err(err) => { | ||||
|                         error_message.set(Some(err)); | ||||
|                         is_loading.set(false); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|         }) | ||||
|     }; | ||||
|  | ||||
|     html! { | ||||
|         <div class="register-container"> | ||||
|             <div class="register-form"> | ||||
|                 <h2>{"Create Account"}</h2> | ||||
|                 <form onsubmit={on_submit}> | ||||
|                     <div class="form-group"> | ||||
|                         <label for="username">{"Username"}</label> | ||||
|                         <input | ||||
|                             ref={username_ref} | ||||
|                             type="text" | ||||
|                             id="username" | ||||
|                             placeholder="Choose a username" | ||||
|                             value={(*username).clone()} | ||||
|                             onchange={on_username_change} | ||||
|                             disabled={*is_loading} | ||||
|                         /> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="form-group"> | ||||
|                         <label for="email">{"Email"}</label> | ||||
|                         <input | ||||
|                             ref={email_ref} | ||||
|                             type="email" | ||||
|                             id="email" | ||||
|                             placeholder="Enter your email" | ||||
|                             value={(*email).clone()} | ||||
|                             onchange={on_email_change} | ||||
|                             disabled={*is_loading} | ||||
|                         /> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="form-group"> | ||||
|                         <label for="password">{"Password"}</label> | ||||
|                         <input | ||||
|                             ref={password_ref} | ||||
|                             type="password" | ||||
|                             id="password" | ||||
|                             placeholder="Choose a password" | ||||
|                             value={(*password).clone()} | ||||
|                             onchange={on_password_change} | ||||
|                             disabled={*is_loading} | ||||
|                         /> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="form-group"> | ||||
|                         <label for="confirm-password">{"Confirm Password"}</label> | ||||
|                         <input | ||||
|                             ref={confirm_password_ref} | ||||
|                             type="password" | ||||
|                             id="confirm-password" | ||||
|                             placeholder="Confirm your password" | ||||
|                             value={(*confirm_password).clone()} | ||||
|                             onchange={on_confirm_password_change} | ||||
|                             disabled={*is_loading} | ||||
|                         /> | ||||
|                     </div> | ||||
|  | ||||
|                     { | ||||
|                         if let Some(error) = (*error_message).clone() { | ||||
|                             html! { <div class="error-message">{error}</div> } | ||||
|                         } else { | ||||
|                             html! {} | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     <button type="submit" disabled={*is_loading} class="register-button"> | ||||
|                         { | ||||
|                             if *is_loading { | ||||
|                                 "Creating Account..." | ||||
|                             } else { | ||||
|                                 "Create Account" | ||||
|                             } | ||||
|                         } | ||||
|                     </button> | ||||
|                 </form> | ||||
|  | ||||
|                 <div class="auth-links"> | ||||
|                     <p>{"Already have an account? "}<a href="/login">{"Sign in here"}</a></p> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Validate registration form data | ||||
| fn validate_registration(username: &str, email: &str, password: &str, confirm_password: &str) -> Result<(), String> { | ||||
|     if username.trim().is_empty() { | ||||
|         return Err("Username is required".to_string()); | ||||
|     } | ||||
|  | ||||
|     if username.len() < 3 { | ||||
|         return Err("Username must be at least 3 characters long".to_string()); | ||||
|     } | ||||
|  | ||||
|     if email.trim().is_empty() { | ||||
|         return Err("Email is required".to_string()); | ||||
|     } | ||||
|  | ||||
|     if !email.contains('@') { | ||||
|         return Err("Please enter a valid email address".to_string()); | ||||
|     } | ||||
|  | ||||
|     if password.is_empty() { | ||||
|         return Err("Password is required".to_string()); | ||||
|     } | ||||
|  | ||||
|     if password.len() < 6 { | ||||
|         return Err("Password must be at least 6 characters long".to_string()); | ||||
|     } | ||||
|  | ||||
|     if password != confirm_password { | ||||
|         return Err("Passwords do not match".to_string()); | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| /// Perform registration using the auth service | ||||
| async fn perform_registration(username: String, email: String, password: String) -> Result<String, String> { | ||||
|     use crate::auth::{AuthService, RegisterRequest}; | ||||
|      | ||||
|     let auth_service = AuthService::new(); | ||||
|     let request = RegisterRequest { username, email, password }; | ||||
|      | ||||
|     match auth_service.register(request).await { | ||||
|         Ok(response) => Ok(response.token), | ||||
|         Err(err) => Err(err), | ||||
|     } | ||||
| } | ||||
| @@ -139,6 +139,7 @@ impl CalendarService { | ||||
|     pub async fn fetch_events_for_month( | ||||
|         &self,  | ||||
|         token: &str,  | ||||
|         password: &str, | ||||
|         year: i32,  | ||||
|         month: u32 | ||||
|     ) -> Result<Vec<CalendarEvent>, String> { | ||||
| @@ -154,6 +155,9 @@ impl CalendarService { | ||||
|  | ||||
|         request.headers().set("Authorization", &format!("Bearer {}", token)) | ||||
|             .map_err(|e| format!("Authorization header setting failed: {:?}", e))?; | ||||
|          | ||||
|         request.headers().set("X-CalDAV-Password", password) | ||||
|             .map_err(|e| format!("Password header setting failed: {:?}", e))?; | ||||
|  | ||||
|         let resp_value = JsFuture::from(window.fetch_with_request(&request)) | ||||
|             .await | ||||
| @@ -407,7 +411,7 @@ impl CalendarService { | ||||
|     } | ||||
|  | ||||
|     /// Refresh a single event by UID from the CalDAV server | ||||
|     pub async fn refresh_event(&self, token: &str, uid: &str) -> Result<Option<CalendarEvent>, String> { | ||||
|     pub async fn refresh_event(&self, token: &str, password: &str, uid: &str) -> Result<Option<CalendarEvent>, String> { | ||||
|         let window = web_sys::window().ok_or("No global window exists")?; | ||||
|          | ||||
|         let opts = RequestInit::new(); | ||||
| @@ -420,6 +424,9 @@ impl CalendarService { | ||||
|  | ||||
|         request.headers().set("Authorization", &format!("Bearer {}", token)) | ||||
|             .map_err(|e| format!("Authorization header setting failed: {:?}", e))?; | ||||
|          | ||||
|         request.headers().set("X-CalDAV-Password", password) | ||||
|             .map_err(|e| format!("Password header setting failed: {:?}", e))?; | ||||
|  | ||||
|         let resp_value = JsFuture::from(window.fetch_with_request(&request)) | ||||
|             .await | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Connor Johnstone
					Connor Johnstone