Files
ScadaBridge/src/ScadaLink.ManagementService/ManagementActor.cs
T
Joseph Doherty eb8ead58d2 feat: wire SQLite replication between site nodes and fix ConfigurationDatabase tests
Add SiteReplicationActor (runs on every site node) to replicate deployed
configs and store-and-forward buffer operations to the standby peer via
cluster member discovery and fire-and-forget Tell. Wire ReplicationService
handler and pass replication actor to DeploymentManagerActor singleton.

Fix 5 pre-existing ConfigurationDatabase test failures: RowVersion NOT NULL
on SQLite, stale migration name assertion, and seed data count mismatch.
2026-03-18 08:28:02 -04:00

1187 lines
54 KiB
C#

using System.Security.Cryptography;
using Akka.Actor;
using Newtonsoft.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ScadaLink.Commons.Entities.ExternalSystems;
using ScadaLink.Commons.Entities.InboundApi;
using ScadaLink.Commons.Entities.Instances;
using ScadaLink.Commons.Entities.Scripts;
using ScadaLink.Commons.Entities.Templates;
using ScadaLink.Commons.Entities.Notifications;
using ScadaLink.Commons.Entities.Security;
using ScadaLink.Commons.Entities.Sites;
using ScadaLink.Commons.Interfaces.Repositories;
using ScadaLink.Commons.Messages.DebugView;
using ScadaLink.Commons.Messages.Management;
using ScadaLink.Commons.Messages.RemoteQuery;
using ScadaLink.DeploymentManager;
using ScadaLink.HealthMonitoring;
using ScadaLink.Communication;
using ScadaLink.TemplateEngine;
using ScadaLink.TemplateEngine.Services;
namespace ScadaLink.ManagementService;
/// <summary>
/// Central actor that handles all management commands from the CLI (via ClusterClient).
/// Receives ManagementEnvelope messages, authorizes based on roles, then delegates to
/// the appropriate service or repository using scoped DI.
/// </summary>
public class ManagementActor : ReceiveActor
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ManagementActor> _logger;
public ManagementActor(IServiceProvider serviceProvider, ILogger<ManagementActor> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
Receive<ManagementEnvelope>(HandleEnvelope);
}
private void HandleEnvelope(ManagementEnvelope envelope)
{
var sender = Sender;
var correlationId = envelope.CorrelationId;
var user = envelope.User;
// Check authorization
var requiredRole = GetRequiredRole(envelope.Command);
if (requiredRole != null && !user.Roles.Contains(requiredRole, StringComparer.OrdinalIgnoreCase))
{
sender.Tell(new ManagementUnauthorized(correlationId,
$"Role '{requiredRole}' required for {envelope.Command.GetType().Name}"));
return;
}
// Process command asynchronously with scoped DI
Task.Run(async () =>
{
using var scope = _serviceProvider.CreateScope();
try
{
var result = await DispatchCommand(scope.ServiceProvider, envelope.Command, user.Username);
var json = JsonConvert.SerializeObject(result, Formatting.None);
sender.Tell(new ManagementSuccess(correlationId, json));
}
catch (Exception ex)
{
_logger.LogError(ex, "Management command {Command} failed (CorrelationId={CorrelationId})",
envelope.Command.GetType().Name, correlationId);
sender.Tell(new ManagementError(correlationId, ex.Message, "COMMAND_FAILED"));
}
});
}
private static string? GetRequiredRole(object command) => command switch
{
// Admin operations
CreateSiteCommand or UpdateSiteCommand or DeleteSiteCommand
or ListRoleMappingsCommand or CreateRoleMappingCommand
or UpdateRoleMappingCommand or DeleteRoleMappingCommand
or ListApiKeysCommand or CreateApiKeyCommand or DeleteApiKeyCommand
or UpdateApiKeyCommand
or ListScopeRulesCommand or AddScopeRuleCommand or DeleteScopeRuleCommand => "Admin",
// Design operations
CreateAreaCommand or DeleteAreaCommand
or CreateTemplateCommand or UpdateTemplateCommand or DeleteTemplateCommand
or ValidateTemplateCommand
or CreateExternalSystemCommand or UpdateExternalSystemCommand
or DeleteExternalSystemCommand
or CreateExternalSystemMethodCommand or UpdateExternalSystemMethodCommand
or DeleteExternalSystemMethodCommand
or CreateNotificationListCommand or UpdateNotificationListCommand
or DeleteNotificationListCommand
or UpdateSmtpConfigCommand
or CreateDataConnectionCommand or UpdateDataConnectionCommand
or DeleteDataConnectionCommand
or AssignDataConnectionToSiteCommand
or UnassignDataConnectionFromSiteCommand
or AddTemplateAttributeCommand or UpdateTemplateAttributeCommand or DeleteTemplateAttributeCommand
or AddTemplateAlarmCommand or UpdateTemplateAlarmCommand or DeleteTemplateAlarmCommand
or AddTemplateScriptCommand or UpdateTemplateScriptCommand or DeleteTemplateScriptCommand
or AddTemplateCompositionCommand or DeleteTemplateCompositionCommand
or CreateSharedScriptCommand or UpdateSharedScriptCommand or DeleteSharedScriptCommand
or CreateDatabaseConnectionDefCommand or UpdateDatabaseConnectionDefCommand or DeleteDatabaseConnectionDefCommand
or CreateApiMethodCommand or UpdateApiMethodCommand or DeleteApiMethodCommand
or UpdateAreaCommand => "Design",
// Deployment operations
CreateInstanceCommand or MgmtDeployInstanceCommand or MgmtEnableInstanceCommand
or MgmtDisableInstanceCommand or MgmtDeleteInstanceCommand
or SetConnectionBindingsCommand
or MgmtDeployArtifactsCommand
or DebugSnapshotCommand => "Deployment",
// Read-only queries -- any authenticated user
_ => null
};
private async Task<object?> DispatchCommand(IServiceProvider sp, object command, string user)
{
return command switch
{
// Templates
ListTemplatesCommand => await HandleListTemplates(sp),
GetTemplateCommand cmd => await HandleGetTemplate(sp, cmd),
CreateTemplateCommand cmd => await HandleCreateTemplate(sp, cmd, user),
UpdateTemplateCommand cmd => await HandleUpdateTemplate(sp, cmd, user),
DeleteTemplateCommand cmd => await HandleDeleteTemplate(sp, cmd, user),
ValidateTemplateCommand cmd => await HandleValidateTemplate(sp, cmd),
// Template members
AddTemplateAttributeCommand cmd => await HandleAddAttribute(sp, cmd, user),
UpdateTemplateAttributeCommand cmd => await HandleUpdateAttribute(sp, cmd, user),
DeleteTemplateAttributeCommand cmd => await HandleDeleteAttribute(sp, cmd, user),
AddTemplateAlarmCommand cmd => await HandleAddAlarm(sp, cmd, user),
UpdateTemplateAlarmCommand cmd => await HandleUpdateAlarm(sp, cmd, user),
DeleteTemplateAlarmCommand cmd => await HandleDeleteAlarm(sp, cmd, user),
AddTemplateScriptCommand cmd => await HandleAddScript(sp, cmd, user),
UpdateTemplateScriptCommand cmd => await HandleUpdateScript(sp, cmd, user),
DeleteTemplateScriptCommand cmd => await HandleDeleteScript(sp, cmd, user),
AddTemplateCompositionCommand cmd => await HandleAddComposition(sp, cmd, user),
DeleteTemplateCompositionCommand cmd => await HandleDeleteComposition(sp, cmd, user),
// Instances
ListInstancesCommand cmd => await HandleListInstances(sp, cmd),
GetInstanceCommand cmd => await HandleGetInstance(sp, cmd),
CreateInstanceCommand cmd => await HandleCreateInstance(sp, cmd, user),
MgmtDeployInstanceCommand cmd => await HandleDeployInstance(sp, cmd, user),
MgmtEnableInstanceCommand cmd => await HandleEnableInstance(sp, cmd, user),
MgmtDisableInstanceCommand cmd => await HandleDisableInstance(sp, cmd, user),
MgmtDeleteInstanceCommand cmd => await HandleDeleteInstance(sp, cmd, user),
SetConnectionBindingsCommand cmd => await HandleSetConnectionBindings(sp, cmd, user),
// Sites
ListSitesCommand => await HandleListSites(sp),
GetSiteCommand cmd => await HandleGetSite(sp, cmd),
CreateSiteCommand cmd => await HandleCreateSite(sp, cmd),
UpdateSiteCommand cmd => await HandleUpdateSite(sp, cmd),
DeleteSiteCommand cmd => await HandleDeleteSite(sp, cmd),
ListAreasCommand cmd => await HandleListAreas(sp, cmd),
CreateAreaCommand cmd => await HandleCreateArea(sp, cmd),
DeleteAreaCommand cmd => await HandleDeleteArea(sp, cmd),
UpdateAreaCommand cmd => await HandleUpdateArea(sp, cmd),
// Data Connections
ListDataConnectionsCommand => await HandleListDataConnections(sp),
GetDataConnectionCommand cmd => await HandleGetDataConnection(sp, cmd),
CreateDataConnectionCommand cmd => await HandleCreateDataConnection(sp, cmd),
UpdateDataConnectionCommand cmd => await HandleUpdateDataConnection(sp, cmd),
DeleteDataConnectionCommand cmd => await HandleDeleteDataConnection(sp, cmd),
AssignDataConnectionToSiteCommand cmd => await HandleAssignDataConnectionToSite(sp, cmd),
UnassignDataConnectionFromSiteCommand cmd => await HandleUnassignDataConnectionFromSite(sp, cmd),
// External Systems
ListExternalSystemsCommand => await HandleListExternalSystems(sp),
GetExternalSystemCommand cmd => await HandleGetExternalSystem(sp, cmd),
CreateExternalSystemCommand cmd => await HandleCreateExternalSystem(sp, cmd),
UpdateExternalSystemCommand cmd => await HandleUpdateExternalSystem(sp, cmd),
DeleteExternalSystemCommand cmd => await HandleDeleteExternalSystem(sp, cmd),
ListExternalSystemMethodsCommand cmd => await HandleListExternalSystemMethods(sp, cmd),
GetExternalSystemMethodCommand cmd => await HandleGetExternalSystemMethod(sp, cmd),
CreateExternalSystemMethodCommand cmd => await HandleCreateExternalSystemMethod(sp, cmd),
UpdateExternalSystemMethodCommand cmd => await HandleUpdateExternalSystemMethod(sp, cmd),
DeleteExternalSystemMethodCommand cmd => await HandleDeleteExternalSystemMethod(sp, cmd),
// Notification Lists
ListNotificationListsCommand => await HandleListNotificationLists(sp),
GetNotificationListCommand cmd => await HandleGetNotificationList(sp, cmd),
CreateNotificationListCommand cmd => await HandleCreateNotificationList(sp, cmd),
UpdateNotificationListCommand cmd => await HandleUpdateNotificationList(sp, cmd),
DeleteNotificationListCommand cmd => await HandleDeleteNotificationList(sp, cmd),
ListSmtpConfigsCommand => await HandleListSmtpConfigs(sp),
UpdateSmtpConfigCommand cmd => await HandleUpdateSmtpConfig(sp, cmd),
// Shared Scripts
ListSharedScriptsCommand => await HandleListSharedScripts(sp),
GetSharedScriptCommand cmd => await HandleGetSharedScript(sp, cmd),
CreateSharedScriptCommand cmd => await HandleCreateSharedScript(sp, cmd, user),
UpdateSharedScriptCommand cmd => await HandleUpdateSharedScript(sp, cmd, user),
DeleteSharedScriptCommand cmd => await HandleDeleteSharedScript(sp, cmd, user),
// Database Connections (External System)
ListDatabaseConnectionsCommand => await HandleListDatabaseConnections(sp),
GetDatabaseConnectionCommand cmd => await HandleGetDatabaseConnection(sp, cmd),
CreateDatabaseConnectionDefCommand cmd => await HandleCreateDatabaseConnection(sp, cmd),
UpdateDatabaseConnectionDefCommand cmd => await HandleUpdateDatabaseConnection(sp, cmd),
DeleteDatabaseConnectionDefCommand cmd => await HandleDeleteDatabaseConnection(sp, cmd),
// Inbound API Methods
ListApiMethodsCommand => await HandleListApiMethods(sp),
GetApiMethodCommand cmd => await HandleGetApiMethod(sp, cmd),
CreateApiMethodCommand cmd => await HandleCreateApiMethod(sp, cmd),
UpdateApiMethodCommand cmd => await HandleUpdateApiMethod(sp, cmd),
DeleteApiMethodCommand cmd => await HandleDeleteApiMethod(sp, cmd),
// Security
ListRoleMappingsCommand => await HandleListRoleMappings(sp),
CreateRoleMappingCommand cmd => await HandleCreateRoleMapping(sp, cmd),
UpdateRoleMappingCommand cmd => await HandleUpdateRoleMapping(sp, cmd),
DeleteRoleMappingCommand cmd => await HandleDeleteRoleMapping(sp, cmd),
ListApiKeysCommand => await HandleListApiKeys(sp),
CreateApiKeyCommand cmd => await HandleCreateApiKey(sp, cmd),
DeleteApiKeyCommand cmd => await HandleDeleteApiKey(sp, cmd),
UpdateApiKeyCommand cmd => await HandleUpdateApiKey(sp, cmd),
ListScopeRulesCommand cmd => await HandleListScopeRules(sp, cmd),
AddScopeRuleCommand cmd => await HandleAddScopeRule(sp, cmd),
DeleteScopeRuleCommand cmd => await HandleDeleteScopeRule(sp, cmd),
// Deployments
MgmtDeployArtifactsCommand cmd => await HandleDeployArtifacts(sp, cmd, user),
QueryDeploymentsCommand cmd => await HandleQueryDeployments(sp, cmd),
// Audit Log
QueryAuditLogCommand cmd => await HandleQueryAuditLog(sp, cmd),
// Health
GetHealthSummaryCommand => HandleGetHealthSummary(sp),
GetSiteHealthCommand cmd => HandleGetSiteHealth(sp, cmd),
// Remote Queries
QueryEventLogsCommand cmd => await HandleQueryEventLogs(sp, cmd),
QueryParkedMessagesCommand cmd => await HandleQueryParkedMessages(sp, cmd),
DebugSnapshotCommand cmd => await HandleDebugSnapshot(sp, cmd),
_ => throw new NotSupportedException($"Unknown command type: {command.GetType().Name}")
};
}
// ========================================================================
// Template handlers
// ========================================================================
private static async Task<object?> HandleListTemplates(IServiceProvider sp)
{
var repo = sp.GetRequiredService<ITemplateEngineRepository>();
return await repo.GetAllTemplatesAsync();
}
private static async Task<object?> HandleGetTemplate(IServiceProvider sp, GetTemplateCommand cmd)
{
var repo = sp.GetRequiredService<ITemplateEngineRepository>();
return await repo.GetTemplateWithChildrenAsync(cmd.TemplateId);
}
private static async Task<object?> HandleCreateTemplate(IServiceProvider sp, CreateTemplateCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var result = await svc.CreateTemplateAsync(cmd.Name, cmd.Description, cmd.ParentTemplateId, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleUpdateTemplate(IServiceProvider sp, UpdateTemplateCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var result = await svc.UpdateTemplateAsync(cmd.TemplateId, cmd.Name, cmd.Description, cmd.ParentTemplateId, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleDeleteTemplate(IServiceProvider sp, DeleteTemplateCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var result = await svc.DeleteTemplateAsync(cmd.TemplateId, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleValidateTemplate(IServiceProvider sp, ValidateTemplateCommand cmd)
{
var svc = sp.GetRequiredService<TemplateService>();
return await svc.DetectCollisionsAsync(cmd.TemplateId);
}
// ========================================================================
// Instance handlers
// ========================================================================
private static async Task<object?> HandleListInstances(IServiceProvider sp, ListInstancesCommand cmd)
{
var repo = sp.GetRequiredService<ICentralUiRepository>();
return await repo.GetInstancesFilteredAsync(cmd.SiteId, cmd.TemplateId, cmd.SearchTerm);
}
private static async Task<object?> HandleGetInstance(IServiceProvider sp, GetInstanceCommand cmd)
{
var repo = sp.GetRequiredService<ITemplateEngineRepository>();
return await repo.GetInstanceByIdAsync(cmd.InstanceId);
}
private static async Task<object?> HandleCreateInstance(IServiceProvider sp, CreateInstanceCommand cmd, string user)
{
var svc = sp.GetRequiredService<InstanceService>();
var result = await svc.CreateInstanceAsync(cmd.UniqueName, cmd.TemplateId, cmd.SiteId, cmd.AreaId, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleDeployInstance(IServiceProvider sp, MgmtDeployInstanceCommand cmd, string user)
{
var svc = sp.GetRequiredService<DeploymentService>();
var result = await svc.DeployInstanceAsync(cmd.InstanceId, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleEnableInstance(IServiceProvider sp, MgmtEnableInstanceCommand cmd, string user)
{
var svc = sp.GetRequiredService<DeploymentService>();
var result = await svc.EnableInstanceAsync(cmd.InstanceId, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleDisableInstance(IServiceProvider sp, MgmtDisableInstanceCommand cmd, string user)
{
var svc = sp.GetRequiredService<DeploymentService>();
var result = await svc.DisableInstanceAsync(cmd.InstanceId, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleDeleteInstance(IServiceProvider sp, MgmtDeleteInstanceCommand cmd, string user)
{
var svc = sp.GetRequiredService<DeploymentService>();
var result = await svc.DeleteInstanceAsync(cmd.InstanceId, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleSetConnectionBindings(IServiceProvider sp, SetConnectionBindingsCommand cmd, string user)
{
var svc = sp.GetRequiredService<InstanceService>();
var result = await svc.SetConnectionBindingsAsync(cmd.InstanceId, cmd.Bindings, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
// ========================================================================
// Site handlers
// ========================================================================
private static async Task<object?> HandleListSites(IServiceProvider sp)
{
var repo = sp.GetRequiredService<ISiteRepository>();
return await repo.GetAllSitesAsync();
}
private static async Task<object?> HandleGetSite(IServiceProvider sp, GetSiteCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
return await repo.GetSiteByIdAsync(cmd.SiteId);
}
private static async Task<object?> HandleCreateSite(IServiceProvider sp, CreateSiteCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
var site = new Site(cmd.Name, cmd.SiteIdentifier)
{
Description = cmd.Description,
NodeAAddress = cmd.NodeAAddress,
NodeBAddress = cmd.NodeBAddress
};
await repo.AddSiteAsync(site);
await repo.SaveChangesAsync();
var commService = sp.GetService<CommunicationService>();
commService?.RefreshSiteAddresses();
return site;
}
private static async Task<object?> HandleUpdateSite(IServiceProvider sp, UpdateSiteCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
var site = await repo.GetSiteByIdAsync(cmd.SiteId)
?? throw new InvalidOperationException($"Site with ID {cmd.SiteId} not found.");
site.Name = cmd.Name;
site.Description = cmd.Description;
site.NodeAAddress = cmd.NodeAAddress;
site.NodeBAddress = cmd.NodeBAddress;
await repo.UpdateSiteAsync(site);
await repo.SaveChangesAsync();
var commService = sp.GetService<CommunicationService>();
commService?.RefreshSiteAddresses();
return site;
}
private static async Task<object?> HandleDeleteSite(IServiceProvider sp, DeleteSiteCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
// Check for instances referencing this site
var instances = await repo.GetInstancesBySiteIdAsync(cmd.SiteId);
if (instances.Count > 0)
throw new InvalidOperationException(
$"Cannot delete site {cmd.SiteId}: it has {instances.Count} instance(s).");
await repo.DeleteSiteAsync(cmd.SiteId);
await repo.SaveChangesAsync();
var commService = sp.GetService<CommunicationService>();
commService?.RefreshSiteAddresses();
return true;
}
private static async Task<object?> HandleListAreas(IServiceProvider sp, ListAreasCommand cmd)
{
var repo = sp.GetRequiredService<ICentralUiRepository>();
return await repo.GetAreaTreeBySiteIdAsync(cmd.SiteId);
}
private static async Task<object?> HandleCreateArea(IServiceProvider sp, CreateAreaCommand cmd)
{
var repo = sp.GetRequiredService<ITemplateEngineRepository>();
var area = new Area(cmd.Name)
{
SiteId = cmd.SiteId,
ParentAreaId = cmd.ParentAreaId
};
await repo.AddAreaAsync(area);
await repo.SaveChangesAsync();
return area;
}
private static async Task<object?> HandleDeleteArea(IServiceProvider sp, DeleteAreaCommand cmd)
{
var repo = sp.GetRequiredService<ITemplateEngineRepository>();
await repo.DeleteAreaAsync(cmd.AreaId);
await repo.SaveChangesAsync();
return true;
}
// ========================================================================
// Data Connection handlers
// ========================================================================
private static async Task<object?> HandleListDataConnections(IServiceProvider sp)
{
var repo = sp.GetRequiredService<ISiteRepository>();
return await repo.GetAllDataConnectionsAsync();
}
private static async Task<object?> HandleGetDataConnection(IServiceProvider sp, GetDataConnectionCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
return await repo.GetDataConnectionByIdAsync(cmd.DataConnectionId);
}
private static async Task<object?> HandleCreateDataConnection(IServiceProvider sp, CreateDataConnectionCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
var conn = new DataConnection(cmd.Name, cmd.Protocol) { Configuration = cmd.Configuration };
await repo.AddDataConnectionAsync(conn);
await repo.SaveChangesAsync();
return conn;
}
private static async Task<object?> HandleUpdateDataConnection(IServiceProvider sp, UpdateDataConnectionCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
var conn = await repo.GetDataConnectionByIdAsync(cmd.DataConnectionId)
?? throw new InvalidOperationException($"DataConnection with ID {cmd.DataConnectionId} not found.");
conn.Name = cmd.Name;
conn.Protocol = cmd.Protocol;
conn.Configuration = cmd.Configuration;
await repo.UpdateDataConnectionAsync(conn);
await repo.SaveChangesAsync();
return conn;
}
private static async Task<object?> HandleDeleteDataConnection(IServiceProvider sp, DeleteDataConnectionCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
await repo.DeleteDataConnectionAsync(cmd.DataConnectionId);
await repo.SaveChangesAsync();
return true;
}
private static async Task<object?> HandleAssignDataConnectionToSite(IServiceProvider sp, AssignDataConnectionToSiteCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
var assignment = new SiteDataConnectionAssignment
{
SiteId = cmd.SiteId,
DataConnectionId = cmd.DataConnectionId
};
await repo.AddSiteDataConnectionAssignmentAsync(assignment);
await repo.SaveChangesAsync();
return assignment;
}
private static async Task<object?> HandleUnassignDataConnectionFromSite(IServiceProvider sp, UnassignDataConnectionFromSiteCommand cmd)
{
var repo = sp.GetRequiredService<ISiteRepository>();
await repo.DeleteSiteDataConnectionAssignmentAsync(cmd.AssignmentId);
await repo.SaveChangesAsync();
return true;
}
// ========================================================================
// External System handlers
// ========================================================================
private static async Task<object?> HandleListExternalSystems(IServiceProvider sp)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
return await repo.GetAllExternalSystemsAsync();
}
private static async Task<object?> HandleGetExternalSystem(IServiceProvider sp, GetExternalSystemCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
return await repo.GetExternalSystemByIdAsync(cmd.ExternalSystemId);
}
private static async Task<object?> HandleCreateExternalSystem(IServiceProvider sp, CreateExternalSystemCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
var def = new ExternalSystemDefinition(cmd.Name, cmd.EndpointUrl, cmd.AuthType)
{
AuthConfiguration = cmd.AuthConfiguration
};
await repo.AddExternalSystemAsync(def);
await repo.SaveChangesAsync();
return def;
}
private static async Task<object?> HandleUpdateExternalSystem(IServiceProvider sp, UpdateExternalSystemCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
var def = await repo.GetExternalSystemByIdAsync(cmd.ExternalSystemId)
?? throw new InvalidOperationException($"ExternalSystem with ID {cmd.ExternalSystemId} not found.");
def.Name = cmd.Name;
def.EndpointUrl = cmd.EndpointUrl;
def.AuthType = cmd.AuthType;
def.AuthConfiguration = cmd.AuthConfiguration;
await repo.UpdateExternalSystemAsync(def);
await repo.SaveChangesAsync();
return def;
}
private static async Task<object?> HandleDeleteExternalSystem(IServiceProvider sp, DeleteExternalSystemCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
await repo.DeleteExternalSystemAsync(cmd.ExternalSystemId);
await repo.SaveChangesAsync();
return true;
}
private static async Task<object?> HandleListExternalSystemMethods(IServiceProvider sp, ListExternalSystemMethodsCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
return await repo.GetMethodsByExternalSystemIdAsync(cmd.ExternalSystemId);
}
private static async Task<object?> HandleGetExternalSystemMethod(IServiceProvider sp, GetExternalSystemMethodCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
return await repo.GetExternalSystemMethodByIdAsync(cmd.MethodId);
}
private static async Task<object?> HandleCreateExternalSystemMethod(IServiceProvider sp, CreateExternalSystemMethodCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
var method = new ExternalSystemMethod(cmd.Name, cmd.HttpMethod, cmd.Path)
{
ExternalSystemDefinitionId = cmd.ExternalSystemId,
ParameterDefinitions = cmd.ParameterDefinitions,
ReturnDefinition = cmd.ReturnDefinition
};
await repo.AddExternalSystemMethodAsync(method);
await repo.SaveChangesAsync();
return method;
}
private static async Task<object?> HandleUpdateExternalSystemMethod(IServiceProvider sp, UpdateExternalSystemMethodCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
var method = await repo.GetExternalSystemMethodByIdAsync(cmd.MethodId)
?? throw new InvalidOperationException($"ExternalSystemMethod with ID {cmd.MethodId} not found.");
if (cmd.Name != null) method.Name = cmd.Name;
if (cmd.HttpMethod != null) method.HttpMethod = cmd.HttpMethod;
if (cmd.Path != null) method.Path = cmd.Path;
if (cmd.ParameterDefinitions != null) method.ParameterDefinitions = cmd.ParameterDefinitions;
if (cmd.ReturnDefinition != null) method.ReturnDefinition = cmd.ReturnDefinition;
await repo.UpdateExternalSystemMethodAsync(method);
await repo.SaveChangesAsync();
return method;
}
private static async Task<object?> HandleDeleteExternalSystemMethod(IServiceProvider sp, DeleteExternalSystemMethodCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
await repo.DeleteExternalSystemMethodAsync(cmd.MethodId);
await repo.SaveChangesAsync();
return true;
}
// ========================================================================
// Notification handlers
// ========================================================================
private static async Task<object?> HandleListNotificationLists(IServiceProvider sp)
{
var repo = sp.GetRequiredService<INotificationRepository>();
return await repo.GetAllNotificationListsAsync();
}
private static async Task<object?> HandleGetNotificationList(IServiceProvider sp, GetNotificationListCommand cmd)
{
var repo = sp.GetRequiredService<INotificationRepository>();
return await repo.GetNotificationListByIdAsync(cmd.NotificationListId);
}
private static async Task<object?> HandleCreateNotificationList(IServiceProvider sp, CreateNotificationListCommand cmd)
{
var repo = sp.GetRequiredService<INotificationRepository>();
var list = new NotificationList(cmd.Name);
foreach (var email in cmd.RecipientEmails)
{
list.Recipients.Add(new NotificationRecipient(email, email));
}
await repo.AddNotificationListAsync(list);
await repo.SaveChangesAsync();
return list;
}
private static async Task<object?> HandleUpdateNotificationList(IServiceProvider sp, UpdateNotificationListCommand cmd)
{
var repo = sp.GetRequiredService<INotificationRepository>();
var list = await repo.GetNotificationListByIdAsync(cmd.NotificationListId)
?? throw new InvalidOperationException($"NotificationList with ID {cmd.NotificationListId} not found.");
list.Name = cmd.Name;
// Remove existing recipients and re-add
var existingRecipients = await repo.GetRecipientsByListIdAsync(cmd.NotificationListId);
foreach (var r in existingRecipients)
{
await repo.DeleteRecipientAsync(r.Id);
}
foreach (var email in cmd.RecipientEmails)
{
await repo.AddRecipientAsync(new NotificationRecipient(email, email)
{
NotificationListId = cmd.NotificationListId
});
}
await repo.UpdateNotificationListAsync(list);
await repo.SaveChangesAsync();
return list;
}
private static async Task<object?> HandleDeleteNotificationList(IServiceProvider sp, DeleteNotificationListCommand cmd)
{
var repo = sp.GetRequiredService<INotificationRepository>();
await repo.DeleteNotificationListAsync(cmd.NotificationListId);
await repo.SaveChangesAsync();
return true;
}
private static async Task<object?> HandleListSmtpConfigs(IServiceProvider sp)
{
var repo = sp.GetRequiredService<INotificationRepository>();
return await repo.GetAllSmtpConfigurationsAsync();
}
private static async Task<object?> HandleUpdateSmtpConfig(IServiceProvider sp, UpdateSmtpConfigCommand cmd)
{
var repo = sp.GetRequiredService<INotificationRepository>();
var config = await repo.GetSmtpConfigurationByIdAsync(cmd.SmtpConfigId)
?? throw new InvalidOperationException($"SmtpConfiguration with ID {cmd.SmtpConfigId} not found.");
config.Host = cmd.Server;
config.Port = cmd.Port;
config.AuthType = cmd.AuthMode;
config.FromAddress = cmd.FromAddress;
await repo.UpdateSmtpConfigurationAsync(config);
await repo.SaveChangesAsync();
return config;
}
// ========================================================================
// Security handlers
// ========================================================================
private static async Task<object?> HandleListRoleMappings(IServiceProvider sp)
{
var repo = sp.GetRequiredService<ISecurityRepository>();
return await repo.GetAllMappingsAsync();
}
private static async Task<object?> HandleCreateRoleMapping(IServiceProvider sp, CreateRoleMappingCommand cmd)
{
var repo = sp.GetRequiredService<ISecurityRepository>();
var mapping = new LdapGroupMapping(cmd.LdapGroupName, cmd.Role);
await repo.AddMappingAsync(mapping);
await repo.SaveChangesAsync();
return mapping;
}
private static async Task<object?> HandleUpdateRoleMapping(IServiceProvider sp, UpdateRoleMappingCommand cmd)
{
var repo = sp.GetRequiredService<ISecurityRepository>();
var mapping = await repo.GetMappingByIdAsync(cmd.MappingId)
?? throw new InvalidOperationException($"RoleMapping with ID {cmd.MappingId} not found.");
mapping.LdapGroupName = cmd.LdapGroupName;
mapping.Role = cmd.Role;
await repo.UpdateMappingAsync(mapping);
await repo.SaveChangesAsync();
return mapping;
}
private static async Task<object?> HandleDeleteRoleMapping(IServiceProvider sp, DeleteRoleMappingCommand cmd)
{
var repo = sp.GetRequiredService<ISecurityRepository>();
await repo.DeleteMappingAsync(cmd.MappingId);
await repo.SaveChangesAsync();
return true;
}
private static async Task<object?> HandleListApiKeys(IServiceProvider sp)
{
var repo = sp.GetRequiredService<IInboundApiRepository>();
return await repo.GetAllApiKeysAsync();
}
private static async Task<object?> HandleCreateApiKey(IServiceProvider sp, CreateApiKeyCommand cmd)
{
var repo = sp.GetRequiredService<IInboundApiRepository>();
var keyValue = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
var apiKey = new ApiKey(cmd.Name, keyValue) { IsEnabled = true };
await repo.AddApiKeyAsync(apiKey);
await repo.SaveChangesAsync();
return apiKey;
}
private static async Task<object?> HandleDeleteApiKey(IServiceProvider sp, DeleteApiKeyCommand cmd)
{
var repo = sp.GetRequiredService<IInboundApiRepository>();
await repo.DeleteApiKeyAsync(cmd.ApiKeyId);
await repo.SaveChangesAsync();
return true;
}
// ========================================================================
// Deployment handlers
// ========================================================================
private static async Task<object?> HandleDeployArtifacts(IServiceProvider sp, MgmtDeployArtifactsCommand cmd, string user)
{
var svc = sp.GetRequiredService<ArtifactDeploymentService>();
var command = await svc.BuildDeployArtifactsCommandAsync();
var result = await svc.DeployToAllSitesAsync(command, user);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleQueryDeployments(IServiceProvider sp, QueryDeploymentsCommand cmd)
{
var repo = sp.GetRequiredService<IDeploymentManagerRepository>();
if (cmd.InstanceId.HasValue)
return await repo.GetDeploymentsByInstanceIdAsync(cmd.InstanceId.Value);
return await repo.GetAllDeploymentRecordsAsync();
}
// ========================================================================
// Audit Log handler
// ========================================================================
private static async Task<object?> HandleQueryAuditLog(IServiceProvider sp, QueryAuditLogCommand cmd)
{
var repo = sp.GetRequiredService<ICentralUiRepository>();
return await repo.GetAuditLogEntriesAsync(
cmd.User, cmd.EntityType, cmd.Action, cmd.From, cmd.To,
page: cmd.Page, pageSize: cmd.PageSize);
}
// ========================================================================
// Health handlers
// ========================================================================
private static object? HandleGetHealthSummary(IServiceProvider sp)
{
var aggregator = sp.GetRequiredService<ICentralHealthAggregator>();
return aggregator.GetAllSiteStates();
}
private static object? HandleGetSiteHealth(IServiceProvider sp, GetSiteHealthCommand cmd)
{
var aggregator = sp.GetRequiredService<ICentralHealthAggregator>();
return aggregator.GetSiteState(cmd.SiteIdentifier);
}
// ========================================================================
// Template member handlers
// ========================================================================
private static async Task<object?> HandleAddAttribute(IServiceProvider sp, AddTemplateAttributeCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var attr = new TemplateAttribute(cmd.Name)
{
DataType = Enum.Parse<ScadaLink.Commons.Types.Enums.DataType>(cmd.DataType, ignoreCase: true),
Value = cmd.Value,
Description = cmd.Description,
DataSourceReference = cmd.DataSourceReference,
IsLocked = cmd.IsLocked
};
var result = await svc.AddAttributeAsync(cmd.TemplateId, attr, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleUpdateAttribute(IServiceProvider sp, UpdateTemplateAttributeCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var attr = new TemplateAttribute(cmd.Name)
{
DataType = Enum.Parse<ScadaLink.Commons.Types.Enums.DataType>(cmd.DataType, ignoreCase: true),
Value = cmd.Value,
Description = cmd.Description,
DataSourceReference = cmd.DataSourceReference,
IsLocked = cmd.IsLocked
};
var result = await svc.UpdateAttributeAsync(cmd.AttributeId, attr, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleDeleteAttribute(IServiceProvider sp, DeleteTemplateAttributeCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var result = await svc.DeleteAttributeAsync(cmd.AttributeId, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleAddAlarm(IServiceProvider sp, AddTemplateAlarmCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var alarm = new TemplateAlarm(cmd.Name)
{
TriggerType = Enum.Parse<ScadaLink.Commons.Types.Enums.AlarmTriggerType>(cmd.TriggerType, ignoreCase: true),
PriorityLevel = cmd.PriorityLevel,
Description = cmd.Description,
TriggerConfiguration = cmd.TriggerConfiguration,
IsLocked = cmd.IsLocked
};
var result = await svc.AddAlarmAsync(cmd.TemplateId, alarm, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleUpdateAlarm(IServiceProvider sp, UpdateTemplateAlarmCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var alarm = new TemplateAlarm(cmd.Name)
{
TriggerType = Enum.Parse<ScadaLink.Commons.Types.Enums.AlarmTriggerType>(cmd.TriggerType, ignoreCase: true),
PriorityLevel = cmd.PriorityLevel,
Description = cmd.Description,
TriggerConfiguration = cmd.TriggerConfiguration,
IsLocked = cmd.IsLocked
};
var result = await svc.UpdateAlarmAsync(cmd.AlarmId, alarm, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleDeleteAlarm(IServiceProvider sp, DeleteTemplateAlarmCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var result = await svc.DeleteAlarmAsync(cmd.AlarmId, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleAddScript(IServiceProvider sp, AddTemplateScriptCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var script = new TemplateScript(cmd.Name, cmd.Code)
{
TriggerType = cmd.TriggerType,
TriggerConfiguration = cmd.TriggerConfiguration,
IsLocked = cmd.IsLocked,
ParameterDefinitions = cmd.ParameterDefinitions,
ReturnDefinition = cmd.ReturnDefinition
};
var result = await svc.AddScriptAsync(cmd.TemplateId, script, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleUpdateScript(IServiceProvider sp, UpdateTemplateScriptCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var script = new TemplateScript(cmd.Name, cmd.Code)
{
TriggerType = cmd.TriggerType,
TriggerConfiguration = cmd.TriggerConfiguration,
IsLocked = cmd.IsLocked,
ParameterDefinitions = cmd.ParameterDefinitions,
ReturnDefinition = cmd.ReturnDefinition
};
var result = await svc.UpdateScriptAsync(cmd.ScriptId, script, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleDeleteScript(IServiceProvider sp, DeleteTemplateScriptCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var result = await svc.DeleteScriptAsync(cmd.ScriptId, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleAddComposition(IServiceProvider sp, AddTemplateCompositionCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var result = await svc.AddCompositionAsync(cmd.TemplateId, cmd.ComposedTemplateId, cmd.InstanceName, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleDeleteComposition(IServiceProvider sp, DeleteTemplateCompositionCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var result = await svc.DeleteCompositionAsync(cmd.CompositionId, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
// ========================================================================
// Shared Script handlers
// ========================================================================
private static async Task<object?> HandleListSharedScripts(IServiceProvider sp)
{
var svc = sp.GetRequiredService<SharedScriptService>();
return await svc.GetAllSharedScriptsAsync();
}
private static async Task<object?> HandleGetSharedScript(IServiceProvider sp, GetSharedScriptCommand cmd)
{
var svc = sp.GetRequiredService<SharedScriptService>();
return await svc.GetSharedScriptByIdAsync(cmd.SharedScriptId);
}
private static async Task<object?> HandleCreateSharedScript(IServiceProvider sp, CreateSharedScriptCommand cmd, string user)
{
var svc = sp.GetRequiredService<SharedScriptService>();
var result = await svc.CreateSharedScriptAsync(cmd.Name, cmd.Code, cmd.ParameterDefinitions, cmd.ReturnDefinition, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleUpdateSharedScript(IServiceProvider sp, UpdateSharedScriptCommand cmd, string user)
{
var svc = sp.GetRequiredService<SharedScriptService>();
var result = await svc.UpdateSharedScriptAsync(cmd.SharedScriptId, cmd.Code, cmd.ParameterDefinitions, cmd.ReturnDefinition, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
private static async Task<object?> HandleDeleteSharedScript(IServiceProvider sp, DeleteSharedScriptCommand cmd, string user)
{
var svc = sp.GetRequiredService<SharedScriptService>();
var result = await svc.DeleteSharedScriptAsync(cmd.SharedScriptId, user);
return result.IsSuccess ? result.Value : throw new InvalidOperationException(result.Error);
}
// ========================================================================
// Database Connection Definition handlers
// ========================================================================
private static async Task<object?> HandleListDatabaseConnections(IServiceProvider sp)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
return await repo.GetAllDatabaseConnectionsAsync();
}
private static async Task<object?> HandleGetDatabaseConnection(IServiceProvider sp, GetDatabaseConnectionCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
return await repo.GetDatabaseConnectionByIdAsync(cmd.DatabaseConnectionId);
}
private static async Task<object?> HandleCreateDatabaseConnection(IServiceProvider sp, CreateDatabaseConnectionDefCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
var def = new DatabaseConnectionDefinition(cmd.Name, cmd.ConnectionString);
await repo.AddDatabaseConnectionAsync(def);
await repo.SaveChangesAsync();
return def;
}
private static async Task<object?> HandleUpdateDatabaseConnection(IServiceProvider sp, UpdateDatabaseConnectionDefCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
var def = await repo.GetDatabaseConnectionByIdAsync(cmd.DatabaseConnectionId)
?? throw new InvalidOperationException($"DatabaseConnection with ID {cmd.DatabaseConnectionId} not found.");
def.Name = cmd.Name;
def.ConnectionString = cmd.ConnectionString;
await repo.UpdateDatabaseConnectionAsync(def);
await repo.SaveChangesAsync();
return def;
}
private static async Task<object?> HandleDeleteDatabaseConnection(IServiceProvider sp, DeleteDatabaseConnectionDefCommand cmd)
{
var repo = sp.GetRequiredService<IExternalSystemRepository>();
await repo.DeleteDatabaseConnectionAsync(cmd.DatabaseConnectionId);
await repo.SaveChangesAsync();
return true;
}
// ========================================================================
// Inbound API Method handlers
// ========================================================================
private static async Task<object?> HandleListApiMethods(IServiceProvider sp)
{
var repo = sp.GetRequiredService<IInboundApiRepository>();
return await repo.GetAllApiMethodsAsync();
}
private static async Task<object?> HandleGetApiMethod(IServiceProvider sp, GetApiMethodCommand cmd)
{
var repo = sp.GetRequiredService<IInboundApiRepository>();
return await repo.GetApiMethodByIdAsync(cmd.ApiMethodId);
}
private static async Task<object?> HandleCreateApiMethod(IServiceProvider sp, CreateApiMethodCommand cmd)
{
var repo = sp.GetRequiredService<IInboundApiRepository>();
var method = new ApiMethod(cmd.Name, cmd.Script)
{
TimeoutSeconds = cmd.TimeoutSeconds,
ParameterDefinitions = cmd.ParameterDefinitions,
ReturnDefinition = cmd.ReturnDefinition
};
await repo.AddApiMethodAsync(method);
await repo.SaveChangesAsync();
return method;
}
private static async Task<object?> HandleUpdateApiMethod(IServiceProvider sp, UpdateApiMethodCommand cmd)
{
var repo = sp.GetRequiredService<IInboundApiRepository>();
var method = await repo.GetApiMethodByIdAsync(cmd.ApiMethodId)
?? throw new InvalidOperationException($"ApiMethod with ID {cmd.ApiMethodId} not found.");
method.Script = cmd.Script;
method.TimeoutSeconds = cmd.TimeoutSeconds;
method.ParameterDefinitions = cmd.ParameterDefinitions;
method.ReturnDefinition = cmd.ReturnDefinition;
await repo.UpdateApiMethodAsync(method);
await repo.SaveChangesAsync();
return method;
}
private static async Task<object?> HandleDeleteApiMethod(IServiceProvider sp, DeleteApiMethodCommand cmd)
{
var repo = sp.GetRequiredService<IInboundApiRepository>();
await repo.DeleteApiMethodAsync(cmd.ApiMethodId);
await repo.SaveChangesAsync();
return true;
}
// ========================================================================
// Additional Security handlers (API key update, scope rules)
// ========================================================================
private static async Task<object?> HandleUpdateApiKey(IServiceProvider sp, UpdateApiKeyCommand cmd)
{
var repo = sp.GetRequiredService<IInboundApiRepository>();
var key = await repo.GetApiKeyByIdAsync(cmd.ApiKeyId)
?? throw new InvalidOperationException($"ApiKey with ID {cmd.ApiKeyId} not found.");
key.IsEnabled = cmd.IsEnabled;
await repo.UpdateApiKeyAsync(key);
await repo.SaveChangesAsync();
return key;
}
private static async Task<object?> HandleListScopeRules(IServiceProvider sp, ListScopeRulesCommand cmd)
{
var repo = sp.GetRequiredService<ISecurityRepository>();
return await repo.GetScopeRulesForMappingAsync(cmd.MappingId);
}
private static async Task<object?> HandleAddScopeRule(IServiceProvider sp, AddScopeRuleCommand cmd)
{
var repo = sp.GetRequiredService<ISecurityRepository>();
var rule = new SiteScopeRule { LdapGroupMappingId = cmd.MappingId, SiteId = cmd.SiteId };
await repo.AddScopeRuleAsync(rule);
await repo.SaveChangesAsync();
return rule;
}
private static async Task<object?> HandleDeleteScopeRule(IServiceProvider sp, DeleteScopeRuleCommand cmd)
{
var repo = sp.GetRequiredService<ISecurityRepository>();
await repo.DeleteScopeRuleAsync(cmd.ScopeRuleId);
await repo.SaveChangesAsync();
return true;
}
// ========================================================================
// Area update handler
// ========================================================================
private static async Task<object?> HandleUpdateArea(IServiceProvider sp, UpdateAreaCommand cmd)
{
var repo = sp.GetRequiredService<ITemplateEngineRepository>();
var area = await repo.GetAreaByIdAsync(cmd.AreaId)
?? throw new InvalidOperationException($"Area with ID {cmd.AreaId} not found.");
area.Name = cmd.Name;
await repo.UpdateAreaAsync(area);
await repo.SaveChangesAsync();
return area;
}
// ========================================================================
// Remote Query handlers
// ========================================================================
private static async Task<object?> HandleQueryEventLogs(IServiceProvider sp, QueryEventLogsCommand cmd)
{
var commService = sp.GetRequiredService<CommunicationService>();
var request = new EventLogQueryRequest(
Guid.NewGuid().ToString("N"),
cmd.SiteIdentifier,
cmd.From, cmd.To,
cmd.EventType, cmd.Severity,
null, // InstanceId
cmd.Keyword,
null, // ContinuationToken
cmd.PageSize,
DateTimeOffset.UtcNow);
return await commService.QueryEventLogsAsync(cmd.SiteIdentifier, request);
}
private static async Task<object?> HandleQueryParkedMessages(IServiceProvider sp, QueryParkedMessagesCommand cmd)
{
var commService = sp.GetRequiredService<CommunicationService>();
var request = new ParkedMessageQueryRequest(
Guid.NewGuid().ToString("N"),
cmd.SiteIdentifier,
cmd.Page,
cmd.PageSize,
DateTimeOffset.UtcNow);
return await commService.QueryParkedMessagesAsync(cmd.SiteIdentifier, request);
}
private static async Task<object?> HandleDebugSnapshot(IServiceProvider sp, DebugSnapshotCommand cmd)
{
var instanceRepo = sp.GetRequiredService<ITemplateEngineRepository>();
var instance = await instanceRepo.GetInstanceByIdAsync(cmd.InstanceId)
?? throw new InvalidOperationException($"Instance {cmd.InstanceId} not found.");
var siteRepo = sp.GetRequiredService<ISiteRepository>();
var site = await siteRepo.GetSiteByIdAsync(instance.SiteId)
?? throw new InvalidOperationException($"Site {instance.SiteId} not found.");
var commService = sp.GetRequiredService<CommunicationService>();
var request = new DebugSnapshotRequest(instance.UniqueName, Guid.NewGuid().ToString("N"));
return await commService.RequestDebugSnapshotAsync(site.SiteIdentifier, request);
}
}