1mod config;
2mod error;
3mod handler;
4
5pub use config::SentryTunnelConfig;
6pub use error::SentryTunnelError;
7pub use handler::handle_sentry_tunnel_inner;
8
9#[cfg(feature = "extension")]
10use axum::{
11 body::Bytes,
12 extract::State,
13 http::StatusCode,
14 routing::post,
15 Router,
16};
17#[cfg(feature = "extension")]
18use std::sync::Arc;
19
20#[cfg(feature = "extension")]
21#[cfg_attr(feature = "utoipa", utoipa::path(
22 post,
23 path = "/tunnel",
24 request_body = Vec<u8>,
25 responses(
26 (status = 200, description = "Successfully tunneled to Sentry"),
27 (status = 400, description = "Bad request - invalid envelope or DSN"),
28 (status = 500, description = "Internal server error - failed to tunnel")
29 ),
30 tag = "sentry"
31))]
32pub async fn sentry_tunnel_handler(
34 State(config): State<Arc<SentryTunnelConfig>>,
35 body: Bytes,
36) -> Result<StatusCode, SentryTunnelError> {
37 handler::handle_sentry_tunnel(config, body).await
38}
39
40#[cfg(feature = "extension")]
41pub trait SentryTunnelExt {
43 fn sentry_tunnel(self, config: SentryTunnelConfig) -> Self;
45}
46
47#[cfg(feature = "extension")]
48impl SentryTunnelExt for Router {
49 fn sentry_tunnel(self, config: SentryTunnelConfig) -> Self {
50 let path = config.path.clone();
51 let config = Arc::new(config);
52
53 self.route(
54 &path,
55 post(sentry_tunnel_handler).with_state(config),
56 )
57 }
58}
59
60#[cfg(feature = "standalone")]
61pub fn create_sentry_tunnel_service(config: SentryTunnelConfig) -> Router {
63 Router::new().sentry_tunnel(config)
64}
65
66#[cfg(feature = "utoipa")]
67impl<S> SentryTunnelExt for utoipa_axum::router::OpenApiRouter<S>
69where
70 S: Clone + Send + Sync + 'static,
71{
72 fn sentry_tunnel(self, config: SentryTunnelConfig) -> Self {
73 let path = config.path.clone();
74 let config_arc = Arc::new(config);
75
76 self.route(&path, post(sentry_tunnel_handler).with_state(config_arc))
79 }
80}
81
82pub struct SentryTunnelBuilder {
84 config: SentryTunnelConfig,
85}
86
87impl SentryTunnelBuilder {
88 pub fn new(sentry_host: impl Into<String>) -> Self {
90 Self {
91 config: SentryTunnelConfig::new(sentry_host, vec![]),
92 }
93 }
94
95 pub fn allow_project_id(mut self, project_id: impl Into<String>) -> Self {
97 self.config.allowed_project_ids.push(project_id.into());
98 self
99 }
100
101 pub fn allow_project_ids<I, S>(mut self, project_ids: I) -> Self
103 where
104 I: IntoIterator<Item = S>,
105 S: Into<String>,
106 {
107 self.config.allowed_project_ids.extend(
108 project_ids.into_iter().map(|s| s.into())
109 );
110 self
111 }
112
113 pub fn path(mut self, path: impl Into<String>) -> Self {
115 self.config.path = path.into();
116 self
117 }
118
119 pub fn timeout_secs(mut self, timeout: u64) -> Self {
121 self.config.timeout_secs = timeout;
122 self
123 }
124
125 pub fn build(self) -> SentryTunnelConfig {
127 self.config
128 }
129}