Add CalDAV dependencies and secure configuration
- Added comprehensive CalDAV/calendar dependencies: - reqwest for HTTP requests - ical for calendar parsing - chrono for date/time handling - serde for serialization - anyhow/thiserror for error handling - uuid for event generation - Implemented secure config management: - dotenvy for environment variable loading - base64 for Basic Auth encoding - .env.example template for development - .gitignore updated to exclude secret files - Created config.rs module with extensive documentation: - CalDAVConfig struct for server credentials - Environment-based configuration loading - HTTP Basic Auth helper methods - Comprehensive error handling - Full rustdoc documentation with examples 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										182
									
								
								src/config.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/config.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,182 @@ | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::env; | ||||
|  | ||||
| /// Configuration for CalDAV server connection and authentication. | ||||
| ///  | ||||
| /// This struct holds all the necessary information to connect to a CalDAV server, | ||||
| /// including server URL, credentials, and optional collection paths. | ||||
| ///  | ||||
| /// # Security Note | ||||
| ///  | ||||
| /// The password field contains sensitive information and should be handled carefully. | ||||
| /// This struct implements `Debug` but in production, consider implementing a custom | ||||
| /// `Debug` that masks the password field. | ||||
| ///  | ||||
| /// # Example | ||||
| ///  | ||||
| /// ```rust | ||||
| /// use crate::config::CalDAVConfig; | ||||
| ///  | ||||
| /// // Load configuration from environment variables | ||||
| /// let config = CalDAVConfig::from_env()?; | ||||
| ///  | ||||
| /// // Use the configuration for HTTP requests | ||||
| /// let auth_header = format!("Basic {}", config.get_basic_auth()); | ||||
| /// ``` | ||||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||||
| pub struct CalDAVConfig { | ||||
|     /// The base URL of the CalDAV server (e.g., "https://caldav.example.com/dav/") | ||||
|     pub server_url: String, | ||||
|      | ||||
|     /// Username for authentication with the CalDAV server | ||||
|     pub username: String, | ||||
|      | ||||
|     /// Password for authentication with the CalDAV server | ||||
|     ///  | ||||
|     /// **Security Note**: This contains sensitive information | ||||
|     pub password: String, | ||||
|      | ||||
|     /// Optional path to the calendar collection on the server | ||||
|     ///  | ||||
|     /// If not provided, the client will need to discover available calendars | ||||
|     /// through CalDAV PROPFIND requests | ||||
|     pub calendar_path: Option<String>, | ||||
|      | ||||
|     /// Optional path to the tasks/todo collection on the server | ||||
|     ///  | ||||
|     /// Some CalDAV servers store tasks separately from calendar events | ||||
|     pub tasks_path: Option<String>, | ||||
| } | ||||
|  | ||||
| impl CalDAVConfig { | ||||
|     /// Creates a new CalDAVConfig by loading values from environment variables. | ||||
|     ///  | ||||
|     /// This method will attempt to load a `.env` file from the current directory | ||||
|     /// and then read the following required environment variables: | ||||
|     ///  | ||||
|     /// - `CALDAV_SERVER_URL`: The CalDAV server base URL | ||||
|     /// - `CALDAV_USERNAME`: Username for authentication | ||||
|     /// - `CALDAV_PASSWORD`: Password for authentication | ||||
|     ///  | ||||
|     /// Optional environment variables: | ||||
|     ///  | ||||
|     /// - `CALDAV_CALENDAR_PATH`: Path to calendar collection | ||||
|     /// - `CALDAV_TASKS_PATH`: Path to tasks collection | ||||
|     ///  | ||||
|     /// # Errors | ||||
|     ///  | ||||
|     /// Returns `ConfigError::MissingVar` if any required environment variable | ||||
|     /// is not set or cannot be read. | ||||
|     ///  | ||||
|     /// # Example | ||||
|     ///  | ||||
|     /// ```rust | ||||
|     /// use crate::config::CalDAVConfig; | ||||
|     ///  | ||||
|     /// match CalDAVConfig::from_env() { | ||||
|     ///     Ok(config) => { | ||||
|     ///         println!("Loaded config for server: {}", config.server_url); | ||||
|     ///     } | ||||
|     ///     Err(e) => { | ||||
|     ///         eprintln!("Failed to load config: {}", e); | ||||
|     ///     } | ||||
|     /// } | ||||
|     /// ``` | ||||
|     pub fn from_env() -> Result<Self, ConfigError> { | ||||
|         // Attempt to load .env file, but don't fail if it doesn't exist | ||||
|         dotenvy::dotenv().ok(); | ||||
|  | ||||
|         let server_url = env::var("CALDAV_SERVER_URL") | ||||
|             .map_err(|_| ConfigError::MissingVar("CALDAV_SERVER_URL".to_string()))?; | ||||
|  | ||||
|         let username = env::var("CALDAV_USERNAME") | ||||
|             .map_err(|_| ConfigError::MissingVar("CALDAV_USERNAME".to_string()))?; | ||||
|  | ||||
|         let password = env::var("CALDAV_PASSWORD") | ||||
|             .map_err(|_| ConfigError::MissingVar("CALDAV_PASSWORD".to_string()))?; | ||||
|  | ||||
|         // Optional paths - it's fine if these are not set | ||||
|         let calendar_path = env::var("CALDAV_CALENDAR_PATH").ok(); | ||||
|         let tasks_path = env::var("CALDAV_TASKS_PATH").ok(); | ||||
|  | ||||
|         Ok(CalDAVConfig { | ||||
|             server_url, | ||||
|             username, | ||||
|             password, | ||||
|             calendar_path, | ||||
|             tasks_path, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Generates a Base64-encoded string for HTTP Basic Authentication. | ||||
|     ///  | ||||
|     /// This method combines the username and password in the format | ||||
|     /// `username:password` and encodes it using Base64, which is the | ||||
|     /// standard format for the `Authorization: Basic` HTTP header. | ||||
|     ///  | ||||
|     /// # Returns | ||||
|     ///  | ||||
|     /// A Base64-encoded string that can be used directly in the | ||||
|     /// `Authorization` header: `Authorization: Basic <returned_value>` | ||||
|     ///  | ||||
|     /// # Example | ||||
|     ///  | ||||
|     /// ```rust | ||||
|     /// use crate::config::CalDAVConfig; | ||||
|     ///  | ||||
|     /// let config = CalDAVConfig { | ||||
|     ///     server_url: "https://example.com".to_string(), | ||||
|     ///     username: "user".to_string(), | ||||
|     ///     password: "pass".to_string(), | ||||
|     ///     calendar_path: None, | ||||
|     ///     tasks_path: None, | ||||
|     /// }; | ||||
|     ///  | ||||
|     /// let auth_value = config.get_basic_auth(); | ||||
|     /// let auth_header = format!("Basic {}", auth_value); | ||||
|     /// ``` | ||||
|     pub fn get_basic_auth(&self) -> String { | ||||
|         let credentials = format!("{}:{}", self.username, self.password); | ||||
|         base64::encode(&credentials) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Errors that can occur when loading or using CalDAV configuration. | ||||
| #[derive(Debug, thiserror::Error)] | ||||
| pub enum ConfigError { | ||||
|     /// A required environment variable is missing or cannot be read. | ||||
|     ///  | ||||
|     /// This error occurs when calling `CalDAVConfig::from_env()` and one of the | ||||
|     /// required environment variables (`CALDAV_SERVER_URL`, `CALDAV_USERNAME`, | ||||
|     /// or `CALDAV_PASSWORD`) is not set. | ||||
|     #[error("Missing environment variable: {0}")] | ||||
|     MissingVar(String), | ||||
|      | ||||
|     /// The configuration contains invalid or malformed values. | ||||
|     ///  | ||||
|     /// This could include malformed URLs, invalid authentication credentials, | ||||
|     /// or other configuration issues that prevent proper CalDAV operation. | ||||
|     #[error("Invalid configuration: {0}")] | ||||
|     Invalid(String), | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use std::env; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_basic_auth_encoding() { | ||||
|         let config = CalDAVConfig { | ||||
|             server_url: "https://example.com".to_string(), | ||||
|             username: "testuser".to_string(), | ||||
|             password: "testpass".to_string(), | ||||
|             calendar_path: None, | ||||
|             tasks_path: None, | ||||
|         }; | ||||
|  | ||||
|         let auth = config.get_basic_auth(); | ||||
|         let expected = base64::encode("testuser:testpass"); | ||||
|         assert_eq!(auth, expected); | ||||
|     } | ||||
| } | ||||
| @@ -1,6 +1,8 @@ | ||||
| use yew::prelude::*; | ||||
|  | ||||
| mod app; | ||||
| mod config; | ||||
|  | ||||
| use app::App; | ||||
|  | ||||
| fn main() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Connor Johnstone
					Connor Johnstone