mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-23 18:56:30 +00:00
refactor: migrate web server to Actix Web
This commit is contained in:
parent
b579dc805d
commit
13f5983c0a
19 changed files with 952 additions and 590 deletions
549
Cargo.lock
generated
549
Cargo.lock
generated
|
@ -8,6 +8,237 @@ version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
|
checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-codec"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.5.0",
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"memchr",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-http"
|
||||||
|
version = "3.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4eb9843d84c775696c37d9a418bbb01b932629d01870722c0f13eb3f95e2536d"
|
||||||
|
dependencies = [
|
||||||
|
"actix-codec",
|
||||||
|
"actix-rt",
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"ahash",
|
||||||
|
"base64",
|
||||||
|
"bitflags 2.5.0",
|
||||||
|
"brotli",
|
||||||
|
"bytes",
|
||||||
|
"bytestring",
|
||||||
|
"derive_more",
|
||||||
|
"encoding_rs",
|
||||||
|
"flate2",
|
||||||
|
"futures-core",
|
||||||
|
"h2 0.3.26",
|
||||||
|
"http 0.2.12",
|
||||||
|
"httparse",
|
||||||
|
"httpdate",
|
||||||
|
"itoa",
|
||||||
|
"language-tags",
|
||||||
|
"local-channel",
|
||||||
|
"mime",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rand",
|
||||||
|
"sha1",
|
||||||
|
"smallvec",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-macros"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-router"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8"
|
||||||
|
dependencies = [
|
||||||
|
"bytestring",
|
||||||
|
"cfg-if",
|
||||||
|
"http 0.2.12",
|
||||||
|
"regex",
|
||||||
|
"regex-lite",
|
||||||
|
"serde",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-rt"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-server"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4"
|
||||||
|
dependencies = [
|
||||||
|
"actix-rt",
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"mio",
|
||||||
|
"socket2",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-service"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"paste",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-utils"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8"
|
||||||
|
dependencies = [
|
||||||
|
"local-waker",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-web"
|
||||||
|
version = "4.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1cf67dadb19d7c95e5a299e2dda24193b89d5d4f33a3b9800888ede9e19aa32"
|
||||||
|
dependencies = [
|
||||||
|
"actix-codec",
|
||||||
|
"actix-http",
|
||||||
|
"actix-macros",
|
||||||
|
"actix-router",
|
||||||
|
"actix-rt",
|
||||||
|
"actix-server",
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web-codegen",
|
||||||
|
"ahash",
|
||||||
|
"bytes",
|
||||||
|
"bytestring",
|
||||||
|
"cfg-if",
|
||||||
|
"cookie",
|
||||||
|
"derive_more",
|
||||||
|
"encoding_rs",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"itoa",
|
||||||
|
"language-tags",
|
||||||
|
"log",
|
||||||
|
"mime",
|
||||||
|
"once_cell",
|
||||||
|
"pin-project-lite",
|
||||||
|
"regex",
|
||||||
|
"regex-lite",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"smallvec",
|
||||||
|
"socket2",
|
||||||
|
"time",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-web-codegen"
|
||||||
|
version = "4.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5"
|
||||||
|
dependencies = [
|
||||||
|
"actix-router",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-web-lab"
|
||||||
|
version = "0.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7675c1a84eec1b179c844cdea8488e3e409d8e4984026e92fa96c87dd86f33c6"
|
||||||
|
dependencies = [
|
||||||
|
"actix-http",
|
||||||
|
"actix-router",
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web",
|
||||||
|
"actix-web-lab-derive",
|
||||||
|
"ahash",
|
||||||
|
"arc-swap",
|
||||||
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
|
"bytestring",
|
||||||
|
"csv",
|
||||||
|
"derive_more",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"http 0.2.12",
|
||||||
|
"impl-more",
|
||||||
|
"itertools",
|
||||||
|
"local-channel",
|
||||||
|
"mediatype",
|
||||||
|
"mime",
|
||||||
|
"once_cell",
|
||||||
|
"pin-project-lite",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_html_form",
|
||||||
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-web-lab-derive"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9aa0b287c8de4a76b691f29dbb5451e8dd5b79d777eaf87350c9b0cbfdb5e968"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
|
@ -30,6 +261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"getrandom",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
|
@ -44,6 +276,21 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alloc-no-stdlib"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alloc-stdlib"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
|
||||||
|
dependencies = [
|
||||||
|
"alloc-no-stdlib",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.18"
|
version = "0.2.18"
|
||||||
|
@ -81,6 +328,17 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.80"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -146,6 +404,27 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "brotli"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
|
||||||
|
dependencies = [
|
||||||
|
"alloc-no-stdlib",
|
||||||
|
"alloc-stdlib",
|
||||||
|
"brotli-decompressor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "brotli-decompressor"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6221fe77a248b9117d431ad93761222e1cf8ff282d9d1d5d9f53d6299a1cf76"
|
||||||
|
dependencies = [
|
||||||
|
"alloc-no-stdlib",
|
||||||
|
"alloc-stdlib",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.9.1"
|
version = "1.9.1"
|
||||||
|
@ -169,6 +448,15 @@ version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytestring"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cadence"
|
name = "cadence"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -201,6 +489,11 @@ name = "cc"
|
||||||
version = "1.0.98"
|
version = "1.0.98"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -226,6 +519,17 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie"
|
||||||
|
version = "0.16.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
"time",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
@ -346,6 +650,27 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "csv"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
|
||||||
|
dependencies = [
|
||||||
|
"csv-core",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "csv-core"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cvss"
|
name = "cvss"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -1493,17 +1818,6 @@ dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-body"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"http 0.2.12",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-body"
|
name = "http-body"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -1523,7 +1837,7 @@ dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"http-body 1.0.0",
|
"http-body",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1539,30 +1853,6 @@ version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper"
|
|
||||||
version = "0.14.28"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"h2 0.3.26",
|
|
||||||
"http 0.2.12",
|
|
||||||
"http-body 0.4.6",
|
|
||||||
"httparse",
|
|
||||||
"httpdate",
|
|
||||||
"itoa",
|
|
||||||
"pin-project-lite",
|
|
||||||
"socket2",
|
|
||||||
"tokio",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
"want",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
|
@ -1574,7 +1864,7 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.4.5",
|
"h2 0.4.5",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"http-body 1.0.0",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
@ -1591,7 +1881,7 @@ checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"hyper 1.3.1",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
@ -1608,7 +1898,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper 1.3.1",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -1626,8 +1916,8 @@ dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"http-body 1.0.0",
|
"http-body",
|
||||||
"hyper 1.3.1",
|
"hyper",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -1646,6 +1936,12 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "impl-more"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.2.6"
|
version = "2.2.6"
|
||||||
|
@ -1673,12 +1969,30 @@ version = "2.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.69"
|
version = "0.3.69"
|
||||||
|
@ -1707,6 +2021,12 @@ dependencies = [
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "language-tags"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lasso"
|
name = "lasso"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
|
@ -1734,6 +2054,23 @@ version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "local-channel"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"local-waker",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "local-waker"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
|
@ -1798,6 +2135,12 @@ dependencies = [
|
||||||
"syn 2.0.66",
|
"syn 2.0.66",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mediatype"
|
||||||
|
version = "0.19.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8878cd8d1b3c8c8ae4b2ba0a36652b7cf192f618a599a7fbdfa25cffd4ea72dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.2"
|
version = "2.7.2"
|
||||||
|
@ -1835,6 +2178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
@ -1989,6 +2333,12 @@ dependencies = [
|
||||||
"windows-targets 0.52.5",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
|
@ -2090,6 +2440,12 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error"
|
name = "proc-macro-error"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
@ -2162,6 +2518,18 @@ version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
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",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2170,6 +2538,9 @@ name = "rand_core"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
|
@ -2241,6 +2612,12 @@ dependencies = [
|
||||||
"regex-syntax 0.8.3",
|
"regex-syntax 0.8.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-lite"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.29"
|
version = "0.6.29"
|
||||||
|
@ -2277,9 +2654,9 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.4.5",
|
"h2 0.4.5",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"http-body 1.0.0",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper 1.3.1",
|
"hyper",
|
||||||
"hyper-rustls",
|
"hyper-rustls",
|
||||||
"hyper-tls",
|
"hyper-tls",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
|
@ -2328,12 +2705,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "route-recognizer"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
|
@ -2535,6 +2906,19 @@ dependencies = [
|
||||||
"syn 2.0.66",
|
"syn 2.0.66",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_html_form"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5"
|
||||||
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
|
"indexmap",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.117"
|
version = "1.0.117"
|
||||||
|
@ -2578,6 +2962,17 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1_smol"
|
name = "sha1_smol"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -2603,6 +2998,8 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||||
name = "shiny-robots"
|
name = "shiny-robots"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"actix-web",
|
||||||
|
"actix-web-lab",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"badge",
|
"badge",
|
||||||
"cadence",
|
"cadence",
|
||||||
|
@ -2614,16 +3011,15 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gix",
|
"gix",
|
||||||
"grass",
|
"grass",
|
||||||
"hyper 0.14.28",
|
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lru_time_cache",
|
"lru_time_cache",
|
||||||
"maud",
|
"maud",
|
||||||
|
"mime",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
"relative-path",
|
"relative-path",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"route-recognizer",
|
|
||||||
"rustsec",
|
"rustsec",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2635,6 +3031,15 @@ dependencies = [
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
|
@ -2868,7 +3273,9 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
|
@ -2906,6 +3313,17 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-stream"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.11"
|
version = "0.7.11"
|
||||||
|
@ -3020,6 +3438,7 @@ version = "0.1.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"log",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tracing-attributes",
|
"tracing-attributes",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
|
@ -3523,3 +3942,31 @@ name = "zeroize"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-safe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-safe"
|
||||||
|
version = "7.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-sys"
|
||||||
|
version = "2.0.10+zstd.1.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
|
@ -14,6 +14,8 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
badge = { path = "./libs/badge" }
|
badge = { path = "./libs/badge" }
|
||||||
|
|
||||||
|
actix-web = "4"
|
||||||
|
actix-web-lab = "0.20"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
cadence = "1"
|
cadence = "1"
|
||||||
crates-index = { version = "2", default-features = false, features = ["git"] }
|
crates-index = { version = "2", default-features = false, features = ["git"] }
|
||||||
|
@ -21,22 +23,21 @@ derive_more = "0.99"
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
font-awesome-as-a-crate = "0.3"
|
font-awesome-as-a-crate = "0.3"
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
||||||
hyper = { version = "0.14.10", features = ["full"] }
|
|
||||||
error_reporter = "1"
|
error_reporter = "1"
|
||||||
indexmap = { version = "2", features = ["serde"] }
|
indexmap = { version = "2", features = ["serde"] }
|
||||||
lru_time_cache = "0.11"
|
lru_time_cache = "0.11"
|
||||||
maud = "0.26"
|
maud = "0.26"
|
||||||
|
mime = "0.3"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
pulldown-cmark = "0.11"
|
pulldown-cmark = "0.11"
|
||||||
relative-path = { version = "1", features = ["serde"] }
|
relative-path = { version = "1", features = ["serde"] }
|
||||||
reqwest = { version = "0.12", features = ["json"] }
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
route-recognizer = "0.3"
|
|
||||||
rustsec = "0.29"
|
rustsec = "0.29"
|
||||||
semver = { version = "1.0", features = ["serde"] }
|
semver = { version = "1.0", features = ["serde"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
tokio = { version = "1.24.2", features = ["rt", "macros", "sync", "time"] }
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
tracing = "0.1.30"
|
tracing = "0.1.30"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use futures_util::{future::BoxFuture, stream::FuturesOrdered, FutureExt as _, StreamExt as _};
|
use futures_util::{
|
||||||
|
future::LocalBoxFuture, stream::FuturesOrdered, FutureExt as _, StreamExt as _,
|
||||||
|
};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -16,8 +18,9 @@ pub async fn crawl_manifest(
|
||||||
entry_point: RelativePathBuf,
|
entry_point: RelativePathBuf,
|
||||||
) -> anyhow::Result<ManifestCrawlerOutput> {
|
) -> anyhow::Result<ManifestCrawlerOutput> {
|
||||||
let mut crawler = ManifestCrawler::new();
|
let mut crawler = ManifestCrawler::new();
|
||||||
let mut futures: FuturesOrdered<BoxFuture<'static, Result<(RelativePathBuf, String), Error>>> =
|
let mut futures: FuturesOrdered<
|
||||||
FuturesOrdered::new();
|
LocalBoxFuture<'static, Result<(RelativePathBuf, String), Error>>,
|
||||||
|
> = FuturesOrdered::new();
|
||||||
|
|
||||||
let engine2 = engine.clone();
|
let engine2 = engine.clone();
|
||||||
let repo_path2 = repo_path.clone();
|
let repo_path2 = repo_path.clone();
|
||||||
|
@ -28,7 +31,7 @@ pub async fn crawl_manifest(
|
||||||
.await?;
|
.await?;
|
||||||
Ok((entry_point, contents))
|
Ok((entry_point, contents))
|
||||||
}
|
}
|
||||||
.boxed();
|
.boxed_local();
|
||||||
|
|
||||||
futures.push_back(fut);
|
futures.push_back(fut);
|
||||||
|
|
||||||
|
@ -47,7 +50,7 @@ pub async fn crawl_manifest(
|
||||||
let contents = engine.retrieve_manifest_at_path(&repo_path, &path).await?;
|
let contents = engine.retrieve_manifest_at_path(&repo_path, &path).await?;
|
||||||
Ok((path, contents))
|
Ok((path, contents))
|
||||||
}
|
}
|
||||||
.boxed();
|
.boxed_local();
|
||||||
|
|
||||||
futures.push_back(fut);
|
futures.push_back(fut);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,14 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use actix_web::dev::Service;
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use cadence::{MetricSink, NopMetricSink, StatsdClient};
|
use cadence::{MetricSink, NopMetricSink, StatsdClient};
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
future::try_join_all,
|
future::try_join_all,
|
||||||
stream::{self, BoxStream},
|
stream::{self, LocalBoxStream},
|
||||||
StreamExt as _,
|
StreamExt as _,
|
||||||
};
|
};
|
||||||
use hyper::service::Service;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use relative_path::{RelativePath, RelativePathBuf};
|
use relative_path::{RelativePath, RelativePathBuf};
|
||||||
use rustsec::database::Database;
|
use rustsec::database::Database;
|
||||||
|
@ -38,7 +38,7 @@ mod machines;
|
||||||
|
|
||||||
use self::fut::{analyze_dependencies, crawl_manifest};
|
use self::fut::{analyze_dependencies, crawl_manifest};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Engine {
|
pub struct Engine {
|
||||||
metrics: Arc<StatsdClient>,
|
metrics: Arc<StatsdClient>,
|
||||||
query_crate: Cache<QueryCrate, CrateName>,
|
query_crate: Cache<QueryCrate, CrateName>,
|
||||||
|
@ -255,7 +255,10 @@ impl Engine {
|
||||||
Ok(latest)
|
Ok(latest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_releases<'a, I>(&'a self, names: I) -> BoxStream<'a, anyhow::Result<Vec<CrateRelease>>>
|
fn fetch_releases<'a, I>(
|
||||||
|
&'a self,
|
||||||
|
names: I,
|
||||||
|
) -> LocalBoxStream<'a, anyhow::Result<Vec<CrateRelease>>>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = CrateName>,
|
I: IntoIterator<Item = CrateName>,
|
||||||
<I as IntoIterator>::IntoIter: Send + 'a,
|
<I as IntoIterator>::IntoIter: Send + 'a,
|
||||||
|
@ -277,7 +280,7 @@ impl Engine {
|
||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
let manifest_path = path.join(RelativePath::new("Cargo.toml"));
|
let manifest_path = path.join(RelativePath::new("Cargo.toml"));
|
||||||
|
|
||||||
let mut service = self.retrieve_file_at_path.clone();
|
let service = self.retrieve_file_at_path.clone();
|
||||||
service.call((repo_path.clone(), manifest_path)).await
|
service.call((repo_path.clone(), manifest_path)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
use std::{
|
use std::{fmt, str};
|
||||||
fmt, str,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use actix_web::dev::Service;
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use crates_index::{Crate, DependencyKind};
|
use crates_index::{Crate, DependencyKind};
|
||||||
use futures_util::FutureExt as _;
|
use futures_util::{future::LocalBoxFuture, FutureExt as _};
|
||||||
use hyper::service::Service;
|
|
||||||
use semver::{Version, VersionReq};
|
use semver::{Version, VersionReq};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
models::crates::{CrateDep, CrateDeps, CrateName, CratePath, CrateRelease},
|
models::crates::{CrateDep, CrateDeps, CrateName, CratePath, CrateRelease},
|
||||||
BoxFuture, ManagedIndex,
|
ManagedIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CRATES_API_BASE_URI: &str = "https://crates.io/api/v1";
|
const CRATES_API_BASE_URI: &str = "https://crates.io/api/v1";
|
||||||
|
@ -86,13 +83,11 @@ impl fmt::Debug for QueryCrate {
|
||||||
impl Service<CrateName> for QueryCrate {
|
impl Service<CrateName> for QueryCrate {
|
||||||
type Response = QueryCrateResponse;
|
type Response = QueryCrateResponse;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = BoxFuture<Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_web::dev::always_ready!();
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, crate_name: CrateName) -> Self::Future {
|
fn call(&self, crate_name: CrateName) -> Self::Future {
|
||||||
let index = self.index.clone();
|
let index = self.index.clone();
|
||||||
Self::query(index, crate_name).boxed()
|
Self::query(index, crate_name).boxed()
|
||||||
}
|
}
|
||||||
|
@ -150,13 +145,11 @@ impl fmt::Debug for GetPopularCrates {
|
||||||
impl Service<()> for GetPopularCrates {
|
impl Service<()> for GetPopularCrates {
|
||||||
type Response = Vec<CratePath>;
|
type Response = Vec<CratePath>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = BoxFuture<Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_web::dev::always_ready!();
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, _req: ()) -> Self::Future {
|
fn call(&self, _req: ()) -> Self::Future {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
Self::query(client).boxed()
|
Self::query(client).boxed()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
use std::{
|
use std::fmt;
|
||||||
fmt,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use actix_web::dev::Service;
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use futures_util::FutureExt as _;
|
use futures_util::{future::LocalBoxFuture, FutureExt as _};
|
||||||
use hyper::service::Service;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::models::repo::{RepoPath, Repository};
|
||||||
models::repo::{RepoPath, Repository},
|
|
||||||
BoxFuture,
|
|
||||||
};
|
|
||||||
|
|
||||||
const GITHUB_API_BASE_URI: &str = "https://api.github.com";
|
const GITHUB_API_BASE_URI: &str = "https://api.github.com";
|
||||||
|
|
||||||
|
@ -72,13 +66,11 @@ impl fmt::Debug for GetPopularRepos {
|
||||||
impl Service<()> for GetPopularRepos {
|
impl Service<()> for GetPopularRepos {
|
||||||
type Response = Vec<Repository>;
|
type Response = Vec<Repository>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = BoxFuture<Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_web::dev::always_ready!();
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, _req: ()) -> Self::Future {
|
fn call(&self, _req: ()) -> Self::Future {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
Self::query(client).boxed()
|
Self::query(client).boxed()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
use std::{
|
use std::fmt;
|
||||||
fmt,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use actix_web::dev::Service;
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use futures_util::FutureExt as _;
|
use futures_util::{future::LocalBoxFuture, FutureExt as _};
|
||||||
use hyper::service::Service;
|
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
use crate::{models::repo::RepoPath, BoxFuture};
|
use crate::models::repo::RepoPath;
|
||||||
|
|
||||||
pub mod crates;
|
pub mod crates;
|
||||||
pub mod github;
|
pub mod github;
|
||||||
|
@ -43,13 +40,11 @@ impl RetrieveFileAtPath {
|
||||||
impl Service<(RepoPath, RelativePathBuf)> for RetrieveFileAtPath {
|
impl Service<(RepoPath, RelativePathBuf)> for RetrieveFileAtPath {
|
||||||
type Response = String;
|
type Response = String;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = BoxFuture<Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_web::dev::always_ready!();
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, (repo_path, path): (RepoPath, RelativePathBuf)) -> Self::Future {
|
fn call(&self, (repo_path, path): (RepoPath, RelativePathBuf)) -> Self::Future {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
Self::query(client, repo_path, path).boxed()
|
Self::query(client, repo_path, path).boxed()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
use std::{
|
use std::{fmt, sync::Arc};
|
||||||
fmt,
|
|
||||||
sync::Arc,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use actix_web::dev::Service;
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use futures_util::FutureExt as _;
|
use futures_util::{future::LocalBoxFuture, FutureExt as _};
|
||||||
use hyper::service::Service;
|
|
||||||
use rustsec::database::Database;
|
use rustsec::database::Database;
|
||||||
|
|
||||||
use crate::BoxFuture;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FetchAdvisoryDatabase {
|
pub struct FetchAdvisoryDatabase {
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
|
@ -30,20 +24,19 @@ impl FetchAdvisoryDatabase {
|
||||||
impl Service<()> for FetchAdvisoryDatabase {
|
impl Service<()> for FetchAdvisoryDatabase {
|
||||||
type Response = Arc<Database>;
|
type Response = Arc<Database>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = BoxFuture<Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_web::dev::always_ready!();
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, _req: ()) -> Self::Future {
|
fn call(&self, _req: ()) -> Self::Future {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
Self::fetch(client).boxed()
|
Self::fetch(client).boxed_local()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for FetchAdvisoryDatabase {
|
impl fmt::Debug for FetchAdvisoryDatabase {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str("FetchAdvisoryDatabase")
|
f.debug_struct("FetchAdvisoryDatabase")
|
||||||
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
58
src/main.rs
58
src/main.rs
|
@ -3,20 +3,14 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
future::Future,
|
net::{Ipv4Addr, UdpSocket},
|
||||||
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
|
|
||||||
pin::Pin,
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use actix_web::{middleware::Logger, web};
|
||||||
|
use actix_web_lab::{extract::ThinData, middleware::NormalizePath};
|
||||||
use cadence::{QueuingMetricSink, UdpMetricSink};
|
use cadence::{QueuingMetricSink, UdpMetricSink};
|
||||||
use hyper::{
|
|
||||||
server::conn::AddrStream,
|
|
||||||
service::{make_service_fn, service_fn},
|
|
||||||
Server,
|
|
||||||
};
|
|
||||||
use reqwest::redirect::Policy as RedirectPolicy;
|
use reqwest::redirect::Policy as RedirectPolicy;
|
||||||
use tracing::Instrument as _;
|
|
||||||
|
|
||||||
mod engine;
|
mod engine;
|
||||||
mod interactors;
|
mod interactors;
|
||||||
|
@ -25,10 +19,7 @@ mod parsers;
|
||||||
mod server;
|
mod server;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use self::{engine::Engine, server::App, utils::index::ManagedIndex};
|
use self::{engine::Engine, utils::index::ManagedIndex};
|
||||||
|
|
||||||
/// Future crate's BoxFuture without the explicit lifetime parameter.
|
|
||||||
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
|
|
||||||
|
|
||||||
const DEPS_RS_UA: &str = "deps.rs";
|
const DEPS_RS_UA: &str = "deps.rs";
|
||||||
|
|
||||||
|
@ -59,7 +50,7 @@ fn init_tracing_subscriber() {
|
||||||
.init();
|
.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenvy::dotenv().ok();
|
dotenvy::dotenv().ok();
|
||||||
init_tracing_subscriber();
|
init_tracing_subscriber();
|
||||||
|
@ -77,8 +68,6 @@ async fn main() {
|
||||||
.parse()
|
.parse()
|
||||||
.expect("could not read port");
|
.expect("could not read port");
|
||||||
|
|
||||||
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port);
|
|
||||||
|
|
||||||
let index = ManagedIndex::new();
|
let index = ManagedIndex::new();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -92,25 +81,24 @@ async fn main() {
|
||||||
let mut engine = Engine::new(client.clone(), index);
|
let mut engine = Engine::new(client.clone(), index);
|
||||||
engine.set_metrics(metrics);
|
engine.set_metrics(metrics);
|
||||||
|
|
||||||
let make_svc = make_service_fn(move |_socket: &AddrStream| {
|
let server = actix_web::HttpServer::new(move || {
|
||||||
let engine = engine.clone();
|
actix_web::App::new()
|
||||||
|
.app_data(ThinData(engine.clone()))
|
||||||
async move {
|
.service(server::index)
|
||||||
let server = App::new(engine.clone());
|
.service(server::crate_redirect)
|
||||||
Ok::<_, hyper::Error>(service_fn(move |req| {
|
.service(server::crate_latest_status_svg)
|
||||||
let server = server.clone();
|
.service(server::crate_status_svg)
|
||||||
async move {
|
.service(server::crate_status_html)
|
||||||
let path = req.uri().path().to_owned();
|
.service(server::repo_status_svg)
|
||||||
|
.service(server::repo_status_html)
|
||||||
server
|
.configure(server::static_files)
|
||||||
.handle(req)
|
.default_service(web::to(server::not_found))
|
||||||
.instrument(tracing::info_span!("@", %path))
|
.wrap(NormalizePath::trim())
|
||||||
.await
|
.wrap(Logger::default())
|
||||||
}
|
})
|
||||||
}))
|
.bind_auto_h2c((Ipv4Addr::UNSPECIFIED, port))
|
||||||
}
|
.unwrap()
|
||||||
});
|
.run();
|
||||||
let server = Server::bind(&addr).serve(make_svc);
|
|
||||||
|
|
||||||
tracing::info!("Server running on port {port}");
|
tracing::info!("Server running on port {port}");
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ impl FromStr for RepoSite {
|
||||||
if let Some((site, domain)) = input.split_once('/') {
|
if let Some((site, domain)) = input.split_once('/') {
|
||||||
match site {
|
match site {
|
||||||
"gitea" => Ok(RepoSite::Gitea(domain.parse()?)),
|
"gitea" => Ok(RepoSite::Gitea(domain.parse()?)),
|
||||||
_ => Err(anyhow!("unknown repo site identifier")),
|
site => Err(anyhow!("unknown repo site identifier: {site}")),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match input {
|
match input {
|
||||||
|
@ -109,7 +109,7 @@ impl FromStr for RepoSite {
|
||||||
"bitbucket" => Ok(RepoSite::Bitbucket),
|
"bitbucket" => Ok(RepoSite::Bitbucket),
|
||||||
"sourcehut" => Ok(RepoSite::Sourcehut),
|
"sourcehut" => Ok(RepoSite::Sourcehut),
|
||||||
"codeberg" => Ok(RepoSite::Codeberg),
|
"codeberg" => Ok(RepoSite::Codeberg),
|
||||||
_ => Err(anyhow!("unknown repo site identifier")),
|
site => Err(anyhow!("unknown repo site identifier: {site}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,9 @@ pub const STATIC_STYLE_CSS_PATH: &str = concat!(
|
||||||
include_str!(concat!(env!("OUT_DIR"), "/style.css.sha1")),
|
include_str!(concat!(env!("OUT_DIR"), "/style.css.sha1")),
|
||||||
".css"
|
".css"
|
||||||
);
|
);
|
||||||
pub const STATIC_STYLE_CSS_ETAG: &str = concat!(
|
pub const STATIC_STYLE_CSS_ETAG: &str = include_str!(concat!(env!("OUT_DIR"), "/style.css.sha1"));
|
||||||
"\"",
|
|
||||||
include_str!(concat!(env!("OUT_DIR"), "/style.css.sha1")),
|
pub const STATIC_FAVICON_PATH: &str = "/static/logo.svg";
|
||||||
"\""
|
|
||||||
);
|
|
||||||
pub static STATIC_FAVICON: &[u8] = include_bytes!("../../assets/logo.svg");
|
pub static STATIC_FAVICON: &[u8] = include_bytes!("../../assets/logo.svg");
|
||||||
|
|
||||||
pub static STATIC_LINKS_JS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/links.js"));
|
pub static STATIC_LINKS_JS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/links.js"));
|
||||||
|
@ -17,8 +15,4 @@ pub const STATIC_LINKS_JS_PATH: &str = concat!(
|
||||||
include_str!(concat!(env!("OUT_DIR"), "/links.js.sha1")),
|
include_str!(concat!(env!("OUT_DIR"), "/links.js.sha1")),
|
||||||
".js"
|
".js"
|
||||||
);
|
);
|
||||||
pub const STATIC_LINKS_JS_ETAG: &str = concat!(
|
pub const STATIC_LINKS_JS_ETAG: &str = include_str!(concat!(env!("OUT_DIR"), "/links.js.sha1"));
|
||||||
"\"",
|
|
||||||
include_str!(concat!(env!("OUT_DIR"), "/links.js.sha1")),
|
|
||||||
"\""
|
|
||||||
);
|
|
||||||
|
|
79
src/server/error.rs
Normal file
79
src/server/error.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
use actix_web::{
|
||||||
|
http::{header::ContentType, StatusCode},
|
||||||
|
HttpResponse, ResponseError,
|
||||||
|
};
|
||||||
|
use derive_more::Display;
|
||||||
|
use maud::Markup;
|
||||||
|
|
||||||
|
use crate::server::views::html::error::{render, render_404};
|
||||||
|
|
||||||
|
#[derive(Debug, Display)]
|
||||||
|
pub(crate) enum ServerError {
|
||||||
|
#[display(fmt = "Could not retrieve popular items")]
|
||||||
|
PopularItemsFailed,
|
||||||
|
|
||||||
|
#[display(fmt = "Crate not found")]
|
||||||
|
CrateNotFound,
|
||||||
|
|
||||||
|
#[display(fmt = "Could not parse crate path")]
|
||||||
|
BadCratePath,
|
||||||
|
|
||||||
|
#[display(fmt = "Could not fetch crate information")]
|
||||||
|
CrateFetchFailed,
|
||||||
|
|
||||||
|
#[display(fmt = "Could not parse repository path")]
|
||||||
|
BadRepoPath,
|
||||||
|
|
||||||
|
#[display(fmt = "Crate/repo analysis failed")]
|
||||||
|
AnalysisFailed(Markup),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError for ServerError {
|
||||||
|
fn status_code(&self) -> StatusCode {
|
||||||
|
match self {
|
||||||
|
ServerError::PopularItemsFailed => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
ServerError::CrateNotFound => StatusCode::NOT_FOUND,
|
||||||
|
ServerError::BadCratePath => StatusCode::BAD_REQUEST,
|
||||||
|
ServerError::CrateFetchFailed => StatusCode::NOT_FOUND,
|
||||||
|
ServerError::BadRepoPath => StatusCode::BAD_REQUEST,
|
||||||
|
ServerError::AnalysisFailed(_) => StatusCode::BAD_REQUEST,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
let mut res = HttpResponse::build(self.status_code());
|
||||||
|
let res = res.insert_header(ContentType::html());
|
||||||
|
|
||||||
|
match self {
|
||||||
|
ServerError::PopularItemsFailed => res.body(render(self.to_string(), "").0),
|
||||||
|
|
||||||
|
ServerError::CrateNotFound => res.body(render_404().0),
|
||||||
|
|
||||||
|
ServerError::BadCratePath => res.body(
|
||||||
|
render(
|
||||||
|
self.to_string(),
|
||||||
|
"Please make sure to provide a valid crate name and version.",
|
||||||
|
)
|
||||||
|
.0,
|
||||||
|
),
|
||||||
|
|
||||||
|
ServerError::CrateFetchFailed => res.body(
|
||||||
|
render(
|
||||||
|
self.to_string(),
|
||||||
|
"Please make sure to provide a valid crate name.",
|
||||||
|
)
|
||||||
|
.0,
|
||||||
|
),
|
||||||
|
|
||||||
|
ServerError::BadRepoPath => res.body(
|
||||||
|
render(
|
||||||
|
self.to_string(),
|
||||||
|
"Please make sure to provide a valid repository path.",
|
||||||
|
)
|
||||||
|
.0,
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::AnalysisFailed(html) => res.body(html.0.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,35 @@
|
||||||
use std::{env, sync::Arc, time::Instant};
|
use std::env;
|
||||||
|
|
||||||
|
use actix_web::{
|
||||||
|
get,
|
||||||
|
http::{
|
||||||
|
header::{ContentType, ETag, EntityTag},
|
||||||
|
Uri,
|
||||||
|
},
|
||||||
|
web::{Redirect, ServiceConfig},
|
||||||
|
Either, HttpResponse, Resource, Responder,
|
||||||
|
};
|
||||||
|
use actix_web_lab::{
|
||||||
|
extract::{Path, ThinData},
|
||||||
|
header::{CacheControl, CacheDirective},
|
||||||
|
respond::Html,
|
||||||
|
};
|
||||||
|
use assets::STATIC_FAVICON_PATH;
|
||||||
use badge::BadgeStyle;
|
use badge::BadgeStyle;
|
||||||
use futures_util::future;
|
use futures_util::future;
|
||||||
use hyper::{
|
|
||||||
header::{CACHE_CONTROL, CONTENT_TYPE, ETAG, LOCATION},
|
|
||||||
Body, Error as HyperError, Method, Request, Response, StatusCode,
|
|
||||||
};
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use route_recognizer::{Params, Router};
|
|
||||||
use semver::VersionReq;
|
use semver::VersionReq;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
|
mod error;
|
||||||
mod views;
|
mod views;
|
||||||
|
|
||||||
use self::assets::{
|
use self::{
|
||||||
|
assets::{
|
||||||
STATIC_LINKS_JS_ETAG, STATIC_LINKS_JS_PATH, STATIC_STYLE_CSS_ETAG, STATIC_STYLE_CSS_PATH,
|
STATIC_LINKS_JS_ETAG, STATIC_LINKS_JS_PATH, STATIC_STYLE_CSS_ETAG, STATIC_STYLE_CSS_PATH,
|
||||||
|
},
|
||||||
|
error::ServerError,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::{AnalyzeDependenciesOutcome, Engine},
|
engine::{AnalyzeDependenciesOutcome, Engine},
|
||||||
|
@ -32,244 +46,109 @@ enum StatusFormat {
|
||||||
Svg,
|
Svg,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[get("/")]
|
||||||
enum StaticFile {
|
pub(crate) async fn index(ThinData(engine): ThinData<Engine>) -> actix_web::Result<impl Responder> {
|
||||||
StyleCss,
|
let popular = future::try_join(engine.get_popular_repos(), engine.get_popular_crates()).await;
|
||||||
FaviconPng,
|
|
||||||
LinksJs,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Route {
|
|
||||||
Index,
|
|
||||||
Static(StaticFile),
|
|
||||||
RepoStatus(StatusFormat),
|
|
||||||
CrateRedirect,
|
|
||||||
CrateStatus(StatusFormat),
|
|
||||||
LatestCrateBadge,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct App {
|
|
||||||
engine: Engine,
|
|
||||||
router: Arc<Router<Route>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn new(engine: Engine) -> App {
|
|
||||||
let mut router = Router::new();
|
|
||||||
|
|
||||||
router.add("/", Route::Index);
|
|
||||||
|
|
||||||
router.add(STATIC_STYLE_CSS_PATH, Route::Static(StaticFile::StyleCss));
|
|
||||||
router.add("/static/logo.svg", Route::Static(StaticFile::FaviconPng));
|
|
||||||
router.add(STATIC_LINKS_JS_PATH, Route::Static(StaticFile::LinksJs));
|
|
||||||
|
|
||||||
router.add(
|
|
||||||
"/repo/*site/:qual/:name",
|
|
||||||
Route::RepoStatus(StatusFormat::Html),
|
|
||||||
);
|
|
||||||
router.add(
|
|
||||||
"/repo/*site/:qual/:name/status.svg",
|
|
||||||
Route::RepoStatus(StatusFormat::Svg),
|
|
||||||
);
|
|
||||||
|
|
||||||
router.add("/crate/:name", Route::CrateRedirect);
|
|
||||||
router.add(
|
|
||||||
"/crate/:name/:version",
|
|
||||||
Route::CrateStatus(StatusFormat::Html),
|
|
||||||
);
|
|
||||||
router.add("/crate/:name/latest/status.svg", Route::LatestCrateBadge);
|
|
||||||
router.add(
|
|
||||||
"/crate/:name/:version/status.svg",
|
|
||||||
Route::CrateStatus(StatusFormat::Svg),
|
|
||||||
);
|
|
||||||
|
|
||||||
App {
|
|
||||||
engine,
|
|
||||||
router: Arc::new(router),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn handle(&self, req: Request<Body>) -> Result<Response<Body>, HyperError> {
|
|
||||||
let start = Instant::now();
|
|
||||||
|
|
||||||
// allows `/path/` to also match `/path`
|
|
||||||
let normalized_path = req.uri().path().trim_end_matches('/');
|
|
||||||
|
|
||||||
let res = if let Ok(route_match) = self.router.recognize(normalized_path) {
|
|
||||||
match (req.method(), route_match.handler()) {
|
|
||||||
(&Method::GET, Route::Index) => self.index(req, route_match.params().clone()).await,
|
|
||||||
|
|
||||||
(&Method::GET, Route::RepoStatus(format)) => {
|
|
||||||
self.repo_status(req, route_match.params().clone(), *format)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
(&Method::GET, Route::CrateStatus(format)) => {
|
|
||||||
self.crate_status(req, route_match.params().clone(), *format)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
(&Method::GET, Route::LatestCrateBadge) => {
|
|
||||||
self.crate_status(req, route_match.params().clone(), StatusFormat::Svg)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
(&Method::GET, Route::CrateRedirect) => {
|
|
||||||
self.crate_redirect(req, route_match.params().clone()).await
|
|
||||||
}
|
|
||||||
|
|
||||||
(&Method::GET, Route::Static(file)) => Ok(App::static_file(*file)),
|
|
||||||
|
|
||||||
_ => Ok(not_found()),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(not_found())
|
|
||||||
};
|
|
||||||
|
|
||||||
let end = Instant::now();
|
|
||||||
let diff = end - start;
|
|
||||||
|
|
||||||
match &res {
|
|
||||||
Ok(res) => tracing::info!(
|
|
||||||
status = %res.status(),
|
|
||||||
time = %format_args!("{}ms", diff.as_millis()),
|
|
||||||
),
|
|
||||||
Err(err) => tracing::error!(%err),
|
|
||||||
};
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
async fn index(
|
|
||||||
&self,
|
|
||||||
_req: Request<Body>,
|
|
||||||
_params: Params,
|
|
||||||
) -> Result<Response<Body>, HyperError> {
|
|
||||||
let engine = self.engine.clone();
|
|
||||||
|
|
||||||
let popular =
|
|
||||||
future::try_join(engine.get_popular_repos(), engine.get_popular_crates()).await;
|
|
||||||
|
|
||||||
match popular {
|
match popular {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let mut response =
|
Err(ServerError::PopularItemsFailed.into())
|
||||||
views::html::error::render("Could not retrieve popular items", "");
|
|
||||||
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
|
||||||
Ok(response)
|
|
||||||
}
|
}
|
||||||
Ok((popular_repos, popular_crates)) => {
|
Ok((popular_repos, popular_crates)) => Ok(Html::new(
|
||||||
Ok(views::html::index::render(popular_repos, popular_crates))
|
views::html::index::render(popular_repos, popular_crates).0,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/repo/{site:.+?}/{qual}/{name}/status.svg")]
|
||||||
|
pub(crate) async fn repo_status_svg(
|
||||||
|
ThinData(engine): ThinData<Engine>,
|
||||||
|
uri: Uri,
|
||||||
|
Path(params): Path<(String, String, String)>,
|
||||||
|
) -> actix_web::Result<impl Responder> {
|
||||||
|
repo_status(engine, uri, params, StatusFormat::Svg).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/repo/{site:.+?}/{qual}/{name}")]
|
||||||
|
pub(crate) async fn repo_status_html(
|
||||||
|
ThinData(engine): ThinData<Engine>,
|
||||||
|
uri: Uri,
|
||||||
|
Path(params): Path<(String, String, String)>,
|
||||||
|
) -> actix_web::Result<impl Responder> {
|
||||||
|
repo_status(engine, uri, params, StatusFormat::Html).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn repo_status(
|
async fn repo_status(
|
||||||
&self,
|
engine: Engine,
|
||||||
req: Request<Body>,
|
uri: Uri,
|
||||||
params: Params,
|
(site, qual, name): (String, String, String),
|
||||||
format: StatusFormat,
|
format: StatusFormat,
|
||||||
) -> Result<Response<Body>, HyperError> {
|
) -> actix_web::Result<impl Responder> {
|
||||||
let server = self.clone();
|
let extra_knobs = ExtraConfig::from_query_string(uri.query());
|
||||||
|
|
||||||
let site = params.find("site").expect("route param 'site' not found");
|
let repo_path_result = RepoPath::from_parts(&site, &qual, &name);
|
||||||
let qual = params.find("qual").expect("route param 'qual' not found");
|
|
||||||
let name = params.find("name").expect("route param 'name' not found");
|
|
||||||
|
|
||||||
let extra_knobs = ExtraConfig::from_query_string(req.uri().query());
|
let repo_path = match repo_path_result {
|
||||||
|
Ok(repo_path) => repo_path,
|
||||||
let repo_path_result = RepoPath::from_parts(site, qual, name);
|
|
||||||
|
|
||||||
match repo_path_result {
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let mut response = views::html::error::render(
|
return Err(ServerError::BadRepoPath.into());
|
||||||
"Could not parse repository path",
|
|
||||||
"Please make sure to provide a valid repository path.",
|
|
||||||
);
|
|
||||||
*response.status_mut() = StatusCode::BAD_REQUEST;
|
|
||||||
Ok(response)
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(repo_path) => {
|
let analyze_result = engine
|
||||||
let analyze_result = server
|
|
||||||
.engine
|
|
||||||
.analyze_repo_dependencies(repo_path.clone(), &extra_knobs.path)
|
.analyze_repo_dependencies(repo_path.clone(), &extra_knobs.path)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match analyze_result {
|
match analyze_result {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let response = App::status_format_analysis(
|
let response =
|
||||||
None,
|
status_format_analysis(None, format, SubjectPath::Repo(repo_path), extra_knobs);
|
||||||
format,
|
|
||||||
SubjectPath::Repo(repo_path),
|
|
||||||
extra_knobs,
|
|
||||||
);
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(analysis_outcome) => {
|
Ok(analysis_outcome) => {
|
||||||
let response = App::status_format_analysis(
|
let response = status_format_analysis(
|
||||||
Some(analysis_outcome),
|
Some(analysis_outcome),
|
||||||
format,
|
format,
|
||||||
SubjectPath::Repo(repo_path),
|
SubjectPath::Repo(repo_path),
|
||||||
extra_knobs,
|
extra_knobs,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[get("/crate/:name")]
|
||||||
async fn crate_redirect(
|
async fn crate_redirect(
|
||||||
&self,
|
ThinData(engine): ThinData<Engine>,
|
||||||
_req: Request<Body>,
|
Path((name,)): Path<(String,)>,
|
||||||
params: Params,
|
) -> actix_web::Result<impl Responder> {
|
||||||
) -> Result<Response<Body>, HyperError> {
|
|
||||||
let engine = self.engine.clone();
|
|
||||||
|
|
||||||
let name = params.find("name").expect("route param 'name' not found");
|
|
||||||
let crate_name_result = name.parse::<CrateName>();
|
let crate_name_result = name.parse::<CrateName>();
|
||||||
|
|
||||||
match crate_name_result {
|
let crate_name = match crate_name_result {
|
||||||
|
Ok(crate_name) => crate_name,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let mut response = views::html::error::render(
|
return Err(ServerError::BadCratePath.into());
|
||||||
"Could not parse crate name",
|
|
||||||
"Please make sure to provide a valid crate name.",
|
|
||||||
);
|
|
||||||
*response.status_mut() = StatusCode::BAD_REQUEST;
|
|
||||||
Ok(response)
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(crate_name) => {
|
|
||||||
let release_result = engine
|
let release_result = engine
|
||||||
.find_latest_stable_crate_release(crate_name, VersionReq::STAR)
|
.find_latest_stable_crate_release(crate_name, VersionReq::STAR)
|
||||||
.await;
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
match release_result {
|
|
||||||
Err(err) => {
|
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let mut response = views::html::error::render(
|
});
|
||||||
"Could not fetch crate information",
|
|
||||||
"Please make sure to provide a valid crate name.",
|
let Ok(Some(release)) = release_result else {
|
||||||
);
|
return Err(ServerError::CrateFetchFailed.into());
|
||||||
*response.status_mut() = StatusCode::NOT_FOUND;
|
};
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
Ok(None) => {
|
|
||||||
let mut response = views::html::error::render(
|
|
||||||
"Could not fetch crate information",
|
|
||||||
"Please make sure to provide a valid crate name.",
|
|
||||||
);
|
|
||||||
*response.status_mut() = StatusCode::NOT_FOUND;
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
Ok(Some(release)) => {
|
|
||||||
let redirect_url = format!(
|
let redirect_url = format!(
|
||||||
"{}/crate/{}/{}",
|
"{}/crate/{}/{}",
|
||||||
&SELF_BASE_URL as &str,
|
&SELF_BASE_URL as &str,
|
||||||
|
@ -277,97 +156,86 @@ impl App {
|
||||||
release.version
|
release.version
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = Response::builder()
|
Ok(Redirect::to(redirect_url))
|
||||||
.status(StatusCode::TEMPORARY_REDIRECT)
|
}
|
||||||
.header(LOCATION, redirect_url)
|
|
||||||
.body(Body::empty())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(res)
|
#[get("/crate/{name}/{version}")]
|
||||||
}
|
async fn crate_status_html(
|
||||||
}
|
ThinData(engine): ThinData<Engine>,
|
||||||
|
uri: Uri,
|
||||||
|
Path((name, version)): Path<(String, String)>,
|
||||||
|
) -> actix_web::Result<impl Responder> {
|
||||||
|
crate_status(engine, uri, (name, Some(version)), StatusFormat::Html).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/crate/{name}/latest/status.svg")]
|
||||||
|
async fn crate_latest_status_svg(
|
||||||
|
ThinData(engine): ThinData<Engine>,
|
||||||
|
uri: Uri,
|
||||||
|
Path((name,)): Path<(String,)>,
|
||||||
|
) -> actix_web::Result<impl Responder> {
|
||||||
|
crate_status(engine, uri, (name, None), StatusFormat::Svg).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/crate/{name}/{version}/status.svg")]
|
||||||
|
async fn crate_status_svg(
|
||||||
|
ThinData(engine): ThinData<Engine>,
|
||||||
|
uri: Uri,
|
||||||
|
Path((name, version)): Path<(String, String)>,
|
||||||
|
) -> actix_web::Result<impl Responder> {
|
||||||
|
crate_status(engine, uri, (name, Some(version)), StatusFormat::Svg).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn crate_status(
|
async fn crate_status(
|
||||||
&self,
|
engine: Engine,
|
||||||
req: Request<Body>,
|
uri: Uri,
|
||||||
params: Params,
|
(name, version): (String, Option<String>),
|
||||||
format: StatusFormat,
|
format: StatusFormat,
|
||||||
) -> Result<Response<Body>, HyperError> {
|
) -> actix_web::Result<impl Responder> {
|
||||||
let server = self.clone();
|
let version = match version {
|
||||||
|
|
||||||
let name = params.find("name").expect("route param 'name' not found");
|
|
||||||
|
|
||||||
let version = match params.find("version") {
|
|
||||||
Some(ver) => ver.to_owned(),
|
Some(ver) => ver.to_owned(),
|
||||||
None => {
|
None => {
|
||||||
let crate_name = match name.parse() {
|
let crate_name = match name.parse() {
|
||||||
Ok(name) => name,
|
Ok(name) => name,
|
||||||
Err(_) => {
|
Err(_) => return Err(ServerError::BadCratePath.into()),
|
||||||
let mut response = views::html::error::render(
|
|
||||||
"Could not parse crate path",
|
|
||||||
"Please make sure to provide a valid crate name and version.",
|
|
||||||
);
|
|
||||||
*response.status_mut() = StatusCode::BAD_REQUEST;
|
|
||||||
return Ok(response);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match server
|
match engine
|
||||||
.engine
|
|
||||||
.find_latest_stable_crate_release(crate_name, VersionReq::STAR)
|
.find_latest_stable_crate_release(crate_name, VersionReq::STAR)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(Some(latest_rel)) => latest_rel.version.to_string(),
|
Ok(Some(latest_rel)) => latest_rel.version.to_string(),
|
||||||
Ok(None) => return Ok(not_found()),
|
|
||||||
|
Ok(None) => return Err(ServerError::CrateNotFound.into()),
|
||||||
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let mut response = views::html::error::render(
|
return Err(ServerError::CrateFetchFailed.into());
|
||||||
"Could not fetch crate information",
|
|
||||||
"Please make sure to provide a valid crate name.",
|
|
||||||
);
|
|
||||||
*response.status_mut() = StatusCode::NOT_FOUND;
|
|
||||||
return Ok(response);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let crate_path_result = CratePath::from_parts(name, &version);
|
let crate_path_result = CratePath::from_parts(&name, &version);
|
||||||
let badge_knobs = ExtraConfig::from_query_string(req.uri().query());
|
let badge_knobs = ExtraConfig::from_query_string(uri.query());
|
||||||
|
|
||||||
match crate_path_result {
|
match crate_path_result {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let mut response = views::html::error::render(
|
Err(ServerError::BadCratePath.into())
|
||||||
"Could not parse crate path",
|
|
||||||
"Please make sure to provide a valid crate name and version.",
|
|
||||||
);
|
|
||||||
*response.status_mut() = StatusCode::BAD_REQUEST;
|
|
||||||
Ok(response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(crate_path) => {
|
Ok(crate_path) => {
|
||||||
let analyze_result = server
|
let analysis_outcome = engine
|
||||||
.engine
|
|
||||||
.analyze_crate_dependencies(crate_path.clone())
|
.analyze_crate_dependencies(crate_path.clone())
|
||||||
.await;
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
match analyze_result {
|
|
||||||
Err(err) => {
|
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let response = App::status_format_analysis(
|
})
|
||||||
None,
|
.ok();
|
||||||
format,
|
|
||||||
SubjectPath::Crate(crate_path),
|
let response = status_format_analysis(
|
||||||
badge_knobs,
|
analysis_outcome,
|
||||||
);
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
Ok(analysis_outcome) => {
|
|
||||||
let response = App::status_format_analysis(
|
|
||||||
Some(analysis_outcome),
|
|
||||||
format,
|
format,
|
||||||
SubjectPath::Crate(crate_path),
|
SubjectPath::Crate(crate_path),
|
||||||
badge_knobs,
|
badge_knobs,
|
||||||
|
@ -377,47 +245,61 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn status_format_analysis(
|
fn status_format_analysis(
|
||||||
analysis_outcome: Option<AnalyzeDependenciesOutcome>,
|
analysis_outcome: Option<AnalyzeDependenciesOutcome>,
|
||||||
format: StatusFormat,
|
format: StatusFormat,
|
||||||
subject_path: SubjectPath,
|
subject_path: SubjectPath,
|
||||||
badge_knobs: ExtraConfig,
|
badge_knobs: ExtraConfig,
|
||||||
) -> Response<Body> {
|
) -> impl Responder {
|
||||||
match format {
|
match format {
|
||||||
StatusFormat::Svg => views::badge::response(analysis_outcome.as_ref(), badge_knobs),
|
StatusFormat::Svg => Either::Left(views::badge::response(
|
||||||
StatusFormat::Html => {
|
analysis_outcome.as_ref(),
|
||||||
views::html::status::render(analysis_outcome, subject_path, badge_knobs)
|
badge_knobs,
|
||||||
}
|
)),
|
||||||
|
|
||||||
|
StatusFormat::Html => Either::Right(views::html::status::response(
|
||||||
|
analysis_outcome,
|
||||||
|
subject_path,
|
||||||
|
badge_knobs,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn static_file(file: StaticFile) -> Response<Body> {
|
pub(crate) fn static_files(cfg: &mut ServiceConfig) {
|
||||||
match file {
|
cfg.service(Resource::new(STATIC_STYLE_CSS_PATH).get(|| async {
|
||||||
StaticFile::StyleCss => Response::builder()
|
HttpResponse::Ok()
|
||||||
.header(CONTENT_TYPE, "text/css; charset=utf-8")
|
.insert_header(ContentType(mime::TEXT_CSS_UTF_8))
|
||||||
.header(ETAG, STATIC_STYLE_CSS_ETAG)
|
.insert_header(ETag(EntityTag::new_strong(
|
||||||
.header(CACHE_CONTROL, "public, max-age=365000000, immutable")
|
STATIC_STYLE_CSS_ETAG.to_owned(),
|
||||||
.body(Body::from(assets::STATIC_STYLE_CSS))
|
)))
|
||||||
.unwrap(),
|
.insert_header(CacheControl(vec![
|
||||||
StaticFile::FaviconPng => Response::builder()
|
CacheDirective::Public,
|
||||||
.header(CONTENT_TYPE, "image/svg+xml")
|
CacheDirective::MaxAge(365000000),
|
||||||
.body(Body::from(assets::STATIC_FAVICON))
|
CacheDirective::Immutable,
|
||||||
.unwrap(),
|
]))
|
||||||
StaticFile::LinksJs => Response::builder()
|
.body(assets::STATIC_STYLE_CSS)
|
||||||
.header(CONTENT_TYPE, "text/javascript; charset=utf-8")
|
}))
|
||||||
.header(ETAG, STATIC_LINKS_JS_ETAG)
|
.service(Resource::new(STATIC_FAVICON_PATH).get(|| async {
|
||||||
.header(CACHE_CONTROL, "public, max-age=365000000, immutable")
|
HttpResponse::Ok()
|
||||||
.body(Body::from(assets::STATIC_LINKS_JS))
|
.insert_header(ContentType(mime::IMAGE_SVG))
|
||||||
.unwrap(),
|
.body(assets::STATIC_FAVICON)
|
||||||
}
|
}))
|
||||||
}
|
.service(Resource::new(STATIC_LINKS_JS_PATH).get(|| async {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.insert_header(ContentType(mime::APPLICATION_JAVASCRIPT_UTF_8))
|
||||||
|
.insert_header(ETag(EntityTag::new_strong(STATIC_LINKS_JS_ETAG.to_owned())))
|
||||||
|
.insert_header(CacheControl(vec![
|
||||||
|
CacheDirective::Public,
|
||||||
|
CacheDirective::MaxAge(365000000),
|
||||||
|
CacheDirective::Immutable,
|
||||||
|
]))
|
||||||
|
.body(assets::STATIC_LINKS_JS)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn not_found() -> Response<Body> {
|
pub(crate) async fn not_found() -> impl Responder {
|
||||||
views::html::error::render_404()
|
Html::new(views::html::error::render_404().0)
|
||||||
}
|
}
|
||||||
|
|
||||||
static SELF_BASE_URL: Lazy<String> =
|
static SELF_BASE_URL: Lazy<String> =
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
use actix_web::{http::header::ContentType, HttpResponse};
|
||||||
use badge::{Badge, BadgeOptions};
|
use badge::{Badge, BadgeOptions};
|
||||||
use hyper::{header::CONTENT_TYPE, Body, Response};
|
|
||||||
|
|
||||||
use crate::{engine::AnalyzeDependenciesOutcome, server::ExtraConfig};
|
use crate::{engine::AnalyzeDependenciesOutcome, server::ExtraConfig};
|
||||||
|
|
||||||
|
@ -73,11 +73,10 @@ pub fn badge(
|
||||||
pub fn response(
|
pub fn response(
|
||||||
analysis_outcome: Option<&AnalyzeDependenciesOutcome>,
|
analysis_outcome: Option<&AnalyzeDependenciesOutcome>,
|
||||||
badge_knobs: ExtraConfig,
|
badge_knobs: ExtraConfig,
|
||||||
) -> Response<Body> {
|
) -> HttpResponse {
|
||||||
let badge = badge(analysis_outcome, badge_knobs).to_svg();
|
let badge = badge(analysis_outcome, badge_knobs).to_svg();
|
||||||
|
|
||||||
Response::builder()
|
HttpResponse::Ok()
|
||||||
.header(CONTENT_TYPE, "image/svg+xml; charset=utf-8")
|
.insert_header(ContentType(mime::IMAGE_SVG))
|
||||||
.body(Body::from(badge))
|
.body(badge)
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use hyper::{
|
use maud::{html, Markup};
|
||||||
header::{CACHE_CONTROL, CONTENT_TYPE},
|
|
||||||
Body, Response, StatusCode,
|
|
||||||
};
|
|
||||||
use maud::html;
|
|
||||||
|
|
||||||
use crate::server::assets::STATIC_STYLE_CSS_PATH;
|
use crate::server::assets::STATIC_STYLE_CSS_PATH;
|
||||||
|
|
||||||
pub fn render(title: &str, descr: &str) -> Response<Body> {
|
pub fn render(title: impl Into<String>, desc: &str) -> Markup {
|
||||||
|
let title = title.into();
|
||||||
|
|
||||||
super::render_html(
|
super::render_html(
|
||||||
title,
|
title.clone(),
|
||||||
html! {
|
html! {
|
||||||
section class="hero is-light" {
|
section class="hero is-light" {
|
||||||
div class="hero-head" { (super::render_navbar()) }
|
div class="hero-head" { (super::render_navbar()) }
|
||||||
|
@ -17,7 +15,7 @@ pub fn render(title: &str, descr: &str) -> Response<Body> {
|
||||||
div class="container" {
|
div class="container" {
|
||||||
div class="notification is-danger" {
|
div class="notification is-danger" {
|
||||||
p class="title is-3" { (title) }
|
p class="title is-3" { (title) }
|
||||||
p { (descr) }
|
p { (desc) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +24,8 @@ pub fn render(title: &str, descr: &str) -> Response<Body> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_404() -> Response<Body> {
|
pub fn render_404() -> Markup {
|
||||||
let rendered = html! {
|
html! {
|
||||||
html {
|
html {
|
||||||
head {
|
head {
|
||||||
meta charset="utf-8";
|
meta charset="utf-8";
|
||||||
|
@ -53,12 +51,5 @@ pub fn render_404() -> Response<Body> {
|
||||||
(super::render_footer(None))
|
(super::render_footer(None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Response::builder()
|
|
||||||
.status(StatusCode::NOT_FOUND)
|
|
||||||
.header(CONTENT_TYPE, "text/html; charset=utf-8")
|
|
||||||
.header(CACHE_CONTROL, "public, max-age=300, immutable")
|
|
||||||
.body(Body::from(rendered.0))
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use hyper::{Body, Response};
|
|
||||||
use maud::{html, Markup};
|
use maud::{html, Markup};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -161,7 +160,7 @@ fn popular_table(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>) -> Response<Body> {
|
pub fn render(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>) -> Markup {
|
||||||
super::render_html(
|
super::render_html(
|
||||||
"Keep your dependencies up-to-date",
|
"Keep your dependencies up-to-date",
|
||||||
html! {
|
html! {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use hyper::{header::CONTENT_TYPE, Body, Response};
|
|
||||||
use maud::{html, Markup, Render, DOCTYPE};
|
use maud::{html, Markup, Render, DOCTYPE};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -9,8 +8,10 @@ pub mod status;
|
||||||
|
|
||||||
use crate::server::{assets::STATIC_STYLE_CSS_PATH, SELF_BASE_URL};
|
use crate::server::{assets::STATIC_STYLE_CSS_PATH, SELF_BASE_URL};
|
||||||
|
|
||||||
fn render_html<B: Render>(title: &str, body: B) -> Response<Body> {
|
fn render_html<B: Render>(title: impl Into<String>, body: B) -> Markup {
|
||||||
let rendered = html! {
|
let title = title.into();
|
||||||
|
|
||||||
|
html! {
|
||||||
(DOCTYPE)
|
(DOCTYPE)
|
||||||
html {
|
html {
|
||||||
head {
|
head {
|
||||||
|
@ -24,12 +25,7 @@ fn render_html<B: Render>(title: &str, body: B) -> Response<Body> {
|
||||||
}
|
}
|
||||||
body { (body) }
|
body { (body) }
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Response::builder()
|
|
||||||
.header(CONTENT_TYPE, "text/html; charset=utf-8")
|
|
||||||
.body(Body::from(rendered.0))
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_navbar() -> Markup {
|
fn render_navbar() -> Markup {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use actix_web::Responder;
|
||||||
|
use actix_web_lab::respond::Html;
|
||||||
use font_awesome_as_a_crate::{svg as fa, Type as FaType};
|
use font_awesome_as_a_crate::{svg as fa, Type as FaType};
|
||||||
use hyper::{Body, Response};
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use maud::{html, Markup, PreEscaped};
|
use maud::{html, Markup, PreEscaped};
|
||||||
use pulldown_cmark::{html, Parser};
|
use pulldown_cmark::{html, Parser};
|
||||||
|
@ -13,9 +14,11 @@ use crate::{
|
||||||
repo::RepoSite,
|
repo::RepoSite,
|
||||||
SubjectPath,
|
SubjectPath,
|
||||||
},
|
},
|
||||||
server::{views::badge, ExtraConfig},
|
server::{error::ServerError, views::badge, ExtraConfig},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::render_html;
|
||||||
|
|
||||||
fn get_crates_url(name: impl AsRef<str>) -> String {
|
fn get_crates_url(name: impl AsRef<str>) -> String {
|
||||||
format!("https://crates.io/crates/{}", name.as_ref())
|
format!("https://crates.io/crates/{}", name.as_ref())
|
||||||
}
|
}
|
||||||
|
@ -453,11 +456,11 @@ fn render_success(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(
|
pub fn response(
|
||||||
analysis_outcome: Option<AnalyzeDependenciesOutcome>,
|
analysis_outcome: Option<AnalyzeDependenciesOutcome>,
|
||||||
subject_path: SubjectPath,
|
subject_path: SubjectPath,
|
||||||
extra_config: ExtraConfig,
|
extra_config: ExtraConfig,
|
||||||
) -> Response<Body> {
|
) -> actix_web::Result<impl Responder> {
|
||||||
let title = match subject_path {
|
let title = match subject_path {
|
||||||
SubjectPath::Repo(ref repo_path) => {
|
SubjectPath::Repo(ref repo_path) => {
|
||||||
format!("{} / {}", repo_path.qual.as_ref(), repo_path.name.as_ref())
|
format!("{} / {}", repo_path.qual.as_ref(), repo_path.name.as_ref())
|
||||||
|
@ -468,8 +471,12 @@ pub fn render(
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(outcome) = analysis_outcome {
|
if let Some(outcome) = analysis_outcome {
|
||||||
super::render_html(&title, render_success(outcome, subject_path, extra_config))
|
Ok(Html::new(render_html(
|
||||||
|
&title,
|
||||||
|
render_success(outcome, subject_path, extra_config),
|
||||||
|
)))
|
||||||
} else {
|
} else {
|
||||||
super::render_html(&title, render_failure(subject_path))
|
let html = render_html(&title, render_failure(subject_path));
|
||||||
|
Err(ServerError::AnalysisFailed(html).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{fmt, sync::Arc, time::Duration};
|
use std::{fmt, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use actix_web::dev::Service;
|
||||||
use derive_more::{Display, Error, From};
|
use derive_more::{Display, Error, From};
|
||||||
use hyper::service::Service;
|
|
||||||
use lru_time_cache::LruCache;
|
use lru_time_cache::LruCache;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ where
|
||||||
cache = "miss",
|
cache = "miss",
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut service = self.inner.clone();
|
let service = self.inner.clone();
|
||||||
let fresh = service.call(req.clone()).await?;
|
let fresh = service.call(req.clone()).await?;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue