initial impl
This commit is contained in:
parent
7bece578c0
commit
f6dd056503
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,3 +1,7 @@
|
||||
/result
|
||||
/.direnv
|
||||
/target
|
||||
/cert.pem
|
||||
/key.pem
|
||||
/.env
|
||||
/tokens.txt
|
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"rust-analyzer.diagnostics.experimental.enable": true,
|
||||
"rust-analyzer.typing.autoClosingAngleBrackets.enable": true,
|
||||
"rust-analyzer.server.path": "rust-analyzer"
|
||||
}
|
604
Cargo.lock
generated
604
Cargo.lock
generated
@ -8,6 +8,18 @@ version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.68"
|
||||
@ -19,6 +31,20 @@ dependencies = [
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-tungstenite"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6d2c69d237cf761215175f390021344f5530101cca8164d69878b8a1779e80c"
|
||||
dependencies = [
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tungstenite 0.19.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@ -27,15 +53,17 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.6.15"
|
||||
version = "0.6.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b32c5ea3aabaf4deb5f5ced2d688ec0844c881c9e6c696a8b769a05fc691e62"
|
||||
checksum = "113713495a32dd0ab52baf5c10044725aa3aec00b31beda84218e469029b72a3"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"base64 0.21.0",
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"headers",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
@ -50,8 +78,10 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sha1",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
@ -94,6 +124,12 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
@ -106,12 +142,38 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"constant_time_eq 0.2.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.4.0"
|
||||
@ -130,6 +192,62 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
@ -151,6 +269,21 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.28"
|
||||
@ -158,6 +291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -166,6 +300,34 @@ version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.28"
|
||||
@ -184,10 +346,37 @@ version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -215,6 +404,31 @@ version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"headers-core",
|
||||
"http",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers-core"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
|
||||
dependencies = [
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
@ -246,6 +460,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
@ -282,6 +502,16 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@ -307,6 +537,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.141"
|
||||
@ -322,6 +558,15 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.0"
|
||||
@ -356,10 +601,33 @@ dependencies = [
|
||||
name = "musikquadrupled"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-tungstenite",
|
||||
"axum",
|
||||
"axum-server",
|
||||
"base64 0.21.0",
|
||||
"dotenvy",
|
||||
"futures",
|
||||
"http",
|
||||
"hyper",
|
||||
"rand",
|
||||
"rust-argon2",
|
||||
"scc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -378,6 +646,12 @@ version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
@ -416,6 +690,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
@ -434,6 +714,60 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
|
||||
dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
@ -449,6 +783,18 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50162d19404029c1ceca6f6980fe40d45c8b369f6f44446fa14bb39573b5bb9"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq 0.1.5",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.8"
|
||||
@ -467,7 +813,7 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -482,6 +828,12 @@ version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "scc"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d9364e52847b408f401d7100885c74b4b26521cb1a5357f6b6ddbef7464aa5b"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
@ -497,6 +849,20 @@ name = "serde"
|
||||
version = "1.0.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
@ -530,6 +896,26 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.8"
|
||||
@ -539,6 +925,12 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.9"
|
||||
@ -583,6 +975,51 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.27.0"
|
||||
@ -622,6 +1059,18 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"tokio",
|
||||
"tungstenite 0.18.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.7"
|
||||
@ -652,6 +1101,25 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-range-header",
|
||||
"pin-project-lite",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.2"
|
||||
@ -673,9 +1141,21 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.30"
|
||||
@ -683,6 +1163,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -691,18 +1201,106 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
|
15
Cargo.toml
15
Cargo.toml
@ -4,7 +4,20 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
axum = "0.6"
|
||||
axum = {version = "0.6", features = ["ws", "headers"]}
|
||||
axum-server = {version = "0.4", features = ["tls-rustls"]}
|
||||
tokio = {version = "1", features = ["rt-multi-thread"]}
|
||||
dotenvy = "0.15"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = {version = "0.3", features = ["env-filter"]}
|
||||
tower-http = {version = "0.4", features = ["trace", "cors"]}
|
||||
hyper = {version = "0.14", features = ["client"]}
|
||||
http = "0.2"
|
||||
async-tungstenite = {version = "0.21", features = ["tokio-runtime"]}
|
||||
futures = {version = "0.3"}
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
serde_json = "1"
|
||||
rust-argon2 = "1.0"
|
||||
rand = "0.8"
|
||||
scc = "1"
|
||||
base64 = "0.21"
|
12
flake.nix
12
flake.nix
@ -27,6 +27,18 @@
|
||||
};
|
||||
devShells.default = crateOutputs.devShell.overrideAttrs (old: {
|
||||
RUST_SRC_PATH = "${config.nci.toolchains.shell}/lib/rustlib/src/rust/library";
|
||||
packages = (old.packages or []) ++ [
|
||||
pkgs.rust-analyzer
|
||||
(pkgs.writeShellApplication {
|
||||
name = "generate-cert";
|
||||
runtimeInputs = with pkgs; [mkcert coreutils];
|
||||
text = ''
|
||||
mkcert localhost 127.0.0.1 ::1
|
||||
mv localhost+2.pem cert.pem
|
||||
mv localhost+2-key.pem key.pem
|
||||
'';
|
||||
})
|
||||
];
|
||||
});
|
||||
packages.default = crateOutputs.packages.release;
|
||||
};
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "stable"
|
||||
components = ["rust-src", "rust-analyzer", "rustfmt"]
|
||||
components = ["rust-src", "rustfmt"]
|
||||
|
38
src/error.rs
Normal file
38
src/error.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use axum::response::IntoResponse;
|
||||
use http::StatusCode;
|
||||
|
||||
type BoxedError = Box<dyn std::error::Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AppError {
|
||||
internal: BoxedError,
|
||||
}
|
||||
|
||||
impl<E> From<E> for AppError
|
||||
where
|
||||
E: Into<BoxedError>,
|
||||
{
|
||||
fn from(err: E) -> Self {
|
||||
Self {
|
||||
internal: err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for AppError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Something went wrong: {}", self.internal),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AppError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.internal.fmt(f)
|
||||
}
|
||||
}
|
414
src/handler.rs
Normal file
414
src/handler.rs
Normal file
@ -0,0 +1,414 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use super::AppError;
|
||||
use async_tungstenite::{
|
||||
tokio::TokioAdapter,
|
||||
tungstenite::{protocol::CloseFrame as TungsteniteCloseFrame, Message as TungsteniteMessage},
|
||||
WebSocketStream,
|
||||
};
|
||||
use axum::{
|
||||
extract::{
|
||||
ws::{CloseFrame as AxumCloseFrame, Message as AxumMessage, WebSocket, WebSocketUpgrade},
|
||||
ConnectInfo, Query, State,
|
||||
},
|
||||
headers::UserAgent,
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Router, TypedHeader,
|
||||
};
|
||||
use base64::Engine;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use http::{
|
||||
header::{AUTHORIZATION, CONTENT_TYPE},
|
||||
HeaderValue, Method, Request, Response, StatusCode,
|
||||
};
|
||||
use hyper::Body;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tokio::net::TcpStream;
|
||||
use tower_http::{
|
||||
cors::CorsLayer,
|
||||
trace::{DefaultMakeSpan, TraceLayer},
|
||||
};
|
||||
use tracing::{Instrument, Span};
|
||||
|
||||
use crate::{get_conf, AppState};
|
||||
|
||||
const B64: base64::engine::GeneralPurpose = base64::engine::general_purpose::STANDARD;
|
||||
|
||||
pub(super) async fn public(state: AppState) -> Result<Router, AppError> {
|
||||
let trace_layer =
|
||||
TraceLayer::new_for_http().make_span_with(DefaultMakeSpan::default().include_headers(true));
|
||||
|
||||
let router = Router::new()
|
||||
.route("/thumbnails/:id", get(http))
|
||||
.route("/audio/id/:id", get(http))
|
||||
.route("/", get(metadata_ws))
|
||||
.layer(trace_layer)
|
||||
.layer(
|
||||
CorsLayer::new()
|
||||
.allow_origin(get_conf("CORS_ALLOW_ORIGIN")?.parse::<HeaderValue>()?)
|
||||
.allow_headers([CONTENT_TYPE])
|
||||
.allow_methods([Method::GET])
|
||||
.allow_credentials(true),
|
||||
)
|
||||
.with_state(state.clone());
|
||||
|
||||
Ok(router)
|
||||
}
|
||||
|
||||
pub(super) async fn internal(state: AppState) -> Router {
|
||||
Router::new()
|
||||
.route("/generate_token", get(generate_token))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
async fn generate_token(State(app): State<AppState>) -> Result<impl IntoResponse, AppError> {
|
||||
let token = app.tokens.generate().await?;
|
||||
// start task to write tokens
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = app.tokens.write(&app.tokens_path).await {
|
||||
tracing::error!("couldn't write tokens file: {err}");
|
||||
}
|
||||
});
|
||||
return Ok(token);
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Auth {
|
||||
#[serde(default)]
|
||||
token: Option<String>,
|
||||
}
|
||||
|
||||
fn extract_password_from_basic_auth(auth: &str) -> Result<String, AppError> {
|
||||
let decoded = B64.decode(auth.trim_start_matches("Basic "))?;
|
||||
let auth = String::from_utf8(decoded)?;
|
||||
Ok(auth.trim_start_matches("default:").to_string())
|
||||
}
|
||||
|
||||
async fn http(
|
||||
State(app): State<AppState>,
|
||||
Query(query): Query<Auth>,
|
||||
mut req: Request<Body>,
|
||||
) -> Result<Response<Body>, AppError> {
|
||||
let path = req.uri().path();
|
||||
let path_query = req
|
||||
.uri()
|
||||
.path_and_query()
|
||||
.map(|v| v.as_str())
|
||||
.unwrap_or(path);
|
||||
|
||||
*req.uri_mut() = format!(
|
||||
"http://{}:{}{}",
|
||||
app.musikcubed_address, app.musikcubed_http_port, path_query
|
||||
)
|
||||
.parse()?;
|
||||
|
||||
let maybe_token = query.token.or_else(|| {
|
||||
req.headers()
|
||||
.get(AUTHORIZATION)
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.and_then(|auth| extract_password_from_basic_auth(auth).ok())
|
||||
});
|
||||
|
||||
'ok: {
|
||||
tracing::debug!("verifying token: {maybe_token:?}");
|
||||
if let Some(token) = maybe_token {
|
||||
if app.tokens.verify(token).await? {
|
||||
break 'ok;
|
||||
}
|
||||
}
|
||||
return Ok(Response::builder()
|
||||
.status(StatusCode::UNAUTHORIZED)
|
||||
.body("Invalid token or token not present".to_string().into())
|
||||
.expect("cant fail"));
|
||||
}
|
||||
|
||||
let auth = B64.encode(format!("default:{}", app.musikcubed_password));
|
||||
req.headers_mut().insert(
|
||||
AUTHORIZATION,
|
||||
format!("Basic {auth}").parse().expect("valid header value"),
|
||||
);
|
||||
|
||||
Ok(app.client.request(req).await?)
|
||||
}
|
||||
|
||||
async fn metadata_ws(
|
||||
State(app): State<AppState>,
|
||||
TypedHeader(user_agent): TypedHeader<UserAgent>,
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
ws: WebSocketUpgrade,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
use async_tungstenite::tokio::connect_async;
|
||||
|
||||
let uri = format!(
|
||||
"ws://{}:{}",
|
||||
app.musikcubed_address, app.musikcubed_metadata_port
|
||||
);
|
||||
let (ws_stream, _) = connect_async(uri).await?;
|
||||
|
||||
let upgrade = ws
|
||||
.on_failed_upgrade({
|
||||
let user_agent = user_agent.clone();
|
||||
move |error| {
|
||||
let _entered = tracing::info_span!("metadata ws http", client_addr = %addr, client_user_agent = %user_agent).entered();
|
||||
tracing::error!("failed to upgrade to websocket for client: {error}");
|
||||
}
|
||||
})
|
||||
.on_upgrade(move |socket| {
|
||||
let span = tracing::info_span!(
|
||||
"metadata ws",
|
||||
client_addr = %addr,
|
||||
client_user_agent = %user_agent
|
||||
);
|
||||
handle_metadata_socket(ws_stream, socket, app).instrument(span)
|
||||
});
|
||||
|
||||
Ok(upgrade)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct WsApiMessage {
|
||||
name: String,
|
||||
r#type: String,
|
||||
id: String,
|
||||
#[serde(default)]
|
||||
device_id: Option<String>,
|
||||
options: serde_json::value::Map<String, Value>,
|
||||
}
|
||||
|
||||
async fn handle_metadata_socket(
|
||||
mut server_socket: WebSocketStream<TokioAdapter<TcpStream>>,
|
||||
mut client_socket: WebSocket,
|
||||
app: AppState,
|
||||
) {
|
||||
// get token
|
||||
let (token, og_auth_msg) = 'ok: {
|
||||
'err: {
|
||||
if let Some(Ok(AxumMessage::Text(raw))) = client_socket.recv().await {
|
||||
tracing::debug!("got client auth message: {raw}");
|
||||
let Ok(parsed) = serde_json::from_str::<WsApiMessage>(&raw) else {
|
||||
tracing::error!("invalid auth message");
|
||||
break 'err;
|
||||
};
|
||||
let Some(token) = parsed.options.get("password").and_then(|v| v.as_str()) else {
|
||||
tracing::error!("token was not provided");
|
||||
break 'err;
|
||||
};
|
||||
break 'ok (token.to_string(), parsed);
|
||||
} else {
|
||||
tracing::error!("did not receive auth message from client, closing socket");
|
||||
break 'err;
|
||||
}
|
||||
}
|
||||
let _ = client_socket.close().await;
|
||||
let _ = server_socket.close(None).await;
|
||||
return;
|
||||
};
|
||||
tracing::debug!("successfully extracted token from client request");
|
||||
|
||||
// validate token
|
||||
'ok: {
|
||||
let verify_res = app
|
||||
.tokens
|
||||
.verify(&token)
|
||||
.await
|
||||
.map_err(|err| err.to_string());
|
||||
'err: {
|
||||
match verify_res {
|
||||
Ok(verified) => {
|
||||
if !verified {
|
||||
tracing::error!("invalid token");
|
||||
break 'err;
|
||||
}
|
||||
break 'ok;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("internal server error while validating token: {err}");
|
||||
break 'err;
|
||||
}
|
||||
}
|
||||
};
|
||||
let _ = client_socket.close().await;
|
||||
let _ = server_socket.close(None).await;
|
||||
return;
|
||||
}
|
||||
tracing::debug!("successfully validated token");
|
||||
|
||||
let og_auth_reply = 'ok: {
|
||||
'err: {
|
||||
// send actual auth message to the musikcubed server
|
||||
let auth_msg = WsApiMessage {
|
||||
name: "authenticate".to_string(),
|
||||
r#type: "request".to_string(),
|
||||
id: og_auth_msg.id,
|
||||
device_id: og_auth_msg.device_id,
|
||||
options: {
|
||||
let mut map = serde_json::Map::with_capacity(1);
|
||||
map.insert(
|
||||
"password".to_string(),
|
||||
app.musikcubed_password.clone().into(),
|
||||
);
|
||||
map
|
||||
},
|
||||
};
|
||||
let auth_msg_ser = serde_json::to_string(&auth_msg).expect("");
|
||||
tracing::debug!("sending auth message to musikcubed: {auth_msg_ser}");
|
||||
if let Err(err) = server_socket
|
||||
.send(TungsteniteMessage::Text(auth_msg_ser))
|
||||
.await
|
||||
{
|
||||
tracing::error!("failed to send auth message to musikcubed: {err}");
|
||||
break 'err;
|
||||
}
|
||||
// wait for auth reply
|
||||
if let Some(Ok(TungsteniteMessage::Text(raw))) = server_socket.next().await {
|
||||
tracing::debug!("got auth reply from musikcubed: {raw}");
|
||||
let Ok(parsed) = serde_json::from_str::<WsApiMessage>(&raw) else {
|
||||
tracing::error!("invalid auth response message: {raw}");
|
||||
break 'err;
|
||||
};
|
||||
let is_authenticated = parsed
|
||||
.options
|
||||
.get("authenticated")
|
||||
.and_then(Value::as_bool)
|
||||
.unwrap_or(false);
|
||||
match is_authenticated {
|
||||
true => break 'ok parsed,
|
||||
false => break 'err,
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = client_socket.close().await;
|
||||
let _ = server_socket.close(None).await;
|
||||
return;
|
||||
};
|
||||
|
||||
'ok: {
|
||||
'err: {
|
||||
// send actual auth message to the musikcubed server
|
||||
let auth_reply_msg = {
|
||||
let mut auth_reply_msg = og_auth_reply;
|
||||
let maybe_env_map = auth_reply_msg
|
||||
.options
|
||||
.get_mut("environment")
|
||||
.and_then(Value::as_object_mut);
|
||||
if let Some(map) = maybe_env_map {
|
||||
map.insert("http_server_port".to_string(), Value::from(app.public_port));
|
||||
}
|
||||
auth_reply_msg
|
||||
};
|
||||
let auth_reply_msg_ser = serde_json::to_string(&auth_reply_msg).expect("");
|
||||
tracing::debug!("sending auth reply message to client: {auth_reply_msg_ser}");
|
||||
match client_socket
|
||||
.send(AxumMessage::Text(auth_reply_msg_ser))
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
tracing::debug!("successfully sent auth reply message to client");
|
||||
break 'ok;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("error while sending auth reply message to client: {err}");
|
||||
break 'err;
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = client_socket.close().await;
|
||||
let _ = server_socket.close(None).await;
|
||||
return;
|
||||
}
|
||||
tracing::info!("successfully authenticated");
|
||||
|
||||
let (mut in_write, mut in_read) = client_socket.split();
|
||||
let (mut out_write, mut out_read) = server_socket.split();
|
||||
|
||||
let in_read_fut = async move {
|
||||
while let Some(res) = in_read.next().await {
|
||||
match res {
|
||||
Ok(msg) => {
|
||||
tracing::trace!("got message from client: {msg:?}");
|
||||
let res = out_write.send(axum_msg_to_tungstenite(msg)).await;
|
||||
if let Err(err) = res {
|
||||
tracing::error!("could not write to server socket: {err}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("could not read from client socket: {err}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = out_write.send(TungsteniteMessage::Close(None)).await;
|
||||
};
|
||||
let in_read_task = tokio::spawn(in_read_fut.instrument(Span::current()));
|
||||
|
||||
let in_write_fut = async move {
|
||||
while let Some(res) = out_read.next().await {
|
||||
match res {
|
||||
Ok(msg) => {
|
||||
tracing::trace!("got message from server: {msg:?}");
|
||||
let res = in_write.send(tungstenite_msg_to_axum(msg)).await;
|
||||
if let Err(err) = res {
|
||||
tracing::error!("could not write to client socket: {err}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("could not read from server socket: {err}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = in_write.send(AxumMessage::Close(None)).await;
|
||||
};
|
||||
let in_write_task = tokio::spawn(in_write_fut.instrument(Span::current()));
|
||||
|
||||
let _ = tokio::join!(in_read_task, in_write_task);
|
||||
|
||||
tracing::debug!("ending metadata ws task");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn tungstenite_msg_to_axum(msg: TungsteniteMessage) -> AxumMessage {
|
||||
match msg {
|
||||
TungsteniteMessage::Text(data) => AxumMessage::Text(data),
|
||||
TungsteniteMessage::Binary(data) => AxumMessage::Binary(data),
|
||||
TungsteniteMessage::Ping(data) => AxumMessage::Ping(data),
|
||||
TungsteniteMessage::Pong(data) => AxumMessage::Pong(data),
|
||||
TungsteniteMessage::Close(frame) => {
|
||||
AxumMessage::Close(frame.map(tungstenite_close_frame_to_axum))
|
||||
}
|
||||
TungsteniteMessage::Frame(_) => unreachable!("we don't use raw frames"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn axum_msg_to_tungstenite(msg: AxumMessage) -> TungsteniteMessage {
|
||||
match msg {
|
||||
AxumMessage::Text(data) => TungsteniteMessage::Text(data),
|
||||
AxumMessage::Binary(data) => TungsteniteMessage::Binary(data),
|
||||
AxumMessage::Ping(data) => TungsteniteMessage::Ping(data),
|
||||
AxumMessage::Pong(data) => TungsteniteMessage::Pong(data),
|
||||
AxumMessage::Close(frame) => {
|
||||
TungsteniteMessage::Close(frame.map(axum_close_frame_to_tungstenite))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn tungstenite_close_frame_to_axum(frame: TungsteniteCloseFrame) -> AxumCloseFrame {
|
||||
AxumCloseFrame {
|
||||
code: frame.code.into(),
|
||||
reason: frame.reason,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn axum_close_frame_to_tungstenite(frame: AxumCloseFrame) -> TungsteniteCloseFrame {
|
||||
TungsteniteCloseFrame {
|
||||
code: frame.code.into(),
|
||||
reason: frame.reason,
|
||||
}
|
||||
}
|
117
src/main.rs
117
src/main.rs
@ -1,11 +1,116 @@
|
||||
use axum::{routing::get, Router};
|
||||
use axum_server::tls_rustls::RustlsConfig;
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use dotenvy::Error as DotenvError;
|
||||
use error::AppError;
|
||||
use hyper::{client::HttpConnector, Body};
|
||||
use token::Tokens;
|
||||
use tracing::{error, info, warn};
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
mod error;
|
||||
mod handler;
|
||||
mod token;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
if let Err(DotenvError::Io(err)) = dotenvy::dotenv() {}
|
||||
|
||||
let app = Router::new().route("/", get(|| async { "Hello world" }));
|
||||
let config = RustlsConfig::from_pem_file(cert, key).await.unwrap();
|
||||
app().await.unwrap();
|
||||
}
|
||||
|
||||
async fn app() -> Result<(), AppError> {
|
||||
// init tracing
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| "musikquadrupled=debug,tower_http=debug".into()),
|
||||
)
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
|
||||
// load config
|
||||
match dotenvy::dotenv() {
|
||||
Err(DotenvError::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
warn!(".env file not found");
|
||||
}
|
||||
Ok(_) => info!(".env file loaded"),
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
|
||||
// let cert_path = get_conf("TLS_CERT_PATH");
|
||||
// let key_path = get_conf("TLS_KEY_PATH");
|
||||
// info!("cert path is: {cert_path}");
|
||||
// info!("key path is: {key_path}");
|
||||
// let config = RustlsConfig::from_pem_file(cert_path, key_path)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
let public_addr: SocketAddr = get_conf("ADDRESS")?.parse()?;
|
||||
let local_addr: SocketAddr = get_conf("LOCAL_ADDRESS")?.parse()?;
|
||||
|
||||
let state = AppState::new(AppStateInternal::new(public_addr.port()).await?);
|
||||
let public = handler::public(state.clone()).await?;
|
||||
let internal = handler::internal(state).await;
|
||||
|
||||
info!("listening on {public_addr} for public APIs, on {local_addr} for internal API use");
|
||||
// axum_server::bind_rustls(addr, config)
|
||||
let public = axum_server::bind(public_addr)
|
||||
.serve(public.into_make_service_with_connect_info::<SocketAddr>());
|
||||
let internal = axum_server::bind(local_addr).serve(internal.into_make_service());
|
||||
|
||||
tokio::try_join!(public, internal)
|
||||
.map(|_| ())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn get_conf(key: &str) -> Result<String, AppError> {
|
||||
const ENV_NAMESPACE: &str = "MUSIKQUAD";
|
||||
|
||||
let key = format!("{ENV_NAMESPACE}_{key}");
|
||||
match std::env::var(&key) {
|
||||
Ok(val) => return Ok(val),
|
||||
Err(err) => {
|
||||
use std::env::VarError;
|
||||
match err {
|
||||
VarError::NotPresent => {
|
||||
error!("Config option {key} was not set but is required");
|
||||
}
|
||||
VarError::NotUnicode(_) => {
|
||||
error!("Config option {key} was not unicode");
|
||||
}
|
||||
}
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Client = hyper::Client<HttpConnector, Body>;
|
||||
|
||||
type AppState = Arc<AppStateInternal>;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppStateInternal {
|
||||
client: Client,
|
||||
tokens: Tokens,
|
||||
tokens_path: String,
|
||||
public_port: u16,
|
||||
musikcubed_address: String,
|
||||
musikcubed_http_port: u16,
|
||||
musikcubed_metadata_port: u16,
|
||||
musikcubed_password: String,
|
||||
}
|
||||
|
||||
impl AppStateInternal {
|
||||
async fn new(public_port: u16) -> Result<Self, AppError> {
|
||||
let tokens_path = get_conf("TOKENS_FILE")?;
|
||||
let this = Self {
|
||||
public_port,
|
||||
musikcubed_address: get_conf("MUSIKCUBED_ADDRESS")?,
|
||||
musikcubed_http_port: get_conf("MUSIKCUBED_HTTP_PORT")?.parse()?,
|
||||
musikcubed_metadata_port: get_conf("MUSIKCUBED_METADATA_PORT")?.parse()?,
|
||||
musikcubed_password: get_conf("MUSIKCUBED_PASSWORD")?,
|
||||
client: Client::new(),
|
||||
tokens: Tokens::read(&tokens_path).await?,
|
||||
tokens_path,
|
||||
};
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
70
src/token.rs
Normal file
70
src/token.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use rand::Rng;
|
||||
use scc::HashSet;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::AppError;
|
||||
|
||||
fn hash_string(data: &[u8]) -> Result<String, argon2::Error> {
|
||||
argon2::hash_encoded(data, "11111111".as_bytes(), &argon2::Config::default())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Tokens {
|
||||
hashed: Arc<HashSet<Cow<'static, str>>>,
|
||||
raw_contents: &'static str,
|
||||
}
|
||||
|
||||
impl Tokens {
|
||||
pub async fn read(path: impl AsRef<Path>) -> Result<Self, AppError> {
|
||||
let this = Self {
|
||||
hashed: Arc::new(HashSet::new()),
|
||||
raw_contents: Box::leak(tokio::fs::read_to_string(path).await?.into_boxed_str()),
|
||||
};
|
||||
|
||||
for token in this.raw_contents.lines() {
|
||||
let token = token.trim();
|
||||
if !token.is_empty() {
|
||||
this.hashed
|
||||
.insert_async(Cow::Borrowed(token))
|
||||
.await
|
||||
.expect("the set will be empty");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
pub async fn write(&self, path: impl AsRef<Path>) -> Result<(), AppError> {
|
||||
let mut contents = String::new();
|
||||
self.hashed
|
||||
.for_each_async(|hash| {
|
||||
writeln!(&mut contents, "{hash}").expect("if this fails then too bad")
|
||||
})
|
||||
.await;
|
||||
|
||||
tokio::fs::write(path, contents).await.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn generate(&self) -> Result<String, AppError> {
|
||||
let token = rand::thread_rng()
|
||||
.sample_iter(rand::distributions::Alphanumeric)
|
||||
.take(30)
|
||||
.map(|c| c as char)
|
||||
.collect::<String>();
|
||||
|
||||
let token_hash = hash_string(token.as_bytes())?;
|
||||
|
||||
self.hashed.insert_async(token_hash.into()).await?;
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
pub async fn verify(&self, token: impl AsRef<str>) -> Result<bool, AppError> {
|
||||
let token = token.as_ref();
|
||||
let token_hash = hash_string(token.as_bytes())?;
|
||||
tracing::debug!("verifying token {token}, hash {token_hash}");
|
||||
Ok(self.hashed.contains_async(&Cow::Owned(token_hash)).await)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user