A local-first private AI assistant for everyday use. Runs on-device models with encrypted P2P sync, and supports sharing chats publicly on ATProto.
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: Refactored root user represenation

madclaws 72005d98 56c771d7

+143 -75
+3 -2
.coderabbit.yml
··· 18 18 path_instructions: 19 19 - path: "**/*.{rs,toml}" 20 20 instructions: 21 - "Review the Rust code for conformity with best practices in Rust, 22 - Systems programming. Highlight any deviations." 21 + "Review the Rust code and Python code for conformity with best practices in Rust, 22 + Systems programming and Python. Highlight any deviations. Also highlight if there any 23 + any security issues in the code" 23 24
+9 -7
tilekit/src/accounts.rs
··· 1 - // Handles stuff related to accounts, identity etc.. 1 + //! Handles stuff related to accounts, identity etc.. 2 + 2 3 use anyhow::Result; 3 4 use ed25519_dalek::{SigningKey, ed25519::signature::rand_core::OsRng}; 4 5 use keyring::Entry; 5 6 use ucan::did::Ed25519Did; 6 7 7 - type DID = String; 8 - type Identity = DID; 8 + type Did = String; 9 + type Identity = Did; 9 10 10 - /// Creates an `Identity` 11 - /// # Parameters 12 - /// - `app`: The service for which Identity is made (for ex: tiles) 11 + /// Creates an `Identity` for given application 13 12 /// The keypair generated will be stored in OS secure storage 14 - /// Returns an `Identity` 13 + /// 14 + /// # Arguments 15 + /// 16 + /// - `app`: The service for which Identity is made (for ex: tiles) 15 17 pub fn create_identity(app: &str) -> Result<Identity> { 16 18 let mut csprng = OsRng; 17 19 let signing_key = SigningKey::generate(&mut csprng);
+28 -22
tiles/src/commands/mod.rs
··· 4 4 use owo_colors::OwoColorize; 5 5 use tiles::runtime::Runtime; 6 6 use tiles::utils::accounts::{ 7 - create_root_account, get_root_user_details, save_root_account, set_nickname, 7 + RootUser, create_root_account, get_root_user_details, save_root_account, set_nickname, 8 8 }; 9 9 use tiles::utils::config::{get_or_create_config, set_memory_path}; 10 10 use tiles::{core::health, runtime::RunArgs}; ··· 40 40 let _ = runtime.stop_server_daemon().await; 41 41 } 42 42 43 - //TODO: add docs 43 + /// Runs the account command with the args being passed. 44 44 pub fn run_account_commands(account_args: AccountArgs) -> Result<()> { 45 45 let config = get_or_create_config()?; 46 46 let root_user_details = get_root_user_details(&config)?; 47 47 match account_args.command { 48 48 Some(AccountCommands::Create { nickname }) => { 49 - //TODO: could show a message if did is already there 50 - let root_user_config = create_root_account(&config, nickname)?; 51 - let id = root_user_config.get("id").unwrap().as_str().unwrap(); 52 - save_root_account(config, &root_user_config)?; 53 - //TODO: color it... 54 - let _ = println!("Root account has been created with id: {}", id).green(); 49 + if !root_user_details.id.is_empty() { 50 + println!("Root account exists with id: {}", root_user_details.id) 51 + } else { 52 + let root_user_config = RootUser::new(&create_root_account(&config, nickname)?)?; 53 + 54 + save_root_account(config, &root_user_config.to_table())?; 55 + println!( 56 + "{}", 57 + format_args!( 58 + "Root account has been created with id: {}", 59 + root_user_config.id 60 + ) 61 + ) 62 + } 55 63 } 56 64 Some(AccountCommands::SetNickname { nickname }) => { 57 - //FIXME: redundant code 58 - if root_user_details.get("id").unwrap().is_empty() { 59 - println!( 60 - "Root account not created yet, use {}", 61 - format!("tiles account create").yellow() 62 - ); 65 + if root_user_details.id.is_empty() { 66 + println!("{}", get_account_not_created_msg()); 63 67 } else { 64 68 match set_nickname(&config, nickname) { 65 69 Ok(root_user_config) => { ··· 75 79 } 76 80 } 77 81 _ => { 78 - if root_user_details.get("id").unwrap().is_empty() { 79 - println!( 80 - "Root account not created yet, use {}", 81 - format!("tiles account create").yellow() 82 - ); 82 + if root_user_details.id.is_empty() { 83 + println!("{}", get_account_not_created_msg()); 83 84 } else { 84 - for elem in root_user_details { 85 - println!("{}: {}", elem.0, elem.1) 86 - } 85 + println!("{}", root_user_details); 87 86 } 88 87 } 89 88 } 90 89 91 90 Ok(()) 92 91 } 92 + 93 + fn get_account_not_created_msg() -> String { 94 + format!( 95 + "Root account not created yet, use {}", 96 + "tiles account create".yellow() 97 + ) 98 + }
+97 -38
tiles/src/utils/accounts.rs
··· 1 1 // Stuff related to account and identity system 2 - 3 - use std::collections::HashMap; 4 - 5 - use anyhow::Result; 2 + use anyhow::{Result, anyhow}; 3 + use std::fmt::Display; 6 4 use tilekit::accounts::create_identity; 7 5 use toml::Table; 8 6 9 7 use crate::utils::config::save_config; 10 8 const ROOT_USER_CONFIG_KEY: &str = "root-user"; 11 9 12 - //TODO: add docs 13 - pub fn get_root_user_details(config: &Table) -> Result<HashMap<String, String>> { 14 - Ok(get_root_account(&config)) 10 + #[allow(dead_code)] 11 + pub struct RootUser { 12 + pub id: String, 13 + pub nickname: String, 15 14 } 16 15 17 - fn get_root_account(config: &Table) -> HashMap<String, String> { 18 - let root_user = config.get(ROOT_USER_CONFIG_KEY).unwrap(); 19 - let root_user_table = root_user.as_table().unwrap(); 20 - let mut root_user_map = HashMap::new(); 21 - for ele in root_user_table { 22 - root_user_map.insert(ele.0.to_string(), ele.1.as_str().unwrap().to_owned()); 16 + impl Display for RootUser { 17 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 18 + write!(f, "id: {}\nnickname: {}\n", self.id, self.nickname) 23 19 } 24 - root_user_map 25 20 } 26 21 27 - // Lets return main config table only, let the caller do whatever it wants... 28 - // TODO: Needed docs 22 + impl RootUser { 23 + pub fn new(config: &Table) -> Result<Self> { 24 + let id = config 25 + .get("id") 26 + .ok_or_else(|| anyhow!("Missing ID"))? 27 + .as_str() 28 + .ok_or_else(|| anyhow!("ID not a string"))?; 29 + let nickname = config 30 + .get("nickname") 31 + .ok_or_else(|| anyhow!("Missing Nickname"))? 32 + .as_str() 33 + .ok_or_else(|| anyhow!("Nickname not a string"))?; 34 + Ok(RootUser { 35 + id: id.to_owned(), 36 + nickname: nickname.to_owned(), 37 + }) 38 + } 39 + 40 + pub fn to_table(&self) -> Table { 41 + let mut root_user_table = Table::new(); 42 + root_user_table.insert(String::from("id"), toml::Value::String(self.id.clone())); 43 + root_user_table.insert( 44 + String::from("nickname"), 45 + toml::Value::String(self.nickname.clone()), 46 + ); 47 + root_user_table 48 + } 49 + } 50 + 51 + /// Returns a `RootUser`, which represents a root user 52 + /// 53 + /// # Params 54 + /// 55 + /// - config: A `Table` type of entire config.toml file 56 + pub fn get_root_user_details(config: &Table) -> Result<RootUser> { 57 + let root_user = config 58 + .get(ROOT_USER_CONFIG_KEY) 59 + .ok_or_else(|| anyhow!("root-user key not found"))?; 60 + let root_user_table = root_user 61 + .as_table() 62 + .ok_or_else(|| anyhow!("root user not a table"))?; 63 + RootUser::new(root_user_table) 64 + } 65 + 66 + /// Create a root account 67 + /// Stores the private credentials in OS secure password manager 68 + /// 69 + /// # Params 70 + /// 71 + /// - config: A `Table` type of entire config.toml file 72 + /// - nickname: Nickname for the identity (Optional) 73 + /// 74 + /// Returns the root_user_config as a `Table` type 29 75 pub fn create_root_account(config: &Table, nickname: Option<String>) -> Result<Table> { 30 - let root_user = config.get(ROOT_USER_CONFIG_KEY).unwrap(); 31 - let root_user_table = root_user.as_table().unwrap(); 32 - let did = root_user_table.get("id").unwrap().as_str().unwrap(); 76 + let root_user = config 77 + .get(ROOT_USER_CONFIG_KEY) 78 + .ok_or_else(|| anyhow!("{} doesn't exist", ROOT_USER_CONFIG_KEY))?; 79 + let root_user_table = root_user 80 + .as_table() 81 + .ok_or_else(|| anyhow!("Failed to parse root user info"))?; 82 + let root_user_data = RootUser::new(root_user_table)?; 83 + let did = root_user_data.id; 33 84 if did.is_empty() { 34 - let root_user_config = create_root_user(root_user_table, nickname)?; 35 - Ok(root_user_config) 85 + Ok(create_root_user(root_user_table, nickname)?) 36 86 } else { 37 87 Ok(root_user_table.clone()) 38 88 } 39 89 } 40 90 41 - //TODO: docs 91 + /// Save the root config in `Table` type to config.toml 92 + /// 93 + /// # Params 94 + /// 95 + /// - config: A `Table` type of entire config.toml file 96 + /// - root_user_config: A `Table` type of root user 42 97 pub fn save_root_account(mut config: Table, root_user_config: &Table) -> Result<()> { 43 98 config.insert( 44 99 String::from(ROOT_USER_CONFIG_KEY), ··· 47 102 save_config(&config) 48 103 } 49 104 50 - // TODO: add docs 105 + /// Sets nickname for the root account 106 + /// 107 + /// # Params 108 + /// 109 + /// - config: A `Table` type of entire config.toml file 110 + /// - nickname: Nickname for the identity 111 + /// 112 + /// Returns the root_user_config as a `Table` type 51 113 pub fn set_nickname(config: &Table, nickname: String) -> Result<Table> { 52 114 let root_user = config.get(ROOT_USER_CONFIG_KEY).unwrap(); 53 115 let mut root_user_table = root_user.as_table().unwrap().clone(); ··· 61 123 } 62 124 } 63 125 64 - // TODO: add docs 65 126 fn create_root_user(root_user_config: &Table, nickname: Option<String>) -> Result<Table> { 66 - // get root user details 67 127 let mut root_user_table = root_user_config.clone(); 68 128 match create_identity("tiles") { 69 129 Ok(did) => { 70 130 root_user_table.insert("id".to_owned(), toml::Value::String(did)); 71 - if nickname.is_some() { 72 - root_user_table.insert( 73 - "nickname".to_owned(), 74 - toml::Value::String(nickname.unwrap()), 75 - ); 131 + if let Some(nickname) = nickname { 132 + root_user_table.insert("nickname".to_owned(), toml::Value::String(nickname)); 76 133 } 77 134 Ok(root_user_table) 78 135 } ··· 81 138 } 82 139 83 140 #[cfg(test)] 84 - 85 141 mod tests { 142 + use anyhow::Result; 86 143 use keyring::{mock, set_default_credential_builder}; 87 144 use toml::Table; 88 145 89 - use crate::utils::accounts::{create_root_account, get_root_account}; 146 + use crate::utils::accounts::{create_root_account, get_root_user_details}; 90 147 91 148 #[test] 92 - fn test_get_root_user_details_empty_id() { 149 + fn test_get_root_user_details_empty_id() -> Result<()> { 93 150 let config: Table = toml::from_str( 94 151 r#" 95 152 [root-user] ··· 98 155 "#, 99 156 ) 100 157 .unwrap(); 101 - let acc_details = get_root_account(&config); 102 - assert!(acc_details.get("id").unwrap().is_empty()); 158 + let acc_details = get_root_user_details(&config)?; 159 + assert!(acc_details.id.is_empty()); 160 + Ok(()) 103 161 } 104 162 105 163 #[test] 106 - fn test_get_root_user_details_valid_id() { 164 + fn test_get_root_user_details_valid_id() -> Result<()> { 107 165 let config: Table = toml::from_str( 108 166 r#" 109 167 [root-user] ··· 112 170 "#, 113 171 ) 114 172 .unwrap(); 115 - let acc_details = get_root_account(&config); 116 - assert!(acc_details.get("id").unwrap().contains("did:key")); 173 + let acc_details = get_root_user_details(&config)?; 174 + assert!(acc_details.id.contains("did:key")); 175 + Ok(()) 117 176 } 118 177 119 178 #[test]
+6 -5
tiles/src/utils/config.rs
··· 1 1 // Configuration related stuff 2 2 3 3 use anyhow::{Context, Result}; 4 - use std::fs::File; 5 4 use std::path::PathBuf; 6 5 use std::str::FromStr; 7 6 use std::{env, fs}; ··· 144 143 } 145 144 } 146 145 146 + /// Saves the root config toml `Table` type 147 147 pub fn save_config(config: &Table) -> Result<()> { 148 148 let tiles_config_dir = DefaultProvider.get_config_dir()?; 149 - let config_toml_path = tiles_config_dir.join("config.toml"); 150 - 151 - fs::write(config_toml_path, config.to_string())?; 149 + let config_path = tiles_config_dir.join("config.toml"); 150 + let tmp_path = tiles_config_dir.join("config.tmp.toml"); 151 + fs::write(&tmp_path, config.to_string())?; 152 + fs::copy(&tmp_path, &config_path)?; 153 + fs::remove_file(tmp_path)?; 152 154 Ok(()) 153 155 } 154 156 155 - // pub fn save_config(config: &Table) 156 157 //TODO: Add more tests for config.toml 157 158 #[cfg(test)] 158 159 mod tests {
-1
tiles/tests/config.rs
··· 63 63 // assert!(default.ends_with("data/memory") || default.ends_with("data\\memory")); 64 64 // Ok(()) 65 65 // } 66 -