From e7c6da593da176b3274c46868f061e7ed9831630 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Fri, 1 May 2026 23:33:42 +0300 Subject: [PATCH] cli: prompt for missing CurseForge credentials on add Signed-off-by: NotAShelf Change-Id: Id0bbdc9ed62bc8b9582ccb89158e53786a6a6964 --- src/cli/commands/add.rs | 92 ++++++++++++++++++++++++++++++++++++- src/cli/commands/add_prj.rs | 12 ++++- 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/src/cli/commands/add.rs b/src/cli/commands/add.rs index 561ac82..3112375 100644 --- a/src/cli/commands/add.rs +++ b/src/cli/commands/add.rs @@ -1,16 +1,99 @@ -use std::collections::HashMap; +use std::{collections::HashMap, time::Duration}; use crate::{ error::{MultiError, PakkerError, Result}, - model::{Config, LockFile, Project, credentials::ResolvedCredentials}, + http, + model::{ + Config, + LockFile, + PakkerCredentialsFile, + Project, + Target, + credentials::ResolvedCredentials, + set_keyring_secret, + }, platform::create_platform, resolver::DependencyResolver, + ui_utils::prompt_curseforge_api_key, }; fn get_loaders(lockfile: &LockFile) -> Vec { lockfile.loaders.keys().cloned().collect() } +fn needs_curseforge(target: Option<&Target>) -> bool { + matches!( + target, + Some(Target::CurseForge) | Some(Target::Multiplatform) + ) +} + +async fn ensure_curseforge_credentials() -> Result { + let creds = ResolvedCredentials::load(); + if creds.curseforge_api_key().is_some() { + return Ok(true); + } + + if let Some(key) = prompt_curseforge_api_key(false)? { + // Verify the key before saving + let client = http::create_http_client(); + let response = client + .get("https://api.curseforge.com/v1/mods/238222") + .header("x-api-key", &key) + .timeout(Duration::from_secs(10)) + .send() + .await; + + match response { + Ok(resp) if resp.status().is_success() => { + let mut creds_file = PakkerCredentialsFile::load()?; + set_keyring_secret("curseforge_api_key", &key)?; + creds_file.curseforge_api_key = Some(key.clone()); + creds_file.save()?; + println!("CurseForge API key verified and saved."); + Ok(true) + }, + Ok(resp) => { + println!( + "Warning: CurseForge API key verification failed (HTTP {}).", + resp.status() + ); + if crate::ui_utils::prompt_yes_no( + "Save this key anyway?", + false, + false, + )? { + let mut creds_file = PakkerCredentialsFile::load()?; + set_keyring_secret("curseforge_api_key", &key)?; + creds_file.curseforge_api_key = Some(key); + creds_file.save()?; + Ok(true) + } else { + Ok(false) + } + }, + Err(e) => { + println!("Warning: Could not verify CurseForge API key: {e}"); + if crate::ui_utils::prompt_yes_no( + "Save this key anyway?", + false, + false, + )? { + let mut creds_file = PakkerCredentialsFile::load()?; + set_keyring_secret("curseforge_api_key", &key)?; + creds_file.curseforge_api_key = Some(key); + creds_file.save()?; + Ok(true) + } else { + Ok(false) + } + }, + } + } else { + Ok(false) + } +} + pub fn create_all_platforms() -> HashMap> { let mut platforms = HashMap::new(); @@ -166,6 +249,11 @@ pub async fn execute( let mut lockfile = LockFile::load_with_validation(lockfile_dir, false)?; + // Prompt for missing CurseForge credentials when needed + if needs_curseforge(lockfile.target.as_ref()) && !skip_prompts { + let _ = ensure_curseforge_credentials().await; + } + // Load config if available let _config = Config::load(config_dir).ok(); diff --git a/src/cli/commands/add_prj.rs b/src/cli/commands/add_prj.rs index 10a1bf9..3db9e32 100644 --- a/src/cli/commands/add_prj.rs +++ b/src/cli/commands/add_prj.rs @@ -11,6 +11,7 @@ use crate::{ }, platform::create_platform, resolver::DependencyResolver, + ui_utils::prompt_curseforge_api_key, }; /// Parse a common project argument (slug or ID with optional file ID) @@ -100,7 +101,16 @@ pub async fn execute( log::info!("Fetching from CurseForge: {cf_input}"); let (input, file_id) = parse_common_arg(&cf_input); - let cf_api_key = std::env::var("CURSEFORGE_API_KEY").ok(); + let credentials = ResolvedCredentials::load(); + let mut cf_api_key = credentials.curseforge_api_key().map(String::from); + + // Prompt for missing CurseForge credentials + if cf_api_key.is_none() && !yes { + if let Some(key) = prompt_curseforge_api_key(false)? { + cf_api_key = Some(key); + } + } + let platform = create_platform("curseforge", cf_api_key)?; let mut project = platform