This commit is contained in:
2025-11-29 00:50:22 +01:00
parent 6bc65c2a29
commit 30b4bb4626
11 changed files with 518 additions and 59 deletions

View File

@@ -309,6 +309,30 @@ public class CollaborationHub : Hub
Console.WriteLine($"[Collaboration] DataSaved sent to others in room {roomKey}");
}
/// <summary>
/// Sincronizza l'intero template con tutti gli altri collaboratori nella room
/// Usato per sync efficienti - invia il template completo invece di richiedere reload dal server
/// Supporta compressione opzionale per template grandi
/// </summary>
public async Task BroadcastTemplateSync(string roomKey, string templateJson, int version, bool compressed = false)
{
var senderId = Context.ConnectionId;
Console.WriteLine($"[Collaboration] BroadcastTemplateSync from {senderId} for room {roomKey}, version {version}, size: {templateJson.Length} bytes, compressed: {compressed}");
UpdateUserActivity(senderId);
await Clients.OthersInGroup(roomKey).SendAsync("TemplateSync", new TemplateSyncMessage
{
TemplateJson = templateJson,
SenderConnectionId = senderId,
Version = version,
Compressed = compressed,
Timestamp = DateTime.UtcNow
});
Console.WriteLine($"[Collaboration] TemplateSync broadcast complete for room {roomKey}");
}
#endregion
#region Lifecycle
@@ -517,6 +541,19 @@ public class DataSavedMessage
public DateTime Timestamp { get; set; }
}
/// <summary>
/// Message for efficient template sync - sends full template JSON (compressed optional)
/// Used for initial sync and full resync requests
/// </summary>
public class TemplateSyncMessage
{
public string TemplateJson { get; set; } = string.Empty;
public string SenderConnectionId { get; set; } = string.Empty;
public int Version { get; set; } // Incrementing version number for conflict detection
public bool Compressed { get; set; } // Whether TemplateJson is base64-encoded gzip
public DateTime Timestamp { get; set; }
}
public class UserLeftMessage
{
public string ConnectionId { get; set; } = string.Empty;

View File

@@ -12,19 +12,50 @@ public class DataHub : Hub
/// </summary>
public async Task NotifyDataChanged(string entityType, string action, object? data = null)
{
await Clients.Others.SendAsync("DataChanged", entityType, action, data);
try
{
await Clients.Others.SendAsync("DataChanged", entityType, action, data);
}
catch (Exception ex)
{
Console.WriteLine($"[DataHub] Error in NotifyDataChanged: {ex.Message}");
}
}
public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
Console.WriteLine($"Client connected: {Context.ConnectionId}");
try
{
Console.WriteLine($"[DataHub] Client connecting: {Context.ConnectionId}");
await base.OnConnectedAsync();
Console.WriteLine($"[DataHub] Client connected: {Context.ConnectionId}");
}
catch (Exception ex)
{
Console.WriteLine($"[DataHub] Error in OnConnectedAsync: {ex.Message}");
throw;
}
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
await base.OnDisconnectedAsync(exception);
Console.WriteLine($"Client disconnected: {Context.ConnectionId}");
try
{
if (exception != null)
{
Console.WriteLine($"[DataHub] Client disconnected with error: {Context.ConnectionId} - {exception.Message}");
}
else
{
Console.WriteLine($"[DataHub] Client disconnected: {Context.ConnectionId}");
}
await base.OnDisconnectedAsync(exception);
}
catch (Exception ex)
{
Console.WriteLine($"[DataHub] Error in OnDisconnectedAsync: {ex.Message}");
throw;
}
}
}

View File

@@ -19,20 +19,23 @@ builder.Services.AddScoped<DemoDataService>();
builder.Services.AddScoped<ReportGeneratorService>();
builder.Services.AddSingleton<DataNotificationService>();
// SignalR
builder.Services.AddSignalR()
// SignalR - with increased message size for template sync (default is 32KB)
builder.Services.AddSignalR(options =>
{
options.MaximumReceiveMessageSize = 1024 * 1024; // 1MB max message size
})
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
options.PayloadSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
// CORS - Allow credentials for SignalR
// CORS - Allow credentials for SignalR (accepts any origin for external access)
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend", policy =>
{
policy.SetIsOriginAllowed(origin => new Uri(origin).Host == "localhost")
policy.SetIsOriginAllowed(_ => true) // Permette qualsiasi origine
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();

Binary file not shown.

Binary file not shown.