diff options
-rw-r--r-- | pkgs/by-name/ba/back/Cargo.lock | 973 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/Cargo.toml | 10 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/contrib/config.json | 5 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/config/mod.rs | 110 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/error/mod.rs | 66 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/error/responder.rs | 23 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/git_bug/dag/mod.rs | 14 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/git_bug/format/mod.rs | 15 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/git_bug/issue/mod.rs | 2 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/main.rs | 33 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/web/generate/mod.rs | 227 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/web/issue_html.rs | 166 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/web/mod.rs | 297 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/web/prefix.rs | 35 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/web/responses.rs | 50 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/templates/issue.html | 57 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/templates/issues.html | 60 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/templates/repos.html | 47 |
18 files changed, 935 insertions, 1255 deletions
diff --git a/pkgs/by-name/ba/back/Cargo.lock b/pkgs/by-name/ba/back/Cargo.lock index 894a927..228a22f 100644 --- a/pkgs/by-name/ba/back/Cargo.lock +++ b/pkgs/by-name/ba/back/Cargo.lock @@ -132,39 +132,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "atom_syndication" version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -178,19 +145,10 @@ dependencies = [ ] [[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic" -version = "0.6.0" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" -dependencies = [ - "bytemuck", -] +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" @@ -202,16 +160,24 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" name = "back" version = "0.1.0" dependencies = [ + "bytes", "chrono", "clap", "gix", + "http", + "http-body-util", + "hyper", + "hyper-util", + "log", "markdown", - "rocket", + "rinja", "rss", "serde", "serde_json", "sha2", + "stderrlog", "thiserror", + "tokio", "url", ] @@ -227,14 +193,17 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "binascii" -version = "0.1.4" +name = "basic-toml" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] [[package]] name = "bitflags" @@ -258,7 +227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", - "regex-automata 0.4.9", + "regex-automata", "serde", ] @@ -269,18 +238,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] -name = "bytemuck" -version = "1.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -374,17 +331,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -483,15 +429,6 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[package]] name = "derive_builder" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -523,39 +460,6 @@ dependencies = [ ] [[package]] -name = "devise" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d90b0c4c777a2cad215e3c7be59ac7c15adf45cf76317009b7d096d46f651d" -dependencies = [ - "devise_codegen", - "devise_core", -] - -[[package]] -name = "devise_codegen" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867" -dependencies = [ - "devise_core", - "quote", -] - -[[package]] -name = "devise_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" -dependencies = [ - "bitflags", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", -] - -[[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -592,12 +496,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] name = "encoding_rs" version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -638,20 +536,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "figment" -version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" -dependencies = [ - "atomic 0.6.0", - "pear", - "serde", - "toml", - "uncased", - "version_check", -] - -[[package]] name = "filetime" version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -695,27 +579,12 @@ dependencies = [ ] [[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -725,12 +594,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -748,28 +611,10 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "futures-channel", "futures-core", - "futures-io", - "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", - "slab", -] - -[[package]] -name = "generator" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows", ] [[package]] @@ -784,17 +629,6 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" @@ -802,7 +636,7 @@ dependencies = [ "cfg-if", "libc", "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -882,7 +716,7 @@ dependencies = [ "gix-utils", "itoa", "thiserror", - "winnow 0.6.26", + "winnow", ] [[package]] @@ -978,7 +812,7 @@ dependencies = [ "smallvec", "thiserror", "unicode-bom", - "winnow 0.6.26", + "winnow", ] [[package]] @@ -1269,7 +1103,7 @@ dependencies = [ "itoa", "smallvec", "thiserror", - "winnow 0.6.26", + "winnow", ] [[package]] @@ -1393,7 +1227,7 @@ dependencies = [ "gix-utils", "maybe-async", "thiserror", - "winnow 0.6.26", + "winnow", ] [[package]] @@ -1425,7 +1259,7 @@ dependencies = [ "gix-validate", "memmap2", "thiserror", - "winnow 0.6.26", + "winnow", ] [[package]] @@ -1685,23 +1519,17 @@ dependencies = [ ] [[package]] -name = "glob" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - -[[package]] name = "h2" -version = "0.3.26" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", - "http 0.2.12", + "http", "indexmap", "slab", "tokio", @@ -1736,12 +1564,6 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" @@ -1757,9 +1579,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.12" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -1767,24 +1589,25 @@ dependencies = [ ] [[package]] -name = "http" -version = "1.2.0" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "fnv", - "itoa", + "http", ] [[package]] -name = "http-body" -version = "0.4.6" +name = "http-body-util" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "http 0.2.12", + "futures-util", + "http", + "http-body", "pin-project-lite", ] @@ -1807,27 +1630,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3b1f728c459d27b12448862017b96ad4767b1ec2ec5e6434e99f1577f085b8" [[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] name = "hyper" -version = "0.14.32" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", - "http 0.2.12", + "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", "tokio", - "tower-service", - "tracing", - "want", ] [[package]] @@ -2015,16 +1858,9 @@ checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown 0.15.2", - "serde", ] [[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - -[[package]] name = "io-close" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2040,7 +1876,7 @@ version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.5.0", + "hermit-abi", "libc", "windows-sys 0.59.0", ] @@ -2106,18 +1942,18 @@ dependencies = [ ] [[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] name = "libc" version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] name = "libredox" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2163,21 +1999,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - -[[package]] name = "markdown" version = "1.0.0-alpha.23" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2187,15 +2008,6 @@ dependencies = [ ] [[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] name = "maybe-async" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2228,6 +2040,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] name = "miniz_oxide" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2248,47 +2076,22 @@ dependencies = [ ] [[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.2.0", - "httparse", - "memchr", - "mime", - "spin", - "tokio", - "tokio-util", - "version_check", -] - -[[package]] name = "never" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" [[package]] -name = "nu-ansi-term" -version = "0.46.0" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "overload", - "winapi", + "memchr", + "minimal-lexical", ] [[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2298,16 +2101,6 @@ dependencies = [ ] [[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.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2323,12 +2116,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2348,30 +2135,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", + "windows-targets", ] [[package]] @@ -2408,21 +2172,6 @@ dependencies = [ ] [[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] name = "proc-macro2" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2432,19 +2181,6 @@ dependencies = [ ] [[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "version_check", - "yansi", -] - -[[package]] name = "prodash" version = "29.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2476,36 +2212,6 @@ dependencies = [ ] [[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.15", -] - -[[package]] name = "redox_syscall" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2515,26 +2221,6 @@ dependencies = [ ] [[package]] -name = "ref-cast" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2542,17 +2228,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -2563,100 +2240,54 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "rocket" -version = "0.5.1" +name = "rinja" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" +checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" dependencies = [ - "async-stream", - "async-trait", - "atomic 0.5.3", - "binascii", - "bytes", - "either", - "figment", - "futures", - "indexmap", - "log", - "memchr", - "multer", - "num_cpus", - "parking_lot", - "pin-project-lite", - "rand", - "ref-cast", - "rocket_codegen", - "rocket_http", - "serde", - "state", - "tempfile", - "time", - "tokio", - "tokio-stream", - "tokio-util", - "ubyte", - "version_check", - "yansi", + "humansize", + "itoa", + "percent-encoding", + "rinja_derive", ] [[package]] -name = "rocket_codegen" -version = "0.5.1" +name = "rinja_derive" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" +checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" dependencies = [ - "devise", - "glob", - "indexmap", + "basic-toml", + "memchr", + "mime", + "mime_guess", "proc-macro2", "quote", - "rocket_http", + "rinja_parser", + "rustc-hash", + "serde", "syn", - "unicode-xid", - "version_check", ] [[package]] -name = "rocket_http" -version = "0.5.1" +name = "rinja_parser" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" +checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" dependencies = [ - "cookie", - "either", - "futures", - "http 0.2.12", - "hyper", - "indexmap", - "log", "memchr", - "pear", - "percent-encoding", - "pin-project-lite", - "ref-cast", + "nom", "serde", - "smallvec", - "stable-pattern", - "state", - "time", - "tokio", - "uncased", ] [[package]] @@ -2678,6 +2309,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2725,12 +2362,6 @@ dependencies = [ ] [[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2769,15 +2400,6 @@ dependencies = [ ] [[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - -[[package]] name = "sha1_smol" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2795,15 +2417,6 @@ dependencies = [ ] [[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 = "shell-words" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2860,42 +2473,31 @@ dependencies = [ ] [[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "stable-pattern" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" -dependencies = [ - "memchr", -] - -[[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "state" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" -dependencies = [ - "loom", -] - -[[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] +name = "stderrlog" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c910772f992ab17d32d6760e167d2353f4130ed50e796752689556af07dc6b" +dependencies = [ + "chrono", + "is-terminal", + "log", + "termcolor", + "thread_local", +] + +[[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2931,13 +2533,22 @@ checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" dependencies = [ "cfg-if", "fastrand", - "getrandom 0.3.1", + "getrandom", "once_cell", "rustix 1.0.1", "windows-sys 0.59.0", ] [[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2968,37 +2579,6 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.3.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" - -[[package]] -name = "time-macros" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3025,16 +2605,15 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a" dependencies = [ "backtrace", "bytes", "libc", "mio", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -3052,17 +2631,6 @@ dependencies = [ ] [[package]] -name = "tokio-stream" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] name = "tokio-util" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3076,128 +2644,31 @@ dependencies = [ ] [[package]] -name = "toml" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.7.3", -] - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", - "tracing-attributes", "tracing-core", ] [[package]] -name = "tracing-attributes" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", - "valuable", ] [[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.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] -name = "ubyte" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" -dependencies = [ - "serde", -] - -[[package]] name = "uluru" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3207,14 +2678,10 @@ dependencies = [ ] [[package]] -name = "uncased" -version = "0.9.10" +name = "unicase" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "serde", - "version_check", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bom" @@ -3244,12 +2711,6 @@ dependencies = [ ] [[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] name = "url" version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3280,12 +2741,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3302,15 +2757,6 @@ dependencies = [ ] [[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3415,21 +2861,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3444,7 +2881,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3453,22 +2890,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -3477,48 +2899,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" @@ -3531,48 +2935,24 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" @@ -3587,15 +2967,6 @@ dependencies = [ ] [[package]] -name = "winnow" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" -dependencies = [ - "memchr", -] - -[[package]] name = "wit-bindgen-rt" version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3617,15 +2988,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" -dependencies = [ - "is-terminal", -] - -[[package]] name = "yoke" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3655,7 +3017,6 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive", ] diff --git a/pkgs/by-name/ba/back/Cargo.toml b/pkgs/by-name/ba/back/Cargo.toml index e19bf06..7fabeed 100644 --- a/pkgs/by-name/ba/back/Cargo.toml +++ b/pkgs/by-name/ba/back/Cargo.toml @@ -22,16 +22,24 @@ license = "AGPL-3.0-or-later" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bytes = "1.10.1" chrono = "0.4.40" clap = { version = "4.5.31", features = ["derive"] } gix = "0.70.0" +http = "1.2.0" +http-body-util = "0.1.2" +hyper = { version = "1.6.0", features = ["http1", "http2", "server"] } +hyper-util = { version = "0.1.10", features = ["tokio"] } +log = "0.4.26" markdown = "1.0.0-alpha.23" -rocket = "0.5.1" +rinja = "0.3.5" rss = "2.0.12" serde = "1.0.218" serde_json = "1.0.140" sha2 = "0.10.8" +stderrlog = "0.6.0" thiserror = "2.0.12" +tokio = { version = "1.44.0", features = ["macros", "net", "rt-multi-thread"] } url = { version = "2.5.4", features = ["serde"] } [profile.release] diff --git a/pkgs/by-name/ba/back/contrib/config.json b/pkgs/by-name/ba/back/contrib/config.json index 2347bf2..81d1041 100644 --- a/pkgs/by-name/ba/back/contrib/config.json +++ b/pkgs/by-name/ba/back/contrib/config.json @@ -1,5 +1,6 @@ { "source_code_repository_url": "https://git.foss-syndicate.org/vhack.eu/nixos-server/tree/pkgs/by-name/ba/back", - "repository_path": "/path/to/your/repository" - "root_url": "https://issues.foss-syndicate.org" + "root_url": "https://issues.foss-syndicate.org", + "scan_path": "/path/to/the/scan/path", + "project_list": "/path/to/the/projects.list" } diff --git a/pkgs/by-name/ba/back/src/config/mod.rs b/pkgs/by-name/ba/back/src/config/mod.rs index 7351ad8..1161ce3 100644 --- a/pkgs/by-name/ba/back/src/config/mod.rs +++ b/pkgs/by-name/ba/back/src/config/mod.rs @@ -18,21 +18,97 @@ use gix::ThreadSafeRepository; use serde::Deserialize; use url::Url; -use crate::error::{self, Error}; +use crate::{ + error::{self, Error}, + git_bug::dag::is_git_bug, +}; pub struct BackConfig { - // NOTE(@bpeetz): We do not need to html escape this, as the value must be a valid url. As such - // `<tags>` of all kinds _should_ be invalid. <2024-12-26> + /// The url to the source code of back. This is needed, because back is licensed under the + /// AGPL. pub source_code_repository_url: Url, - pub repository: ThreadSafeRepository, + + /// A list of the repositories known to back. + /// This list is constructed from the `scan_path` and the `project_list` file. + pub repositories: BackRepositories, + + /// The root url this instance of back is hosted on. + /// For example: + /// `issues.foss-syndicate.org` pub root: Url, } +pub struct BackRepositories { + repositories: Vec<BackRepository>, + + /// The path that is the common parent of all the repositories. + scan_path: PathBuf, +} + +impl BackRepositories { + pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter { + self.into_iter() + } +} + +impl<'a> IntoIterator for &'a BackRepositories { + type Item = <&'a Vec<BackRepository> as IntoIterator>::Item; + + type IntoIter = <&'a Vec<BackRepository> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.repositories.iter() + } +} + +impl BackRepositories { + /// Try to get the repository at path `path`. + /// If no repository was registered/found at `path`, returns an error. + pub fn get(&self, path: &Path) -> Result<&BackRepository, error::Error> { + self.repositories + .iter() + .find(|p| p.repo_path == path) + .ok_or(error::Error::RepoFind { + repository_path: path.to_owned(), + }) + } + + pub fn scan_path(&self) -> &Path { + &self.scan_path + } +} + +pub struct BackRepository { + repo_path: PathBuf, +} + +impl BackRepository { + pub fn open(&self, scan_path: &Path) -> Result<ThreadSafeRepository, error::Error> { + let repo = ThreadSafeRepository::open(scan_path.join(&self.repo_path)).map_err(|err| { + Error::RepoOpen { + repository_path: self.repo_path.to_owned(), + error: Box::new(err), + } + })?; + if is_git_bug(&repo.to_thread_local())? { + Ok(repo) + } else { + Err(error::Error::NotGitBug { + path: self.repo_path.clone(), + }) + } + } + pub fn path(&self) -> &Path { + &self.repo_path + } +} + #[derive(Deserialize)] struct RawBackConfig { source_code_repository_url: Url, - repository_path: PathBuf, root_url: Url, + project_list: PathBuf, + scan_path: PathBuf, } impl BackConfig { @@ -56,16 +132,26 @@ impl TryFrom<RawBackConfig> for BackConfig { type Error = error::Error; fn try_from(value: RawBackConfig) -> Result<Self, Self::Error> { - let repository = { - ThreadSafeRepository::open(&value.repository_path).map_err(|err| Error::RepoOpen { - repository_path: value.repository_path, - error: Box::new(err), - }) - }?; + let repositories = fs::read_to_string(&value.project_list) + .map_err(|err| error::Error::ProjectListRead { + error: err, + file: value.project_list, + })? + .lines() + .try_fold(vec![], |mut acc, path| { + acc.push(BackRepository { + repo_path: PathBuf::from(path), + }); + + Ok::<_, Self::Error>(acc) + })?; Ok(Self { - repository, source_code_repository_url: value.source_code_repository_url, + repositories: BackRepositories { + repositories, + scan_path: value.scan_path, + }, root: value.root_url, }) } diff --git a/pkgs/by-name/ba/back/src/error/mod.rs b/pkgs/by-name/ba/back/src/error/mod.rs index 8b71700..8889033 100644 --- a/pkgs/by-name/ba/back/src/error/mod.rs +++ b/pkgs/by-name/ba/back/src/error/mod.rs @@ -9,37 +9,53 @@ // You should have received a copy of the License along with this program. // If not, see <https://www.gnu.org/licenses/agpl.txt>. -use std::{fmt::Display, io, path::PathBuf}; +use std::{fmt::Display, io, net::SocketAddr, path::PathBuf}; +use gix::hash::Prefix; use thiserror::Error; -use crate::web::prefix::BackPrefix; - pub type Result<T> = std::result::Result<T, Error>; -pub mod responder; - #[derive(Error, Debug)] pub enum Error { ConfigParse { file: PathBuf, error: serde_json::Error, }, + + ProjectListRead { + file: PathBuf, + error: io::Error, + }, ConfigRead { file: PathBuf, error: io::Error, }, - RocketLaunch(#[from] rocket::Error), - + NotGitBug { + path: PathBuf, + }, RepoOpen { repository_path: PathBuf, error: Box<gix::open::Error>, }, + RepoFind { + repository_path: PathBuf, + }, RepoRefsIter(#[from] gix::refs::packed::buffer::open::Error), - RepoRefsPrefixed(#[from] std::io::Error), + RepoRefsPrefixed { + error: io::Error, + }, + + TcpBind { + addr: SocketAddr, + err: io::Error, + }, + TcpAccept { + err: io::Error, + }, IssuesPrefixMissing { - prefix: BackPrefix, + prefix: Prefix, }, IssuesPrefixParse(#[from] gix::hash::prefix::from_hex::Error), } @@ -54,6 +70,13 @@ impl Display for Error { file.display() ) } + Error::ProjectListRead { file, error } => { + write!( + f, + "while trying to read the project.list file ({}): {error}", + file.display() + ) + } Error::ConfigRead { file, error } => { write!( f, @@ -61,9 +84,6 @@ impl Display for Error { file.display() ) } - Error::RocketLaunch(error) => { - write!(f, "while trying to start back: {error}") - } Error::RepoOpen { repository_path, error, @@ -74,10 +94,24 @@ impl Display for Error { repository_path.display() ) } + Error::NotGitBug { path } => { + write!( + f, + "Repository ('{}') has no initialized git-bug data", + path.display() + ) + } + Error::RepoFind { repository_path } => { + write!( + f, + "failed to find the repository at path: '{}'", + repository_path.display() + ) + } Error::RepoRefsIter(error) => { write!(f, "while iteration over the refs in a repository: {error}",) } - Error::RepoRefsPrefixed(error) => { + Error::RepoRefsPrefixed { error, .. } => { write!(f, "while prefixing the refs with a path: {error}") } Error::IssuesPrefixMissing { prefix } => { @@ -89,6 +123,12 @@ impl Display for Error { Error::IssuesPrefixParse(error) => { write!(f, "The given prefix can not be parsed as prefix: {error}") } + Error::TcpBind { addr, err } => { + write!(f, "while trying to open tcp {addr} for listening: {err}.") + } + Error::TcpAccept { err } => { + write!(f, "while trying to accept a tcp connection: {err}.") + } } } } diff --git a/pkgs/by-name/ba/back/src/error/responder.rs b/pkgs/by-name/ba/back/src/error/responder.rs deleted file mode 100644 index 7bea961..0000000 --- a/pkgs/by-name/ba/back/src/error/responder.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Back - An extremely simple git issue tracking system. Inspired by tvix's -// panettone -// -// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This file is part of Back. -// -// You should have received a copy of the License along with this program. -// If not, see <https://www.gnu.org/licenses/agpl.txt>. - -use rocket::{ - response::{self, Responder, Response}, - Request, -}; - -use super::Error; - -impl<'r> Responder<'r, 'static> for Error { - fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { - Response::build_from(self.to_string().respond_to(req)?).ok() - } -} diff --git a/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs b/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs index 9c158a7..3d22b04 100644 --- a/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs +++ b/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs @@ -123,11 +123,23 @@ impl Dag { } } +/// Check whether `git-bug` has been initialized in this repository +pub fn is_git_bug(repo: &Repository) -> error::Result<bool> { + Ok(repo + .refs + .iter()? + .prefixed(Path::new("refs/bugs/")) + .map_err(|err| error::Error::RepoRefsPrefixed { error: err })? + .count() + > 0) +} + pub fn issues_from_repository(repo: &Repository) -> error::Result<Vec<Dag>> { let dags = repo .refs .iter()? - .prefixed(Path::new("refs/bugs/"))? + .prefixed(Path::new("refs/bugs/")) + .map_err(|err| error::Error::RepoRefsPrefixed { error: err })? .map(|val| { let reference = val.expect("All `git-bug` references in 'refs/bugs' should be objects"); diff --git a/pkgs/by-name/ba/back/src/git_bug/format/mod.rs b/pkgs/by-name/ba/back/src/git_bug/format/mod.rs index b3b6bcc..ffe44fd 100644 --- a/pkgs/by-name/ba/back/src/git_bug/format/mod.rs +++ b/pkgs/by-name/ba/back/src/git_bug/format/mod.rs @@ -16,8 +16,8 @@ use markdown::to_html; use serde::Deserialize; use serde_json::Value; -#[derive(Debug, Default, Clone)] /// Markdown content. +#[derive(Debug, Default, Clone)] pub struct MarkDown { value: String, } @@ -88,6 +88,19 @@ pub struct HtmlString { value: String, } +impl From<String> for HtmlString { + fn from(value: String) -> Self { + Self { value } + } +} +impl From<&str> for HtmlString { + fn from(value: &str) -> Self { + Self { + value: value.to_owned(), + } + } +} + impl From<MarkDown> for HtmlString { fn from(value: MarkDown) -> Self { Self { value: value.value } diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs index f27bfec..d382b54 100644 --- a/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs +++ b/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs @@ -128,7 +128,7 @@ impl RawCollapsedIssue { } => { self.id = Some(entity.id.clone()); self.author = Some(entity.author.clone()); - self.timestamp = Some(timestamp.clone()); + self.timestamp = Some(timestamp); self.title = Some(title); self.message = Some(message); self.status = Some(Status::Open); // This is the default in git_bug diff --git a/pkgs/by-name/ba/back/src/main.rs b/pkgs/by-name/ba/back/src/main.rs index 961c39b..61953c4 100644 --- a/pkgs/by-name/ba/back/src/main.rs +++ b/pkgs/by-name/ba/back/src/main.rs @@ -9,14 +9,11 @@ // You should have received a copy of the License along with this program. // If not, see <https://www.gnu.org/licenses/agpl.txt>. -use std::process; +use std::{process, sync::Arc}; use clap::Parser; -use config::BackConfig; -use rocket::routes; -use web::feed; -use crate::web::{closed, open, show_issue, styles}; +use crate::config::BackConfig; mod cli; pub mod config; @@ -25,7 +22,7 @@ pub mod git_bug; mod web; fn main() -> Result<(), String> { - if let Err(err) = rocket_main() { + if let Err(err) = server_main() { eprintln!("Error {err}"); process::exit(1); } else { @@ -33,20 +30,24 @@ fn main() -> Result<(), String> { } } -#[rocket::main] -async fn rocket_main() -> Result<(), error::Error> { +#[tokio::main] +async fn server_main() -> Result<(), error::Error> { let args = cli::Cli::parse(); + stderrlog::new() + .module(module_path!()) + .modules(["hyper", "http"]) + .quiet(false) + .show_module_names(false) + .color(stderrlog::ColorChoice::Auto) + .verbosity(2) + .timestamp(stderrlog::Timestamp::Off) + .init() + .expect("Let's just hope that this does not panic"); + let config = BackConfig::from_config_file(&args.config_file)?; - rocket::build() - .mount("/", routes![open, closed, show_issue, styles, feed]) - .manage(config) - .ignite() - .await - .expect("This error should only happen on a miss-configuration.") - .launch() - .await?; + web::main(Arc::new(config)).await?; Ok(()) } diff --git a/pkgs/by-name/ba/back/src/web/generate/mod.rs b/pkgs/by-name/ba/back/src/web/generate/mod.rs new file mode 100644 index 0000000..10146bb --- /dev/null +++ b/pkgs/by-name/ba/back/src/web/generate/mod.rs @@ -0,0 +1,227 @@ +use std::{fs, path::Path}; + +use gix::hash::Prefix; +use log::info; +use rinja::Template; +use url::Url; + +use crate::{ + config::BackConfig, + error, + git_bug::{ + dag::issues_from_repository, + issue::{CollapsedIssue, Status}, + }, +}; + +#[derive(Template)] +#[template(path = "./issues.html")] +struct IssuesTemplate { + wanted_status: Status, + counter_status: Status, + issues: Vec<CollapsedIssue>, + + /// The path to the repository + repo_path: String, + + /// The URL to `back`'s source code + source_code_repository_url: Url, +} +pub fn issues( + config: &BackConfig, + wanted_status: Status, + counter_status: Status, + repo_path: &Path, +) -> error::Result<String> { + let repository = config + .repositories + .get(repo_path)? + .open(config.repositories.scan_path())?; + + let mut issue_list = issues_from_repository(&repository.to_thread_local())? + .into_iter() + .map(|issue| issue.collapse()) + .filter(|issue| issue.status == wanted_status) + .collect::<Vec<CollapsedIssue>>(); + + // Sort by date descending. + // SAFETY: + // The time stamp is only used for sorting, so a malicious attacker could only affect the issue + // sorting. + issue_list.sort_by_key(|issue| unsafe { issue.timestamp.to_unsafe() }); + issue_list.reverse(); + + Ok(IssuesTemplate { + wanted_status, + counter_status, + source_code_repository_url: config.source_code_repository_url.clone(), + issues: issue_list, + repo_path: repo_path.display().to_string(), + } + .render() + .expect("This should always work")) +} + +use crate::git_bug::format::HtmlString; +#[derive(Template)] +#[template(path = "./issue.html")] +struct IssueTemplate { + issue: CollapsedIssue, + + /// The path to the repository + repo_path: String, + + /// The URL to `back`'s source code + source_code_repository_url: Url, +} +pub fn issue(config: &BackConfig, repo_path: &Path, prefix: Prefix) -> error::Result<String> { + let repository = config + .repositories + .get(repo_path)? + .open(config.repositories.scan_path())? + .to_thread_local(); + + let maybe_issue = issues_from_repository(&repository)? + .into_iter() + .map(|val| val.collapse()) + .find(|issue| issue.id.to_string().starts_with(&prefix.to_string())); + + match maybe_issue { + Some(issue) => Ok(IssueTemplate { + issue, + repo_path: repo_path.display().to_string(), + source_code_repository_url: config.source_code_repository_url.clone(), + } + .render() + .expect("This should always work")), + None => Err(error::Error::IssuesPrefixMissing { prefix }), + } +} + +#[derive(Template)] +#[template(path = "./repos.html")] +struct ReposTemplate { + repos: Vec<RepoValue>, + + /// The URL to `back`'s source code + source_code_repository_url: Url, +} +struct RepoValue { + description: String, + owner: String, + path: String, +} +pub fn repos(config: &BackConfig) -> error::Result<String> { + let repos: Vec<RepoValue> = config + .repositories + .iter() + .filter_map( + |raw_repo| match raw_repo.open(config.repositories.scan_path()) { + Ok(repo) => { + let repo = repo.to_thread_local(); + let git_config = repo.config_snapshot(); + + let path = raw_repo.path().to_string_lossy().to_string(); + + let owner = git_config + .string("cgit.owner") + .map(|v| v.to_string()) + .unwrap_or("<No owner>".to_owned()); + + let description = fs::read_to_string(repo.git_dir().join("description")) + .unwrap_or("<No description>".to_owned()); + + Some(RepoValue { + description, + owner, + path, + }) + } + Err(err) => { + info!( + "Repo '{}' could not be opened: '{err}'", + raw_repo.path().display() + ); + None + } + }, + ) + .collect(); + + Ok(ReposTemplate { + repos, + source_code_repository_url: config.source_code_repository_url.clone(), + } + .render() + .expect("this should work")) +} + +pub fn feed(config: &BackConfig, repo_path: &Path) -> error::Result<String> { + use rss::{ChannelBuilder, Item, ItemBuilder}; + + let repository = config + .repositories + .get(repo_path)? + .open(config.repositories.scan_path())? + .to_thread_local(); + + let issues: Vec<CollapsedIssue> = issues_from_repository(&repository)? + .into_iter() + .map(|issue| issue.collapse()) + .collect(); + + // Collect all Items as rss items + let mut items: Vec<Item> = issues + .iter() + .map(|issue| { + ItemBuilder::default() + .title(issue.title.to_string()) + .author(issue.author.to_string()) + .description(issue.message.to_string()) + .pub_date(issue.timestamp.to_string()) + .link(format!( + "/{}/{}/issue/{}", + repo_path.display(), + &config.root, + issue.id + )) + .build() + }) + .collect(); + + // Append all comments after converting them to rss items + items.extend( + issues + .iter() + .filter(|issue| !issue.comments.is_empty()) + .flat_map(|issue| { + issue + .comments + .iter() + .map(|comment| { + ItemBuilder::default() + .title(issue.title.to_string()) + .author(comment.author.to_string()) + .description(comment.message.to_string()) + .pub_date(comment.timestamp.to_string()) + .link(format!( + "/{}/{}/issue/{}", + repo_path.display(), + &config.root, + issue.id + )) + .build() + }) + .collect::<Vec<Item>>() + }) + .collect::<Vec<Item>>(), + ); + + let channel = ChannelBuilder::default() + .title("Issues") + .link(config.root.to_string()) + .description(format!("The rss feed for issues on {}.", &config.root)) + .items(items) + .build(); + Ok(channel.to_string()) +} diff --git a/pkgs/by-name/ba/back/src/web/issue_html.rs b/pkgs/by-name/ba/back/src/web/issue_html.rs deleted file mode 100644 index 45c0281..0000000 --- a/pkgs/by-name/ba/back/src/web/issue_html.rs +++ /dev/null @@ -1,166 +0,0 @@ -// Back - An extremely simple git issue tracking system. Inspired by tvix's -// panettone -// -// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This file is part of Back. -// -// You should have received a copy of the License along with this program. -// If not, see <https://www.gnu.org/licenses/agpl.txt>. - -use rocket::response::content::RawHtml; - -use crate::{ - config::BackConfig, - git_bug::{ - format::HtmlString, - issue::{identity::Author, CollapsedIssue, Comment}, - }, -}; - -impl CollapsedIssue { - pub fn to_list_entry(&self) -> RawHtml<String> { - let comment_list = if self.comments.is_empty() { - String::new() - } else { - let comments_string = if self.comments.len() > 1 { - "comments" - } else { - "comment" - }; - - format!( - r#" - <span class="comment-count"> - {} {}</span> - "#, - self.comments.len(), - comments_string - ) - }; - - let CollapsedIssue { - id, - title, - message: _, - author, - timestamp, - comments: _, - status: _, - last_status_change: _, - labels: _, - } = self; - - let Author { name, email, id: _ } = author; - - RawHtml(format!( - r#" - <li> - <a href="/issue/{id}"> - <p> - <span class="issue-subject">{title}</span> - </p> - <span class="issue-number">{id}</span> - <span class="created-by-at">Opened by <span class="user-name">{name}</span> <span class="user-email"><{email}></span> at <span class="timestamp">{timestamp}</span></span>{comment_list} </a> - </li> -"#, - )) - } - - pub fn to_html(&self, config: &BackConfig) -> RawHtml<String> { - let comments = if self.comments.is_empty() { - String::new() - } else { - let fmt_comments: String = self - .comments - .iter() - .map(|val| { - let Comment { - id, - author, - message, - timestamp, - } = val; - let Author { - name, - email: _, - id: _, - } = author; - - format!( - r#" - <li class="comment" id="{id}"> - {message} - <p class="comment-info"><span class="user-name">{name} at {timestamp}</span></p> - </li> - "#, - ) - }) - .collect::<Vec<String>>() - .join("\n"); - - format!( - r#" - <ol class="issue-history"> - {fmt_comments} - </ol> - "# - ) - }; - - { - let CollapsedIssue { - id, - title, - message, - author, - timestamp, - comments: _, - status: _, - last_status_change: _, - labels: _, - } = self; - let Author { name, email, id: _ } = author; - let html_title = HtmlString::from(title.clone()); - - RawHtml(format!( - r#" -<!DOCTYPE html> -<html lang="en"> - <head> - <title>{html_title} | Back</title> - <link href="/style.css" rel="stylesheet" type="text/css"> - <meta content="width=device-width,initial-scale=1" name="viewport"> - </head> - <body> - <div class="content"> - <nav> - <a href="/issues/open">Open Issues</a> - <a href="/issues/closed">Closed Issues</a> - </nav> - <header> - <h1>{title}</h1> - <div class="issue-number">{id}</div> - </header> - <main> - <div class="issue-info"> - <span class="created-by-at">Opened by <span class="user-name">{name}</span> <span class="user-email"><{email}></span> at <span class="timestamp">{timestamp}</span></span> - </div> - {message} - {comments} - </main> - <footer> - <nav> - <a href="/issues/open">Open Issues</a> - <a href="{}">Source code</a> - <a href="/issues/closed">Closed Issues</a> - </nav> - </footer> - </div> - </body> -</html> -"#, - config.source_code_repository_url - )) - } - } -} diff --git a/pkgs/by-name/ba/back/src/web/mod.rs b/pkgs/by-name/ba/back/src/web/mod.rs index f7a4077..cc087ab 100644 --- a/pkgs/by-name/ba/back/src/web/mod.rs +++ b/pkgs/by-name/ba/back/src/web/mod.rs @@ -1,186 +1,127 @@ -// Back - An extremely simple git issue tracking system. Inspired by tvix's -// panettone -// -// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This file is part of Back. -// -// You should have received a copy of the License along with this program. -// If not, see <https://www.gnu.org/licenses/agpl.txt>. - -use crate::{ - config::BackConfig, - error::{self, Error}, - git_bug::{ - dag::issues_from_repository, - issue::{CollapsedIssue, Status}, - }, -}; -use prefix::BackPrefix; -use rocket::{ - get, - response::content::{RawCss, RawHtml}, - State, -}; - -mod issue_html; -pub mod prefix; - -#[get("/style.css")] -pub fn styles() -> RawCss<String> { - RawCss(include_str!("../../assets/style.css").to_owned()) -} +use bytes::Bytes; +use http_body_util::combinators::BoxBody; +use hyper::{server::conn::http1, service::service_fn, Method, Request, Response, StatusCode}; +use hyper_util::rt::TokioIo; +use log::{error, info}; +use responses::{html_response, html_response_status, html_response_status_content_type}; +use tokio::net::TcpListener; + +use std::{convert::Infallible, net::SocketAddr, path::PathBuf, sync::Arc}; + +use crate::{config::BackConfig, error, git_bug::issue::Status}; + +mod generate; +mod responses; + +async fn match_uri( + config: Arc<BackConfig>, + req: Request<hyper::body::Incoming>, +) -> Result<Response<BoxBody<Bytes, Infallible>>, hyper::Error> { + if req.method() != Method::GET { + return Ok(html_response_status( + "Only get requests are supported", + StatusCode::NOT_ACCEPTABLE, + )); + } -pub fn issue_list_boilerplate( - config: &State<BackConfig>, - wanted_status: Status, - counter_status: Status, -) -> error::Result<RawHtml<String>> { - let repository = &config.repository; - - let mut issue_list = issues_from_repository(&repository.to_thread_local())? - .into_iter() - .map(|issue| issue.collapse()) - .collect::<Vec<CollapsedIssue>>(); - - // Sort by date descending. - issue_list.sort_by_key(|issue| unsafe { issue.timestamp.to_unsafe() }); - issue_list.reverse(); - - let issue_list_str = issue_list.into_iter().fold(String::new(), |acc, issue| { - format!("{}{}", acc, { - if issue.status == wanted_status { - let issue_entry = issue.to_list_entry(); - issue_entry.0 - } else { - String::new() + let output = || -> Result<Response<BoxBody<Bytes, Infallible>>, error::Error> { + match req.uri().path().trim_end_matches("/") { + "" => Ok(html_response(generate::repos(&config)?)), + + "/style.css" => Ok(responses::html_response_status_content_type( + include_str!("../../assets/style.css"), + StatusCode::OK, + "text/css", + )), + + path if path.ends_with("/issues/open") => { + let repo_path = PathBuf::from( + path.strip_suffix("/issues/open") + .expect("This suffix exists") + .strip_prefix("/") + .expect("This also exists"), + ); + + let issues = generate::issues(&config, Status::Open, Status::Closed, &repo_path)?; + Ok(html_response(issues)) + } + path if path.ends_with("/issues/closed") => { + let repo_path = PathBuf::from( + path.strip_suffix("/issues/closed") + .expect("This suffix exists") + .strip_prefix("/") + .expect("This also exists"), + ); + + let issues = generate::issues(&config, Status::Closed, Status::Open, &repo_path)?; + Ok(html_response(issues)) + } + path if path.ends_with("/issues/feed") => { + let repo_path = PathBuf::from( + path.strip_suffix("/issues/feed") + .expect("This suffix exists") + .strip_prefix("/") + .expect("This also exists"), + ); + + let feed = generate::feed(&config, &repo_path)?; + Ok(html_response_status_content_type( + feed, + StatusCode::OK, + "text/xml", + )) } - }) - }); - - let counter_status_lower = counter_status.to_string().to_lowercase(); - Ok(RawHtml(format!( - r#" - <!DOCTYPE html> - <html lang="en"> - <head> - <title>Back</title> - <link href="/style.css" rel="stylesheet" type="text/css"> - <meta content="width=device-width,initial-scale=1" name="viewport"> - </head> - <body> - <div class="content"> - <header> - <h1>{wanted_status} Issues</h1> - </header> - <main> - <div class="issue-links"> - <a href="/issues/{counter_status_lower}/">View {counter_status} issues</a> - <a href="{}">Source code</a> - <!-- - <form class="issue-search" method="get"> - <input name="search" title="Issue search query" type="search"> - <input class="sr-only" type="submit" value="Search Issues"> - </form> - --> - </div> - <ol class="issue-list"> - {issue_list_str} - </ol> - </main> - </div> - </body> - </html> - "#, - config.source_code_repository_url - ))) -} -#[get("/issues/open")] -pub fn open(config: &State<BackConfig>) -> error::Result<RawHtml<String>> { - issue_list_boilerplate(config, Status::Open, Status::Closed) -} -#[get("/issues/closed")] -pub fn closed(config: &State<BackConfig>) -> error::Result<RawHtml<String>> { - issue_list_boilerplate(config, Status::Closed, Status::Open) -} + path if path.contains("/issue/") => { + let (repo_path, prefix) = { + let split: Vec<&str> = path.split("/issue/").collect(); + + let prefix = + gix::hash::Prefix::from_hex(split[1]).map_err(error::Error::from)?; + + let repo_path = + PathBuf::from(split[0].strip_prefix("/").expect("This prefix exists")); -#[get("/issues/feed")] -pub fn feed(config: &State<BackConfig>) -> error::Result<RawHtml<String>> { - use rss::{ChannelBuilder, Item, ItemBuilder}; - - //Collect all Items as rss items - let mut items: Vec<Item> = issues_from_repository(&config.repository.to_thread_local())? - .into_iter() - .map(|issue| issue.collapse()) - .map(|issue| { - ItemBuilder::default() - .title(issue.title.to_string()) - .author(issue.author.to_string()) - .description(issue.message.to_string()) - .pub_date(issue.timestamp.to_string()) - .link(format!("{}/issue/{}", &config.root.to_string(), issue.id)) - .build() - }) - .collect(); - //Append all comments after converting them to rss items - items.extend( - issues_from_repository(&config.repository.to_thread_local())? - .into_iter() - .map(|issue| issue.collapse()) - .filter(|issue| issue.comments.len() > 0) - .map(|issue| { - issue - .comments - .into_iter() - .map(|comment| { - ItemBuilder::default() - .title(issue.title.to_string()) - .author(comment.author.to_string()) - .description(comment.message.to_string()) - .pub_date(comment.timestamp.to_string()) - .link(format!("{}/issue/{}", &config.root.to_string(), issue.id)) - .build() - }) - .collect::<Vec<Item>>() - }) - .flatten() - .collect::<Vec<Item>>(), - ); - - let channel = ChannelBuilder::default() - .title("Issues") - .link(config.root.to_string()) - .description(format!("The rss feed for issues on {}.", config.root)) - .items(items) - .build(); - Ok(RawHtml(channel.to_string())) + (repo_path, prefix) + }; + Ok(html_response(generate::issue(&config, &repo_path, prefix)?)) + } + + other => Ok(responses::html_response_status_content_type( + format!("'{}' not found", other), + StatusCode::NOT_FOUND, + "text/plain", + )), + } + }; + match output() { + Ok(response) => Ok(response), + Err(err) => Ok(err.into_response()), + } } -#[get("/issue/<prefix>")] -pub fn show_issue( - config: &State<BackConfig>, - prefix: Result<BackPrefix, gix::hash::prefix::from_hex::Error>, -) -> error::Result<RawHtml<String>> { - // NOTE(@bpeetz): Explicitly unwrap the `prefix` here (instead of taking the unwrapped value as - // argument), to avoid triggering rockets "errors forward to the next route" feature. - // This ensures, that our error message actually reaches the user. <2024-12-26> - let prefix = prefix?; - - let repository = config.repository.to_thread_local(); - - let all_issues: Vec<CollapsedIssue> = issues_from_repository(&repository)? - .into_iter() - .map(|val| val.collapse()) - .collect(); - - let maybe_issue = all_issues - .iter() - .find(|issue| issue.id.to_string().starts_with(&prefix.to_string())); - - match maybe_issue { - Some(issue) => Ok(issue.to_html(config)), - None => Err(Error::IssuesPrefixMissing { prefix }), +pub async fn main(config: Arc<BackConfig>) -> Result<(), error::Error> { + let addr: SocketAddr = ([127, 0, 0, 1], 8000).into(); + + let listener = TcpListener::bind(addr) + .await + .map_err(|err| error::Error::TcpBind { addr, err })?; + info!("Listening on http://{}", addr); + loop { + let (stream, _) = listener + .accept() + .await + .map_err(|err| error::Error::TcpAccept { err })?; + let io = TokioIo::new(stream); + + let local_config = Arc::clone(&config); + + let service = service_fn(move |req| match_uri(Arc::clone(&local_config), req)); + + tokio::task::spawn(async move { + if let Err(err) = http1::Builder::new().serve_connection(io, service).await { + error!("Error serving connection: {:?}", err); + } + }); } } diff --git a/pkgs/by-name/ba/back/src/web/prefix.rs b/pkgs/by-name/ba/back/src/web/prefix.rs deleted file mode 100644 index 5143799..0000000 --- a/pkgs/by-name/ba/back/src/web/prefix.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Back - An extremely simple git issue tracking system. Inspired by tvix's -// panettone -// -// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This file is part of Back. -// -// You should have received a copy of the License along with this program. -// If not, see <https://www.gnu.org/licenses/agpl.txt>. - -use std::fmt::Display; - -use gix::hash::Prefix; -use rocket::request::FromParam; - -#[derive(Debug)] -pub struct BackPrefix { - prefix: Prefix, -} -impl Display for BackPrefix { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.prefix.fmt(f) - } -} - -impl<'a> FromParam<'a> for BackPrefix { - type Error = gix::hash::prefix::from_hex::Error; - - fn from_param(param: &'a str) -> Result<Self, Self::Error> { - let prefix = Prefix::from_hex(param)?; - - Ok(Self { prefix }) - } -} diff --git a/pkgs/by-name/ba/back/src/web/responses.rs b/pkgs/by-name/ba/back/src/web/responses.rs new file mode 100644 index 0000000..e50f8c2 --- /dev/null +++ b/pkgs/by-name/ba/back/src/web/responses.rs @@ -0,0 +1,50 @@ +use std::convert::Infallible; + +use bytes::Bytes; +use http::{Response, StatusCode, Version}; +use http_body_util::{combinators::BoxBody, BodyExt, Full}; + +use crate::{error, git_bug::format::HtmlString}; + +pub(super) fn html_response<T: Into<Bytes>>(html_text: T) -> Response<BoxBody<Bytes, Infallible>> { + html_response_status(html_text, StatusCode::OK) +} + +pub(super) fn html_response_status<T: Into<Bytes>>( + html_text: T, + status: StatusCode, +) -> Response<BoxBody<Bytes, Infallible>> { + html_response_status_content_type(html_text, status, "text/html") +} + +pub(super) fn html_response_status_content_type<T: Into<Bytes>>( + html_text: T, + status: StatusCode, + content_type: &str, +) -> Response<BoxBody<Bytes, Infallible>> { + Response::builder() + .status(status) + .version(Version::HTTP_2) + .header("Content-Type", format!("{}; charset=utf-8", content_type)) + .header("x-content-type-options", "nosniff") + .header("x-frame-options", "SAMEORIGIN") + .body(full(html_text)) + .expect("This will always build") +} + +fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, Infallible> { + Full::new(chunk.into()).boxed() +} + +// FIXME: Not all errors should return `INTERNAL_SERVER_ERROR`. <2025-03-08> +impl error::Error { + pub fn into_response(self) -> Response<BoxBody<Bytes, Infallible>> { + html_response_status( + format!( + "<h1> Internal server error. </h1> <pre>Error: {}</pre>", + HtmlString::from(self.to_string()) + ), + StatusCode::INTERNAL_SERVER_ERROR, + ) + } +} diff --git a/pkgs/by-name/ba/back/templates/issue.html b/pkgs/by-name/ba/back/templates/issue.html new file mode 100644 index 0000000..5b452c5 --- /dev/null +++ b/pkgs/by-name/ba/back/templates/issue.html @@ -0,0 +1,57 @@ +<!doctype html> +<html lang="en"> + <head> + <title>{{ HtmlString::from(issue.title.clone()) }} | Back</title> + <link + href="/style.css" + rel="stylesheet" + type="text/css" /> + <meta + content="width=device-width,initial-scale=1" + name="viewport" /> + </head> + <body> + <div class="content"> + <nav> + <a href="/{{repo_path}}/issues/open">Open Issues</a> + <a href="/{{repo_path}}/issues/closed">Closed Issues</a> + </nav> + <header> + <h1>{{issue.title|safe}}</h1> + <div class="issue-number">{{issue.id}}</div> + </header> + <main> + <div class="issue-info"> + <span class="created-by-at" + >Opened by <span class="user-name">{{issue.author.name|safe}}</span> + <span class="user-email"><{{issue.author.email|safe}}></span> at + <span class="timestamp">{{issue.timestamp}}</span></span + > + </div> + {{issue.message|safe}} {% if !issue.comments.is_empty() %} + <ol class="issue-history"> + {% for comment in issue.comments %} + <li + class="comment" + id="{{comment.id}}"> + {{comment.message|safe}} + <p class="comment-info"> + <span class="user-name" + >{{comment.author.name|safe}} at {{comment.timestamp}}</span + > + </p> + </li> + {% endfor %} + </ol> + {% endif %} + </main> + <footer> + <nav> + <a href="/{{repo_path}}/issues/open">Open Issues</a> + <a href="{{source_code_repository_url}}">Source code</a> + <a href="/{{repo_path}}/issues/closed">Closed Issues</a> + </nav> + </footer> + </div> + </body> +</html> diff --git a/pkgs/by-name/ba/back/templates/issues.html b/pkgs/by-name/ba/back/templates/issues.html new file mode 100644 index 0000000..b6cc9b8 --- /dev/null +++ b/pkgs/by-name/ba/back/templates/issues.html @@ -0,0 +1,60 @@ +<!doctype html> +<html lang="en"> + <head> + <title>Back</title> + <link + href="/style.css" + rel="stylesheet" + type="text/css" /> + <meta + content="width=device-width,initial-scale=1" + name="viewport" /> + </head> + <body> + <div class="content"> + <header> + <h1>{{wanted_status}} Issues</h1> + </header> + <main> + <div class="issue-links"> + <a href="/{{repo_path}}/issues/{{counter_status|lowercase}}/" + >View {{counter_status}} issues</a + > + <a href="{{source_code_repository_url}}">Source code</a> + <!-- + <form class="issue-search" method="get"> + <input name="search" title="Issue search query" type="search"> + <input class="sr-only" type="submit" value="Search Issues"> + </form> + --> + </div> + <ol class="issue-list"> + {% for issue in issues -%} + <li> + <a href="/{{repo_path}}/issue/{{issue.id}}"> + <p> + <span class="issue-subject">{{issue.title|safe}}</span> + </p> + <span class="issue-number">{{issue.id}}</span> + <span class="created-by-at" + >Opened by {{ " " }} + <span class="user-name">{{issue.author.name|safe}}</span> + {{ " " }} + <span class="user-email"><{{issue.author.email|safe}}></span> + {{ "at" }} + <span class="timestamp">{{issue.timestamp}}</span> + </span> + {% if !issue.comments.is_empty() +%} + <span class="comment-count"> + - {{issue.comments.len()}} + comment{{issue.comments.len()|pluralize}}</span + > + {%+ endif %} + </a> + </li> + {%- endfor %} + </ol> + </main> + </div> + </body> +</html> diff --git a/pkgs/by-name/ba/back/templates/repos.html b/pkgs/by-name/ba/back/templates/repos.html new file mode 100644 index 0000000..dbccba0 --- /dev/null +++ b/pkgs/by-name/ba/back/templates/repos.html @@ -0,0 +1,47 @@ +<!doctype html> +<html lang="en"> + <head> + <title>Back</title> + <link + href="/style.css" + rel="stylesheet" + type="text/css" /> + <meta + content="width=device-width,initial-scale=1" + name="viewport" /> + </head> + <body> + <div class="content"> + <header> + <h1>Repositories</h1> + </header> + <main> + <div class="issue-links"> + <a href="{{source_code_repository_url}}">Source code</a> + <!-- + <form class="issue-search" method="get"> + <input name="search" title="Issue search query" type="search"> + <input class="sr-only" type="submit" value="Search Issues"> + </form> + --> + </div> + <ol class="issue-list"> + {% for repo in repos -%} + <li> + <a href="/{{repo.path}}/issues/open"> + <p> + <span class="issue-subject">{{repo.path}}</span> + </p> + <span class="created-by-at"> + <span class="timestamp">{{repo.description}}</span> + {{ "-" }} + <span class="user-name">{{repo.owner}}</span> + </span> + </a> + </li> + {%- endfor %} + </ol> + </main> + </div> + </body> +</html> |