Add proxying and caching and CLI to configure those
This commit is contained in:
parent
797ba248fb
commit
b86fa50f77
17 changed files with 1173 additions and 37 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/store
|
||||
|
|
604
Cargo.lock
generated
604
Cargo.lock
generated
|
@ -180,7 +180,7 @@ dependencies = [
|
|||
"serde_urlencoded",
|
||||
"smallvec",
|
||||
"socket2",
|
||||
"time",
|
||||
"time 0.3.17",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
@ -237,6 +237,15 @@ dependencies = [
|
|||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -255,6 +264,15 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.3"
|
||||
|
@ -285,6 +303,12 @@ dependencies = [
|
|||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.3.0"
|
||||
|
@ -315,6 +339,74 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time 0.1.45",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chunked_transfer"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
|
@ -328,10 +420,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
"time 0.3.17",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.5"
|
||||
|
@ -360,6 +458,50 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decky_api"
|
||||
version = "0.1.0"
|
||||
|
@ -380,13 +522,22 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.10.3",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
|
@ -399,6 +550,27 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.25"
|
||||
|
@ -472,7 +644,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -500,6 +672,12 @@ version = "0.12.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
|
@ -509,6 +687,21 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
|
@ -532,6 +725,30 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
|
@ -552,6 +769,28 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
|
@ -567,6 +806,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.3.2"
|
||||
|
@ -579,6 +827,21 @@ version = "0.2.138"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
||||
|
||||
[[package]]
|
||||
name = "local-channel"
|
||||
version = "0.1.3"
|
||||
|
@ -645,7 +908,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
@ -655,7 +918,35 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"actix-cors",
|
||||
"actix-web",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"clap",
|
||||
"decky_api",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha256",
|
||||
"simplelog",
|
||||
"ureq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -664,7 +955,16 @@ version = "1.14.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -674,6 +974,18 @@ version = "1.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
|
@ -727,6 +1039,30 @@ version = "0.2.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
|
@ -801,6 +1137,21 @@ version = "0.6.28"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
|
@ -810,6 +1161,32 @@ dependencies = [
|
|||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
|
@ -822,6 +1199,22 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.14"
|
||||
|
@ -879,7 +1272,30 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"digest 0.10.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha256"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e334db67871c14c18fc066ad14af13f9fdf5f9a91c61af432d1e3a39c8c6a141"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -891,6 +1307,17 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simplelog"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48dfff04aade74dd495b007c831cd6f4e0cee19c344dd9dc0884c0289b70a786"
|
||||
dependencies = [
|
||||
"log",
|
||||
"termcolor",
|
||||
"time 0.3.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
|
@ -916,6 +1343,18 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.105"
|
||||
|
@ -927,6 +1366,26 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.17"
|
||||
|
@ -934,6 +1393,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
|
@ -1049,6 +1510,37 @@ dependencies = [
|
|||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chunked_transfer",
|
||||
"flate2",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
|
@ -1066,12 +1558,101 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -1088,6 +1669,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
|
17
Cargo.toml
17
Cargo.toml
|
@ -7,11 +7,28 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
decky_api = { version = "0.1.0", path = "./decky_api" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
bytes = "1.3"
|
||||
sha256 = "1.1"
|
||||
|
||||
# logging
|
||||
log = "0.4"
|
||||
simplelog = "0.12"
|
||||
|
||||
# web framework
|
||||
actix-web = "4.2"
|
||||
actix-cors = "0.6"
|
||||
|
||||
# proxy storage impl
|
||||
ureq = { version = "2.5", features = ["json"] }
|
||||
|
||||
# cache storage impl
|
||||
chrono = { version = "0.4" }
|
||||
|
||||
# cli
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
|
||||
[workspace]
|
||||
include = [
|
||||
"decky_api"
|
||||
|
|
|
@ -7,4 +7,3 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
#serde_json = { version = "1.0" }
|
||||
|
|
|
@ -1,32 +1,16 @@
|
|||
/*export interface StorePluginVersion {
|
||||
name: string;
|
||||
hash: string;
|
||||
artifact: string | undefined | null;
|
||||
}
|
||||
|
||||
export interface StorePlugin {
|
||||
id: number;
|
||||
name: string;
|
||||
versions: StorePluginVersion[];
|
||||
author: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
image_url: string;
|
||||
}*/
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
pub type StorePluginList = Vec<StorePlugin>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct StorePlugin {
|
||||
id: usize,
|
||||
name: String,
|
||||
versions: Vec<StorePluginVersion>,
|
||||
author: String,
|
||||
description: String,
|
||||
tags: Vec<String>,
|
||||
image_url: String,
|
||||
pub id: usize,
|
||||
pub name: String,
|
||||
pub versions: Vec<StorePluginVersion>,
|
||||
pub author: String,
|
||||
pub description: String,
|
||||
pub tags: Vec<String>,
|
||||
pub image_url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
|
|
43
src/cli.rs
Normal file
43
src/cli.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use clap::{Parser, Subcommand, Args};
|
||||
|
||||
/// An alternative plugin store
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[command(author, version, about, long_about = None, propagate_version = true)]
|
||||
pub struct CliArgs {
|
||||
/// Proxy offerings from another store
|
||||
#[arg(name = "store", long)]
|
||||
pub proxy_store: Option<String>,
|
||||
/// Proxy main store offerings
|
||||
#[arg(name = "proxy", short, long)]
|
||||
pub proxy: bool,
|
||||
/// Cache results for a period
|
||||
#[arg(name = "cache", long)]
|
||||
pub cache_duration: Option<i64>,
|
||||
/// Storage adapter
|
||||
#[command(subcommand)]
|
||||
pub storage: StorageArgs,
|
||||
}
|
||||
|
||||
impl CliArgs {
|
||||
pub fn get() -> Self {
|
||||
Self::parse()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum StorageArgs {
|
||||
/// Use default storage settings (filesystem)
|
||||
Default,
|
||||
/// Use the filesystem
|
||||
Filesystem(FilesystemArgs),
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct FilesystemArgs {
|
||||
#[arg(name = "folder", default_value_t = {"./store".into()})]
|
||||
pub root: String,
|
||||
#[arg(name = "domain", default_value_t = {"http://localhost:22252".into()})]
|
||||
pub domain_root: String,
|
||||
#[arg(name = "stats", long)]
|
||||
pub enable_stats: bool,
|
||||
}
|
37
src/main.rs
37
src/main.rs
|
@ -1,7 +1,12 @@
|
|||
mod cli;
|
||||
mod consts;
|
||||
mod not_decky;
|
||||
mod storage;
|
||||
|
||||
use actix_web::{get, App, HttpResponse, HttpServer, Responder};
|
||||
use crate::storage::IStorageWrap;
|
||||
|
||||
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
|
||||
use simplelog::{LevelFilter, WriteLogger};
|
||||
|
||||
#[get("/version_info")]
|
||||
async fn hello() -> impl Responder {
|
||||
|
@ -10,17 +15,45 @@ async fn hello() -> impl Responder {
|
|||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(|| {
|
||||
let args = cli::CliArgs::get();
|
||||
let log_filepath = std::path::Path::new("/tmp").join(format!("{}.log", consts::PACKAGE_NAME));
|
||||
WriteLogger::init(
|
||||
LevelFilter::Debug,
|
||||
Default::default(),
|
||||
std::fs::File::create(&log_filepath).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("Logging to {}", log_filepath.display());
|
||||
|
||||
HttpServer::new(move || {
|
||||
let cors = actix_cors::Cors::default()
|
||||
.allowed_origin("https://steamloopback.host")
|
||||
.allow_any_header()
|
||||
.expose_any_header();
|
||||
|
||||
let storage_data: Box<dyn storage::IStorage> = match &args.storage {
|
||||
cli::StorageArgs::Default => storage::FileStorage::new(
|
||||
"./store".into(),
|
||||
"http://192.168.0.128:22252".into(),
|
||||
true,
|
||||
).wrap(args.clone()),
|
||||
cli::StorageArgs::Filesystem(fs) => storage::FileStorage::new(
|
||||
fs.root.clone().into(),
|
||||
fs.domain_root.clone().into(),
|
||||
fs.enable_stats,
|
||||
).wrap(args.clone()),
|
||||
};
|
||||
|
||||
App::new()
|
||||
.wrap(cors)
|
||||
.app_data(web::Data::new(storage_data))
|
||||
.service(hello)
|
||||
.service(not_decky::decky_index)
|
||||
.service(not_decky::decky_plugins)
|
||||
.service(not_decky::decky_artifact)
|
||||
.service(not_decky::decky_image)
|
||||
.service(not_decky::decky_statistics)
|
||||
})
|
||||
.bind(("0.0.0.0", 22252))?
|
||||
.run()
|
||||
|
|
9
src/not_decky/artifact.rs
Normal file
9
src/not_decky/artifact.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use actix_web::{get, web, Responder};
|
||||
|
||||
use crate::storage::IStorage;
|
||||
|
||||
#[get("/plugins/{name}/{version}/{hash}.zip")]
|
||||
pub async fn decky_artifact(data: web::Data<Box<dyn IStorage>>, path: web::Path<(String, String, String)>) -> actix_web::Result<impl Responder> {
|
||||
let zip = data.get_artifact(&path.0, &path.1, &path.2).map_err(|e| actix_web::error::ErrorNotFound(e.to_string()))?;
|
||||
Ok(zip)
|
||||
}
|
9
src/not_decky/image.rs
Normal file
9
src/not_decky/image.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use actix_web::{get, web, Responder};
|
||||
|
||||
use crate::storage::IStorage;
|
||||
|
||||
#[get("/plugins/{name}.png")]
|
||||
pub async fn decky_image(data: web::Data<Box<dyn IStorage>>, path: web::Path<String>) -> actix_web::Result<impl Responder> {
|
||||
let zip = data.get_image(&path).map_err(|e| actix_web::error::ErrorNotFound(e.to_string()))?;
|
||||
Ok(zip)
|
||||
}
|
|
@ -1,5 +1,11 @@
|
|||
mod artifact;
|
||||
mod image;
|
||||
mod index;
|
||||
mod plugins;
|
||||
mod stats;
|
||||
|
||||
pub use artifact::decky_artifact;
|
||||
pub use image::decky_image;
|
||||
pub use index::decky_index;
|
||||
pub use plugins::decky_plugins;
|
||||
pub use stats::decky_statistics;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use decky_api::StorePluginList;
|
||||
|
||||
use actix_web::{get, web::Json, Responder};
|
||||
use actix_web::{get, web, Responder};
|
||||
|
||||
use crate::storage::IStorage;
|
||||
|
||||
#[get("/plugins")]
|
||||
pub async fn decky_plugins() -> impl Responder {
|
||||
let plugins: Vec<StorePluginList> = Vec::new();
|
||||
Json(plugins)
|
||||
pub async fn decky_plugins(data: actix_web::web::Data<Box<dyn IStorage>>) -> impl Responder {
|
||||
let plugins: StorePluginList = data.plugins();
|
||||
web::Json(plugins)
|
||||
}
|
||||
|
|
11
src/not_decky/stats.rs
Normal file
11
src/not_decky/stats.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use actix_web::{get, web, Responder};
|
||||
|
||||
use crate::storage::IStorage;
|
||||
|
||||
#[get("/stats")]
|
||||
pub async fn decky_statistics(data: actix_web::web::Data<Box<dyn IStorage>>) -> impl Responder {
|
||||
let plugins: HashMap<String, u64> = data.get_statistics();
|
||||
web::Json(plugins)
|
||||
}
|
102
src/storage/cache.rs
Normal file
102
src/storage/cache.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use std::sync::{RwLock, atomic::{AtomicI64, Ordering}};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use decky_api::StorePluginList;
|
||||
use bytes::Bytes;
|
||||
use chrono::Utc;
|
||||
|
||||
use super::IStorage;
|
||||
|
||||
struct Cached<T: Clone> {
|
||||
expiry: AtomicI64,
|
||||
value: RwLock<T>,
|
||||
ttl: i64
|
||||
}
|
||||
|
||||
impl<T: Clone> Cached<T> {
|
||||
fn new(value: T, duration: i64) -> Self {
|
||||
Self {
|
||||
expiry: AtomicI64::new(Utc::now().timestamp() + duration),
|
||||
value: RwLock::new(value),
|
||||
ttl: duration,
|
||||
}
|
||||
}
|
||||
|
||||
fn get<F: FnOnce() -> T>(&self, getter: F) -> T {
|
||||
let now = Utc::now().timestamp();
|
||||
let expiry = self.expiry.load(Ordering::Acquire);
|
||||
if expiry < now {
|
||||
// refresh required
|
||||
let new_value = getter();
|
||||
let new_expiry = now + self.ttl;
|
||||
let mut write_lock = self.value.write().expect("Failed to acquire cache write lock");
|
||||
self.expiry.store(new_expiry, Ordering::Release);
|
||||
*write_lock = new_value.clone();
|
||||
new_value
|
||||
} else {
|
||||
// cache is still good
|
||||
self.value.read().expect("Failed to acquire cache read lock").clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh(&self, new_value: T) {
|
||||
let new_expiry = Utc::now().timestamp() + self.ttl;
|
||||
let mut write_lock = self.value.write().expect("Failed to acquire cache write lock");
|
||||
self.expiry.store(new_expiry, Ordering::Release);
|
||||
*write_lock = new_value;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CachedStorage<S: IStorage> {
|
||||
fallback: S,
|
||||
plugins_cache: Cached<StorePluginList>,
|
||||
statistics_cache: Cached<HashMap<String, u64>>,
|
||||
artifacts_cache: Cached<HashMap<String, Bytes>>,
|
||||
images_cache: Cached<HashMap<String, Bytes>>,
|
||||
}
|
||||
|
||||
impl<S: IStorage> CachedStorage<S> {
|
||||
pub fn new(duration: i64, inner: S) -> Self {
|
||||
Self {
|
||||
plugins_cache: Cached::new(inner.plugins(), duration),
|
||||
statistics_cache: Cached::new(inner.get_statistics(), duration),
|
||||
artifacts_cache: Cached::new(HashMap::new(), duration),
|
||||
images_cache: Cached::new(HashMap::new(), duration),
|
||||
fallback: inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: IStorage> IStorage for CachedStorage<S> {
|
||||
fn plugins(&self) -> StorePluginList {
|
||||
self.plugins_cache.get(|| self.fallback.plugins())
|
||||
}
|
||||
|
||||
fn get_artifact(&self, name: &str, version: &str, hash: &str) -> Result<bytes::Bytes, std::io::Error> {
|
||||
let mut cached = self.artifacts_cache.get(|| HashMap::new());
|
||||
if let Some(bytes) = cached.get(hash) {
|
||||
Ok(bytes.to_owned())
|
||||
} else {
|
||||
let new_artifact = self.fallback.get_artifact(name, version, hash)?;
|
||||
cached.insert(hash.to_owned(), new_artifact.clone());
|
||||
self.artifacts_cache.refresh(cached);
|
||||
Ok(new_artifact)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_image(&self, name: &str) -> Result<bytes::Bytes, std::io::Error> {
|
||||
let mut cached = self.images_cache.get(|| HashMap::new());
|
||||
if let Some(bytes) = cached.get(name) {
|
||||
Ok(bytes.to_owned())
|
||||
} else {
|
||||
let new_image = self.fallback.get_image(name)?;
|
||||
cached.insert(name.to_owned(), new_image.clone());
|
||||
self.images_cache.refresh(cached);
|
||||
Ok(new_image)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_statistics(&self) -> std::collections::HashMap<String, u64> {
|
||||
self.statistics_cache.get(|| self.fallback.get_statistics())
|
||||
}
|
||||
}
|
197
src/storage/filesystem.rs
Normal file
197
src/storage/filesystem.rs
Normal file
|
@ -0,0 +1,197 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::RwLock;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use decky_api::{StorePlugin, StorePluginList, StorePluginVersion};
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use super::IStorage;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct PluginMetadata {
|
||||
id: usize,
|
||||
author: String,
|
||||
description: String,
|
||||
tags: Vec<String>,
|
||||
}
|
||||
|
||||
impl PluginMetadata {
|
||||
fn complete(self, name: String, versions: Vec<StorePluginVersion>, image: String) -> StorePlugin {
|
||||
StorePlugin {
|
||||
id: self.id,
|
||||
name: name,
|
||||
versions: versions,
|
||||
author: self.author,
|
||||
description: self.description,
|
||||
tags: self.tags,
|
||||
image_url: image,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FileStorage {
|
||||
stats: Option<RwLock<HashMap<String, AtomicU64>>>, // TODO collect hit counts on actions
|
||||
root: PathBuf,
|
||||
domain_root: String,
|
||||
}
|
||||
|
||||
impl FileStorage {
|
||||
pub fn new(root: PathBuf, domain_root: String, enable_stats: bool) -> Self {
|
||||
Self {
|
||||
root: root,
|
||||
domain_root: domain_root,
|
||||
stats: if enable_stats { Some(RwLock::new(HashMap::new())) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
fn plugins_path(&self) -> PathBuf {
|
||||
self.root.join("plugins")
|
||||
}
|
||||
|
||||
fn plugin_json_path(&self, plugin_root: impl AsRef<Path>) -> PathBuf {
|
||||
plugin_root.as_ref().join("plugin.json")
|
||||
}
|
||||
|
||||
fn plugin_root_path(&self, plugin_name: &str) -> PathBuf {
|
||||
self.plugins_path().join(plugin_name)
|
||||
}
|
||||
|
||||
fn plugin_artifact_path(&self, plugin_name: &str, version_name: &str, _hash: &str) -> PathBuf {
|
||||
self.plugin_root_path(plugin_name)
|
||||
.join(format!("{}.zip", version_name))
|
||||
}
|
||||
|
||||
fn plugin_image_path(&self, plugin_name: &str) -> PathBuf {
|
||||
self.plugin_root_path(plugin_name)
|
||||
.join(format!("image.png"))
|
||||
}
|
||||
|
||||
fn read_all_plugins(&self) -> std::io::Result<StorePluginList> {
|
||||
let plugins = self.plugins_path();
|
||||
let dir_reader = plugins.read_dir()?;
|
||||
let mut results = Vec::with_capacity(dir_reader.size_hint().1.unwrap_or(32));
|
||||
for entry in dir_reader {
|
||||
let entry = entry?;
|
||||
if entry.file_type()?.is_dir() {
|
||||
results.push(self.read_single_plugin(&entry.path())?);
|
||||
}
|
||||
}
|
||||
// build stats counters
|
||||
if let Some(stats) = &self.stats {
|
||||
let mut lock = stats.write().expect("Couldn't acquire stats write lock");
|
||||
for plugin in &results {
|
||||
for version in &plugin.versions {
|
||||
if !lock.contains_key(&version.hash) {
|
||||
lock.insert(version.hash.clone(), AtomicU64::new(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn read_single_plugin(&self, path: &PathBuf) -> std::io::Result<StorePlugin> {
|
||||
let plugin_name = path.file_name().unwrap().to_string_lossy().into_owned();
|
||||
let json_path = self.plugin_json_path(path);
|
||||
let plugin_info: PluginMetadata = match serde_json::from_reader(File::open(&json_path)?) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("`{}` JSON err: {}", json_path.display(), e);
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))
|
||||
}
|
||||
};
|
||||
// find plugin versions
|
||||
let dir_reader = path.read_dir()?;
|
||||
let mut versions = Vec::with_capacity(dir_reader.size_hint().1.unwrap_or(4));
|
||||
for entry in dir_reader {
|
||||
let entry = entry?;
|
||||
if entry.file_type()?.is_file() {
|
||||
let entry_path = entry.path();
|
||||
let extension = entry_path.extension().unwrap().to_string_lossy().into_owned();
|
||||
if extension == "zip" {
|
||||
let version_name = entry_path.file_stem().unwrap().to_string_lossy().into_owned();
|
||||
let hash_str = sha256::try_digest(entry_path.as_ref())?;
|
||||
let artifact_url = format!("{}/plugins/{}/{}/{}.zip", self.domain_root, plugin_name, version_name, hash_str);
|
||||
versions.push(StorePluginVersion {
|
||||
name: version_name,
|
||||
hash: hash_str,
|
||||
artifact: Some(artifact_url)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
let image_url = format!("{}/plugins/{}.png", self.domain_root, plugin_name);
|
||||
Ok(
|
||||
plugin_info.complete(
|
||||
plugin_name,
|
||||
versions,
|
||||
image_url,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl IStorage for FileStorage {
|
||||
fn plugins(&self) -> StorePluginList {
|
||||
match self.read_all_plugins() {
|
||||
Err(e) => {
|
||||
log::error!("Plugins read error: {}", e);
|
||||
vec![]
|
||||
},
|
||||
Ok(x) => x
|
||||
}
|
||||
}
|
||||
|
||||
fn get_artifact(&self, name: &str, version: &str, hash: &str) -> Result<bytes::Bytes, std::io::Error> {
|
||||
let path = self.plugin_artifact_path(name, version, hash);
|
||||
log::debug!("Opening artifact path: {}", path.display());
|
||||
let mut file = File::open(path)?;
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer)?;
|
||||
if let Some(stats) = &self.stats {
|
||||
let lock = stats.read().expect("Failed to acquire stats read lock");
|
||||
if let Some(counter) = lock.get(hash) {
|
||||
counter.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
Ok(buffer.into())
|
||||
}
|
||||
|
||||
fn get_image(&self, name: &str) -> Result<bytes::Bytes, std::io::Error> {
|
||||
let path = self.plugin_image_path(name);
|
||||
log::debug!("Opening image path: {}", path.display());
|
||||
let mut file = File::open(path)?;
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer)?;
|
||||
Ok(buffer.into())
|
||||
}
|
||||
|
||||
fn get_statistics(&self) -> std::collections::HashMap<String, u64> {
|
||||
if let Some(stats) = &self.stats {
|
||||
if let Ok(plugins) = self.read_all_plugins() {
|
||||
let lock = stats.read().expect("Failed to acquire stats read lock");
|
||||
let mut map = std::collections::HashMap::with_capacity(64);
|
||||
for plugin in plugins {
|
||||
let mut total = 0;
|
||||
for version in plugin.versions {
|
||||
if let Some(count) = lock.get(&version.hash) {
|
||||
let count_val = count.load(Ordering::SeqCst);
|
||||
total += count_val;
|
||||
map.insert(format!("{} {}", plugin.name, version.name), count_val);
|
||||
}
|
||||
}
|
||||
map.insert(format!("{}", plugin.name), total);
|
||||
}
|
||||
map
|
||||
} else {
|
||||
std::collections::HashMap::with_capacity(0)
|
||||
}
|
||||
} else {
|
||||
std::collections::HashMap::with_capacity(0)
|
||||
}
|
||||
}
|
||||
}
|
50
src/storage/interface.rs
Normal file
50
src/storage/interface.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
pub trait IStorage {
|
||||
fn plugins(&self) -> decky_api::StorePluginList;
|
||||
|
||||
fn get_artifact(&self, _name: &str, _version: &str, _hash: &str) -> Result<bytes::Bytes, std::io::Error> {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Artifact downloading not supported"))
|
||||
}
|
||||
|
||||
fn get_image(&self, _name: &str) -> Result<bytes::Bytes, std::io::Error> {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Image downloading not supported"))
|
||||
}
|
||||
|
||||
fn get_statistics(&self) -> std::collections::HashMap<String, u64> {
|
||||
std::collections::HashMap::with_capacity(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IStorageWrap: Sized + IStorage {
|
||||
fn wrap(self, conf: crate::cli::CliArgs) -> Box<dyn IStorage>;
|
||||
}
|
||||
|
||||
impl<X: Sized + IStorage + 'static> IStorageWrap for X {
|
||||
fn wrap(self, conf: crate::cli::CliArgs) -> Box<dyn IStorage> {
|
||||
let proxy = if let Some(store) = conf.proxy_store {
|
||||
Some(store)
|
||||
} else if conf.proxy {
|
||||
Some("https://plugins.deckbrew.xyz".to_owned())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
match (proxy, conf.cache_duration) {
|
||||
(Some(proxy), Some(cache_dur)) => Box::new(
|
||||
super::CachedStorage::new(
|
||||
cache_dur,
|
||||
super::ProxiedStorage::new(proxy, self),
|
||||
)
|
||||
),
|
||||
(Some(proxy), None) => Box::new(super::ProxiedStorage::new(proxy, self)),
|
||||
(None, Some(cache_dur)) => Box::new(super::CachedStorage::new(cache_dur, self)),
|
||||
(None, None) => Box::new(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmptyStorage;
|
||||
|
||||
impl IStorage for EmptyStorage {
|
||||
fn plugins(&self) -> decky_api::StorePluginList {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
9
src/storage/mod.rs
Normal file
9
src/storage/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
mod cache;
|
||||
mod filesystem;
|
||||
mod interface;
|
||||
mod proxy;
|
||||
|
||||
pub use cache::CachedStorage;
|
||||
pub use filesystem::FileStorage;
|
||||
pub use interface::{IStorage, EmptyStorage, IStorageWrap};
|
||||
pub use proxy::ProxiedStorage;
|
74
src/storage/proxy.rs
Normal file
74
src/storage/proxy.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use decky_api::{StorePluginList, StorePluginVersion};
|
||||
|
||||
use super::IStorage;
|
||||
|
||||
pub struct ProxiedStorage<S: IStorage> {
|
||||
store_url: String,
|
||||
fallback: S,
|
||||
agent: ureq::Agent,
|
||||
}
|
||||
|
||||
impl<S: IStorage> ProxiedStorage<S> {
|
||||
pub fn new(target_store: String, inner: S) -> Self {
|
||||
Self {
|
||||
store_url: target_store,
|
||||
fallback: inner,
|
||||
agent: ureq::Agent::new(),
|
||||
}
|
||||