Instrukcja uruchomienia i przeprowadzenia webinaru
Dokument prowadzi krok po kroku od pustego środowiska do zakończonego webinaru z nagraniem i certyfikatami. Opisuje uruchomienie lokalne, na localhost, przeznaczone do testów przed wdrożeniem u klienta.
Dla kogo. Dla osoby, która ma uruchomić platformę na własnym komputerze i przeprowadzić na niej pełny webinar testowy: od utworzenia wydarzenia, przez zapis uczestnika, spotkanie na żywo, aż po podsumowanie i certyfikat.
Co obejmuje ten dokument
Rozdziały 2-5 to przygotowanie środowiska, wykonywane raz. Rozdziały 6-10 to cykl życia pojedynczego webinaru, powtarzany przy każdym wydarzeniu. Rozdział 12 to gotowy scenariusz testu, rozdział 12 to katalog typowych problemów.
Czym jest ta platforma
Aplikacja obsługuje pełny cykl życia szkolenia online: rejestrację uczestnika, przypomnienia mailowe i SMS, spotkanie wideo, interakcje na żywo (czat, pytania, zapisy na zadania, zapytania ofertowe), nagranie, podsumowanie i certyfikaty uczestnictwa.
Model wdrożenia zakłada osobną instancję dla każdego klienta, na jego serwerze i jego domenie. Jedno repozytorium, jedna wersja kodu, a różnice między klientami wyłącznie w konfiguracji.
Mapa cyklu życia wydarzenia
Uruchomienie środowiska
Całość działa w Dockerze. Do testów lokalnych wystarczą cztery usługi, a wideo we własnym serwerze dokłada kolejne cztery.
Wymagania
| Składnik | Wersja | Uwagi |
|---|---|---|
| Docker Desktop | aktualny | Z obsługą Docker Compose v2 |
| Wolna pamięć RAM | min. 4 GB | 8 GB przy profilu livekit: Egress uruchamia przeglądarkę |
| Wolne porty | 8080, 8081, 5432, 9000, 9001 | Dodatkowo 7880, 7881 i zakres UDP przy LiveKit |
| Przeglądarka | Chrome / Edge | Do testu wideo najlepiej dwie różne albo okno prywatne |
Krok po kroku
Przygotuj plik konfiguracyjny
W katalogu projektu skopiuj wzorzec i uzupełnij sekrety. Plik .env nie trafia do repozytorium i jest inny dla każdej instancji.
# w katalogu Webinar-platform
cp .env.example .env
Uzupełnij co najmniej: JWT_SECRET (minimum 32 znaki, inaczej aplikacja nie wystartuje), DB_PASSWORD, MINIO_SECRET_KEY oraz ADMIN_EMAIL i ADMIN_PASSWORD.
Zbuduj i uruchom kontenery
Pierwsze uruchomienie buduje obrazy i pobiera zależności, więc trwa kilka minut.
docker compose up -d --build
Startują cztery usługi: postgres, minio, backend i frontend. Backend sam wykonuje migracje Flyway, więc baza jest gotowa bez dodatkowych poleceń.
Sprawdź, czy wszystko wstało
docker compose ps
curl http://localhost:8080/api/health
Poprawna odpowiedź to {"status":"UP","version":"0.1.0"}. Jeśli backend nie odpowiada, zajrzyj do logów: docker compose logs -f backend.
Uruchom wideo, jeśli chcesz testować spotkanie u siebie
Serwer LiveKit siedzi w osobnym profilu, wyłączonym domyślnie: instancja w trybie NONE albo na Jitsi nie ma powodu go uruchamiać.
docker compose --profile livekit up -d
Profil dokłada cztery kontenery: livekit (serwer), coturn (TURN), redis i livekit-egress (nagrywanie). Same spotkania działają bez dwóch ostatnich, ale nagrywanie bez nich nie działa wcale.
Do uruchomienia lokalnego ustaw LIVEKIT_NODE_IP=127.0.0.1 w .env. Bez tego serwer rozgłasza publiczny adres łącza, a przeglądarka na tej samej maszynie nie zestawi połączenia: uczestnik wejdzie do pokoju i zobaczy listę obecnych, ale nie popłynie ani obraz, ani dźwięk.
Adresy po uruchomieniu
| Adres | Co to jest |
|---|---|
| http://localhost:8081 | Aplikacja: panel i strony publiczne |
| http://localhost:8080/api | API backendu |
| http://localhost:8080/api/health | Stan aplikacji |
| http://localhost:9001 | Konsola MinIO (nagrania, materiały, pliki) |
| localhost:5432 | PostgreSQL, do podłączenia klientem SQL |
Nigdy nie używaj docker compose down -v. Flaga -v kasuje wolumeny, czyli bazę i MinIO razem z całą konfiguracją instancji: danymi SMTP, kluczami bramki SMS i kluczami dostawcy wideo. Tych wartości nie ma nigdzie indziej, bo konfiguracja poczty i SMS idzie wyłącznie przez panel, a nie przez .env. Do zatrzymania służy docker compose down bez flagi.
Pierwsze logowanie
Konto administratora powstaje automatycznie przy pierwszym starcie backendu, na podstawie zmiennych z .env.
Otwórz panel
Wejdź na http://localhost:8081. Aplikacja przekieruje na ekran logowania pod /logowanie.

Zaloguj się danymi z .env
Login to wartość ADMIN_EMAIL, hasło to ADMIN_PASSWORD. Jeśli hasła nie podano, aplikacja wylosowała je i wypisała raz w logu przy pierwszym starcie:
docker compose logs backend | grep -i has
Zmień hasło, jeśli aplikacja o to poprosi
Konto z hasłem wygenerowanym automatycznie ma ustawioną flagę wymuszającą zmianę przy pierwszym logowaniu.
Trzy pomyłki i konto czeka. Aplikacja liczy nieudane logowania: po pięciu próbach na ten sam adres konto jest blokowane na 15 minut. Wszystkie błędy logowania dają identyczny komunikat: złe hasło, nieznany adres i konto nieaktywne wyglądają tak samo, żeby nie dało się ustalić, które konta istnieją.
Co widzisz po zalogowaniu

Ekranem głównym panelu jest Podsumowanie pod adresem /. Pokazuje wyłącznie rzeczy, na które można zareagować: co się zbliża i co czeka na obsługę. Lewe menu ma następujące pozycje:
| Pozycja | Adres | Do czego służy | Rola |
|---|---|---|---|
| Podsumowanie | / | Nadchodzące wydarzenia i sprawy do obsługi | wszyscy |
| Webinary | /webinary | Lista wydarzeń, tworzenie i edycja | wszyscy |
| Kontakty | /kontakty | Baza kontaktów, grupy, import, zaproszenia | wszyscy |
| Zapytania | /zapytania | Zapytania ofertowe ze wszystkich webinarów | wszyscy |
| Banery | /banery | Reklamy globalne instancji | tylko ADMIN |
| Ustawienia | /ustawienia | Konfiguracja instancji | tylko ADMIN |
| Użytkownicy | /uzytkownicy | Konta panelu | tylko ADMIN |
Dwie role. ADMIN ma dostęp do wszystkiego. MODERATOR prowadzi webinary i obsługuje zapytania, ale nie widzi ustawień, banerów globalnych ani użytkowników. Ukrycie pozycji menu to wygoda, a nie zabezpieczenie: regułą jest kontrola po stronie backendu.
Konfiguracja instancji
Wszystko poza sekretami startowymi ustawia się w panelu, w /ustawienia. Zmiany działają natychmiast, bez restartu aplikacji.
Dlaczego przez panel, a nie przez .env. Konfiguracja poczty i SMS trafia do tabeli app_settings, dzięki czemu klient zmienia ją sam, bez dostępu do serwera i bez restartu. W .env zostają wyłącznie rzeczy potrzebne do wystartowania: sekret JWT, hasło bazy i klucze MinIO.
Podstrony ustawień
| Podstrona | Adres | Co ustawia |
|---|---|---|
| Aplikacja | /ustawienia | Nazwa marki, adres publiczny, strefa czasowa, wideo, pokój, limity |
| Grafika | /ustawienia/grafika | Logo i ikona karty przeglądarki, z kadrowaniem |
| /ustawienia/email | Serwer SMTP, nadawca, tryb bezpieczny, limit godzinowy | |
| SMS | /ustawienia/sms | Bramka SMS, klucz API, tryb bezpieczny |
| Przypomnienia | /ustawienia/przypomnienia | Domyślny harmonogram przypomnień dla nowych webinarów |
| Zgody | /ustawienia/zgody | Treść zgód i ich wersja |

Minimum do przeprowadzenia webinaru
Ustaw adres publiczny instancji
W /ustawienia pole APP_PUBLIC_URL. Lokalnie: http://localhost:8081.
To jest przedrostek każdego linku wysyłanego w mailu: potwierdzenia zapisu, przypomnienia, wejścia do pokoju. Nie ma wartości domyślnej i to jest celowe: adres localhost wstawiony automatycznie trafiłby do linków na produkcji i nic nie sygnalizowałoby błędu.
Nazwa marki i strefa czasowa
APP_BRAND_NAME pojawia się w panelu, na stronach publicznych i w mailach. APP_TIMEZONE to domyślna strefa nowych webinarów, standardowo Europe/Warsaw.
Skonfiguruj pocztę
W /ustawienia/email uzupełnij serwer SMTP, port, login, hasło i nazwę nadawcy. Bez działającej poczty uczestnik nie dostanie potwierdzenia zapisu ani linku do pokoju.
Pod formularzem jest test wysyłki: wpisz adres i wyślij wiadomość próbną. Test zwraca pełną treść błędu z serwera SMTP, więc od razu widać, czy problem jest w porcie, w SSL czy w haśle.
Sprawdź tryb bezpieczny
Zanim wyślesz cokolwiek, przeczytaj rozdział 5. Domyślnie cała wysyłka jest przekierowana na jeden adres i to jest zachowanie zamierzone.

Pole sekretne zachowuje się inaczej
Hasła i klucze API nigdy nie wracają z API. Pole jest zawsze puste, a obok widnieje oznaczenie „ustawione”, gdy wartość jest w bazie. Puste pole przy zapisie oznacza „nie zmieniaj”, a nie „wyczyść”. Dzięki temu zapis formularza nie kasuje hasła, którego nigdy nie widziałeś.
Tryb bezpieczny wysyłki
Najważniejszy rozdział dla testów. Domyślnie włączony bezpiecznik kieruje całą pocztę i wszystkie SMS-y na jeden wskazany adres.
Ten mechanizm powstał po realnym incydencie. Zadanie podsumowań potraktowało webinary z danymi testowymi jako zakończone i wysłało 12 wiadomości przez produkcyjny serwer SMTP klienta, w tym 10 na nieistniejące adresy. Dane testowe z terminem w przeszłości wystarczą, żeby scheduler ruszył.
Jak to działa
| Ustawienie | Domyślnie | Znaczenie |
|---|---|---|
| MAIL_SAFE_MODE | true | Cała poczta idzie na adres z MAIL_SAFE_RECIPIENT |
| MAIL_SAFE_RECIPIENT | puste | Adres zastępczy odbiorcy |
| SMS_SAFE_MODE | true | Wszystkie SMS-y na numer z SMS_SAFE_RECIPIENT |
| MAIL_RATE_LIMIT_PER_HOUR | 100 | Bezpiecznik ilościowy poczty |
| SMS_RATE_LIMIT_PER_HOUR | 20 | Bezpiecznik ilościowy SMS |
Tryb włączony bez ustawionego adresu wstrzymuje wysyłkę, zamiast przepuścić ją do prawdziwego odbiorcy. Jeśli maile „nie wychodzą”, sprawdź najpierw, czy MAIL_SAFE_RECIPIENT jest uzupełnione. Cicha wysyłka przy włączonym zabezpieczeniu byłaby najgorszym z możliwych zachowań: administrator jest wtedy przekonany, że nic nie wychodzi.
Sprawdzenie stanu przed testem
Ustawienia zmieniają się przez panel, bez restartu aplikacji, więc wartość sprzed kilku minut może już nie obowiązywać. Przed każdym krokiem, który może wysłać wiadomość, sprawdź faktyczny stan w bazie:
docker exec webinar-postgres psql -U webinar -d webinar -c \
"SELECT \"key\", value FROM app_settings
WHERE \"key\" LIKE '%SAFE%' OR \"key\"='SMS_ENABLED';"
Zalecenie na czas testów lokalnych: zostaw oba tryby bezpieczne włączone i ustaw w nich swój własny adres oraz numer. Zobaczysz każdą wiadomość, którą system chciał wysłać, i nikt postronny jej nie dostanie.
Co zapisuje dziennik
Wpis w tabeli notifications zachowuje prawdziwego odbiorcę, nie zastępczego. Dziennik dokumentuje zamiar wysyłki, więc po wyłączeniu trybu bezpiecznego nadal wiadomo, kto co miał dostać.
Przekroczenie limitu to nie błąd
Wiadomość ponad limit godzinowy dostaje status oczekującej, a nie nieudanej. Ma pójść, tylko później: zadanie w tle podejmie ją przy kolejnym przebiegu. Zaproszenie na całą bazę kontaktów to największy jednorazowy ruch w aplikacji, więc to tam bezpiecznik zadziała najczęściej.
Webinar testowy: komplet danych do wpisania
Wszystkie kolejne rozdziały pokazują jeden konkretny webinar: szkolenie o ocenie ryzyka i bezpieczeństwie wody po nowelizacji z 13 marca 2026 r. Ten rozdział zbiera wartości do wpisania, a następne pokazują je na zrzutach z działającej instancji.
Dlaczego akurat ten temat. Szkolenie o zmianie przepisów jest typowym zastosowaniem tej platformy: ma ostry termin wynikający z kalendarza ustawowego, precyzyjnie określoną grupę odbiorców i naturalny powód, by zbierać zapytania ofertowe. Sprawdza więc te mechanizmy, które przy sztucznych danych testowych zwykle zostają nietknięte.
Zakładka Podstawowe
| Tytuł | Ocena ryzyka i bezpieczeństwo wody - obowiązki, terminy i sankcje 2026 |
| Adres strony zapisu | ocena-ryzyka-i-bezpieczenstwo-wody-obowiazki-terminy-i-sankcje-2026 |
| Krótki opis | Trzy warstwy oceny ryzyka, kalendarz terminów i katalog kar po nowelizacji z 13 marca 2026 r. Dla wodociągów, zakładów przemysłowych i zarządców obiektów priorytetowych. |
Adresu nie wpisuj ręcznie. Powstaje sam z tytułu: polskie znaki są transliterowane, spacje zamieniane na łączniki, a konflikt z istniejącym adresem rozstrzyga sufiks liczbowy. Powyższa wartość to dokładnie to, co wygeneruje aplikacja.
Zakładka Termin
| Strefa czasowa | Europe/Warsaw |
| Rozpoczęcie | dziś, za 10 minut (do testu) |
| Zakończenie | dziś, za 70 minut (godzina szkolenia) |
Start za 10 minut to celowy wybór. Okno wejścia otwiera się 15 minut przed startem, więc pokój jest dostępny natychmiast, a termin pozostaje w przyszłości i nie uruchamia zadania podsumowań. Przy prawdziwym szkoleniu wpisz oczywiście rzeczywistą datę.
Zakładka Zapisy
| Zapisy otwarte | włączone |
| Limit miejsc | 50 |
| Termin zapisów | puste (bez terminu granicznego) |
| Link do spotkania | puste - pokój zakłada LiveKit |
Zakładka Treść strony
Treść buduje program szkolenia z materiału źródłowego. Struktura, która sprawdza się w praktyce: dla kogo, program, podstawa prawna, zastrzeżenie.
| Dla przedsiębiorstw wodociągowo-kanalizacyjnych, zakładów przemysłowych z własnym ujęciem wody oraz właścicieli i zarządców obiektów priorytetowych, w których ponad 50 osób dziennie jest narażonych na ryzyko związane z wodą. |
| 1 | Trzy warstwy oceny ryzyka: obszar zasilania ujęcia, system zaopatrzenia w wodę, wewnętrzny system wodociągowy. |
| 2 | Kto podlega i od jakiego progu: 10 m3 na dobę, 50 osób, 500 osób, 100 m3 na dobę. |
| 3 | Kalendarz terminów: 31 grudnia 2026 r. dla obszaru zasilania, 30 czerwca 2028 r. dla pozostałych warstw. |
| 4 | Katalog sankcji: 10 zł za każdy dzień zwłoki, pasmo do 60 000 zł dla dostawcy wody, do 10 000 zł dla obiektu priorytetowego. |
| 5 | Kto może wykonać ocenę ryzyka wewnętrznego systemu wodociągowego: wymagania kwalifikacyjne z art. 4l. |
| 6 | Sześć kwestii, które przemysł musi rozstrzygnąć przed 31 grudnia 2026 r. |
| 7 | Najczęstsze błędy w opracowaniach obiegowych i jak ich uniknąć w dokumentacji. |
| Materiał opiera się na ustawie z dnia 13 marca 2026 r. (Dz.U. 2026 poz. 605), obowiązującej od 21 maja 2026 r., oraz na rozporządzeniu Ministra Zdrowia z dnia 22 maja 2026 r. (Dz.U. 2026 poz. 748). | |
| Szkolenie nie stanowi porady prawnej. Kwalifikacja konkretnego podmiotu jako dostawcy wody lub obiektu priorytetowego wymaga potwierdzenia u właściwego organu nadzoru sanitarnego. |
Listę numeruje edytor, nie Ty. Wpisanie własnych numerów na początku pozycji daje na stronie „2. 2.”. Użyj przycisku listy numerowanej i wpisuj samą treść. To zdarzyło się przy przygotowaniu tej dokumentacji i widać było na pierwszym zrzucie.
Zakładka Wideo
| Dostawca | Domyślny z instancji (LiveKit) |
| Pokój | przycisk „Utwórz pokój u dostawcy” |
| Prelegent: imię i nazwisko | Dr inż. Anna Wiśniewska |
| Prelegent: adres e-mail | prelegent@example.test |
Zakładka Przypomnienia
Przy szkoleniu z terminem ustawowym harmonogram domyślny instancji jest wystarczający:
| 24 godziny przed | |
| 1 godzina przed | |
| 15 minut przed | SMS (tylko za zgodą i z numerem telefonu) |
Dane uczestnika testowego
| Imię | Piotr |
| Nazwisko | Zatorski |
| Adres e-mail | pzators@go2.pl |
| Telefon | 453193605 |
| Firma | SoftSPM |
| Zgody | wszystkie trzy zaznaczone |
Użyj własnego adresu i numeru. Przy włączonym trybie bezpiecznym i tak wszystko trafi na adres zastępczy, ale zapis z prawdziwym adresem pozwala sprawdzić, czy wiadomość dotarła i jak wygląda.
Materiał źródłowy
Treść merytoryczna pochodzi z opracowania „Plany bezpieczeństwa wody i ocena ryzyka - podstawy prawne, terminy i sankcje”, stan prawny na 27 sierpnia 2026 r. Kluczowe liczby, przydatne przy budowaniu zadań i tematów zapytań:
| Wartość | Znaczenie |
|---|---|
| 31.12.2026 | Pierwsza ocena ryzyka obszaru zasilania ujęcia - najbliższy twardy termin |
| 30.06.2028 | Pierwsza ocena systemu zaopatrzenia i wewnętrznego systemu wodociągowego |
| 50 osób | Próg obiektu priorytetowego: powyżej tej liczby dziennie narażonych na ryzyko |
| 10 zł / dzień | Kara za zwłokę w przekazaniu oceny, maksymalnie za 365 dni (3 650 zł) |
| do 60 000 zł | Pasmo kar dla dostawcy wody za naruszenia operacyjne |
| do 10 000 zł | Pasmo kar dla właściciela lub zarządcy obiektu priorytetowego |
| 12 lat | Okres przechowywania oceny ryzyka wewnętrznego systemu wodociągowego |
Utworzenie webinaru
Formularz jest podzielony na zakładki, ale zapis jest jeden. Przechodzenie między zakładkami nie gubi niezapisanych zmian.
Wejdź w Webinary i kliknij Nowy webinar (adres /webinary/nowy).

Trzy zakładki są dostępne dopiero po zapisaniu. Nagrania i materiały, Przypomnienia oraz Wideo wymagają istniejącego webinaru, bo pliki, reguły i pokój muszą być do czegoś przypisane. Przy tworzeniu zobaczysz w nich komunikat wyjaśniający.
Zakładka: Podstawowe
| Pole | Wymagane | Opis |
|---|---|---|
| Tytuł | tak | Maksymalnie 255 znaków. Widoczny wszędzie: na stronie zapisu, w mailach, na certyfikacie |
| Adres strony zapisu | nie | Końcówka adresu po /w/. Podpowiadana z tytułu, dopóki jej nie dotkniesz. Konflikty rozstrzyga backend sufiksem liczbowym |
| Krótki opis | nie | Jedno, dwa zdania bez formatowania. Trafia do zajawki linku w komunikatorach i do treści maili |
Przy adresie strony są przyciski Kopiuj oraz, w trybie edycji, Otwórz. Jeśli nie ustawiłeś adresu publicznego instancji, pojawi się tu ostrzeżenie.
Opis to nie treść strony. To dwie różne rzeczy: opis jest krótką zajawką, a treść strony to długi materiał z edytora. Nie zastępuj jednego drugim.
Zakładka: Treść strony

Edytor wizualny, w którym buduje się zawartość publicznej strony zapisu: nagłówki, listy, obrazki, linki i wyrównanie. Treść pojawia się nad formularzem zapisu. Pusta treść oznacza stronę z samym formularzem.
Obrazki wstawia się z biblioteki mediów. Trafiają do MinIO, a do treści wchodzi adres, nigdy dane w base64.
Treść jest czyszczona na backendzie przy każdym zapisie, według białej listy. Do bazy trafia wyłącznie wersja oczyszczona. Obrazek z obcego serwera jest usuwany, bo ujawniałby adresy IP odwiedzających właścicielowi obcego serwera.
Zakładka: Termin
Godziny podaje się w strefie webinaru, a nie w strefie przeglądarki. Strefa jest wypisana przy każdym polu.
| Pole | Wymagane | Opis |
|---|---|---|
| Strefa czasowa | nie | Domyślnie Europe/Warsaw |
| Rozpoczęcie | tak | Data i godzina startu |
| Zakończenie | tak | Musi być późniejsze niż rozpoczęcie. Wyznacza moment startu automatów po webinarze |

Do testu ustaw termin w przyszłości. Termin w przeszłości uruchamia zadanie podsumowań i zamyka zapisy. Jeśli chcesz od razu wejść do pokoju, patrz scenariusz w rozdziale 12.
Zakładka: Zapisy
| Pole | Opis |
|---|---|
| Zapisy otwarte | Przełącznik, domyślnie włączony. To intencja, a nie stan faktyczny |
| Termin zapisów | Nie może być późniejszy niż początek webinaru. Puste oznacza brak terminu granicznego |
| Limit miejsc | Liczba całkowita, minimum 1. Puste oznacza brak limitu |
| Link do spotkania | Wysyłany w mailu potwierdzającym. Nie jest pokazywany publicznie. Wypełniany ręcznie w trybie NONE |

Przełącznik „zapisy otwarte” to intencja, a nie stan faktyczny. Aplikacja nigdy nie zmienia go sama. Rzeczywistą dostępność wylicza z intencji, terminu, limitu miejsc i statusu, dzięki czemu po rezygnacji uczestnika zapisy wracają do stanu ustawionego przez Ciebie, zamiast otwierać się same.
Limit miejsc jest pilnowany pod blokadą wiersza w bazie. Kilkadziesiąt równoczesnych zapisów na dziesięć miejsc skończy się dokładnie dziesięcioma wpisami.
Zakładka: Przypomnienia
Lista reguł, każda z odstępem przed startem i kanałem. Nowy webinar dostaje harmonogram domyślny z ustawień instancji, a tu można go zmienić dla konkretnego wydarzenia.
Odstęp wybiera się z gotowych wartości albo podaje własny w dniach, godzinach lub minutach. Pod spodem widać skutek zwykłym zdaniem, na przykład „Zostanie dodane przypomnienie 7 dni przed, wysyłane przez e-mail”.
SMS wymaga zgody marketingowej i numeru telefonu. Uczestnik bez zgody dostanie tylko przypomnienia mailowe. Treść SMS jest celowo bez polskich znaków: ogonki przełączają bramkę na kodowanie, w którym limit spada ze 160 do 70 znaków i jedna wiadomość rozbija się na kilka płatnych części.
Przypomnienie spóźnione bardziej niż okno tolerancji (domyślnie 30 minut) nie idzie w ogóle. Chroni to przed lawiną wysyłek wstecz po restarcie aplikacji.
Pozostałe zakładki

Wideo - wybór dostawcy dla tego webinaru, zakładanie pokoju i lista prelegentów. Opisane w rozdziale 8.
Nagrania i materiały - wgrywanie plików do własnego składu, z ustawieniem widoczności.
Po webinarze - adresy nagrania i materiałów, gdy pliki leżą poza systemem (dysk sieciowy, YouTube), oraz przycisk Wyślij powiadomienie o nagraniu.
Przycisk powiadomienia działa na zapisanym adresie nagrania, a nie na treści pola. Inaczej powiadomienie niosłoby link sprzed edycji.
Walidacja
Formularz sprawdza dane przed wysłaniem i pokazuje zbiorczy komunikat „Formularz zawiera błędy”. Reguły:
- tytuł nie może być pusty,
- oba terminy są wymagane, a zakończenie musi być późniejsze niż rozpoczęcie,
- termin zapisów nie może być późniejszy niż początek webinaru,
- limit miejsc musi być liczbą dodatnią.
Błąd walidacji na nieaktywnej zakładce jest widoczny. Zakładka z błędem dostaje kropkę, a próba zapisu przeskakuje na pierwszą wadliwą. Komunikat nigdy nie wskazuje pola, którego nie widać.

Zapis i status
Po zapisaniu webinar ma status DRAFT. Nie jest widoczny publicznie: strona zapisu zwraca 404, a zajawka dla robotów też go nie pokaże. To stan roboczy, w którym można spokojnie dopracować treść.
Wideo i prelegenci
Platforma nie jest przywiązana do jednego dostawcy wideo. Wybór jest świadomą decyzją, bo dostawcy różnią się tym, czy uprawnienia są egzekwowane, czy jedynie deklarowane.
Co gwarantuje który dostawca
| Dostawca | Uprawnienia egzekwuje | Rozróżnia audio i wideo | Nagrywanie |
|---|---|---|---|
| LIVEKIT | serwer, na podstawie podpisanego tokenu | tak | Wymaga kontenerów Egress i Redis |
| DAILY | serwer | tak | Pobierane od dostawcy po zakończeniu |
| JITSI z JWT | serwer, ale tylko rolę moderatora | nie | Ręczne wgranie pliku |
| JITSI bez JWT | nikt: pokój otwarty | nie | Ręczne wgranie pliku |
| NONE | nie dotyczy, obraz poza platformą | nie dotyczy | Ręczne wgranie pliku |
Przy Jitsi bez JWT role są umowne. Pokój jest otwarty dla każdego, kto zna adres, a ukrycie przycisku kamery w interfejsie to jedyne, co dzieli uczestnika od publikacji: wystarczy otworzyć ten sam pokój wprost na domenie Jitsi, żeby to obejść. Panel ostrzega o tym przy wyborze dostawcy. Kto potrzebuje gwarancji, wybiera LiveKit.
Tryb NONE jest równoprawny, nie awaryjny. Instancja bez dostawcy w pełni obsługuje webinary: organizator wkleja własny link do spotkania, a czat, pytania, zadania i banery zostają w platformie. Wielu klientów zostaje przy tym na stałe.
Trzy role w pokoju
| Rola | Uprawnienia |
|---|---|
| Prelegent | Publikuje obraz i dźwięk |
| Uczestnik | Tylko odbiera, ma czat i pytania |
| Uczestnik z głosem | Publikuje wyłącznie dźwięk, po zgodzie moderatora |
Wideo uczestników jest zawsze wyłączone na poziomie uprawnień w tokenie, także po podniesieniu do roli z głosem. Przy LiveKit uczestnik nie włączy kamery, choćby podmienił kod strony albo wszedł własnym klientem: serwer odrzuci strumień.
Założenie pokoju
Otwórz zakładkę Wideo w formularzu webinaru
Możesz zostawić dostawcę instancji albo wybrać innego tylko dla tego wydarzenia.
Kliknij „Utwórz pokój u dostawcy”
Konfiguracja jest sprawdzana od razu, żebyś zobaczył brak klucza teraz, a nie dowiedział się o nim od uczestnika minutę przed startem.
W trybie NONE nie ma czego zakładać: zamiast tego wklej własny link w polu „Link do spotkania” w zakładce Zapisy. Panel ostrzeże, jeśli link jest pusty, bo uczestnik nie miałby wtedy gdzie wejść.
Dodaj prelegentów
W sekcji Prelegenci podaj Imię i nazwisko oraz Adres e-mail, po czym kliknij Dodaj prelegenta. Aplikacja wygeneruje indywidualny link wejścia.
Adres służy wyłącznie do odróżnienia prelegentów. Zaproszenie wysyłasz sam, kopiując link przyciskiem Kopiuj link.


Prelegent nie jest uczestnikiem. Nie przechodzi przez formularz zapisu i nie wyraża zgód, więc nie liczy się do liczby zapisanych ani do statystyk zgód. Wchodzi bez ograniczenia okna czasowego, bo musi sprawdzić sprzęt przed uczestnikami.

Podniesienie głosu zapisanemu uczestnikowi
Poza prelegentami można nadać prawo głosu osobie, która zapisała się normalnie. Służy do tego kolumna Głos na liście zapisanych. Taki uczestnik dostaje prawo publikacji wyłącznie dźwięku: uprawnienia moderatora ma tylko prelegent wchodzący własnym linkiem.
Dwie kolumny dostawcy, nie jedna
Webinar pamięta dwie różne rzeczy: kto ma go obsługiwać (wybór administratora) i u kogo pokój faktycznie stoi (stan zapisany przez aplikację). Rozbieżność między nimi nie jest błędem, tylko sygnałem: „zmieniono dostawcę, pokój wciąż stoi u poprzedniego”. Panel pyta wtedy o potwierdzenie i zamyka stary pokój.
Publikacja i zapisy
Publikacja otwiera stronę zapisu dla świata. Do tego momentu wydarzenie jest niewidoczne.
Statusy webinaru
| Status | Znaczenie | Strona publiczna |
|---|---|---|
| DRAFT | Wersja robocza, stan początkowy | 404, także dla robotów |
| PUBLISHED | Opublikowany, zapisy wg reguł dostępności | Widoczna |
| CANCELLED | Odwołany, zapisy zamykają się same | Widoczna z informacją o odwołaniu |
| FINISHED | Status istnieje w modelu, ale nic go nie ustawia | - |
Podział na wydarzenia aktywne i zakończone idzie po terminie zakończenia, nie po statusie. Zakładka „Zakończone” na liście pokazuje webinary po ich terminie końca. Filtrowanie po statusie FINISHED dawałoby zawsze pustą listę, bo nic w aplikacji tego statusu nie ustawia.
Warunki publikacji
Przycisk publikacji jest w wierszu listy webinarów, widoczny dopóki wydarzenie nie jest opublikowane ani odwołane. Publikacja przechodzi tylko wtedy, gdy webinar ma:
- niepusty tytuł,
- niepusty adres strony zapisu,
- oba terminy uzupełnione,
- status inny niż odwołany - webinaru odwołanego nie da się opublikować ponownie.
Sprawdź stronę przed publikacją
Ikona oka w wierszu listy otwiera kartę webinaru /webinary/{id} w nowej karcie przeglądarki. Zakładka „Treść strony” pokazuje dokładnie to, co zobaczy uczestnik.
Opublikuj
Status zmienia się na PUBLISHED, strona zapisu staje się dostępna, a w wierszu listy pojawia się ikona Prowadź spotkanie.
Otwórz stronę zapisu
Adres to http://localhost:8081/w/{slug}. Ta sama strona, którą dostanie uczestnik.

Karta webinaru
Karta pod /webinary/{id} zbiera wszystko, co należy do jednego wydarzenia, w ośmiu zakładkach:
| Zakładka | Zawartość |
|---|---|
| Treść strony | Podgląd tego, co widzi uczestnik |
| Zaproszenia | Wysyłka do kontaktów z bazy, z podglądem liczby odbiorców |
| Zapisani | Lista uczestników z licznikiem |
| Banery | Reklamy tego webinaru |
| Certyfikaty | Wydawanie i unieważnianie dokumentów |
| Statystyki | Obecność, aktywność, skuteczność |
| Zapytania ofertowe | Kontakty handlowe z tego wydarzenia, z licznikiem |
| Historia czatu | Archiwum rozmowy, z wiadomościami usuniętymi przez moderatora |

Zapis uczestnika
Uczestnik podaje dane, zaznacza zgody i wysyła formularz. Po zapisie dostaje mail z potwierdzeniem, załącznikiem kalendarza invite.ics i linkiem do własnego zapisu pod /zapis/{token}, gdzie może sprawdzić szczegóły albo zrezygnować.

Zgody
Żadna zgoda nie jest zaznaczona domyślnie, także ta wymagana. Zaznaczanie zgody za użytkownika jest niedopuszczalne.
Zgody są dopisywane, nigdy nadpisywane. Ponowny zapis tworzy nowe wpisy, a stare zostają jako ślad rozliczalny. Zapisywana jest także odmowa.
Brak enumeracji uczestników
Przy próbie zapisu na już zapisany adres API zwraca tę samą odpowiedź co przy sukcesie i wysyła mail informujący, że zapis już istnieje.
Gdyby zwracało błąd, dałoby się sprawdzać, kto jest zapisany na dane szkolenie.
Link do spotkania nie trafia do publicznego API. Idzie wyłącznie do maila potwierdzającego i do podglądu własnego zapisu po tokenie, i tylko wtedy, gdy zapis nie jest anulowany.


Rezygnacja z udziału jest możliwa ze strony własnego zapisu. Ponowny zapis po rezygnacji jest dozwolony, a zakolejkowane przypomnienia są przy rezygnacji kasowane.
Zaproszenia z bazy kontaktów
Poza publiczną stroną zapisu można zaprosić kontakty z bazy. Zakładka Zaproszenia pozwala wybrać grupy, zobaczyć podgląd liczby odbiorców po odfiltrowaniu wypisanych, osób bez zgody i już zaproszonych, a potem wysłać.
Jedno zaproszenie na osobę i wydarzenie. Powtórna wysyłka po dołożeniu kontaktów do grupy nie zaprosi drugi raz tych, którzy już dostali wiadomość. Warunki są sprawdzane ponownie tuż przed wysyłką: kontakt mógł się wypisać między zakolejkowaniem a wysłaniem.
Każda wiadomość marketingowa ma stopkę z linkiem wypisania. Maile transakcyjne - potwierdzenie, przypomnienie, podsumowanie - jej nie mają i nie podlegają wypisaniu: to obsługa zamówionej usługi, a nie marketing.
Przeprowadzenie webinaru
W dniu wydarzenia pracujesz na dwóch ekranach: sali spotkania i panelu prowadzenia. Uczestnik ma jeden ekran, na którym ma wszystko.
Okno czasowe pokoju
Uczestnik wchodzi od 15 minut przed startem do 30 minut po zakończeniu (wartości domyślne, do zmiany w ustawieniach). Poza tym oknem dostaje czytelny komunikat z odliczaniem „Pokój otworzy się za...”, a strona odświeży się sama, gdy nadejdzie pora. Prelegent wchodzi bez tego ograniczenia.
To najczęstsza przyczyna zgłoszenia „link nie działa” przy poprawnie skonfigurowanej instancji.
Ekran uczestnika
Adres /spotkanie/{token} jest wspólny dla uczestnika i prelegenta: strona sama rozpoznaje, kto wchodzi, więc organizator nie musi pamiętać, który link komu wysłał.
Przedpokój
Najpierw uczestnik widzi tytuł, godzinę w strefie webinaru i ramkę „Wchodzisz jako” z imieniem i nazwiskiem z zapisu. Prelegent ma tam dodatkowo oznaczenie roli.
Link otwiera się bez logowania, więc bez tego ekranu osoba z przekazanym linkiem zaczynałaby występować pod cudzym nazwiskiem, nie mając szansy tego zauważyć.
Wejście na salę
Uczestnik klika Dołącz do webinaru, prelegent Wejdź i rozpocznij. Pod przyciskiem jest informacja, że mikrofon i kamera są wyłączone, a o głos można poprosić w trakcie.
Sala
Obraz spotkania i panel interakcji obok. Nad panelem jest pasek akcji, widoczny zawsze, niezależnie od otwartej zakładki: schowany w zakładce byłby bezużyteczny dokładnie wtedy, gdy jest potrzebny.


Pasek akcji uczestnika
| Przycisk | Zachowanie |
|---|---|
| Chcę zabrać głos | Zmienia się w „Czekasz na głos” z możliwością wycofania, a po przyznaniu w „Masz głos, mikrofon jest włączony” z opcją oddania. Przy otwartym mikrofonie zamiast przycisku jest napis „Możesz mówić bez zgłaszania się” |
| Proszę o kontakt | Zapytanie bez pisania treści. Uczestnik w trakcie webinaru często nie ma pomysłu, co napisać, a wymuszanie treści kończy się tym, że nie wysyła nic |
| Interesuje mnie oferta | Pełny formularz zapytania, w oknie, bez opuszczania sali |
Po wysłaniu przycisk pokazuje potwierdzenie, a kolejne zapytanie wymaga świadomego kliknięcia „Wyślij kolejne”. Bez widocznej zmiany uczestnik kliknąłby trzeci raz, a handlowiec dostałby trzy zgłoszenia od tej samej osoby.
Zgoda na kontakt handlowy jest wymagana tak samo w obu trybach: uproszczenie dotyczy tego, ile uczestnik pisze, a nie tego, na co się zgadza.
Cztery zakładki panelu uczestnika
| Zakładka | Zawartość |
|---|---|
| Czat | Rozmowa na żywo. Autor pokazany jako imię i pierwsza litera nazwiska, nigdy adres e-mail |
| Pytania | Pytania do prowadzącego, z głosowaniem. Jedna osoba głosuje raz |
| Zadania | Zapisy na zadania ogłoszone podczas webinaru, z limitem miejsc |
| Oferta | Zapytanie ofertowe |
Licznik przy zakładce pokazuje nowe pozycje od ostatniego zajrzenia, a nie łączną liczbę wpisów. Łączna liczba przy czacie rośnie cały czas i po kwadransie nie niesie żadnej informacji.

W trybie NONE panel interakcji zostaje. Zamiast osadzonego obrazu jest przycisk otwierający spotkanie w nowej karcie, ale czat, pytania, zadania i banery działają dalej u nas. To jest sedno tego trybu: bez dostawcy platforma nadal ma sens.
Panel prowadzenia
Adres /webinary/{id}/prowadzenie, wejście ikoną w wierszu listy. Nagłówek pokazuje stan połączenia i ma przełącznik sygnału dźwiękowego. Poniżej cztery kolumny, każda to niezależny strumień pracy:
Zgłoszenia do głosu
Osoby z przyznanym głosem na górze („Mówi od...”), pod nimi oczekujący, najstarsi na górze, z informacją, ile już czekają. Akcje: Daj głos, Odrzuć, Odbierz.
Tu też jest przełącznik otwartego mikrofonu.
Czat na żywo
Podgląd rozmowy, wypowiedzi prelegenta wyróżnione. Ikona kosza usuwa wiadomość, zostawiając ślad dla moderatora zamiast kasować wpis.
Kolejka pytań
Najpierw sekcja oczekujących na zatwierdzenie: Zatwierdź albo Odrzuć. Opublikowane są sortowane: przypięte pierwsze, potem po liczbie głosów.
Do każdego można przypiąć, ukryć albo wpisać odpowiedź widoczną dla uczestników.
Zadania
Lista zadań z licznikiem zapisanych i limitem, z możliwością zamknięcia i otwarcia zapisów. Na dole formularz: tytuł, opcjonalny limit miejsc i przycisk Ogłoś.

Pytanie oczekujące nie jest widoczne dla uczestników. To jedyna bariera przed pokazaniem niezatwierdzonej treści całej sali, więc kolejka wymaga świadomego zatwierdzenia każdej pozycji.
Gdy przyjdzie nowe zapytanie ofertowe, na górze panelu pojawia się baner z odnośnikiem do listy zapytań. Jest też sygnał dźwiękowy, domyślnie włączony: to zgłoszenie, którego prowadzący najbardziej nie chce przegapić, a w trakcie mówienia nie patrzy na ekran.
Przeglądarki blokują dźwięk do pierwszej interakcji użytkownika. Kliknij cokolwiek w panelu, żeby sygnał zaczął działać. Wybór jest zapamiętywany w przeglądarce.
Tryb otwartego mikrofonu
Przełącznik pozwala mówić każdemu bez proszenia o głos. Domyślnie wyłączony i to jest świadome: otwarty mikrofon na webinarze dla stu osób oznacza sto otwartych mikrofonów naraz. Ma sens przy warsztacie dla kilkunastu osób.
Włączenie pyta o potwierdzenie, wyłączenie nie: otwarcie mikrofonów całej sali jest słyszalne natychmiast i trudno to cofnąć po fakcie.
Włączenie nie kasuje kolejki. Przy włączonym trybie jest ona nieaktywna, a nie nieaktualna, więc po wyłączeniu osoby czekające od kwadransa nie tracą miejsca.
Wyłączenie nie odbiera głosu osobom, którym przyznano go indywidualnie: ich uprawnienie wynika z roli w zapisie, a nie z trybu.
Wideo pozostaje wyłączone także w tym trybie: otwarty mikrofon nie włącza nikomu kamery.
Obecność
Obecność liczy się z sesji, a nie z flagi. Ludzie wchodzą i wychodzą, tracą połączenie, wracają z telefonu; dopiero suma sesji mówi, ile ktoś naprawdę był obecny, i pozwala narysować wykres obecności w czasie.
Przeglądarka wysyła sygnał co minutę istniejącym już połączeniem, więc działa to także w trybie NONE, gdzie żadnego dostawcy nie ma. Sesje porzucone - zamknięta karta bez wylogowania - domyka zadanie w tle, przyjmując jako koniec ostatni sygnał, a nie chwilę zamknięcia.
Po webinarze
Część dzieje się sama, część wymaga decyzji organizatora.
Co dzieje się automatycznie
Godzinę po zakończeniu (wartość SUMMARY_DELAY_MINUTES) zadanie w tle kolejkuje maile: uczestnikom podziękowanie z listą zadań i linkami, prowadzącemu zestawienie.
Podsumowanie dostaje tylko uczestnik ze statusem potwierdzonym. Osoba, która zrezygnowała, nie dostanie maila po wydarzeniu.
Podsumowania nie da się wysłać drugi raz i to jest celowe: uczestnik nie ma dostać dwa razy tej samej wiadomości. Gwarancję „dokładnie raz” daje ograniczenie w bazie, a nie samo sprawdzenie w kodzie.
Zestawienie dla prowadzącego zawiera dane kontaktowe, więc idzie wyłącznie na adresy wpisane w ustawieniach, nigdy na adres wzięty z formularza.
Nagranie
Wgraj plik albo poczekaj na pobranie
Przy LiveKit z Egress nagranie trafia prosto do MinIO. Przy pozostałych dostawcach wgrywasz plik ręcznie w zakładce Nagrania i materiały. Dopuszczone są MP4 i WEBM, a typ jest rozpoznawany z zawartości pliku, nie z rozszerzenia.
Ustaw widoczność
Trzy poziomy: prywatne, dla zapisanych i publiczne. Domyślnie dla zapisanych, a nie publicznie, bo nagranie zawiera wypowiedzi uczestników i pytania z imionami: udostępnienie go całemu internetowi musi być świadomą decyzją.
Powiadom uczestników
Przycisk w zakładce „Po webinarze”. To osobna wysyłka, bo montaż zwykle trwa dłużej niż godzina, a podsumowania nie da się powtórzyć. Każdy odbiorca dostaje ją dokładnie raz.
Pliki idą przez backend, nie prosto z MinIO. Dzięki temu każde pobranie przechodzi przez kontrolę widoczności. Token musi dotyczyć tego webinaru, a po rezygnacji przestaje działać natychmiast.
Własny skład ma pierwszeństwo przed adresem zewnętrznym. Mail najpierw szuka nagrania w składzie, a dopiero potem sięga po wpisany adres.
Certyfikaty
Zakładka Certyfikaty na karcie webinaru. Można wydać dokument pojedynczej osobie albo wszystkim naraz.
Certyfikat to dokument, nie widok danych. Plik powstaje raz i leży w składzie: wydany dziś ma wyglądać tak samo za rok, niezależnie od tego, czy ktoś zmienił tytuł webinaru, szablon albo logo.
Automat wydaje wyłącznie uczestnikom z odnotowaną obecnością. Certyfikat dla osoby bez obecności jest decyzją organizatora, a nie automatu; panel pyta o nią wprost.
Unieważnienie nie kasuje dokumentu. Weryfikacja pokaże wtedy „nieważny”, a nie „nie znaleziono”, bo dokument mógł już trafić do teczki uczestnika albo do pracodawcy. Unieważniony certyfikat znika za to ze strony uczestnika i z maila.
Weryfikacja publiczna pod /certyfikat/{numer} zwraca minimum: numer, tytuł, datę, imię z pierwszą literą nazwiska i informację o ważności. Endpoint jest otwarty dla całego internetu, więc nigdy nie pokazuje adresu e-mail, pełnego nazwiska ani linku do pliku.
Dostęp do pliku certyfikatu jest węższy niż do nagrania. Nagranie jest wspólne dla webinaru, więc wystarcza token dowolnego uczestnika. Certyfikat to dokument jednej osoby, z jej imieniem i nazwiskiem, więc token musi wskazywać właśnie ją.

Statystyki
Zakładka Statystyki pokazuje liczby zapisów i obecnych, wykres obecności w czasie, aktywność na czacie oraz skuteczność banerów i zaproszeń. Wszystko liczone w bazie, zapytaniami agregującymi.
Wykres obecności kończy się na chwili bieżącej, gdy webinar jeszcze trwa: rysowanie do przyszłego końca dawałoby długą linię zer. Skuteczność zaproszeń liczy się po wysłanych, nie po zakolejkowanych: zaproszenie, które nie wyszło, nie miało szansy przynieść zapisu.

Eksport listy zapisanych
Na liście zapisanych jest eksport CSV. Obejmuje wszystkich zapisanych, a nie bieżącą stronę, i zawiera czas obecności oraz wersję i datę zgody. Flaga obecności jest przy okazji przeliczana, żeby liczba na ekranie nie rozjechała się z kolumną w pliku.

Kopiowanie webinaru
Przy szkoleniach cyklicznych najczęstszą czynnością jest skopiowanie istniejącego wydarzenia ikoną kopiowania w wierszu listy.
Co przechodzi
Treść strony, treść zaproszenia, ustawienia certyfikatów, reguły przypomnień, tematy zapytań i materiały. Reguła wyłączona świadomie zostaje wyłączona także w kopii.
Czego nie ma w kopii
Zapisy, obecność, interakcje, nagrania, certyfikaty i pokój u dostawcy. Skopiowanie czegokolwiek z tej listy oznaczałoby uczestników zapisanych na wydarzenie, które się nie odbyło, albo certyfikaty z cudzego szkolenia.
Kopia dostaje termin zastępczy: pełną godzinę za tydzień, o długości przepisanej z oryginału. Data jest w przyszłości, a nie w przeszłości, żeby przy przypadkowej publikacji zapisy się otworzyły, a nie zamknęły natychmiast. Panel mówi o tym wprost i od razu przechodzi do edycji kopii.
Usunięcie webinaru
Panel najpierw sprawdza, ile przepadnie: liczbę zapisów i wydanych certyfikatów, i dopiero z tymi liczbami pyta o potwierdzenie. Przy webinarze ze setką zapisów odpowiedź jest zupełnie inna niż przy pustym szkicu.
Pytanie podpowiada odwołanie jako łagodniejszą alternatywę, bo w większości przypadków chodzi o zamknięcie zapisów, a nie skasowanie danych. Pliki w MinIO zostają: utrata pliku jest gorsza niż zajęte miejsce.
Scenariusz testowy na localhost
Gotowa ścieżka, która przechodzi przez wszystkie mechanizmy w jednym posiedzeniu, w około 30 minut.
Wartości do wpisania są w rozdziale 6. Ten rozdział podaje kolejność czynności, a nie treść pól: komplet danych webinaru o ocenie ryzyka i bezpieczeństwie wody zebrany jest w jednym miejscu, żeby dało się go przepisać bez przewijania instrukcji.
Zanim zaczniesz: potwierdź, że tryby bezpieczne są włączone i mają ustawiony Twój adres oraz numer (rozdział 5). Cała wysyłka trafi wtedy do Ciebie.
Przygotowanie
- Kontenery działają:
docker compose pspokazuje usługi jakoUp curl http://localhost:8080/api/healthzwracaUP- Profil wideo uruchomiony, jeśli testujesz spotkanie:
docker compose --profile livekit up -d - Adres publiczny instancji ustawiony na
http://localhost:8081 - Poczta skonfigurowana i test wysyłki przechodzi
- Tryb bezpieczny włączony, adres i numer zastępczy uzupełnione
Przebieg
Utwórz webinar z terminem tuż przed nosem
Ustaw rozpoczęcie za 10 minut, zakończenie za 70 minut. Dzięki oknu wejścia otwartemu 15 minut przed startem pokój będzie dostępny od razu, a jednocześnie termin jest w przyszłości, więc nie ruszy zadanie podsumowań.
Nie ustawiaj terminu w przeszłości. Dane testowe z terminem wstecz wystarczą, żeby scheduler potraktował webinar jako zakończony i rozesłał podsumowania.
Dodaj treść strony i limit miejsc
Wpisz kilka akapitów w edytorze i ustaw limit, na przykład 5 miejsc. Pozwoli to później sprawdzić licznik wolnych miejsc. Zapisz webinar.
Załóż pokój i dodaj siebie jako prelegenta
Wejdź w edycję, zakładka Wideo: „Utwórz pokój u dostawcy”, potem dodaj prelegenta i skopiuj jego link. Zapisz go, będzie potrzebny za chwilę.
Opublikuj i zapisz się jako uczestnik
Opublikuj webinar, otwórz http://localhost:8081/w/{slug} w oknie prywatnym i wypełnij formularz swoim adresem. Zaznacz zgodę wymaganą i marketingową.
Sprawdź skrzynkę: powinien przyjść mail z potwierdzeniem, załącznikiem kalendarza i linkiem do własnego zapisu.
Sprawdź brak enumeracji
Wyślij formularz drugi raz tym samym adresem. Ekran potwierdzenia ma być identyczny jak za pierwszym razem, a na skrzynkę przyjdzie wiadomość o istniejącym już zapisie.
Wejdź na salę z dwóch stron
W zwykłym oknie otwórz link prelegenta, w oknie prywatnym link uczestnika z maila. W obu przypadkach potwierdź tożsamość w przedpokoju i wejdź na salę.
Sprawdź, czy prelegent publikuje obraz i dźwięk, a uczestnik tylko odbiera.
Otwórz panel prowadzenia i przetestuj interakcje
W trzeciej karcie otwórz /webinary/{id}/prowadzenie. Z okna uczestnika kolejno:
- napisz wiadomość na czacie i sprawdź, czy widać ją w panelu,
- zadaj pytanie, zatwierdź je w panelu i sprawdź, czy pojawia się uczestnikom,
- zgłoś się po głos i przyznaj go z panelu, sprawdzając, czy mikrofon się otwiera,
- ogłoś zadanie z panelu i zapisz się na nie z okna uczestnika,
- wyślij zapytanie ofertowe i sprawdź sygnał dźwiękowy oraz wpis w
/zapytania.
Sprawdź otwarty mikrofon
Włącz przełącznik w kolumnie zgłoszeń i potwierdź. W oknie uczestnika przycisk proszenia o głos ma zniknąć na rzecz napisu o możliwości mówienia. Wyłącz go z powrotem i sprawdź, czy kolejka wróciła.
Sprawdź obecność i statystyki
Zostaw okno uczestnika otwarte na kilka minut, potem wejdź na kartę webinaru, zakładka Statystyki. Powinna pokazać obecność i wykres w czasie.
Zakończ i sprawdź automaty
Aby nie czekać godziny, skróć webinar: w edycji ustaw zakończenie na czas miniony i odczekaj do następnego przebiegu zadania. Sprawdź, czy przyszło podsumowanie i czy w dzienniku powiadomień jest wpis.
Pamiętaj, że w trybie bezpiecznym wszystkie te wiadomości przyjdą na Twój adres zastępczy, także te zaadresowane formalnie do kogoś innego.
Wgraj nagranie i wydaj certyfikat
W zakładce „Nagrania i materiały” wgraj dowolny plik MP4, ustaw widoczność „dla zapisanych” i wyślij powiadomienie o nagraniu. Następnie w zakładce Certyfikaty wydaj dokument i sprawdź go pod /certyfikat/{numer}.
Otwórz PDF i sprawdź, czy polskie znaki wyświetlają się poprawnie.
Skopiuj webinar
Na liście kliknij ikonę kopiowania. Sprawdź, czy kopia ma treść i przypomnienia, a nie ma zapisów ani certyfikatów, oraz czy dostała termin zastępczy za tydzień.
Lista kontrolna testu
- Strona zapisu widoczna dopiero po publikacji
- Mail potwierdzający z załącznikiem kalendarza
- Powtórny zapis daje identyczny ekran
- Przedpokój pokazuje imię z zapisu do potwierdzenia
- Prelegent publikuje obraz, uczestnik tylko odbiera
- Czat, pytania i głosowanie działają w obie strony
- Kolejka głosu otwiera i zamyka mikrofon
- Otwarty mikrofon nie kasuje kolejki
- Zadanie ogłoszone w panelu widoczne u uczestnika
- Zapytanie ofertowe trafia na listę i daje sygnał dźwiękowy
- Obecność i wykres w statystykach
- Podsumowanie po zakończeniu, dokładnie raz
- Nagranie dostępne dla zapisanych, niedostępne bez tokenu
- Certyfikat z poprawnymi polskimi znakami i działającą weryfikacją
- Kopia bez zapisów, z terminem zastępczym
Rozwiązywanie problemów
Katalog objawów, które przy tej architekturze pojawiają się najczęściej, wraz z rzeczywistą przyczyną.
Najczęstsza przyczyna przy poprawnie skonfigurowanym serwerze: zablokowany zakres portów UDP. To główna droga ruchu audio i wideo. Sygnalizacja idzie osobno, więc pokój wygląda na działający.
Przy uruchomieniu lokalnym druga typowa przyczyna to brak LIVEKIT_NODE_IP=127.0.0.1: serwer rozgłasza wtedy publiczny adres łącza, pod który przeglądarka na tej samej maszynie się nie połączy.
Wersja serwera musi nadążać za biblioteką kliencką. Nowszy klient łączy się ścieżką, której starszy serwer nie zna: uczestnik wchodzi i widzi listę obecnych, ale media nie płyną. Podnosząc bibliotekę, sprawdź wersję obrazu serwera.
Sprawdź, czy tryb bezpieczny jest włączony bez ustawionego adresu zastępczego. Wtedy wysyłka jest celowo wstrzymywana, zamiast trafić do prawdziwego odbiorcy.
Druga możliwość to bezpiecznik ilościowy: wiadomość dostaje wtedy status oczekującej i pójdzie później. To nie jest błąd.
Stan sprawdzisz zapytaniem z rozdziału 5. Nie polegaj na tym, co było ustawione wcześniej: ustawienia zmieniają się bez restartu.
Objaw jest mylący i rzadko dotyczy logowania. Zwykle oznacza błąd 500 po stronie backendu, który front tłumaczy jako wygasłą sesję.
Typowa przyczyna to encja opuszczająca warstwę serwisu: pole doczytywane leniwie poza transakcją. Szukanie tego w warstwie logowania nie ma sensu, zajrzyj do logów backendu.
To nie błąd, tylko okno czasowe: wejście otwiera się 15 minut przed startem i zamyka 30 minut po zakończeniu. Uczestnik widzi odliczanie, a strona odświeży się sama.
Jeśli chcesz wejść wcześniej do testów, użyj linku prelegenta: on nie podlega temu ograniczeniu.
Publikacja wymaga tytułu, adresu strony zapisu i obu terminów. Sprawdź też status: webinaru odwołanego nie da się opublikować ponownie.
Typowy objaw braku rzutowania typu przy parametrze opcjonalnym w zapytaniu. Baza nie potrafi wywnioskować typu pustej wartości, więc lista psuje się dokładnie wtedy, gdy filtr nie jest używany, czyli w najzwyklejszym użyciu.
Czcionka musi być osadzona w pliku i rejestrowana w kodzie. Deklaracja czcionki w arkuszu stylów szablonu przykrywa tę rejestrację: generator próbuje pobrać plik, nie znajduje go i podstawia czcionkę zastępczą.
Certyfikat z błędem w nazwisku jest bezużyteczny, więc to jest błąd blokujący, a nie kosmetyczny.
Sam profil wideo bez usług nagrywania nie nagrywa. Serwer przyjmuje zadanie i przekazuje je przez pośrednika; bez niego zadanie nie ma odbiorcy i kończy się przekroczeniem czasu, przy w pełni sprawnym serwerze.
Uruchom cały profil: docker compose --profile livekit up -d.
Stan weryfikacji: nagrywanie automatyczne nie jest potwierdzone od początku do końca w środowisku lokalnym. Ręczne wgranie pliku działa i pozostaje drogą awaryjną.
Sprawdź, czy webinar ma reguły przypomnień i czy są włączone. Przypomnienie spóźnione bardziej niż okno tolerancji nie idzie w ogóle, co chroni przed lawiną wysyłek wstecz po restarcie.
SMS wymaga dodatkowo zgody marketingowej i numeru telefonu u uczestnika.
Pytanie oczekujące na zatwierdzenie nie jest widoczne publicznie. Zatwierdź je w kolejce pytań w panelu prowadzenia.
Przydatne polecenia
# logi backendu na żywo
docker compose logs -f backend
# stan kontenerów
docker compose ps
# restart pojedynczej usługi
docker compose restart backend
# zatrzymanie całości, dane w wolumenach zostają
docker compose down
# dziennik powiadomień: co i do kogo poszło
docker exec webinar-postgres psql -U webinar -d webinar -c \
"SELECT created_at, template, recipient, status FROM notifications
ORDER BY created_at DESC LIMIT 20;"
Nigdy nie kasuj wolumenów. Ani przez docker compose down -v, ani przez docker volume rm, ani przez docker system prune -a --volumes. Tabela ustawień i konto administratora są nietykalne: odtworzenie wymaga ręcznego wpisania od nowa danych SMTP, kluczy bramki SMS i kluczy dostawcy wideo, których nie ma nigdzie indziej.
Czyszczenie danych testowych ogranicza się do tabel operacyjnych: webinarów i wszystkiego, co od nich zależy, oraz powiadomień i plików multimedialnych.