diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..9e7e55a3 --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +# Copy this file to .env and fill in values to enable optional features at build time. +# .env is gitignored and will never be committed. + +# CurseForge API key — required only to build with CurseForge modpack support. +# Obtain one at https://console.curseforge.com/ +# If absent, CurseForge modpack import is disabled at runtime (all other features work normally). +# CURSEFORGE_API_KEY=your_key_here diff --git a/.gitignore b/.gitignore index 22fcb7f0..81ca5c5d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,10 @@ __pycache__/ # Tauri artifacts artifacts/ +# Local secrets (do not commit) +.env +.env.local + # AUR Release release/ diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index b375c6e2..ccec4630 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -52,6 +52,7 @@ ctor = "0.6.3" inventory = "0.3.21" [build-dependencies] +dotenvy = { version = "0.15", default-features = false } tauri-build = { version = "2.0", features = [] } [target.'cfg(all(windows, target_env = "gnu"))'.build-dependencies] diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 63f98e2b..00f5755d 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,4 +1,11 @@ fn main() { + // Load .env file if present so optional build-time vars (e.g. CURSEFORGE_API_KEY) + // are available to option_env!() without requiring CI to have a real .env file. + if let Ok(path) = dotenvy::dotenv() { + println!("cargo:rerun-if-changed={}", path.display()); + } + println!("cargo:rerun-if-env-changed=CURSEFORGE_API_KEY"); + // For MinGW targets, use embed-resource to generate proper COFF format #[cfg(all(windows, target_env = "gnu"))] { diff --git a/src-tauri/src/core/modpack.rs b/src-tauri/src/core/modpack.rs index 2998167d..97d2fa1f 100644 --- a/src-tauri/src/core/modpack.rs +++ b/src-tauri/src/core/modpack.rs @@ -294,6 +294,8 @@ fn parse_multimc(archive: &mut Archive) -> Result { // ── CurseForge API resolution ───────────────────────────────────────────── +const CURSEFORGE_API_KEY: Option<&str> = option_env!("CURSEFORGE_API_KEY"); + async fn resolve_curseforge_files(files: &[ModpackFile]) -> Result, String> { let file_ids: Vec = files .iter() @@ -366,12 +368,9 @@ async fn cf_post( endpoint: &str, body: &serde_json::Value, ) -> Result { - let api_key = std::env::var("CURSEFORGE_API_KEY") - .map_err(|_| "CURSEFORGE_API_KEY is not set".to_string())?; - if api_key.trim().is_empty() { - return Err("CURSEFORGE_API_KEY is empty".to_string()); - } - + let api_key = CURSEFORGE_API_KEY + .ok_or("CurseForge modpack support requires CURSEFORGE_API_KEY set at build time")?; + let resp = client .post(format!("https://api.curseforge.com{endpoint}")) .header("x-api-key", api_key)