feat(voice): вебхуки LiveKit и сторож голосовых состояний
AGENT.md 7.14: - POST /api/v1/livekit/webhook: подпись проверяется JWT-токеном, подписанным API-секретом, с проверкой sha256 тела (иначе 401), при выключенном голосе — 503; обработка идемпотентна по event.id (повторная доставка → duplicate); - события: participant_joined восстанавливает состояние после рестарта приложения, participant_left и participant_connection_aborted убирают участника, room_finished очищает комнату целиком, track_published и track_unpublished обновляют флаги камеры и демонстрации экрана; каждое изменение рассылает VOICE_STATE_UPDATE участникам сервера; - сторож `StartVoiceWatchdog` раз в 15 минут удаляет состояния без обновлений дольше шести часов (сбои без вебхука) и уведомляет клиентов; - тесты: подпись (чужой секрет, другое тело, отсутствие заголовка), полный цикл событий SFU и идемпотентность повторной доставки.
This commit is contained in:
@@ -89,3 +89,34 @@ func TestResolveLiveKitURL(t *testing.T) {
|
||||
t.Fatal("http-адрес должен отклоняться")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyWebhookSignature(t *testing.T) {
|
||||
issuer := voice.NewIssuer("key123", "secret456", time.Hour)
|
||||
body := []byte(`{"event":"participant_joined"}`)
|
||||
|
||||
// Без заголовка — отказ.
|
||||
if err := issuer.VerifyWebhook("", body); err == nil {
|
||||
t.Fatal("подпись обязательна")
|
||||
}
|
||||
// Чужой подписью — отказ.
|
||||
other := voice.NewIssuer("key123", "another-secret", time.Hour)
|
||||
token, err := other.IssueWebhookForTest(body)
|
||||
if err != nil {
|
||||
t.Fatalf("issue: %v", err)
|
||||
}
|
||||
if err := issuer.VerifyWebhook(token, body); err == nil {
|
||||
t.Fatal("подпись чужим секретом должна отклоняться")
|
||||
}
|
||||
// Своей подписью, но другим телом — отказ.
|
||||
valid, err := issuer.IssueWebhookForTest(body)
|
||||
if err != nil {
|
||||
t.Fatalf("issue: %v", err)
|
||||
}
|
||||
if err := issuer.VerifyWebhook(valid, []byte(`{"event":"room_finished"}`)); err == nil {
|
||||
t.Fatal("несовпадение тела должно отклоняться")
|
||||
}
|
||||
// Корректная подпись — успех.
|
||||
if err := issuer.VerifyWebhook(valid, body); err != nil {
|
||||
t.Fatalf("VerifyWebhook: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user