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.Persistence;
|
||||
using ColaFlow.Modules.Mcp.Infrastructure.Extensions;
|
||||
using ColaFlow.Modules.Mcp.Infrastructure.Hubs;
|
||||
using ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ColaFlow.API.Hubs;
|
||||
namespace ColaFlow.Modules.Mcp.Infrastructure.Hubs;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR Hub for MCP real-time notifications
|
||||
/// Supports notifying AI agents and users about PendingChange status updates
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
public class McpNotificationHub : BaseHub
|
||||
public class McpNotificationHub : Hub
|
||||
{
|
||||
private readonly ILogger<McpNotificationHub> _logger;
|
||||
|
||||
@@ -19,15 +20,26 @@ public class McpNotificationHub : BaseHub
|
||||
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
var connectionId = Context.ConnectionId;
|
||||
var userId = GetCurrentUserId();
|
||||
var tenantId = GetCurrentTenantId();
|
||||
try
|
||||
{
|
||||
var connectionId = Context.ConnectionId;
|
||||
var userId = GetCurrentUserId();
|
||||
var tenantId = GetCurrentTenantId();
|
||||
|
||||
_logger.LogInformation(
|
||||
"MCP client connected - ConnectionId={ConnectionId}, UserId={UserId}, TenantId={TenantId}",
|
||||
connectionId, userId, tenantId);
|
||||
// Automatically join tenant group (tenant isolation)
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, GetTenantGroupName(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)
|
||||
@@ -78,11 +90,37 @@ public class McpNotificationHub : BaseHub
|
||||
Context.ConnectionId, groupName, pendingChangeId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the SignalR group name for a pending change
|
||||
/// </summary>
|
||||
/// <param name="pendingChangeId">The pending change ID</param>
|
||||
/// <returns>The group name</returns>
|
||||
// Helper methods (copied from BaseHub to avoid cross-layer dependency)
|
||||
protected Guid GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = Context.User?.FindFirst("sub")
|
||||
?? 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)
|
||||
{
|
||||
return $"pending-change-{pendingChangeId}";
|
||||
@@ -1,6 +1,6 @@
|
||||
using ColaFlow.API.Hubs;
|
||||
using ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||
using ColaFlow.Modules.Mcp.Application.Services;
|
||||
using ColaFlow.Modules.Mcp.Infrastructure.Hubs;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using ColaFlow.API.Hubs;
|
||||
using ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||
using ColaFlow.Modules.Mcp.Infrastructure.Hubs;
|
||||
using ColaFlow.Modules.Mcp.Infrastructure.Services;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
Reference in New Issue
Block a user