summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEllie Huxtable <e@elm.sh>2021-04-20 21:53:07 +0100
committerGitHub <noreply@github.com>2021-04-20 20:53:07 +0000
commita21737e2b7f8d1e426726bdd7536033f299d476a (patch)
treee940afdff9c145d25d9a2895fd44a77d70719a2e /src
parent34888827f8a06de835cbe5833a06914f28cce514 (diff)
Use cargo workspaces (#37)
* Switch to Cargo workspaces Breaking things into "client", "server" and "common" makes managing the codebase much easier! client - anything running on a user's machine for adding history server - handles storing/syncing history and running a HTTP server common - request/response API definitions, common utils, etc * Update dockerfile
Diffstat (limited to 'src')
-rw-r--r--src/api.rs70
-rw-r--r--src/command/history.rs10
-rw-r--r--src/command/import.rs6
-rw-r--r--src/command/login.rs8
-rw-r--r--src/command/mod.rs40
-rw-r--r--src/command/register.rs8
-rw-r--r--src/command/search.rs5
-rw-r--r--src/command/server.rs15
-rw-r--r--src/command/stats.rs8
-rw-r--r--src/command/sync.rs6
-rw-r--r--src/local/api_client.rs95
-rw-r--r--src/local/database.rs272
-rw-r--r--src/local/encryption.rs108
-rw-r--r--src/local/history.rs66
-rw-r--r--src/local/import.rs176
-rw-r--r--src/local/mod.rs6
-rw-r--r--src/local/sync.rs141
-rw-r--r--src/main.rs39
-rw-r--r--src/server/auth.rs222
-rw-r--r--src/server/database.rs202
-rw-r--r--src/server/handlers/history.rs89
-rw-r--r--src/server/handlers/mod.rs6
-rw-r--r--src/server/handlers/user.rs140
-rw-r--r--src/server/mod.rs23
-rw-r--r--src/server/models.rs49
-rw-r--r--src/server/router.rs121
-rw-r--r--src/settings.rs172
-rw-r--r--src/utils.rs24
28 files changed, 60 insertions, 2067 deletions
diff --git a/src/api.rs b/src/api.rs
deleted file mode 100644
index 82ee6604..00000000
--- a/src/api.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use chrono::Utc;
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct UserResponse {
- pub username: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct RegisterRequest {
- pub email: String,
- pub username: String,
- pub password: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct RegisterResponse {
- pub session: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct LoginRequest {
- pub username: String,
- pub password: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct LoginResponse {
- pub session: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct AddHistoryRequest {
- pub id: String,
- pub timestamp: chrono::DateTime<Utc>,
- pub data: String,
- pub hostname: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct CountResponse {
- pub count: i64,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct SyncHistoryRequest {
- pub sync_ts: chrono::DateTime<chrono::FixedOffset>,
- pub history_ts: chrono::DateTime<chrono::FixedOffset>,
- pub host: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct SyncHistoryResponse {
- pub history: Vec<String>,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct ErrorResponse {
- pub reason: String,
-}
-
-impl ErrorResponse {
- pub fn reply(reason: &str, status: warp::http::StatusCode) -> impl warp::Reply {
- warp::reply::with_status(
- warp::reply::json(&ErrorResponse {
- reason: String::from(reason),
- }),
- status,
- )
- }
-}
diff --git a/src/command/history.rs b/src/command/history.rs
index 627efae4..2b691bac 100644
--- a/src/command/history.rs
+++ b/src/command/history.rs
@@ -4,10 +4,10 @@ use eyre::Result;
use fork::{fork, Fork};
use structopt::StructOpt;
-use crate::local::database::Database;
-use crate::local::history::History;
-use crate::local::sync;
-use crate::settings::Settings;
+use atuin_client::database::Database;
+use atuin_client::history::History;
+use atuin_client::settings::Settings;
+use atuin_client::sync;
#[derive(StructOpt)]
pub enum Cmd {
@@ -79,7 +79,7 @@ impl Cmd {
db.update(&h)?;
- if settings.local.should_sync()? {
+ if settings.should_sync()? {
match fork() {
Ok(Fork::Parent(child)) => {
debug!("launched sync background process with PID {}", child);
diff --git a/src/command/import.rs b/src/command/import.rs
index ae927aaf..56fb30a7 100644
--- a/src/command/import.rs
+++ b/src/command/import.rs
@@ -5,9 +5,9 @@ use directories::UserDirs;
use eyre::{eyre, Result};
use structopt::StructOpt;
-use crate::local::database::Database;
-use crate::local::history::History;
-use crate::local::import::Zsh;
+use atuin_client::database::Database;
+use atuin_client::history::History;
+use atuin_client::import::Zsh;
use indicatif::ProgressBar;
#[derive(StructOpt)]
diff --git a/src/command/login.rs b/src/command/login.rs
index 636ac0d3..eaeb297f 100644
--- a/src/command/login.rs
+++ b/src/command/login.rs
@@ -5,7 +5,7 @@ use std::io::prelude::*;
use eyre::{eyre, Result};
use structopt::StructOpt;
-use crate::settings::Settings;
+use atuin_client::settings::Settings;
#[derive(StructOpt)]
#[structopt(setting(structopt::clap::AppSettings::DeriveDisplayOrder))]
@@ -26,7 +26,7 @@ impl Cmd {
map.insert("username", self.username.clone());
map.insert("password", self.password.clone());
- let url = format!("{}/login", settings.local.sync_address);
+ let url = format!("{}/login", settings.sync_address);
let client = reqwest::blocking::Client::new();
let resp = client.post(url).json(&map).send()?;
@@ -38,11 +38,11 @@ impl Cmd {
let session = resp.json::<HashMap<String, String>>()?;
let session = session["session"].clone();
- let session_path = settings.local.session_path.as_str();
+ let session_path = settings.session_path.as_str();
let mut file = File::create(session_path)?;
file.write_all(session.as_bytes())?;
- let key_path = settings.local.key_path.as_str();
+ let key_path = settings.key_path.as_str();
let mut file = File::create(key_path)?;
file.write_all(&base64::decode(self.key.clone())?)?;
diff --git a/src/command/mod.rs b/src/command/mod.rs
index cd857e9f..6fd52613 100644
--- a/src/command/mod.rs
+++ b/src/command/mod.rs
@@ -1,9 +1,12 @@
+use std::path::PathBuf;
+
use eyre::Result;
use structopt::StructOpt;
-use uuid::Uuid;
-use crate::local::database::Database;
-use crate::settings::Settings;
+use atuin_client::database::Sqlite;
+use atuin_client::settings::Settings as ClientSettings;
+use atuin_common::utils::uuid_v4;
+use atuin_server::settings::Settings as ServerSettings;
mod event;
mod history;
@@ -58,30 +61,33 @@ pub enum AtuinCmd {
Key,
}
-pub fn uuid_v4() -> String {
- Uuid::new_v4().to_simple().to_string()
-}
-
impl AtuinCmd {
- pub async fn run<T: Database + Send>(self, db: &mut T, settings: &Settings) -> Result<()> {
+ pub async fn run(self) -> Result<()> {
+ let client_settings = ClientSettings::new()?;
+ let server_settings = ServerSettings::new()?;
+
+ let db_path = PathBuf::from(client_settings.db_path.as_str());
+
+ let mut db = Sqlite::new(db_path)?;
+
match self {
- Self::History(history) => history.run(settings, db).await,
- Self::Import(import) => import.run(db),
- Self::Server(server) => server.run(settings).await,
- Self::Stats(stats) => stats.run(db, settings),
+ Self::History(history) => history.run(&client_settings, &mut db).await,
+ Self::Import(import) => import.run(&mut db),
+ Self::Server(server) => server.run(&server_settings).await,
+ Self::Stats(stats) => stats.run(&mut db, &client_settings),
Self::Init => init::init(),
- Self::Search { query } => search::run(&query, db),
+ Self::Search { query } => search::run(&query, &mut db),
- Self::Sync { force } => sync::run(settings, force, db).await,
- Self::Login(l) => l.run(settings),
+ Self::Sync { force } => sync::run(&client_settings, force, &mut db).await,
+ Self::Login(l) => l.run(&client_settings),
Self::Register(r) => register::run(
- settings,
+ &client_settings,
r.username.as_str(),
r.email.as_str(),
r.password.as_str(),
),
Self::Key => {
- let key = std::fs::read(settings.local.key_path.as_str())?;
+ let key = std::fs::read(client_settings.key_path.as_str())?;
println!("{}", base64::encode(key));
Ok(())
}
diff --git a/src/command/register.rs b/src/command/register.rs
index 62bbeaeb..1126645a 100644
--- a/src/command/register.rs
+++ b/src/command/register.rs
@@ -5,7 +5,7 @@ use std::io::prelude::*;
use eyre::{eyre, Result};
use structopt::StructOpt;
-use crate::settings::Settings;
+use atuin_client::settings::Settings;
#[derive(StructOpt)]
#[structopt(setting(structopt::clap::AppSettings::DeriveDisplayOrder))]
@@ -26,7 +26,7 @@ pub fn run(settings: &Settings, username: &str, email: &str, password: &str) ->
map.insert("email", email);
map.insert("password", password);
- let url = format!("{}/user/{}", settings.local.sync_address, username);
+ let url = format!("{}/user/{}", settings.sync_address, username);
let resp = reqwest::blocking::get(url)?;
if resp.status().is_success() {
@@ -34,7 +34,7 @@ pub fn run(settings: &Settings, username: &str, email: &str, password: &str) ->
return Ok(());
}
- let url = format!("{}/register", settings.local.sync_address);
+ let url = format!("{}/register", settings.sync_address);
let client = reqwest::blocking::Client::new();
let resp = client.post(url).json(&map).send()?;
@@ -46,7 +46,7 @@ pub fn run(settings: &Settings, username: &str, email: &str, password: &str) ->
let session = resp.json::<HashMap<String, String>>()?;
let session = session["session"].clone();
- let path = settings.local.session_path.as_str();
+ let path = settings.session_path.as_str();
let mut file = File::create(path)?;
file.write_all(session.as_bytes())?;
diff --git a/src/command/search.rs b/src/command/search.rs
index d7b477da..773c112f 100644
--- a/src/command/search.rs
+++ b/src/command/search.rs
@@ -14,9 +14,10 @@ use tui::{
};
use unicode_width::UnicodeWidthStr;
+use atuin_client::database::Database;
+use atuin_client::history::History;
+
use crate::command::event::{Event, Events};
-use crate::local::database::Database;
-use crate::local::history::History;
const VERSION: &str = env!("CARGO_PKG_VERSION");
diff --git a/src/command/server.rs b/src/command/server.rs
index a7835092..2fcf23d6 100644
--- a/src/command/server.rs
+++ b/src/command/server.rs
@@ -1,8 +1,8 @@
use eyre::Result;
use structopt::StructOpt;
-use crate::server;
-use crate::settings::Settings;
+use atuin_server::launch;
+use atuin_server::settings::Settings;
#[derive(StructOpt)]
pub enum Cmd {
@@ -23,13 +23,12 @@ impl Cmd {
pub async fn run(&self, settings: &Settings) -> Result<()> {
match self {
Self::Start { host, port } => {
- let host = host.as_ref().map_or(
- settings.server.host.clone(),
- std::string::ToString::to_string,
- );
- let port = port.map_or(settings.server.port, |p| p);
+ let host = host
+ .as_ref()
+ .map_or(settings.host.clone(), std::string::ToString::to_string);
+ let port = port.map_or(settings.port, |p| p);
- server::launch(settings, host, port).await
+ launch(settings, host, port).await
}
}
}
diff --git a/src/command/stats.rs b/src/command/stats.rs
index 694484bc..0da303d7 100644
--- a/src/command/stats.rs
+++ b/src/command/stats.rs
@@ -8,9 +8,9 @@ use cli_table::{format::Justify, print_stdout, Cell, Style, Table};
use eyre::{eyre, Result};
use structopt::StructOpt;
-use crate::local::database::Database;
-use crate::local::history::History;
-use crate::settings::Settings;
+use atuin_client::database::Database;
+use atuin_client::history::History;
+use atuin_client::settings::Settings;
#[derive(StructOpt)]
pub enum Cmd {
@@ -80,7 +80,7 @@ impl Cmd {
words.join(" ")
};
- let start = match settings.local.dialect.to_lowercase().as_str() {
+ let start = match settings.dialect.to_lowercase().as_str() {
"uk" => parse_date_string(&words, Local::now(), Dialect::Uk)?,
_ => parse_date_string(&words, Local::now(), Dialect::Us)?,
};
diff --git a/src/command/sync.rs b/src/command/sync.rs
index 88217b3c..d70b554f 100644
--- a/src/command/sync.rs
+++ b/src/command/sync.rs
@@ -1,8 +1,8 @@
use eyre::Result;
-use crate::local::database::Database;
-use crate::local::sync;
-use crate::settings::Settings;
+use atuin_client::database::Database;
+use atuin_client::settings::Settings;
+use atuin_client::sync;
pub async fn run(settings: &Settings, force: bool, db: &mut (impl Database + Send)) -> Result<()> {
sync::sync(settings, force, db).await?;
diff --git a/src/local/api_client.rs b/src/local/api_client.rs
deleted file mode 100644
index 1b64a295..00000000
--- a/src/local/api_client.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-use chrono::Utc;
-use eyre::Result;
-use reqwest::header::{HeaderMap, AUTHORIZATION};
-use reqwest::Url;
-use sodiumoxide::crypto::secretbox;
-
-use crate::api::{AddHistoryRequest, CountResponse, SyncHistoryResponse};
-use crate::local::encryption::decrypt;
-use crate::local::history::History;
-use crate::utils::hash_str;
-
-pub struct Client<'a> {
- sync_addr: &'a str,
- token: &'a str,
- key: secretbox::Key,
- client: reqwest::Client,
-}
-
-impl<'a> Client<'a> {
- pub fn new(sync_addr: &'a str, token: &'a str, key: secretbox::Key) -> Self {
- Client {
- sync_addr,
- token,
- key,
- client: reqwest::Client::new(),
- }
- }
-
- pub async fn count(&self) -> Result<i64> {
- let url = format!("{}/sync/count", self.sync_addr);
- let url = Url::parse(url.as_str())?;
- let token = format!("Token {}", self.token);
- let token = token.parse()?;
-
- let mut headers = HeaderMap::new();
- headers.insert(AUTHORIZATION, token);
-
- let resp = self.client.get(url).headers(headers).send().await?;
-
- let count = resp.json::<CountResponse>().await?;
-
- Ok(count.count)
- }
-
- pub async fn get_history(
- &self,
- sync_ts: chrono::DateTime<Utc>,
- history_ts: chrono::DateTime<Utc>,
- host: Option<String>,
- ) -> Result<Vec<History>> {
- let host = match host {
- None => hash_str(&format!("{}:{}", whoami::hostname(), whoami::username())),
- Some(h) => h,
- };
-
- let url = format!(
- "{}/sync/history?sync_ts={}&history_ts={}&host={}",
- self.sync_addr,
- urlencoding::encode(sync_ts.to_rfc3339().as_str()),
- urlencoding::encode(history_ts.to_rfc3339().as_str()),
- host,
- );
-
- let resp = self
- .client
- .get(url)
- .header(AUTHORIZATION, format!("Token {}", self.token))
- .send()
- .await?;
-
- let history = resp.json::<SyncHistoryResponse>().await?;
- let history = history
- .history
- .iter()
- .map(|h| serde_json::from_str(h).expect("invalid base64"))
- .map(|h| decrypt(&h, &self.key).expect("failed to decrypt history! check your key"))
- .collect();
-
- Ok(history)
- }
-
- pub async fn post_history(&self, history: &[AddHistoryRequest]) -> Result<()> {
- let url = format!("{}/history", self.sync_addr);
- let url = Url::parse(url.as_str())?;
-
- self.client
- .post(url)
- .json(history)
- .header(AUTHORIZATION, format!("Token {}", self.token))
- .send()
- .await?;
-
- Ok(())
- }
-}
diff --git a/src/local/database.rs b/src/local/database.rs
deleted file mode 100644
index abc22bb8..00000000
--- a/src/local/database.rs
+++ /dev/null
@@ -1,272 +0,0 @@
-use chrono::prelude::*;
-use chrono::Utc;
-use std::path::Path;
-
-use eyre::Result;
-
-use rusqlite::{params, Connection};
-use rusqlite::{Params, Transaction};
-
-use super::history::History;
-
-pub trait Database {
- fn save(&mut self, h: &History) -> Result<()>;
- fn save_bulk(&mut self, h: &[History]) -> Result<()>;
-
- fn load(&self, id: &str) -> Result<History>;
- fn list(&self) -> Result<Vec<History>>;
- fn range(&self, from: chrono::DateTime<Utc>, to: chrono::DateTime<Utc>)
- -> Result<Vec<History>>;
-
- fn query(&self, query: &str, params: impl Params) -> Result<Vec<History>>;
- fn update(&self, h: &History) -> Result<()>;
- fn history_count(&self) -> Result<i64>;
-
- fn first(&self) -> Result<History>;
- fn last(&self) -> Result<History>;
- fn before(&self, timestamp: chrono::DateTime<Utc>, count: i64) -> Result<Vec<History>>;
-
- fn prefix_search(&self, query: &str) -> Result<Vec<History>>;
-}
-
-// Intended for use on a developer machine and not a sync server.
-// TODO: implement IntoIterator
-pub struct Sqlite {
- conn: Connection,
-}
-
-impl Sqlite {
- pub fn new(path: impl AsRef<Path>) -> Result<Self> {
- let path = path.as_ref();
- debug!("opening sqlite database at {:?}", path);
-
- let create = !path.exists();
- if create {
- if let Some(dir) = path.parent() {
- std::fs::create_dir_all(dir)?;
- }
- }
-
- let conn = Connection::open(path)?;
-
- Self::setup_db(&conn)?;
-
- Ok(Self { conn })
- }
-
- fn setup_db(conn: &Connection) -> Result<()> {
- debug!("running sqlite database setup");
-
- conn.execute(
- "create table if not exists history (
- id text primary key,
- timestamp integer not null,
- duration integer not null,
- exit integer not null,
- command text not null,
- cwd text not null,
- session text not null,
- hostname text not null,
-
- unique(timestamp, cwd, command)
- )",
- [],
- )?;
-
- conn.execute(
- "create table if not exists history_encrypted (
- id text primary key,
- data blob not null
- )",
- [],
- )?;
-
- Ok(())
- }
-
- fn save_raw(tx: &Transaction, h: &History) -> Result<()> {
- tx.execute(
- "insert or ignore into history (
- id,
- timestamp,
- duration,
- exit,
- command,
- cwd,
- session,
- hostname
- ) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
- params![
- h.id,
- h.timestamp.timestamp_nanos(),
- h.duration,
- h.exit,
- h.command,
- h.cwd,
- h.session,
- h.hostname
- ],
- )?;
-
- Ok(())
- }
-}
-
-impl Database for Sqlite {
- fn save(&mut self, h: &History) -> Result<()> {
- debug!("saving history to sqlite");
-
- let tx = self.conn.transaction()?;
- Self::save_raw(&tx, h)?;
- tx.commit()?;
-
- Ok(())
- }
-
- fn save_bulk(&mut self, h: &[History]) -> Result<()> {
- debug!("saving history to sqlite");
-
- let tx = self.conn.transaction()?;
- for i in h {
- Self::save_raw(&tx, i)?
- }
- tx.commit()?;
-
- Ok(())
- }
-
- fn load(&self, id: &str) -> Result<History> {
- debug!("loading history item");
-
- let mut stmt = self.conn.prepare(
- "select id, timestamp, duration, exit, command, cwd, session, hostname from history
- where id = ?1",
- )?;
-
- let history = stmt.query_row(params![id], |row| {
- history_from_sqlite_row(Some(id.to_string()), row)
- })?;
-
- Ok(history)
- }
-
- fn update(&self, h: &History) -> Result<()> {
- debug!("updating sqlite history");
-
- self.conn.execute(
- "update history
- set timestamp = ?2, duration = ?3, exit = ?4, command = ?5, cwd = ?6, session = ?7, hostname = ?8
- where id = ?1",
- params![h.id, h.timestamp.timestamp_nanos(), h.duration, h.exit, h.command, h.cwd, h.session, h.hostname],
- )?;
-
- Ok(())
- }
-
- fn list(&self) -> Result<Vec<History>> {
- debug!("listing history");
-
- let mut stmt = self
- .conn
- .prepare("SELECT * FROM history order by timestamp asc")?;
-
- let history_iter = stmt.query_map(params![], |row| history_from_sqlite_row(None, row))?;
-
- Ok(history_iter.filter_map(Result::ok).collect())
- }
-
- fn range(
- &self,
- from: chrono::DateTime<Utc>,
- to: chrono::DateTime<Utc>,
- ) -> Result<Vec<History>> {
- debug!("listing history from {:?} to {:?}", from, to);
-
- let mut stmt = self.conn.prepare(
- "SELECT * FROM history where timestamp >= ?1 and timestamp <= ?2 order by timestamp asc",
- )?;
-
- let history_iter = stmt.query_map(
- params![from.timestamp_nanos(), to.timestamp_nanos()],
- |row| history_from_sqlite_row(None, row),
- )?;
-
- Ok(history_iter.filter_map(Result::ok).collect())
- }
-
- fn first(&self) -> Result<History> {
- let mut stmt = self
- .conn
- .prepare("SELECT * FROM history order by timestamp asc limit 1")?;
-
- let history = stmt.query_row(params![], |row| history_from_sqlite_row(None, row))?;
-
- Ok(history)
- }
-
- fn last(&self) -> Result<History> {
- let mut stmt = self
- .conn
- .prepare("SELECT * FROM history order by timestamp desc limit 1")?;
-
- let history = stmt.query_row(params![], |row| history_from_sqlite_row(None, row))?;
-
- Ok(history)
- }
-
- fn before(&self, timestamp: chrono::DateTime<Utc>, count: i64) -> Result<Vec<History>> {
- let mut stmt = self
- .conn
- .prepare("SELECT * FROM history where timestamp < ? order by timestamp desc limit ?")?;
-
- let history_iter = stmt.query_map(params![timestamp.timestamp_nanos(), count], |row| {
- history_from_sqlite_row(None, row)
- })?;
-
- Ok(history_iter.filter_map(Result::ok).collect())
- }
-
- fn query(&self, query: &str, params: impl Params) -> Result<Vec<History>> {
- let mut stmt = self.conn.prepare(query)?;
-
- let history_iter = stmt.query_map(params, |row| history_from_sqlite_row(None, row))?;
-
- Ok(history_iter.filter_map(Result::ok).collect())
- }
-
- fn prefix_search(&self, query: &str) -> Result<Vec<History>> {
- self.query(
- "select * from history where command like ?1 || '%' order by timestamp asc limit 1000",
- &[query],
- )
- }
-
- fn history_count(&self) -> Result<i64> {
- let res: i64 =
- self.conn
- .query_row_and_then("select count(1) from history;", params![], |row| row.get(0))?;
-
- Ok(res)
- }
-}
-
-fn history_from_sqlite_row(
- id: Option<String>,
- row: &rusqlite::Row,
-) -> Result<History, rusqlite::Error> {
- let id = match id {
- Some(id) => id,
- None => row.get(0)?,
- };
-
- Ok(History {
- id,
- timestamp: Utc.timestamp_nanos(row.get(1)?),
- duration: row.get(2)?,
- exit: row.get(3)?,
- command: row.get(4)?,
- cwd: row.get(5)?,
- session: row.get(6)?,
- hostname: row.get(7)?,
- })
-}
diff --git a/src/local/encryption.rs b/src/local/encryption.rs
deleted file mode 100644
index 3c1699e3..00000000
--- a/src/local/encryption.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-// The general idea is that we NEVER send cleartext history to the server
-// This way the odds of anything private ending up where it should not are
-// very low
-// The server authenticates via the usual username and password. This has
-// nothing to do with the encryption, and is purely authentication! The client
-// generates its own secret key, and encrypts all shell history with libsodium's
-// secretbox. The data is then sent to the server, where it is stored. All
-// clients must share the secret in order to be able to sync, as it is needed
-// to decrypt
-
-use std::fs::File;
-use std::io::prelude::*;
-use std::path::PathBuf;
-
-use eyre::{eyre, Result};
-use sodiumoxide::crypto::secretbox;
-
-use crate::local::history::History;
-use crate::settings::Settings;
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct EncryptedHistory {
- pub ciphertext: Vec<u8>,
- pub nonce: secretbox::Nonce,
-}
-
-// Loads the secret key, will create + save if it doesn't exist
-pub fn load_key(settings: &Settings) -> Result<secretbox::Key> {
- let path = settings.local.key_path.as_str();
-
- if PathBuf::from(path).exists() {
- let bytes = std::fs::read(path)?;
- let key: secretbox::Key = rmp_serde::from_read_ref(&bytes)?;
- Ok(key)
- } else {
- let key = secretbox::gen_key();
- let buf = rmp_serde::to_vec(&key)?;
-
- let mut file = File::create(path)?;
- file.write_all(&buf)?;
-
- Ok(key)
- }
-}
-
-pub fn encrypt(history: &History, key: &secretbox::Key) -> Result<EncryptedHistory> {
- // serialize with msgpack
- let buf = rmp_serde::to_vec(history)?;
-
- let nonce = secretbox::gen_nonce();
-
- let ciphertext = secretbox::seal(&buf, &nonce, key);
-
- Ok(EncryptedHistory { ciphertext, nonce })
-}
-
-pub fn decrypt(encrypted_history: &EncryptedHistory, key: &secretbox::Key) -> Result<History> {
- let plaintext = secretbo