feat(backend): Implement SignalR real-time communication infrastructure

Add complete SignalR infrastructure for real-time project collaboration and notifications with multi-tenant isolation and JWT authentication.

Changes:
- Created BaseHub with multi-tenant isolation and JWT authentication helpers
- Created ProjectHub for real-time project collaboration (join/leave, typing indicators)
- Created NotificationHub for user-level notifications
- Implemented IRealtimeNotificationService for application layer integration
- Configured SignalR in Program.cs with CORS and JWT query string support
- Added SignalRTestController for connection testing
- Documented hub endpoints, client events, and integration examples

Features:
- Multi-tenant isolation via automatic tenant group membership
- JWT authentication (Bearer header + query string for WebSocket)
- Hub endpoints: /hubs/project, /hubs/notification
- Project-level events: IssueCreated, IssueUpdated, IssueStatusChanged, etc.
- User-level notifications with tenant-wide broadcasting
- Test endpoints for validation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-04 09:04:13 +01:00
parent 172d0de1fe
commit 5a1ad2eb97
8 changed files with 745 additions and 3 deletions

View File

@@ -0,0 +1,93 @@
using Microsoft.AspNetCore.SignalR;
using ColaFlow.API.Hubs;
namespace ColaFlow.API.Services;
public class RealtimeNotificationService : IRealtimeNotificationService
{
private readonly IHubContext<ProjectHub> _projectHubContext;
private readonly IHubContext<NotificationHub> _notificationHubContext;
private readonly ILogger<RealtimeNotificationService> _logger;
public RealtimeNotificationService(
IHubContext<ProjectHub> projectHubContext,
IHubContext<NotificationHub> notificationHubContext,
ILogger<RealtimeNotificationService> logger)
{
_projectHubContext = projectHubContext;
_notificationHubContext = notificationHubContext;
_logger = logger;
}
public async Task NotifyProjectUpdate(Guid tenantId, Guid projectId, object data)
{
var groupName = $"project-{projectId}";
_logger.LogInformation("Sending project update to group {GroupName}", groupName);
await _projectHubContext.Clients.Group(groupName).SendAsync("ProjectUpdated", data);
}
public async Task NotifyIssueCreated(Guid tenantId, Guid projectId, object issue)
{
var groupName = $"project-{projectId}";
await _projectHubContext.Clients.Group(groupName).SendAsync("IssueCreated", issue);
}
public async Task NotifyIssueUpdated(Guid tenantId, Guid projectId, object issue)
{
var groupName = $"project-{projectId}";
await _projectHubContext.Clients.Group(groupName).SendAsync("IssueUpdated", issue);
}
public async Task NotifyIssueDeleted(Guid tenantId, Guid projectId, Guid issueId)
{
var groupName = $"project-{projectId}";
await _projectHubContext.Clients.Group(groupName).SendAsync("IssueDeleted", new { IssueId = issueId });
}
public async Task NotifyIssueStatusChanged(
Guid tenantId,
Guid projectId,
Guid issueId,
string oldStatus,
string newStatus)
{
var groupName = $"project-{projectId}";
await _projectHubContext.Clients.Group(groupName).SendAsync("IssueStatusChanged", new
{
IssueId = issueId,
OldStatus = oldStatus,
NewStatus = newStatus,
ChangedAt = DateTime.UtcNow
});
}
public async Task NotifyUser(Guid userId, string message, string type = "info")
{
var userConnectionId = $"user-{userId}";
await _notificationHubContext.Clients.User(userId.ToString()).SendAsync("Notification", new
{
Message = message,
Type = type,
Timestamp = DateTime.UtcNow
});
}
public async Task NotifyUsersInTenant(Guid tenantId, string message, string type = "info")
{
var groupName = $"tenant-{tenantId}";
await _notificationHubContext.Clients.Group(groupName).SendAsync("Notification", new
{
Message = message,
Type = type,
Timestamp = DateTime.UtcNow
});
}
}