1use log::{error, warn};
2use reqwest::header::HeaderMap;
3use reqwest::{Client, RequestBuilder};
4use serde::de::DeserializeOwned;
5use types::account::UserData;
6use url::Url;
7
8use crate::api::error::EpicAPIError;
9
10pub mod types;
12
13pub mod utils;
15
16#[allow(dead_code)]
18pub(crate) mod binary_rw;
19
20pub mod error;
22
23pub mod fab;
25
26pub mod account;
28
29pub mod egs;
31#[allow(dead_code)]
32pub mod cloud_save;
34pub mod login;
36
37pub mod commerce;
39
40pub mod status;
42
43pub mod presence;
45
46pub mod store;
48
49#[derive(Debug, Clone)]
50pub(crate) struct EpicAPI {
51 client: Client,
52 pub(crate) user_data: UserData,
53}
54
55impl Default for EpicAPI {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl EpicAPI {
62 pub fn new() -> Self {
63 let mut headers = HeaderMap::new();
64 headers.insert(
65 "User-Agent",
66 "UELauncher/17.0.1-37584233+++Portal+Release-Live Windows/10.0.19043.1.0.64bit"
67 .parse()
68 .unwrap(),
69 );
70 headers.insert(
71 "X-Epic-Correlation-ID",
72 "UE4-c176f7154c2cda1061cc43ab52598e2b-93AFB486488A22FDF70486BD1D883628-BFCD88F649E997BA203FF69F07CE578C".parse().unwrap()
73 );
74 let client = reqwest::Client::builder()
75 .default_headers(headers)
76 .cookie_store(true)
77 .build()
78 .unwrap();
79 EpicAPI {
80 client,
81 user_data: Default::default(),
82 }
83 }
84
85 fn authorized_get_client(&self, url: Url) -> RequestBuilder {
86 self.set_authorization_header(self.client.get(url))
87 }
88
89 fn authorized_post_client(&self, url: Url) -> RequestBuilder {
90 self.set_authorization_header(self.client.post(url))
91 }
92
93 fn set_authorization_header(&self, rb: RequestBuilder) -> RequestBuilder {
94 rb.header(
95 "Authorization",
96 format!(
97 "{} {}",
98 self.user_data
99 .token_type
100 .as_ref()
101 .unwrap_or(&"bearer".to_string()),
102 self.user_data
103 .access_token
104 .as_ref()
105 .unwrap_or(&"".to_string())
106 ),
107 )
108 }
109
110 pub(crate) async fn authorized_get_json<T: DeserializeOwned>(
112 &self,
113 url: &str,
114 ) -> Result<T, EpicAPIError> {
115 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
116 let response = self
117 .authorized_get_client(parsed_url)
118 .send()
119 .await
120 .map_err(|e| {
121 error!("{:?}", e);
122 EpicAPIError::NetworkError(e)
123 })?;
124 if response.status() == reqwest::StatusCode::OK {
125 response.json::<T>().await.map_err(|e| {
126 error!("{:?}", e);
127 EpicAPIError::DeserializationError(format!("{}", e))
128 })
129 } else {
130 let status = response.status();
131 let body = response.text().await.unwrap_or_default();
132 warn!("{} result: {}", status, body);
133 Err(EpicAPIError::HttpError { status, body })
134 }
135 }
136
137 pub(crate) async fn authorized_post_form_json<T: DeserializeOwned>(
139 &self,
140 url: &str,
141 form: &[(String, String)],
142 ) -> Result<T, EpicAPIError> {
143 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
144 let response = self
145 .authorized_post_client(parsed_url)
146 .form(form)
147 .send()
148 .await
149 .map_err(|e| {
150 error!("{:?}", e);
151 EpicAPIError::NetworkError(e)
152 })?;
153 if response.status() == reqwest::StatusCode::OK {
154 response.json::<T>().await.map_err(|e| {
155 error!("{:?}", e);
156 EpicAPIError::DeserializationError(format!("{}", e))
157 })
158 } else {
159 let status = response.status();
160 let body = response.text().await.unwrap_or_default();
161 warn!("{} result: {}", status, body);
162 Err(EpicAPIError::HttpError { status, body })
163 }
164 }
165
166 pub(crate) async fn authorized_post_json<T: DeserializeOwned, B: serde::Serialize>(
168 &self,
169 url: &str,
170 body: &B,
171 ) -> Result<T, EpicAPIError> {
172 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
173 let response = self
174 .authorized_post_client(parsed_url)
175 .json(body)
176 .send()
177 .await
178 .map_err(|e| {
179 error!("{:?}", e);
180 EpicAPIError::NetworkError(e)
181 })?;
182 if response.status() == reqwest::StatusCode::OK {
183 response.json::<T>().await.map_err(|e| {
184 error!("{:?}", e);
185 EpicAPIError::DeserializationError(format!("{}", e))
186 })
187 } else {
188 let status = response.status();
189 let body = response.text().await.unwrap_or_default();
190 warn!("{} result: {}", status, body);
191 Err(EpicAPIError::HttpError { status, body })
192 }
193 }
194
195 pub(crate) async fn get_bytes(&self, url: &str) -> Result<Vec<u8>, EpicAPIError> {
197 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
198 let response = self
199 .client
200 .get(parsed_url)
201 .send()
202 .await
203 .map_err(|e| {
204 error!("{:?}", e);
205 EpicAPIError::NetworkError(e)
206 })?;
207 if response.status() == reqwest::StatusCode::OK {
208 response.bytes().await.map(|b| b.to_vec()).map_err(|e| {
209 error!("{:?}", e);
210 EpicAPIError::DeserializationError(format!("{}", e))
211 })
212 } else {
213 let status = response.status();
214 let body = response.text().await.unwrap_or_default();
215 warn!("{} result: {}", status, body);
216 Err(EpicAPIError::HttpError { status, body })
217 }
218 }
219
220 #[allow(dead_code)]
221 pub(crate) async fn authorized_delete(&self, url: &str) -> Result<(), EpicAPIError> {
223 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
224 let response = self
225 .set_authorization_header(self.client.delete(parsed_url))
226 .send()
227 .await
228 .map_err(|e| {
229 error!("{:?}", e);
230 EpicAPIError::NetworkError(e)
231 })?;
232 if response.status() == reqwest::StatusCode::OK
233 || response.status() == reqwest::StatusCode::NO_CONTENT
234 {
235 Ok(())
236 } else {
237 let status = response.status();
238 let body = response.text().await.unwrap_or_default();
239 warn!("{} result: {}", status, body);
240 Err(EpicAPIError::HttpError { status, body })
241 }
242 }
243}