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:
@@ -1,6 +1,8 @@
|
||||
using ColaFlow.API.Extensions;
|
||||
using ColaFlow.API.Handlers;
|
||||
using ColaFlow.API.Hubs;
|
||||
using ColaFlow.API.Middleware;
|
||||
using ColaFlow.API.Services;
|
||||
using ColaFlow.Modules.Identity.Application;
|
||||
using ColaFlow.Modules.Identity.Infrastructure;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
@@ -65,6 +67,25 @@ builder.Services.AddAuthentication(options =>
|
||||
IssuerSigningKey = new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"] ?? throw new InvalidOperationException("JWT SecretKey not configured")))
|
||||
};
|
||||
|
||||
// Configure SignalR to use JWT from query string (for WebSocket upgrade)
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnMessageReceived = context =>
|
||||
{
|
||||
var accessToken = context.Request.Query["access_token"];
|
||||
|
||||
// If the request is for SignalR hub...
|
||||
var path = context.HttpContext.Request.Path;
|
||||
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
|
||||
{
|
||||
// Read the token from query string
|
||||
context.Token = accessToken;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Configure Authorization Policies for RBAC
|
||||
@@ -92,17 +113,35 @@ builder.Services.AddAuthorization(options =>
|
||||
policy.RequireRole("AIAgent"));
|
||||
});
|
||||
|
||||
// Configure CORS for frontend
|
||||
// Configure CORS for frontend (SignalR requires AllowCredentials)
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowFrontend", policy =>
|
||||
{
|
||||
policy.WithOrigins("http://localhost:3000")
|
||||
policy.WithOrigins("http://localhost:3000", "https://localhost:3000")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
.AllowAnyMethod()
|
||||
.AllowCredentials(); // Required for SignalR
|
||||
});
|
||||
});
|
||||
|
||||
// Configure SignalR
|
||||
builder.Services.AddSignalR(options =>
|
||||
{
|
||||
// Enable detailed errors (development only)
|
||||
options.EnableDetailedErrors = builder.Environment.IsDevelopment();
|
||||
|
||||
// Client timeout settings
|
||||
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
|
||||
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
|
||||
|
||||
// Keep alive interval
|
||||
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
|
||||
});
|
||||
|
||||
// Register Realtime Notification Service
|
||||
builder.Services.AddScoped<IRealtimeNotificationService, RealtimeNotificationService>();
|
||||
|
||||
// Configure OpenAPI/Scalar
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
@@ -138,6 +177,10 @@ app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
// Map SignalR Hubs (after UseAuthorization)
|
||||
app.MapHub<ProjectHub>("/hubs/project");
|
||||
app.MapHub<NotificationHub>("/hubs/notification");
|
||||
|
||||
app.Run();
|
||||
|
||||
// Make the implicit Program class public for integration tests
|
||||
|
||||
Reference in New Issue
Block a user