1use actix_web::web::{Data, Json};
2use actix_web::{HttpRequest, HttpResponse};
3use cadence::{Histogrammed, StatsdClient};
4use uuid::Uuid;
5
6use crate::error::{ApiErrorKind, ApiResult};
7use crate::extractors::{
8 authorization_check::AuthorizationCheck, new_channel_data::NewChannelData,
9 registration_path_args::RegistrationPathArgs,
10 registration_path_args_with_uaid::RegistrationPathArgsWithUaid,
11 router_data_input::RouterDataInput, routers::Routers,
12};
13use crate::headers::util::get_header;
14use crate::server::AppState;
15
16use autopush_common::db::User;
17use autopush_common::endpoint::make_endpoint;
18use autopush_common::metric_name::MetricName;
19use autopush_common::metrics::StatsdClientExt;
20
21pub async fn register_uaid_route(
23 path_args: RegistrationPathArgs,
24 router_data_input: RouterDataInput,
25 routers: Routers,
26 app_state: Data<AppState>,
27 request: HttpRequest,
28) -> ApiResult<HttpResponse> {
29 debug!(
31 "Registering a user with the {} router",
32 path_args.router_type
33 );
34 trace!("🌍 token = {}", router_data_input.token);
35 let router = routers.get(path_args.router_type);
36 let router_data = router.register(&router_data_input, &path_args.app_id)?;
37 incr_metric(MetricName::UaCommandRegister, &app_state.metrics, &request);
38
39 let user = User::builder()
41 .router_type(path_args.router_type.to_string())
42 .router_data(router_data)
43 .build()
44 .map_err(|e| ApiErrorKind::General(format!("User::builder error: {e}")))?;
45 let channel_id = router_data_input.channel_id.unwrap_or_else(Uuid::new_v4);
46 trace!("🌍 Creating user with UAID {}", user.uaid);
47 trace!("🌍 user = {:?}", user);
48 trace!("🌍 channel_id = {}", channel_id);
49 app_state.db.add_user(&user).await?;
50 app_state.db.add_channel(&user.uaid, &channel_id).await?;
51
52 trace!("🌍 Creating endpoint for user");
54 let endpoint_url = make_endpoint(
55 &user.uaid,
56 &channel_id,
57 router_data_input.key.as_deref(),
58 app_state.settings.endpoint_url().as_str(),
59 &app_state.fernet,
60 )
61 .map_err(ApiErrorKind::EndpointUrl)?;
62 trace!("🌍 endpoint = {}", endpoint_url);
63
64 trace!("🌍 Creating secret for UAID {}", user.uaid);
66 let auth_keys = app_state.settings.auth_keys();
67 let auth_key = auth_keys
68 .first()
69 .expect("At least one auth key must be provided in the settings");
70 let secret = AuthorizationCheck::generate_token(auth_key, &user.uaid)
71 .map_err(ApiErrorKind::RegistrationSecretHash)?;
72
73 trace!("🌍 Finished registering UAID {}", user.uaid);
74 Ok(HttpResponse::Ok().json(serde_json::json!({
75 "uaid": user.uaid,
76 "channelID": channel_id,
77 "endpoint": endpoint_url,
78 "secret": secret
79 })))
80}
81
82pub async fn unregister_user_route(
84 _auth: AuthorizationCheck,
85 path_args: RegistrationPathArgsWithUaid,
86 app_state: Data<AppState>,
87) -> ApiResult<HttpResponse> {
88 let uaid = path_args.user.uaid;
89 debug!("🌍 Unregistering UAID {uaid}");
90 app_state.db.remove_user(&uaid).await?;
91 Ok(HttpResponse::Ok().finish())
92}
93
94pub async fn update_token_route(
96 _auth: AuthorizationCheck,
97 path_args: RegistrationPathArgsWithUaid,
98 router_data_input: RouterDataInput,
99 routers: Routers,
100 app_state: Data<AppState>,
101) -> ApiResult<HttpResponse> {
102 let RegistrationPathArgsWithUaid {
104 router_type,
105 app_id,
106 mut user,
107 } = path_args;
108 let uaid = user.uaid;
109 debug!("🌍 Updating the token of UAID {uaid} with the {router_type} router");
110 trace!("token = {}", router_data_input.token);
111 let router = routers.get(path_args.router_type);
112 let router_data = router.register(&router_data_input, &app_id)?;
113
114 user.router_type = path_args.router_type.to_string();
116 user.router_data = Some(router_data);
117 trace!("🌍 Updating user with UAID {uaid}");
118 trace!("🌍 user = {user:?}");
119 if !app_state.db.update_user(&mut user).await? {
120 return Err(ApiErrorKind::Conditional("update_user".to_owned()).into());
122 }
123
124 trace!("🌍 Finished updating token for UAID {uaid}");
125 Ok(HttpResponse::Ok().finish())
126}
127
128pub async fn new_channel_route(
130 _auth: AuthorizationCheck,
131 path_args: RegistrationPathArgsWithUaid,
132 channel_data: Option<Json<NewChannelData>>,
133 app_state: Data<AppState>,
134) -> ApiResult<HttpResponse> {
135 let uaid = path_args.user.uaid;
137 debug!("🌍 Adding a channel to UAID {uaid}");
138 let channel_data = channel_data.map(Json::into_inner).unwrap_or_default();
139 let channel_id = channel_data.channel_id.unwrap_or_else(Uuid::new_v4);
140 trace!("🌍 channel_id = {channel_id}");
141 app_state.db.add_channel(&uaid, &channel_id).await?;
142
143 trace!("🌍 Creating endpoint for the new channel");
145 let endpoint_url = make_endpoint(
146 &uaid,
147 &channel_id,
148 channel_data.key.as_deref(),
149 app_state.settings.endpoint_url().as_str(),
150 &app_state.fernet,
151 )
152 .map_err(ApiErrorKind::EndpointUrl)?;
153 trace!("endpoint = {endpoint_url}");
154
155 Ok(HttpResponse::Ok().json(serde_json::json!({
156 "channelID": channel_id,
157 "endpoint": endpoint_url,
158 })))
159}
160
161pub async fn get_channels_route(
168 auth: AuthorizationCheck,
169 path_args: RegistrationPathArgsWithUaid,
170 app_state: Data<AppState>,
171) -> ApiResult<HttpResponse> {
172 let router_type = path_args.router_type.to_string();
178 let mut user = path_args.user;
179 let uaid = user.uaid;
180 let db = &app_state.db;
181 debug!("🌍 Getting channel IDs for UAID {uaid}");
182 db.update_user(&mut user).await?;
183 let os = auth.user_agent.metrics_os.clone();
185 let browser = auth.user_agent.metrics_browser.clone();
186 info!("Mobile client check";
188 "os" => auth.user_agent.os,
189 "os_version" => auth.user_agent.os_version,
190 "browser" => auth.user_agent.browser_name,
191 "browser_version" => auth.user_agent.browser_version);
192 app_state
194 .metrics
195 .incr_with_tags(MetricName::UaConnectionCheck)
196 .with_tag("os", &os)
197 .with_tag("browser", &browser)
198 .with_tag("router_type", &router_type)
199 .send();
200
201 let channel_ids = db.get_channels(&uaid).await?;
202
203 app_state
204 .metrics
205 .histogram_with_tags(
206 MetricName::UaConnectionChannelCount.as_ref(),
207 channel_ids.len() as u64,
208 )
209 .with_tag_value("mobile")
210 .send();
211
212 Ok(HttpResponse::Ok().json(serde_json::json!({
213 "uaid": uaid,
214 "channelIDs": channel_ids
215 })))
216}
217
218pub async fn unregister_channel_route(
220 _auth: AuthorizationCheck,
221 path_args: RegistrationPathArgsWithUaid,
222 app_state: Data<AppState>,
223 request: HttpRequest,
224) -> ApiResult<HttpResponse> {
225 let channel_id = request
226 .match_info()
227 .get("chid")
228 .expect("{chid} must be part of the path")
229 .parse::<Uuid>()
230 .map_err(|_| ApiErrorKind::NoSubscription)?;
231 let uaid = path_args.user.uaid;
232 debug!("🌍 Unregistering CHID {channel_id} for UAID {uaid}");
233
234 incr_metric(
235 MetricName::UaCommandUnregister,
236 &app_state.metrics,
237 &request,
238 );
239 let channel_did_exist = app_state.db.remove_channel(&uaid, &channel_id).await?;
240
241 if channel_did_exist {
242 Ok(HttpResponse::Ok().finish())
243 } else {
244 debug!("Channel did not exist");
245 Err(ApiErrorKind::NoSubscription.into())
246 }
247}
248
249fn incr_metric(metric: MetricName, metrics: &StatsdClient, request: &HttpRequest) {
251 metrics
252 .incr_with_tags(metric)
253 .with_tag(
254 "user_agent",
255 get_header(request, "User-Agent").unwrap_or("unknown"),
256 )
257 .with_tag("host", get_header(request, "Host").unwrap_or("unknown"))
258 .send()
259}