1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
pub mod driver;
pub mod error;
mod middleware;
pub mod routes;
pub mod shutdown;
pub mod state;
use std::time::Duration;
use activitypub_federation::config::{FederationConfig, FederationMiddleware};
use axum::{
Router,
http::{HeaderName, StatusCode},
};
use tower_http::{
cors::{self, CorsLayer},
request_id::PropagateRequestIdLayer,
timeout::TimeoutLayer,
trace::TraceLayer,
};
use tracing::{error, info_span};
use utoipa::OpenApi;
use utoipa_axum::router::OpenApiRouter;
use crate::{
config::Config,
server::{
middleware::request_id::{REQUEST_ID_HEADER, add_request_id},
routes::auth::OAuthDoc,
state::{AppState, federation},
},
};
#[derive(OpenApi)]
#[openapi(
tags(
(name = routes::HEALTH, description = "Check API health"),
),
)]
pub struct ApiDoc;
pub async fn router(config: &Config, state: AppState) -> anyhow::Result<Router<()>> {
let state = federation::add_federation(state, config).await?;
let mut doc = ApiDoc::openapi();
doc.merge(OAuthDoc::openapi());
let (router, _api) = OpenApiRouter::with_openapi(doc)
.routes(utoipa_axum::routes!(routes::health_check))
.routes(utoipa_axum::routes!(routes::auth::auth))
.split_for_parts();
#[cfg(feature = "swagger")]
let router = router.merge(
utoipa_swagger_ui::SwaggerUi::new("/swagger-ui")
.url("/api-docs/swaggerdoc.json", _api.clone()),
);
#[cfg(feature = "redoc")]
let router = {
use utoipa_redoc::Servable as _;
router.merge(utoipa_redoc::Redoc::with_url("/redoc", _api.clone()))
};
#[cfg(feature = "scalar")]
let router = {
use utoipa_scalar::Servable as _;
router.merge(utoipa_scalar::Scalar::with_url("/scalar", _api.clone()))
};
#[cfg(feature = "rapidoc")]
let router = router.merge(
utoipa_rapidoc::RapiDoc::with_openapi("/api-docs/rapidoc.json", _api).path("/rapidoc"),
);
let router = router
.layer(
TraceLayer::new_for_http().make_span_with(|request: &axum::http::Request<_>| {
if let Some(request_id) = request.headers().get(REQUEST_ID_HEADER) {
info_span!(
"http_request",
request_id = ?request_id,
)
} else {
error!("could not extract request_id");
info_span!("http_request")
}
}),
)
.layer(TimeoutLayer::with_status_code(
StatusCode::REQUEST_TIMEOUT,
Duration::from_secs(config.server.request_timeout),
))
.layer(FederationMiddleware::new(state))
// send headers from request to response headers
.layer(PropagateRequestIdLayer::new(HeaderName::from_static(
REQUEST_ID_HEADER,
)))
.layer(axum::middleware::from_fn(add_request_id))
.layer(
CorsLayer::new()
.allow_origin(cors::Any)
.allow_headers(cors::Any)
.allow_methods(cors::Any),
);
Ok(router)
}
#[cfg(test)]
pub mod bootstrap {
use async_trait::async_trait;
use crate::server::driver::SellershutDriver;
#[derive(Debug, Default)]
pub struct TestDriver {}
#[async_trait]
impl SellershutDriver for TestDriver {
async fn hello(&self) {
todo!()
}
}
}
|