Files
akademiawodociagowca/DEPLOYMENT_CHECKLIST.md
T
debian c53ad95104
Deploy PROD / deploy (push) Failing after 0s
LiveKit: jeden port UDP dla wszystkich (mux) zamiast zakresu
Zakres port_range 50000-50100 rezerwowal osobny port na kazde polaczenie.
Test z 200 botami wszedl dokladnie w 49, a log serwera pokazal kandydata
UDP tylko przy 52 polaczeniach i TCP przy wszystkich 204: kolejni
uczestnicy dostawali juz tylko TCP. Zmienna UDP_PORT z zakresem nie
wlaczala muxa, bo przegrywala z zakresem z pliku.

Teraz udp_port: 7882 (sprawdzone lokalnie: rtc.portUDP Start 7882, End 0),
nowa zmienna LIVEKIT_UDP_PORT, zeby stara LIVEKIT_UDP_RANGE w .env nie
mogla przywrocic zakresu.

Automatyczne wdrozenie nie dotyka profilu livekit: kontener trzeba
przeladowac recznie.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:16:01 +02:00

237 lines
12 KiB
Markdown

# Test odbiorczy po wdrozeniu
Lista do wykonania **w podanej kolejnosci** po uruchomieniu instancji u klienta.
Kazdy punkt ma oczekiwany wynik: jesli sie nie zgadza, nie przechodz dalej, bo
kolejne punkty na nim polegaja.
Czas wykonania calosci: okolo 90 minut, plus czekanie na przypomnienia i na
podsumowanie (do dwoch godzin, mozna w tle).
**Wszystkie punkty wykonuje sie przy WLACZONYM trybie bezpiecznym wysylki**, poza
sekcja I, ktora ten tryb celowo wylacza na koniec. Dzieki temu zadna wiadomosc
z testow nie trafi do prawdziwych ludzi.
---
## A. Przed uruchomieniem
| # | Czynnosc | Oczekiwany wynik |
| --- | --- | --- |
| A1 | `cp .env.production.example .env.production` i uzupelnic | plik istnieje, `chmod 600` |
| A2 | `./scripts/check-config.sh --skip-db` | `Konfiguracja w porzadku`, kod wyjscia 0 |
| A3 | Sprawdzic DNS domeny: `dig +short webinary.klient.pl` | adres IP serwera |
| A4 | Sprawdzic certyfikat SSL na reverse proxy klienta | waznosc powyzej 14 dni |
| A5 | Porty otwarte na firewallu: 80, 443 | dostepne z internetu |
| A6 | Przy `VIDEO_PROVIDER=LIVEKIT`: dodatkowo 7880, 7881 TCP oraz **port UDP 7882** (tryb mux, jeden port dla wszystkich) | dostepne z internetu |
**A6 jest najczestszym przeoczeniem.** Bez otwartego zakresu UDP uczestnicy wchodza
do pokoju i widza liste obecnych, ale **sie nie slysza**. Objaw wyglada jak awaria
aplikacji, a jest regula firewalla.
---
## B. Uruchomienie
| # | Czynnosc | Oczekiwany wynik |
| --- | --- | --- |
| B1 | `docker compose -f docker-compose.prod.yml --env-file .env.production up -d` | wszystkie kontenery `Started` |
| B2 | `docker compose -f docker-compose.prod.yml ps` | postgres i minio `healthy`, backend `healthy` (do 90 s) |
| B3 | `docker compose logs backend \| grep "KONFIGURACJA INSTANCJI" -A 20` | wypis stanu, `Brak ostrzezen konfiguracyjnych` |
| B4 | `curl -s https://webinary.klient.pl/api/health` | `{"status":"UP",...}` |
| B5 | `./scripts/check-config.sh` (juz bez `--skip-db`) | kod wyjscia 0 |
Jesli B3 pokazuje ostrzezenia, przeczytaj je: kazde nazywa skutek, nie ustawienie.
Ostrzezenie o wylaczonym trybie bezpiecznym na tym etapie oznacza, ze **testy
ponizej wysla prawdziwe wiadomosci**.
| # | Czynnosc | Oczekiwany wynik |
| --- | --- | --- |
| B6 | `docker compose logs backend \| grep -i "haslo administratora"` | jednorazowe haslo (gdy `ADMIN_PASSWORD` bylo puste) |
| B7 | Zalogowac sie na `/logowanie` i **od razu zmienic haslo** | wejscie do `/panel`, haslo zmienione |
---
## C. Konfiguracja przez panel
Poczta i bramka SMS **nie sa** w `.env`: ustawia sie je w panelu, bo zmiana nie
moze wymagac dostepu do serwera.
| # | Czynnosc | Oczekiwany wynik |
| --- | --- | --- |
| C1 | Ustawienia -> E-mail: host, port, login, haslo, nazwa nadawcy | zapisane |
| C2 | **Sprawdzic, ze `MAIL_SAFE_MODE` jest wlaczony i ma adres zastepczy** | wlaczony, adres wlasny |
| C3 | Ustawienia -> E-mail -> Test wysylki | wiadomosc dociera na adres zastepczy |
| C4 | Ustawienia -> Aplikacja: nazwa marki, adres publiczny, strefa czasowa | zapisane |
| C5 | Ustawienia -> Grafika: logo i ikona | widoczne w panelu i na stronie glownej |
| C6 | Ustawienia -> Zgody: tresc regulaminu i zgod marketingowych | zapisane, `CONSENT_VERSION` ustawiona |
| C7 | Przy uzyciu SMS: Ustawienia -> SMS, klucz bramki, tryb bezpieczny **wlaczony** | test SMS dociera na numer zastepczy |
---
## D. Sciezka webinarowa
| # | Czynnosc | Oczekiwany wynik |
| --- | --- | --- |
| D1 | Panel -> Webinary -> nowy: tytul, termin **w przyszlosci**, limit miejsc | zapisany jako DRAFT |
| D2 | Dodac tresc strony w edytorze, wgrac obrazek | obrazek widoczny w podgladzie |
| D3 | Opublikowac webinar | status PUBLISHED |
| D4 | Otworzyc `/w/{slug}` w oknie prywatnym | strona z trescia i formularzem zapisu |
| D5 | Zapisac sie z wlasnego adresu, zaznaczyc zgody | ekran potwierdzenia |
| D6 | Sprawdzic skrzynke | mail potwierdzajacy z **dzialajacym** linkiem i zalacznikiem `invite.ics` |
| D7 | Otworzyc `invite.ics` w kalendarzu | wydarzenie z poprawna data i strefa |
| D8 | Kliknac link z maila | strona wlasnego zapisu, widoczny przycisk rezygnacji |
| D9 | Panel -> lista zapisanych | wpis widoczny, zgody z wersja i data |
| D10 | Eksport CSV listy zapisanych | plik otwiera sie w Excelu, polskie znaki poprawne |
| D11 | Dodac regule przypomnienia na **za 5 minut** i odczekac | przypomnienie dociera (na adres zastepczy) |
| D12 | Wejsc na `/spotkanie/{token}` z linku | przedpokoj z tytulem i imieniem do potwierdzenia |
| D13 | Wejsc na sale, wyslac wiadomosc na czacie | wiadomosc widoczna, autor jako imie i inicjal |
| D14 | Zadac pytanie i zaglosowac na nie z drugiej przegladarki | licznik glosow rosnie o 1, drugi glos tej samej osoby odrzucony |
| D15 | Panel prowadzenia: zatwierdzic pytanie | pytanie pojawia sie u uczestnika |
| D16 | Zglosic sie do zabrania glosu, przyznac glos z panelu | mikrofon uczestnika dziala |
| D17 | Zakonczyc webinar, zmienic termin na przeszly, odczekac `SUMMARY_DELAY_MINUTES` | podsumowanie dociera raz, nie dwa razy |
| D18 | Wystawic certyfikat uczestnikowi z odnotowana obecnoscia | PDF z **polskimi znakami** w nazwisku |
| D19 | Otworzyc `/certyfikat/{numer}` w oknie prywatnym | weryfikacja pokazuje numer, tytul, imie z inicjalem, **bez adresu e-mail** |
**D18 jest krytyczny.** Certyfikat z polamanym nazwiskiem jest bezuzyteczny, a blad
osadzania czcionki widac wylacznie na gotowym pliku.
---
## E. Sciezka Akademii
| # | Czynnosc | Oczekiwany wynik |
| --- | --- | --- |
| E1 | Otworzyc `/` w oknie prywatnym | strona glowna Akademii, przycisk `Zaloguj sie` |
| E2 | Panel -> Kursy -> nowy kurs z modulem i lekcja wideo | zapisany jako DRAFT |
| E3 | Wgrac nagranie lekcji, ustawic dlugosc | plik widoczny |
| E4 | Opublikowac kurs, ustawic cene | widoczny na `/kursy` |
| E5 | Zarejestrowac konto kursanta na `/rejestracja` | mail z linkiem potwierdzajacym |
| E6 | Potwierdzic adres z linku | konto aktywne, wejscie na `/moje-konto` |
| E7 | Zapisac sie na kurs **darmowy** | dostep od reki, kurs na pulpicie |
| E8 | Otworzyc odtwarzacz, obejrzec lekcje do konca | lekcja oznaczona jako ukonczona, postep rosnie |
| E9 | Przewinac nagranie w polowie | przewijanie dziala (naglowek Range) |
| E10 | Dodac quiz do lekcji, rozwiazac go jako kursant | wynik policzony, **poprawne odpowiedzi nie sa widoczne w zrodle strony przed koncem** |
| E11 | Ukonczyc wszystkie lekcje wymagane | kurs COMPLETED, certyfikat wystawiony automatycznie |
| E12 | Sprawdzic mail o ukonczeniu | zalacznik PDF, dzialajacy link do pulpitu |
| E13 | Panel -> Kursanci -> szczegoly | postep, zgody z historia, ostatnia aktywnosc |
| E14 | Zalogowac sie adresem majacym konto w obu swiatach | ekran wyboru: `Moje konto` i `Panel` |
---
## F. Czego NIE DA SIE sprawdzic lokalnie
Te punkty **musza** zostac wykonane po wdrozeniu, bo lokalnie nie ma jak ich
przejsc. Kazdy z nich to droga, ktora dotad nie zostala przebyta end-to-end.
### F1. Notyfikacja ITN Autopay
Operator laczy sie **z naszym serwerem**, wiec wymaga publicznej domeny z waznym
certyfikatem. Na localhost nie da sie tego wywolac.
| Krok | Oczekiwany wynik |
| --- | --- |
| Ustawic `AUTOPAY_MODE=TEST`, ServiceID i klucz ze srodowiska testowego operatora | Autopay: konfiguracja kompletna w wypisie startowym |
| Zamowic kurs platny jako kursant | przekierowanie na bramke testowa |
| Zaplacic karta testowa operatora | powrot na `/kurs/{slug}/platnosc` |
| Odczekac na ITN (do kilkudziesieciu sekund) | strona sama pokazuje nadany dostep |
| Panel -> Zamowienia | status PAID, w dzienniku wpis notyfikacji |
| Powtorzyc notyfikacje z panelu operatora | **dostep nadany raz**, drugi komunikat potwierdzony bez zmiany |
| Sprobowac zaplacic kwota inna niz w zamowieniu | notyfikacja odrzucona, wpis w dzienniku |
**Dopiero po przejsciu wszystkich krokow** przelaczyc `AUTOPAY_MODE=PRODUCTION`.
Wypis startowy pokaze wtedy ostrzezenie o prawdziwych pieniadzach: to jest
oczekiwane i ma zostac zauwazone.
### F2. Nagrywanie przez Egress
Lokalnie proces zatrzymywal sie na tym, ze szablon nie subskrybowal zadnej sciezki:
do domkniecia potrzebny jest publikujacy uczestnik w tej samej sieci co Egress,
czego nie da sie zestawic na Docker Desktop przy jednym `NODE_IP`.
| Krok | Oczekiwany wynik |
| --- | --- |
| `docker compose ... --profile livekit up -d` | cztery kontenery wideo dzialaja |
| `docker compose logs livekit-egress \| head -30` | polaczenie z Redisem, brak bledow ICE |
| Zestawic spotkanie z **dwoma** uczestnikami, jeden publikuje obraz | obraz i dzwiek plyna w obie strony |
| Uruchomic nagrywanie z panelu prowadzenia | wpis w `recordings` ze statusem DOWNLOADING |
| Zakonczyc nagrywanie, odczekac | status READY, plik w MinIO |
| Odtworzyc nagranie z panelu, przewinac | odtwarza sie i przewija |
Gdy nagrywanie nie ruszy: **reczne wgranie pliku dziala i jest droga awaryjna**.
Nie blokuje to wdrozenia.
### F3. Webhook dostawcy wideo
| Krok | Oczekiwany wynik |
| --- | --- |
| Ustawic `VIDEO_WEBHOOK_SECRET` w panelu i ten sam sekret u dostawcy | zapisane |
| Wywolac zdarzenie u dostawcy (zakonczenie nagrania) | wpis w logu backendu, odpowiedz 200 |
| Wyslac zadanie z **blednym** sekretem: `curl -X POST .../api/webhooks/video/livekit -d '{}'` | **404**, nie 403 |
Odpowiedz 404 jest celowa: 403 pozwalalaby sprawdzic, ktore webinary istnieja.
### F4. Zajawki w komunikatorach
Wymagaja publicznego adresu: robot musi pobrac strone z internetu.
| Krok | Oczekiwany wynik |
| --- | --- |
| Wkleic `https://webinary.klient.pl/w/{slug}` na Slacku albo Facebooku | zajawka z tytulem webinaru i opisem |
| `curl -A "facebookexternalhit/1.1" https://webinary.klient.pl/w/{slug}` | HTML z `og:title` zawierajacym tytul |
| To samo dla webinaru w stanie DRAFT | **404**, wersje robocze nie moga wyciekac |
### F5. Poczta wychodzaca do prawdziwych odbiorcow
| Krok | Oczekiwany wynik |
| --- | --- |
| Sprawdzic rekordy SPF i DKIM domeny nadawcy | skonfigurowane |
| Wyslac test na adres w Gmailu i w Outlooku | wiadomosc **nie trafia do spamu** |
| Sprawdzic naglowki dostarczonej wiadomosci | SPF `pass`, DKIM `pass` |
---
## G. Kopia zapasowa
| # | Czynnosc | Oczekiwany wynik |
| --- | --- | --- |
| G1 | `./scripts/backup.sh` | katalog w `./backups`, pliki `baza.dump` i `minio.tar.gz` niepuste |
| G2 | Sprawdzic `INFO.txt` w kopii | data, nazwa bazy, wersja obrazu |
| G3 | Dodac wpis do `crontab` (patrz README) | `crontab -l` pokazuje wpis |
| G4 | **Odtworzyc kopie na maszynie testowej**, nie na produkcji | dane zgodne ze stanem z chwili kopii |
**G4 jest obowiazkowy.** Kopia, ktorej nigdy nie odtworzono, nie jest kopia
zapasowa, tylko plikiem o nieznanej zawartosci.
---
## H. Bezpieczenstwo
| # | Czynnosc | Oczekiwany wynik |
| --- | --- | --- |
| H1 | `curl -s https://webinary.klient.pl/api/admin/webinars` | 401, nie lista |
| H2 | `nmap -p 5432,9000,9001 webinary.klient.pl` | porty **zamkniete** z internetu |
| H3 | Otworzyc `/swagger-ui.html` | 404 albo 403 (`SWAGGER_ENABLED=false`) |
| H4 | Zalogowac sie 6 razy blednym haslem | blokada po 5 probie, komunikat **taki sam** jak przy zlym hasle |
| H5 | Sprobowac pobrac certyfikat cudzym tokenem zapisu | 404 |
| H6 | Sprawdzic `/api/public/config` | wylacznie nazwa marki, adres i strefa, **zadnych sekretow** |
---
## I. Przelaczenie na ruch produkcyjny
**Dopiero po przejsciu wszystkich punktow powyzej.**
| # | Czynnosc | Oczekiwany wynik |
| --- | --- | --- |
| I1 | Skasowac dane testowe: webinary, kursy, zamowienia, kontakty testowe | listy puste albo z prawdziwymi danymi |
| I2 | **Nie kasowac** `app_settings` ani konta administratora | konfiguracja zachowana |
| I3 | Ustawienia -> E-mail: **wylaczyc** `MAIL_SAFE_MODE` | zapisane |
| I4 | Przy uzyciu SMS: wylaczyc `SMS_SAFE_MODE` | zapisane |
| I5 | Zrestartowac backend i przeczytac wypis startowy | ostrzezenie o wylaczonym trybie bezpiecznym: **oczekiwane** |
| I6 | Wyslac jedna prawdziwa wiadomosc testowa do siebie | dociera na wlasciwy adres |
| I7 | `./scripts/backup.sh` na czysto | kopia stanu wyjsciowego |
Po I3 kazda wysylka idzie do **prawdziwych odbiorcow**. Od tego momentu dane
testowe z terminem w przeszlosci moga uruchomic scheduler i wyslac wiadomosci
do osob z bazy: dlatego I1 jest **przed** I3, a nie po.