Support merging legacy and multi-config enterprise settings

This commit is contained in:
Thorsten Sommer 2026-02-15 15:30:07 +01:00
parent d1d325ef59
commit 9eb1b9d726
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
2 changed files with 46 additions and 43 deletions

View File

@ -39,7 +39,7 @@ MINDWORK_AI_STUDIO_ENTERPRISE_CONFIGS=9072b77d-ca81-40da-be6a-861da525ef7b@https
### Single configuration (legacy)
The following single-configuration keys and variables are still supported for backwards compatibility. If the multi-config variables above are not set, AI Studio falls back to these:
The following single-configuration keys and variables are still supported for backwards compatibility. AI Studio always reads both the multi-config and legacy variables and merges all found configurations into one list. If a configuration ID appears in both, the entry from the multi-config format takes priority (first occurrence wins). This means you can migrate to the new format incrementally without losing existing configurations:
- Key `HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT`, value `config_id` or variable `MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ID`: This must be a valid [GUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Globally_unique_identifier). It uniquely identifies the configuration. You can use an ID per department, institute, or even per person.

View File

@ -152,45 +152,43 @@ pub struct EnterpriseConfig {
pub server_url: String,
}
/// Returns all enterprise configurations. Supports the new multi-config format
/// (`id1@url1;id2@url2`) as well as the legacy single-config environment variables.
/// 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.");
// Try the new combined format first:
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; ...
let configs: Vec<EnterpriseConfig> = combined
.split(';')
.filter_map(|entry| {
let entry = entry.trim();
if entry.is_empty() {
return None;
// 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 });
}
// Split at the first '@' (GUIDs never contain '@'):
entry.split_once('@').and_then(|(id, url)| {
let id = id.trim().to_string();
let url = url.trim().to_string();
if id.is_empty() || url.is_empty() {
None
} else {
Some(EnterpriseConfig { id, server_url: url })
}
})
})
.collect();
return Json(configs);
}
}
}
// Fallback: read the legacy single-config variables:
// Also read the legacy single-config variables:
let config_id = get_enterprise_configuration(
"config_id",
"MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ID",
@ -202,13 +200,13 @@ pub fn read_enterprise_configs(_token: APIToken) -> Json<Vec<EnterpriseConfig>>
);
if !config_id.is_empty() && !config_server_url.is_empty() {
return Json(vec![EnterpriseConfig {
id: config_id,
server_url: config_server_url,
}]);
let id = config_id.trim().to_lowercase();
if seen_ids.insert(id.clone()) {
configs.push(EnterpriseConfig { id, server_url: config_server_url });
}
}
Json(vec![])
Json(configs)
}
/// Returns all enterprise configuration IDs that should be deleted. Supports the new
@ -217,33 +215,38 @@ pub fn read_enterprise_configs(_token: APIToken) -> Json<Vec<EnterpriseConfig>>
pub fn read_enterprise_delete_config_ids(_token: APIToken) -> Json<Vec<String>> {
info!("Trying to read the enterprise environment for configuration IDs to delete.");
// Try the new combined format first:
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() {
let ids: Vec<String> = combined
.split(';')
.map(|id| id.trim().to_string())
.filter(|id| !id.is_empty())
.collect();
return Json(ids);
for id in combined.split(';') {
let id = id.trim().to_lowercase();
if !id.is_empty() && seen.insert(id.clone()) {
ids.push(id);
}
}
}
// Fallback: read the legacy single-delete variable:
// 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() {
return Json(vec![delete_id]);
let id = delete_id.trim().to_lowercase();
if seen.insert(id.clone()) {
ids.push(id);
}
}
Json(vec![])
Json(ids)
}
fn get_enterprise_configuration(_reg_value: &str, env_name: &str) -> String {