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 uaid = path_args.user.uaid;
173 let db = &app_state.db;
174 debug!("🌍 Getting channel IDs for UAID {uaid}");
175 if let Some(mut user) = db.get_user(&uaid).await? {
177 db.update_user(&mut user).await?;
178 let os = auth.user_agent.metrics_os.clone();
180 let browser = auth.user_agent.metrics_browser.clone();
181 info!("Mobile client check";
183 "os" => auth.user_agent.os,
184 "os_version" => auth.user_agent.os_version,
185 "browser" => auth.user_agent.browser_name,
186 "browser_version" => auth.user_agent.browser_version);
187 app_state
189 .metrics
190 .incr_with_tags(MetricName::UaConnectionCheck)
191 .with_tag("os", &os)
192 .with_tag("browser", &browser)
193 .send();
194 }
195 let channel_ids = db.get_channels(&uaid).await?;
196
197 app_state
198 .metrics
199 .histogram_with_tags(
200 MetricName::UaConnectionChannelCount.as_ref(),
201 channel_ids.len() as u64,
202 )
203 .with_tag_value("mobile")
204 .send();
205
206 Ok(HttpResponse::Ok().json(serde_json::json!({
207 "uaid": uaid,
208 "channelIDs": channel_ids
209 })))
210}
211
212pub async fn unregister_channel_route(
214 _auth: AuthorizationCheck,
215 path_args: RegistrationPathArgsWithUaid,
216 app_state: Data<AppState>,
217 request: HttpRequest,
218) -> ApiResult<HttpResponse> {
219 let channel_id = request
220 .match_info()
221 .get("chid")
222 .expect("{chid} must be part of the path")
223 .parse::<Uuid>()
224 .map_err(|_| ApiErrorKind::NoSubscription)?;
225 let uaid = path_args.user.uaid;
226 debug!("🌍 Unregistering CHID {channel_id} for UAID {uaid}");
227
228 incr_metric(
229 MetricName::UaCommandUnregister,
230 &app_state.metrics,
231 &request,
232 );
233 let channel_did_exist = app_state.db.remove_channel(&uaid, &channel_id).await?;
234
235 if channel_did_exist {
236 Ok(HttpResponse::Ok().finish())
237 } else {
238 debug!("Channel did not exist");
239 Err(ApiErrorKind::NoSubscription.into())
240 }
241}
242
243fn incr_metric(metric: MetricName, metrics: &StatsdClient, request: &HttpRequest) {
245 metrics
246 .incr_with_tags(metric)
247 .with_tag(
248 "user_agent",
249 get_header(request, "User-Agent").unwrap_or("unknown"),
250 )
251 .with_tag("host", get_header(request, "Host").unwrap_or("unknown"))
252 .send()
253}