Jesús Pérez 9cef9b8d57 refactor: consolidate configuration directories
Merge _configs/ into config/ for single configuration directory.
Update all path references.

Changes:
- Move _configs/* to config/
- Update .gitignore for new patterns
- No code references to _configs/ found

Impact: -1 root directory (layout_conventions.md compliance)
2025-12-26 18:36:23 +00:00

125 lines
3.6 KiB
Rust

//! Runtime configuration for the Syntaxis Dashboard
//!
//! This module handles loading API configuration at runtime from the browser environment.
//! The configuration is injected into `window.SYNTAXIS_CONFIG` by `index.html`.
//!
//! # Configuration Sources
//!
//! In order of precedence:
//! 1. URL query parameter: `?apiUrl=http://custom-api:3000`
//! 2. `window.SYNTAXIS_CONFIG.apiUrl` (set by index.html)
//! 3. Default: `window.location.origin` (same-origin, current domain)
//!
//! # Example
//!
//! ```
//! // Default behavior (auto-detect from current domain)
//! let url = get_api_base_url();
//! // Result: "http://localhost:3000" (if served on localhost:3000)
//!
//! // Override via URL parameter
//! // Visit: http://localhost:8080?apiUrl=http://remote-api:3000
//! let url = get_api_base_url();
//! // Result: "http://remote-api:3000"
//! ```
#![forbid(unsafe_code)]
use js_sys::Reflect;
use wasm_bindgen::JsValue;
use web_sys::window;
/// Get the API base URL from browser configuration
///
/// Returns the configured API URL with priority:
/// 1. URL parameter `?apiUrl=...`
/// 2. `window.SYNTAXIS_CONFIG.apiUrl`
/// 3. `window.location.origin` (same-origin default)
///
/// # Panics
///
/// Panics if `window` is not available (shouldn't happen in browser context)
#[must_use]
pub fn get_api_base_url() -> String {
let window = window().expect("window should be available");
// Try to get apiUrl from window.SYNTAXIS_CONFIG
let config_url = get_config_api_url(&window);
if let Some(url) = config_url {
return url;
}
// Fall back to window.location.origin
window
.location()
.origin()
.unwrap_or_else(|_| "http://localhost:3000".to_string())
}
/// Get the API path prefix from configuration (default: /api)
#[must_use]
pub fn get_api_path() -> String {
let window = match window() {
Some(w) => w,
None => return "/api".to_string(),
};
get_config_api_path(&window).unwrap_or_else(|| "/api".to_string())
}
/// Get the full API URL including path prefix
///
/// Example: `http://localhost:3000/api`
#[must_use]
pub fn get_api_url() -> String {
format!("{}{}", get_api_base_url(), get_api_path())
}
fn get_config_api_url(window: &web_sys::Window) -> Option<String> {
// First, try URL parameter (?apiUrl=...)
if let Some(url) = get_url_param_api_url(window) {
return Some(url);
}
// Then try window.SYNTAXIS_CONFIG.apiUrl
let config = Reflect::get(window, &JsValue::from_str("SYNTAXIS_CONFIG")).ok()?;
Reflect::get(&config, &JsValue::from_str("apiUrl"))
.ok()?
.as_string()
}
fn get_config_api_path(window: &web_sys::Window) -> Option<String> {
let config = Reflect::get(window, &JsValue::from_str("SYNTAXIS_CONFIG")).ok()?;
Reflect::get(&config, &JsValue::from_str("apiPath"))
.ok()?
.as_string()
}
fn get_url_param_api_url(window: &web_sys::Window) -> Option<String> {
let location = window.location();
let search = location.search().ok()?;
// Parse URL parameters
let params = web_sys::UrlSearchParams::new_with_str(&search).ok()?;
params.get("apiUrl")
}
#[cfg(test)]
mod tests {
#[test]
fn test_get_api_path() {
// This test runs in a test environment without a browser
// The actual function requires window, so we just verify the default
let path = "/api".to_string();
assert_eq!(path, "/api");
}
#[test]
fn test_api_url_construction() {
let base = "http://localhost:3000".to_string();
let path = "/api".to_string();
let full_url = format!("{}{}", base, path);
assert_eq!(full_url, "http://localhost:3000/api");
}
}