mirror of
https://github.com/modrinth/code.git
synced 2026-08-27 01:54:47 +00:00
Have app send download analytics meta (#5954)
* wip: add download reasons to app * update how download meta is gathered * cargo fmt * prepr frontend
This commit is contained in:
@@ -9,6 +9,7 @@ use parking_lot::Mutex;
|
||||
use rand::Rng;
|
||||
use reqwest::Method;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -17,6 +18,30 @@ use std::time::{self};
|
||||
use tokio::sync::Semaphore;
|
||||
use tokio::{fs::File, io::AsyncWriteExt};
|
||||
|
||||
pub const DOWNLOAD_META_HEADER: &str = "modrinth-download-meta";
|
||||
|
||||
#[derive(Debug, derive_more::Display, Clone, Copy, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[display(rename_all = "snake_case")]
|
||||
pub enum DownloadReason {
|
||||
Standalone,
|
||||
Dependency,
|
||||
Modpack,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct DownloadMeta {
|
||||
pub reason: DownloadReason,
|
||||
pub game_version: String,
|
||||
pub loader: String,
|
||||
}
|
||||
|
||||
impl DownloadMeta {
|
||||
pub fn to_header_value(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IoSemaphore(pub Semaphore);
|
||||
#[derive(Debug)]
|
||||
@@ -156,17 +181,29 @@ const FETCH_ATTEMPTS: usize = 2;
|
||||
pub async fn fetch(
|
||||
url: &str,
|
||||
sha1: Option<&str>,
|
||||
download_meta: Option<&DownloadMeta>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
) -> crate::Result<Bytes> {
|
||||
fetch_advanced(Method::GET, url, sha1, None, None, None, semaphore, exec)
|
||||
.await
|
||||
fetch_advanced(
|
||||
Method::GET,
|
||||
url,
|
||||
sha1,
|
||||
None,
|
||||
None,
|
||||
download_meta,
|
||||
None,
|
||||
semaphore,
|
||||
exec,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(semaphore))]
|
||||
pub async fn fetch_with_client(
|
||||
url: &str,
|
||||
sha1: Option<&str>,
|
||||
download_meta: Option<&DownloadMeta>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
client: &reqwest::Client,
|
||||
@@ -177,6 +214,7 @@ pub async fn fetch_with_client(
|
||||
sha1,
|
||||
None,
|
||||
None,
|
||||
download_meta,
|
||||
None,
|
||||
semaphore,
|
||||
exec,
|
||||
@@ -198,7 +236,7 @@ where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let result = fetch_advanced(
|
||||
method, url, sha1, json_body, None, None, semaphore, exec,
|
||||
method, url, sha1, json_body, None, None, None, semaphore, exec,
|
||||
)
|
||||
.await?;
|
||||
let value = serde_json::from_slice(&result)?;
|
||||
@@ -215,6 +253,7 @@ pub async fn fetch_advanced(
|
||||
sha1: Option<&str>,
|
||||
json_body: Option<serde_json::Value>,
|
||||
header: Option<(&str, &str)>,
|
||||
download_meta: Option<&DownloadMeta>,
|
||||
loading_bar: Option<(&LoadingBarId, f64)>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
@@ -225,6 +264,7 @@ pub async fn fetch_advanced(
|
||||
sha1,
|
||||
json_body,
|
||||
header,
|
||||
download_meta,
|
||||
loading_bar,
|
||||
semaphore,
|
||||
exec,
|
||||
@@ -242,6 +282,7 @@ pub async fn fetch_advanced_with_client(
|
||||
sha1: Option<&str>,
|
||||
json_body: Option<serde_json::Value>,
|
||||
header: Option<(&str, &str)>,
|
||||
download_meta: Option<&DownloadMeta>,
|
||||
loading_bar: Option<(&LoadingBarId, f64)>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
@@ -262,12 +303,15 @@ pub async fn fetch_advanced_with_client(
|
||||
None
|
||||
};
|
||||
|
||||
let download_meta_header = download_meta
|
||||
.map(|m| (DOWNLOAD_META_HEADER.to_string(), m.to_header_value()));
|
||||
|
||||
for attempt in 1..=(FETCH_ATTEMPTS + 1) {
|
||||
if is_api_url && GLOBAL_FETCH_FENCE.is_blocked() {
|
||||
return Err(ErrorKind::ApiIsDownError.into());
|
||||
}
|
||||
|
||||
let mut req = INSECURE_REQWEST_CLIENT.request(method.clone(), url);
|
||||
let mut req = client.request(method.clone(), url);
|
||||
|
||||
if let Some(body) = json_body.clone() {
|
||||
req = req.json(&body);
|
||||
@@ -281,6 +325,11 @@ pub async fn fetch_advanced_with_client(
|
||||
req = req.header("Authorization", &creds.session);
|
||||
}
|
||||
|
||||
if let Some((name, value)) = &download_meta_header {
|
||||
tracing::info!("Sending download analytics: {value}");
|
||||
req = req.header(name.as_str(), value.as_str());
|
||||
}
|
||||
|
||||
let result = req.send().await;
|
||||
match result {
|
||||
Ok(resp) => {
|
||||
@@ -375,6 +424,7 @@ pub async fn fetch_advanced_with_client(
|
||||
pub async fn fetch_mirrors(
|
||||
mirrors: &[&str],
|
||||
sha1: Option<&str>,
|
||||
download_meta: Option<&DownloadMeta>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite> + Copy,
|
||||
) -> crate::Result<Bytes> {
|
||||
@@ -385,9 +435,15 @@ pub async fn fetch_mirrors(
|
||||
}
|
||||
|
||||
for (index, mirror) in mirrors.iter().enumerate() {
|
||||
let result =
|
||||
fetch_with_client(mirror, sha1, semaphore, exec, &REQWEST_CLIENT)
|
||||
.await;
|
||||
let result = fetch_with_client(
|
||||
mirror,
|
||||
sha1,
|
||||
download_meta,
|
||||
semaphore,
|
||||
exec,
|
||||
&REQWEST_CLIENT,
|
||||
)
|
||||
.await;
|
||||
|
||||
if result.is_ok() || (result.is_err() && index == (mirrors.len() - 1)) {
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user