AI-Studio/runtime/src/environment.rs

303 lines
11 KiB
Rust
Raw Normal View History

use std::env;
2024-11-05 20:39:21 +00:00
use std::sync::OnceLock;
use log::{debug, info, warn};
use rocket::{delete, get};
use rocket::serde::json::Json;
use serde::Serialize;
use sys_locale::get_locale;
2024-11-05 20:39:21 +00:00
use crate::api_token::APIToken;
/// The data directory where the application stores its data.
pub static DATA_DIRECTORY: OnceLock<String> = OnceLock::new();
/// The config directory where the application stores its configuration.
pub static CONFIG_DIRECTORY: OnceLock<String> = OnceLock::new();
/// Returns the config directory.
#[get("/system/directories/config")]
pub fn get_config_directory(_token: APIToken) -> String {
match CONFIG_DIRECTORY.get() {
Some(config_directory) => config_directory.clone(),
None => String::from(""),
}
}
/// Returns the data directory.
#[get("/system/directories/data")]
pub fn get_data_directory(_token: APIToken) -> String {
match DATA_DIRECTORY.get() {
Some(data_directory) => data_directory.clone(),
None => String::from(""),
}
}
/// Returns true if the application is running in development mode.
pub fn is_dev() -> bool {
cfg!(debug_assertions)
}
/// Returns true if the application is running in production mode.
pub fn is_prod() -> bool {
!is_dev()
}
#[get("/system/language")]
pub fn read_user_language(_token: APIToken) -> String {
get_locale().unwrap_or_else(|| {
2025-06-09 12:06:54 +00:00
warn!("Could not determine the system language. Use default 'en-US'.");
String::from("en-US")
})
}
#[get("/system/enterprise/config/id")]
pub fn read_enterprise_env_config_id(_token: APIToken) -> String {
//
// When we are on a Windows machine, we try to read the enterprise config from
// the Windows registry. In case we can't find the registry key, or we are on a
// macOS or Linux machine, we try to read the enterprise config from the
// environment variables.
//
// The registry key is:
// HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT
//
// In this registry key, we expect the following values:
// - config_id
//
// The environment variable is:
// MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ID
//
2025-06-09 12:06:54 +00:00
debug!("Trying to read the enterprise environment for some config ID.");
get_enterprise_configuration(
"config_id",
"MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ID",
)
}
#[delete("/system/enterprise/config/id")]
pub fn delete_enterprise_env_config_id(_token: APIToken) -> String {
//
// When we are on a Windows machine, we try to read the enterprise config from
// the Windows registry. In case we can't find the registry key, or we are on a
// macOS or Linux machine, we try to read the enterprise config from the
// environment variables.
//
// The registry key is:
// HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT
//
// In this registry key, we expect the following values:
// - delete_config_id
//
// The environment variable is:
// MINDWORK_AI_STUDIO_ENTERPRISE_DELETE_CONFIG_ID
//
2025-06-09 12:06:54 +00:00
debug!("Trying to read the enterprise environment for some config ID, which should be deleted.");
get_enterprise_configuration(
"delete_config_id",
"MINDWORK_AI_STUDIO_ENTERPRISE_DELETE_CONFIG_ID",
)
}
#[get("/system/enterprise/config/server")]
pub fn read_enterprise_env_config_server_url(_token: APIToken) -> String {
//
// When we are on a Windows machine, we try to read the enterprise config from
// the Windows registry. In case we can't find the registry key, or we are on a
// macOS or Linux machine, we try to read the enterprise config from the
// environment variables.
//
// The registry key is:
// HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT
//
// In this registry key, we expect the following values:
// - config_server_url
//
// The environment variable is:
// MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_SERVER_URL
//
2025-06-09 12:06:54 +00:00
debug!("Trying to read the enterprise environment for the config server URL.");
get_enterprise_configuration(
"config_server_url",
"MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_SERVER_URL",
)
}
#[get("/system/enterprise/config/encryption_secret")]
pub fn read_enterprise_env_config_encryption_secret(_token: APIToken) -> String {
//
// When we are on a Windows machine, we try to read the enterprise config from
// the Windows registry. In case we can't find the registry key, or we are on a
// macOS or Linux machine, we try to read the enterprise config from the
// environment variables.
//
// The registry key is:
// HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT
//
// In this registry key, we expect the following values:
// - config_encryption_secret
//
// The environment variable is:
// MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ENCRYPTION_SECRET
//
debug!("Trying to read the enterprise environment for the config encryption secret.");
get_enterprise_configuration(
"config_encryption_secret",
"MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ENCRYPTION_SECRET",
)
}
/// Represents a single enterprise configuration entry with an ID and server URL.
#[derive(Serialize)]
pub struct EnterpriseConfig {
pub id: String,
pub server_url: String,
}
/// Returns all enterprise configurations. Collects configurations from both the
/// new multi-config format (`id1@url1;id2@url2`) and the legacy single-config
/// environment variables, merging them into one list. Duplicates (by ID) are
/// skipped — the first occurrence wins.
#[get("/system/enterprise/configs")]
pub fn read_enterprise_configs(_token: APIToken) -> Json<Vec<EnterpriseConfig>> {
info!("Trying to read the enterprise environment for all configurations.");
let mut configs: Vec<EnterpriseConfig> = Vec::new();
let mut seen_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
// Read the new combined format:
let combined = get_enterprise_configuration(
"configs",
"MINDWORK_AI_STUDIO_ENTERPRISE_CONFIGS",
);
if !combined.is_empty() {
// Parse the new format: id1@url1;id2@url2;...
for entry in combined.split(';') {
let entry = entry.trim();
if entry.is_empty() {
continue;
}
// Split at the first '@' (GUIDs never contain '@'):
if let Some((id, url)) = entry.split_once('@') {
let id = id.trim().to_lowercase();
let url = url.trim().to_string();
if !id.is_empty() && !url.is_empty() && seen_ids.insert(id.clone()) {
configs.push(EnterpriseConfig { id, server_url: url });
}
}
}
}
// Also read the legacy single-config variables:
let config_id = get_enterprise_configuration(
"config_id",
"MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ID",
);
let config_server_url = get_enterprise_configuration(
"config_server_url",
"MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_SERVER_URL",
);
if !config_id.is_empty() && !config_server_url.is_empty() {
let id = config_id.trim().to_lowercase();
if seen_ids.insert(id.clone()) {
configs.push(EnterpriseConfig { id, server_url: config_server_url });
}
}
Json(configs)
}
/// Returns all enterprise configuration IDs that should be deleted. Supports the new
/// multi-delete format (`id1;id2;id3`) as well as the legacy single-delete variable.
#[get("/system/enterprise/delete-configs")]
pub fn read_enterprise_delete_config_ids(_token: APIToken) -> Json<Vec<String>> {
info!("Trying to read the enterprise environment for configuration IDs to delete.");
let mut ids: Vec<String> = Vec::new();
let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
// Read the new combined format:
let combined = get_enterprise_configuration(
"delete_config_ids",
"MINDWORK_AI_STUDIO_ENTERPRISE_DELETE_CONFIG_IDS",
);
if !combined.is_empty() {
for id in combined.split(';') {
let id = id.trim().to_lowercase();
if !id.is_empty() && seen.insert(id.clone()) {
ids.push(id);
}
}
}
// Also read the legacy single-delete variable:
let delete_id = get_enterprise_configuration(
"delete_config_id",
"MINDWORK_AI_STUDIO_ENTERPRISE_DELETE_CONFIG_ID",
);
if !delete_id.is_empty() {
let id = delete_id.trim().to_lowercase();
if seen.insert(id.clone()) {
ids.push(id);
}
}
Json(ids)
}
fn get_enterprise_configuration(_reg_value: &str, env_name: &str) -> String {
cfg_if::cfg_if! {
if #[cfg(target_os = "windows")] {
info!(r"Detected a Windows machine, trying to read the registry key 'HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT\{}' or the environment variable '{}'.", _reg_value, env_name);
use windows_registry::*;
let key_path = r"Software\github\MindWork AI Studio\Enterprise IT";
let key = match CURRENT_USER.open(key_path) {
Ok(key) => key,
Err(_) => {
info!(r"Could not read the registry key 'HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT\{}'. Falling back to the environment variable '{}'.", _reg_value, env_name);
return match env::var(env_name) {
Ok(val) => {
info!("Falling back to the environment variable '{}' was successful.", env_name);
val
},
Err(_) => {
info!("Falling back to the environment variable '{}' was not successful. It seems that there is no enterprise environment available.", env_name);
"".to_string()
},
}
},
};
match key.get_string(_reg_value) {
Ok(val) => val,
Err(_) => {
info!(r"We could read the registry key 'HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT', but the value '{}' could not be read. Falling back to the environment variable '{}'.", _reg_value, env_name);
match env::var(env_name) {
Ok(val) => {
info!("Falling back to the environment variable '{}' was successful.", env_name);
val
},
Err(_) => {
info!("Falling back to the environment variable '{}' was not successful. It seems that there is no enterprise environment available.", env_name);
"".to_string()
}
}
},
}
} else {
// In the case of macOS or Linux, we just read the environment variable:
info!(r"Detected a Unix machine, trying to read the environment variable '{}'.", env_name);
match env::var(env_name) {
Ok(val) => val,
Err(_) => {
info!("The environment variable '{}' was not found. It seems that there is no enterprise environment available.", env_name);
"".to_string()
}
}
}
}
2024-11-05 20:39:21 +00:00
}