diff options
| author | Ellie Huxtable <e@elm.sh> | 2021-04-13 19:14:07 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-13 19:14:07 +0100 |
| commit | 5751463942cc91f1f1ffaf6e2ac633d7a0085f25 (patch) | |
| tree | f7b5b9a4702c4c3ef29aa60d36612f61ffeae052 /src/settings.rs | |
| parent | Update config (diff) | |
| download | atuin-5751463942cc91f1f1ffaf6e2ac633d7a0085f25.zip | |
Add history sync, resolves #13 (#31)
* Add encryption
* Add login and register command
* Add count endpoint
* Write initial sync push
* Add single sync command
Confirmed working for one client only
* Automatically sync on a configurable frequency
* Add key command, key arg to login
* Only load session if it exists
* Use sync and history timestamps for download
* Bind other key code
Seems like some systems have this code for up arrow? I'm not sure why,
and it's not an easy one to google.
* Simplify upload
* Try and fix download sync loop
* Change sync order to avoid uploading what we just downloaded
* Multiline import fix
* Fix time parsing
* Fix importing history with no time
* Add hostname to sync
* Use hostname to filter sync
* Fixes
* Add binding
* Stuff from yesterday
* Set cursor modes
* Make clippy happy
* Bump version
Diffstat (limited to 'src/settings.rs')
| -rw-r--r-- | src/settings.rs | 131 |
1 files changed, 110 insertions, 21 deletions
diff --git a/src/settings.rs b/src/settings.rs index 0e554bed..dcf69a7c 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,31 +1,90 @@ -use std::path::PathBuf; +use std::fs::{create_dir_all, File}; +use std::io::prelude::*; +use std::path::{Path, PathBuf}; -use config::{Config, File}; +use chrono::prelude::*; +use chrono::Utc; +use config::{Config, File as ConfigFile}; use directories::ProjectDirs; use eyre::{eyre, Result}; -use std::fs; +use parse_duration::parse; -#[derive(Debug, Deserialize)] +pub const HISTORY_PAGE_SIZE: i64 = 100; + +#[derive(Clone, Debug, Deserialize)] pub struct Local { pub dialect: String, - pub sync: bool, + pub auto_sync: bool, pub sync_address: String, pub sync_frequency: String, pub db_path: String, + pub key_path: String, + pub session_path: String, + + // This is automatically loaded when settings is created. Do not set in + // config! Keep secrets and settings apart. + pub session_token: String, } -#[derive(Debug, Deserialize)] -pub struct Remote { +impl Local { + pub fn save_sync_time() -> Result<()> { + let sync_time_path = ProjectDirs::from("com", "elliehuxtable", "atuin") + .ok_or_else(|| eyre!("could not determine key file location"))?; + let sync_time_path = sync_time_path.data_dir().join("last_sync_time"); + + std::fs::write(sync_time_path, Utc::now().to_rfc3339())?; + + Ok(()) + } + + pub fn last_sync() -> Result<chrono::DateTime<Utc>> { + let sync_time_path = ProjectDirs::from("com", "elliehuxtable", "atuin"); + + if sync_time_path.is_none() { + debug!("failed to load projectdirs, not syncing"); + return Err(eyre!("could not load project dirs")); + } + + let sync_time_path = sync_time_path.unwrap(); + let sync_time_path = sync_time_path.data_dir().join("last_sync_time"); + + if !sync_time_path.exists() { + return Ok(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)); + } + + let time = std::fs::read_to_string(sync_time_path)?; + let time = chrono::DateTime::parse_from_rfc3339(time.as_str())?; + + Ok(time.with_timezone(&Utc)) + } + + pub fn should_sync(&self) -> Result<bool> { + if !self.auto_sync { + return Ok(false); + } + + match parse(self.sync_frequency.as_str()) { + Ok(d) => { + let d = chrono::Duration::from_std(d).unwrap(); + Ok(Utc::now() - Local::last_sync()? >= d) + } + Err(e) => Err(eyre!("failed to check sync: {}", e)), + } + } +} + +#[derive(Clone, Debug, Deserialize)] +pub struct Server { pub host: String, pub port: u16, pub db_uri: String, pub open_registration: bool, } -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize)] pub struct Settings { pub local: Local, - pub remote: Remote, + pub server: Server, } impl Settings { @@ -33,7 +92,7 @@ impl Settings { let config_dir = ProjectDirs::from("com", "elliehuxtable", "atuin").unwrap(); let config_dir = config_dir.config_dir(); - fs::create_dir_all(config_dir)?; + create_dir_all(config_dir)?; let mut config_file = PathBuf::new(); config_file.push(config_dir); @@ -45,31 +104,61 @@ impl Settings { let mut s = Config::new(); let db_path = ProjectDirs::from("com", "elliehuxtable", "atuin") - .ok_or_else(|| { - eyre!("could not determine db file location\nspecify one using the --db flag") - })? + .ok_or_else(|| eyre!("could not determine db file location"))? .data_dir() .join("history.db"); + let key_path = ProjectDirs::from("com", "elliehuxtable", "atuin") + .ok_or_else(|| eyre!("could not determine key file location"))? + .data_dir() + .join("key"); + + let session_path = ProjectDirs::from("com", "elliehuxtable", "atuin") + .ok_or_else(|| eyre!("could not determine session file location"))? + .data_dir() + .join("session"); + s.set_default("local.db_path", db_path.to_str())?; + s.set_default("local.key_path", key_path.to_str())?; + s.set_default("local.session_path", session_path.to_str())?; s.set_default("local.dialect", "us")?; - s.set_default("local.sync", false)?; + s.set_default("local.auto_sync", true)?; s.set_default("local.sync_frequency", "5m")?; - s.set_default("local.sync_address", "https://atuin.ellie.wtf")?; + s.set_default("local.sync_address", "https://api.atuin.sh")?; - s.set_default("remote.host", "127.0.0.1")?; - s.set_default("remote.port", 8888)?; - s.set_default("remote.open_registration", false)?; - s.set_default("remote.db_uri", "please set a postgres url")?; + s.set_default("server.host", "127.0.0.1")?; + s.set_default("server.port", 8888)?; + s.set_default("server.open_registration", false)?; + s.set_default("server.db_uri", "please set a postgres url")?; if config_file.exists() { - s.merge(File::with_name(config_file.to_str().unwrap()))?; + s.merge(ConfigFile::with_name(config_file.to_str().unwrap()))?; + } else { + let example_config = include_bytes!("../config.toml"); + let mut file = File::create(config_file)?; + file.write_all(example_config)?; } // all paths should be expanded let db_path = s.get_str("local.db_path")?; let db_path = shellexpand::full(db_path.as_str())?; - s.set("local.db.path", db_path.to_string())?; + s.set("local.db_path", db_path.to_string())?; + + let key_path = s.get_str("local.key_path")?; + let key_path = shellexpand::full(key_path.as_str())?; + s.set("local.key_path", key_path.to_string())?; + + let session_path = s.get_str("local.session_path")?; + let session_path = shellexpand::full(session_path.as_str())?; + s.set("local.session_path", session_path.to_string())?; + + // Finally, set the auth token + if Path::new(session_path.to_string().as_str()).exists() { + let token = std::fs::read_to_string(session_path.to_string())?; + s.set("local.session_token", token)?; + } else { + s.set("local.session_token", "not logged in")?; + } s.try_into() .map_err(|e| eyre!("failed to deserialize: {}", e)) |
