warg_server/api/v1/
mod.rs1use crate::{
2 policy::{content::ContentPolicy, record::RecordPolicy},
3 services::CoreService,
4};
5use anyhow::Result;
6use axum::{
7 async_trait,
8 extract::{
9 rejection::{JsonRejection, PathRejection},
10 FromRequest, FromRequestParts,
11 },
12 http::{request::Parts, StatusCode},
13 response::IntoResponse,
14 Router,
15};
16use serde::{Serialize, Serializer};
17use std::{path::PathBuf, str::FromStr, sync::Arc};
18use url::Url;
19use warg_api::v1::REGISTRY_HEADER_NAME;
20
21pub mod content;
22pub mod fetch;
23pub mod ledger;
24pub mod monitor;
25pub mod package;
26pub mod proof;
27
28#[derive(FromRequest)]
32#[from_request(via(axum::Json), rejection(Error))]
33pub struct Json<T>(pub T);
34
35impl<T> IntoResponse for Json<T>
36where
37 T: Serialize,
38{
39 fn into_response(self) -> axum::response::Response {
40 axum::Json(self.0).into_response()
41 }
42}
43
44fn serialize_status<S>(status: &StatusCode, serializer: S) -> Result<S::Ok, S::Error>
45where
46 S: Serializer,
47{
48 serializer.serialize_u16(status.as_u16())
49}
50
51#[derive(Serialize, Debug)]
53pub struct Error {
54 #[serde(serialize_with = "serialize_status")]
55 status: StatusCode,
56 message: String,
57}
58
59impl From<JsonRejection> for Error {
60 fn from(rejection: JsonRejection) -> Self {
61 Self {
62 status: rejection.status(),
63 message: rejection.body_text(),
64 }
65 }
66}
67
68impl IntoResponse for Error {
69 fn into_response(self) -> axum::response::Response {
70 (self.status, axum::Json(self)).into_response()
71 }
72}
73
74#[derive(FromRequestParts)]
78#[from_request(via(axum::extract::Path), rejection(Error))]
79pub struct Path<T>(T);
80
81impl From<PathRejection> for Error {
82 fn from(rejection: PathRejection) -> Self {
83 Self {
84 status: rejection.status(),
85 message: rejection.body_text(),
86 }
87 }
88}
89
90pub async fn not_found() -> impl IntoResponse {
91 Error {
92 status: StatusCode::NOT_FOUND,
93 message: "the requested resource was not found".to_string(),
94 }
95}
96
97pub struct RegistryHeader(Option<String>);
100
101#[async_trait]
102impl<S> FromRequestParts<S> for RegistryHeader
103where
104 S: Send + Sync,
105{
106 type Rejection = (StatusCode, &'static str);
107
108 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
109 if parts.headers.contains_key(REGISTRY_HEADER_NAME) {
110 Err((
111 StatusCode::NOT_IMPLEMENTED,
112 "`Warg-Registry` header is not supported",
113 ))
114 } else {
115 Ok(RegistryHeader(None))
116 }
117 }
118}
119
120impl FromStr for RegistryHeader {
121 type Err = std::convert::Infallible;
122 fn from_str(src: &str) -> Result<Self, Self::Err> {
123 Ok(RegistryHeader(Some(src.to_string())))
124 }
125}
126
127pub fn create_router(
128 content_base_url: Url,
129 core: CoreService,
130 temp_dir: PathBuf,
131 files_dir: PathBuf,
132 content_policy: Option<Arc<dyn ContentPolicy>>,
133 record_policy: Option<Arc<dyn RecordPolicy>>,
134) -> Router {
135 let proof_config = proof::Config::new(core.clone());
136 let package_config = package::Config::new(
137 core.clone(),
138 files_dir.clone(),
139 temp_dir,
140 content_policy,
141 record_policy,
142 );
143 let fetch_config = fetch::Config::new(core.clone());
144 let content_config = content::Config::new(content_base_url, files_dir);
145 let monitor_config = monitor::Config::new(core.clone());
146 let ledger_config = ledger::Config::new(core);
147
148 Router::new()
149 .nest("/content", content_config.into_router())
150 .nest("/fetch", fetch_config.into_router())
151 .nest("/ledger", ledger_config.into_router())
152 .nest("/package", package_config.into_router())
153 .nest("/proof", proof_config.into_router())
154 .nest("/verify", monitor_config.into_router())
155 .fallback(not_found)
156}