Implement server (#23)
* Add initial database and server setup * Set up all routes, auth, etc * Implement sessions, password auth, hashing with argon2, and history storage
This commit is contained in:
parent
716c7722cd
commit
c9579cb9ca
24 changed files with 980 additions and 55 deletions
1
.env
Normal file
1
.env
Normal file
|
@ -0,0 +1 @@
|
|||
DATABASE_URL=postgres://atuin:aeBafah6AiJu@localhost/atuin
|
466
Cargo.lock
generated
466
Cargo.lock
generated
|
@ -78,7 +78,7 @@ version = "0.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -101,7 +101,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
|||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -112,18 +112,23 @@ dependencies = [
|
|||
"chrono-english",
|
||||
"cli-table",
|
||||
"config",
|
||||
"diesel",
|
||||
"diesel_migrations",
|
||||
"directories",
|
||||
"dotenv",
|
||||
"eyre",
|
||||
"fern",
|
||||
"hostname",
|
||||
"indicatif",
|
||||
"itertools",
|
||||
"log 0.4.14",
|
||||
"pretty_env_logger",
|
||||
"rocket",
|
||||
"rocket_contrib",
|
||||
"rusqlite",
|
||||
"serde 1.0.124",
|
||||
"serde_derive",
|
||||
"shellexpand",
|
||||
"sodiumoxide",
|
||||
"structopt",
|
||||
"termion",
|
||||
"tui",
|
||||
|
@ -252,7 +257,7 @@ dependencies = [
|
|||
"num-integer",
|
||||
"num-traits 0.2.14",
|
||||
"time",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -331,7 +336,7 @@ dependencies = [
|
|||
"regex",
|
||||
"terminal_size",
|
||||
"unicode-width",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -409,6 +414,41 @@ dependencies = [
|
|||
"syn 0.15.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "047bfc4d5c3bd2ef6ca6f981941046113524b9a9f9a7cbdfdd7ff40f58e6f542"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"diesel_derives",
|
||||
"pq-sys",
|
||||
"r2d2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel_derives"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel_migrations"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c"
|
||||
dependencies = [
|
||||
"migrations_internals",
|
||||
"migrations_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
|
@ -445,7 +485,7 @@ checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"redox_users 0.3.5",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -456,9 +496,15 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"redox_users 0.4.0",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenv"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
|
@ -471,19 +517,6 @@ version = "0.3.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log 0.4.14",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.5"
|
||||
|
@ -512,6 +545,62 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fern"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065"
|
||||
dependencies = [
|
||||
"log 0.4.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.2.4",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fsevent-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fuchsia-zircon-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon-sys"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
|
@ -622,7 +711,7 @@ checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"match_cfg",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -631,15 +720,6 @@ version = "1.3.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.10.16"
|
||||
|
@ -698,6 +778,44 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"inotify-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify-sys"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iovec"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.0"
|
||||
|
@ -713,6 +831,16 @@ version = "0.4.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.2.2"
|
||||
|
@ -725,6 +853,12 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.7.5"
|
||||
|
@ -744,6 +878,17 @@ version = "0.2.86"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
||||
|
||||
[[package]]
|
||||
name = "libsodium-sys"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a685b64f837b339074115f2e7f7b431ac73681d08d75b389db7498b8892b8a58"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.20.1"
|
||||
|
@ -771,6 +916,15 @@ version = "0.5.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.9"
|
||||
|
@ -807,6 +961,27 @@ version = "2.3.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "migrations_internals"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "migrations_macros"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c"
|
||||
dependencies = [
|
||||
"migrations_internals",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.2.6"
|
||||
|
@ -816,6 +991,60 @@ dependencies = [
|
|||
"log 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"fuchsia-zircon",
|
||||
"fuchsia-zircon-sys",
|
||||
"iovec",
|
||||
"kernel32-sys",
|
||||
"libc",
|
||||
"log 0.4.14",
|
||||
"miow",
|
||||
"net2",
|
||||
"slab",
|
||||
"winapi 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio-extras"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
|
||||
dependencies = [
|
||||
"lazycell",
|
||||
"log 0.4.14",
|
||||
"mio",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
|
||||
dependencies = [
|
||||
"kernel32-sys",
|
||||
"net2",
|
||||
"winapi 0.2.8",
|
||||
"ws2_32-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
|
@ -827,6 +1056,24 @@ dependencies = [
|
|||
"version_check 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "4.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"filetime",
|
||||
"fsevent",
|
||||
"fsevent-sys",
|
||||
"inotify",
|
||||
"libc",
|
||||
"mio",
|
||||
"mio-extras",
|
||||
"walkdir",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
|
@ -889,6 +1136,31 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall 0.2.4",
|
||||
"smallvec",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pear"
|
||||
version = "0.1.4"
|
||||
|
@ -946,13 +1218,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
name = "pq-sys"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"log 0.4.14",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -997,12 +1268,6 @@ dependencies = [
|
|||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.13"
|
||||
|
@ -1021,6 +1286,17 @@ dependencies = [
|
|||
"proc-macro2 1.0.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r2d2"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
|
||||
dependencies = [
|
||||
"log 0.4.14",
|
||||
"parking_lot",
|
||||
"scheduled-thread-pool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
|
@ -1161,6 +1437,34 @@ dependencies = [
|
|||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket_contrib"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7954a707f9ca18aa74ca8c1f5d1f900f52a4dceb68e96e3112143f759cfd20e"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
"log 0.4.14",
|
||||
"notify",
|
||||
"r2d2",
|
||||
"rocket",
|
||||
"rocket_contrib_codegen",
|
||||
"serde 1.0.124",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket_contrib_codegen"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30deb6dec53b91fac3538a2a3935cf13e0f462745f9f33bf27bedffbe7265b5d"
|
||||
dependencies = [
|
||||
"devise",
|
||||
"quote 0.6.13",
|
||||
"version_check 0.9.2",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket_http"
|
||||
version = "0.4.7"
|
||||
|
@ -1223,12 +1527,36 @@ version = "0.3.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scanlex"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "088c5d71572124929ea7549a8ce98e1a6fd33d0a38367b09027b382e67c033db"
|
||||
|
||||
[[package]]
|
||||
name = "scheduled-thread-pool"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
|
||||
dependencies = [
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "0.8.23"
|
||||
|
@ -1306,12 +1634,29 @@ dependencies = [
|
|||
"dirs-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "sodiumoxide"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7038b67c941e23501573cb7242ffb08709abe9b11eb74bceff875bbda024a6a8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libsodium-sys",
|
||||
"serde 1.0.124",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "state"
|
||||
version = "0.4.2"
|
||||
|
@ -1404,7 +1749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1444,7 +1789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1616,6 +1961,17 @@ version = "0.9.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi 0.3.9",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
|
@ -1628,6 +1984,12 @@ version = "0.10.2+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -1638,6 +2000,12 @@ dependencies = [
|
|||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
@ -1650,7 +2018,7 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1659,6 +2027,16 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -8,7 +8,7 @@ description = "atuin - magical shell history"
|
|||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.4"
|
||||
fern = "0.6.0"
|
||||
chrono = "0.4"
|
||||
eyre = "0.6"
|
||||
shellexpand = "2"
|
||||
|
@ -27,7 +27,16 @@ tui = "0.14"
|
|||
termion = "1.5"
|
||||
unicode-width = "0.1"
|
||||
itertools = "0.10.0"
|
||||
diesel = { version = "1.4.4", features = ["postgres", "chrono"] }
|
||||
diesel_migrations = "1.4.0"
|
||||
dotenv = "0.15.0"
|
||||
sodiumoxide = "0.2.6"
|
||||
|
||||
[dependencies.rusqlite]
|
||||
version = "0.24"
|
||||
features = ["bundled"]
|
||||
|
||||
[dependencies.rocket_contrib]
|
||||
version = "0.4.7"
|
||||
default-features = false
|
||||
features = ["diesel_postgres_pool", "json"]
|
||||
|
|
5
diesel.toml
Normal file
5
diesel.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
# For documentation on how to configure this file,
|
||||
# see diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/schema.rs"
|
0
migrations/.gitkeep
Normal file
0
migrations/.gitkeep
Normal file
6
migrations/00000000000000_diesel_initial_setup/down.sql
Normal file
6
migrations/00000000000000_diesel_initial_setup/down.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
||||
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
36
migrations/00000000000000_diesel_initial_setup/up.sql
Normal file
36
migrations/00000000000000_diesel_initial_setup/up.sql
Normal file
|
@ -0,0 +1,36 @@
|
|||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
|
||||
|
||||
|
||||
-- Sets up a trigger for the given table to automatically set a column called
|
||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
||||
-- in the modified columns)
|
||||
--
|
||||
-- # Example
|
||||
--
|
||||
-- ```sql
|
||||
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
||||
--
|
||||
-- SELECT diesel_manage_updated_at('users');
|
||||
-- ```
|
||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
||||
BEGIN
|
||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
IF (
|
||||
NEW IS DISTINCT FROM OLD AND
|
||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
||||
) THEN
|
||||
NEW.updated_at := current_timestamp;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
2
migrations/2021-03-20-151809_create_history/down.sql
Normal file
2
migrations/2021-03-20-151809_create_history/down.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table history;
|
11
migrations/2021-03-20-151809_create_history/up.sql
Normal file
11
migrations/2021-03-20-151809_create_history/up.sql
Normal file
|
@ -0,0 +1,11 @@
|
|||
-- Your SQL goes here
|
||||
-- lower case SQL please, this isn't a shouting match
|
||||
create table history (
|
||||
id bigserial primary key,
|
||||
client_id text not null unique, -- the client-generated ID
|
||||
user_id bigserial not null, -- allow multiple users
|
||||
mac varchar(128) not null, -- store a hashed mac address, to identify machines - more likely to be unique than hostname
|
||||
timestamp timestamp not null, -- one of the few non-encrypted metadatas
|
||||
|
||||
data varchar(8192) not null -- store the actual history data, encrypted. I don't wanna know!
|
||||
);
|
2
migrations/2021-03-20-171007_create_users/down.sql
Normal file
2
migrations/2021-03-20-171007_create_users/down.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table users;
|
6
migrations/2021-03-20-171007_create_users/up.sql
Normal file
6
migrations/2021-03-20-171007_create_users/up.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
-- Your SQL goes here
|
||||
create table users (
|
||||
id bigserial primary key, -- also store our own ID
|
||||
email varchar(128) not null unique, -- being able to contact users is useful
|
||||
password varchar(128) not null unique
|
||||
);
|
2
migrations/2021-03-21-181750_create_sessions/down.sql
Normal file
2
migrations/2021-03-21-181750_create_sessions/down.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table sessions;
|
6
migrations/2021-03-21-181750_create_sessions/up.sql
Normal file
6
migrations/2021-03-21-181750_create_sessions/up.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
-- Your SQL goes here
|
||||
create table sessions (
|
||||
id bigserial primary key,
|
||||
user_id bigserial,
|
||||
token varchar(128) unique not null
|
||||
);
|
|
@ -49,7 +49,7 @@ impl AtuinCmd {
|
|||
match self {
|
||||
Self::History(history) => history.run(db),
|
||||
Self::Import(import) => import.run(db),
|
||||
Self::Server(server) => server.run(),
|
||||
Self::Server(server) => server.run(settings),
|
||||
Self::Stats(stats) => stats.run(db, settings),
|
||||
Self::Init => init::init(),
|
||||
Self::Search { query } => search::run(&query, db),
|
||||
|
|
|
@ -2,6 +2,7 @@ use eyre::Result;
|
|||
use structopt::StructOpt;
|
||||
|
||||
use crate::remote::server;
|
||||
use crate::settings::Settings;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub enum Cmd {
|
||||
|
@ -10,8 +11,8 @@ pub enum Cmd {
|
|||
|
||||
#[allow(clippy::unused_self)] // I'll use it later
|
||||
impl Cmd {
|
||||
pub fn run(&self) -> Result<()> {
|
||||
server::launch();
|
||||
pub fn run(&self, settings: &Settings) -> Result<()> {
|
||||
server::launch(settings);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -17,6 +17,15 @@ extern crate rocket;
|
|||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel_migrations;
|
||||
|
||||
#[macro_use]
|
||||
extern crate rocket_contrib;
|
||||
|
||||
use command::AtuinCmd;
|
||||
use local::database::Sqlite;
|
||||
use settings::Settings;
|
||||
|
@ -26,6 +35,8 @@ mod local;
|
|||
mod remote;
|
||||
mod settings;
|
||||
|
||||
pub mod schema;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
author = "Ellie Huxtable <e@elm.sh>",
|
||||
|
@ -61,7 +72,18 @@ impl Atuin {
|
|||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
pretty_env_logger::init();
|
||||
fern::Dispatch::new()
|
||||
.format(|out, message, record| {
|
||||
out.finish(format_args!(
|
||||
"{} [{}] {}",
|
||||
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
|
||||
record.level(),
|
||||
message
|
||||
))
|
||||
})
|
||||
.level(log::LevelFilter::Info)
|
||||
.chain(std::io::stdout())
|
||||
.apply()?;
|
||||
|
||||
Atuin::from_args().run()
|
||||
}
|
||||
|
|
200
src/remote/auth.rs
Normal file
200
src/remote/auth.rs
Normal file
|
@ -0,0 +1,200 @@
|
|||
use self::diesel::prelude::*;
|
||||
use rocket::http::Status;
|
||||
use rocket::request::{self, FromRequest, Outcome, Request};
|
||||
use rocket_contrib::databases::diesel;
|
||||
use sodiumoxide::crypto::pwhash::argon2id13;
|
||||
|
||||
use rocket_contrib::json::Json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::models::{NewSession, NewUser, Session, User};
|
||||
use super::views::ApiResponse;
|
||||
use crate::schema::{sessions, users};
|
||||
|
||||
use super::database::AtuinDbConn;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum KeyError {
|
||||
Missing,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
pub fn hash_str(secret: &str) -> String {
|
||||
sodiumoxide::init().unwrap();
|
||||
let hash = argon2id13::pwhash(
|
||||
secret.as_bytes(),
|
||||
argon2id13::OPSLIMIT_INTERACTIVE,
|
||||
argon2id13::MEMLIMIT_INTERACTIVE,
|
||||
)
|
||||
.unwrap();
|
||||
let texthash = std::str::from_utf8(&hash.0).unwrap().to_string();
|
||||
|
||||
// postgres hates null chars. don't do that to postgres
|
||||
texthash.trim_end_matches('\u{0}').to_string()
|
||||
}
|
||||
|
||||
pub fn verify_str(secret: &str, verify: &str) -> bool {
|
||||
sodiumoxide::init().unwrap();
|
||||
|
||||
let mut padded = [0_u8; 128];
|
||||
secret.as_bytes().iter().enumerate().for_each(|(i, val)| {
|
||||
padded[i] = *val;
|
||||
});
|
||||
|
||||
match argon2id13::HashedPassword::from_slice(&padded) {
|
||||
Some(hp) => argon2id13::pwhash_verify(&hp, verify.as_bytes()),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for User {
|
||||
type Error = KeyError;
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<User, Self::Error> {
|
||||
let session: Vec<_> = request.headers().get("authorization").collect();
|
||||
|
||||
if session.is_empty() {
|
||||
return Outcome::Failure((Status::BadRequest, KeyError::Missing));
|
||||
} else if session.len() > 1 {
|
||||
return Outcome::Failure((Status::BadRequest, KeyError::Invalid));
|
||||
}
|
||||
|
||||
let session: Vec<_> = session[0].split(' ').collect();
|
||||
|
||||
if session.len() != 2 {
|
||||
return Outcome::Failure((Status::BadRequest, KeyError::Invalid));
|
||||
}
|
||||
|
||||
if session[0] != "Token" {
|
||||
return Outcome::Failure((Status::BadRequest, KeyError::Invalid));
|
||||
}
|
||||
|
||||
let session = session[1];
|
||||
|
||||
let db = request
|
||||
.guard::<AtuinDbConn>()
|
||||
.succeeded()
|
||||
.expect("failed to load database");
|
||||
|
||||
let session = sessions::table
|
||||
.filter(sessions::token.eq(session))
|
||||
.first::<Session>(&*db);
|
||||
|
||||
if session.is_err() {
|
||||
return Outcome::Failure((Status::Unauthorized, KeyError::Invalid));
|
||||
}
|
||||
|
||||
let session = session.unwrap();
|
||||
|
||||
let user = users::table.find(session.user_id).first(&*db);
|
||||
|
||||
match user {
|
||||
Ok(user) => Outcome::Success(user),
|
||||
Err(_) => Outcome::Failure((Status::Unauthorized, KeyError::Invalid)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Register {
|
||||
email: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[post("/register", data = "<register>")]
|
||||
#[allow(clippy::clippy::needless_pass_by_value)]
|
||||
pub fn register(conn: AtuinDbConn, register: Json<Register>) -> ApiResponse {
|
||||
let hashed = hash_str(register.password.as_str());
|
||||
|
||||
let new_user = NewUser {
|
||||
email: register.email.as_str(),
|
||||
password: hashed.as_str(),
|
||||
};
|
||||
|
||||
let user = diesel::insert_into(users::table)
|
||||
.values(&new_user)
|
||||
.get_result(&*conn);
|
||||
|
||||
if user.is_err() {
|
||||
return ApiResponse {
|
||||
status: Status::BadRequest,
|
||||
json: json!({
|
||||
"status": "error",
|
||||
"message": "failed to create user - is the email already in use?",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
let user: User = user.unwrap();
|
||||
let token = Uuid::new_v4().to_simple().to_string();
|
||||
|
||||
let new_session = NewSession {
|
||||
user_id: user.id,
|
||||
token: token.as_str(),
|
||||
};
|
||||
|
||||
match diesel::insert_into(sessions::table)
|
||||
.values(&new_session)
|
||||
.execute(&*conn)
|
||||
{
|
||||
Ok(_) => ApiResponse {
|
||||
status: Status::Ok,
|
||||
json: json!({"status": "ok", "message": "user created!", "session": token}),
|
||||
},
|
||||
Err(_) => ApiResponse {
|
||||
status: Status::BadRequest,
|
||||
json: json!({"status": "error", "message": "failed to create user"}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Login {
|
||||
email: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[post("/login", data = "<login>")]
|
||||
#[allow(clippy::clippy::needless_pass_by_value)]
|
||||
pub fn login(conn: AtuinDbConn, login: Json<Login>) -> ApiResponse {
|
||||
let user = users::table
|
||||
.filter(users::email.eq(login.email.as_str()))
|
||||
.first(&*conn);
|
||||
|
||||
if user.is_err() {
|
||||
return ApiResponse {
|
||||
status: Status::NotFound,
|
||||
json: json!({"status": "error", "message": "user not found"}),
|
||||
};
|
||||
}
|
||||
|
||||
let user: User = user.unwrap();
|
||||
|
||||
let session = sessions::table
|
||||
.filter(sessions::user_id.eq(user.id))
|
||||
.first(&*conn);
|
||||
|
||||
// a session should exist...
|
||||
if session.is_err() {
|
||||
return ApiResponse {
|
||||
status: Status::InternalServerError,
|
||||
json: json!({"status": "error", "message": "something went wrong"}),
|
||||
};
|
||||
}
|
||||
|
||||
let verified = verify_str(user.password.as_str(), login.password.as_str());
|
||||
|
||||
if !verified {
|
||||
return ApiResponse {
|
||||
status: Status::NotFound,
|
||||
json: json!({"status": "error", "message": "user not found"}),
|
||||
};
|
||||
}
|
||||
|
||||
let session: Session = session.unwrap();
|
||||
|
||||
ApiResponse {
|
||||
status: Status::Ok,
|
||||
json: json!({"status": "ok", "token": session.token}),
|
||||
}
|
||||
}
|
14
src/remote/database.rs
Normal file
14
src/remote/database.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use diesel::pg::PgConnection;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::settings::Settings;
|
||||
|
||||
#[database("atuin")]
|
||||
pub struct AtuinDbConn(diesel::PgConnection);
|
||||
|
||||
// TODO: connection pooling
|
||||
pub fn establish_connection(settings: &Settings) -> PgConnection {
|
||||
let database_url = &settings.remote.db.url;
|
||||
PgConnection::establish(database_url)
|
||||
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
|
||||
}
|
|
@ -1 +1,5 @@
|
|||
pub mod auth;
|
||||
pub mod database;
|
||||
pub mod models;
|
||||
pub mod server;
|
||||
pub mod views;
|
||||
|
|
56
src/remote/models.rs
Normal file
56
src/remote/models.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use chrono::naive::NaiveDateTime;
|
||||
|
||||
use crate::schema::{history, sessions, users};
|
||||
|
||||
#[derive(Identifiable, Queryable, Associations)]
|
||||
#[table_name = "history"]
|
||||
#[belongs_to(User)]
|
||||
pub struct History {
|
||||
pub id: i64,
|
||||
pub client_id: String,
|
||||
pub user_id: i64,
|
||||
pub mac: String,
|
||||
pub timestamp: NaiveDateTime,
|
||||
|
||||
pub data: String,
|
||||
}
|
||||
|
||||
#[derive(Identifiable, Queryable, Associations)]
|
||||
pub struct User {
|
||||
pub id: i64,
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Identifiable, Associations)]
|
||||
#[belongs_to(User)]
|
||||
pub struct Session {
|
||||
pub id: i64,
|
||||
pub user_id: i64,
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[table_name = "history"]
|
||||
pub struct NewHistory<'a> {
|
||||
pub client_id: &'a str,
|
||||
pub user_id: i64,
|
||||
pub mac: &'a str,
|
||||
pub timestamp: NaiveDateTime,
|
||||
|
||||
pub data: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[table_name = "users"]
|
||||
pub struct NewUser<'a> {
|
||||
pub email: &'a str,
|
||||
pub password: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[table_name = "sessions"]
|
||||
pub struct NewSession<'a> {
|
||||
pub user_id: i64,
|
||||
pub token: &'a str,
|
||||
}
|
|
@ -1,8 +1,42 @@
|
|||
#[get("/")]
|
||||
const fn index() -> &'static str {
|
||||
"Hello, world!"
|
||||
}
|
||||
use rocket::config::{Config, Environment, LoggingLevel, Value};
|
||||
|
||||
pub fn launch() {
|
||||
rocket::ignite().mount("/", routes![index]).launch();
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::remote::database::establish_connection;
|
||||
use crate::settings::Settings;
|
||||
|
||||
use super::database::AtuinDbConn;
|
||||
|
||||
// a bunch of these imports are generated by macros, it's easier to wildcard
|
||||
#[allow(clippy::clippy::wildcard_imports)]
|
||||
use super::views::*;
|
||||
|
||||
#[allow(clippy::clippy::wildcard_imports)]
|
||||
use super::auth::*;
|
||||
|
||||
embed_migrations!("migrations");
|
||||
|
||||
pub fn launch(settings: &Settings) {
|
||||
let mut database_config = HashMap::new();
|
||||
let mut databases = HashMap::new();
|
||||
|
||||
database_config.insert("url", Value::from(settings.remote.db.url.clone()));
|
||||
databases.insert("atuin", Value::from(database_config));
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
embedded_migrations::run(&connection).expect("failed to run migrations");
|
||||
|
||||
let config = Config::build(Environment::Production)
|
||||
.address("0.0.0.0")
|
||||
.log_level(LoggingLevel::Normal)
|
||||
.port(8080)
|
||||
.extra("databases", databases)
|
||||
.finalize()
|
||||
.unwrap();
|
||||
|
||||
let app = rocket::custom(config);
|
||||
app.mount("/", routes![index, register, add_history, login])
|
||||
.attach(AtuinDbConn::fairing())
|
||||
.register(catchers![internal_error, bad_request])
|
||||
.launch();
|
||||
}
|
||||
|
|
89
src/remote/views.rs
Normal file
89
src/remote/views.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use self::diesel::prelude::*;
|
||||
use rocket::http::{ContentType, Status};
|
||||
use rocket::request::Request;
|
||||
use rocket::response;
|
||||
use rocket::response::{Responder, Response};
|
||||
use rocket_contrib::databases::diesel;
|
||||
use rocket_contrib::json::{Json, JsonValue};
|
||||
|
||||
use super::database::AtuinDbConn;
|
||||
use super::models::{NewHistory, User};
|
||||
use crate::schema::history;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ApiResponse {
|
||||
pub json: JsonValue,
|
||||
pub status: Status,
|
||||
}
|
||||
|
||||
impl<'r> Responder<'r> for ApiResponse {
|
||||
fn respond_to(self, req: &Request) -> response::Result<'r> {
|
||||
Response::build_from(self.json.respond_to(req).unwrap())
|
||||
.status(self.status)
|
||||
.header(ContentType::JSON)
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
pub const fn index() -> &'static str {
|
||||
"\"Through the fathomless deeps of space swims the star turtle Great A\u{2019}Tuin, bearing on its back the four giant elephants who carry on their shoulders the mass of the Discworld.\"\n\t-- Sir Terry Pratchett"
|
||||
}
|
||||
|
||||
#[catch(500)]
|
||||
pub fn internal_error(_req: &Request) -> ApiResponse {
|
||||
ApiResponse {
|
||||
status: Status::InternalServerError,
|
||||
json: json!({"status": "error", "message": "an internal server error has occured"}),
|
||||
}
|
||||
}
|
||||
|
||||
#[catch(400)]
|
||||
pub fn bad_request(_req: &Request) -> ApiResponse {
|
||||
ApiResponse {
|
||||
status: Status::InternalServerError,
|
||||
json: json!({"status": "error", "message": "bad request. don't do that."}),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AddHistory {
|
||||
id: String,
|
||||
timestamp: i64,
|
||||
data: String,
|
||||
mac: String,
|
||||
}
|
||||
|
||||
#[post("/history", data = "<add_history>")]
|
||||
#[allow(
|
||||
clippy::clippy::cast_sign_loss,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::clippy::needless_pass_by_value
|
||||
)]
|
||||
pub fn add_history(conn: AtuinDbConn, user: User, add_history: Json<AddHistory>) -> ApiResponse {
|
||||
let secs: i64 = add_history.timestamp / 1_000_000_000;
|
||||
let nanosecs: u32 = (add_history.timestamp - (secs * 1_000_000_000)) as u32;
|
||||
let datetime = chrono::NaiveDateTime::from_timestamp(secs, nanosecs);
|
||||
|
||||
let new_history = NewHistory {
|
||||
client_id: add_history.id.as_str(),
|
||||
user_id: user.id,
|
||||
mac: add_history.mac.as_str(),
|
||||
timestamp: datetime,
|
||||
data: add_history.data.as_str(),
|
||||
};
|
||||
|
||||
match diesel::insert_into(history::table)
|
||||
.values(&new_history)
|
||||
.execute(&*conn)
|
||||
{
|
||||
Ok(_) => ApiResponse {
|
||||
status: Status::Ok,
|
||||
json: json!({"status": "ok", "message": "history added", "id": new_history.client_id}),
|
||||
},
|
||||
Err(_) => ApiResponse {
|
||||
status: Status::BadRequest,
|
||||
json: json!({"status": "error", "message": "failed to add history"}),
|
||||
},
|
||||
}
|
||||
}
|
28
src/schema.rs
Normal file
28
src/schema.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
table! {
|
||||
history (id) {
|
||||
id -> Int8,
|
||||
client_id -> Text,
|
||||
user_id -> Int8,
|
||||
mac -> Varchar,
|
||||
timestamp -> Timestamp,
|
||||
data -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
sessions (id) {
|
||||
id -> Int8,
|
||||
user_id -> Int8,
|
||||
token -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
users (id) {
|
||||
id -> Int8,
|
||||
email -> Varchar,
|
||||
password -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
allow_tables_to_appear_in_same_query!(history, sessions, users,);
|
|
@ -10,6 +10,11 @@ pub struct LocalDatabase {
|
|||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct RemoteDatabase {
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Local {
|
||||
pub server_address: String,
|
||||
|
@ -17,9 +22,15 @@ pub struct Local {
|
|||
pub db: LocalDatabase,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Remote {
|
||||
pub db: RemoteDatabase,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Settings {
|
||||
pub local: Local,
|
||||
pub remote: Remote,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
|
@ -49,6 +60,8 @@ impl Settings {
|
|||
s.set_default("local.dialect", "us")?;
|
||||
s.set_default("local.db.path", db_path.to_str())?;
|
||||
|
||||
s.set_default("remote.db.url", "please set a postgres url")?;
|
||||
|
||||
if config_file.exists() {
|
||||
s.merge(File::with_name(config_file.to_str().unwrap()))?;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue