commit 02
This commit is contained in:
parent
7a65b34ef4
commit
556035d278
9 changed files with 492 additions and 279 deletions
299
Cargo.lock
generated
299
Cargo.lock
generated
|
@ -237,6 +237,7 @@ dependencies = [
|
|||
"blocking",
|
||||
"futures-lite 2.6.0",
|
||||
"once_cell",
|
||||
"tokio 1.43.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -315,6 +316,18 @@ dependencies = [
|
|||
"pin-project-lite 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-native-tls"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e9e7a929bd34c68a82d58a4de7f86fffdaf97fb2af850162a7bb19dd7269b33"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"native-tls",
|
||||
"thiserror 1.0.69",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "2.3.0"
|
||||
|
@ -336,20 +349,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-session"
|
||||
version = "3.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07da4ce523b4e2ebaaf330746761df23a465b951a83d84bbce4233dabedae630"
|
||||
checksum = "345022a2eed092cd105cc1b26fd61c341e100bd5fcbbd792df4baf31c2cc631f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-lock 2.8.0",
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
"base64 0.12.3",
|
||||
"bincode",
|
||||
"blake3",
|
||||
"chrono",
|
||||
"hmac 0.11.0",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"hmac 0.8.1",
|
||||
"kv-log-macro",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
|
@ -473,6 +486,12 @@ version = "0.2.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
|
@ -561,6 +580,12 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.0"
|
||||
|
@ -602,8 +627,10 @@ checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
|||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
|
@ -658,6 +685,17 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.11"
|
||||
|
@ -727,6 +765,15 @@ dependencies = [
|
|||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
|
@ -753,16 +800,6 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.6.0"
|
||||
|
@ -807,6 +844,33 @@ dependencies = [
|
|||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deadpool"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d126179d86aee4556e54f5f3c6bf6d9884e7cc52cef82f77ee6f90a7747616d"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"config",
|
||||
"crossbeam-queue",
|
||||
"num_cpus",
|
||||
"serde",
|
||||
"tokio 1.43.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.1"
|
||||
|
@ -1219,18 +1283,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
|
@ -1259,6 +1329,16 @@ dependencies = [
|
|||
"hmac 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840"
|
||||
dependencies = [
|
||||
"crypto-mac 0.8.0",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.10.1"
|
||||
|
@ -1269,23 +1349,13 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
|
||||
dependencies = [
|
||||
"crypto-mac 0.11.0",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
@ -1296,7 +1366,7 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"http",
|
||||
]
|
||||
|
||||
|
@ -1306,7 +1376,7 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
|
@ -1319,10 +1389,17 @@ version = "6.5.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1947510dc91e2bf586ea5ffb412caad7673264e14bb39fb9078da114a94ce1a5"
|
||||
dependencies = [
|
||||
"async-h1",
|
||||
"async-native-tls",
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"cfg-if 1.0.0",
|
||||
"dashmap",
|
||||
"deadpool",
|
||||
"futures",
|
||||
"http-types",
|
||||
"log",
|
||||
"tokio 0.2.25",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1365,7 +1442,7 @@ version = "1.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"h2",
|
||||
|
@ -1375,7 +1452,7 @@ dependencies = [
|
|||
"itoa",
|
||||
"pin-project-lite 0.2.16",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
"want",
|
||||
]
|
||||
|
||||
|
@ -1391,7 +1468,7 @@ dependencies = [
|
|||
"hyper-util",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
]
|
||||
|
@ -1402,12 +1479,12 @@ version = "0.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
]
|
||||
|
@ -1418,7 +1495,7 @@ version = "0.1.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http",
|
||||
|
@ -1426,7 +1503,7 @@ dependencies = [
|
|||
"hyper",
|
||||
"pin-project-lite 0.2.16",
|
||||
"socket2 0.5.8",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
@ -1612,7 +1689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1684,6 +1761,19 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if 1.0.0",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.170"
|
||||
|
@ -1708,6 +1798,16 @@ version = "0.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lockfree-object-pool"
|
||||
version = "0.1.6"
|
||||
|
@ -1782,6 +1882,17 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b"
|
||||
dependencies = [
|
||||
"lexical-core",
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
|
@ -1791,6 +1902,16 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
|
@ -1868,6 +1989,19 @@ version = "2.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
|
@ -2097,6 +2231,15 @@ dependencies = [
|
|||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
|
@ -2133,7 +2276,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
|
@ -2159,7 +2302,7 @@ dependencies = [
|
|||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
"tokio-native-tls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
|
@ -2187,14 +2330,10 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "routefinder"
|
||||
version = "0.4.0"
|
||||
name = "route-recognizer"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44ef95cc607e41a7021da5cfb0f357ee0805113af2e9e6c617857c260940db4"
|
||||
dependencies = [
|
||||
"smartcow",
|
||||
"smartstring",
|
||||
]
|
||||
checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
|
@ -2298,6 +2437,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
|
@ -2492,24 +2637,6 @@ version = "1.14.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
||||
|
||||
[[package]]
|
||||
name = "smartcow"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05e3ed3ccf93c7425507e5e2261a3fc90d14267d491f360b9b679ae0a4ce693e"
|
||||
dependencies = [
|
||||
"smartstring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smartstring"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e714dff2b33f2321fdcd475b71cec79781a692d846f37f415fb395a1d2bcd48e"
|
||||
dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.10"
|
||||
|
@ -2534,22 +2661,23 @@ dependencies = [
|
|||
name = "sscdc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-h1",
|
||||
"async-std",
|
||||
"camino",
|
||||
"color-eyre",
|
||||
"env_logger",
|
||||
"figment",
|
||||
"futures",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"http-client",
|
||||
"log",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_regex",
|
||||
"tide",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
"toml",
|
||||
"validator",
|
||||
"zip",
|
||||
|
@ -2844,9 +2972,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tide"
|
||||
version = "0.17.0-beta.1"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5a885fbeb66af9d607a731ce167e3fdfe65e49a68f37f4bbd8618b5efc6ad51"
|
||||
checksum = "c459573f0dd2cc734b539047f57489ea875af8ee950860ded20cf93a79a1dee0"
|
||||
dependencies = [
|
||||
"async-h1",
|
||||
"async-session",
|
||||
|
@ -2860,7 +2988,7 @@ dependencies = [
|
|||
"kv-log-macro",
|
||||
"log",
|
||||
"pin-project-lite 0.2.16",
|
||||
"routefinder",
|
||||
"route-recognizer",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
@ -2913,6 +3041,17 @@ dependencies = [
|
|||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092"
|
||||
dependencies = [
|
||||
"bytes 0.5.6",
|
||||
"pin-project-lite 0.1.12",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.43.0"
|
||||
|
@ -2920,7 +3059,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite 0.2.16",
|
||||
|
@ -2948,7 +3087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2958,7 +3097,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2967,11 +3106,11 @@ version = "0.7.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.10.0",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite 0.2.16",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3018,7 +3157,7 @@ dependencies = [
|
|||
"futures-util",
|
||||
"pin-project-lite 0.2.16",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio 1.43.0",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
|
|
@ -17,9 +17,10 @@ log = "0.4.26"
|
|||
env_logger = "0.11.6"
|
||||
serde_json = "1.0.139"
|
||||
camino = "1.1.9"
|
||||
tide = "0.17.0-beta.1"
|
||||
tide = { version = "0.16.0"}
|
||||
zip = { version = "2.2.3", default-features = false, features = ["deflate"] }
|
||||
futures = "0.3.31"
|
||||
hyper = { version = "1.6.0", features = ["client", "http1"] }
|
||||
hyper-util = { version = "0.1.10", features = ["client", "tokio"] }
|
||||
http-body-util = "0.1.2"
|
||||
async-std = { version = "1.13.0", features = ["tokio1"] }
|
||||
async-h1 = "2.3.4"
|
||||
http-client = { version = "6.5.3", features = ["async-h1", "tokio"] }
|
||||
once_cell = "1.20.3"
|
|
@ -1,7 +0,0 @@
|
|||
port = 8000
|
||||
sites_directory = "./sites"
|
||||
sockets_directory = "./sockets"
|
||||
|
||||
[scopes.moritzruth_de]
|
||||
domain_pattern = "moritzruth\\.de"
|
||||
secret = "Q9hqq3hq^^MY2^JnD23isbqFm3oXAqiHArtp`F@dNgJ4fYs`LqEzXoK%&5W%Yi9#cjdY3DA%UYc%kxCMqAwJ%K^LxsWCFH~nvyzp"
|
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
|
@ -0,0 +1 @@
|
|||
max_width = 160
|
77
src/api.rs
77
src/api.rs
|
@ -1,24 +1,73 @@
|
|||
use crate::config::Config;
|
||||
use crate::sites::SitesWorker;
|
||||
use camino::Utf8Path;
|
||||
use color_eyre::Result;
|
||||
use color_eyre::eyre::{WrapErr, eyre};
|
||||
use log::error;
|
||||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
use std::os::unix::net::UnixListener;
|
||||
use tide::Request;
|
||||
use std::str::pattern::Pattern;
|
||||
use std::sync::Arc;
|
||||
use tide::http::headers::HeaderName;
|
||||
use tide::{Request, StatusCode};
|
||||
use tokio::fs;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct State {
|
||||
sites_worker: Arc<SitesWorker>,
|
||||
config: &'static Config,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct NotifyBody {
|
||||
pub site_archive_url: String,
|
||||
pub id: String,
|
||||
pub download_url: String,
|
||||
}
|
||||
|
||||
pub async fn start_api_server(socket_path: &Utf8Path) -> Result<()> {
|
||||
fs::remove_file(&socket_path)?;
|
||||
let listener = UnixListener::bind(&socket_path)?;
|
||||
let mut server = tide::new();
|
||||
server
|
||||
.at("/notify")
|
||||
.post(|req: Request<()>| async { Ok("hey") });
|
||||
pub async fn start_api_server(
|
||||
api_socket_path: &Utf8Path,
|
||||
sites_worker: Arc<SitesWorker>,
|
||||
config: &'static Config,
|
||||
) -> color_eyre::Result<JoinHandle<color_eyre::Result<!>>> {
|
||||
let mut server = tide::with_state(State { sites_worker, config });
|
||||
|
||||
server.bind(listener).await?;
|
||||
Ok(())
|
||||
server.at("/notify").post(handle_notify);
|
||||
|
||||
server.at("/version").get(|_| async { Ok(env!("CARGO_PKG_VERSION")) });
|
||||
|
||||
let path = api_socket_path.to_path_buf().into_std_path_buf();
|
||||
fs::remove_file(&path).await?;
|
||||
|
||||
Ok(tokio::spawn(async {
|
||||
server.listen(path).await.wrap_err("IO error in API server")?;
|
||||
Err(eyre!("The API unexpectedly stopped without error."))
|
||||
}))
|
||||
}
|
||||
|
||||
async fn handle_notify(mut request: Request<State>) -> tide::Result {
|
||||
let provided_secret = request
|
||||
.header("Authorization")
|
||||
.and_then(|h| h.get(0))
|
||||
.and_then(|h| h.to_string().strip_prefix(&"Bearer ").map(|s| s.to_owned()))
|
||||
.ok_or(tide::Error::from_str(StatusCode::Unauthorized, "No secret was provided."))?;
|
||||
|
||||
let domain = request.host().unwrap().to_string();
|
||||
|
||||
let (_, scope) = request
|
||||
.state()
|
||||
.config
|
||||
.scopes
|
||||
.iter()
|
||||
.find(|(_, s)| s.secret == provided_secret)
|
||||
.ok_or(tide::Error::from_str(StatusCode::Unauthorized, "The provided secret is invalid."))?;
|
||||
|
||||
if scope.domain_pattern.is_match(&domain) {
|
||||
let body = request.body_json::<NotifyBody>().await?;
|
||||
let sites_worker = Arc::clone(&request.state().sites_worker);
|
||||
|
||||
sites_worker.set_current_version(domain, body.id, body.download_url).await.unwrap();
|
||||
|
||||
Ok(StatusCode::NoContent.into())
|
||||
} else {
|
||||
Err(tide::Error::from_str(StatusCode::Unauthorized, "The provided secret is not valid for this domain."))
|
||||
}
|
||||
}
|
||||
|
|
166
src/caddy.rs
166
src/caddy.rs
|
@ -1,102 +1,94 @@
|
|||
use async_std::os::unix::net::UnixStream;
|
||||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
use color_eyre::Result;
|
||||
use color_eyre::eyre::{OptionExt, eyre};
|
||||
use http_body_util::BodyExt;
|
||||
use hyper::{Request, StatusCode};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use http_client::http_types::{Method, StatusCode, Url};
|
||||
use http_client::{Request, Response};
|
||||
use log::debug;
|
||||
use serde_json::json;
|
||||
use std::process::Stdio;
|
||||
use std::str::FromStr;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::UnixStream;
|
||||
use tokio::process::Command;
|
||||
use tokio::time;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CaddyController {
|
||||
caddy_admin_socket_path: Utf8PathBuf,
|
||||
api_socket_path: Utf8PathBuf,
|
||||
admin_api_socket_path: Utf8PathBuf,
|
||||
}
|
||||
|
||||
impl CaddyController {
|
||||
pub async fn upsert_site_configuration(
|
||||
&self,
|
||||
domain: &String,
|
||||
content_path: &Utf8Path,
|
||||
) -> Result<()> {
|
||||
pub async fn upsert_site_configuration(&mut self, domain: &String, content_path: &Utf8Path) -> Result<()> {
|
||||
let configuration_object = json!({
|
||||
"@id": domain,
|
||||
"match": [{ "host": [domain] }],
|
||||
"handle": [
|
||||
{
|
||||
"group": "1",
|
||||
"match": [{ "path": ["/sscdc/*"] }],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "rewrite",
|
||||
"strip_path_prefix": "/_sscdc"
|
||||
},
|
||||
{
|
||||
"handler": "reverse_proxy",
|
||||
"upstreams": [{ "dial": format!("unix/{}", &self.api_socket_path) }],
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "1",
|
||||
"handle": [
|
||||
{ "handler": "vars", "root": content_path.to_string() },
|
||||
{
|
||||
"handler": "file_server",
|
||||
"precompressed": {
|
||||
"br": {},
|
||||
"gzip": {}
|
||||
"handle": [{
|
||||
"handler": "subroute",
|
||||
"routes": [
|
||||
{
|
||||
"group": "1",
|
||||
"match": [{ "path": ["/_sscdc/*"] }],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "rewrite",
|
||||
"strip_path_prefix": "/_sscdc"
|
||||
},
|
||||
"precompressed_order": ["br", "gzip"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
{
|
||||
"handler": "reverse_proxy",
|
||||
"upstreams": [{ "dial": format!("unix/{}", &self.api_socket_path) }],
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "1",
|
||||
"handle": [
|
||||
{ "handler": "vars", "root": content_path.to_string() },
|
||||
{
|
||||
"handler": "file_server",
|
||||
"precompressed": {
|
||||
"br": {},
|
||||
"gzip": {}
|
||||
},
|
||||
"precompressed_order": ["br", "gzip"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}],
|
||||
"terminal": true
|
||||
});
|
||||
|
||||
let stream = UnixStream::connect(&self.caddy_admin_socket_path).await?;
|
||||
let (mut sender, connection) =
|
||||
hyper::client::conn::http1::handshake(TokioIo::new(stream)).await?;
|
||||
|
||||
tokio::task::spawn(async move { connection.await.unwrap() });
|
||||
|
||||
let response = sender
|
||||
.send_request(
|
||||
Request::builder()
|
||||
.uri(&format!("/id/{}", domain))
|
||||
.body(configuration_object.to_string().boxed())?,
|
||||
)
|
||||
.await?;
|
||||
let mut request = Request::post(Url::from_str(&format!("http://localhost/id/{}", domain))?);
|
||||
request.insert_header("Content-Type", "application/json");
|
||||
request.set_body(configuration_object.to_string());
|
||||
let mut response = request_uds(&self.admin_api_socket_path, request.clone()).await.unwrap();
|
||||
|
||||
match response.status() {
|
||||
StatusCode::OK => {}
|
||||
StatusCode::NOT_FOUND => {
|
||||
StatusCode::Ok => {
|
||||
debug!("Caddy configuration for {domain} was updated.")
|
||||
}
|
||||
StatusCode::NotFound => {
|
||||
// The site does not yet exist.
|
||||
let response = sender
|
||||
.send_request(
|
||||
Request::builder()
|
||||
.uri("/config/apps/http/servers/srv0/routes")
|
||||
.body(configuration_object.to_string().boxed())?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
*request.url_mut() = Url::from_str("http://localhost/config/apps/http/servers/srv0/routes")?;
|
||||
let mut response = request_uds(&self.admin_api_socket_path, request).await.unwrap();
|
||||
let status = response.status();
|
||||
|
||||
if !status.is_success() {
|
||||
return Err(eyre!(
|
||||
"The configuration update request to Caddy failed with status code {}",
|
||||
status
|
||||
"The configuration update request to Caddy failed with status code {}:\n{}",
|
||||
status,
|
||||
response.body_string().await.unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
debug!("Caddy configuration for {domain} was created.")
|
||||
}
|
||||
c => {
|
||||
return Err(eyre!(
|
||||
"The configuration update request to Caddy failed with status code {}",
|
||||
c
|
||||
"The configuration update request to Caddy failed with status code {}:\n{}",
|
||||
c,
|
||||
response.body_string().await.unwrap()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -105,14 +97,13 @@ impl CaddyController {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_initial_caddy_configuration_object(
|
||||
admin_api_socket_path: &Utf8Path,
|
||||
api_socket_path: &Utf8Path,
|
||||
) -> serde_json::Value {
|
||||
fn get_initial_caddy_configuration_object(admin_api_socket_path: &Utf8Path, api_socket_path: &Utf8Path) -> serde_json::Value {
|
||||
json!({
|
||||
"admin": {
|
||||
"listen": format!("unix/{}", admin_api_socket_path),
|
||||
"config": { "persist": false }
|
||||
"enforce_origin": false,
|
||||
"origins": ["localhost"],
|
||||
"config": { "persist": false },
|
||||
},
|
||||
"logging": {
|
||||
"sink": {
|
||||
|
@ -134,7 +125,7 @@ fn get_initial_caddy_configuration_object(
|
|||
"srv0": {
|
||||
"listen": [":80"],
|
||||
"routes": [{
|
||||
"match": [{ "path": ["/sscdc/*"] }],
|
||||
"match": [{ "path": ["/_sscdc/*"] }],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "rewrite",
|
||||
|
@ -154,24 +145,23 @@ fn get_initial_caddy_configuration_object(
|
|||
})
|
||||
}
|
||||
|
||||
pub async fn start_caddy(
|
||||
api_socket_path: &Utf8Path,
|
||||
sockets_directory_path: &Utf8Path,
|
||||
) -> Result<CaddyController> {
|
||||
async fn request_uds(path: &Utf8Path, request: Request) -> http_client::http_types::Result<Response> {
|
||||
let stream = UnixStream::connect(path.as_std_path()).await?;
|
||||
async_h1::connect(stream, request).await
|
||||
}
|
||||
|
||||
pub async fn start_caddy(api_socket_path: &Utf8Path, sockets_directory_path: &Utf8Path) -> Result<CaddyController> {
|
||||
let caddy_admin_socket_path = sockets_directory_path.join("caddy-admin-api.sock");
|
||||
let process = Command::new("caddy")
|
||||
.args(&["run", "--config", "-"])
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let initial_configuration_object =
|
||||
get_initial_caddy_configuration_object(&caddy_admin_socket_path, api_socket_path);
|
||||
// Stop not properly cleaned up proxy.
|
||||
let _ = request_uds(&caddy_admin_socket_path, Request::new(Method::Post, "http://localhost/stop")).await;
|
||||
|
||||
// Spawn a new proxy.
|
||||
let process = Command::new("caddy").args(&["run", "--config", "-"]).stdin(Stdio::piped()).spawn()?;
|
||||
|
||||
let initial_configuration_object = get_initial_caddy_configuration_object(&caddy_admin_socket_path, api_socket_path);
|
||||
let initial_configuration_string = initial_configuration_object.to_string();
|
||||
log::debug!(
|
||||
"Initial caddy configuration: {}",
|
||||
initial_configuration_string
|
||||
);
|
||||
debug!("Initial caddy configuration: {}", initial_configuration_string);
|
||||
|
||||
process
|
||||
.stdin
|
||||
|
@ -179,8 +169,10 @@ pub async fn start_caddy(
|
|||
.write_all(initial_configuration_string.as_ref())
|
||||
.await?;
|
||||
|
||||
time::sleep(time::Duration::from_secs(1)).await;
|
||||
|
||||
Ok(CaddyController {
|
||||
caddy_admin_socket_path,
|
||||
api_socket_path: api_socket_path.to_path_buf(),
|
||||
admin_api_socket_path: caddy_admin_socket_path,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use figment::Figment;
|
|||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use color_eyre::eyre::WrapErr;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use validator::Validate;
|
||||
|
||||
|
@ -16,11 +17,13 @@ pub struct Config {
|
|||
pub scopes: HashMap<String, ConfigScope>
|
||||
}
|
||||
|
||||
static SECRET_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[a-zA-Z0-9]*$").unwrap());
|
||||
|
||||
#[derive(Deserialize, Debug, Validate)]
|
||||
pub struct ConfigScope {
|
||||
#[serde(with = "serde_regex")]
|
||||
pub domain_pattern: Regex,
|
||||
#[validate(length(equal = 100))]
|
||||
#[validate(length(equal = 100), regex(path = *SECRET_PATTERN))]
|
||||
pub secret: String
|
||||
}
|
||||
|
||||
|
@ -30,5 +33,6 @@ pub fn load_config() -> Result<Config> {
|
|||
.extract()
|
||||
.wrap_err("Failed to load the configuration.")?;
|
||||
|
||||
config.validate().wrap_err("Failed to validate the configuration.")?;
|
||||
Ok(config)
|
||||
}
|
24
src/main.rs
24
src/main.rs
|
@ -1,4 +1,6 @@
|
|||
#![feature(never_type)]
|
||||
#![feature(duration_constructors)]
|
||||
#![feature(pattern)]
|
||||
|
||||
use crate::api::start_api_server;
|
||||
use crate::caddy::start_caddy;
|
||||
|
@ -9,6 +11,7 @@ use color_eyre::Result;
|
|||
use color_eyre::eyre::WrapErr;
|
||||
use log::LevelFilter;
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod api;
|
||||
mod caddy;
|
||||
|
@ -18,30 +21,25 @@ mod sites;
|
|||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
env_logger::Builder::new()
|
||||
.filter_module("sscdc", LevelFilter::Info)
|
||||
.parse_default_env()
|
||||
.init();
|
||||
env_logger::Builder::new().filter_module("sscdc", LevelFilter::Info).parse_default_env().init();
|
||||
|
||||
log::info!("Loading configuration…");
|
||||
let config = load_config()?;
|
||||
|
||||
let sockets_directory_path = Utf8Path::new(&config.sockets_directory);
|
||||
fs::create_dir_all(&sockets_directory_path)
|
||||
.wrap_err("Failed to access or create the sockets directory.")?;
|
||||
fs::create_dir_all(&sockets_directory_path).wrap_err("Failed to access or create the sockets directory.")?;
|
||||
let sockets_directory_path = sockets_directory_path.canonicalize_utf8().unwrap();
|
||||
|
||||
log::info!("Starting internal API server…");
|
||||
let api_socket_path = sockets_directory_path.join("api.sock");
|
||||
start_api_server(&api_socket_path).await?;
|
||||
log::debug!("The internal API server is listening at {api_socket_path}");
|
||||
|
||||
log::info!("Starting the reverse proxy…");
|
||||
let caddy_controller = start_caddy(&api_socket_path, &sockets_directory_path).await?;
|
||||
|
||||
let sites_worker = start_sites_worker(config.sites_directory.to_string().into(), caddy_controller).await?;
|
||||
let (sites_worker, sites_worker_join_handle) = start_sites_worker(config.sites_directory.to_string().into(), caddy_controller).await?;
|
||||
|
||||
log::info!("Starting internal API server…");
|
||||
start_api_server(&api_socket_path, Arc::new(sites_worker), Box::leak(Box::new(config))).await?;
|
||||
log::debug!("The internal API server is listening at {api_socket_path}");
|
||||
|
||||
log::info!("Startup complete.");
|
||||
|
||||
sites_worker.join_handle.await??;
|
||||
sites_worker_join_handle.await??;
|
||||
}
|
||||
|
|
164
src/sites.rs
164
src/sites.rs
|
@ -1,16 +1,19 @@
|
|||
use crate::caddy::CaddyController;
|
||||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
use color_eyre::Result;
|
||||
use color_eyre::eyre::WrapErr;
|
||||
use color_eyre::eyre::{eyre, WrapErr};
|
||||
use futures::StreamExt;
|
||||
use log::{debug, info};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLockReadGuard, RwLockWriteGuard};
|
||||
use std::time::Duration;
|
||||
use tide::http::bail;
|
||||
use tokio::fs;
|
||||
use tokio::io::{AsyncWriteExt, BufWriter};
|
||||
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tokio::sync::{RwLock};
|
||||
use tokio::task::JoinHandle;
|
||||
use futures::StreamExt;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SiteState {
|
||||
|
@ -25,24 +28,20 @@ impl SiteState {
|
|||
}
|
||||
|
||||
async fn write_to_site_directory(&self, site_directory_path: &Utf8Path) -> Result<()> {
|
||||
fs::write(
|
||||
site_directory_path.join("site.toml"),
|
||||
toml::to_string(&self).unwrap(),
|
||||
)
|
||||
.await?;
|
||||
fs::create_dir_all(site_directory_path).await?;
|
||||
let mut file = fs::File::create(site_directory_path.join("site.toml")).await?;
|
||||
file.write_all(toml::to_string(&self).unwrap().as_bytes()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_outdated(&self) -> bool {
|
||||
self.get_current_version_if_outdated().is_some()
|
||||
}
|
||||
|
||||
fn get_current_version_if_outdated(&self) -> Option<&SiteStateVersion> {
|
||||
if let Some(current_version) = &self.current_version {
|
||||
if let Some(active_version) = &self.active_version {
|
||||
if current_version.id != active_version.id {
|
||||
return Some(&active_version)
|
||||
return Some(current_version);
|
||||
}
|
||||
} else {
|
||||
return Some(current_version)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,25 +67,21 @@ pub struct SitesWorker {
|
|||
sites: Arc<Sites>,
|
||||
sites_directory_path: Utf8PathBuf,
|
||||
download_tasks_sender: UnboundedSender<String>,
|
||||
pub join_handle: JoinHandle<Result<!>>,
|
||||
}
|
||||
|
||||
impl SitesWorker {
|
||||
pub async fn set_current_version(
|
||||
&self,
|
||||
domain: String,
|
||||
id: String,
|
||||
download_url: String,
|
||||
) -> Result<()> {
|
||||
pub async fn set_current_version(&self, domain: String, id: String, download_url: String) -> Result<()> {
|
||||
let mut sites = self.sites.write().await;
|
||||
let site = sites.entry(domain.clone()).or_insert_with(|| Arc::new(Site {
|
||||
domain: domain.clone(),
|
||||
path: self.sites_directory_path.join(&domain),
|
||||
state: RwLock::new(SiteState {
|
||||
current_version: None,
|
||||
active_version: None,
|
||||
}),
|
||||
}));
|
||||
let site = sites.entry(domain.clone()).or_insert_with(|| {
|
||||
Arc::new(Site {
|
||||
domain: domain.clone(),
|
||||
path: self.sites_directory_path.join(&domain),
|
||||
state: RwLock::new(SiteState {
|
||||
current_version: None,
|
||||
active_version: None,
|
||||
}),
|
||||
})
|
||||
});
|
||||
|
||||
let mut state = site.state.write().await;
|
||||
state.current_version = Some(SiteStateVersion { id, download_url });
|
||||
|
@ -131,35 +126,58 @@ pub async fn load_sites(sites_directory_path: &Utf8Path) -> Result<Sites> {
|
|||
Ok(RwLock::new(sites))
|
||||
}
|
||||
|
||||
async fn handle_download(sites: &Sites, caddy_controller: &CaddyController, domain: String) -> Result<()> {
|
||||
async fn handle_download(reqwest_client: &mut reqwest::Client, sites: &Sites, caddy_controller: &mut CaddyController, domain: String) -> Result<()> {
|
||||
let site = {
|
||||
let sites = sites.read().await;
|
||||
match sites.get(&domain) {
|
||||
None => return Ok(()),
|
||||
Some(a) => Arc::clone(a)
|
||||
None => {
|
||||
debug!("Skipping download for {domain} because it is no longer managed.");
|
||||
return Ok(());
|
||||
}
|
||||
Some(a) => Arc::clone(a),
|
||||
}
|
||||
};
|
||||
|
||||
let mut state = site.state.write().await;
|
||||
let current_version = match state.get_current_version_if_outdated() {
|
||||
None => return Ok(()),
|
||||
Some(v) => v
|
||||
None => {
|
||||
debug!("Skipping download for {domain} because it is not outdated.");
|
||||
return Ok(());
|
||||
}
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
info!("Starting download for {domain} ({}).", current_version.id);
|
||||
|
||||
// Download
|
||||
let mut archive_file = fs::File::create(site.path.join(format!("{}.zip", current_version.id))).await?;
|
||||
let archive_file_path = site.path.join(format!("{}.zip", current_version.id));
|
||||
let mut archive_file = fs::OpenOptions::new()
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(&archive_file_path).await?;
|
||||
|
||||
let mut file_writer = BufWriter::new(&mut archive_file);
|
||||
|
||||
let mut response = reqwest::get(¤t_version.download_url).await?;
|
||||
let mut response = reqwest_client.get(¤t_version.download_url).send().await?;
|
||||
let status = response.status();
|
||||
if !status.is_success() {
|
||||
return Err(eyre!("Download request failed with status code {status}"))
|
||||
}
|
||||
|
||||
while let Some(chunk) = response.chunk().await? {
|
||||
file_writer.write_all(&chunk).await?;
|
||||
}
|
||||
|
||||
file_writer.flush().await?;
|
||||
|
||||
let extraction_directory_path = site.path.join(¤t_version.id);
|
||||
let _ = fs::remove_dir_all(&extraction_directory_path).await;
|
||||
fs::create_dir_all(&extraction_directory_path).await?;
|
||||
|
||||
debug!("Finished download for {domain} ({}), now unpacking…", current_version.id);
|
||||
|
||||
// Unpack to temp dir
|
||||
tokio::task::spawn_blocking({
|
||||
let archive_file = archive_file.into_std().await;
|
||||
|
@ -171,63 +189,81 @@ async fn handle_download(sites: &Sites, caddy_controller: &CaddyController, doma
|
|||
|
||||
Ok(())
|
||||
}
|
||||
}).await??;
|
||||
})
|
||||
.await??;
|
||||
|
||||
// Update and write state
|
||||
state.active_version = Some(current_version.clone());
|
||||
let current_version = current_version.clone();
|
||||
let old_active_version = state.active_version.replace(current_version);
|
||||
state.write_to_site_directory(&site.path).await?;
|
||||
|
||||
// Update Caddy configuration
|
||||
info!("Download for {domain} successful, now updating proxy configuration…");
|
||||
caddy_controller.upsert_site_configuration(&domain, &extraction_directory_path).await?;
|
||||
|
||||
// Cleanup
|
||||
fs::remove_file(archive_file_path).await?;
|
||||
if let Some(old_active_version) = old_active_version {
|
||||
fs::remove_dir_all(site.path.join(old_active_version.id)).await?;
|
||||
}
|
||||
|
||||
info!("Cleanup finished for {domain}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn sites_worker(
|
||||
mut download_tasks_receiver: UnboundedReceiver<String>,
|
||||
sites: Arc<Sites>,
|
||||
caddy_controller: CaddyController,
|
||||
) -> Result<!> {
|
||||
async fn sites_worker(mut download_tasks_receiver: UnboundedReceiver<String>, sites: Arc<Sites>, mut caddy_controller: CaddyController) -> Result<!> {
|
||||
let mut client = reqwest::Client::builder()
|
||||
.read_timeout(Duration::from_secs(60))
|
||||
.timeout(Duration::from_hours(1))
|
||||
.build()?;
|
||||
|
||||
loop {
|
||||
let domain = download_tasks_receiver.recv().await.unwrap();
|
||||
handle_download(&sites, &caddy_controller, domain).await?;
|
||||
handle_download(&mut client, &sites, &mut caddy_controller, domain).await?;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_sites_worker(
|
||||
sites_directory_path: Utf8PathBuf,
|
||||
caddy_controller: CaddyController,
|
||||
) -> Result<SitesWorker> {
|
||||
pub async fn start_sites_worker(sites_directory_path: Utf8PathBuf, mut caddy_controller: CaddyController) -> Result<(SitesWorker, JoinHandle<Result<!>>)> {
|
||||
let (download_tasks_sender, download_tasks_receiver) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
info!("Discovering managed sites…");
|
||||
let sites = Arc::new(load_sites(&sites_directory_path).await?);
|
||||
let mut total_count = 0;
|
||||
let mut outdated_count = 0;
|
||||
|
||||
for (_, site) in sites.read().await.iter() {
|
||||
let state = site.state.read().await;
|
||||
|
||||
if let Some(current_version) = &state.current_version {
|
||||
let mut is_outdated = true;
|
||||
total_count += 1;
|
||||
|
||||
if let Some(active_version) = &state.active_version {
|
||||
if current_version.id != active_version.id {
|
||||
download_tasks_sender.send(site.domain.clone()).unwrap()
|
||||
}
|
||||
is_outdated = current_version.id != active_version.id;
|
||||
|
||||
caddy_controller.upsert_site_configuration(&site.domain, &site.path.join(&active_version.id)).await?;
|
||||
caddy_controller
|
||||
.upsert_site_configuration(&site.domain, &site.path.join(&active_version.id))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
if state.is_outdated() {
|
||||
download_tasks_sender.send(site.domain.clone()).unwrap()
|
||||
if is_outdated {
|
||||
outdated_count += 1;
|
||||
download_tasks_sender.send(site.domain.clone()).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let join_handle = tokio::spawn(sites_worker(
|
||||
download_tasks_receiver,
|
||||
Arc::clone(&sites),
|
||||
caddy_controller,
|
||||
));
|
||||
info!("Discovered {total_count} site(s), {outdated_count} outdated.");
|
||||
|
||||
Ok(SitesWorker {
|
||||
sites,
|
||||
sites_directory_path,
|
||||
download_tasks_sender,
|
||||
let join_handle = tokio::spawn(sites_worker(download_tasks_receiver, Arc::clone(&sites), caddy_controller));
|
||||
|
||||
Ok((
|
||||
SitesWorker {
|
||||
sites,
|
||||
sites_directory_path,
|
||||
download_tasks_sender,
|
||||
},
|
||||
join_handle,
|
||||
})
|
||||
))
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue