// Authentication module for Archipelago // Handles user setup, onboarding, and login use anyhow::Result; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use tokio::fs; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct User { pub password_hash: String, pub setup_complete: bool, pub onboarding_complete: bool, } pub struct AuthManager { data_dir: PathBuf, } impl AuthManager { pub fn new(data_dir: PathBuf) -> Self { Self { data_dir } } pub async fn is_setup(&self) -> Result { let user_file = self.data_dir.join("user.json"); Ok(user_file.exists()) } pub async fn get_user(&self) -> Result> { let user_file = self.data_dir.join("user.json"); if !user_file.exists() { return Ok(None); } let content = fs::read_to_string(&user_file).await?; let user: User = serde_json::from_str(&content)?; Ok(Some(user)) } pub async fn setup_user(&self, password: &str) -> Result<()> { use bcrypt::{hash, DEFAULT_COST}; let password_hash = hash(password, DEFAULT_COST)?; let user = User { password_hash, setup_complete: true, onboarding_complete: false, }; let user_file = self.data_dir.join("user.json"); let content = serde_json::to_string_pretty(&user)?; fs::write(&user_file, content).await?; Ok(()) } pub async fn complete_onboarding(&self) -> Result<()> { if let Some(mut user) = self.get_user().await? { user.onboarding_complete = true; let user_file = self.data_dir.join("user.json"); let content = serde_json::to_string_pretty(&user)?; fs::write(&user_file, content).await?; } Ok(()) } pub async fn verify_password(&self, password: &str) -> Result { use bcrypt::verify; if let Some(user) = self.get_user().await? { Ok(verify(password, &user.password_hash)?) } else { Ok(false) } } }