From 8e754cfeb5da73a291105bb8447d578c358bf464 Mon Sep 17 00:00:00 2001 From: Jai A Date: Wed, 8 Jan 2025 22:06:05 -0800 Subject: [PATCH] Add delphi integration, fix search showing plugins --- apps/labrinth/.env | 5 +- apps/labrinth/src/lib.rs | 3 + apps/labrinth/src/queue/moderation.rs | 2 +- apps/labrinth/src/routes/internal/admin.rs | 73 ++++++++++++++++++- apps/labrinth/src/routes/v2/projects.rs | 5 -- apps/labrinth/src/routes/v2_reroute.rs | 22 ------ .../src/routes/v3/version_creation.rs | 19 +++++ 7 files changed, 99 insertions(+), 30 deletions(-) diff --git a/apps/labrinth/.env b/apps/labrinth/.env index ecc4df48f..640a8964f 100644 --- a/apps/labrinth/.env +++ b/apps/labrinth/.env @@ -112,4 +112,7 @@ ADITUDE_API_KEY=none PYRO_API_KEY=none BREX_API_URL=https://platform.brexapis.com/v2/ -BREX_API_KEY=none \ No newline at end of file +BREX_API_KEY=none + +DELPHI_URL=none +DELPHI_SLACK_WEBHOOK=none \ No newline at end of file diff --git a/apps/labrinth/src/lib.rs b/apps/labrinth/src/lib.rs index 98157db5d..db1416a71 100644 --- a/apps/labrinth/src/lib.rs +++ b/apps/labrinth/src/lib.rs @@ -489,7 +489,10 @@ pub fn check_env_vars() -> bool { failed |= check_var::("PYRO_API_KEY"); + failed |= check_var::("BREX_API_URL"); failed |= check_var::("BREX_API_KEY"); + failed |= check_var::("DELPHI_URL"); + failed } diff --git a/apps/labrinth/src/queue/moderation.rs b/apps/labrinth/src/queue/moderation.rs index 49eac5b16..2b9b9319e 100644 --- a/apps/labrinth/src/queue/moderation.rs +++ b/apps/labrinth/src/queue/moderation.rs @@ -18,7 +18,7 @@ use std::io::{Cursor, Read}; use std::time::Duration; use zip::ZipArchive; -const AUTOMOD_ID: i64 = 0; +pub const AUTOMOD_ID: i64 = 0; pub struct ModerationMessages { pub messages: Vec, diff --git a/apps/labrinth/src/routes/internal/admin.rs b/apps/labrinth/src/routes/internal/admin.rs index 3a39449f9..60d69cf48 100644 --- a/apps/labrinth/src/routes/internal/admin.rs +++ b/apps/labrinth/src/routes/internal/admin.rs @@ -1,10 +1,13 @@ use crate::auth::validate::get_user_record_from_bearer_token; +use crate::database::models::thread_item::ThreadMessageBuilder; use crate::database::redis::RedisPool; use crate::models::analytics::Download; use crate::models::ids::ProjectId; use crate::models::pats::Scopes; +use crate::models::threads::MessageBody; use crate::queue::analytics::AnalyticsQueue; use crate::queue::maxmind::MaxMindIndexer; +use crate::queue::moderation::AUTOMOD_ID; use crate::queue::payouts::PayoutsQueue; use crate::queue::session::AuthQueue; use crate::routes::ApiError; @@ -23,7 +26,8 @@ pub fn config(cfg: &mut web::ServiceConfig) { web::scope("admin") .service(count_download) .service(force_reindex) - .service(get_balances), + .service(get_balances) + .service(delphi_result_ingest), ); } @@ -178,3 +182,70 @@ pub async fn get_balances( "tremendous": tremendous, }))) } + +#[derive(Deserialize)] +pub struct DelphiIngest { + pub url: String, + pub project_id: crate::models::ids::ProjectId, + pub version_id: crate::models::ids::VersionId, + pub issues: Vec, +} + +#[post("/_delphi", guard = "admin_key_guard")] +pub async fn delphi_result_ingest( + pool: web::Data, + redis: web::Data, + body: web::Json, +) -> Result { + let webhook_url = dotenvy::var("DELPHI_SLACK_WEBHOOK")?; + + let project = crate::database::models::Project::get_id( + body.project_id.into(), + &**pool, + &redis, + ) + .await? + .ok_or_else(|| { + ApiError::InvalidInput(format!( + "Project {} does not exist", + body.project_id + )) + })?; + + crate::util::webhook::send_slack_webhook( + body.project_id, + &pool, + &redis, + webhook_url, + Some(format!( + "Suspicious traces found at {}. Traces: {}", + body.url, + body.issues.join(", ") + )), + ) + .await + .ok(); + + let mut transaction = pool.begin().await?; + ThreadMessageBuilder { + author_id: Some(crate::database::models::UserId(AUTOMOD_ID)), + body: MessageBody::Text { + body: format!( + "WSR; Suspicious traces found for version_id {}. Traces: {}", + body.version_id, + body.issues.join(", ") + ), + private: true, + replying_to: None, + associated_images: vec![], + }, + thread_id: project.thread_id, + hide_identity: false, + } + .insert(&mut transaction) + .await?; + + transaction.commit().await?; + + Ok(HttpResponse::NoContent().finish()) +} diff --git a/apps/labrinth/src/routes/v2/projects.rs b/apps/labrinth/src/routes/v2/projects.rs index 7bc5eed60..c7b128ef8 100644 --- a/apps/labrinth/src/routes/v2/projects.rs +++ b/apps/labrinth/src/routes/v2/projects.rs @@ -61,11 +61,6 @@ pub async fn project_search( let facets: Option>> = if let Some(facets) = info.facets { let facets = serde_json::from_str::>>(&facets)?; - // These loaders specifically used to be combined with 'mod' to be a plugin, but now - // they are their own loader type. We will convert 'mod' to 'mod' OR 'plugin' - // as it essentially was before. - let facets = v2_reroute::convert_plugin_loader_facets_v3(facets); - Some( facets .into_iter() diff --git a/apps/labrinth/src/routes/v2_reroute.rs b/apps/labrinth/src/routes/v2_reroute.rs index 665f11a7f..fc0fc57e2 100644 --- a/apps/labrinth/src/routes/v2_reroute.rs +++ b/apps/labrinth/src/routes/v2_reroute.rs @@ -190,28 +190,6 @@ pub fn convert_side_types_v3( fields } -// Converts plugin loaders from v2 to v3, for search facets -// Within every 1st and 2nd level (the ones allowed in v2), we convert every instance of: -// "project_type:mod" to "project_type:plugin" OR "project_type:mod" -pub fn convert_plugin_loader_facets_v3( - facets: Vec>, -) -> Vec> { - facets - .into_iter() - .map(|inner_facets| { - if inner_facets == ["project_type:mod"] { - vec![ - "project_type:plugin".to_string(), - "project_type:datapack".to_string(), - "project_type:mod".to_string(), - ] - } else { - inner_facets - } - }) - .collect::>() -} - // Convert search facets from V3 back to v2 // this is not lossless. (See tests) pub fn convert_side_types_v2( diff --git a/apps/labrinth/src/routes/v3/version_creation.rs b/apps/labrinth/src/routes/v3/version_creation.rs index 8d531c22b..5382d1afa 100644 --- a/apps/labrinth/src/routes/v3/version_creation.rs +++ b/apps/labrinth/src/routes/v3/version_creation.rs @@ -31,6 +31,7 @@ use actix_web::{web, HttpRequest, HttpResponse}; use chrono::Utc; use futures::stream::StreamExt; use itertools::Itertools; +use log::error; use serde::{Deserialize, Serialize}; use sqlx::postgres::PgPool; use std::collections::{HashMap, HashSet}; @@ -980,6 +981,24 @@ pub async fn upload_file( } } + let url = format!("{cdn_url}/{file_path_encode}"); + + let client = reqwest::Client::new(); + let delphi_url = dotenvy::var("DELPHI_URL")?; + let res = client + .post(delphi_url) + .json(&serde_json::json!({ + "url": url, + "project_id": project_id, + "version_id": version_id, + })) + .send() + .await?; + + if !res.status().is_success() { + error!("Failed to upload file to Delphi: {url}"); + } + version_files.push(VersionFileBuilder { filename: file_name.to_string(), url: format!("{cdn_url}/{file_path_encode}"),