using System.Text.Json; using RelayShared.Rtc; using RelayServer.Services.Rtc; namespace RelayServer.Endpoints; public static class RtcEndpoints { public static void MapRtcEndpoints(this WebApplication app) { app.MapPost("/api/rtc/offer", async (RtcOffer request, RtcCallService rtcCallService) => { await rtcCallService.WriteOfferAsync( request.ChannelId, request.Username, request.TargetUsername, request.SessionDescription); RtcNotificationService.BroadcastToChannel(new RtcNotificationMessage { Type = SignalType.OfferUpdated, ChannelId = request.ChannelId, Username = request.Username, TargetUsername = request.TargetUsername }); return Results.Ok(); }); app.MapGet("/api/rtc/offers", async (RtcCallService rtcCallService) => { return Results.Ok(await rtcCallService.GetOffersAsync()); }); app.MapGet("/api/rtc/active/{channelId}", async (string channelId, RtcCallService rtcCallService) => { return Results.Ok(await rtcCallService.HasActiveCallAsync(channelId)); }); app.MapGet("/api/rtc/participants/{channelId}", async (string channelId, RtcCallService rtcCallService) => { return Results.Ok(await rtcCallService.GetParticipantsAsync(channelId)); }); app.MapGet("/api/rtc/offer/{channelId}/{fromUsername}/{targetUsername}", async ( string channelId, string fromUsername, string targetUsername, RtcCallService rtcCallService) => { var offer = await rtcCallService.GetOfferAsync(channelId, fromUsername, targetUsername); return offer is null ? Results.NotFound() : Results.Ok(offer); }); app.MapPost("/api/rtc/answer", async (RtcAnswer request, RtcCallService rtcCallService) => { await rtcCallService.WriteAnswerAsync( request.ChannelId, request.Username, request.TargetUsername, request.SessionDescription); RtcNotificationService.BroadcastToChannel(new RtcNotificationMessage { Type = SignalType.AnswerUpdated, ChannelId = request.ChannelId, Username = request.Username, TargetUsername = request.TargetUsername }); return Results.Ok(); }); app.MapGet("/api/rtc/answer/{channelId}/{fromUsername}/{targetUsername}", async ( string channelId, string fromUsername, string targetUsername, RtcCallService rtcCallService) => { var answer = await rtcCallService.GetAnswerAsync(channelId, fromUsername, targetUsername); return answer is null ? Results.NotFound() : Results.Ok(answer); }); app.MapPost("/api/rtc/candidate", async (DBIceCandidate request, RtcCallService rtcCallService) => { await rtcCallService.WriteIceCandidateAsync( request.ChannelId, request.Username, request.TargetUsername, request.Candidate.candidate, request.Candidate.sdpMid, request.Candidate.sdpMLineIndex); RtcNotificationService.BroadcastToChannel(new RtcNotificationMessage { Type = SignalType.CandidateAdded, ChannelId = request.ChannelId, Username = request.Username, TargetUsername = request.TargetUsername, Direction = JsonSerializer.Serialize(request.Candidate) }); return Results.Ok(); }); app.MapGet("/api/rtc/candidates/{channelId}", async (string channelId, RtcCallService rtcCallService) => { return Results.Ok(await rtcCallService.GetIceCandidatesAsync(channelId)); }); app.MapPost("/api/rtc/leave", async (RtcLeaveRequest request, RtcCallService rtcCallService) => { await rtcCallService.LeaveCallAsync(request.ChannelId, request.Username); RtcNotificationService.BroadcastToChannel(new RtcNotificationMessage { Type = SignalType.CallLeft, ChannelId = request.ChannelId, Username = request.Username }); return Results.Ok(); }); } }