using System.Security.Claims; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; using SameBeat.Api.Hubs; using SameBeat.Application.Interfejsy; using SameBeat.Application.Komendy; namespace SameBeat.Api.Controllers; /// /// Sterowanie mikrofonem przez hosta (ETAP 5) -- NAJWAZNIEJSZA funkcja produktu. Trasa zaczyna sie od /// "api/host", wiec jest automatycznie chroniona [Authorize] przez KonwencjaAutoryzacjiHosta. /// /// BEZPIECZENSTWO: kazda zmiana rozglaszana jest przez SignalR do WSZYSTKICH polaczonych klientow -- /// payload to zawsze StanMikrofonuDto (numerSlotu + nick + koniecUtc), NIGDY uczestnikId, dokladnie ten /// sam kontrakt co przy scenie (patrz HostStageController). /// /// Autorytatywne wygasniecie czasu NIE przechodzi przez ten kontroler -- tym zajmuje sie /// SerwisTimeraMikrofonu (Infrastructure/Audio), ktory dziala w tle i sam rozglasza "MikrofonOdebrany" /// przez IRozgloszenieZdarzenImprezy. Ten kontroler obsluguje wylacznie akcje hosta: przyznaj/przerwij. /// [ApiController] [Route("api/host/mikrofon")] public class HostMicrophoneController : ControllerBase { private readonly ISerwisMikrofonu _serwisMikrofonu; private readonly IHubContext _hubKontekst; public HostMicrophoneController(ISerwisMikrofonu serwisMikrofonu, IHubContext hubKontekst) { _serwisMikrofonu = serwisMikrofonu; _hubKontekst = hubKontekst; } /// Biezacy stan mikrofonu -- do wyswietlenia w panelu hosta po odswiezeniu strony. [HttpGet] public async Task PobierzStanAsync(CancellationToken token) { var wynik = await _serwisMikrofonu.PobierzStanAsync(token); if (!wynik.Sukces || wynik.Dane is null) { return BadRequest(new { blad = wynik.Blad }); } return Ok(wynik.Dane); } [HttpPost("{uczestnikId:guid}/przyznaj")] public async Task PrzyznajAsync(Guid uczestnikId, [FromBody] PrzyznajMikrofonKomenda komenda, CancellationToken token) { var hostNick = User.FindFirstValue(ClaimTypes.Name); if (string.IsNullOrWhiteSpace(hostNick)) { // Nie powinno wystapic przy poprawnym tokenie JWT (GeneratorTokenowJwt zawsze dodaje ten // claim) -- ale gdyby jednak, host dostaje czytelny blad zamiast NullReferenceException. return BadRequest(new { blad = "Nie udalo sie odczytac tozsamosci z sesji -- zaloguj sie ponownie." }); } var wynik = await _serwisMikrofonu.PrzyznajAsync(uczestnikId, komenda.CzasSekundy, hostNick, token); if (!wynik.Sukces || wynik.Dane is null) { return BadRequest(new { blad = wynik.Blad }); } await _hubKontekst.Clients.All.SendAsync("MikrofonPrzyznany", wynik.Dane, cancellationToken: token); return Ok(wynik.Dane); } [HttpPost("{uczestnikId:guid}/przerwij")] public async Task PrzerwijAsync(Guid uczestnikId, CancellationToken token) { var wynik = await _serwisMikrofonu.PrzerwijAsync(uczestnikId, token); if (!wynik.Sukces || wynik.Dane is null) { return BadRequest(new { blad = wynik.Blad }); } await _hubKontekst.Clients.All.SendAsync("MikrofonOdebrany", wynik.Dane, cancellationToken: token); return Ok(wynik.Dane); } }