feat: added basic ui
This commit is contained in:
446
Cargo.lock
generated
446
Cargo.lock
generated
@@ -61,6 +61,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
@@ -126,6 +132,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
@@ -243,6 +255,21 @@ version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "cassowary"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbc"
|
||||
version = "0.1.2"
|
||||
@@ -314,6 +341,20 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"rustversion",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
@@ -344,6 +385,31 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.5.5"
|
||||
@@ -403,6 +469,41 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.8.0"
|
||||
@@ -482,6 +583,12 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.13.8"
|
||||
@@ -538,6 +645,22 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.1"
|
||||
@@ -564,6 +687,18 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
@@ -704,6 +839,23 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
@@ -766,6 +918,18 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
@@ -776,6 +940,19 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instability"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"indoc",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "internal-russh-forked-ssh-key"
|
||||
version = "0.6.10+upstream-0.6.7"
|
||||
@@ -810,6 +987,21 @@ version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.4"
|
||||
@@ -865,12 +1057,48 @@ version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md5"
|
||||
version = "0.7.0"
|
||||
@@ -899,6 +1127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
@@ -973,6 +1202,12 @@ dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
@@ -1048,6 +1283,29 @@ dependencies = [
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
@@ -1059,6 +1317,12 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.12.2"
|
||||
@@ -1197,11 +1461,14 @@ dependencies = [
|
||||
name = "publik"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"env_logger",
|
||||
"log",
|
||||
"ratatui",
|
||||
"russh",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tui-textarea",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1243,6 +1510,43 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"crossterm",
|
||||
"indoc",
|
||||
"instability",
|
||||
"itertools",
|
||||
"lru",
|
||||
"paste",
|
||||
"strum",
|
||||
"termion",
|
||||
"unicode-segmentation",
|
||||
"unicode-truncate",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
@@ -1407,12 +1711,31 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "salsa20"
|
||||
version = "0.10.2"
|
||||
@@ -1422,6 +1745,12 @@ dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "scrypt"
|
||||
version = "0.11.0"
|
||||
@@ -1501,6 +1830,36 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
@@ -1581,6 +1940,40 @@ dependencies = [
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@@ -1598,6 +1991,18 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "4.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f359c854fbecc1ea65bc3683f1dcb2dce78b174a1ca7fda37acd1fff81df6ff"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libredox",
|
||||
"numtoa",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
@@ -1665,6 +2070,18 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tui-textarea"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a5318dd619ed73c52a9417ad19046724effc1287fb75cdcc4eca1d6ac1acbae"
|
||||
dependencies = [
|
||||
"crossterm",
|
||||
"ratatui",
|
||||
"termion",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
@@ -1677,6 +2094,35 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-truncate"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"unicode-segmentation",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
|
||||
@@ -4,8 +4,11 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.97"
|
||||
env_logger = "0.11.7"
|
||||
log = "0.4.26"
|
||||
ratatui = "0.29.0"
|
||||
russh = "0.51.1"
|
||||
thiserror = "2.0.12"
|
||||
tokio = "1.44.1"
|
||||
tui-textarea = { version = "0.7.0", features = ["termion"] }
|
||||
|
||||
308
src/main.rs
308
src/main.rs
@@ -1,54 +1,27 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::format;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use authfile::Entity;
|
||||
// use crossterm::event::{Event, read};
|
||||
use ratatui::backend::CrosstermBackend;
|
||||
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
||||
use ratatui::style::{Color, Style};
|
||||
use ratatui::termion::event::Event;
|
||||
use ratatui::text::Text;
|
||||
use ratatui::widgets::{Block, Borders, Clear, List, Paragraph};
|
||||
use ratatui::{Terminal, TerminalOptions, Viewport};
|
||||
use russh::keys::ssh_key;
|
||||
use russh::keys::{PublicKey, ssh_key::public::KeyData, ssh_key::rand_core::OsRng};
|
||||
use russh::server::{self, Auth, Handle, Msg, Server as _, Session};
|
||||
use russh::{Channel, ChannelId, CryptoVec};
|
||||
use russh::server::{self, Auth, Config, Handle, Handler, Msg, Server, Session};
|
||||
use russh::{Channel, ChannelId, CryptoVec, Pty};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::mpsc::{UnboundedSender, unbounded_channel};
|
||||
use tui_textarea::TextArea;
|
||||
mod authfile;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Debug)
|
||||
.init();
|
||||
|
||||
let mut methods = russh::MethodSet::empty();
|
||||
methods.push(russh::MethodKind::PublicKey);
|
||||
|
||||
let keychain = authfile::read(Path::new("./authfile")).await.unwrap();
|
||||
let key_data_pool = new_atomic(build_key_data_pool(&keychain));
|
||||
let key_data_to_id = new_atomic(HashMap::new());
|
||||
let id_to_user = new_atomic(HashMap::new());
|
||||
let clients = new_atomic(HashMap::new());
|
||||
let key_data_to_user = new_atomic(keychain.iter().map(|e| (e.key_data(), e.clone())).collect());
|
||||
let keychain = new_atomic(keychain);
|
||||
|
||||
let config = russh::server::Config {
|
||||
inactivity_timeout: Some(std::time::Duration::from_secs(3600)),
|
||||
auth_rejection_time: std::time::Duration::from_secs(3),
|
||||
auth_rejection_time_initial: Some(std::time::Duration::from_secs(0)),
|
||||
methods,
|
||||
keys: vec![
|
||||
russh::keys::PrivateKey::random(&mut OsRng, russh::keys::Algorithm::Ed25519).unwrap(),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
let config = Arc::new(config);
|
||||
|
||||
let mut sh = Server {
|
||||
keychain,
|
||||
id_to_user,
|
||||
key_data_to_id,
|
||||
key_data_pool,
|
||||
key_data_to_user,
|
||||
clients,
|
||||
id: 0,
|
||||
};
|
||||
sh.run_on_address(config, ("0.0.0.0", 2222)).await.unwrap();
|
||||
}
|
||||
type SshTerminal = Terminal<CrosstermBackend<TerminalHandle>>;
|
||||
|
||||
fn build_key_data_pool(entities: &[Arc<Entity>]) -> HashSet<KeyData> {
|
||||
entities.iter().map(|e| e.key_data()).collect()
|
||||
@@ -62,6 +35,12 @@ fn new_atomic<T>(object: T) -> Atomic<T> {
|
||||
|
||||
type Atomic<T> = Arc<Mutex<T>>;
|
||||
|
||||
#[derive(Default)]
|
||||
struct App {
|
||||
pub history: Vec<String>,
|
||||
pub counter: usize,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("failed to disconnect client with id {0}")]
|
||||
@@ -75,11 +54,58 @@ pub enum Error {
|
||||
pub struct Client {
|
||||
channel: ChannelId,
|
||||
handle: Handle,
|
||||
terminal: SshTerminal,
|
||||
textarea: TextArea<'static>,
|
||||
// entity: Arc<Entity>,
|
||||
}
|
||||
|
||||
struct TerminalHandle {
|
||||
sender: UnboundedSender<Vec<u8>>,
|
||||
// The sink collects the data which is finally sent to sender.
|
||||
sink: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TerminalHandle {
|
||||
async fn start(handle: Handle, channel_id: ChannelId) -> Self {
|
||||
let (sender, mut receiver) = unbounded_channel::<Vec<u8>>();
|
||||
tokio::spawn(async move {
|
||||
while let Some(data) = receiver.recv().await {
|
||||
let result = handle.data(channel_id, data.into()).await;
|
||||
if result.is_err() {
|
||||
eprintln!("Failed to send data: {:?}", result);
|
||||
}
|
||||
}
|
||||
});
|
||||
Self {
|
||||
sender,
|
||||
sink: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The crossterm backend writes to the terminal handle.
|
||||
impl std::io::Write for TerminalHandle {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.sink.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
let result = self.sender.send(self.sink.clone());
|
||||
if result.is_err() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::BrokenPipe,
|
||||
result.unwrap_err(),
|
||||
));
|
||||
}
|
||||
|
||||
self.sink.clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Server {
|
||||
struct AppServer {
|
||||
keychain: Atomic<Vec<Arc<Entity>>>,
|
||||
key_data_pool: Atomic<HashSet<KeyData>>,
|
||||
key_data_to_user: Atomic<HashMap<KeyData, Arc<Entity>>>,
|
||||
@@ -88,9 +114,32 @@ struct Server {
|
||||
clients: Atomic<HashMap<usize, Client>>,
|
||||
|
||||
id: usize,
|
||||
app: Atomic<App>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
impl AppServer {
|
||||
pub async fn run(&mut self) -> Result<(), anyhow::Error> {
|
||||
let app = self.app.clone();
|
||||
let clients = self.clients.clone();
|
||||
let mut methods = russh::MethodSet::empty();
|
||||
methods.push(russh::MethodKind::PublicKey);
|
||||
|
||||
let config = russh::server::Config {
|
||||
inactivity_timeout: Some(std::time::Duration::from_secs(3600)),
|
||||
auth_rejection_time: std::time::Duration::from_secs(3),
|
||||
auth_rejection_time_initial: Some(std::time::Duration::from_secs(0)),
|
||||
methods,
|
||||
keys: vec![
|
||||
russh::keys::PrivateKey::random(&mut OsRng, russh::keys::Algorithm::Ed25519)
|
||||
.unwrap(),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
self.run_on_address(Arc::new(config), ("0.0.0.0", 2222))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn reload(&mut self) -> Result<(), Error> {
|
||||
let new_keychain = authfile::read(Path::new("./authfile")).await?;
|
||||
let new_key_data_pool = build_key_data_pool(&new_keychain);
|
||||
@@ -156,7 +205,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Server for Server {
|
||||
impl Server for AppServer {
|
||||
type Handler = Self;
|
||||
fn new_client(&mut self, _: Option<std::net::SocketAddr>) -> Self {
|
||||
let s = self.clone();
|
||||
@@ -168,7 +217,7 @@ impl server::Server for Server {
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler for Server {
|
||||
impl Handler for AppServer {
|
||||
type Error = Error;
|
||||
|
||||
async fn channel_open_session(
|
||||
@@ -180,14 +229,25 @@ impl server::Handler for Server {
|
||||
// let entity = self.entity().await;
|
||||
let channel = channel.id();
|
||||
let handle = session.handle();
|
||||
let terminal_handle = TerminalHandle::start(handle.clone(), channel.clone()).await;
|
||||
|
||||
let backend = CrosstermBackend::new(terminal_handle);
|
||||
|
||||
// the correct viewport area will be set when the client request a pty
|
||||
let options = TerminalOptions {
|
||||
viewport: Viewport::Fixed(Rect::default()),
|
||||
};
|
||||
|
||||
let terminal = Terminal::with_options(backend, options).unwrap();
|
||||
|
||||
let mut clients = self.clients.lock().await;
|
||||
clients.insert(
|
||||
self.id,
|
||||
Client {
|
||||
textarea: TextArea::default(),
|
||||
channel,
|
||||
handle,
|
||||
// entity,
|
||||
terminal,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -225,23 +285,134 @@ impl server::Handler for Server {
|
||||
return Err(russh::Error::Disconnect.into());
|
||||
}
|
||||
|
||||
// Alt+Return
|
||||
if data == [27, 13] {
|
||||
let text = self
|
||||
.clients
|
||||
.lock()
|
||||
.await
|
||||
.get_mut(&self.id)
|
||||
.unwrap()
|
||||
.textarea
|
||||
.lines()
|
||||
.to_vec()
|
||||
.join("\n");
|
||||
|
||||
let name = self.entity().await.name().to_string();
|
||||
let message = format!("[{name}]: {text}");
|
||||
self.app.lock().await.history.push(message);
|
||||
}
|
||||
|
||||
if !data.is_empty() {
|
||||
let mut iterator = data.iter().skip(1).map(|d| Ok(*d));
|
||||
let keycode = ratatui::termion::event::parse_event(data[0], &mut iterator).unwrap();
|
||||
self.clients
|
||||
.lock()
|
||||
.await
|
||||
.get_mut(&self.id)
|
||||
.unwrap()
|
||||
.textarea
|
||||
.input(keycode);
|
||||
}
|
||||
|
||||
// Press `r` to reload the authorization file
|
||||
if data == [114] {
|
||||
self.reload().await?;
|
||||
}
|
||||
|
||||
let data = CryptoVec::from(format!(
|
||||
"[{}]: {}\r\n",
|
||||
self.entity().await.name(),
|
||||
String::from_utf8_lossy(data)
|
||||
));
|
||||
self.post(data.clone()).await;
|
||||
session.data(channel, data)?;
|
||||
let clients = self.clients.clone();
|
||||
let history: Vec<String> = self
|
||||
.app
|
||||
.lock()
|
||||
.await
|
||||
.history
|
||||
.iter()
|
||||
.rev()
|
||||
.take(20)
|
||||
.cloned()
|
||||
.collect();
|
||||
tokio::spawn(async move {
|
||||
for (
|
||||
_,
|
||||
Client {
|
||||
terminal, textarea, ..
|
||||
},
|
||||
) in clients.lock().await.iter_mut()
|
||||
{
|
||||
terminal
|
||||
.draw(|f| {
|
||||
// clear the screen
|
||||
let area = f.area();
|
||||
f.render_widget(Clear, area);
|
||||
|
||||
// split vertically as 80-20
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(vec![
|
||||
Constraint::Percentage(80),
|
||||
Constraint::Percentage(20),
|
||||
])
|
||||
.split(f.area());
|
||||
let style = Style::default().fg(Color::Green);
|
||||
|
||||
let paragraphs: Vec<_> = history
|
||||
.iter()
|
||||
.map(|message| Text::styled(message.to_string(), style))
|
||||
.collect();
|
||||
|
||||
let paragraphs =
|
||||
List::new(paragraphs).block(Block::bordered().title("chat"));
|
||||
|
||||
// let block = Block::default()
|
||||
// .title("chat")
|
||||
// .borders(Borders::ALL);
|
||||
|
||||
// f.render_widget(paragraph.block(block), area);
|
||||
f.render_widget(paragraphs, layout[0]);
|
||||
f.render_widget(&*textarea, layout[1]);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
// let data = CryptoVec::from(format!(
|
||||
// "[{}]: {}\r\n",
|
||||
// self.entity().await.name(),
|
||||
// String::from_utf8_lossy(data)
|
||||
// ));
|
||||
// self.post(data.clone()).await;
|
||||
// session.data(channel, data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn pty_request(
|
||||
&mut self,
|
||||
channel: ChannelId,
|
||||
_: &str,
|
||||
col_width: u32,
|
||||
row_height: u32,
|
||||
_: u32,
|
||||
_: u32,
|
||||
_: &[(Pty, u32)],
|
||||
session: &mut Session,
|
||||
) -> Result<(), Self::Error> {
|
||||
let rect = Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: col_width as u16,
|
||||
height: row_height as u16,
|
||||
};
|
||||
|
||||
let mut clients = self.clients.lock().await;
|
||||
let client = clients.get_mut(&self.id).unwrap();
|
||||
client.terminal.resize(rect).unwrap();
|
||||
|
||||
session.channel_success(channel)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Server {
|
||||
impl Drop for AppServer {
|
||||
fn drop(&mut self) {
|
||||
let id = self.id;
|
||||
let clients = self.clients.clone();
|
||||
@@ -251,3 +422,30 @@ impl Drop for Server {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Debug)
|
||||
.init();
|
||||
|
||||
let keychain = authfile::read(Path::new("./authfile")).await.unwrap();
|
||||
let key_data_pool = new_atomic(build_key_data_pool(&keychain));
|
||||
let key_data_to_id = new_atomic(HashMap::new());
|
||||
let id_to_user = new_atomic(HashMap::new());
|
||||
let clients = new_atomic(HashMap::new());
|
||||
let key_data_to_user = new_atomic(keychain.iter().map(|e| (e.key_data(), e.clone())).collect());
|
||||
let keychain = new_atomic(keychain);
|
||||
|
||||
let mut sh = AppServer {
|
||||
app: new_atomic(App::default()),
|
||||
keychain,
|
||||
id_to_user,
|
||||
key_data_to_id,
|
||||
key_data_pool,
|
||||
key_data_to_user,
|
||||
clients,
|
||||
id: 0,
|
||||
};
|
||||
sh.run().await.unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user