From 5751463942cc91f1f1ffaf6e2ac633d7a0085f25 Mon Sep 17 00:00:00 2001 From: Ellie Huxtable Date: Tue, 13 Apr 2021 19:14:07 +0100 Subject: 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 --- src/local/database.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) (limited to 'src/local/database.rs') diff --git a/src/local/database.rs b/src/local/database.rs index ad7078e5..977f11cc 100644 --- a/src/local/database.rs +++ b/src/local/database.rs @@ -1,3 +1,4 @@ +use chrono::prelude::*; use chrono::Utc; use std::path::Path; @@ -21,6 +22,10 @@ pub trait Database { fn update(&self, h: &History) -> Result<()>; fn history_count(&self) -> Result; + fn first(&self) -> Result; + fn last(&self) -> Result; + fn before(&self, timestamp: chrono::DateTime, count: i64) -> Result>; + fn prefix_search(&self, query: &str) -> Result>; } @@ -44,9 +49,7 @@ impl Sqlite { let conn = Connection::open(path)?; - if create { - Self::setup_db(&conn)?; - } + Self::setup_db(&conn)?; Ok(Self { conn }) } @@ -70,6 +73,14 @@ impl Sqlite { [], )?; + conn.execute( + "create table if not exists history_encrypted ( + id text primary key, + data blob not null + )", + [], + )?; + Ok(()) } @@ -87,7 +98,7 @@ impl Sqlite { ) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", params![ h.id, - h.timestamp, + h.timestamp.timestamp_nanos(), h.duration, h.exit, h.command, @@ -146,7 +157,7 @@ impl Database for Sqlite { "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, h.duration, h.exit, h.command, h.cwd, h.session, h.hostname], + params![h.id, h.timestamp.timestamp_nanos(), h.duration, h.exit, h.command, h.cwd, h.session, h.hostname], )?; Ok(()) @@ -183,6 +194,38 @@ impl Database for Sqlite { Ok(history_iter.filter_map(Result::ok).collect()) } + fn first(&self) -> Result { + 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 { + 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, count: i64) -> Result> { + 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> { let mut stmt = self.conn.prepare(query)?; @@ -218,7 +261,7 @@ fn history_from_sqlite_row( Ok(History { id, - timestamp: row.get(1)?, + timestamp: Utc.timestamp_nanos(row.get(1)?), duration: row.get(2)?, exit: row.get(3)?, command: row.get(4)?, -- cgit v1.3.1