relay_server/endpoints/
unreal.rs1use axum::extract::{DefaultBodyLimit, FromRequest, Query};
2use axum::response::IntoResponse;
3use axum::routing::{MethodRouter, post};
4use bytes::Bytes;
5use relay_config::Config;
6use relay_event_schema::protocol::EventId;
7use serde::Deserialize;
8
9use crate::constants::UNREAL_USER_HEADER;
10use crate::endpoints::common::{self, BadStoreRequest, TextResponse};
11use crate::envelope::{ContentType, Envelope, Item, ItemType};
12use crate::extractors::RequestMeta;
13use crate::middlewares;
14use crate::service::ServiceState;
15
16#[derive(Debug, Deserialize)]
17struct UnrealQuery {
18 #[serde(rename = "UserID")]
19 user_id: Option<String>,
20}
21
22#[derive(Debug, FromRequest)]
23#[from_request(state(ServiceState))]
24struct UnrealParams {
25 meta: RequestMeta,
26 #[from_request(via(Query))]
27 query: UnrealQuery,
28 data: Bytes,
29}
30
31impl UnrealParams {
32 fn extract_envelope(self) -> Result<Box<Envelope>, BadStoreRequest> {
33 let Self { meta, query, data } = self;
34
35 if data.is_empty() {
36 return Err(BadStoreRequest::EmptyBody);
37 }
38
39 let mut envelope = Envelope::from_request(Some(EventId::new()), meta);
40
41 let mut item = Item::new(ItemType::UnrealReport);
42 item.set_payload(ContentType::OctetStream, data);
43 envelope.add_item(item);
44
45 if let Some(user_id) = query.user_id {
46 envelope.set_header(UNREAL_USER_HEADER, user_id);
47 }
48
49 Ok(envelope)
50 }
51}
52
53async fn handle(
54 state: ServiceState,
55 params: UnrealParams,
56) -> Result<impl IntoResponse, BadStoreRequest> {
57 let envelope = params.extract_envelope()?;
58 let id = envelope.event_id();
59
60 match common::handle_envelope(&state, envelope)
62 .await
63 .map_err(|err| err.into_inner())
64 {
65 Ok(_) | Err(BadStoreRequest::RateLimited(_)) => (),
66 Err(error) => return Err(error),
67 };
68
69 Ok(TextResponse(id))
72}
73
74pub fn route(config: &Config) -> MethodRouter<ServiceState> {
75 post(handle)
76 .route_layer(DefaultBodyLimit::max(config.max_attachments_size()))
77 .route_layer(axum::middleware::from_fn(middlewares::content_length))
78}