@@ -6,44 +6,64 @@ use hyper::{
6
6
Error , Server ,
7
7
} ;
8
8
use once_cell:: sync:: Lazy ;
9
- use primitives:: config:: Environment ;
9
+ use primitives:: { config:: Environment , ValidatorId } ;
10
10
use redis:: ConnectionInfo ;
11
11
use serde:: { Deserialize , Deserializer } ;
12
12
use slog:: { error, info} ;
13
13
14
+ use crate :: {
15
+ db:: { CampaignRemaining , DbPool } ,
16
+ middleware:: {
17
+ auth:: Authenticate ,
18
+ cors:: { cors, Cors } ,
19
+ Middleware ,
20
+ } ,
21
+ platform:: PlatformApi ,
22
+ response:: { map_response_error, ResponseError } ,
23
+ routes:: {
24
+ get_cfg,
25
+ routers:: { analytics_router, campaigns_router, channels_router} ,
26
+ } ,
27
+ } ;
28
+ use adapter:: Adapter ;
29
+ use hyper:: { Body , Method , Request , Response } ;
30
+ use redis:: aio:: MultiplexedConnection ;
31
+ use slog:: Logger ;
32
+
14
33
/// an error used when deserializing a [`Config`] instance from environment variables
15
34
/// see [`Config::from_env()`]
16
35
pub use envy:: Error as EnvError ;
17
36
18
- use crate :: Application ;
19
-
20
37
pub const DEFAULT_PORT : u16 = 8005 ;
21
38
pub const DEFAULT_IP_ADDR : IpAddr = IpAddr :: V4 ( Ipv4Addr :: new ( 0 , 0 , 0 , 0 ) ) ;
22
39
pub static DEFAULT_REDIS_URL : Lazy < ConnectionInfo > = Lazy :: new ( || {
23
40
"redis://127.0.0.1:6379"
24
41
. parse :: < ConnectionInfo > ( )
25
42
. expect ( "Valid URL" )
26
43
} ) ;
44
+ /// Sentry Application config set by environment variables
27
45
#[ derive( Debug , Deserialize , Clone ) ]
28
- pub struct Config {
46
+ pub struct EnvConfig {
29
47
/// Defaults to `Development`: [`Environment::default()`]
30
48
pub env : Environment ,
31
49
/// The port on which the Sentry REST API will be accessible.
32
- # [ serde ( default = "default_port" ) ]
50
+ ///
33
51
/// Defaults to `8005`: [`DEFAULT_PORT`]
52
+ #[ serde( default = "default_port" ) ]
34
53
pub port : u16 ,
35
54
/// The address on which the Sentry REST API will be accessible.
36
55
/// `0.0.0.0` can be used for Docker.
37
56
/// `127.0.0.1` can be used for locally running servers.
38
- # [ serde ( default = "default_ip_addr" ) ]
57
+ ///
39
58
/// Defaults to `0.0.0.0`: [`DEFAULT_IP_ADDR`]
59
+ #[ serde( default = "default_ip_addr" ) ]
40
60
pub ip_addr : IpAddr ,
41
61
#[ serde( deserialize_with = "redis_url" , default = "default_redis_url" ) ]
42
62
/// Defaults to locally running Redis server: [`DEFAULT_REDIS_URL`]
43
63
pub redis_url : ConnectionInfo ,
44
64
}
45
65
46
- impl Config {
66
+ impl EnvConfig {
47
67
/// Deserialize the application [`Config`] from Environment variables.
48
68
pub fn from_env ( ) -> Result < Self , EnvError > {
49
69
envy:: from_env ( )
@@ -69,6 +89,72 @@ fn default_redis_url() -> ConnectionInfo {
69
89
DEFAULT_REDIS_URL . clone ( )
70
90
}
71
91
92
+ /// The Sentry REST web application
93
+ pub struct Application < C : Locked + ' static > {
94
+ /// For sentry to work properly, we need an [`adapter::Adapter`] in a [`adapter::LockedState`] state.
95
+ pub adapter : Adapter < C > ,
96
+ pub config : primitives:: Config ,
97
+ pub logger : Logger ,
98
+ pub redis : MultiplexedConnection ,
99
+ pub pool : DbPool ,
100
+ pub campaign_remaining : CampaignRemaining ,
101
+ pub platform_api : PlatformApi ,
102
+ }
103
+
104
+ impl < C > Application < C >
105
+ where
106
+ C : Locked ,
107
+ {
108
+ pub fn new (
109
+ adapter : Adapter < C > ,
110
+ config : primitives:: Config ,
111
+ logger : Logger ,
112
+ redis : MultiplexedConnection ,
113
+ pool : DbPool ,
114
+ campaign_remaining : CampaignRemaining ,
115
+ platform_api : PlatformApi ,
116
+ ) -> Self {
117
+ Self {
118
+ adapter,
119
+ config,
120
+ logger,
121
+ redis,
122
+ pool,
123
+ campaign_remaining,
124
+ platform_api,
125
+ }
126
+ }
127
+
128
+ pub async fn handle_routing ( & self , req : Request < Body > ) -> Response < Body > {
129
+ let headers = match cors ( & req) {
130
+ Some ( Cors :: Simple ( headers) ) => headers,
131
+ // if we have a Preflight, just return the response directly
132
+ Some ( Cors :: Preflight ( response) ) => return response,
133
+ None => Default :: default ( ) ,
134
+ } ;
135
+
136
+ let req = match Authenticate . call ( req, self ) . await {
137
+ Ok ( req) => req,
138
+ Err ( error) => return map_response_error ( error) ,
139
+ } ;
140
+
141
+ let mut response = match ( req. uri ( ) . path ( ) , req. method ( ) ) {
142
+ ( "/cfg" , & Method :: GET ) => get_cfg ( req, self ) . await ,
143
+ ( route, _) if route. starts_with ( "/v5/analytics" ) => analytics_router ( req, self ) . await ,
144
+ // This is important because it prevents us from doing
145
+ // expensive regex matching for routes without /channel
146
+ ( path, _) if path. starts_with ( "/v5/channel" ) => channels_router ( req, self ) . await ,
147
+ ( path, _) if path. starts_with ( "/v5/campaign" ) => campaigns_router ( req, self ) . await ,
148
+ _ => Err ( ResponseError :: NotFound ) ,
149
+ }
150
+ . unwrap_or_else ( map_response_error) ;
151
+
152
+ // extend the headers with the initial headers we have from CORS (if there are some)
153
+ response. headers_mut ( ) . extend ( headers) ;
154
+ response
155
+ }
156
+ }
157
+
72
158
impl < C : Locked + ' static > Application < C > {
73
159
/// Starts the `hyper` `Server`.
74
160
pub async fn run ( self , socket_addr : SocketAddr ) {
@@ -107,6 +193,24 @@ impl<C: Locked> Clone for Application<C> {
107
193
}
108
194
}
109
195
196
+ /// Sentry [`Application`] Session
197
+ #[ derive( Debug , Clone ) ]
198
+ pub struct Session {
199
+ pub ip : Option < String > ,
200
+ pub country : Option < String > ,
201
+ pub referrer_header : Option < String > ,
202
+ pub os : Option < String > ,
203
+ }
204
+
205
+ /// Validated Authentication for the Sentry [`Application`].
206
+ #[ derive( Debug , Clone ) ]
207
+ pub struct Auth {
208
+ pub era : i64 ,
209
+ pub uid : ValidatorId ,
210
+ /// The Chain for which this authentication was validated
211
+ pub chain : primitives:: Chain ,
212
+ }
213
+
110
214
#[ cfg( test) ]
111
215
mod test {
112
216
use serde_json:: json;
0 commit comments