diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 6a83c511f9..3e7b177d68 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -24,6 +24,7 @@ dependencies = [ "agama-lib", "agama-settings", "anyhow", + "async-trait", "clap", "console", "convert_case", @@ -48,19 +49,29 @@ dependencies = [ "agama-locale-data", "anyhow", "async-trait", + "axum", "cidr", + "clap", "gettext-rs", + "http-body-util", "log", "macaddr", "once_cell", "regex", "serde", + "serde_json", "serde_yaml", "simplelog", "systemd-journal-logger", "thiserror", "tokio", "tokio-stream", + "tower", + "tower-http", + "tracing", + "tracing-journald", + "tracing-subscriber", + "utoipa", "uuid", "zbus", "zbus_macros", @@ -72,7 +83,7 @@ version = "1.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] @@ -81,6 +92,7 @@ version = "1.0.0" dependencies = [ "agama-settings", "anyhow", + "async-trait", "cidr", "curl", "futures-util", @@ -142,9 +154,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -301,7 +313,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] @@ -330,13 +342,13 @@ checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" [[package]] name = "async-trait" -version = "0.1.75" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] @@ -351,6 +363,64 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" +dependencies = [ + "async-trait", + "axum-core", + "base64 0.21.7", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -372,6 +442,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "bit-set" version = "0.5.3" @@ -505,9 +581,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.11" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", "clap_derive", @@ -515,9 +591,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstream", "anstyle", @@ -528,21 +604,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" @@ -648,6 +724,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + [[package]] name = "deranged" version = "0.3.10" @@ -703,7 +785,7 @@ checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] @@ -795,6 +877,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -820,6 +908,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.29" @@ -865,7 +962,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] @@ -942,6 +1039,25 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "h2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -975,6 +1091,87 @@ dependencies = [ "digest", ] +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2 0.5.5", + "tokio", +] + [[package]] name = "idna" version = "0.5.0" @@ -993,6 +1190,7 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", + "serde", ] [[package]] @@ -1051,7 +1249,7 @@ checksum = "6ca9e2b45609132ae2214d50482c03aeee78826cd6fd53a8940915b81acedf16" dependencies = [ "ahash", "anyhow", - "base64", + "base64 0.13.1", "bytecount", "fancy-regex", "fraction", @@ -1171,6 +1369,12 @@ dependencies = [ "libc", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.6.4" @@ -1195,6 +1399,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1254,6 +1464,16 @@ dependencies = [ "minimal-lexical", ] +[[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]] name = "num" version = "0.4.1" @@ -1433,6 +1653,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking" version = "2.2.0" @@ -1515,6 +1741,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1602,11 +1848,35 @@ dependencies = [ "toml_edit", ] +[[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 1.0.109", + "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.70" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -1623,9 +1893,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1731,6 +2001,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.16" @@ -1754,35 +2030,45 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.17" @@ -1791,7 +2077,19 @@ checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", ] [[package]] @@ -1829,6 +2127,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1898,9 +2205,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "subtle" @@ -1921,15 +2228,21 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "systemd-journal-logger" version = "1.0.0" @@ -1995,7 +2308,17 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", +] + +[[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]] @@ -2071,7 +2394,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] @@ -2085,6 +2408,32 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml_datetime" version = "0.6.5" @@ -2102,12 +2451,58 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da193277a4e2c33e59e09b5861580c33dd0a637c3883d0fa74ba40c0374af2e" +dependencies = [ + "bitflags 2.4.1", + "bytes", + "http", + "http-body", + "http-body-util", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2121,7 +2516,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] @@ -2131,6 +2526,62 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-journald" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba316a74e8fc3c3896a850dba2375928a9fa171b085ecddfc7c054d39970f3fd" +dependencies = [ + "libc", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", ] [[package]] @@ -2200,12 +2651,43 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "utoipa" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "272ebdfbc99111033031d2f10e018836056e4d2c8e2acda76450ec7974269fa7" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c9f4d08338c1bfa70dde39412a040a884c6f318b3d09aaaf3437a1e52027fc" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn 2.0.48", +] + [[package]] name = "uuid" version = "1.6.1" @@ -2216,6 +2698,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "value-bag" version = "1.4.2" @@ -2572,7 +3060,7 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] diff --git a/rust/agama-cli/Cargo.toml b/rust/agama-cli/Cargo.toml index d649ba3693..51873d5e41 100644 --- a/rust/agama-cli/Cargo.toml +++ b/rust/agama-cli/Cargo.toml @@ -24,6 +24,7 @@ fs_extra = "1.3.0" nix = { version = "0.27.1", features = ["user"] } zbus = { version = "3", default-features = false, features = ["tokio"] } tokio = { version = "1.33.0", features = ["macros", "rt-multi-thread"] } +async-trait = "0.1.77" [[bin]] name = "agama" diff --git a/rust/agama-cli/src/progress.rs b/rust/agama-cli/src/progress.rs index 4694f0da59..e4c5c64aa2 100644 --- a/rust/agama-cli/src/progress.rs +++ b/rust/agama-cli/src/progress.rs @@ -1,4 +1,5 @@ use agama_lib::progress::{Progress, ProgressPresenter}; +use async_trait::async_trait; use console::style; use indicatif::{ProgressBar, ProgressStyle}; use std::time::Duration; @@ -26,14 +27,15 @@ impl InstallerProgress { } } +#[async_trait] impl ProgressPresenter for InstallerProgress { - fn start(&mut self, progress: &Progress) { + async fn start(&mut self, progress: &Progress) { if !progress.finished { - self.update_main(progress); + self.update_main(progress).await; } } - fn update_main(&mut self, progress: &Progress) { + async fn update_main(&mut self, progress: &Progress) { let counter = format!("[{}/{}]", &progress.current_step, &progress.max_steps); println!( @@ -43,7 +45,7 @@ impl ProgressPresenter for InstallerProgress { ); } - fn update_detail(&mut self, progress: &Progress) { + async fn update_detail(&mut self, progress: &Progress) { if progress.finished { if let Some(bar) = self.bar.take() { bar.finish_and_clear(); @@ -53,7 +55,7 @@ impl ProgressPresenter for InstallerProgress { } } - fn finish(&mut self) { + async fn finish(&mut self) { if let Some(bar) = self.bar.take() { bar.finish_and_clear(); } diff --git a/rust/agama-dbus-server/Cargo.toml b/rust/agama-dbus-server/Cargo.toml index 6877d621f4..326394ffc9 100644 --- a/rust/agama-dbus-server/Cargo.toml +++ b/rust/agama-dbus-server/Cargo.toml @@ -27,3 +27,23 @@ regex = "1.10.2" once_cell = "1.18.0" macaddr = "1.0" async-trait = "0.1.75" +axum = { version = "0.7.4", features = ["ws"] } +serde_json = "1.0.113" +tower-http = { version = "0.5.1", features = ["trace"] } +tracing-subscriber = "0.3.18" +tracing-journald = "0.3.0" +tracing = "0.1.40" +clap = { version = "4.5.0", features = ["derive", "wrap_help"] } +tower = "0.4.13" +utoipa = { version = "4.2.0", features = ["axum_extras"] } + +[[bin]] +name = "agama-dbus-server" +path = "src/agama-dbus-server.rs" + +[[bin]] +name = "agama-web-server" +path = "src/agama-web-server.rs" + +[dev-dependencies] +http-body-util = "0.1.0" diff --git a/rust/agama-dbus-server/src/main.rs b/rust/agama-dbus-server/src/agama-dbus-server.rs similarity index 100% rename from rust/agama-dbus-server/src/main.rs rename to rust/agama-dbus-server/src/agama-dbus-server.rs diff --git a/rust/agama-dbus-server/src/agama-web-server.rs b/rust/agama-dbus-server/src/agama-web-server.rs new file mode 100644 index 0000000000..4e45bfacfd --- /dev/null +++ b/rust/agama-dbus-server/src/agama-web-server.rs @@ -0,0 +1,57 @@ +use agama_dbus_server::web; +use agama_lib::connection; +use clap::{Parser, Subcommand}; +use tracing_subscriber::prelude::*; +use utoipa::OpenApi; + +#[derive(Subcommand, Debug)] +enum Commands { + /// Start the API server. + Serve { + /// Address to listen on (default: "0.0.0.0:3000") + #[arg(long, default_value = "0.0.0.0:3000")] + address: String, + }, + /// Display the API documentation in OpenAPI format. + Openapi, +} + +#[derive(Parser, Debug)] +#[command( + version, + about = "Starts the Agama web-based API.", + long_about = None)] +struct Cli { + #[command(subcommand)] + pub command: Commands, +} + +/// Start serving the API. +async fn serve_command(address: &str) { + let journald = tracing_journald::layer().expect("could not connect to journald"); + tracing_subscriber::registry().with(journald).init(); + + let listener = tokio::net::TcpListener::bind(address) + .await + .unwrap_or_else(|_| panic!("could not listen on {}", address)); + + let dbus_connection = connection().await.unwrap(); + axum::serve(listener, web::service(dbus_connection)) + .await + .expect("could not mount app on listener"); +} + +/// Display the API documentation in OpenAPI format. +fn openapi_command() { + println!("{}", web::ApiDoc::openapi().to_pretty_json().unwrap()); +} + +#[tokio::main] +async fn main() { + let cli = Cli::parse(); + + match cli.command { + Commands::Serve { address } => serve_command(&address).await, + Commands::Openapi => openapi_command(), + } +} diff --git a/rust/agama-dbus-server/src/lib.rs b/rust/agama-dbus-server/src/lib.rs index 5c04239e62..421b9e3efd 100644 --- a/rust/agama-dbus-server/src/lib.rs +++ b/rust/agama-dbus-server/src/lib.rs @@ -2,3 +2,5 @@ pub mod error; pub mod l10n; pub mod network; pub mod questions; +pub mod web; +pub use web::service; diff --git a/rust/agama-dbus-server/src/web.rs b/rust/agama-dbus-server/src/web.rs new file mode 100644 index 0000000000..cfc44ef4d4 --- /dev/null +++ b/rust/agama-dbus-server/src/web.rs @@ -0,0 +1,13 @@ +//! This module implements a web-based API for Agama. It is responsible for: +//! +//! * Exposing an HTTP API to interact with Agama. +//! * Emit relevant events via websocket. +//! * Serve the code for the web user interface (not implemented yet). + +mod docs; +mod http; +mod service; +mod ws; + +pub use docs::ApiDoc; +pub use service::service; diff --git a/rust/agama-dbus-server/src/web/docs.rs b/rust/agama-dbus-server/src/web/docs.rs new file mode 100644 index 0000000000..a45465aa44 --- /dev/null +++ b/rust/agama-dbus-server/src/web/docs.rs @@ -0,0 +1,9 @@ +use utoipa::OpenApi; + +#[derive(OpenApi)] +#[openapi( + info(description = "Agama web API description"), + paths(super::http::ping), + components(schemas(super::http::PingResponse)) +)] +pub struct ApiDoc; diff --git a/rust/agama-dbus-server/src/web/http.rs b/rust/agama-dbus-server/src/web/http.rs new file mode 100644 index 0000000000..9635638bb9 --- /dev/null +++ b/rust/agama-dbus-server/src/web/http.rs @@ -0,0 +1,20 @@ +//! Implements the handlers for the HTTP-based API. + +use axum::Json; +use serde::Serialize; +use utoipa::ToSchema; + +#[derive(Serialize, ToSchema)] +pub struct PingResponse { + /// API status + status: String, +} + +#[utoipa::path(get, path = "/ping", responses( + (status = 200, description = "The API is working", body = PingResponse) +))] +pub async fn ping() -> Json { + Json(PingResponse { + status: "success".to_string(), + }) +} diff --git a/rust/agama-dbus-server/src/web/service.rs b/rust/agama-dbus-server/src/web/service.rs new file mode 100644 index 0000000000..01dac81b28 --- /dev/null +++ b/rust/agama-dbus-server/src/web/service.rs @@ -0,0 +1,17 @@ +use axum::{routing::get, Router}; +use tower_http::trace::TraceLayer; + +/// Returns a service that implements the web-based Agama API. +pub fn service(dbus_connection: zbus::Connection) -> Router { + let state = ServiceState { dbus_connection }; + Router::new() + .route("/ping", get(super::http::ping)) + .route("/ws", get(super::ws::ws_handler)) + .layer(TraceLayer::new_for_http()) + .with_state(state) +} + +#[derive(Clone)] +pub struct ServiceState { + pub dbus_connection: zbus::Connection, +} diff --git a/rust/agama-dbus-server/src/web/ws.rs b/rust/agama-dbus-server/src/web/ws.rs new file mode 100644 index 0000000000..c1d3cb2770 --- /dev/null +++ b/rust/agama-dbus-server/src/web/ws.rs @@ -0,0 +1,56 @@ +//! Implements the websocket handling. + +use super::service::ServiceState; +use agama_lib::progress::{Progress, ProgressMonitor, ProgressPresenter}; +use async_trait::async_trait; +use axum::{ + extract::{ + ws::{Message, WebSocket}, + State, WebSocketUpgrade, + }, + response::IntoResponse, +}; + +pub async fn ws_handler( + State(state): State, + ws: WebSocketUpgrade, +) -> impl IntoResponse { + ws.on_upgrade(move |socket| handle_socket(socket, state.dbus_connection)) +} + +async fn handle_socket(socket: WebSocket, connection: zbus::Connection) { + let presenter = WebSocketProgressPresenter::new(socket); + let mut monitor = ProgressMonitor::new(connection).await.unwrap(); + _ = monitor.run(presenter).await; +} + +/// Experimental ProgressPresenter to emit progress events over a WebSocket. +struct WebSocketProgressPresenter(WebSocket); + +impl WebSocketProgressPresenter { + pub fn new(socket: WebSocket) -> Self { + Self(socket) + } + + pub async fn report_progress(&mut self, progress: &Progress) { + let payload = serde_json::to_string(&progress).unwrap(); + _ = self.0.send(Message::Text(payload)).await; + } +} + +#[async_trait] +impl ProgressPresenter for WebSocketProgressPresenter { + async fn start(&mut self, progress: &Progress) { + self.report_progress(progress).await; + } + + async fn update_main(&mut self, progress: &Progress) { + self.report_progress(progress).await; + } + + async fn update_detail(&mut self, progress: &Progress) { + self.report_progress(progress).await; + } + + async fn finish(&mut self) {} +} diff --git a/rust/agama-dbus-server/tests/service.rs b/rust/agama-dbus-server/tests/service.rs new file mode 100644 index 0000000000..93e60e42fe --- /dev/null +++ b/rust/agama-dbus-server/tests/service.rs @@ -0,0 +1,31 @@ +mod common; + +use self::common::DBusServer; +use agama_dbus_server::service; +use axum::{ + body::Body, + http::{Request, StatusCode}, +}; +use http_body_util::BodyExt; +use std::error::Error; +use tokio::test; +use tower::ServiceExt; + +async fn body_to_string(body: Body) -> String { + let bytes = body.collect().await.unwrap().to_bytes(); + String::from_utf8(bytes.to_vec()).unwrap() +} + +#[test] +async fn test_ping() -> Result<(), Box> { + let dbus_server = DBusServer::new().start().await?; + let web_server = service(dbus_server.connection()); + let request = Request::builder().uri("/ping").body(Body::empty()).unwrap(); + + let response = web_server.oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + + let body = body_to_string(response.into_body()).await; + assert_eq!(&body, "{\"status\":\"success\"}"); + Ok(()) +} diff --git a/rust/agama-lib/Cargo.toml b/rust/agama-lib/Cargo.toml index bd386984a0..5ef8166609 100644 --- a/rust/agama-lib/Cargo.toml +++ b/rust/agama-lib/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] agama-settings = { path="../agama-settings" } anyhow = "1.0" +async-trait = "0.1.77" cidr = { version = "0.2.2", features = ["serde"] } curl = { version = "0.4.44", features = ["protocol-ftp"] } futures-util = "0.3.29" diff --git a/rust/agama-lib/src/progress.rs b/rust/agama-lib/src/progress.rs index 9f872e1f99..0e3b1a7237 100644 --- a/rust/agama-lib/src/progress.rs +++ b/rust/agama-lib/src/progress.rs @@ -6,6 +6,7 @@ //! //! ```no_run //! # use agama_lib::progress::{Progress, ProgressMonitor, ProgressPresenter}; +//! # use async_trait::async_trait; //! # use tokio::{runtime::Handle, task}; //! # use zbus; //! @@ -18,21 +19,22 @@ //! } //! } //! +//! #[async_trait] //! impl ProgressPresenter for SimplePresenter { -//! fn start(&mut self, progress: &Progress) { +//! async fn start(&mut self, progress: &Progress) { //! println!("Starting..."); //! self.report_progress(progress); //! } //! -//! fn update_main(&mut self, progress: &Progress) { +//! async fn update_main(&mut self, progress: &Progress) { //! self.report_progress(progress); //! } //! -//! fn update_detail(&mut self, progress: &Progress) { +//! async fn update_detail(&mut self, progress: &Progress) { //! self.report_progress(progress); //! } //! -//! fn finish(&mut self) { +//! async fn finish(&mut self) { //! println!("Done"); //! } //! } @@ -40,17 +42,18 @@ //! async fn run_monitor() { //! let connection = zbus::Connection::system().await.unwrap(); //! let mut monitor = ProgressMonitor::new(connection).await.unwrap(); -//! monitor.run(SimplePresenter {}); +//! monitor.run(SimplePresenter {}).await; //!} //! ``` -use crate::error::ServiceError; -use crate::proxies::ProgressProxy; +use crate::{error::ServiceError, proxies::ProgressProxy}; +use async_trait::async_trait; +use serde::Serialize; use tokio_stream::{StreamExt, StreamMap}; use zbus::Connection; /// Represents the progress for an Agama service. -#[derive(Default, Debug)] +#[derive(Default, Debug, Serialize)] pub struct Progress { /// Current step pub current_step: u32, @@ -109,7 +112,7 @@ impl<'a> ProgressMonitor<'a> { /// Runs the monitor until the current operation finishes. pub async fn run(&mut self, mut presenter: impl ProgressPresenter) -> Result<(), ServiceError> { - presenter.start(&self.main_progress().await); + presenter.start(&self.main_progress().await).await; let mut changes = self.build_stream().await; while let Some(stream) = changes.next().await { @@ -117,14 +120,15 @@ impl<'a> ProgressMonitor<'a> { ("/org/opensuse/Agama/Manager1", _) => { let progress = self.main_progress().await; if progress.finished { - presenter.finish(); + presenter.finish().await; return Ok(()); } else { - presenter.update_main(&progress); + presenter.update_main(&progress).await; } } ("/org/opensuse/Agama/Software1", _) => { - presenter.update_detail(&self.detail_progress().await) + let progress = &self.detail_progress().await; + presenter.update_detail(progress).await; } _ => eprintln!("Unknown"), }; @@ -161,22 +165,23 @@ impl<'a> ProgressMonitor<'a> { } /// Presents the progress to the user. +#[async_trait] pub trait ProgressPresenter { /// Starts the progress reporting. /// /// * `progress`: current main progress. - fn start(&mut self, progress: &Progress); + async fn start(&mut self, progress: &Progress); /// Updates the progress. /// /// * `progress`: current progress. - fn update_main(&mut self, progress: &Progress); + async fn update_main(&mut self, progress: &Progress); /// Updates the progress detail. /// /// * `progress`: current progress detail. - fn update_detail(&mut self, progress: &Progress); + async fn update_detail(&mut self, progress: &Progress); /// Finishes the progress reporting. - fn finish(&mut self); + async fn finish(&mut self); } diff --git a/rust/package/agama-cli.spec b/rust/package/agama-cli.spec index d234ac3a01..9a6fdd25ea 100644 --- a/rust/package/agama-cli.spec +++ b/rust/package/agama-cli.spec @@ -58,7 +58,19 @@ Requires: python-langtable-data Requires: dbus-1-common %description -n agama-dbus-server -DBus service for agama project. It provides so far localization service. +D-Bus service for agama project. It provides localization, networking and questions services. + +%package -n agama-web-server +# This will be set by osc services, that will run after this. +Version: 0 +Release: 0 +Summary: Agama web server +License: GPL-2.0-only +Url: https://github.com/opensuse/agama +Requires: agama-dbus-server + +%description -n agama-web-server +Agama project web server. It provides a web-based API to Agama. %prep %autosetup -a1 -n agama @@ -72,6 +84,7 @@ DBus service for agama project. It provides so far localization service. install -D -d -m 0755 %{buildroot}%{_bindir} install -m 0755 %{_builddir}/agama/target/release/agama %{buildroot}%{_bindir}/agama install -m 0755 %{_builddir}/agama/target/release/agama-dbus-server %{buildroot}%{_bindir}/agama-dbus-server +install -m 0755 %{_builddir}/agama/target/release/agama-web-server %{buildroot}%{_bindir}/agama-web-server install -D -d -m 0755 %{buildroot}%{_datadir}/agama-cli install -m 0644 %{_builddir}/agama/agama-lib/share/profile.schema.json %{buildroot}%{_datadir}/agama-cli install --directory %{buildroot}%{_datadir}/dbus-1/agama-services @@ -94,4 +107,7 @@ install -m 0644 --target-directory=%{buildroot}%{_datadir}/dbus-1/agama-services %{_bindir}/agama-dbus-server %{_datadir}/dbus-1/agama-services +%files -n agama-web-server +%{_bindir}/agama-web-server + %changelog