Download meta info in header

This commit is contained in:
aecsocket
2026-04-23 16:46:22 +01:00
parent ad2c70ce70
commit b3d0e61e15
3 changed files with 52 additions and 2 deletions
+15 -1
View File
@@ -9,6 +9,8 @@ use crate::env::ENV;
use crate::queue::server_ping; use crate::queue::server_ping;
use crate::routes::analytics::MINECRAFT_SERVER_PLAYS; use crate::routes::analytics::MINECRAFT_SERVER_PLAYS;
pub const DOWNLOADS: &str = "downloads";
pub async fn init_client() -> clickhouse::error::Result<clickhouse::Client> { pub async fn init_client() -> clickhouse::error::Result<clickhouse::Client> {
init_client_with_database(&ENV.CLICKHOUSE_DATABASE).await init_client_with_database(&ENV.CLICKHOUSE_DATABASE).await
} }
@@ -90,7 +92,7 @@ pub async fn init_client_with_database(
client client
.query(&format!( .query(&format!(
" "
CREATE TABLE IF NOT EXISTS {database}.downloads {cluster_line} CREATE TABLE IF NOT EXISTS {database}.{DOWNLOADS} {cluster_line}
( (
recorded DateTime64(4), recorded DateTime64(4),
domain String, domain String,
@@ -238,5 +240,17 @@ pub async fn init_client_with_database(
.execute() .execute()
.await?; .await?;
client
.query(&format!(
"
ALTER TABLE {database}.{DOWNLOADS} {cluster_line}
ADD COLUMN IF NOT EXISTS reason String,
ADD COLUMN IF NOT EXISTS game_version String,
ADD COLUMN IF NOT EXISTS loader String
"
))
.execute()
.await?;
Ok(client.with_database(database)) Ok(client.with_database(database))
} }
+17
View File
@@ -23,6 +23,23 @@ pub struct Download {
pub country: String, pub country: String,
pub user_agent: String, pub user_agent: String,
pub headers: Vec<(String, String)>, pub headers: Vec<(String, String)>,
// added retroactively - may be missing
pub reason: Option<DownloadReason>,
pub game_version: Option<String>,
pub loader: Option<String>,
}
/// Why a project was downloaded.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DownloadReason {
/// Project was downloaded directly by the user.
Standalone,
/// Project was downloaded as a dependency, possibly transitive, of another
/// project.
Dependency,
/// Project was downloaded as part of a modpack.
Modpack,
} }
#[derive(Debug, Row, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Row, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
+20 -1
View File
@@ -1,7 +1,7 @@
use crate::auth::validate::get_user_record_from_bearer_token; use crate::auth::validate::get_user_record_from_bearer_token;
use crate::database::PgPool; use crate::database::PgPool;
use crate::database::redis::RedisPool; use crate::database::redis::RedisPool;
use crate::models::analytics::Download; use crate::models::analytics::{Download, DownloadReason};
use crate::models::ids::ProjectId; use crate::models::ids::ProjectId;
use crate::models::pats::Scopes; use crate::models::pats::Scopes;
use crate::queue::analytics::AnalyticsQueue; use crate::queue::analytics::AnalyticsQueue;
@@ -35,6 +35,17 @@ pub struct DownloadBody {
pub headers: HashMap<String, String>, pub headers: HashMap<String, String>,
} }
/// Extra data attached to each download request, transmitted through the
/// [`DOWNLOAD_META_HEADER`] header.
#[derive(Debug, Clone, Deserialize)]
pub struct DownloadMeta {
pub reason: DownloadReason,
pub game_version: String,
pub loader: String,
}
pub const DOWNLOAD_META_HEADER: &str = "modrinth-download-meta";
// This is an internal route, cannot be used without key // This is an internal route, cannot be used without key
#[utoipa::path( #[utoipa::path(
patch, patch,
@@ -118,6 +129,11 @@ pub async fn count_download(
let ip = crate::util::ip::convert_to_ip_v6(&download_body.ip) let ip = crate::util::ip::convert_to_ip_v6(&download_body.ip)
.unwrap_or_else(|_| Ipv4Addr::new(127, 0, 0, 1).to_ipv6_mapped()); .unwrap_or_else(|_| Ipv4Addr::new(127, 0, 0, 1).to_ipv6_mapped());
let meta = download_body
.headers
.get(DOWNLOAD_META_HEADER)
.and_then(|v| serde_json::from_str::<DownloadMeta>(v).ok());
analytics_queue.add_download(Download { analytics_queue.add_download(Download {
recorded: get_current_tenths_of_ms(), recorded: get_current_tenths_of_ms(),
domain: url.host_str().unwrap_or_default().to_string(), domain: url.host_str().unwrap_or_default().to_string(),
@@ -153,6 +169,9 @@ pub async fn count_download(
.contains(&&*x.0.to_lowercase()) .contains(&&*x.0.to_lowercase())
}) })
.collect(), .collect(),
reason: meta.as_ref().map(|m| m.reason),
game_version: meta.as_ref().map(|m| m.game_version.clone()),
loader: meta.as_ref().map(|m| m.loader.clone()),
}); });
Ok(HttpResponse::NoContent().body("")) Ok(HttpResponse::NoContent().body(""))