Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions src/achievements.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::auth::{AuthManager, AuthSource};
use crate::auth::require_api_key;
use crate::config;
use anyhow::{Context, Result};
use serde::Deserialize;
Expand Down Expand Up @@ -80,17 +80,6 @@ async fn upload_achievement_image(
Ok(presigned.r2_key)
}

fn require_api_key() -> Result<String> {
let auth_manager = AuthManager::new()?;
let auth_info = auth_manager.get_auth_info();
match auth_info.source {
AuthSource::None => {
anyhow::bail!("Not authenticated. Run `wavedash auth login` first.")
}
_ => Ok(auth_info.api_key.unwrap()),
}
}

pub struct CreateAchievementArgs<'a> {
pub game_id: &'a str,
pub identifier: &'a str,
Expand Down
14 changes: 14 additions & 0 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,20 @@ impl AuthManager {
}
}

/// Return the active API key, or bail with the standard "not authenticated"
/// message. Shared by every scripted command so the message and the
/// [`AuthSource`] match stay in sync in one place.
pub(crate) fn require_api_key() -> Result<String> {
let auth_manager = AuthManager::new()?;
let auth_info = auth_manager.get_auth_info();
match auth_info.source {
AuthSource::None => {
bail!("Not authenticated. Run `wavedash auth login` first.")
}
_ => Ok(auth_info.api_key.unwrap()),
}
}

pub(crate) fn generate_state() -> String {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
Expand Down
32 changes: 5 additions & 27 deletions src/clear_playtest_data.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::auth::{AuthManager, AuthSource};
use crate::auth::require_api_key;
use crate::config;
use anyhow::Result;
use colored::Colorize;
use serde::Deserialize;
use serde_json::json;
use std::collections::BTreeMap;
use std::io::IsTerminal;

/// A single kind of playtest data that can be wiped for a game.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -90,29 +89,6 @@ impl ClearPlaytestDataArgs<'_> {
}
}

fn require_api_key() -> Result<String> {
let auth_manager = AuthManager::new()?;
let auth_info = auth_manager.get_auth_info();
match auth_info.source {
AuthSource::None => {
anyhow::bail!("Not authenticated. Run `wavedash auth login` first.")
}
_ => Ok(auth_info.api_key.unwrap()),
}
}

/// True when we can't safely prompt for confirmation (CI or piped stdin).
/// Mirrors `is_browser_login_unavailable` in main.rs.
fn is_non_interactive() -> bool {
let ci = std::env::var("CI")
.map(|value| {
let value = value.trim().to_ascii_lowercase();
!value.is_empty() && value != "0" && value != "false"
})
.unwrap_or(false);
ci || !std::io::stdin().is_terminal()
}

#[derive(Debug, Deserialize)]
struct ClearResult {
/// Per-category result, keyed by the category's `api_key`. Values are counts
Expand Down Expand Up @@ -147,9 +123,11 @@ pub async fn handle_clear_playtest_data(args: ClearPlaytestDataArgs<'_>) -> Resu
None => "ALL players".to_string(),
};

// Confirm before doing anything destructive.
// Confirm before doing anything destructive. When we can't prompt (CI or
// piped stdin), the same check `wavedash auth login` uses, refuse rather
// than silently deleting.
if !args.force {
if is_non_interactive() {
if crate::is_browser_login_unavailable() {
anyhow::bail!(
"Refusing to clear playtest data without confirmation.\n\
Re-run with --force (alias --yes / -y) to proceed non-interactively."
Expand Down
13 changes: 1 addition & 12 deletions src/init.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::auth::{AuthManager, AuthSource};
use crate::auth::{require_api_key, AuthManager, AuthSource};
use crate::config;
use anyhow::Result;
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
Expand Down Expand Up @@ -444,17 +444,6 @@ pub async fn handle_init() -> Result<()> {

// ── Scripted create commands ─────────────────────────────────────────

fn require_api_key() -> Result<String> {
let auth_manager = AuthManager::new()?;
let auth_info = auth_manager.get_auth_info();
match auth_info.source {
AuthSource::None => {
anyhow::bail!("Not authenticated. Run `wavedash auth login` first.")
}
_ => Ok(auth_info.api_key.unwrap()),
}
}

pub async fn handle_team_create(name: &str) -> Result<()> {
let api_key = require_api_key()?;
let team = create_organization(&api_key, name).await?;
Expand Down
6 changes: 4 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ enum AchievementCommands {
},
}

fn env_flag_enabled(name: &str) -> bool {
pub(crate) fn env_flag_enabled(name: &str) -> bool {
std::env::var(name)
.map(|value| {
let value = value.trim().to_ascii_lowercase();
Expand All @@ -439,7 +439,9 @@ fn env_flag_enabled(name: &str) -> bool {
.unwrap_or(false)
}

fn is_browser_login_unavailable() -> bool {
/// True when we can't drive an interactive flow (CI or piped stdin), so
/// browser login and confirmation prompts must be skipped.
pub(crate) fn is_browser_login_unavailable() -> bool {
env_flag_enabled("CI") || !std::io::stdin().is_terminal()
}

Expand Down
13 changes: 1 addition & 12 deletions src/stats.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::auth::{AuthManager, AuthSource};
use crate::auth::require_api_key;
use crate::config;
use anyhow::Result;
use serde::Deserialize;
Expand All @@ -12,17 +12,6 @@ struct Stat {
display_name: String,
}

fn require_api_key() -> Result<String> {
let auth_manager = AuthManager::new()?;
let auth_info = auth_manager.get_auth_info();
match auth_info.source {
AuthSource::None => {
anyhow::bail!("Not authenticated. Run `wavedash auth login` first.")
}
_ => Ok(auth_info.api_key.unwrap()),
}
}

pub async fn handle_stat_create(game_id: &str, identifier: &str, name: &str) -> Result<()> {
let api_key = require_api_key()?;
let client = config::create_http_client()?;
Expand Down
Loading