mirror of
https://github.com/atuinsh/atuin.git
synced 2025-08-17 18:41:37 +02:00
chore: migrate to rust 2024 (#2635)
* chore: upgrade to 2024 edition * ugh unsafe * format * nixxxxxxxxxxx why
This commit is contained in:
1119
Cargo.lock
generated
1119
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "atuin-client"
|
name = "atuin-client"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
description = "client library for atuin"
|
description = "client library for atuin"
|
||||||
|
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
@ -2,12 +2,16 @@ use std::collections::HashMap;
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use eyre::{bail, Result};
|
use eyre::{Result, bail};
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{HeaderMap, AUTHORIZATION, USER_AGENT},
|
|
||||||
Response, StatusCode, Url,
|
Response, StatusCode, Url,
|
||||||
|
header::{AUTHORIZATION, HeaderMap, USER_AGENT},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use atuin_common::{
|
||||||
|
api::{ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ATUIN_VERSION},
|
||||||
|
record::{EncryptedData, HostId, Record, RecordIdx},
|
||||||
|
};
|
||||||
use atuin_common::{
|
use atuin_common::{
|
||||||
api::{
|
api::{
|
||||||
AddHistoryRequest, ChangePasswordRequest, CountResponse, DeleteHistoryRequest,
|
AddHistoryRequest, ChangePasswordRequest, CountResponse, DeleteHistoryRequest,
|
||||||
@ -17,14 +21,10 @@ use atuin_common::{
|
|||||||
},
|
},
|
||||||
record::RecordStatus,
|
record::RecordStatus,
|
||||||
};
|
};
|
||||||
use atuin_common::{
|
|
||||||
api::{ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ATUIN_VERSION},
|
|
||||||
record::{EncryptedData, HostId, Record, RecordIdx},
|
|
||||||
};
|
|
||||||
|
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use time::format_description::well_known::Rfc3339;
|
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
use time::format_description::well_known::Rfc3339;
|
||||||
|
|
||||||
use crate::{history::History, sync::hash_str, utils::get_host_user};
|
use crate::{history::History, sync::hash_str, utils::get_host_user};
|
||||||
|
|
||||||
@ -126,7 +126,9 @@ pub fn ensure_version(response: &Response) -> Result<bool> {
|
|||||||
|
|
||||||
// If the client is newer than the server
|
// If the client is newer than the server
|
||||||
if version.major < ATUIN_VERSION.major {
|
if version.major < ATUIN_VERSION.major {
|
||||||
println!("Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin");
|
println!(
|
||||||
|
"Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin"
|
||||||
|
);
|
||||||
println!("Client: {}", ATUIN_CARGO_VERSION);
|
println!("Client: {}", ATUIN_CARGO_VERSION);
|
||||||
println!("Server: {}", version);
|
println!("Server: {}", version);
|
||||||
|
|
||||||
@ -157,10 +159,14 @@ async fn handle_resp_error(resp: Response) -> Result<Response> {
|
|||||||
bail!("Invalid request to the service: {status} - {reason}.")
|
bail!("Invalid request to the service: {status} - {reason}.")
|
||||||
}
|
}
|
||||||
|
|
||||||
bail!("There was an error with the atuin sync service, server error {status}: {reason}.\nIf the problem persists, contact the host")
|
bail!(
|
||||||
|
"There was an error with the atuin sync service, server error {status}: {reason}.\nIf the problem persists, contact the host"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
bail!("There was an error with the atuin sync service: Status {status:?}.\nIf the problem persists, contact the host")
|
bail!(
|
||||||
|
"There was an error with the atuin sync service: Status {status:?}.\nIf the problem persists, contact the host"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
|
@ -10,14 +10,14 @@ use async_trait::async_trait;
|
|||||||
use atuin_common::utils;
|
use atuin_common::utils;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{Rng, distributions::Alphanumeric};
|
||||||
use sql_builder::{bind::Bind, esc, quote, SqlBuilder, SqlName};
|
use sql_builder::{SqlBuilder, SqlName, bind::Bind, esc, quote};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
|
Result, Row,
|
||||||
sqlite::{
|
sqlite::{
|
||||||
SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqlitePoolOptions, SqliteRow,
|
SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqlitePoolOptions, SqliteRow,
|
||||||
SqliteSynchronous,
|
SqliteSynchronous,
|
||||||
},
|
},
|
||||||
Result, Row,
|
|
||||||
};
|
};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
@ -55,7 +55,9 @@ pub struct OptFilters {
|
|||||||
|
|
||||||
pub fn current_context() -> Context {
|
pub fn current_context() -> Context {
|
||||||
let Ok(session) = env::var("ATUIN_SESSION") else {
|
let Ok(session) = env::var("ATUIN_SESSION") else {
|
||||||
eprintln!("ERROR: Failed to find $ATUIN_SESSION in the environment. Check that you have correctly set up your shell.");
|
eprintln!(
|
||||||
|
"ERROR: Failed to find $ATUIN_SESSION in the environment. Check that you have correctly set up your shell."
|
||||||
|
);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
};
|
};
|
||||||
let hostname = get_host_user();
|
let hostname = get_host_user();
|
||||||
@ -131,7 +133,9 @@ impl Sqlite {
|
|||||||
debug!("opening sqlite database at {:?}", path);
|
debug!("opening sqlite database at {:?}", path);
|
||||||
|
|
||||||
if utils::broken_symlink(path) {
|
if utils::broken_symlink(path) {
|
||||||
eprintln!("Atuin: Sqlite db path ({path:?}) is a broken symlink. Unable to read or create replacement.");
|
eprintln!(
|
||||||
|
"Atuin: Sqlite db path ({path:?}) is a broken symlink. Unable to read or create replacement."
|
||||||
|
);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,17 +10,17 @@
|
|||||||
|
|
||||||
use std::{io::prelude::*, path::PathBuf};
|
use std::{io::prelude::*, path::PathBuf};
|
||||||
|
|
||||||
use base64::prelude::{Engine, BASE64_STANDARD};
|
use base64::prelude::{BASE64_STANDARD, Engine};
|
||||||
pub use crypto_secretbox::Key;
|
pub use crypto_secretbox::Key;
|
||||||
use crypto_secretbox::{
|
use crypto_secretbox::{
|
||||||
aead::{Nonce, OsRng},
|
|
||||||
AeadCore, AeadInPlace, KeyInit, XSalsa20Poly1305,
|
AeadCore, AeadInPlace, KeyInit, XSalsa20Poly1305,
|
||||||
|
aead::{Nonce, OsRng},
|
||||||
};
|
};
|
||||||
use eyre::{bail, ensure, eyre, Context, Result};
|
use eyre::{Context, Result, bail, ensure, eyre};
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use rmp::{decode::Bytes, Marker};
|
use rmp::{Marker, decode::Bytes};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::{format_description::well_known::Rfc3339, macros::format_description, OffsetDateTime};
|
use time::{OffsetDateTime, format_description::well_known::Rfc3339, macros::format_description};
|
||||||
|
|
||||||
use crate::{history::History, settings::Settings};
|
use crate::{history::History, settings::Settings};
|
||||||
|
|
||||||
@ -265,9 +265,9 @@ fn error_report<E: std::fmt::Debug>(err: E) -> eyre::Report {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crypto_secretbox::{aead::OsRng, KeyInit, XSalsa20Poly1305};
|
use crypto_secretbox::{KeyInit, XSalsa20Poly1305, aead::OsRng};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use time::{macros::datetime, OffsetDateTime};
|
use time::{OffsetDateTime, macros::datetime};
|
||||||
|
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
|
|
||||||
@ -392,7 +392,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn key_encodings() {
|
fn key_encodings() {
|
||||||
use super::{decode_key, encode_key, Key};
|
use super::{Key, decode_key, encode_key};
|
||||||
|
|
||||||
// a history of our key encodings.
|
// a history of our key encodings.
|
||||||
// v11.0.0 xCAbWypb0msJ2Kq+8j4GVEWUlDX7deKnrTRSIopuqXxc5Q==
|
// v11.0.0 xCAbWypb0msJ2Kq+8j4GVEWUlDX7deKnrTRSIopuqXxc5Q==
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use core::fmt::Formatter;
|
use core::fmt::Formatter;
|
||||||
use rmp::decode::ValueReadError;
|
use rmp::decode::ValueReadError;
|
||||||
use rmp::{decode::Bytes, Marker};
|
use rmp::{Marker, decode::Bytes};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use atuin_common::record::DecryptedData;
|
use atuin_common::record::DecryptedData;
|
||||||
use atuin_common::utils::uuid_v7;
|
use atuin_common::utils::uuid_v7;
|
||||||
|
|
||||||
use eyre::{bail, eyre, Result};
|
use eyre::{Result, bail, eyre};
|
||||||
|
|
||||||
use crate::secrets::SECRET_PATTERNS_RE;
|
use crate::secrets::SECRET_PATTERNS_RE;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use std::{collections::HashSet, fmt::Write, time::Duration};
|
use std::{collections::HashSet, fmt::Write, time::Duration};
|
||||||
|
|
||||||
use eyre::{bail, eyre, Result};
|
use eyre::{Result, bail, eyre};
|
||||||
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
|
||||||
use rmp::decode::Bytes;
|
use rmp::decode::Bytes;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::{current_context, Database},
|
database::{Database, current_context},
|
||||||
record::{encryption::PASETO_V4, sqlite_store::SqliteStore, store::Store},
|
record::{encryption::PASETO_V4, sqlite_store::SqliteStore, store::Store},
|
||||||
};
|
};
|
||||||
use atuin_common::record::{DecryptedData, Host, HostId, Record, RecordId, RecordIdx};
|
use atuin_common::record::{DecryptedData, Host, HostId, Record, RecordId, RecordIdx};
|
||||||
|
|
||||||
use super::{History, HistoryId, HISTORY_TAG, HISTORY_VERSION};
|
use super::{HISTORY_TAG, HISTORY_VERSION, History, HistoryId};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct HistoryStore {
|
pub struct HistoryStore {
|
||||||
@ -246,10 +246,11 @@ impl HistoryStore {
|
|||||||
for id in ids {
|
for id in ids {
|
||||||
let record = self.store.get(*id).await;
|
let record = self.store.get(*id).await;
|
||||||
|
|
||||||
let record = if let Ok(record) = record {
|
let record = match record {
|
||||||
record
|
Ok(record) => record,
|
||||||
} else {
|
_ => {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if record.tag != HISTORY_TAG {
|
if record.tag != HISTORY_TAG {
|
||||||
@ -342,7 +343,7 @@ mod tests {
|
|||||||
use atuin_common::record::DecryptedData;
|
use atuin_common::record::DecryptedData;
|
||||||
use time::macros::datetime;
|
use time::macros::datetime;
|
||||||
|
|
||||||
use crate::history::{store::HistoryRecord, HISTORY_VERSION};
|
use crate::history::{HISTORY_VERSION, store::HistoryRecord};
|
||||||
|
|
||||||
use super::History;
|
use super::History;
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@ use std::{path::PathBuf, str};
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use directories::UserDirs;
|
use directories::UserDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
use super::{get_histpath, unix_byte_lines, Importer, Loader};
|
use super::{Importer, Loader, get_histpath, unix_byte_lines};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
use crate::import::read_to_end;
|
use crate::import::read_to_end;
|
||||||
|
|
||||||
@ -73,7 +73,9 @@ impl Importer for Bash {
|
|||||||
LineType::Empty => {} // do nothing
|
LineType::Empty => {} // do nothing
|
||||||
LineType::Timestamp(t) => {
|
LineType::Timestamp(t) => {
|
||||||
if t < next_timestamp {
|
if t < next_timestamp {
|
||||||
warn!("Time reversal detected in Bash history! Commands may be ordered incorrectly.");
|
warn!(
|
||||||
|
"Time reversal detected in Bash history! Commands may be ordered incorrectly."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
next_timestamp = t;
|
next_timestamp = t;
|
||||||
}
|
}
|
||||||
@ -126,9 +128,9 @@ fn try_parse_line_as_timestamp(line: &str) -> Option<OffsetDateTime> {
|
|||||||
mod test {
|
mod test {
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use itertools::{assert_equal, Itertools};
|
use itertools::{Itertools, assert_equal};
|
||||||
|
|
||||||
use crate::import::{tests::TestLoader, Importer};
|
use crate::import::{Importer, tests::TestLoader};
|
||||||
|
|
||||||
use super::Bash;
|
use super::Bash;
|
||||||
|
|
||||||
|
@ -5,10 +5,10 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use directories::BaseDirs;
|
use directories::BaseDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use super::{unix_byte_lines, Importer, Loader};
|
use super::{Importer, Loader, unix_byte_lines};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
use crate::import::read_to_end;
|
use crate::import::read_to_end;
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ impl Importer for Fish {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use crate::import::{tests::TestLoader, Importer};
|
use crate::import::{Importer, tests::TestLoader};
|
||||||
|
|
||||||
use super::Fish;
|
use super::Fish;
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ ERROR
|
|||||||
|
|
||||||
// simple wrapper for fish history entry
|
// simple wrapper for fish history entry
|
||||||
macro_rules! fishtory {
|
macro_rules! fishtory {
|
||||||
($timestamp:expr, $command:expr) => {
|
($timestamp:expr_2021, $command:expr_2021) => {
|
||||||
let h = history.next().expect("missing entry in history");
|
let h = history.next().expect("missing entry in history");
|
||||||
assert_eq!(h.command.as_str(), $command);
|
assert_eq!(h.command.as_str(), $command);
|
||||||
assert_eq!(h.timestamp.unix_timestamp(), $timestamp);
|
assert_eq!(h.timestamp.unix_timestamp(), $timestamp);
|
||||||
|
@ -3,7 +3,7 @@ use std::io::Read;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use eyre::{bail, Result};
|
use eyre::{Result, bail};
|
||||||
use memchr::Memchr;
|
use memchr::Memchr;
|
||||||
|
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
|
@ -5,10 +5,10 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use directories::BaseDirs;
|
use directories::BaseDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use super::{unix_byte_lines, Importer, Loader};
|
use super::{Importer, Loader, unix_byte_lines};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
use crate::import::read_to_end;
|
use crate::import::read_to_end;
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use directories::BaseDirs;
|
use directories::BaseDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use sqlx::{sqlite::SqlitePool, Pool};
|
use sqlx::{Pool, sqlite::SqlitePool};
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
use super::Importer;
|
use super::Importer;
|
||||||
|
@ -2,10 +2,10 @@ use std::{path::PathBuf, str};
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use directories::UserDirs;
|
use directories::UserDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use time::{macros::format_description, OffsetDateTime, PrimitiveDateTime};
|
use time::{OffsetDateTime, PrimitiveDateTime, macros::format_description};
|
||||||
|
|
||||||
use super::{get_histpath, unix_byte_lines, Importer, Loader};
|
use super::{Importer, Loader, get_histpath, unix_byte_lines};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
use crate::import::read_to_end;
|
use crate::import::read_to_end;
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ fn try_parse_line_as_timestamp(line: &str) -> Option<OffsetDateTime> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use crate::import::{tests::TestLoader, Importer};
|
use crate::import::{Importer, tests::TestLoader};
|
||||||
|
|
||||||
use super::Replxx;
|
use super::Replxx;
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ CREATE TABLE test( stamp DateTime('UTC'))ENGINE = MergeTreePARTITION BY toDat
|
|||||||
|
|
||||||
// simple wrapper for replxx history entry
|
// simple wrapper for replxx history entry
|
||||||
macro_rules! history {
|
macro_rules! history {
|
||||||
($timestamp:expr, $command:expr) => {
|
($timestamp:expr_2021, $command:expr_2021) => {
|
||||||
let h = history.next().expect("missing entry in history");
|
let h = history.next().expect("missing entry in history");
|
||||||
assert_eq!(h.command.as_str(), $command);
|
assert_eq!(h.command.as_str(), $command);
|
||||||
assert_eq!(h.timestamp.unix_timestamp(), $timestamp);
|
assert_eq!(h.timestamp.unix_timestamp(), $timestamp);
|
||||||
@ -114,6 +114,9 @@ CREATE TABLE test( stamp DateTime('UTC'))ENGINE = MergeTreePARTITION BY toDat
|
|||||||
history!(1707603396, "select * from numbers(10)");
|
history!(1707603396, "select * from numbers(10)");
|
||||||
history!(1707603401, "select * from system.numbers");
|
history!(1707603401, "select * from system.numbers");
|
||||||
history!(1707603568, "select 1");
|
history!(1707603568, "select 1");
|
||||||
history!(1708600533, "CREATE TABLE test\n( stamp DateTime('UTC'))\nENGINE = MergeTree\nPARTITION BY toDate(stamp)\norder by tuple() as select toDateTime('2020-01-01')+number*60 from numbers(80000);");
|
history!(
|
||||||
|
1708600533,
|
||||||
|
"CREATE TABLE test\n( stamp DateTime('UTC'))\nENGINE = MergeTree\nPARTITION BY toDate(stamp)\norder by tuple() as select toDateTime('2020-01-01')+number*60 from numbers(80000);"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,13 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use directories::UserDirs;
|
use directories::UserDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use atuin_common::utils::uuid_v7;
|
use atuin_common::utils::uuid_v7;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use super::{get_histpath, unix_byte_lines, Importer, Loader};
|
use super::{Importer, Loader, get_histpath, unix_byte_lines};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
use crate::import::read_to_end;
|
use crate::import::read_to_end;
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use directories::BaseDirs;
|
use directories::BaseDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use uuid::timestamp::{context::NoContext, Timestamp};
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use uuid::timestamp::{Timestamp, context::NoContext};
|
||||||
|
|
||||||
use super::{get_histpath, Importer, Loader};
|
use super::{Importer, Loader, get_histpath};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
use crate::utils::get_host_user;
|
use crate::utils::get_host_user;
|
||||||
|
|
||||||
|
@ -3,14 +3,14 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use directories::BaseDirs;
|
use directories::BaseDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use sqlx::{sqlite::SqlitePool, FromRow, Row};
|
use sqlx::{FromRow, Row, sqlite::SqlitePool};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use uuid::timestamp::{context::NoContext, Timestamp};
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use uuid::timestamp::{Timestamp, context::NoContext};
|
||||||
|
|
||||||
use super::{get_histpath, Importer, Loader};
|
use super::{Importer, Loader, get_histpath};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
use crate::utils::get_host_user;
|
use crate::utils::get_host_user;
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use directories::UserDirs;
|
use directories::UserDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use super::{get_histpath, unix_byte_lines, Importer, Loader};
|
use super::{Importer, Loader, get_histpath, unix_byte_lines};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
use crate::import::read_to_end;
|
use crate::import::read_to_end;
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ fn default_histpath() -> Result<PathBuf> {
|
|||||||
None => {
|
None => {
|
||||||
break Err(eyre!(
|
break Err(eyre!(
|
||||||
"Could not find history file. Try setting and exporting $HISTFILE"
|
"Could not find history file. Try setting and exporting $HISTFILE"
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,8 @@ use std::path::{Path, PathBuf};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use atuin_common::utils::uuid_v7;
|
use atuin_common::utils::uuid_v7;
|
||||||
use directories::UserDirs;
|
use directories::UserDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use sqlx::{sqlite::SqlitePool, Pool};
|
use sqlx::{Pool, sqlite::SqlitePool};
|
||||||
use time::PrimitiveDateTime;
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
use super::Importer;
|
use super::Importer;
|
||||||
@ -178,10 +178,12 @@ mod test {
|
|||||||
use sqlx::sqlite::SqlitePoolOptions;
|
use sqlx::sqlite::SqlitePoolOptions;
|
||||||
use std::env;
|
use std::env;
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
#[allow(unsafe_code)]
|
||||||
async fn test_env_vars() {
|
async fn test_env_vars() {
|
||||||
let test_env_db = "nonstd-zsh-history.db";
|
let test_env_db = "nonstd-zsh-history.db";
|
||||||
let key = "HISTDB_FILE";
|
let key = "HISTDB_FILE";
|
||||||
env::set_var(key, test_env_db);
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
|
unsafe { env::set_var(key, test_env_db) };
|
||||||
|
|
||||||
// test the env got set
|
// test the env got set
|
||||||
assert_eq!(env::var(key).unwrap(), test_env_db.to_string());
|
assert_eq!(env::var(key).unwrap(), test_env_db.to_string());
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use atuin_common::record::{DecryptedData, Host, HostId};
|
use atuin_common::record::{DecryptedData, Host, HostId};
|
||||||
use eyre::{bail, ensure, eyre, Result};
|
use eyre::{Result, bail, ensure, eyre};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::record::encryption::PASETO_V4;
|
use crate::record::encryption::PASETO_V4;
|
||||||
@ -200,7 +200,7 @@ mod tests {
|
|||||||
use crate::record::sqlite_store::SqliteStore;
|
use crate::record::sqlite_store::SqliteStore;
|
||||||
use crate::settings::test_local_timeout;
|
use crate::settings::test_local_timeout;
|
||||||
|
|
||||||
use super::{KvRecord, KvStore, KV_VERSION};
|
use super::{KV_VERSION, KvRecord, KvStore};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_decode() {
|
fn encode_decode() {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use atuin_common::api::LoginRequest;
|
use atuin_common::api::LoginRequest;
|
||||||
use eyre::{bail, Context, Result};
|
use eyre::{Context, Result, bail};
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api_client,
|
api_client,
|
||||||
encryption::{decode_key, encode_key, load_key, Key},
|
encryption::{Key, decode_key, encode_key, load_key},
|
||||||
record::{sqlite_store::SqliteStore, store::Store},
|
record::{sqlite_store::SqliteStore, store::Store},
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
};
|
};
|
||||||
@ -23,7 +23,8 @@ pub async fn login(
|
|||||||
let key = match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) {
|
let key = match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) {
|
||||||
Ok(mnemonic) => encode_key(Key::from_slice(mnemonic.entropy()))?,
|
Ok(mnemonic) => encode_key(Key::from_slice(mnemonic.entropy()))?,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(err) = err.downcast_ref::<bip39::ErrorKind>() {
|
match err.downcast_ref::<bip39::ErrorKind>() {
|
||||||
|
Some(err) => {
|
||||||
match err {
|
match err {
|
||||||
// assume they copied in the base64 key
|
// assume they copied in the base64 key
|
||||||
bip39::ErrorKind::InvalidWord => key,
|
bip39::ErrorKind::InvalidWord => key,
|
||||||
@ -36,11 +37,13 @@ pub async fn login(
|
|||||||
bail!("key was not the correct length")
|
bail!("key was not the correct length")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
// unknown error. assume they copied the base64 key
|
// unknown error. assume they copied the base64 key
|
||||||
key
|
key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let key_path = settings.key_path.as_str();
|
let key_path = settings.key_path.as_str();
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use atuin_common::record::{
|
use atuin_common::record::{
|
||||||
AdditionalData, DecryptedData, EncryptedData, Encryption, HostId, RecordId, RecordIdx,
|
AdditionalData, DecryptedData, EncryptedData, Encryption, HostId, RecordId, RecordIdx,
|
||||||
};
|
};
|
||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{Engine, engine::general_purpose};
|
||||||
use eyre::{ensure, Context, Result};
|
use eyre::{Context, Result, ensure};
|
||||||
use rusty_paserk::{Key, KeyId, Local, PieWrappedKey};
|
use rusty_paserk::{Key, KeyId, Local, PieWrappedKey};
|
||||||
use rusty_paseto::core::{
|
use rusty_paseto::core::{
|
||||||
ImplicitAssertion, Key as DataKey, Local as LocalPurpose, Paseto, PasetoNonce, Payload, V4,
|
ImplicitAssertion, Key as DataKey, Local as LocalPurpose, Paseto, PasetoNonce, Payload, V4,
|
||||||
|
@ -6,12 +6,12 @@ use std::str::FromStr;
|
|||||||
use std::{path::Path, time::Duration};
|
use std::{path::Path, time::Duration};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
|
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqlitePoolOptions, SqliteRow},
|
|
||||||
Row,
|
Row,
|
||||||
|
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqlitePoolOptions, SqliteRow},
|
||||||
};
|
};
|
||||||
|
|
||||||
use atuin_common::record::{
|
use atuin_common::record::{
|
||||||
@ -35,7 +35,9 @@ impl SqliteStore {
|
|||||||
debug!("opening sqlite database at {:?}", path);
|
debug!("opening sqlite database at {:?}", path);
|
||||||
|
|
||||||
if utils::broken_symlink(path) {
|
if utils::broken_symlink(path) {
|
||||||
eprintln!("Atuin: Sqlite db path ({path:?}) is a broken symlink. Unable to read or create replacement.");
|
eprintln!(
|
||||||
|
"Atuin: Sqlite db path ({path:?}) is a broken symlink. Unable to read or create replacement."
|
||||||
|
);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ pub async fn operations(
|
|||||||
msg: String::from(
|
msg: String::from(
|
||||||
"diff has nothing for local or remote - (host, tag) does not exist",
|
"diff has nothing for local or remote - (host, tag) does not exist",
|
||||||
),
|
),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,7 +38,9 @@ pub static SECRET_PATTERNS: &[(&str, &str, TestValue)] = &[
|
|||||||
(
|
(
|
||||||
"Atuin login",
|
"Atuin login",
|
||||||
r"atuin\s+login",
|
r"atuin\s+login",
|
||||||
TestValue::Single("atuin login -u mycoolusername -p mycoolpassword -k \"lots of random words\""),
|
TestValue::Single(
|
||||||
|
"atuin login -u mycoolusername -p mycoolpassword -k \"lots of random words\"",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"GitHub PAT (old)",
|
"GitHub PAT (old)",
|
||||||
@ -51,7 +53,7 @@ pub static SECRET_PATTERNS: &[(&str, &str, TestValue)] = &[
|
|||||||
TestValue::Multiple(&[
|
TestValue::Multiple(&[
|
||||||
"gh1_1234567890abcdefghijk_1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklm",
|
"gh1_1234567890abcdefghijk_1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklm",
|
||||||
"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", // also legit, also expired
|
"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", // also legit, also expired
|
||||||
])
|
]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"GitHub OAuth Access Token",
|
"GitHub OAuth Access Token",
|
||||||
@ -71,7 +73,9 @@ pub static SECRET_PATTERNS: &[(&str, &str, TestValue)] = &[
|
|||||||
(
|
(
|
||||||
"GitHub Refresh Token",
|
"GitHub Refresh Token",
|
||||||
"ghr_[A-Za-z0-9]{76}",
|
"ghr_[A-Za-z0-9]{76}",
|
||||||
TestValue::Single("ghr_1234567890abcdefghijklmnopqrstuvwx1234567890abcdefghijklmnopqrstuvwx1234567890abcdefghijklmnopqrstuvwx"), // not a real token
|
TestValue::Single(
|
||||||
|
"ghr_1234567890abcdefghijklmnopqrstuvwx1234567890abcdefghijklmnopqrstuvwx1234567890abcdefghijklmnopqrstuvwx",
|
||||||
|
), // not a real token
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"GitHub App Installation Access Token v1",
|
"GitHub App Installation Access Token v1",
|
||||||
@ -96,10 +100,20 @@ pub static SECRET_PATTERNS: &[(&str, &str, TestValue)] = &[
|
|||||||
(
|
(
|
||||||
"Slack webhook",
|
"Slack webhook",
|
||||||
"T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}",
|
"T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}",
|
||||||
TestValue::Single("https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"),
|
TestValue::Single(
|
||||||
|
"https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Stripe test key",
|
||||||
|
"sk_test_[0-9a-zA-Z]{24}",
|
||||||
|
TestValue::Single("sk_test_1234567890abcdefghijklmnop"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Stripe live key",
|
||||||
|
"sk_live_[0-9a-zA-Z]{24}",
|
||||||
|
TestValue::Single("sk_live_1234567890abcdefghijklmnop"),
|
||||||
),
|
),
|
||||||
("Stripe test key", "sk_test_[0-9a-zA-Z]{24}", TestValue::Single("sk_test_1234567890abcdefghijklmnop")),
|
|
||||||
("Stripe live key", "sk_live_[0-9a-zA-Z]{24}", TestValue::Single("sk_live_1234567890abcdefghijklmnop")),
|
|
||||||
(
|
(
|
||||||
"Netlify authentication token",
|
"Netlify authentication token",
|
||||||
"nf[pcoub]_[0-9a-zA-Z]{36}",
|
"nf[pcoub]_[0-9a-zA-Z]{36}",
|
||||||
@ -127,7 +141,7 @@ pub static SECRET_PATTERNS_RE: LazyLock<RegexSet> = LazyLock::new(|| {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::secrets::{TestValue, SECRET_PATTERNS};
|
use crate::secrets::{SECRET_PATTERNS, TestValue};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_secrets() {
|
fn test_secrets() {
|
||||||
|
@ -6,19 +6,19 @@ use atuin_common::record::HostId;
|
|||||||
use atuin_common::utils;
|
use atuin_common::utils;
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use config::{
|
use config::{
|
||||||
builder::DefaultState, Config, ConfigBuilder, Environment, File as ConfigFile, FileFormat,
|
Config, ConfigBuilder, Environment, File as ConfigFile, FileFormat, builder::DefaultState,
|
||||||
};
|
};
|
||||||
use eyre::{bail, eyre, Context, Error, Result};
|
use eyre::{Context, Error, Result, bail, eyre};
|
||||||
use fs_err::{create_dir_all, File};
|
use fs_err::{File, create_dir_all};
|
||||||
use humantime::parse_duration;
|
use humantime::parse_duration;
|
||||||
use regex::RegexSet;
|
use regex::RegexSet;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::DeserializeFromStr;
|
use serde_with::DeserializeFromStr;
|
||||||
use time::{
|
use time::{
|
||||||
format_description::{well_known::Rfc3339, FormatItem},
|
|
||||||
macros::format_description,
|
|
||||||
OffsetDateTime, UtcOffset,
|
OffsetDateTime, UtcOffset,
|
||||||
|
format_description::{FormatItem, well_known::Rfc3339},
|
||||||
|
macros::format_description,
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@ -141,8 +141,9 @@ impl fmt::Display for Timezone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// format: <+|-><hour>[:<minute>[:<second>]]
|
/// format: <+|-><hour>[:<minute>[:<second>]]
|
||||||
static OFFSET_FMT: &[FormatItem<'_>] =
|
static OFFSET_FMT: &[FormatItem<'_>] = format_description!(
|
||||||
format_description!("[offset_hour sign:mandatory padding:none][optional [:[offset_minute padding:none][optional [:[offset_second padding:none]]]]]");
|
"[offset_hour sign:mandatory padding:none][optional [:[offset_minute padding:none][optional [:[offset_second padding:none]]]]]"
|
||||||
|
);
|
||||||
impl FromStr for Timezone {
|
impl FromStr for Timezone {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
|
@ -109,15 +109,18 @@ async fn sync_download(
|
|||||||
for i in remote_status.deleted {
|
for i in remote_status.deleted {
|
||||||
// we will update the stored history to have this data
|
// we will update the stored history to have this data
|
||||||
// pretty much everything can be nullified
|
// pretty much everything can be nullified
|
||||||
if let Some(h) = db.load(i.as_str()).await? {
|
match db.load(i.as_str()).await? {
|
||||||
|
Some(h) => {
|
||||||
db.delete(h).await?;
|
db.delete(h).await?;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
info!(
|
info!(
|
||||||
"could not delete history with id {}, not found locally",
|
"could not delete history with id {}, not found locally",
|
||||||
i.as_str()
|
i.as_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok((local_count - initial_local, local_count))
|
Ok((local_count - initial_local, local_count))
|
||||||
}
|
}
|
||||||
|
@ -416,7 +416,7 @@ impl ThemeManager {
|
|||||||
"set theme debug on for more info".to_string()
|
"set theme debug on for more info".to_string()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let colors: HashMap<Meaning, String> = theme_config.colors;
|
let colors: HashMap<Meaning, String> = theme_config.colors;
|
||||||
@ -697,7 +697,8 @@ mod theme_tests {
|
|||||||
|
|
||||||
testing_logger::validate(|captured_logs| {
|
testing_logger::validate(|captured_logs| {
|
||||||
assert_eq!(captured_logs.len(), 1);
|
assert_eq!(captured_logs.len(), 1);
|
||||||
assert_eq!(captured_logs[0].body,
|
assert_eq!(
|
||||||
|
captured_logs[0].body,
|
||||||
"Could not load theme nonsolarized: Empty theme directory override and could not find theme elsewhere"
|
"Could not load theme nonsolarized: Empty theme directory override and could not find theme elsewhere"
|
||||||
);
|
);
|
||||||
assert_eq!(captured_logs[0].level, log::Level::Warn)
|
assert_eq!(captured_logs[0].level, log::Level::Warn)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "atuin-common"
|
name = "atuin-common"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
description = "common library for atuin"
|
description = "common library for atuin"
|
||||||
|
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
|
|
||||||
/// Defines a new UUID type wrapper
|
/// Defines a new UUID type wrapper
|
||||||
macro_rules! new_uuid {
|
macro_rules! new_uuid {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{ffi::OsStr, path::Path, process::Command};
|
use std::{ffi::OsStr, path::Path, process::Command};
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sysinfo::{get_current_pid, Process, System};
|
use sysinfo::{Process, System, get_current_pid};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
@ -2,9 +2,9 @@ use std::borrow::Cow;
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
|
|
||||||
use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
|
use base64::prelude::{BASE64_URL_SAFE_NO_PAD, Engine};
|
||||||
use getrandom::getrandom;
|
use getrandom::getrandom;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@ -194,6 +194,7 @@ pub fn unquote(s: &str) -> Result<String> {
|
|||||||
|
|
||||||
impl<T: AsRef<str>> Escapable for T {}
|
impl<T: AsRef<str>> Escapable for T {}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pretty_assertions::assert_ne;
|
use pretty_assertions::assert_ne;
|
||||||
@ -214,36 +215,48 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_config_dir_xdg() {
|
fn test_config_dir_xdg() {
|
||||||
env::remove_var("HOME");
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
env::set_var("XDG_CONFIG_HOME", "/home/user/custom_config");
|
unsafe { env::remove_var("HOME") };
|
||||||
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
|
unsafe { env::set_var("XDG_CONFIG_HOME", "/home/user/custom_config") };
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config_dir(),
|
config_dir(),
|
||||||
PathBuf::from("/home/user/custom_config/atuin")
|
PathBuf::from("/home/user/custom_config/atuin")
|
||||||
);
|
);
|
||||||
env::remove_var("XDG_CONFIG_HOME");
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
|
unsafe { env::remove_var("XDG_CONFIG_HOME") };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_config_dir() {
|
fn test_config_dir() {
|
||||||
env::set_var("HOME", "/home/user");
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
env::remove_var("XDG_CONFIG_HOME");
|
unsafe { env::set_var("HOME", "/home/user") };
|
||||||
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
|
unsafe { env::remove_var("XDG_CONFIG_HOME") };
|
||||||
|
|
||||||
assert_eq!(config_dir(), PathBuf::from("/home/user/.config/atuin"));
|
assert_eq!(config_dir(), PathBuf::from("/home/user/.config/atuin"));
|
||||||
|
|
||||||
env::remove_var("HOME");
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
|
unsafe { env::remove_var("HOME") };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_data_dir_xdg() {
|
fn test_data_dir_xdg() {
|
||||||
env::remove_var("HOME");
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
env::set_var("XDG_DATA_HOME", "/home/user/custom_data");
|
unsafe { env::remove_var("HOME") };
|
||||||
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
|
unsafe { env::set_var("XDG_DATA_HOME", "/home/user/custom_data") };
|
||||||
assert_eq!(data_dir(), PathBuf::from("/home/user/custom_data/atuin"));
|
assert_eq!(data_dir(), PathBuf::from("/home/user/custom_data/atuin"));
|
||||||
env::remove_var("XDG_DATA_HOME");
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
|
unsafe { env::remove_var("XDG_DATA_HOME") };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_data_dir() {
|
fn test_data_dir() {
|
||||||
env::set_var("HOME", "/home/user");
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
env::remove_var("XDG_DATA_HOME");
|
unsafe { env::set_var("HOME", "/home/user") };
|
||||||
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
|
unsafe { env::remove_var("XDG_DATA_HOME") };
|
||||||
assert_eq!(data_dir(), PathBuf::from("/home/user/.local/share/atuin"));
|
assert_eq!(data_dir(), PathBuf::from("/home/user/.local/share/atuin"));
|
||||||
env::remove_var("HOME");
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
||||||
|
unsafe { env::remove_var("HOME") };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "atuin-daemon"
|
name = "atuin-daemon"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
version = { workspace = true }
|
version = { workspace = true }
|
||||||
description = "The daemon crate for Atuin"
|
description = "The daemon crate for Atuin"
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use tokio::net::UnixStream;
|
|||||||
use atuin_client::history::History;
|
use atuin_client::history::History;
|
||||||
|
|
||||||
use crate::history::{
|
use crate::history::{
|
||||||
history_client::HistoryClient as HistoryServiceClient, EndHistoryRequest, StartHistoryRequest,
|
EndHistoryRequest, StartHistoryRequest, history_client::HistoryClient as HistoryServiceClient,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct HistoryClient {
|
pub struct HistoryClient {
|
||||||
|
@ -7,13 +7,13 @@ use atuin_client::settings::Settings;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tracing::{instrument, Level};
|
use tracing::{Level, instrument};
|
||||||
|
|
||||||
use atuin_client::database::{Database, Sqlite as HistoryDatabase};
|
use atuin_client::database::{Database, Sqlite as HistoryDatabase};
|
||||||
use atuin_client::history::{History, HistoryId};
|
use atuin_client::history::{History, HistoryId};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use tonic::{transport::Server, Request, Response, Status};
|
use tonic::{Request, Response, Status, transport::Server};
|
||||||
|
|
||||||
use crate::history::history_server::{History as HistorySvc, HistoryServer};
|
use crate::history::history_server::{History as HistorySvc, HistoryServer};
|
||||||
|
|
||||||
@ -194,7 +194,9 @@ async fn start_server(settings: Settings, history: HistoryService) -> Result<()>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::warn!("could not detect systemd socket path, ensure that it's at the configured path: {socket_path:?}, error: {err:?}");
|
tracing::warn!(
|
||||||
|
"could not detect systemd socket path, ensure that it's at the configured path: {socket_path:?}, error: {err:?}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(UnixListener::from_std(listener)?, false)
|
(UnixListener::from_std(listener)?, false)
|
||||||
|
@ -10,7 +10,7 @@ use atuin_client::{
|
|||||||
settings::Settings,
|
settings::Settings,
|
||||||
};
|
};
|
||||||
|
|
||||||
use atuin_dotfiles::store::{var::VarStore, AliasStore};
|
use atuin_dotfiles::store::{AliasStore, var::VarStore};
|
||||||
|
|
||||||
pub async fn worker(
|
pub async fn worker(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "atuin-dotfiles"
|
name = "atuin-dotfiles"
|
||||||
description = "The dotfiles crate for Atuin"
|
description = "The dotfiles crate for Atuin"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
version = { workspace = true }
|
version = { workspace = true }
|
||||||
|
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use eyre::{ensure, eyre, Result};
|
use eyre::{Result, ensure, eyre};
|
||||||
use rmp::{decode, encode};
|
use rmp::{decode, encode};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ pub async fn import_aliases(store: &AliasStore) -> Result<Vec<Alias>> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::shell::{parse_alias, Alias};
|
use crate::shell::{Alias, parse_alias};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_simple_alias() {
|
fn test_parse_simple_alias() {
|
||||||
@ -177,7 +177,10 @@ mod tests {
|
|||||||
|
|
||||||
let git_alias = super::parse_alias("gwip='git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify --no-gpg-sign --message \"--wip-- [skip ci]\"'").expect("failed to parse alias");
|
let git_alias = super::parse_alias("gwip='git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify --no-gpg-sign --message \"--wip-- [skip ci]\"'").expect("failed to parse alias");
|
||||||
assert_eq!(git_alias.name, "gwip");
|
assert_eq!(git_alias.name, "gwip");
|
||||||
assert_eq!(git_alias.value, "'git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify --no-gpg-sign --message \"--wip-- [skip ci]\"'");
|
assert_eq!(
|
||||||
|
git_alias.value,
|
||||||
|
"'git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify --no-gpg-sign --message \"--wip-- [skip ci]\"'"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::store::{var::VarStore, AliasStore};
|
use crate::store::{AliasStore, var::VarStore};
|
||||||
|
|
||||||
async fn cached_aliases(path: PathBuf, store: &AliasStore) -> String {
|
async fn cached_aliases(path: PathBuf, store: &AliasStore) -> String {
|
||||||
match tokio::fs::read_to_string(path).await {
|
match tokio::fs::read_to_string(path).await {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Configuration for fish
|
// Configuration for fish
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::store::{var::VarStore, AliasStore};
|
use crate::store::{AliasStore, var::VarStore};
|
||||||
|
|
||||||
async fn cached_aliases(path: PathBuf, store: &AliasStore) -> String {
|
async fn cached_aliases(path: PathBuf, store: &AliasStore) -> String {
|
||||||
match tokio::fs::read_to_string(path).await {
|
match tokio::fs::read_to_string(path).await {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::store::{var::VarStore, AliasStore};
|
use crate::store::{AliasStore, var::VarStore};
|
||||||
|
|
||||||
async fn cached_aliases(path: PathBuf, store: &AliasStore) -> String {
|
async fn cached_aliases(path: PathBuf, store: &AliasStore) -> String {
|
||||||
match tokio::fs::read_to_string(path).await {
|
match tokio::fs::read_to_string(path).await {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::store::{var::VarStore, AliasStore};
|
use crate::store::{AliasStore, var::VarStore};
|
||||||
|
|
||||||
async fn cached_aliases(path: PathBuf, store: &AliasStore) -> String {
|
async fn cached_aliases(path: PathBuf, store: &AliasStore) -> String {
|
||||||
match tokio::fs::read_to_string(path).await {
|
match tokio::fs::read_to_string(path).await {
|
||||||
|
@ -7,7 +7,7 @@ use atuin_client::record::sqlite_store::SqliteStore;
|
|||||||
// + stores, rather than one mega config store.
|
// + stores, rather than one mega config store.
|
||||||
use atuin_common::record::{DecryptedData, Host, HostId};
|
use atuin_common::record::{DecryptedData, Host, HostId};
|
||||||
use atuin_common::utils::unquote;
|
use atuin_common::utils::unquote;
|
||||||
use eyre::{bail, ensure, eyre, Result};
|
use eyre::{Result, bail, ensure, eyre};
|
||||||
|
|
||||||
use atuin_client::record::encryption::PASETO_V4;
|
use atuin_client::record::encryption::PASETO_V4;
|
||||||
use atuin_client::record::store::Store;
|
use atuin_client::record::store::Store;
|
||||||
@ -315,7 +315,7 @@ mod tests {
|
|||||||
|
|
||||||
use crate::shell::Alias;
|
use crate::shell::Alias;
|
||||||
|
|
||||||
use super::{test_local_timeout, AliasRecord, AliasStore, CONFIG_SHELL_ALIAS_VERSION};
|
use super::{AliasRecord, AliasStore, CONFIG_SHELL_ALIAS_VERSION, test_local_timeout};
|
||||||
use crypto_secretbox::{KeyInit, XSalsa20Poly1305};
|
use crypto_secretbox::{KeyInit, XSalsa20Poly1305};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -6,7 +6,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use atuin_client::record::sqlite_store::SqliteStore;
|
use atuin_client::record::sqlite_store::SqliteStore;
|
||||||
use atuin_common::record::{DecryptedData, Host, HostId};
|
use atuin_common::record::{DecryptedData, Host, HostId};
|
||||||
use eyre::{bail, ensure, eyre, Result};
|
use eyre::{Result, bail, ensure, eyre};
|
||||||
|
|
||||||
use atuin_client::record::encryption::PASETO_V4;
|
use atuin_client::record::encryption::PASETO_V4;
|
||||||
use atuin_client::record::store::Store;
|
use atuin_client::record::store::Store;
|
||||||
@ -294,7 +294,7 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{shell::Var, store::test_local_timeout};
|
use crate::{shell::Var, store::test_local_timeout};
|
||||||
|
|
||||||
use super::{VarRecord, VarStore, DOTFILES_VAR_VERSION};
|
use super::{DOTFILES_VAR_VERSION, VarRecord, VarStore};
|
||||||
use crypto_secretbox::{KeyInit, XSalsa20Poly1305};
|
use crypto_secretbox::{KeyInit, XSalsa20Poly1305};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "atuin-history"
|
name = "atuin-history"
|
||||||
description = "The history crate for Atuin"
|
description = "The history crate for Atuin"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
version = { workspace = true }
|
version = { workspace = true }
|
||||||
|
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
@ -178,7 +178,9 @@ pub fn pretty_print(stats: Stats, ngram_size: usize, theme: &Theme) {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" | ");
|
.join(" | ");
|
||||||
|
|
||||||
println!("{ResetColor}] {gray}{count:num_pad$}{ResetColor} {bold}{formatted_command}{ResetColor}");
|
println!(
|
||||||
|
"{ResetColor}] {gray}{count:num_pad$}{ResetColor} {bold}{formatted_command}{ResetColor}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
println!("Total commands: {}", stats.total_commands);
|
println!("Total commands: {}", stats.total_commands);
|
||||||
println!("Unique commands: {}", stats.unique_commands);
|
println!("Unique commands: {}", stats.unique_commands);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "atuin-server-database"
|
name = "atuin-server-database"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
description = "server database library for atuin"
|
description = "server database library for atuin"
|
||||||
|
|
||||||
version = { workspace = true }
|
version = { workspace = true }
|
||||||
|
@ -15,7 +15,7 @@ use self::{
|
|||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use atuin_common::record::{EncryptedData, HostId, Record, RecordIdx, RecordStatus};
|
use atuin_common::record::{EncryptedData, HostId, Record, RecordIdx, RecordStatus};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
use time::{Date, Duration, Month, OffsetDateTime, Time, UtcOffset};
|
use time::{Date, Duration, Month, OffsetDateTime, Time, UtcOffset};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "atuin-server-postgres"
|
name = "atuin-server-postgres"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
description = "server postgres database library for atuin"
|
description = "server postgres database library for atuin"
|
||||||
|
|
||||||
version = { workspace = true }
|
version = { workspace = true }
|
||||||
|
@ -10,8 +10,8 @@ use atuin_server_database::{Database, DbError, DbResult};
|
|||||||
use futures_util::TryStreamExt;
|
use futures_util::TryStreamExt;
|
||||||
use metrics::counter;
|
use metrics::counter;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::postgres::PgPoolOptions;
|
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
use sqlx::postgres::PgPoolOptions;
|
||||||
|
|
||||||
use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
|
use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
|
||||||
use tracing::{instrument, trace};
|
use tracing::{instrument, trace};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use ::sqlx::{FromRow, Result};
|
use ::sqlx::{FromRow, Result};
|
||||||
use atuin_common::record::{EncryptedData, Host, Record};
|
use atuin_common::record::{EncryptedData, Host, Record};
|
||||||
use atuin_server_database::models::{History, Session, User};
|
use atuin_server_database::models::{History, Session, User};
|
||||||
use sqlx::{postgres::PgRow, Row};
|
use sqlx::{Row, postgres::PgRow};
|
||||||
use time::PrimitiveDateTime;
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
pub struct DbUser(pub User);
|
pub struct DbUser(pub User);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "atuin-server"
|
name = "atuin-server"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
description = "server library for atuin"
|
description = "server library for atuin"
|
||||||
|
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use axum::{http, response::IntoResponse, Json};
|
use axum::{Json, http, response::IntoResponse};
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::{collections::HashMap, convert::TryFrom};
|
use std::{collections::HashMap, convert::TryFrom};
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
|
Json,
|
||||||
extract::{Path, Query, State},
|
extract::{Path, Query, State},
|
||||||
http::{HeaderMap, StatusCode},
|
http::{HeaderMap, StatusCode},
|
||||||
Json,
|
|
||||||
};
|
};
|
||||||
use metrics::counter;
|
use metrics::counter;
|
||||||
use time::{Month, UtcOffset};
|
use time::{Month, UtcOffset};
|
||||||
@ -15,9 +15,9 @@ use crate::{
|
|||||||
utils::client_version_min,
|
utils::client_version_min,
|
||||||
};
|
};
|
||||||
use atuin_server_database::{
|
use atuin_server_database::{
|
||||||
|
Database,
|
||||||
calendar::{TimePeriod, TimePeriodInfo},
|
calendar::{TimePeriod, TimePeriodInfo},
|
||||||
models::NewHistory,
|
models::NewHistory,
|
||||||
Database,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use atuin_common::api::*;
|
use atuin_common::api::*;
|
||||||
@ -223,7 +223,7 @@ pub async fn calendar<DB: Database>(
|
|||||||
"day" => TimePeriod::Day { year, month },
|
"day" => TimePeriod::Day { year, month },
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ErrorResponse::reply("invalid focus: use year/month/day")
|
return Err(ErrorResponse::reply("invalid focus: use year/month/day")
|
||||||
.with_status(StatusCode::BAD_REQUEST))
|
.with_status(StatusCode::BAD_REQUEST));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use atuin_common::api::{ErrorResponse, IndexResponse};
|
use atuin_common::api::{ErrorResponse, IndexResponse};
|
||||||
use atuin_server_database::Database;
|
use atuin_server_database::Database;
|
||||||
use axum::{extract::State, http, response::IntoResponse, Json};
|
use axum::{Json, extract::State, http, response::IntoResponse};
|
||||||
|
|
||||||
use crate::router::AppState;
|
use crate::router::AppState;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use axum::{http::StatusCode, response::IntoResponse, Json};
|
use axum::{Json, http::StatusCode, response::IntoResponse};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use axum::{extract::State, http::StatusCode, Json};
|
use axum::{Json, extract::State, http::StatusCode};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use super::{ErrorResponse, ErrorResponseStatus, RespExt};
|
use super::{ErrorResponse, ErrorResponseStatus, RespExt};
|
||||||
@ -28,7 +28,7 @@ pub async fn status<DB: Database>(
|
|||||||
Ok(count) => count,
|
Ok(count) => count,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(ErrorResponse::reply("failed to query history count")
|
return Err(ErrorResponse::reply("failed to query history count")
|
||||||
.with_status(StatusCode::INTERNAL_SERVER_ERROR))
|
.with_status(StatusCode::INTERNAL_SERVER_ERROR));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,17 +3,17 @@ use std::collections::HashMap;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use argon2::{
|
use argon2::{
|
||||||
password_hash::SaltString, Algorithm, Argon2, Params, PasswordHash, PasswordHasher,
|
Algorithm, Argon2, Params, PasswordHash, PasswordHasher, PasswordVerifier, Version,
|
||||||
PasswordVerifier, Version,
|
password_hash::SaltString,
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
|
Json,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
Json,
|
|
||||||
};
|
};
|
||||||
use metrics::counter;
|
use metrics::counter;
|
||||||
|
|
||||||
use postmark::{reqwest::PostmarkClient, Query};
|
use postmark::{Query, reqwest::PostmarkClient};
|
||||||
|
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use tracing::{debug, error, info, instrument};
|
use tracing::{debug, error, info, instrument};
|
||||||
@ -21,8 +21,8 @@ use tracing::{debug, error, info, instrument};
|
|||||||
use super::{ErrorResponse, ErrorResponseStatus, RespExt};
|
use super::{ErrorResponse, ErrorResponseStatus, RespExt};
|
||||||
use crate::router::{AppState, UserAuth};
|
use crate::router::{AppState, UserAuth};
|
||||||
use atuin_server_database::{
|
use atuin_server_database::{
|
||||||
models::{NewSession, NewUser},
|
|
||||||
Database, DbError,
|
Database, DbError,
|
||||||
|
models::{NewSession, NewUser},
|
||||||
};
|
};
|
||||||
|
|
||||||
use reqwest::header::CONTENT_TYPE;
|
use reqwest::header::CONTENT_TYPE;
|
||||||
@ -104,7 +104,7 @@ pub async fn register<DB: Database>(
|
|||||||
return Err(ErrorResponse::reply(
|
return Err(ErrorResponse::reply(
|
||||||
"Only alphanumeric and hyphens (-) are allowed in usernames",
|
"Only alphanumeric and hyphens (-) are allowed in usernames",
|
||||||
)
|
)
|
||||||
.with_status(StatusCode::BAD_REQUEST))
|
.with_status(StatusCode::BAD_REQUEST));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,12 +201,13 @@ pub async fn send_verification<DB: Database>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: if we ever add another mail provider, can match on them all here.
|
// TODO: if we ever add another mail provider, can match on them all here.
|
||||||
let postmark_token = if let Some(token) = settings.mail.postmark.token {
|
let postmark_token = match settings.mail.postmark.token {
|
||||||
token
|
Some(token) => token,
|
||||||
} else {
|
_ => {
|
||||||
error!("Failed to verify email: got None for postmark token");
|
error!("Failed to verify email: got None for postmark token");
|
||||||
return Err(ErrorResponse::reply("mail not configured")
|
return Err(ErrorResponse::reply("mail not configured")
|
||||||
.with_status(StatusCode::INTERNAL_SERVER_ERROR));
|
.with_status(StatusCode::INTERNAL_SERVER_ERROR));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let db = &state.0.database;
|
let db = &state.0.database;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use axum::{extract::Query, extract::State, http::StatusCode, Json};
|
use axum::{Json, extract::Query, extract::State, http::StatusCode};
|
||||||
use metrics::counter;
|
use metrics::counter;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tracing::{error, instrument};
|
use tracing::{error, instrument};
|
||||||
|
@ -4,18 +4,18 @@ use std::future::Future;
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use atuin_server_database::Database;
|
use atuin_server_database::Database;
|
||||||
use axum::{serve, Router};
|
use axum::{Router, serve};
|
||||||
use axum_server::tls_rustls::RustlsConfig;
|
|
||||||
use axum_server::Handle;
|
use axum_server::Handle;
|
||||||
use eyre::{eyre, Context, Result};
|
use axum_server::tls_rustls::RustlsConfig;
|
||||||
|
use eyre::{Context, Result, eyre};
|
||||||
|
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
mod router;
|
mod router;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub use settings::example_config;
|
|
||||||
pub use settings::Settings;
|
pub use settings::Settings;
|
||||||
|
pub use settings::example_config;
|
||||||
|
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
||||||
|
@ -28,10 +28,9 @@ pub fn setup_metrics_recorder() -> PrometheusHandle {
|
|||||||
pub async fn track_metrics(req: Request, next: Next) -> impl IntoResponse {
|
pub async fn track_metrics(req: Request, next: Next) -> impl IntoResponse {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
let path = if let Some(matched_path) = req.extensions().get::<MatchedPath>() {
|
let path = match req.extensions().get::<MatchedPath>() {
|
||||||
matched_path.as_str().to_owned()
|
Some(matched_path) => matched_path.as_str().to_owned(),
|
||||||
} else {
|
_ => req.uri().path().to_owned(),
|
||||||
req.uri().path().to_owned()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let method = req.method().clone();
|
let method = req.method().clone();
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use atuin_common::api::{ErrorResponse, ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION};
|
use atuin_common::api::{ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ErrorResponse};
|
||||||
use axum::{
|
use axum::{
|
||||||
|
Router,
|
||||||
extract::{FromRequestParts, Request},
|
extract::{FromRequestParts, Request},
|
||||||
http::{self, request::Parts},
|
http::{self, request::Parts},
|
||||||
middleware::Next,
|
middleware::Next,
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
routing::{delete, get, patch, post},
|
routing::{delete, get, patch, post},
|
||||||
Router,
|
|
||||||
};
|
};
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use tower::ServiceBuilder;
|
use tower::ServiceBuilder;
|
||||||
@ -18,7 +18,7 @@ use crate::{
|
|||||||
metrics,
|
metrics,
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
};
|
};
|
||||||
use atuin_server_database::{models::User, Database, DbError};
|
use atuin_server_database::{Database, DbError, models::User};
|
||||||
|
|
||||||
pub struct UserAuth(pub User);
|
pub struct UserAuth(pub User);
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::{io::prelude::*, path::PathBuf};
|
use std::{io::prelude::*, path::PathBuf};
|
||||||
|
|
||||||
use config::{Config, Environment, File as ConfigFile, FileFormat};
|
use config::{Config, Environment, File as ConfigFile, FileFormat};
|
||||||
use eyre::{eyre, Result};
|
use eyre::{Result, eyre};
|
||||||
use fs_err::{create_dir_all, File};
|
use fs_err::{File, create_dir_all};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||||
|
|
||||||
static EXAMPLE_CONFIG: &str = include_str!("../server.toml");
|
static EXAMPLE_CONFIG: &str = include_str!("../server.toml");
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "atuin"
|
name = "atuin"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
description = "atuin - magical shell history"
|
description = "atuin - magical shell history"
|
||||||
readme = "./README.md"
|
readme = "./README.md"
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use eyre::{bail, Result};
|
use eyre::{Result, bail};
|
||||||
|
|
||||||
use atuin_client::{api_client, settings::Settings};
|
use atuin_client::{api_client, settings::Settings};
|
||||||
use rpassword::prompt_password;
|
use rpassword::prompt_password;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use atuin_client::{api_client, settings::Settings};
|
use atuin_client::{api_client, settings::Settings};
|
||||||
use eyre::{bail, Result};
|
use eyre::{Result, bail};
|
||||||
use std::fs::remove_file;
|
use std::fs::remove_file;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use std::{io, path::PathBuf};
|
use std::{io, path::PathBuf};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use eyre::{bail, Context, Result};
|
use eyre::{Context, Result, bail};
|
||||||
use tokio::{fs::File, io::AsyncWriteExt};
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
|
|
||||||
use atuin_client::{
|
use atuin_client::{
|
||||||
api_client,
|
api_client,
|
||||||
encryption::{decode_key, encode_key, load_key, Key},
|
encryption::{Key, decode_key, encode_key, load_key},
|
||||||
record::sqlite_store::SqliteStore,
|
record::sqlite_store::SqliteStore,
|
||||||
record::store::Store,
|
record::store::Store,
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
@ -56,7 +56,9 @@ impl Cmd {
|
|||||||
let key_path = PathBuf::from(key_path);
|
let key_path = PathBuf::from(key_path);
|
||||||
|
|
||||||
println!("IMPORTANT");
|
println!("IMPORTANT");
|
||||||
println!("If you are already logged in on another machine, you must ensure that the key you use here is the same as the key you used there.");
|
println!(
|
||||||
|
"If you are already logged in on another machine, you must ensure that the key you use here is the same as the key you used there."
|
||||||
|
);
|
||||||
println!("You can find your key by running 'atuin key' on the other machine");
|
println!("You can find your key by running 'atuin key' on the other machine");
|
||||||
println!("Do not share this key with anyone");
|
println!("Do not share this key with anyone");
|
||||||
println!("\nRead more here: https://docs.atuin.sh/guide/sync/#login \n");
|
println!("\nRead more here: https://docs.atuin.sh/guide/sync/#login \n");
|
||||||
@ -75,7 +77,8 @@ impl Cmd {
|
|||||||
match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) {
|
match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) {
|
||||||
Ok(mnemonic) => encode_key(Key::from_slice(mnemonic.entropy()))?,
|
Ok(mnemonic) => encode_key(Key::from_slice(mnemonic.entropy()))?,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(err) = err.downcast_ref::<bip39::ErrorKind>() {
|
match err.downcast_ref::<bip39::ErrorKind>() {
|
||||||
|
Some(err) => {
|
||||||
match err {
|
match err {
|
||||||
// assume they copied in the base64 key
|
// assume they copied in the base64 key
|
||||||
bip39::ErrorKind::InvalidWord => key,
|
bip39::ErrorKind::InvalidWord => key,
|
||||||
@ -88,12 +91,14 @@ impl Cmd {
|
|||||||
bail!("key was not the correct length")
|
bail!("key was not the correct length")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
// unknown error. assume they copied the base64 key
|
// unknown error. assume they copied the base64 key
|
||||||
key
|
key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// I've simplified this a little, but it could really do with a refactor
|
// I've simplified this a little, but it could really do with a refactor
|
||||||
@ -106,7 +111,9 @@ impl Cmd {
|
|||||||
bail!("the key in existing key file was invalid");
|
bail!("the key in existing key file was invalid");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("No key provided. Please use 'atuin key' on your other machine, or recover your key from a backup.")
|
panic!(
|
||||||
|
"No key provided. Please use 'atuin key' on your other machine, or recover your key from a backup."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else if !key_path.exists() {
|
} else if !key_path.exists() {
|
||||||
if decode_key(key.clone()).is_err() {
|
if decode_key(key.clone()).is_err() {
|
||||||
@ -184,6 +191,9 @@ mod tests {
|
|||||||
.into_phrase();
|
.into_phrase();
|
||||||
let mnemonic = bip39::Mnemonic::from_phrase(&phrase, bip39::Language::English).unwrap();
|
let mnemonic = bip39::Mnemonic::from_phrase(&phrase, bip39::Language::English).unwrap();
|
||||||
assert_eq!(mnemonic.entropy(), key.as_slice());
|
assert_eq!(mnemonic.entropy(), key.as_slice());
|
||||||
assert_eq!(phrase, "adapt amused able anxiety mother adapt beef gaze amount else seat alcohol cage lottery avoid scare alcohol cactus school avoid coral adjust catch pink");
|
assert_eq!(
|
||||||
|
phrase,
|
||||||
|
"adapt amused able anxiety mother adapt beef gaze amount else seat alcohol cage lottery avoid scare alcohol cactus school avoid coral adjust catch pink"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use eyre::{bail, Result};
|
use eyre::{Result, bail};
|
||||||
use tokio::{fs::File, io::AsyncWriteExt};
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
|
|
||||||
use atuin_client::{api_client, settings::Settings};
|
use atuin_client::{api_client, settings::Settings};
|
||||||
@ -51,8 +51,12 @@ pub async fn run(
|
|||||||
|
|
||||||
let _key = atuin_client::encryption::load_key(settings)?;
|
let _key = atuin_client::encryption::load_key(settings)?;
|
||||||
|
|
||||||
println!("Registration successful! Please make a note of your key (run 'atuin key') and keep it safe.");
|
println!(
|
||||||
println!("You will need it to log in on other devices, and we cannot help recover it if you lose it.");
|
"Registration successful! Please make a note of your key (run 'atuin key') and keep it safe."
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"You will need it to log in on other devices, and we cannot help recover it if you lose it."
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,15 @@ pub async fn run(settings: &Settings, token: Option<String>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
println!("Your Atuin server does not have mail setup. This is not required, though your account cannot be verified. Speak to your admin.");
|
println!(
|
||||||
|
"Your Atuin server does not have mail setup. This is not required, though your account cannot be verified. Speak to your admin."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
println!("Invalid email and verification status. This is a bug. Please open an issue: https://github.com/atuinsh/atuin");
|
println!(
|
||||||
|
"Invalid email and verification status. This is a bug. Please open an issue: https://github.com/atuinsh/atuin"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@ use std::{env, path::PathBuf, str::FromStr};
|
|||||||
|
|
||||||
use atuin_client::database::Sqlite;
|
use atuin_client::database::Sqlite;
|
||||||
use atuin_client::settings::Settings;
|
use atuin_client::settings::Settings;
|
||||||
use atuin_common::shell::{shell_name, Shell};
|
use atuin_common::shell::{Shell, shell_name};
|
||||||
use atuin_common::utils;
|
use atuin_common::utils;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use sysinfo::{get_current_pid, Disks, System};
|
use sysinfo::{Disks, System, get_current_pid};
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct ShellInfo {
|
struct ShellInfo {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use eyre::{eyre, Context, Result};
|
use eyre::{Context, Result, eyre};
|
||||||
|
|
||||||
use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings};
|
use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings};
|
||||||
|
|
||||||
@ -92,7 +92,9 @@ impl Cmd {
|
|||||||
|
|
||||||
pub async fn run(&self, settings: &Settings, store: SqliteStore) -> Result<()> {
|
pub async fn run(&self, settings: &Settings, store: SqliteStore) -> Result<()> {
|
||||||
if !settings.dotfiles.enabled {
|
if !settings.dotfiles.enabled {
|
||||||
eprintln!("Dotfiles are not enabled. Add\n\n[dotfiles]\nenabled = true\n\nto your configuration file to enable them.\n");
|
eprintln!(
|
||||||
|
"Dotfiles are not enabled. Add\n\n[dotfiles]\nenabled = true\n\nto your configuration file to enable them.\n"
|
||||||
|
);
|
||||||
eprintln!("The default configuration file is located at ~/.config/atuin/config.toml.");
|
eprintln!("The default configuration file is located at ~/.config/atuin/config.toml.");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,9 @@ impl Cmd {
|
|||||||
|
|
||||||
pub async fn run(&self, settings: &Settings, store: SqliteStore) -> Result<()> {
|
pub async fn run(&self, settings: &Settings, store: SqliteStore) -> Result<()> {
|
||||||
if !settings.dotfiles.enabled {
|
if !settings.dotfiles.enabled {
|
||||||
eprintln!("Dotfiles are not enabled. Add\n\n[dotfiles]\nenabled = true\n\nto your configuration file to enable them.\n");
|
eprintln!(
|
||||||
|
"Dotfiles are not enabled. Add\n\n[dotfiles]\nenabled = true\n\nto your configuration file to enable them.\n"
|
||||||
|
);
|
||||||
eprintln!("The default configuration file is located at ~/.config/atuin/config.toml.");
|
eprintln!("The default configuration file is located at ~/.config/atuin/config.toml.");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ use eyre::{Context, Result};
|
|||||||
use runtime_format::{FormatKey, FormatKeyError, ParseSegment, ParsedFmt};
|
use runtime_format::{FormatKey, FormatKeyError, ParseSegment, ParsedFmt};
|
||||||
|
|
||||||
use atuin_client::{
|
use atuin_client::{
|
||||||
database::{current_context, Database, Sqlite},
|
database::{Database, Sqlite, current_context},
|
||||||
encryption,
|
encryption,
|
||||||
history::{store::HistoryStore, History},
|
history::{History, store::HistoryStore},
|
||||||
record::sqlite_store::SqliteStore,
|
record::sqlite_store::SqliteStore,
|
||||||
settings::{
|
settings::{
|
||||||
FilterMode::{Directory, Global, Session},
|
FilterMode::{Directory, Global, Session},
|
||||||
@ -25,7 +25,7 @@ use atuin_client::{
|
|||||||
use atuin_client::{record, sync};
|
use atuin_client::{record, sync};
|
||||||
|
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use time::{macros::format_description, OffsetDateTime};
|
use time::{OffsetDateTime, macros::format_description};
|
||||||
|
|
||||||
use super::search::format_duration_into;
|
use super::search::format_duration_into;
|
||||||
|
|
||||||
@ -285,7 +285,9 @@ fn parse_fmt(format: &str) -> ParsedFmt {
|
|||||||
Ok(fmt) => fmt,
|
Ok(fmt) => fmt,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("ERROR: History formatting failed with the following error: {err}");
|
eprintln!("ERROR: History formatting failed with the following error: {err}");
|
||||||
println!("If your formatting string contains curly braces (eg: {{var}}) you need to escape them this way: {{{{var}}.");
|
println!(
|
||||||
|
"If your formatting string contains curly braces (eg: {{var}}) you need to escape them this way: {{{{var}}."
|
||||||
|
);
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -550,11 +552,11 @@ impl Cmd {
|
|||||||
if settings.daemon.enabled {
|
if settings.daemon.enabled {
|
||||||
match self {
|
match self {
|
||||||
Self::Start { command } => {
|
Self::Start { command } => {
|
||||||
return Self::handle_daemon_start(settings, &command).await
|
return Self::handle_daemon_start(settings, &command).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::End { id, exit, duration } => {
|
Self::End { id, exit, duration } => {
|
||||||
return Self::handle_daemon_end(settings, &id, exit, duration).await
|
return Self::handle_daemon_end(settings, &id, exit, duration).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -9,8 +9,8 @@ use atuin_client::{
|
|||||||
database::Database,
|
database::Database,
|
||||||
history::History,
|
history::History,
|
||||||
import::{
|
import::{
|
||||||
bash::Bash, fish::Fish, nu::Nu, nu_histdb::NuHistDb, replxx::Replxx, resh::Resh,
|
Importer, Loader, bash::Bash, fish::Fish, nu::Nu, nu_histdb::NuHistDb, replxx::Replxx,
|
||||||
xonsh::Xonsh, xonsh_sqlite::XonshSqlite, zsh::Zsh, zsh_histdb::ZshHistDb, Importer, Loader,
|
resh::Resh, xonsh::Xonsh, xonsh_sqlite::XonshSqlite, zsh::Zsh, zsh_histdb::ZshHistDb,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,7 +57,9 @@ impl Cmd {
|
|||||||
match self {
|
match self {
|
||||||
Self::Auto => {
|
Self::Auto => {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
println!("This feature does not work on windows. Please run atuin import <SHELL>. To view a list of shells, run atuin import.");
|
println!(
|
||||||
|
"This feature does not work on windows. Please run atuin import <SHELL>. To view a list of shells, run atuin import."
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings};
|
use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings};
|
||||||
use atuin_dotfiles::store::{var::VarStore, AliasStore};
|
use atuin_dotfiles::store::{AliasStore, var::VarStore};
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use eyre::{Result, WrapErr};
|
use eyre::{Result, WrapErr};
|
||||||
|
|
||||||
@ -160,7 +160,9 @@ $env.config = (
|
|||||||
|
|
||||||
pub async fn run(self, settings: &Settings) -> Result<()> {
|
pub async fn run(self, settings: &Settings) -> Result<()> {
|
||||||
if !settings.paths_ok() {
|
if !settings.paths_ok() {
|
||||||
eprintln!("Atuin settings paths are broken. Disabling atuin shell hooks. Run `atuin doctor` to diagnose.");
|
eprintln!(
|
||||||
|
"Atuin settings paths are broken. Disabling atuin shell hooks. Run `atuin doctor` to diagnose."
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use atuin_dotfiles::store::{var::VarStore, AliasStore};
|
use atuin_dotfiles::store::{AliasStore, var::VarStore};
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
|
||||||
pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool) {
|
pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use atuin_dotfiles::store::{var::VarStore, AliasStore};
|
use atuin_dotfiles::store::{AliasStore, var::VarStore};
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
|
||||||
pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool) {
|
pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use atuin_dotfiles::store::{var::VarStore, AliasStore};
|
use atuin_dotfiles::store::{AliasStore, var::VarStore};
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
|
||||||
pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool) {
|
pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use atuin_dotfiles::store::{var::VarStore, AliasStore};
|
use atuin_dotfiles::store::{AliasStore, var::VarStore};
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
|
||||||
pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool) {
|
pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::io::{stderr, IsTerminal as _};
|
use std::io::{IsTerminal as _, stderr};
|
||||||
|
|
||||||
use atuin_common::utils::{self, Escapable as _};
|
use atuin_common::utils::{self, Escapable as _};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
@ -6,9 +6,9 @@ use eyre::Result;
|
|||||||
|
|
||||||
use atuin_client::{
|
use atuin_client::{
|
||||||
database::Database,
|
database::Database,
|
||||||
database::{current_context, OptFilters},
|
database::{OptFilters, current_context},
|
||||||
encryption,
|
encryption,
|
||||||
history::{store::HistoryStore, History},
|
history::{History, store::HistoryStore},
|
||||||
record::sqlite_store::SqliteStore,
|
record::sqlite_store::SqliteStore,
|
||||||
settings::{FilterMode, KeymapMode, SearchMode, Settings, Timezone},
|
settings::{FilterMode, KeymapMode, SearchMode, Settings, Timezone},
|
||||||
theme::Theme,
|
theme::Theme,
|
||||||
@ -165,7 +165,9 @@ impl Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.delete && query.is_empty() {
|
if self.delete && query.is_empty() {
|
||||||
eprintln!("Please specify a query to match the items you wish to delete. If you wish to delete all history, pass --delete-it-all");
|
eprintln!(
|
||||||
|
"Please specify a query to match the items you wish to delete. If you wish to delete all history, pass --delete-it-all"
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,11 +184,7 @@ impl Cursor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn back(&mut self) -> Option<char> {
|
pub fn back(&mut self) -> Option<char> {
|
||||||
if self.left() {
|
if self.left() { self.remove() } else { None }
|
||||||
self.remove()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
|
@ -3,7 +3,7 @@ use std::path::Path;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use atuin_client::{database::Database, history::History, settings::FilterMode};
|
use atuin_client::{database::Database, history::History, settings::FilterMode};
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tokio::task::yield_now;
|
use tokio::task::yield_now;
|
||||||
|
@ -6,12 +6,12 @@ use atuin_client::{
|
|||||||
settings::Settings,
|
settings::Settings,
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
|
Frame,
|
||||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
prelude::{Constraint, Direction, Layout},
|
prelude::{Constraint, Direction, Layout},
|
||||||
style::Style,
|
style::Style,
|
||||||
widgets::{Bar, BarChart, BarGroup, Block, Borders, Padding, Paragraph, Row, Table},
|
widgets::{Bar, BarChart, BarGroup, Block, Borders, Padding, Paragraph, Row, Table},
|
||||||
Frame,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::duration::format_duration;
|
use super::duration::format_duration;
|
||||||
@ -21,11 +21,7 @@ use super::interactive::{InputAction, State};
|
|||||||
|
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
fn u64_or_zero(num: i64) -> u64 {
|
fn u64_or_zero(num: i64) -> u64 {
|
||||||
if num < 0 {
|
if num < 0 { 0 } else { num as u64 }
|
||||||
0
|
|
||||||
} else {
|
|
||||||
num as u64
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_commands(
|
pub fn draw_commands(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{stdout, Write},
|
io::{Write, stdout},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -11,8 +11,8 @@ use time::OffsetDateTime;
|
|||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
use atuin_client::{
|
use atuin_client::{
|
||||||
database::{current_context, Database},
|
database::{Database, current_context},
|
||||||
history::{store::HistoryStore, History, HistoryStats},
|
history::{History, HistoryStats, store::HistoryStore},
|
||||||
settings::{
|
settings::{
|
||||||
CursorStyle, ExitMode, FilterMode, KeymapMode, PreviewStrategy, SearchMode, Settings,
|
CursorStyle, ExitMode, FilterMode, KeymapMode, PreviewStrategy, SearchMode, Settings,
|
||||||
},
|
},
|
||||||
@ -25,9 +25,10 @@ use super::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::command::client::theme::{Meaning, Theme};
|
use crate::command::client::theme::{Meaning, Theme};
|
||||||
use crate::{command::client::search::engines, VERSION};
|
use crate::{VERSION, command::client::search::engines};
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
|
Frame, Terminal, TerminalOptions, Viewport,
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
crossterm::{
|
crossterm::{
|
||||||
cursor::SetCursorStyle,
|
cursor::SetCursorStyle,
|
||||||
@ -42,8 +43,7 @@ use ratatui::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
style::{Modifier, Style},
|
style::{Modifier, Style},
|
||||||
text::{Line, Span, Text},
|
text::{Line, Span, Text},
|
||||||
widgets::{block::Title, Block, BorderType, Borders, Padding, Paragraph, Tabs},
|
widgets::{Block, BorderType, Borders, Padding, Paragraph, Tabs, block::Title},
|
||||||
Frame, Terminal, TerminalOptions, Viewport,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TAB_TITLES: [&str; 2] = ["Search", "Inspect"];
|
const TAB_TITLES: [&str; 2] = ["Search", "Inspect"];
|
||||||
@ -390,7 +390,7 @@ impl State {
|
|||||||
KeyCode::Char(c @ '1'..='9') if modfr => {
|
KeyCode::Char(c @ '1'..='9') if modfr => {
|
||||||
return c.to_digit(10).map_or(InputAction::Continue, |c| {
|
return c.to_digit(10).map_or(InputAction::Continue, |c| {
|
||||||
InputAction::Accept(self.results_state.selected() + c as usize)
|
InputAction::Accept(self.results_state.selected() + c as usize)
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
KeyCode::Left if ctrl => self
|
KeyCode::Left if ctrl => self
|
||||||
.search
|
.search
|
||||||
@ -763,7 +763,9 @@ impl State {
|
|||||||
|
|
||||||
// HACK: I'm following up with abstracting this into the UI container, with a
|
// HACK: I'm following up with abstracting this into the UI container, with a
|
||||||
// sub-widget for search + for inspector
|
// sub-widget for search + for inspector
|
||||||
let feedback = Paragraph::new("The inspector is new - please give feedback (good, or bad) at https://forum.atuin.sh");
|
let feedback = Paragraph::new(
|
||||||
|
"The inspector is new - please give feedback (good, or bad) at https://forum.atuin.sh",
|
||||||
|
);
|
||||||
f.render_widget(feedback, input_chunk);
|
f.render_widget(feedback, input_chunk);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -4,7 +4,7 @@ use interim::parse_date_string;
|
|||||||
use time::{Duration, OffsetDateTime, Time};
|
use time::{Duration, OffsetDateTime, Time};
|
||||||
|
|
||||||
use atuin_client::{
|
use atuin_client::{
|
||||||
database::{current_context, Database},
|
database::{Database, current_context},
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
theme::Theme,
|
theme::Theme,
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use atuin_dotfiles::store::{var::VarStore, AliasStore};
|
use atuin_dotfiles::store::{AliasStore, var::VarStore};
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use eyre::{bail, Result};
|
use eyre::{Result, bail};
|
||||||
|
|
||||||
use atuin_client::{
|
use atuin_client::{
|
||||||
database::Database, encryption, history::store::HistoryStore,
|
database::Database, encryption, history::store::HistoryStore,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use clap::Args;
|
use clap::Args;
|
||||||
use eyre::{bail, Result};
|
use eyre::{Result, bail};
|
||||||
use tokio::{fs::File, io::AsyncWriteExt};
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
|
|
||||||
use atuin_client::{
|
use atuin_client::{
|
||||||
encryption::{decode_key, encode_key, generate_encoded_key, load_key, Key},
|
encryption::{Key, decode_key, encode_key, generate_encoded_key, load_key},
|
||||||
record::sqlite_store::SqliteStore,
|
record::sqlite_store::SqliteStore,
|
||||||
record::store::Store,
|
record::store::Store,
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
@ -23,7 +23,8 @@ impl Rekey {
|
|||||||
let key = match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) {
|
let key = match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) {
|
||||||
Ok(mnemonic) => encode_key(Key::from_slice(mnemonic.entropy()))?,
|
Ok(mnemonic) => encode_key(Key::from_slice(mnemonic.entropy()))?,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(err) = err.downcast_ref::<bip39::ErrorKind>() {
|
match err.downcast_ref::<bip39::ErrorKind>() {
|
||||||
|
Some(err) => {
|
||||||
match err {
|
match err {
|
||||||
// assume they copied in the base64 key
|
// assume they copied in the base64 key
|
||||||
bip39::ErrorKind::InvalidWord => key,
|
bip39::ErrorKind::InvalidWord => key,
|
||||||
@ -36,11 +37,13 @@ impl Rekey {
|
|||||||
bail!("key was not the correct length")
|
bail!("key was not the correct length")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
// unknown error. assume they copied the base64 key
|
// unknown error. assume they copied the base64 key
|
||||||
key
|
key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
key
|
key
|
||||||
|
@ -5,7 +5,7 @@ use time::{Date, Duration, Month, OffsetDateTime, Time};
|
|||||||
|
|
||||||
use atuin_client::{database::Database, settings::Settings, theme::Theme};
|
use atuin_client::{database::Database, settings::Settings, theme::Theme};
|
||||||
|
|
||||||
use atuin_history::stats::{compute, Stats};
|
use atuin_history::stats::{Stats, compute};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct WrappedStats {
|
struct WrappedStats {
|
||||||
@ -293,7 +293,9 @@ pub async fn run(
|
|||||||
|
|
||||||
let history = db.range(start, end).await?;
|
let history = db.range(start, end).await?;
|
||||||
if history.is_empty() {
|
if history.is_empty() {
|
||||||
println!("Your history for {year} is empty!\nMaybe 'atuin import' could help you import your previous history 🪄");
|
println!(
|
||||||
|
"Your history for {year} is empty!\nMaybe 'atuin import' could help you import your previous history 🪄"
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use clap::{CommandFactory, Parser, ValueEnum};
|
use clap::{CommandFactory, Parser, ValueEnum};
|
||||||
use clap_complete::{generate, generate_to, Generator, Shell};
|
use clap_complete::{Generator, Shell, generate, generate_to};
|
||||||
use clap_complete_nushell::Nushell;
|
use clap_complete_nushell::Nushell;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use atuin_server_postgres::Postgres;
|
use atuin_server_postgres::Postgres;
|
||||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use eyre::{Context, Result};
|
use eyre::{Context, Result};
|
||||||
|
|
||||||
use atuin_server::{example_config, launch, launch_metrics_server, Settings};
|
use atuin_server::{Settings, example_config, launch, launch_metrics_server};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(infer_subcommands = true)]
|
#[clap(infer_subcommands = true)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use atuin_dotfiles::store::{var::VarStore, AliasStore};
|
use atuin_dotfiles::store::{AliasStore, var::VarStore};
|
||||||
use eyre::{Context, Result};
|
use eyre::{Context, Result};
|
||||||
|
|
||||||
use atuin_client::{
|
use atuin_client::{
|
||||||
|
@ -2,12 +2,12 @@ use std::{env, time::Duration};
|
|||||||
|
|
||||||
use atuin_client::api_client;
|
use atuin_client::api_client;
|
||||||
use atuin_common::utils::uuid_v7;
|
use atuin_common::utils::uuid_v7;
|
||||||
use atuin_server::{launch_with_tcp_listener, Settings as ServerSettings};
|
use atuin_server::{Settings as ServerSettings, launch_with_tcp_listener};
|
||||||
use atuin_server_postgres::{Postgres, PostgresSettings};
|
use atuin_server_postgres::{Postgres, PostgresSettings};
|
||||||
use futures_util::TryFutureExt;
|
use futures_util::TryFutureExt;
|
||||||
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
|
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
|
||||||
use tracing::{dispatcher, Dispatch};
|
use tracing::{Dispatch, dispatcher};
|
||||||
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
|
use tracing_subscriber::{EnvFilter, layer::SubscriberExt};
|
||||||
|
|
||||||
pub async fn start_server(path: &str) -> (String, oneshot::Sender<()>, JoinHandle<()>) {
|
pub async fn start_server(path: &str) -> (String, oneshot::Sender<()>, JoinHandle<()>) {
|
||||||
let formatting_layer = tracing_tree::HierarchicalLayer::default()
|
let formatting_layer = tracing_tree::HierarchicalLayer::default()
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
fenix.packages.${system}.fromToolchainFile
|
fenix.packages.${system}.fromToolchainFile
|
||||||
{
|
{
|
||||||
file = ./rust-toolchain.toml;
|
file = ./rust-toolchain.toml;
|
||||||
sha256 = "sha256-AJ6LX/Q/Er9kS15bn9iflkUwcgYqRQxiOIL2ToVAXaU=";
|
sha256 = "sha256-Hn2uaQzRLidAWpfmRwSRdImifGUCAb9HeAqTYFXWeQk=";
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
pkgs.makeRustPlatform {
|
pkgs.makeRustPlatform {
|
||||||
|
Reference in New Issue
Block a user