fix(backend): Move McpNotificationHub to Infrastructure layer to fix dependency inversion violation
Fixed compilation error where Infrastructure layer was referencing API layer (ColaFlow.API.Hubs). This violated the dependency inversion principle and Clean Architecture layering rules. Changes: - Moved McpNotificationHub from ColaFlow.API/Hubs to ColaFlow.Modules.Mcp.Infrastructure/Hubs - Updated McpNotificationHub to inherit directly from Hub instead of BaseHub - Copied necessary helper methods (GetCurrentUserId, GetCurrentTenantId, GetTenantGroupName) to avoid cross-layer dependency - Updated McpNotificationService to use new namespace (ColaFlow.Modules.Mcp.Infrastructure.Hubs) - Updated Program.cs to import new Hub namespace - Updated McpNotificationServiceTests to use new namespace - Kept BaseHub in API layer for ProjectHub and NotificationHub Architecture Impact: - Infrastructure layer no longer depends on API layer - Proper dependency flow: API -> Infrastructure -> Application -> Domain - McpNotificationHub is now properly encapsulated within the MCP module 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ using ColaFlow.Modules.Identity.Application;
|
|||||||
using ColaFlow.Modules.Identity.Infrastructure;
|
using ColaFlow.Modules.Identity.Infrastructure;
|
||||||
using ColaFlow.Modules.Identity.Infrastructure.Persistence;
|
using ColaFlow.Modules.Identity.Infrastructure.Persistence;
|
||||||
using ColaFlow.Modules.Mcp.Infrastructure.Extensions;
|
using ColaFlow.Modules.Mcp.Infrastructure.Extensions;
|
||||||
|
using ColaFlow.Modules.Mcp.Infrastructure.Hubs;
|
||||||
using ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence;
|
using ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace ColaFlow.API.Hubs;
|
namespace ColaFlow.Modules.Mcp.Infrastructure.Hubs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SignalR Hub for MCP real-time notifications
|
/// SignalR Hub for MCP real-time notifications
|
||||||
/// Supports notifying AI agents and users about PendingChange status updates
|
/// Supports notifying AI agents and users about PendingChange status updates
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class McpNotificationHub : BaseHub
|
public class McpNotificationHub : Hub
|
||||||
{
|
{
|
||||||
private readonly ILogger<McpNotificationHub> _logger;
|
private readonly ILogger<McpNotificationHub> _logger;
|
||||||
|
|
||||||
@@ -19,15 +20,26 @@ public class McpNotificationHub : BaseHub
|
|||||||
|
|
||||||
public override async Task OnConnectedAsync()
|
public override async Task OnConnectedAsync()
|
||||||
{
|
{
|
||||||
var connectionId = Context.ConnectionId;
|
try
|
||||||
var userId = GetCurrentUserId();
|
{
|
||||||
var tenantId = GetCurrentTenantId();
|
var connectionId = Context.ConnectionId;
|
||||||
|
var userId = GetCurrentUserId();
|
||||||
|
var tenantId = GetCurrentTenantId();
|
||||||
|
|
||||||
_logger.LogInformation(
|
// Automatically join tenant group (tenant isolation)
|
||||||
"MCP client connected - ConnectionId={ConnectionId}, UserId={UserId}, TenantId={TenantId}",
|
await Groups.AddToGroupAsync(Context.ConnectionId, GetTenantGroupName(tenantId));
|
||||||
connectionId, userId, tenantId);
|
|
||||||
|
|
||||||
await base.OnConnectedAsync();
|
_logger.LogInformation(
|
||||||
|
"MCP client connected - ConnectionId={ConnectionId}, UserId={UserId}, TenantId={TenantId}",
|
||||||
|
connectionId, userId, tenantId);
|
||||||
|
|
||||||
|
await base.OnConnectedAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "MCP client connection error");
|
||||||
|
Context.Abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task OnDisconnectedAsync(Exception? exception)
|
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||||
@@ -78,11 +90,37 @@ public class McpNotificationHub : BaseHub
|
|||||||
Context.ConnectionId, groupName, pendingChangeId);
|
Context.ConnectionId, groupName, pendingChangeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// Helper methods (copied from BaseHub to avoid cross-layer dependency)
|
||||||
/// Get the SignalR group name for a pending change
|
protected Guid GetCurrentUserId()
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="pendingChangeId">The pending change ID</param>
|
var userIdClaim = Context.User?.FindFirst("sub")
|
||||||
/// <returns>The group name</returns>
|
?? Context.User?.FindFirst("user_id");
|
||||||
|
|
||||||
|
if (userIdClaim == null || !Guid.TryParse(userIdClaim.Value, out var userId))
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException("User ID not found in token");
|
||||||
|
}
|
||||||
|
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Guid GetCurrentTenantId()
|
||||||
|
{
|
||||||
|
var tenantIdClaim = Context.User?.FindFirst("tenant_id");
|
||||||
|
|
||||||
|
if (tenantIdClaim == null || !Guid.TryParse(tenantIdClaim.Value, out var tenantId))
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException("Tenant ID not found in token");
|
||||||
|
}
|
||||||
|
|
||||||
|
return tenantId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string GetTenantGroupName(Guid tenantId)
|
||||||
|
{
|
||||||
|
return $"tenant-{tenantId}";
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetPendingChangeGroupName(Guid pendingChangeId)
|
private static string GetPendingChangeGroupName(Guid pendingChangeId)
|
||||||
{
|
{
|
||||||
return $"pending-change-{pendingChangeId}";
|
return $"pending-change-{pendingChangeId}";
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using ColaFlow.API.Hubs;
|
|
||||||
using ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
using ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||||
using ColaFlow.Modules.Mcp.Application.Services;
|
using ColaFlow.Modules.Mcp.Application.Services;
|
||||||
|
using ColaFlow.Modules.Mcp.Infrastructure.Hubs;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using ColaFlow.API.Hubs;
|
|
||||||
using ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
using ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||||
|
using ColaFlow.Modules.Mcp.Infrastructure.Hubs;
|
||||||
using ColaFlow.Modules.Mcp.Infrastructure.Services;
|
using ColaFlow.Modules.Mcp.Infrastructure.Services;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|||||||
Reference in New Issue
Block a user