mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-22 02:16:30 +00:00
feat: migrate to axum router
This commit is contained in:
parent
d4d0db2e1e
commit
3dfb480ba4
5 changed files with 165 additions and 201 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -2336,12 +2336,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"
|
||||||
|
@ -2647,7 +2641,6 @@ dependencies = [
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
"relative-path",
|
"relative-path",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"route-recognizer",
|
|
||||||
"rustsec",
|
"rustsec",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2656,6 +2649,7 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.8.13",
|
"toml 0.8.13",
|
||||||
"tower",
|
"tower",
|
||||||
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
@ -3034,6 +3028,23 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-http"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.5.0",
|
||||||
|
"bytes",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-layer"
|
name = "tower-layer"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
|
|
@ -31,7 +31,6 @@ 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"] }
|
||||||
|
@ -39,6 +38,7 @@ serde_urlencoded = "0.7"
|
||||||
tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
tower = "0.4"
|
tower = "0.4"
|
||||||
|
tower-http = { version = "0.5", features = ["normalize-path", "trace"] }
|
||||||
tracing = "0.1.30"
|
tracing = "0.1.30"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -9,10 +9,9 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use axum::{extract::Request, Router};
|
|
||||||
use cadence::{QueuingMetricSink, UdpMetricSink};
|
use cadence::{QueuingMetricSink, UdpMetricSink};
|
||||||
use reqwest::redirect::Policy as RedirectPolicy;
|
use reqwest::redirect::Policy as RedirectPolicy;
|
||||||
use tracing::Instrument as _;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
mod engine;
|
mod engine;
|
||||||
mod interactors;
|
mod interactors;
|
||||||
|
@ -90,18 +89,8 @@ async fn main() {
|
||||||
|
|
||||||
let app = App::new(engine.clone());
|
let app = App::new(engine.clone());
|
||||||
|
|
||||||
let lst = tokio::net::TcpListener::bind(addr).await.unwrap();
|
let lst = TcpListener::bind(addr).await.unwrap();
|
||||||
|
let server = axum::serve(lst, App::router().with_state(app));
|
||||||
let router = Router::new().fallback(|req: Request| async move {
|
|
||||||
let path = req.uri().path().to_owned();
|
|
||||||
|
|
||||||
app.handle(req)
|
|
||||||
.instrument(tracing::info_span!("@", %path))
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
let server = axum::serve(lst, router);
|
|
||||||
|
|
||||||
tracing::info!("Server running on port {port}");
|
tracing::info!("Server running on port {port}");
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ pub const STATIC_STYLE_CSS_ETAG: &str = concat!(
|
||||||
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"));
|
||||||
|
|
|
@ -1,26 +1,29 @@
|
||||||
use std::{env, sync::Arc, time::Instant};
|
use std::env;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Body,
|
body::Body,
|
||||||
extract::Request,
|
extract::{Path, Request, State},
|
||||||
http::{
|
http::{
|
||||||
header::{CACHE_CONTROL, CONTENT_TYPE, ETAG, LOCATION},
|
header::{CACHE_CONTROL, CONTENT_TYPE, ETAG},
|
||||||
Method, StatusCode,
|
StatusCode,
|
||||||
},
|
},
|
||||||
response::Response,
|
response::{IntoResponse as _, Redirect, Response},
|
||||||
|
routing::get,
|
||||||
|
Router,
|
||||||
};
|
};
|
||||||
use badge::BadgeStyle;
|
use badge::BadgeStyle;
|
||||||
use futures_util::future;
|
use futures_util::future;
|
||||||
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;
|
||||||
|
use tower_http::{normalize_path::NormalizePathLayer, trace::TraceLayer};
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
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_FAVICON_PATH, STATIC_LINKS_JS_ETAG, STATIC_LINKS_JS_PATH, STATIC_STYLE_CSS_ETAG,
|
||||||
|
STATIC_STYLE_CSS_PATH,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::{AnalyzeDependenciesOutcome, Engine},
|
engine::{AnalyzeDependenciesOutcome, Engine},
|
||||||
|
@ -44,116 +47,54 @@ enum StaticFile {
|
||||||
LinksJs,
|
LinksJs,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Route {
|
|
||||||
Index,
|
|
||||||
Static(StaticFile),
|
|
||||||
RepoStatus(StatusFormat),
|
|
||||||
CrateRedirect,
|
|
||||||
CrateStatus(StatusFormat),
|
|
||||||
LatestCrateBadge,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
engine: Engine,
|
engine: Engine,
|
||||||
router: Arc<Router<Route>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(engine: Engine) -> App {
|
pub fn new(engine: Engine) -> App {
|
||||||
let mut router = Router::new();
|
App { engine }
|
||||||
|
}
|
||||||
|
|
||||||
router.add("/", Route::Index);
|
pub(crate) fn router() -> Router<App> {
|
||||||
|
Router::new()
|
||||||
router.add(STATIC_STYLE_CSS_PATH, Route::Static(StaticFile::StyleCss));
|
.route("/", get(App::index))
|
||||||
router.add("/static/logo.svg", Route::Static(StaticFile::FaviconPng));
|
.route("/crate/:name", get(App::crate_redirect))
|
||||||
router.add(STATIC_LINKS_JS_PATH, Route::Static(StaticFile::LinksJs));
|
.route("/crate/:name/:version", get(App::crate_status_html))
|
||||||
|
.route(
|
||||||
router.add(
|
"/crate/:name/latest/status.svg",
|
||||||
"/repo/*site/:qual/:name",
|
get(App::crate_latest_status_svg),
|
||||||
Route::RepoStatus(StatusFormat::Html),
|
)
|
||||||
);
|
.route(
|
||||||
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",
|
"/crate/:name/:version/status.svg",
|
||||||
Route::CrateStatus(StatusFormat::Svg),
|
get(App::crate_status_svg),
|
||||||
);
|
)
|
||||||
|
// TODO: `:site` isn't quite right, original was `*site`
|
||||||
App {
|
.route("/repo/:site/:qual/:name", get(App::repo_status_html))
|
||||||
engine,
|
.route(
|
||||||
router: Arc::new(router),
|
"/repo/:site/:qual/:name/status.svg",
|
||||||
}
|
get(App::repo_status_svg),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
STATIC_STYLE_CSS_PATH,
|
||||||
|
get(|| App::static_file(StaticFile::StyleCss)),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
STATIC_FAVICON_PATH,
|
||||||
|
get(|| App::static_file(StaticFile::FaviconPng)),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
STATIC_LINKS_JS_PATH,
|
||||||
|
get(|| App::static_file(StaticFile::LinksJs)),
|
||||||
|
)
|
||||||
|
.fallback(|| async { not_found() })
|
||||||
|
.layer(NormalizePathLayer::trim_trailing_slash())
|
||||||
|
.layer(TraceLayer::new_for_http())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle(&self, req: Request<Body>) -> Result<Response<Body>, axum::Error> {
|
async fn index(State(app): State<App>) -> Response {
|
||||||
let start = Instant::now();
|
let engine = app.engine.clone();
|
||||||
|
|
||||||
// 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>, axum::Error> {
|
|
||||||
let engine = self.engine.clone();
|
|
||||||
|
|
||||||
let popular =
|
let popular =
|
||||||
future::try_join(engine.get_popular_repos(), engine.get_popular_crates()).await;
|
future::try_join(engine.get_popular_repos(), engine.get_popular_crates()).await;
|
||||||
|
@ -164,29 +105,42 @@ impl App {
|
||||||
let mut response =
|
let mut response =
|
||||||
views::html::error::render("Could not retrieve popular items", "");
|
views::html::error::render("Could not retrieve popular items", "");
|
||||||
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
||||||
Ok(response)
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((popular_repos, popular_crates)) => {
|
Ok((popular_repos, popular_crates)) => {
|
||||||
Ok(views::html::index::render(popular_repos, popular_crates))
|
views::html::index::render(popular_repos, popular_crates)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn repo_status_html(
|
||||||
|
State(app): State<App>,
|
||||||
|
Path(params): Path<(String, String, String)>,
|
||||||
|
req: Request,
|
||||||
|
) -> Response {
|
||||||
|
Self::repo_status(app, params, req, StatusFormat::Html).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn repo_status_svg(
|
||||||
|
State(app): State<App>,
|
||||||
|
Path(params): Path<(String, String, String)>,
|
||||||
|
req: Request,
|
||||||
|
) -> Response {
|
||||||
|
Self::repo_status(app, params, req, StatusFormat::Svg).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn repo_status(
|
async fn repo_status(
|
||||||
&self,
|
app: App,
|
||||||
req: Request<Body>,
|
(site, qual, name): (String, String, String),
|
||||||
params: Params,
|
req: Request,
|
||||||
format: StatusFormat,
|
format: StatusFormat,
|
||||||
) -> Result<Response<Body>, axum::Error> {
|
) -> Response {
|
||||||
let server = self.clone();
|
let engine = app.engine.clone();
|
||||||
|
|
||||||
let site = params.find("site").expect("route param 'site' not found");
|
|
||||||
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 extra_knobs = ExtraConfig::from_query_string(req.uri().query());
|
||||||
|
|
||||||
let repo_path_result = RepoPath::from_parts(site, qual, name);
|
let repo_path_result = RepoPath::from_parts(&site, &qual, &name);
|
||||||
|
|
||||||
match repo_path_result {
|
match repo_path_result {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -196,48 +150,40 @@ impl App {
|
||||||
"Please make sure to provide a valid repository path.",
|
"Please make sure to provide a valid repository path.",
|
||||||
);
|
);
|
||||||
*response.status_mut() = StatusCode::BAD_REQUEST;
|
*response.status_mut() = StatusCode::BAD_REQUEST;
|
||||||
Ok(response)
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(repo_path) => {
|
Ok(repo_path) => {
|
||||||
let analyze_result = server
|
let analyze_result = engine
|
||||||
.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(
|
|
||||||
|
App::status_format_analysis(
|
||||||
None,
|
None,
|
||||||
format,
|
format,
|
||||||
SubjectPath::Repo(repo_path),
|
SubjectPath::Repo(repo_path),
|
||||||
extra_knobs,
|
extra_knobs,
|
||||||
);
|
)
|
||||||
Ok(response)
|
|
||||||
}
|
}
|
||||||
Ok(analysis_outcome) => {
|
|
||||||
let response = App::status_format_analysis(
|
Ok(analysis_outcome) => App::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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn crate_redirect(
|
async fn crate_redirect(State(app): State<App>, Path((name,)): Path<(String,)>) -> Response {
|
||||||
&self,
|
let engine = app.engine.clone();
|
||||||
_req: Request<Body>,
|
|
||||||
params: Params,
|
|
||||||
) -> Result<Response<Body>, axum::Error> {
|
|
||||||
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 {
|
match crate_name_result {
|
||||||
|
@ -248,7 +194,7 @@ impl App {
|
||||||
"Please make sure to provide a valid crate name.",
|
"Please make sure to provide a valid crate name.",
|
||||||
);
|
);
|
||||||
*response.status_mut() = StatusCode::BAD_REQUEST;
|
*response.status_mut() = StatusCode::BAD_REQUEST;
|
||||||
Ok(response)
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(crate_name) => {
|
Ok(crate_name) => {
|
||||||
|
@ -264,7 +210,7 @@ impl App {
|
||||||
"Please make sure to provide a valid crate name.",
|
"Please make sure to provide a valid crate name.",
|
||||||
);
|
);
|
||||||
*response.status_mut() = StatusCode::NOT_FOUND;
|
*response.status_mut() = StatusCode::NOT_FOUND;
|
||||||
Ok(response)
|
response
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
let mut response = views::html::error::render(
|
let mut response = views::html::error::render(
|
||||||
|
@ -272,7 +218,7 @@ impl App {
|
||||||
"Please make sure to provide a valid crate name.",
|
"Please make sure to provide a valid crate name.",
|
||||||
);
|
);
|
||||||
*response.status_mut() = StatusCode::NOT_FOUND;
|
*response.status_mut() = StatusCode::NOT_FOUND;
|
||||||
Ok(response)
|
response
|
||||||
}
|
}
|
||||||
Ok(Some(release)) => {
|
Ok(Some(release)) => {
|
||||||
let redirect_url = format!(
|
let redirect_url = format!(
|
||||||
|
@ -282,31 +228,48 @@ impl App {
|
||||||
release.version
|
release.version
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = Response::builder()
|
Redirect::temporary(&redirect_url).into_response()
|
||||||
.status(StatusCode::TEMPORARY_REDIRECT)
|
}
|
||||||
.header(LOCATION, redirect_url)
|
}
|
||||||
.body(Body::empty())
|
}
|
||||||
.unwrap();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(res)
|
async fn crate_status_html(
|
||||||
}
|
State(app): State<App>,
|
||||||
}
|
Path((name, version)): Path<(String, String)>,
|
||||||
|
req: Request,
|
||||||
|
) -> Response {
|
||||||
|
Self::crate_status(app, (name, Some(version)), req, StatusFormat::Html).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn crate_status_svg(
|
||||||
|
State(app): State<App>,
|
||||||
|
Path((name, version)): Path<(String, String)>,
|
||||||
|
req: Request,
|
||||||
|
) -> Response {
|
||||||
|
Self::crate_status(app, (name, Some(version)), req, StatusFormat::Svg).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn crate_latest_status_svg(
|
||||||
|
State(app): State<App>,
|
||||||
|
Path((name,)): Path<(String,)>,
|
||||||
|
req: Request,
|
||||||
|
) -> Response {
|
||||||
|
Self::crate_status(app, (name, None), req, StatusFormat::Svg).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn crate_status(
|
async fn crate_status(
|
||||||
&self,
|
app: App,
|
||||||
req: Request<Body>,
|
(name, version): (String, Option<String>),
|
||||||
params: Params,
|
req: Request,
|
||||||
format: StatusFormat,
|
format: StatusFormat,
|
||||||
) -> Result<Response<Body>, axum::Error> {
|
) -> Response {
|
||||||
let server = self.clone();
|
let server = app.clone();
|
||||||
|
|
||||||
let name = params.find("name").expect("route param 'name' not found");
|
let version = match version {
|
||||||
|
|
||||||
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,
|
||||||
|
@ -316,7 +279,7 @@ impl App {
|
||||||
"Please make sure to provide a valid crate name and version.",
|
"Please make sure to provide a valid crate name and version.",
|
||||||
);
|
);
|
||||||
*response.status_mut() = StatusCode::BAD_REQUEST;
|
*response.status_mut() = StatusCode::BAD_REQUEST;
|
||||||
return Ok(response);
|
return response;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -326,7 +289,7 @@ impl App {
|
||||||
.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 not_found(),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let mut response = views::html::error::render(
|
let mut response = views::html::error::render(
|
||||||
|
@ -334,13 +297,13 @@ impl App {
|
||||||
"Please make sure to provide a valid crate name.",
|
"Please make sure to provide a valid crate name.",
|
||||||
);
|
);
|
||||||
*response.status_mut() = StatusCode::NOT_FOUND;
|
*response.status_mut() = StatusCode::NOT_FOUND;
|
||||||
return Ok(response);
|
return 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(req.uri().query());
|
||||||
|
|
||||||
match crate_path_result {
|
match crate_path_result {
|
||||||
|
@ -351,8 +314,9 @@ impl App {
|
||||||
"Please make sure to provide a valid crate name and version.",
|
"Please make sure to provide a valid crate name and version.",
|
||||||
);
|
);
|
||||||
*response.status_mut() = StatusCode::BAD_REQUEST;
|
*response.status_mut() = StatusCode::BAD_REQUEST;
|
||||||
Ok(response)
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(crate_path) => {
|
Ok(crate_path) => {
|
||||||
let analyze_result = server
|
let analyze_result = server
|
||||||
.engine
|
.engine
|
||||||
|
@ -362,24 +326,20 @@ impl App {
|
||||||
match analyze_result {
|
match analyze_result {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
let response = App::status_format_analysis(
|
App::status_format_analysis(
|
||||||
None,
|
None,
|
||||||
format,
|
format,
|
||||||
SubjectPath::Crate(crate_path),
|
SubjectPath::Crate(crate_path),
|
||||||
badge_knobs,
|
badge_knobs,
|
||||||
);
|
)
|
||||||
Ok(response)
|
|
||||||
}
|
}
|
||||||
Ok(analysis_outcome) => {
|
|
||||||
let response = App::status_format_analysis(
|
Ok(analysis_outcome) => App::status_format_analysis(
|
||||||
Some(analysis_outcome),
|
Some(analysis_outcome),
|
||||||
format,
|
format,
|
||||||
SubjectPath::Crate(crate_path),
|
SubjectPath::Crate(crate_path),
|
||||||
badge_knobs,
|
badge_knobs,
|
||||||
);
|
),
|
||||||
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,7 +350,7 @@ impl App {
|
||||||
format: StatusFormat,
|
format: StatusFormat,
|
||||||
subject_path: SubjectPath,
|
subject_path: SubjectPath,
|
||||||
badge_knobs: ExtraConfig,
|
badge_knobs: ExtraConfig,
|
||||||
) -> Response<Body> {
|
) -> Response {
|
||||||
match format {
|
match format {
|
||||||
StatusFormat::Svg => views::badge::response(analysis_outcome.as_ref(), badge_knobs),
|
StatusFormat::Svg => views::badge::response(analysis_outcome.as_ref(), badge_knobs),
|
||||||
StatusFormat::Html => {
|
StatusFormat::Html => {
|
||||||
|
@ -399,7 +359,7 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn static_file(file: StaticFile) -> Response<Body> {
|
async fn static_file(file: StaticFile) -> Response {
|
||||||
match file {
|
match file {
|
||||||
StaticFile::StyleCss => Response::builder()
|
StaticFile::StyleCss => Response::builder()
|
||||||
.header(CONTENT_TYPE, "text/css; charset=utf-8")
|
.header(CONTENT_TYPE, "text/css; charset=utf-8")
|
||||||
|
@ -407,10 +367,12 @@ impl App {
|
||||||
.header(CACHE_CONTROL, "public, max-age=365000000, immutable")
|
.header(CACHE_CONTROL, "public, max-age=365000000, immutable")
|
||||||
.body(Body::from(assets::STATIC_STYLE_CSS))
|
.body(Body::from(assets::STATIC_STYLE_CSS))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
|
||||||
StaticFile::FaviconPng => Response::builder()
|
StaticFile::FaviconPng => Response::builder()
|
||||||
.header(CONTENT_TYPE, "image/svg+xml")
|
.header(CONTENT_TYPE, "image/svg+xml")
|
||||||
.body(Body::from(assets::STATIC_FAVICON))
|
.body(Body::from(assets::STATIC_FAVICON))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
|
||||||
StaticFile::LinksJs => Response::builder()
|
StaticFile::LinksJs => Response::builder()
|
||||||
.header(CONTENT_TYPE, "text/javascript; charset=utf-8")
|
.header(CONTENT_TYPE, "text/javascript; charset=utf-8")
|
||||||
.header(ETAG, STATIC_LINKS_JS_ETAG)
|
.header(ETAG, STATIC_LINKS_JS_ETAG)
|
||||||
|
@ -421,7 +383,7 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn not_found() -> Response<Body> {
|
fn not_found() -> Response {
|
||||||
views::html::error::render_404()
|
views::html::error::render_404()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue