Use cargo workspaces (#37)
* Switch to Cargo workspaces Breaking things into "client", "server" and "common" makes managing the codebase much easier! client - anything running on a user's machine for adding history server - handles storing/syncing history and running a HTTP server common - request/response API definitions, common utils, etc * Update dockerfile
This commit is contained in:
parent
34888827f8
commit
a21737e2b7
39 changed files with 416 additions and 310 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
|
*/target
|
||||||
.env
|
.env
|
||||||
|
|
168
Cargo.lock
generated
168
Cargo.lock
generated
|
@ -2,21 +2,6 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "addr2line"
|
|
||||||
version = "0.14.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7"
|
|
||||||
dependencies = [
|
|
||||||
"gimli",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "adler"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
@ -100,17 +85,46 @@ name = "atuin"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"atuin-client",
|
||||||
|
"atuin-common",
|
||||||
|
"atuin-server",
|
||||||
"base64",
|
"base64",
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-english",
|
"chrono-english",
|
||||||
"cli-table",
|
"cli-table",
|
||||||
"config",
|
|
||||||
"directories",
|
"directories",
|
||||||
"dotenv",
|
|
||||||
"eyre",
|
"eyre",
|
||||||
"fern",
|
"fern",
|
||||||
"fork",
|
"fork",
|
||||||
"human-panic",
|
"humantime",
|
||||||
|
"indicatif",
|
||||||
|
"itertools",
|
||||||
|
"log",
|
||||||
|
"reqwest",
|
||||||
|
"rusqlite",
|
||||||
|
"serde 1.0.125",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"structopt",
|
||||||
|
"termion",
|
||||||
|
"tokio",
|
||||||
|
"tui",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atuin-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"atuin-common",
|
||||||
|
"base64",
|
||||||
|
"chrono",
|
||||||
|
"chrono-english",
|
||||||
|
"config",
|
||||||
|
"directories",
|
||||||
|
"eyre",
|
||||||
|
"fern",
|
||||||
"humantime",
|
"humantime",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
@ -126,11 +140,55 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"sodiumoxide",
|
"sodiumoxide",
|
||||||
"sqlx",
|
|
||||||
"structopt",
|
|
||||||
"termion",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tui",
|
"urlencoding",
|
||||||
|
"uuid",
|
||||||
|
"whoami",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atuin-common"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"eyre",
|
||||||
|
"rmp-serde",
|
||||||
|
"rust-crypto",
|
||||||
|
"serde 1.0.125",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"sodiumoxide",
|
||||||
|
"uuid",
|
||||||
|
"warp",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atuin-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"atuin-common",
|
||||||
|
"base64",
|
||||||
|
"chrono",
|
||||||
|
"chrono-english",
|
||||||
|
"config",
|
||||||
|
"directories",
|
||||||
|
"eyre",
|
||||||
|
"fern",
|
||||||
|
"fork",
|
||||||
|
"indicatif",
|
||||||
|
"log",
|
||||||
|
"parse_duration",
|
||||||
|
"rand 0.8.3",
|
||||||
|
"reqwest",
|
||||||
|
"rmp-serde",
|
||||||
|
"rust-crypto",
|
||||||
|
"serde 1.0.125",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"sodiumoxide",
|
||||||
|
"sqlx",
|
||||||
|
"tokio",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
@ -144,20 +202,6 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "backtrace"
|
|
||||||
version = "0.3.56"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc"
|
|
||||||
dependencies = [
|
|
||||||
"addr2line",
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"miniz_oxide",
|
|
||||||
"object",
|
|
||||||
"rustc-demangle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
|
@ -771,12 +815,6 @@ dependencies = [
|
||||||
"wasi 0.10.2+wasi-snapshot-preview1",
|
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gimli"
|
|
||||||
version = "0.23.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -907,21 +945,6 @@ version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "human-panic"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "39f357a500abcbd7c5f967c1d45c8838585b36743823b9d43488f24850534e36"
|
|
||||||
dependencies = [
|
|
||||||
"backtrace",
|
|
||||||
"os_type",
|
|
||||||
"serde 1.0.125",
|
|
||||||
"serde_derive",
|
|
||||||
"termcolor",
|
|
||||||
"toml",
|
|
||||||
"uuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humantime"
|
name = "humantime"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -1168,16 +1191,6 @@ dependencies = [
|
||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
|
||||||
dependencies = [
|
|
||||||
"adler",
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.7.11"
|
version = "0.7.11"
|
||||||
|
@ -1377,12 +1390,6 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "object"
|
|
||||||
version = "0.23.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.5.2"
|
version = "1.5.2"
|
||||||
|
@ -1428,15 +1435,6 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "os_type"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7edc011af0ae98b7f88cf7e4a83b70a54a75d2b8cb013d6efd02e5956207e9eb"
|
|
||||||
dependencies = [
|
|
||||||
"regex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
@ -1900,12 +1898,6 @@ version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
|
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-demangle"
|
|
||||||
version = "0.1.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-serialize"
|
name = "rustc-serialize"
|
||||||
version = "0.3.24"
|
version = "0.3.24"
|
||||||
|
|
33
Cargo.toml
33
Cargo.toml
|
@ -1,47 +1,40 @@
|
||||||
[package]
|
[package]
|
||||||
name = "atuin"
|
name = "atuin"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
authors = ["Ellie Huxtable <e@elm.sh>"]
|
authors = ["Ellie Huxtable <ellie@elliehuxtable.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "atuin - magical shell history"
|
description = "atuin - magical shell history"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["./atuin-client", "./atuin-server", "./atuin-common"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
atuin-server = { path = "atuin-server", version = "0.1.0" }
|
||||||
|
atuin-client = { path = "atuin-client", version = "0.1.0" }
|
||||||
|
atuin-common = { path = "atuin-common", version = "0.1.0" }
|
||||||
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
fern = {version = "0.6.0", features = ["colored"] }
|
fern = {version = "0.6.0", features = ["colored"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
eyre = "0.6"
|
eyre = "0.6"
|
||||||
shellexpand = "2"
|
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
directories = "3"
|
directories = "3"
|
||||||
uuid = { version = "0.8", features = ["v4"] }
|
|
||||||
indicatif = "0.15.0"
|
indicatif = "0.15.0"
|
||||||
whoami = "1.1.2"
|
|
||||||
chrono-english = "0.1.4"
|
|
||||||
cli-table = "0.4"
|
|
||||||
config = "0.11"
|
|
||||||
serde_derive = "1.0.125"
|
serde_derive = "1.0.125"
|
||||||
serde = "1.0.125"
|
serde = "1.0.125"
|
||||||
serde_json = "1.0.64"
|
serde_json = "1.0.64"
|
||||||
rmp-serde = "0.15.4"
|
|
||||||
tui = "0.14"
|
tui = "0.14"
|
||||||
termion = "1.5"
|
termion = "1.5"
|
||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
dotenv = "0.15.0"
|
fork = "0.1.18"
|
||||||
sodiumoxide = "0.2.6"
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
async-trait = "0.1.49"
|
||||||
|
chrono-english = "0.1.4"
|
||||||
|
cli-table = "0.4"
|
||||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
fork = "0.1.18"
|
|
||||||
parse_duration = "2.1.1"
|
|
||||||
rand = "0.8.3"
|
|
||||||
rust-crypto = "^0.2"
|
|
||||||
human-panic = "1.0.3"
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
|
||||||
warp = "0.3"
|
|
||||||
sqlx = { version = "0.5", features = [ "runtime-tokio-native-tls", "uuid", "chrono", "postgres" ] }
|
|
||||||
async-trait = "0.1.49"
|
|
||||||
urlencoding = "1.1.1"
|
|
||||||
humantime = "2.1.0"
|
humantime = "2.1.0"
|
||||||
|
|
||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
|
|
47
Dockerfile
47
Dockerfile
|
@ -1,31 +1,22 @@
|
||||||
FROM rust:1.51-buster as builder
|
FROM lukemathwalker/cargo-chef as planner
|
||||||
|
WORKDIR app
|
||||||
|
COPY . .
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
RUN cargo new --bin atuin
|
FROM lukemathwalker/cargo-chef as cacher
|
||||||
WORKDIR /atuin
|
WORKDIR app
|
||||||
COPY ./Cargo.toml ./Cargo.toml
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
COPY ./Cargo.lock ./Cargo.lock
|
RUN cargo chef cook --release --recipe-path recipe.json
|
||||||
|
|
||||||
RUN cargo build --release
|
FROM rust as builder
|
||||||
|
WORKDIR app
|
||||||
|
COPY . .
|
||||||
|
# Copy over the cached dependencies
|
||||||
|
COPY --from=cacher /app/target target
|
||||||
|
COPY --from=cacher $CARGO_HOME $CARGO_HOME
|
||||||
|
RUN cargo build --release --bin atuin
|
||||||
|
|
||||||
RUN rm src/*.rs
|
FROM debian:buster-slim as runtime
|
||||||
|
WORKDIR app
|
||||||
ADD . ./
|
COPY --from=builder /app/target/release/atuin /usr/local/bin
|
||||||
|
ENTRYPOINT ["/usr/local/bin/atuin"]
|
||||||
RUN rm ./target/release/deps/atuin*
|
|
||||||
RUN cargo build --release
|
|
||||||
|
|
||||||
FROM debian:buster-slim
|
|
||||||
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y ca-certificates tzdata libpq-dev \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
EXPOSE 8888
|
|
||||||
|
|
||||||
ENV TZ=Etc/UTC
|
|
||||||
ENV RUST_LOG=info
|
|
||||||
ENV ATUIN_CONFIG=/config/config.toml
|
|
||||||
|
|
||||||
COPY --from=builder /atuin/target/release/atuin ./atuin
|
|
||||||
|
|
||||||
ENTRYPOINT ["./atuin"]
|
|
||||||
|
|
40
atuin-client/Cargo.toml
Normal file
40
atuin-client/Cargo.toml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
[package]
|
||||||
|
name = "atuin-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Ellie Huxtable <ellie@elliehuxtable.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT"
|
||||||
|
description = "client library for atuin"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
atuin-common = { path = "../atuin-common", version = "0.1.0" }
|
||||||
|
|
||||||
|
log = "0.4"
|
||||||
|
fern = {version = "0.6.0", features = ["colored"] }
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
eyre = "0.6"
|
||||||
|
directories = "3"
|
||||||
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
|
indicatif = "0.15.0"
|
||||||
|
whoami = "1.1.2"
|
||||||
|
chrono-english = "0.1.4"
|
||||||
|
config = "0.11"
|
||||||
|
serde_derive = "1.0.125"
|
||||||
|
serde = "1.0.125"
|
||||||
|
serde_json = "1.0.64"
|
||||||
|
rmp-serde = "0.15.4"
|
||||||
|
sodiumoxide = "0.2.6"
|
||||||
|
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
|
base64 = "0.13.0"
|
||||||
|
parse_duration = "2.1.1"
|
||||||
|
rand = "0.8.3"
|
||||||
|
rust-crypto = "^0.2"
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
async-trait = "0.1.49"
|
||||||
|
urlencoding = "1.1.1"
|
||||||
|
humantime = "2.1.0"
|
||||||
|
rusqlite= { version = "0.25", features = ["bundled"] }
|
||||||
|
itertools = "0.10.0"
|
||||||
|
shellexpand = "2"
|
|
@ -1,8 +1,3 @@
|
||||||
# A'tuin example config
|
|
||||||
|
|
||||||
# This section specifies the config for a local client,
|
|
||||||
# ie where your shell history is on your local machine
|
|
||||||
[local]
|
|
||||||
## where to store your database, default is your system data directory
|
## where to store your database, default is your system data directory
|
||||||
## mac: ~/Library/Application Support/com.elliehuxtable.atuin/history.db
|
## mac: ~/Library/Application Support/com.elliehuxtable.atuin/history.db
|
||||||
## linux: ~/.local/share/atuin/history.db
|
## linux: ~/.local/share/atuin/history.db
|
||||||
|
@ -27,17 +22,3 @@
|
||||||
|
|
||||||
## address of the sync server
|
## address of the sync server
|
||||||
# sync_address = "https://api.atuin.sh"
|
# sync_address = "https://api.atuin.sh"
|
||||||
|
|
||||||
# This section configures the sync server, if you decide to host your own
|
|
||||||
[server]
|
|
||||||
## host to bind, can also be passed via CLI args
|
|
||||||
# host = "127.0.0.1"
|
|
||||||
|
|
||||||
## port to bind, can also be passed via CLI args
|
|
||||||
# port = 8888
|
|
||||||
|
|
||||||
## whether to allow anyone to register an account
|
|
||||||
# open_registration = false
|
|
||||||
|
|
||||||
## URI for postgres (using development creds here)
|
|
||||||
# db_uri="postgres://username:password@localhost/atuin"
|
|
|
@ -4,10 +4,11 @@ use reqwest::header::{HeaderMap, AUTHORIZATION};
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use sodiumoxide::crypto::secretbox;
|
use sodiumoxide::crypto::secretbox;
|
||||||
|
|
||||||
use crate::api::{AddHistoryRequest, CountResponse, SyncHistoryResponse};
|
use atuin_common::api::{AddHistoryRequest, CountResponse, SyncHistoryResponse};
|
||||||
use crate::local::encryption::decrypt;
|
use atuin_common::utils::hash_str;
|
||||||
use crate::local::history::History;
|
|
||||||
use crate::utils::hash_str;
|
use crate::encryption::decrypt;
|
||||||
|
use crate::history::History;
|
||||||
|
|
||||||
pub struct Client<'a> {
|
pub struct Client<'a> {
|
||||||
sync_addr: &'a str,
|
sync_addr: &'a str,
|
|
@ -15,7 +15,7 @@ use std::path::PathBuf;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{eyre, Result};
|
||||||
use sodiumoxide::crypto::secretbox;
|
use sodiumoxide::crypto::secretbox;
|
||||||
|
|
||||||
use crate::local::history::History;
|
use crate::history::History;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
@ -26,7 +26,7 @@ pub struct EncryptedHistory {
|
||||||
|
|
||||||
// Loads the secret key, will create + save if it doesn't exist
|
// Loads the secret key, will create + save if it doesn't exist
|
||||||
pub fn load_key(settings: &Settings) -> Result<secretbox::Key> {
|
pub fn load_key(settings: &Settings) -> Result<secretbox::Key> {
|
||||||
let path = settings.local.key_path.as_str();
|
let path = settings.key_path.as_str();
|
||||||
|
|
||||||
if PathBuf::from(path).exists() {
|
if PathBuf::from(path).exists() {
|
||||||
let bytes = std::fs::read(path)?;
|
let bytes = std::fs::read(path)?;
|
|
@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
|
||||||
use crate::command::uuid_v4;
|
use atuin_common::utils::uuid_v4;
|
||||||
|
|
||||||
// Any new fields MUST be Optional<>!
|
// Any new fields MUST be Optional<>!
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
@ -1,6 +1,13 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
pub mod api_client;
|
pub mod api_client;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
pub mod encryption;
|
pub mod encryption;
|
||||||
pub mod history;
|
pub mod history;
|
||||||
pub mod import;
|
pub mod import;
|
||||||
|
pub mod settings;
|
||||||
pub mod sync;
|
pub mod sync;
|
|
@ -12,7 +12,7 @@ use parse_duration::parse;
|
||||||
pub const HISTORY_PAGE_SIZE: i64 = 100;
|
pub const HISTORY_PAGE_SIZE: i64 = 100;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct Local {
|
pub struct Settings {
|
||||||
pub dialect: String,
|
pub dialect: String,
|
||||||
pub auto_sync: bool,
|
pub auto_sync: bool,
|
||||||
pub sync_address: String,
|
pub sync_address: String,
|
||||||
|
@ -26,7 +26,7 @@ pub struct Local {
|
||||||
pub session_token: String,
|
pub session_token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Local {
|
impl Settings {
|
||||||
pub fn save_sync_time() -> Result<()> {
|
pub fn save_sync_time() -> Result<()> {
|
||||||
let sync_time_path = ProjectDirs::from("com", "elliehuxtable", "atuin")
|
let sync_time_path = ProjectDirs::from("com", "elliehuxtable", "atuin")
|
||||||
.ok_or_else(|| eyre!("could not determine key file location"))?;
|
.ok_or_else(|| eyre!("could not determine key file location"))?;
|
||||||
|
@ -66,28 +66,12 @@ impl Local {
|
||||||
match parse(self.sync_frequency.as_str()) {
|
match parse(self.sync_frequency.as_str()) {
|
||||||
Ok(d) => {
|
Ok(d) => {
|
||||||
let d = chrono::Duration::from_std(d).unwrap();
|
let d = chrono::Duration::from_std(d).unwrap();
|
||||||
Ok(Utc::now() - Local::last_sync()? >= d)
|
Ok(Utc::now() - Settings::last_sync()? >= d)
|
||||||
}
|
}
|
||||||
Err(e) => Err(eyre!("failed to check sync: {}", e)),
|
Err(e) => Err(eyre!("failed to check sync: {}", e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
pub struct Server {
|
|
||||||
pub host: String,
|
|
||||||
pub port: u16,
|
|
||||||
pub db_uri: String,
|
|
||||||
pub open_registration: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
pub struct Settings {
|
|
||||||
pub local: Local,
|
|
||||||
pub server: Server,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Settings {
|
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let config_dir = ProjectDirs::from("com", "elliehuxtable", "atuin").unwrap();
|
let config_dir = ProjectDirs::from("com", "elliehuxtable", "atuin").unwrap();
|
||||||
let config_dir = config_dir.config_dir();
|
let config_dir = config_dir.config_dir();
|
||||||
|
@ -103,8 +87,6 @@ impl Settings {
|
||||||
config_file
|
config_file
|
||||||
};
|
};
|
||||||
|
|
||||||
// create the config file if it does not exist
|
|
||||||
|
|
||||||
let mut s = Config::new();
|
let mut s = Config::new();
|
||||||
|
|
||||||
let db_path = ProjectDirs::from("com", "elliehuxtable", "atuin")
|
let db_path = ProjectDirs::from("com", "elliehuxtable", "atuin")
|
||||||
|
@ -122,18 +104,13 @@ impl Settings {
|
||||||
.data_dir()
|
.data_dir()
|
||||||
.join("session");
|
.join("session");
|
||||||
|
|
||||||
s.set_default("local.db_path", db_path.to_str())?;
|
s.set_default("db_path", db_path.to_str())?;
|
||||||
s.set_default("local.key_path", key_path.to_str())?;
|
s.set_default("key_path", key_path.to_str())?;
|
||||||
s.set_default("local.session_path", session_path.to_str())?;
|
s.set_default("session_path", session_path.to_str())?;
|
||||||
s.set_default("local.dialect", "us")?;
|
s.set_default("dialect", "us")?;
|
||||||
s.set_default("local.auto_sync", true)?;
|
s.set_default("auto_sync", true)?;
|
||||||
s.set_default("local.sync_frequency", "5m")?;
|
s.set_default("sync_frequency", "5m")?;
|
||||||
s.set_default("local.sync_address", "https://api.atuin.sh")?;
|
s.set_default("sync_address", "https://api.atuin.sh")?;
|
||||||
|
|
||||||
s.set_default("server.host", "127.0.0.1")?;
|
|
||||||
s.set_default("server.port", 8888)?;
|
|
||||||
s.set_default("server.open_registration", false)?;
|
|
||||||
s.set_default("server.db_uri", "default_uri")?;
|
|
||||||
|
|
||||||
if config_file.exists() {
|
if config_file.exists() {
|
||||||
s.merge(ConfigFile::with_name(config_file.to_str().unwrap()))?;
|
s.merge(ConfigFile::with_name(config_file.to_str().unwrap()))?;
|
||||||
|
@ -146,24 +123,24 @@ impl Settings {
|
||||||
s.merge(Environment::with_prefix("atuin").separator("_"))?;
|
s.merge(Environment::with_prefix("atuin").separator("_"))?;
|
||||||
|
|
||||||
// all paths should be expanded
|
// all paths should be expanded
|
||||||
let db_path = s.get_str("local.db_path")?;
|
let db_path = s.get_str("db_path")?;
|
||||||
let db_path = shellexpand::full(db_path.as_str())?;
|
let db_path = shellexpand::full(db_path.as_str())?;
|
||||||
s.set("local.db_path", db_path.to_string())?;
|
s.set("db_path", db_path.to_string())?;
|
||||||
|
|
||||||
let key_path = s.get_str("local.key_path")?;
|
let key_path = s.get_str("key_path")?;
|
||||||
let key_path = shellexpand::full(key_path.as_str())?;
|
let key_path = shellexpand::full(key_path.as_str())?;
|
||||||
s.set("local.key_path", key_path.to_string())?;
|
s.set("key_path", key_path.to_string())?;
|
||||||
|
|
||||||
let session_path = s.get_str("local.session_path")?;
|
let session_path = s.get_str("session_path")?;
|
||||||
let session_path = shellexpand::full(session_path.as_str())?;
|
let session_path = shellexpand::full(session_path.as_str())?;
|
||||||
s.set("local.session_path", session_path.to_string())?;
|
s.set("session_path", session_path.to_string())?;
|
||||||
|
|
||||||
// Finally, set the auth token
|
// Finally, set the auth token
|
||||||
if Path::new(session_path.to_string().as_str()).exists() {
|
if Path::new(session_path.to_string().as_str()).exists() {
|
||||||
let token = std::fs::read_to_string(session_path.to_string())?;
|
let token = std::fs::read_to_string(session_path.to_string())?;
|
||||||
s.set("local.session_token", token.trim())?;
|
s.set("session_token", token.trim())?;
|
||||||
} else {
|
} else {
|
||||||
s.set("local.session_token", "not logged in")?;
|
s.set("session_token", "not logged in")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.try_into()
|
s.try_into()
|
|
@ -3,11 +3,12 @@ use std::convert::TryInto;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
|
||||||
use crate::local::api_client;
|
use atuin_common::{api::AddHistoryRequest, utils::hash_str};
|
||||||
use crate::local::database::Database;
|
|
||||||
use crate::local::encryption::{encrypt, load_key};
|
use crate::api_client;
|
||||||
use crate::settings::{Local, Settings, HISTORY_PAGE_SIZE};
|
use crate::database::Database;
|
||||||
use crate::{api::AddHistoryRequest, utils::hash_str};
|
use crate::encryption::{encrypt, load_key};
|
||||||
|
use crate::settings::{Settings, HISTORY_PAGE_SIZE};
|
||||||
|
|
||||||
// Currently sync is kinda naive, and basically just pages backwards through
|
// Currently sync is kinda naive, and basically just pages backwards through
|
||||||
// history. This means newly added stuff shows up properly! We also just use
|
// history. This means newly added stuff shows up properly! We also just use
|
||||||
|
@ -33,7 +34,7 @@ async fn sync_download(
|
||||||
let mut last_sync = if force {
|
let mut last_sync = if force {
|
||||||
Utc.timestamp_millis(0)
|
Utc.timestamp_millis(0)
|
||||||
} else {
|
} else {
|
||||||
Local::last_sync()?
|
Settings::last_sync()?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut last_timestamp = Utc.timestamp_millis(0);
|
let mut last_timestamp = Utc.timestamp_millis(0);
|
||||||
|
@ -124,8 +125,8 @@ async fn sync_upload(
|
||||||
|
|
||||||
pub async fn sync(settings: &Settings, force: bool, db: &mut (impl Database + Send)) -> Result<()> {
|
pub async fn sync(settings: &Settings, force: bool, db: &mut (impl Database + Send)) -> Result<()> {
|
||||||
let client = api_client::Client::new(
|
let client = api_client::Client::new(
|
||||||
settings.local.sync_address.as_str(),
|
settings.sync_address.as_str(),
|
||||||
settings.local.session_token.as_str(),
|
settings.session_token.as_str(),
|
||||||
load_key(settings)?,
|
load_key(settings)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -135,7 +136,7 @@ pub async fn sync(settings: &Settings, force: bool, db: &mut (impl Database + Se
|
||||||
|
|
||||||
debug!("sync downloaded {}", download.0);
|
debug!("sync downloaded {}", download.0);
|
||||||
|
|
||||||
Local::save_sync_time()?;
|
Settings::save_sync_time()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
21
atuin-common/Cargo.toml
Normal file
21
atuin-common/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "atuin-common"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Ellie Huxtable <ellie@elliehuxtable.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT"
|
||||||
|
description = "common library for atuin"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rust-crypto = "^0.2"
|
||||||
|
sodiumoxide = "0.2.6"
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
eyre = "0.6"
|
||||||
|
serde_derive = "1.0.125"
|
||||||
|
serde = "1.0.125"
|
||||||
|
serde_json = "1.0.64"
|
||||||
|
rmp-serde = "0.15.4"
|
||||||
|
warp = "0.3"
|
||||||
|
uuid = { version = "0.8", features = ["v4"] }
|
5
atuin-common/src/lib.rs
Normal file
5
atuin-common/src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
pub mod api;
|
||||||
|
pub mod utils;
|
|
@ -1,6 +1,7 @@
|
||||||
use crypto::digest::Digest;
|
use crypto::digest::Digest;
|
||||||
use crypto::sha2::Sha256;
|
use crypto::sha2::Sha256;
|
||||||
use sodiumoxide::crypto::pwhash::argon2id13;
|
use sodiumoxide::crypto::pwhash::argon2id13;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub fn hash_secret(secret: &str) -> String {
|
pub fn hash_secret(secret: &str) -> String {
|
||||||
sodiumoxide::init().unwrap();
|
sodiumoxide::init().unwrap();
|
||||||
|
@ -22,3 +23,7 @@ pub fn hash_str(string: &str) -> String {
|
||||||
|
|
||||||
hasher.result_str()
|
hasher.result_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uuid_v4() -> String {
|
||||||
|
Uuid::new_v4().to_simple().to_string()
|
||||||
|
}
|
38
atuin-server/Cargo.toml
Normal file
38
atuin-server/Cargo.toml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
[package]
|
||||||
|
name = "atuin-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Ellie Huxtable <ellie@elliehuxtable.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT"
|
||||||
|
description = "server library for atuin"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
atuin-common = { path = "../atuin-common", version = "0.1.0" }
|
||||||
|
|
||||||
|
log = "0.4"
|
||||||
|
fern = {version = "0.6.0", features = ["colored"] }
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
eyre = "0.6"
|
||||||
|
directories = "3"
|
||||||
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
|
indicatif = "0.15.0"
|
||||||
|
whoami = "1.1.2"
|
||||||
|
chrono-english = "0.1.4"
|
||||||
|
config = "0.11"
|
||||||
|
serde_derive = "1.0.125"
|
||||||
|
serde = "1.0.125"
|
||||||
|
serde_json = "1.0.64"
|
||||||
|
rmp-serde = "0.15.4"
|
||||||
|
unicode-width = "0.1"
|
||||||
|
sodiumoxide = "0.2.6"
|
||||||
|
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
|
base64 = "0.13.0"
|
||||||
|
fork = "0.1.18"
|
||||||
|
parse_duration = "2.1.1"
|
||||||
|
rand = "0.8.3"
|
||||||
|
rust-crypto = "^0.2"
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
warp = "0.3"
|
||||||
|
sqlx = { version = "0.5", features = [ "runtime-tokio-native-tls", "uuid", "chrono", "postgres" ] }
|
||||||
|
async-trait = "0.1.49"
|
||||||
|
urlencoding = "1.1.1"
|
11
atuin-server/server.toml
Normal file
11
atuin-server/server.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
## host to bind, can also be passed via CLI args
|
||||||
|
# host = "127.0.0.1"
|
||||||
|
|
||||||
|
## port to bind, can also be passed via CLI args
|
||||||
|
# port = 8888
|
||||||
|
|
||||||
|
## whether to allow anyone to register an account
|
||||||
|
# open_registration = false
|
||||||
|
|
||||||
|
## URI for postgres (using development creds here)
|
||||||
|
# db_uri="postgres://username:password@localhost/atuin"
|
|
@ -2,11 +2,11 @@ use std::convert::Infallible;
|
||||||
|
|
||||||
use warp::{http::StatusCode, reply::json};
|
use warp::{http::StatusCode, reply::json};
|
||||||
|
|
||||||
use crate::api::{
|
use crate::database::Database;
|
||||||
|
use crate::models::{NewHistory, User};
|
||||||
|
use atuin_common::api::{
|
||||||
AddHistoryRequest, CountResponse, ErrorResponse, SyncHistoryRequest, SyncHistoryResponse,
|
AddHistoryRequest, CountResponse, ErrorResponse, SyncHistoryRequest, SyncHistoryResponse,
|
||||||
};
|
};
|
||||||
use crate::server::database::Database;
|
|
||||||
use crate::server::models::{NewHistory, User};
|
|
||||||
|
|
||||||
pub async fn count(
|
pub async fn count(
|
||||||
user: User,
|
user: User,
|
|
@ -5,13 +5,14 @@ use uuid::Uuid;
|
||||||
use warp::http::StatusCode;
|
use warp::http::StatusCode;
|
||||||
use warp::reply::json;
|
use warp::reply::json;
|
||||||
|
|
||||||
use crate::api::{
|
use atuin_common::api::{
|
||||||
ErrorResponse, LoginRequest, LoginResponse, RegisterRequest, RegisterResponse, UserResponse,
|
ErrorResponse, LoginRequest, LoginResponse, RegisterRequest, RegisterResponse, UserResponse,
|
||||||
};
|
};
|
||||||
use crate::server::database::Database;
|
use atuin_common::utils::hash_secret;
|
||||||
use crate::server::models::{NewSession, NewUser};
|
|
||||||
|
use crate::database::Database;
|
||||||
|
use crate::models::{NewSession, NewUser};
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crate::utils::hash_secret;
|
|
||||||
|
|
||||||
pub fn verify_str(secret: &str, verify: &str) -> bool {
|
pub fn verify_str(secret: &str, verify: &str) -> bool {
|
||||||
sodiumoxide::init().unwrap();
|
sodiumoxide::init().unwrap();
|
||||||
|
@ -52,7 +53,7 @@ pub async fn register(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
db: impl Database + Clone + Send + Sync,
|
db: impl Database + Clone + Send + Sync,
|
||||||
) -> Result<Box<dyn warp::Reply>, Infallible> {
|
) -> Result<Box<dyn warp::Reply>, Infallible> {
|
||||||
if !settings.server.open_registration {
|
if !settings.open_registration {
|
||||||
return Ok(Box::new(ErrorResponse::reply(
|
return Ok(Box::new(ErrorResponse::reply(
|
||||||
"this server is not open for registrations",
|
"this server is not open for registrations",
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
|
@ -4,11 +4,18 @@ use eyre::Result;
|
||||||
|
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
pub mod handlers;
|
pub mod handlers;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod router;
|
pub mod router;
|
||||||
|
pub mod settings;
|
||||||
|
|
||||||
pub async fn launch(settings: &Settings, host: String, port: u16) -> Result<()> {
|
pub async fn launch(settings: &Settings, host: String, port: u16) -> Result<()> {
|
||||||
// routes to run:
|
// routes to run:
|
|
@ -3,10 +3,12 @@ use std::convert::Infallible;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
|
||||||
|
use atuin_common::api::SyncHistoryRequest;
|
||||||
|
|
||||||
use super::handlers;
|
use super::handlers;
|
||||||
use super::{database::Database, database::Postgres};
|
use super::{database::Database, database::Postgres};
|
||||||
use crate::server::models::User;
|
use crate::models::User;
|
||||||
use crate::{api::SyncHistoryRequest, settings::Settings};
|
use crate::settings::Settings;
|
||||||
|
|
||||||
fn with_settings(
|
fn with_settings(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
|
@ -55,7 +57,7 @@ fn with_user(
|
||||||
pub async fn router(
|
pub async fn router(
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Result<impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone> {
|
) -> Result<impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone> {
|
||||||
let postgres = Postgres::new(settings.server.db_uri.as_str()).await?;
|
let postgres = Postgres::new(settings.db_uri.as_str()).await?;
|
||||||
let index = warp::get().and(warp::path::end()).map(handlers::index);
|
let index = warp::get().and(warp::path::end()).map(handlers::index);
|
||||||
|
|
||||||
let count = warp::get()
|
let count = warp::get()
|
57
atuin-server/src/settings.rs
Normal file
57
atuin-server/src/settings.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use std::fs::{create_dir_all, File};
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use config::{Config, Environment, File as ConfigFile};
|
||||||
|
use directories::ProjectDirs;
|
||||||
|
use eyre::{eyre, Result};
|
||||||
|
|
||||||
|
pub const HISTORY_PAGE_SIZE: i64 = 100;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub host: String,
|
||||||
|
pub port: u16,
|
||||||
|
pub db_uri: String,
|
||||||
|
pub open_registration: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let config_dir = ProjectDirs::from("com", "elliehuxtable", "atuin").unwrap();
|
||||||
|
let config_dir = config_dir.config_dir();
|
||||||
|
|
||||||
|
create_dir_all(config_dir)?;
|
||||||
|
|
||||||
|
let config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG") {
|
||||||
|
PathBuf::from(p)
|
||||||
|
} else {
|
||||||
|
let mut config_file = PathBuf::new();
|
||||||
|
config_file.push(config_dir);
|
||||||
|
config_file.push("server.toml");
|
||||||
|
config_file
|
||||||
|
};
|
||||||
|
|
||||||
|
// create the config file if it does not exist
|
||||||
|
|
||||||
|
let mut s = Config::new();
|
||||||
|
|
||||||
|
if config_file.exists() {
|
||||||
|
s.merge(ConfigFile::with_name(config_file.to_str().unwrap()))?;
|
||||||
|
} else {
|
||||||
|
let example_config = include_bytes!("../server.toml");
|
||||||
|
let mut file = File::create(config_file)?;
|
||||||
|
file.write_all(example_config)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
s.set_default("host", "127.0.0.1")?;
|
||||||
|
s.set_default("port", 8888)?;
|
||||||
|
s.set_default("open_registration", false)?;
|
||||||
|
s.set_default("db_uri", "default_uri")?;
|
||||||
|
|
||||||
|
s.merge(Environment::with_prefix("atuin").separator("_"))?;
|
||||||
|
|
||||||
|
s.try_into()
|
||||||
|
.map_err(|e| eyre!("failed to deserialize: {}", e))
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,10 +4,10 @@ use eyre::Result;
|
||||||
use fork::{fork, Fork};
|
use fork::{fork, Fork};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::local::database::Database;
|
use atuin_client::database::Database;
|
||||||
use crate::local::history::History;
|
use atuin_client::history::History;
|
||||||
use crate::local::sync;
|
use atuin_client::settings::Settings;
|
||||||
use crate::settings::Settings;
|
use atuin_client::sync;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
|
@ -79,7 +79,7 @@ impl Cmd {
|
||||||
|
|
||||||
db.update(&h)?;
|
db.update(&h)?;
|
||||||
|
|
||||||
if settings.local.should_sync()? {
|
if settings.should_sync()? {
|
||||||
match fork() {
|
match fork() {
|
||||||
Ok(Fork::Parent(child)) => {
|
Ok(Fork::Parent(child)) => {
|
||||||
debug!("launched sync background process with PID {}", child);
|
debug!("launched sync background process with PID {}", child);
|
||||||
|
|
|
@ -5,9 +5,9 @@ use directories::UserDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{eyre, Result};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::local::database::Database;
|
use atuin_client::database::Database;
|
||||||
use crate::local::history::History;
|
use atuin_client::history::History;
|
||||||
use crate::local::import::Zsh;
|
use atuin_client::import::Zsh;
|
||||||
use indicatif::ProgressBar;
|
use indicatif::ProgressBar;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::io::prelude::*;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{eyre, Result};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::settings::Settings;
|
use atuin_client::settings::Settings;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
#[structopt(setting(structopt::clap::AppSettings::DeriveDisplayOrder))]
|
#[structopt(setting(structopt::clap::AppSettings::DeriveDisplayOrder))]
|
||||||
|
@ -26,7 +26,7 @@ impl Cmd {
|
||||||
map.insert("username", self.username.clone());
|
map.insert("username", self.username.clone());
|
||||||
map.insert("password", self.password.clone());
|
map.insert("password", self.password.clone());
|
||||||
|
|
||||||
let url = format!("{}/login", settings.local.sync_address);
|
let url = format!("{}/login", settings.sync_address);
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
|
|
||||||
let resp = client.post(url).json(&map).send()?;
|
let resp = client.post(url).json(&map).send()?;
|
||||||
|
@ -38,11 +38,11 @@ impl Cmd {
|
||||||
let session = resp.json::<HashMap<String, String>>()?;
|
let session = resp.json::<HashMap<String, String>>()?;
|
||||||
let session = session["session"].clone();
|
let session = session["session"].clone();
|
||||||
|
|
||||||
let session_path = settings.local.session_path.as_str();
|
let session_path = settings.session_path.as_str();
|
||||||
let mut file = File::create(session_path)?;
|
let mut file = File::create(session_path)?;
|
||||||
file.write_all(session.as_bytes())?;
|
file.write_all(session.as_bytes())?;
|
||||||
|
|
||||||
let key_path = settings.local.key_path.as_str();
|
let key_path = settings.key_path.as_str();
|
||||||
let mut file = File::create(key_path)?;
|
let mut file = File::create(key_path)?;
|
||||||
file.write_all(&base64::decode(self.key.clone())?)?;
|
file.write_all(&base64::decode(self.key.clone())?)?;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::local::database::Database;
|
use atuin_client::database::Sqlite;
|
||||||
use crate::settings::Settings;
|
use atuin_client::settings::Settings as ClientSettings;
|
||||||
|
use atuin_common::utils::uuid_v4;
|
||||||
|
use atuin_server::settings::Settings as ServerSettings;
|
||||||
|
|
||||||
mod event;
|
mod event;
|
||||||
mod history;
|
mod history;
|
||||||
|
@ -58,30 +61,33 @@ pub enum AtuinCmd {
|
||||||
Key,
|
Key,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uuid_v4() -> String {
|
|
||||||
Uuid::new_v4().to_simple().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AtuinCmd {
|
impl AtuinCmd {
|
||||||
pub async fn run<T: Database + Send>(self, db: &mut T, settings: &Settings) -> Result<()> {
|
pub async fn run(self) -> Result<()> {
|
||||||
match self {
|
let client_settings = ClientSettings::new()?;
|
||||||
Self::History(history) => history.run(settings, db).await,
|
let server_settings = ServerSettings::new()?;
|
||||||
Self::Import(import) => import.run(db),
|
|
||||||
Self::Server(server) => server.run(settings).await,
|
|
||||||
Self::Stats(stats) => stats.run(db, settings),
|
|
||||||
Self::Init => init::init(),
|
|
||||||
Self::Search { query } => search::run(&query, db),
|
|
||||||
|
|
||||||
Self::Sync { force } => sync::run(settings, force, db).await,
|
let db_path = PathBuf::from(client_settings.db_path.as_str());
|
||||||
Self::Login(l) => l.run(settings),
|
|
||||||
|
let mut db = Sqlite::new(db_path)?;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::History(history) => history.run(&client_settings, &mut db).await,
|
||||||
|
Self::Import(import) => import.run(&mut db),
|
||||||
|
Self::Server(server) => server.run(&server_settings).await,
|
||||||
|
Self::Stats(stats) => stats.run(&mut db, &client_settings),
|
||||||
|
Self::Init => init::init(),
|
||||||
|
Self::Search { query } => search::run(&query, &mut db),
|
||||||
|
|
||||||
|
Self::Sync { force } => sync::run(&client_settings, force, &mut db).await,
|
||||||
|
Self::Login(l) => l.run(&client_settings),
|
||||||
Self::Register(r) => register::run(
|
Self::Register(r) => register::run(
|
||||||
settings,
|
&client_settings,
|
||||||
r.username.as_str(),
|
r.username.as_str(),
|
||||||
r.email.as_str(),
|
r.email.as_str(),
|
||||||
r.password.as_str(),
|
r.password.as_str(),
|
||||||
),
|
),
|
||||||
Self::Key => {
|
Self::Key => {
|
||||||
let key = std::fs::read(settings.local.key_path.as_str())?;
|
let key = std::fs::read(client_settings.key_path.as_str())?;
|
||||||
println!("{}", base64::encode(key));
|
println!("{}", base64::encode(key));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::io::prelude::*;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{eyre, Result};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::settings::Settings;
|
use atuin_client::settings::Settings;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
#[structopt(setting(structopt::clap::AppSettings::DeriveDisplayOrder))]
|
#[structopt(setting(structopt::clap::AppSettings::DeriveDisplayOrder))]
|
||||||
|
@ -26,7 +26,7 @@ pub fn run(settings: &Settings, username: &str, email: &str, password: &str) ->
|
||||||
map.insert("email", email);
|
map.insert("email", email);
|
||||||
map.insert("password", password);
|
map.insert("password", password);
|
||||||
|
|
||||||
let url = format!("{}/user/{}", settings.local.sync_address, username);
|
let url = format!("{}/user/{}", settings.sync_address, username);
|
||||||
let resp = reqwest::blocking::get(url)?;
|
let resp = reqwest::blocking::get(url)?;
|
||||||
|
|
||||||
if resp.status().is_success() {
|
if resp.status().is_success() {
|
||||||
|
@ -34,7 +34,7 @@ pub fn run(settings: &Settings, username: &str, email: &str, password: &str) ->
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = format!("{}/register", settings.local.sync_address);
|
let url = format!("{}/register", settings.sync_address);
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let resp = client.post(url).json(&map).send()?;
|
let resp = client.post(url).json(&map).send()?;
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ pub fn run(settings: &Settings, username: &str, email: &str, password: &str) ->
|
||||||
let session = resp.json::<HashMap<String, String>>()?;
|
let session = resp.json::<HashMap<String, String>>()?;
|
||||||
let session = session["session"].clone();
|
let session = session["session"].clone();
|
||||||
|
|
||||||
let path = settings.local.session_path.as_str();
|
let path = settings.session_path.as_str();
|
||||||
let mut file = File::create(path)?;
|
let mut file = File::create(path)?;
|
||||||
file.write_all(session.as_bytes())?;
|
file.write_all(session.as_bytes())?;
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,10 @@ use tui::{
|
||||||
};
|
};
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
|
use atuin_client::database::Database;
|
||||||
|
use atuin_client::history::History;
|
||||||
|
|
||||||
use crate::command::event::{Event, Events};
|
use crate::command::event::{Event, Events};
|
||||||
use crate::local::database::Database;
|
|
||||||
use crate::local::history::History;
|
|
||||||
|
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::server;
|
use atuin_server::launch;
|
||||||
use crate::settings::Settings;
|
use atuin_server::settings::Settings;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
|
@ -23,13 +23,12 @@ impl Cmd {
|
||||||
pub async fn run(&self, settings: &Settings) -> Result<()> {
|
pub async fn run(&self, settings: &Settings) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Start { host, port } => {
|
Self::Start { host, port } => {
|
||||||
let host = host.as_ref().map_or(
|
let host = host
|
||||||
settings.server.host.clone(),
|
.as_ref()
|
||||||
std::string::ToString::to_string,
|
.map_or(settings.host.clone(), std::string::ToString::to_string);
|
||||||
);
|
let port = port.map_or(settings.port, |p| p);
|
||||||
let port = port.map_or(settings.server.port, |p| p);
|
|
||||||
|
|
||||||
server::launch(settings, host, port).await
|
launch(settings, host, port).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ use cli_table::{format::Justify, print_stdout, Cell, Style, Table};
|
||||||
use eyre::{eyre, Result};
|
use eyre::{eyre, Result};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::local::database::Database;
|
use atuin_client::database::Database;
|
||||||
use crate::local::history::History;
|
use atuin_client::history::History;
|
||||||
use crate::settings::Settings;
|
use atuin_client::settings::Settings;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
|
@ -80,7 +80,7 @@ impl Cmd {
|
||||||
words.join(" ")
|
words.join(" ")
|
||||||
};
|
};
|
||||||
|
|
||||||
let start = match settings.local.dialect.to_lowercase().as_str() {
|
let start = match settings.dialect.to_lowercase().as_str() {
|
||||||
"uk" => parse_date_string(&words, Local::now(), Dialect::Uk)?,
|
"uk" => parse_date_string(&words, Local::now(), Dialect::Uk)?,
|
||||||
_ => parse_date_string(&words, Local::now(), Dialect::Us)?,
|
_ => parse_date_string(&words, Local::now(), Dialect::Us)?,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
|
||||||
use crate::local::database::Database;
|
use atuin_client::database::Database;
|
||||||
use crate::local::sync;
|
use atuin_client::settings::Settings;
|
||||||
use crate::settings::Settings;
|
use atuin_client::sync;
|
||||||
|
|
||||||
pub async fn run(settings: &Settings, force: bool, db: &mut (impl Database + Send)) -> Result<()> {
|
pub async fn run(settings: &Settings, force: bool, db: &mut (impl Database + Send)) -> Result<()> {
|
||||||
sync::sync(settings, force, db).await?;
|
sync::sync(settings, force, db).await?;
|
||||||
|
|
39
src/main.rs
39
src/main.rs
|
@ -1,29 +1,16 @@
|
||||||
#![warn(clippy::pedantic, clippy::nursery)]
|
#![warn(clippy::pedantic, clippy::nursery)]
|
||||||
#![allow(clippy::use_self)] // not 100% reliable
|
#![allow(clippy::use_self)] // not 100% reliable
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use eyre::Result;
|
||||||
|
|
||||||
use eyre::{eyre, Result};
|
|
||||||
use fern::colors::{Color, ColoredLevelConfig};
|
use fern::colors::{Color, ColoredLevelConfig};
|
||||||
use human_panic::setup_panic;
|
|
||||||
use structopt::{clap::AppSettings, StructOpt};
|
use structopt::{clap::AppSettings, StructOpt};
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
use command::AtuinCmd;
|
use command::AtuinCmd;
|
||||||
use local::database::Sqlite;
|
|
||||||
use settings::Settings;
|
|
||||||
|
|
||||||
mod api;
|
|
||||||
mod command;
|
mod command;
|
||||||
mod local;
|
|
||||||
mod server;
|
|
||||||
mod settings;
|
|
||||||
mod utils;
|
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
#[structopt(
|
#[structopt(
|
||||||
|
@ -33,28 +20,13 @@ mod utils;
|
||||||
global_settings(&[AppSettings::ColoredHelp, AppSettings::DeriveDisplayOrder])
|
global_settings(&[AppSettings::ColoredHelp, AppSettings::DeriveDisplayOrder])
|
||||||
)]
|
)]
|
||||||
struct Atuin {
|
struct Atuin {
|
||||||
#[structopt(long, parse(from_os_str), help = "db file path")]
|
|
||||||
db: Option<PathBuf>,
|
|
||||||
|
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
atuin: AtuinCmd,
|
atuin: AtuinCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Atuin {
|
impl Atuin {
|
||||||
async fn run(self, settings: &Settings) -> Result<()> {
|
async fn run(self) -> Result<()> {
|
||||||
let db_path = if let Some(db_path) = self.db {
|
self.atuin.run().await
|
||||||
let path = db_path
|
|
||||||
.to_str()
|
|
||||||
.ok_or_else(|| eyre!("path {:?} was not valid UTF-8", db_path))?;
|
|
||||||
let path = shellexpand::full(path)?;
|
|
||||||
PathBuf::from(path.as_ref())
|
|
||||||
} else {
|
|
||||||
PathBuf::from(settings.local.db_path.as_str())
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut db = Sqlite::new(db_path)?;
|
|
||||||
|
|
||||||
self.atuin.run(&mut db, settings).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +50,5 @@ async fn main() -> Result<()> {
|
||||||
.chain(std::io::stdout())
|
.chain(std::io::stdout())
|
||||||
.apply()?;
|
.apply()?;
|
||||||
|
|
||||||
let settings = Settings::new()?;
|
Atuin::from_args().run().await
|
||||||
setup_panic!();
|
|
||||||
|
|
||||||
Atuin::from_args().run(&settings).await
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue