Keep track of the config env id to show a mismatch

This commit is contained in:
Thorsten Sommer 2026-02-19 10:08:48 +01:00
parent 99e985cb3a
commit 0378c29da5
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
5 changed files with 84 additions and 25 deletions

View File

@ -137,25 +137,46 @@
break; break;
case true: case true:
<MudText Typo="Typo.body1"> @if (this.HasAnyLoadedEnterpriseConfigurationPlugin)
@T("AI Studio runs with an enterprise configuration and configuration servers. The configuration plugins are active.") {
</MudText> <MudText Typo="Typo.body1">
@T("AI Studio runs with an enterprise configuration and configuration servers. The configuration plugins are active.")
</MudText>
}
else
{
<MudText Typo="Typo.body1">
@T("AI Studio runs with an enterprise configuration and configuration servers. The configuration plugins are not yet available.")
</MudText>
}
<MudCollapse Expanded="@this.showEnterpriseConfigDetails"> <MudCollapse Expanded="@this.showEnterpriseConfigDetails">
@foreach (var env in EnterpriseEnvironmentService.CURRENT_ENVIRONMENTS.Where(e => e.IsActive)) @foreach (var env in EnterpriseEnvironmentService.CURRENT_ENVIRONMENTS.Where(e => e.IsActive))
{ {
var matchingPlugin = this.configPlugins.FirstOrDefault(p => p.Id == env.ConfigurationId); var matchingPlugin = this.FindManagedConfigurationPlugin(env.ConfigurationId);
if (matchingPlugin is null)
{
<MudPaper Outlined="true" Class="pa-3 mt-2 mb-2">
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
<MudIcon Icon="@Icons.Material.Filled.HourglassBottom" Size="Size.Small"/>
<MudText Typo="Typo.subtitle2">@T("Waiting for the configuration plugin...")</MudText>
</div>
<div style="display: flex; align-items: center; gap: 8px;">
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
<span>@T("Enterprise configuration ID:") @env.ConfigurationId</span>
<MudCopyClipboardButton TooltipMessage="@T("Copies the config ID to the clipboard")" StringContent=@env.ConfigurationId.ToString()/>
</div>
<div style="display: flex; align-items: center; gap: 8px; margin-top: 4px;">
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
<span>@T("Configuration server:") @env.ConfigurationServerUrl</span>
<MudCopyClipboardButton TooltipMessage="@T("Copies the server URL to the clipboard")" StringContent=@env.ConfigurationServerUrl/>
</div>
</MudPaper>
continue;
}
<MudPaper Outlined="true" Class="pa-3 mt-2 mb-2"> <MudPaper Outlined="true" Class="pa-3 mt-2 mb-2">
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;"> <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
@if (matchingPlugin is not null) <MudIcon Icon="@Icons.Material.Filled.Extension" Size="Size.Small"/>
{ <MudText Typo="Typo.subtitle2">@matchingPlugin.Name</MudText>
<MudIcon Icon="@Icons.Material.Filled.Extension" Size="Size.Small"/>
<MudText Typo="Typo.subtitle2">@matchingPlugin.Name</MudText>
}
else
{
<MudIcon Icon="@Icons.Material.Filled.Warning" Size="Size.Small" Color="Color.Warning"/>
<MudText Typo="Typo.subtitle2">@T("ID mismatch: the plugin ID differs from the enterprise configuration ID.")</MudText>
}
</div> </div>
<div style="display: flex; align-items: center; gap: 8px;"> <div style="display: flex; align-items: center; gap: 8px;">
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/> <MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
@ -167,12 +188,16 @@
<span>@T("Configuration server:") @env.ConfigurationServerUrl</span> <span>@T("Configuration server:") @env.ConfigurationServerUrl</span>
<MudCopyClipboardButton TooltipMessage="@T("Copies the server URL to the clipboard")" StringContent=@env.ConfigurationServerUrl/> <MudCopyClipboardButton TooltipMessage="@T("Copies the server URL to the clipboard")" StringContent=@env.ConfigurationServerUrl/>
</div> </div>
@if (matchingPlugin is not null) <div style="display: flex; align-items: center; gap: 8px; margin-top: 4px;">
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
<span>@T("Configuration plugin ID:") @matchingPlugin.Id</span>
<MudCopyClipboardButton TooltipMessage="@T("Copies the configuration plugin ID to the clipboard")" StringContent=@matchingPlugin.Id.ToString()/>
</div>
@if (this.IsManagedConfigurationIdMismatch(matchingPlugin, env.ConfigurationId))
{ {
<div style="display: flex; align-items: center; gap: 8px; margin-top: 4px;"> <div style="display: flex; align-items: center; gap: 8px; margin-top: 4px;">
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/> <MudIcon Icon="@Icons.Material.Filled.Warning" Size="Size.Small" Color="Color.Warning"/>
<span>@T("Configuration plugin ID:") @matchingPlugin.Id</span> <MudText Typo="Typo.subtitle2">@T("ID mismatch: the plugin ID differs from the enterprise configuration ID.")</MudText>
<MudCopyClipboardButton TooltipMessage="@T("Copies the configuration plugin ID to the clipboard")" StringContent=@matchingPlugin.Id.ToString()/>
</div> </div>
} }
</MudPaper> </MudPaper>

View File

@ -69,13 +69,20 @@ public partial class Information : MSGComponentBase
private bool showDatabaseDetails; private bool showDatabaseDetails;
private List<IPluginMetadata> configPlugins = PluginFactory.AvailablePlugins.Where(x => x.Type is PluginType.CONFIGURATION).ToList(); private List<IAvailablePlugin> configPlugins = PluginFactory.AvailablePlugins
.Where(x => x.Type is PluginType.CONFIGURATION)
.OfType<IAvailablePlugin>()
.ToList();
private sealed record DatabaseDisplayInfo(string Label, string Value); private sealed record DatabaseDisplayInfo(string Label, string Value);
private readonly List<DatabaseDisplayInfo> databaseDisplayInfo = new(); private readonly List<DatabaseDisplayInfo> databaseDisplayInfo = new();
private static bool HasAnyActiveEnvironment => EnterpriseEnvironmentService.CURRENT_ENVIRONMENTS.Any(e => e.IsActive); private static bool HasAnyActiveEnvironment => EnterpriseEnvironmentService.CURRENT_ENVIRONMENTS.Any(e => e.IsActive);
private bool HasAnyLoadedEnterpriseConfigurationPlugin => EnterpriseEnvironmentService.CURRENT_ENVIRONMENTS
.Where(e => e.IsActive)
.Any(env => this.FindManagedConfigurationPlugin(env.ConfigurationId) is not null);
/// <summary> /// <summary>
/// Determines whether the enterprise configuration has details that can be shown/hidden. /// Determines whether the enterprise configuration has details that can be shown/hidden.
@ -130,7 +137,10 @@ public partial class Information : MSGComponentBase
switch (triggeredEvent) switch (triggeredEvent)
{ {
case Event.PLUGINS_RELOADED: case Event.PLUGINS_RELOADED:
this.configPlugins = PluginFactory.AvailablePlugins.Where(x => x.Type is PluginType.CONFIGURATION).ToList(); this.configPlugins = PluginFactory.AvailablePlugins
.Where(x => x.Type is PluginType.CONFIGURATION)
.OfType<IAvailablePlugin>()
.ToList();
await this.InvokeAsync(this.StateHasChanged); await this.InvokeAsync(this.StateHasChanged);
break; break;
} }
@ -196,6 +206,18 @@ public partial class Information : MSGComponentBase
this.showDatabaseDetails = !this.showDatabaseDetails; this.showDatabaseDetails = !this.showDatabaseDetails;
} }
private IAvailablePlugin? FindManagedConfigurationPlugin(Guid configurationId)
{
return this.configPlugins.FirstOrDefault(plugin => plugin.ManagedConfigurationId == configurationId)
// Backward compatibility for already downloaded plugins without ManagedConfigurationId.
?? this.configPlugins.FirstOrDefault(plugin => plugin.ManagedConfigurationId is null && plugin.Id == configurationId);
}
private bool IsManagedConfigurationIdMismatch(IAvailablePlugin plugin, Guid configurationId)
{
return plugin.ManagedConfigurationId == configurationId && plugin.Id != configurationId;
}
private async Task CopyStartupLogPath() private async Task CopyStartupLogPath()
{ {
await this.RustService.CopyText2Clipboard(this.Snackbar, this.logPaths.LogStartupPath); await this.RustService.CopyText2Clipboard(this.Snackbar, this.logPaths.LogStartupPath);

View File

@ -3,5 +3,8 @@ namespace AIStudio.Tools.PluginSystem;
public interface IAvailablePlugin : IPluginMetadata public interface IAvailablePlugin : IPluginMetadata
{ {
public string LocalPath { get; } public string LocalPath { get; }
public bool IsManagedByConfigServer { get; } public bool IsManagedByConfigServer { get; }
public Guid? ManagedConfigurationId { get; }
} }

View File

@ -109,6 +109,7 @@ public static partial class PluginFactory
pluginPath.StartsWith(CONFIGURATION_PLUGINS_ROOT, StringComparison.OrdinalIgnoreCase); pluginPath.StartsWith(CONFIGURATION_PLUGINS_ROOT, StringComparison.OrdinalIgnoreCase);
var isManagedByConfigServer = false; var isManagedByConfigServer = false;
Guid? managedConfigurationId = null;
if (plugin is PluginConfiguration configPlugin) if (plugin is PluginConfiguration configPlugin)
{ {
if (configPlugin.DeployedUsingConfigServer.HasValue) if (configPlugin.DeployedUsingConfigServer.HasValue)
@ -123,14 +124,20 @@ public static partial class PluginFactory
// For configuration plugins, validate that the plugin ID matches the enterprise config ID // For configuration plugins, validate that the plugin ID matches the enterprise config ID
// (the directory name under which the plugin was downloaded): // (the directory name under which the plugin was downloaded):
if (isConfigurationPluginInConfigDirectory) if (isConfigurationPluginInConfigDirectory && isManagedByConfigServer)
{ {
var directoryName = Path.GetFileName(pluginPath); var directoryName = Path.GetFileName(pluginPath);
if (Guid.TryParse(directoryName, out var enterpriseConfigId) && enterpriseConfigId != plugin.Id) if (Guid.TryParse(directoryName, out var enterpriseConfigId))
LOG.LogWarning($"The configuration plugin's ID ('{plugin.Id}') does not match the enterprise configuration ID ('{enterpriseConfigId}'). These IDs should be identical. Please update the plugin's ID field to match the enterprise configuration ID."); {
managedConfigurationId = enterpriseConfigId;
if (enterpriseConfigId != plugin.Id)
LOG.LogWarning($"The configuration plugin's ID ('{plugin.Id}') does not match the enterprise configuration ID ('{enterpriseConfigId}'). These IDs should be identical. Please update the plugin's ID field to match the enterprise configuration ID.");
}
else
LOG.LogWarning($"Could not determine the managed configuration ID for configuration plugin '{plugin.Id}'. The plugin directory '{pluginPath}' does not end with a valid GUID.");
} }
AVAILABLE_PLUGINS.Add(new PluginMetadata(plugin, pluginPath, isManagedByConfigServer)); AVAILABLE_PLUGINS.Add(new PluginMetadata(plugin, pluginPath, isManagedByConfigServer, managedConfigurationId));
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -1,6 +1,6 @@
namespace AIStudio.Tools.PluginSystem; namespace AIStudio.Tools.PluginSystem;
public sealed class PluginMetadata(PluginBase plugin, string localPath, bool isManagedByConfigServer = false) : IAvailablePlugin public sealed class PluginMetadata(PluginBase plugin, string localPath, bool isManagedByConfigServer = false, Guid? managedConfigurationId = null) : IAvailablePlugin
{ {
#region Implementation of IPluginMetadata #region Implementation of IPluginMetadata
@ -53,6 +53,8 @@ public sealed class PluginMetadata(PluginBase plugin, string localPath, bool isM
public string LocalPath { get; } = localPath; public string LocalPath { get; } = localPath;
public bool IsManagedByConfigServer { get; } = isManagedByConfigServer; public bool IsManagedByConfigServer { get; } = isManagedByConfigServer;
public Guid? ManagedConfigurationId { get; } = managedConfigurationId;
#endregion #endregion
} }