Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Icbca98ba56bc30e85d0f3972af616ca96a6a6964
219 lines
5.1 KiB
Rust
219 lines
5.1 KiB
Rust
use std::{io::Write, time::Duration};
|
|
|
|
use crate::{
|
|
error::{PakkerError, Result},
|
|
http,
|
|
model::{PakkerCredentialsFile, set_keyring_secret},
|
|
ui_utils::{prompt_secret, prompt_yes_no},
|
|
};
|
|
|
|
pub async fn execute(
|
|
curseforge_api_key: Option<String>,
|
|
modrinth_token: Option<String>,
|
|
github_access_token: Option<String>,
|
|
) -> Result<()> {
|
|
let mut cf_key = curseforge_api_key;
|
|
let mut mr_token = modrinth_token;
|
|
let mut gh_token = github_access_token;
|
|
|
|
let any_cli_args =
|
|
cf_key.is_some() || mr_token.is_some() || gh_token.is_some();
|
|
|
|
// Enter interactive mode when no CLI args provided
|
|
if !any_cli_args {
|
|
println!("No credentials provided via command line.");
|
|
println!();
|
|
|
|
if let Some(key) =
|
|
prompt_secret("CurseForge API key (press Enter to skip)")?
|
|
{
|
|
cf_key = Some(key);
|
|
}
|
|
|
|
if let Some(token) = prompt_secret("Modrinth token (press Enter to skip)")?
|
|
{
|
|
mr_token = Some(token);
|
|
}
|
|
|
|
if let Some(token) =
|
|
prompt_secret("GitHub access token (press Enter to skip)")?
|
|
{
|
|
gh_token = Some(token);
|
|
}
|
|
}
|
|
|
|
let updated_any =
|
|
cf_key.is_some() || mr_token.is_some() || gh_token.is_some();
|
|
|
|
if !updated_any {
|
|
println!("No credentials to save.");
|
|
return Ok(());
|
|
}
|
|
|
|
// Verify credentials before saving
|
|
let client = http::create_http_client();
|
|
let mut verified = Vec::new();
|
|
|
|
if let Some(ref key) = cf_key {
|
|
print!("Verifying CurseForge API key... ");
|
|
std::io::stdout().flush().ok();
|
|
match verify_curseforge(&client, key).await {
|
|
Ok(()) => {
|
|
println!("valid");
|
|
verified.push("CurseForge");
|
|
},
|
|
Err(e) => {
|
|
println!("failed ({e})");
|
|
if !prompt_yes_no(
|
|
"CurseForge key appears invalid. Save anyway?",
|
|
false,
|
|
false,
|
|
)? {
|
|
cf_key = None;
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
if let Some(ref token) = mr_token {
|
|
print!("Verifying Modrinth token... ");
|
|
std::io::stdout().flush().ok();
|
|
match verify_modrinth(&client, token).await {
|
|
Ok(()) => {
|
|
println!("valid");
|
|
verified.push("Modrinth");
|
|
},
|
|
Err(e) => {
|
|
println!("failed ({e})");
|
|
if !prompt_yes_no(
|
|
"Modrinth token appears invalid. Save anyway?",
|
|
false,
|
|
false,
|
|
)? {
|
|
mr_token = None;
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
if let Some(ref token) = gh_token {
|
|
print!("Verifying GitHub access token... ");
|
|
std::io::stdout().flush().ok();
|
|
match verify_github(&client, token).await {
|
|
Ok(()) => {
|
|
println!("valid");
|
|
verified.push("GitHub");
|
|
},
|
|
Err(e) => {
|
|
println!("failed ({e})");
|
|
if !prompt_yes_no(
|
|
"GitHub token appears invalid. Save anyway?",
|
|
false,
|
|
false,
|
|
)? {
|
|
gh_token = None;
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
let mut creds = PakkerCredentialsFile::load()?;
|
|
|
|
if let Some(key) = cf_key {
|
|
let key = key.trim().to_string();
|
|
if !key.is_empty() {
|
|
set_keyring_secret("curseforge_api_key", &key)?;
|
|
creds.curseforge_api_key = Some(key);
|
|
}
|
|
}
|
|
|
|
if let Some(token) = mr_token {
|
|
let token = token.trim().to_string();
|
|
if !token.is_empty() {
|
|
set_keyring_secret("modrinth_token", &token)?;
|
|
creds.modrinth_token = Some(token);
|
|
}
|
|
}
|
|
|
|
if let Some(token) = gh_token {
|
|
let token = token.trim().to_string();
|
|
if !token.is_empty() {
|
|
set_keyring_secret("github_access_token", &token)?;
|
|
creds.github_access_token = Some(token);
|
|
}
|
|
}
|
|
|
|
creds.save()?;
|
|
|
|
println!();
|
|
if verified.is_empty() {
|
|
println!("Credentials saved (unverified).");
|
|
} else {
|
|
println!("Credentials saved and verified: {}", verified.join(", "));
|
|
}
|
|
println!(
|
|
"Credentials file: {}",
|
|
PakkerCredentialsFile::get_path()?.display()
|
|
);
|
|
println!("Keyring service: pakker");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn verify_curseforge(
|
|
client: &reqwest::Client,
|
|
api_key: &str,
|
|
) -> Result<()> {
|
|
let response = client
|
|
.get("https://api.curseforge.com/v1/mods/238222")
|
|
.header("x-api-key", api_key)
|
|
.timeout(Duration::from_secs(10))
|
|
.send()
|
|
.await?;
|
|
|
|
if response.status().is_success() {
|
|
Ok(())
|
|
} else {
|
|
Err(PakkerError::PlatformApiError(format!(
|
|
"HTTP {}",
|
|
response.status()
|
|
)))
|
|
}
|
|
}
|
|
|
|
async fn verify_modrinth(client: &reqwest::Client, token: &str) -> Result<()> {
|
|
let response = client
|
|
.get("https://api.modrinth.com/v2/user")
|
|
.header("Authorization", token)
|
|
.timeout(Duration::from_secs(10))
|
|
.send()
|
|
.await?;
|
|
|
|
if response.status().is_success() {
|
|
Ok(())
|
|
} else {
|
|
Err(PakkerError::PlatformApiError(format!(
|
|
"HTTP {}",
|
|
response.status()
|
|
)))
|
|
}
|
|
}
|
|
|
|
async fn verify_github(client: &reqwest::Client, token: &str) -> Result<()> {
|
|
let response = client
|
|
.get("https://api.github.com/user")
|
|
.header("Authorization", format!("Bearer {token}"))
|
|
.header("User-Agent", "pakker")
|
|
.timeout(Duration::from_secs(10))
|
|
.send()
|
|
.await?;
|
|
|
|
if response.status().is_success() {
|
|
Ok(())
|
|
} else {
|
|
Err(PakkerError::PlatformApiError(format!(
|
|
"HTTP {}",
|
|
response.status()
|
|
)))
|
|
}
|
|
}
|