Add enterprise config support via env vars & Windows registry (#491)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2025-06-01 21:14:21 +02:00
1 parent b88b78a08e
commit e8cfce05fe
33 files changed
+1061 -98

No files matched your search

@@ -0,0 +1,75 @@
using AIStudio.Tools.PluginSystem;
namespace AIStudio.Tools.Services;
public sealed class EnterpriseEnvironmentService(ILogger<TemporaryChatService> logger, RustService rustService) : BackgroundService
{
public static EnterpriseEnvironment CURRENT_ENVIRONMENT;
private static readonly TimeSpan CHECK_INTERVAL = TimeSpan.FromMinutes(16);
#region Overrides of BackgroundService
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
logger.LogInformation("The enterprise environment service was initialized.");
await this.StartUpdating();
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(CHECK_INTERVAL, stoppingToken);
await this.StartUpdating();
}
}
#endregion
private async Task StartUpdating()
{
try
{
logger.LogInformation("Starting update of the enterprise environment.");
var enterpriseRemoveConfigId = await rustService.EnterpriseEnvRemoveConfigId();
var isPlugin2RemoveInUse = PluginFactory.AvailablePlugins.Any(plugin => plugin.Id == enterpriseRemoveConfigId);
if (enterpriseRemoveConfigId != Guid.Empty && isPlugin2RemoveInUse)
{
logger.LogWarning($"The enterprise environment configuration ID '{enterpriseRemoveConfigId}' must be removed.");
PluginFactory.RemovePluginAsync(enterpriseRemoveConfigId);
}
var enterpriseConfigServerUrl = await rustService.EnterpriseEnvConfigServerUrl();
var enterpriseConfigId = await rustService.EnterpriseEnvConfigId();
var nextEnterpriseEnvironment = new EnterpriseEnvironment(enterpriseConfigServerUrl, enterpriseConfigId);
if (CURRENT_ENVIRONMENT != nextEnterpriseEnvironment)
{
logger.LogInformation("The enterprise environment has changed. Updating the current environment.");
CURRENT_ENVIRONMENT = nextEnterpriseEnvironment;
switch (enterpriseConfigServerUrl)
{
case null when enterpriseConfigId == Guid.Empty:
logger.LogInformation("AI Studio runs without an enterprise configuration.");
break;
case null:
logger.LogWarning($"AI Studio runs with an enterprise configuration id ('{enterpriseConfigId}'), but the configuration server URL is not set.");
break;
case not null when enterpriseConfigId == Guid.Empty:
logger.LogWarning($"AI Studio runs with an enterprise configuration server URL ('{enterpriseConfigServerUrl}'), but the configuration ID is not set.");
break;
default:
logger.LogInformation($"AI Studio runs with an enterprise configuration id ('{enterpriseConfigId}') and configuration server URL ('{enterpriseConfigServerUrl}').");
await PluginFactory.TryDownloadingConfigPluginAsync(enterpriseConfigId, enterpriseConfigServerUrl);
break;
}
}
}
catch (Exception e)
{
logger.LogError(e, "An error occurred while updating the enterprise environment.");
}
}
}
@@ -0,0 +1,68 @@
namespace AIStudio.Tools.Services;
public sealed partial class RustService
{
/// <summary>
/// Tries to read the enterprise environment for the current user's configuration ID.
/// </summary>
/// <returns>
/// Returns the empty Guid when the environment is not set or the request fails.
/// Otherwise, the configuration ID.
/// </returns>
public async Task<Guid> EnterpriseEnvConfigId()
{
var result = await this.http.GetAsync("/system/enterprise/config/id");
if (!result.IsSuccessStatusCode)
{
this.logger!.LogError($"Failed to query the enterprise configuration ID: '{result.StatusCode}'");
return Guid.Empty;
}
Guid.TryParse(await result.Content.ReadAsStringAsync(), out var configurationId);
return configurationId;
}
/// <summary>
/// Tries to read the enterprise environment for a configuration ID, which must be removed.
/// </summary>
/// <remarks>
/// Removing a configuration ID is necessary when the user moved to another department or
/// left the company, or when the configuration ID is no longer valid.
/// </remarks>
/// <returns>
/// Returns the empty Guid when the environment is not set or the request fails.
/// Otherwise, the configuration ID.
/// </returns>
public async Task<Guid> EnterpriseEnvRemoveConfigId()
{
var result = await this.http.DeleteAsync("/system/enterprise/config/id");
if (!result.IsSuccessStatusCode)
{
this.logger!.LogError($"Failed to query the enterprise configuration ID for removal: '{result.StatusCode}'");
return Guid.Empty;
}
Guid.TryParse(await result.Content.ReadAsStringAsync(), out var configurationId);
return configurationId;
}
/// <summary>
/// Tries to read the enterprise environment for the current user's configuration server URL.
/// </summary>
/// <returns>
/// Returns null when the environment is not set or the request fails.
/// Otherwise, the configuration server URL.
/// </returns>
public async Task<string> EnterpriseEnvConfigServerUrl()
{
var result = await this.http.GetAsync("/system/enterprise/config/server");
if (!result.IsSuccessStatusCode)
{
this.logger!.LogError($"Failed to query the enterprise configuration server URL: '{result.StatusCode}'");
return string.Empty;
}
var serverUrl = await result.Content.ReadAsStringAsync();
return string.IsNullOrWhiteSpace(serverUrl) ? string.Empty : serverUrl;
}
}
@@ -3,13 +3,11 @@ using AIStudio.Settings.DataModel;
namespace AIStudio.Tools.Services;
public class TemporaryChatService(ILogger<TemporaryChatService> logger, SettingsManager settingsManager) : BackgroundService
public sealed class TemporaryChatService(ILogger<TemporaryChatService> logger, SettingsManager settingsManager) : BackgroundService
{
private static readonly TimeSpan CHECK_INTERVAL = TimeSpan.FromDays(1);
private static bool IS_INITIALIZED;
private readonly ILogger<TemporaryChatService> logger = logger;
#region Overrides of BackgroundService
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@@ -17,12 +15,12 @@ public class TemporaryChatService(ILogger<TemporaryChatService> logger, Settings
while (!stoppingToken.IsCancellationRequested && !IS_INITIALIZED)
await Task.Delay(TimeSpan.FromSeconds(3), stoppingToken);
this.logger.LogInformation("The temporary chat maintenance service was initialized.");
logger.LogInformation("The temporary chat maintenance service was initialized.");
await settingsManager.LoadSettings();
if(settingsManager.ConfigurationData.Workspace.StorageTemporaryMaintenancePolicy is WorkspaceStorageTemporaryMaintenancePolicy.NO_AUTOMATIC_MAINTENANCE)
{
this.logger.LogWarning("Automatic maintenance of temporary chat storage is disabled. Exiting maintenance service.");
logger.LogWarning("Automatic maintenance of temporary chat storage is disabled. Exiting maintenance service.");
return;
}
@@ -38,11 +36,11 @@ public class TemporaryChatService(ILogger<TemporaryChatService> logger, Settings
private Task StartMaintenance()
{
this.logger.LogInformation("Starting maintenance of temporary chat storage.");
logger.LogInformation("Starting maintenance of temporary chat storage.");
var temporaryDirectories = Path.Join(SettingsManager.DataDirectory, "tempChats");
if(!Directory.Exists(temporaryDirectories))
{
this.logger.LogWarning("Temporary chat storage directory does not exist. End maintenance.");
logger.LogWarning("Temporary chat storage directory does not exist. End maintenance.");
return Task.CompletedTask;
}
@@ -67,12 +65,12 @@ public class TemporaryChatService(ILogger<TemporaryChatService> logger, Settings
if(deleteChat)
{
this.logger.LogInformation($"Deleting temporary chat storage directory '{tempChatDirPath}' due to maintenance policy.");
logger.LogInformation($"Deleting temporary chat storage directory '{tempChatDirPath}' due to maintenance policy.");
Directory.Delete(tempChatDirPath, true);
}
}
this.logger.LogInformation("Finished maintenance of temporary chat storage.");
logger.LogInformation("Finished maintenance of temporary chat storage.");
return Task.CompletedTask;
}