feat(calls): add consent-based signaling to existing ChatHub
Wire shared PostIt call signaling, account-derived relay policy and temporary TURN credentials. Keep calls disabled pending native Android/Linux media integration. Document experimental Android crash and laboratory removal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
56ca115f50
commit
5e48831d6d
20 changed files with 1368 additions and 16 deletions
76
src/Yavsc.Server/Hubs/ChatHub.Calls.cs
Normal file
76
src/Yavsc.Server/Hubs/ChatHub.Calls.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#nullable enable
|
||||
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Abstract.Chat.Calls;
|
||||
using Yavsc.Server.Helpers;
|
||||
|
||||
namespace Yavsc.Server.Hubs;
|
||||
|
||||
public partial class ChatHub
|
||||
{
|
||||
public async Task<CallSession> InviteCall(string roomName, string callee)
|
||||
{
|
||||
var caller = CallUser();
|
||||
if (string.IsNullOrWhiteSpace(roomName) || !InputValidator.ValidateRoomName(roomName) ||
|
||||
string.IsNullOrWhiteSpace(callee) || !InputValidator.ValidateUserName(callee))
|
||||
throw new HubException("Invalid call participant or room.");
|
||||
RequireCallPresence(roomName, caller, callee);
|
||||
var relayOnly = await RequiresRelayAsync(caller, callee);
|
||||
return await _calls.InviteAsync(roomName, caller, callee, Context.ConnectionId,
|
||||
_cxManager.GetConnexionIds(callee)?.Distinct().ToArray() ?? [], relayOnly);
|
||||
}
|
||||
|
||||
public async Task AcceptCall(Guid callId)
|
||||
{
|
||||
var user = CallUser();
|
||||
var session = await _calls.GetSessionAsync(callId, user, Context.ConnectionId);
|
||||
RequireCallPresence(session.RoomName, session.Caller, session.Callee);
|
||||
await _calls.AcceptAsync(callId, user, Context.ConnectionId,
|
||||
await RequiresRelayAsync(session.Caller, session.Callee));
|
||||
}
|
||||
|
||||
public Task EndCall(Guid callId) => _calls.EndAsync(callId, CallUser(), Context.ConnectionId);
|
||||
|
||||
public async Task SendCallSignal(Guid callId, CallSignal signal)
|
||||
{
|
||||
var user = CallUser();
|
||||
var session = await _calls.GetSessionAsync(callId, user, Context.ConnectionId);
|
||||
RequireCallPresence(session.RoomName, session.Caller, session.Callee);
|
||||
if (!session.RelayOnly && await RequiresRelayAsync(session.Caller, session.Callee))
|
||||
{
|
||||
await _calls.EndForUserAsync(user, "privacy-changed");
|
||||
throw new HubException("Call ended because the IP privacy policy changed. Start a new call.");
|
||||
}
|
||||
await _calls.SignalAsync(callId, user, Context.ConnectionId, signal);
|
||||
}
|
||||
|
||||
private string CallUser()
|
||||
{
|
||||
var user = Context.User?.GetUserName();
|
||||
if (string.IsNullOrWhiteSpace(user) || _cxManager.GetUserName(Context.ConnectionId) != user)
|
||||
throw new HubException("An authenticated chat connection is required.");
|
||||
return user;
|
||||
}
|
||||
|
||||
private void RequireCallPresence(string room, string caller, string callee)
|
||||
{
|
||||
if (!_cxManager.IsConnected(caller) || !_cxManager.IsConnected(callee) ||
|
||||
!_cxManager.IsPresent(room, caller) || !_cxManager.IsPresent(room, callee))
|
||||
throw new HubException("Both call participants must be present in the chat room.");
|
||||
}
|
||||
|
||||
private async Task<bool> RequiresRelayAsync(string caller, string callee)
|
||||
{
|
||||
var users = await _dbContext.Users.AsNoTracking()
|
||||
.Where(u => u.UserName == caller || u.UserName == callee)
|
||||
.Select(u => new { u.Id, u.HideIpAddressInCalls }).ToArrayAsync(Context.ConnectionAborted);
|
||||
if (users.Length != 2)
|
||||
throw new HubException("Call participants are unavailable.");
|
||||
var ids = users.Select(u => u.Id).ToArray();
|
||||
if (await _dbContext.BlackListed.AnyAsync(
|
||||
b => ids.Contains(b.OwnerId) && ids.Contains(b.UserId), Context.ConnectionAborted))
|
||||
throw new HubException("Calls between these participants are not allowed.");
|
||||
return users.Any(u => u.HideIpAddressInCalls);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue