From 2c3fbc62360338c8d29a075bf56bf5ea67cc9b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zatorski?= Date: Fri, 4 Sep 2026 11:27:30 +0200 Subject: [PATCH] Skrypt wdrozenia w repozytorium, dokumentacja komend compose SKRYPT WDROZENIA `deploy-prod.sh` istnial wylacznie na serwerze, tak samo jak wczesniej cztery zmienne LiveKit w compose: kto odtwarzalby instancje z repozytorium, nie dostalby go wcale. Wchodzi do `scripts/` w wersji dzialajacej, z jedna zmiana: sprawdzeniem czystosci repozytorium na starcie. Powod jest praktyczny. `git merge --ff-only` odrzuca wdrozenie, gdy na serwerze sa lokalne zmiany, ale robi to CICHO: deploy konczy sie statusem "sukces", a kod nie wchodzi na serwer. Zdarzylo sie to dzis i wykrylo dopiero reczne `git log` na produkcji. Teraz skrypt przerywa i wypisuje pliki. NAZWA PROJEKTU W KOMENDACH COMPOSE README uczyl komend produkcyjnych BEZ `-p`, czyli dokladnie tych, ktore wchodza w cudzy projekt. Katalog aplikacji nazywa sie `app`, wiec compose bierze z niego nazwe projektu, a na serwerze z kilkoma aplikacjami trafia w nie te. Zdarzylo sie realnie: `up -d --force-recreate coturn` utworzylo kontener w projekcie drugiej aplikacji tego samego wlasciciela, walczacy o ten sam port 3478. Jedynym ostrzezeniem byl komunikat "Found orphan containers", latwy do przeoczenia. Poprawione 11 komend produkcyjnych plus dwie nowe sekcje: o nazwie projektu i o tym, ze `docker restart` NIE przeladowuje zmiennych z `.env` (argumenty sa zamrozone przy tworzeniu kontenera, wiec po zmianie hasla kontener wstaje ze stara wartoscia, wygladajac na dzialajacy poprawnie). Komendy lokalne zostaja bez `-p`: tam nazwa z katalogu nikomu nie wchodzi w droge. INSTRUKCJA TWORZENIA WEBINARU Uzupelniona o funkcje dodane w ostatnich dniach: link do sali trafiajacy do uczestnika automatycznie (potwierdzenie, przypomnienia, strona zgloszenia), nagrywanie wlaczane przyciskiem w panelu prowadzenia oraz rozmycie tla. Co-Authored-By: Claude Opus 5 --- README.md | 66 +++++++++++++++++++++++++------ docs/instrukcja_nowy_webinar.html | 22 ++++++++++- scripts/deploy-prod.sh | 65 ++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 12 deletions(-) create mode 100644 scripts/deploy-prod.sh diff --git a/README.md b/README.md index ffcd0f3..0d81852 100644 --- a/README.md +++ b/README.md @@ -373,7 +373,7 @@ rozjedzie sie z kodem przy dodaniu nowego ustawienia. ### Krok 4: uruchomienie ```bash -docker compose -f docker-compose.prod.yml --env-file .env.production up -d --build +docker compose -p akademia -f docker-compose.prod.yml --env-file .env.production up -d --build ``` Pierwszy build pobiera zaleznosci Mavena i npm, wiec trwa kilka minut. @@ -381,7 +381,7 @@ Pierwszy build pobiera zaleznosci Mavena i npm, wiec trwa kilka minut. Sprawdz, czy wszystkie kontenery dzialaja: ```bash -docker compose -f docker-compose.prod.yml --env-file .env.production ps +docker compose -p akademia -f docker-compose.prod.yml --env-file .env.production ps ``` ### Krok 5: haslo administratora i pierwsze logowanie @@ -389,7 +389,7 @@ docker compose -f docker-compose.prod.yml --env-file .env.production ps Jesli `ADMIN_PASSWORD` bylo puste, odczytaj wygenerowane haslo z logu: ```bash -docker compose -f docker-compose.prod.yml --env-file .env.production \ +docker compose -p akademia -f docker-compose.prod.yml --env-file .env.production \ logs backend | grep -A6 "UTWORZONO PIERWSZE KONTO" ``` @@ -461,24 +461,68 @@ Sprawdz jeszcze: ```bash git pull -docker compose -f docker-compose.prod.yml --env-file .env.production up -d --build +docker compose -p akademia -f docker-compose.prod.yml --env-file .env.production up -d --build ``` Migracje Flyway wykonuja sie automatycznie przy starcie backendu. Przed aktualizacja wykonaj kopie zapasowa, patrz sekcja nizej. +### Nazwa projektu jest obowiazkowa, gdy serwer dzieli kilka aplikacji + +**`-p ` w kazdej komendzie `docker compose`.** Bez tej flagi compose +bierze nazwe projektu **z katalogu roboczego**, a katalogi bywaja nazwane +ogolnie (`app`, `www`, `docker`). Na serwerze z kilkoma aplikacjami konczy sie +to wejsciem w cudzy projekt: `up -d` restartuje wtedy nie te uslugi, a +`up -d ` tworzy obcy kontener walczacy o ten sam port hosta. + +Zdarzylo sie to realnie na instancji, gdzie katalog nazywal sie `app`, a na tym +samym serwerze stala druga aplikacja w projekcie o tej samej nazwie. Jedynym +ostrzezeniem byl latwy do przeoczenia komunikat "Found orphan containers". + +Nazwa projektu ma odpowiadac instancji, nie katalogowi. Po kazdej recznej +komendzie warto sprawdzic, czy nie przybylo kontenerow z obcym prefiksem: + +```bash +docker ps --format '{{.Label "com.docker.compose.project"}}\t{{.Names}}\t{{.Status}}' | sort +``` + +### Zmienne z `.env` wymagaja odtworzenia kontenera, nie restartu + +**`docker restart` NIE przeladowuje `.env`.** Argumenty uruchomienia sa +zamrozone w konfiguracji kontenera przy jego tworzeniu, wiec po zmianie hasla +albo klucza kontener wstaje ze **stara wartoscia**, wygladajac przy tym na +dzialajacy poprawnie. Objawia sie to dopiero tym, ze usluga cicho nie +uwierzytelnia sie u drugiej strony. + +```bash +# zmiana wartosci w .env: konieczne odtworzenie +docker compose -p akademia -f docker-compose.prod.yml --env-file .env.production up -d --force-recreate coturn + +# sprawdzenie, ze kontener ma wartosc z pliku, bez pokazywania sekretu +docker inspect akademia-coturn-1 --format '{{json .Config.Cmd}}' | grep -o 'webinar:[^"]*' | cut -d: -f2 | sha256sum | cut -c1-16 +grep '^TURN_PASSWORD=' .env.production | cut -d= -f2- | sha256sum | cut -c1-16 +``` + ### Zatrzymanie i restart ```bash -# restart pojedynczej uslugi -docker compose -f docker-compose.prod.yml --env-file .env.production restart backend +# restart pojedynczej uslugi (bez zmian w .env) +docker compose -p akademia -f docker-compose.prod.yml --env-file .env.production restart backend # zatrzymanie calosci, dane w wolumenach zostaja -docker compose -f docker-compose.prod.yml --env-file .env.production down +docker compose -p akademia -f docker-compose.prod.yml --env-file .env.production down ``` Nie uzywaj `down -v` na produkcji: ta komenda usuwa wolumeny wraz z danymi. +### Wdrozenie automatyczne + +`scripts/deploy-prod.sh` pobiera zmiany z `main` i przebudowuje obrazy. +Wolany przez Gitea Actions po kazdym pushu, dziala takze recznie. Skrypt +**przerywa wdrozenie, gdy repozytorium na serwerze ma lokalne zmiany**: +`git merge --ff-only` i tak by ich nie nadpisal, ale robil to cicho, wiec +deploy konczyl sie statusem "sukces", a kod nie wchodzil na serwer. + ## Kopia zapasowa i odtworzenie ### Wykonanie kopii @@ -554,7 +598,7 @@ wymaga **okolo 170 GB**. Przy takiej skali warto: docker system df -v | grep minio-data # baza -docker compose -f docker-compose.prod.yml exec -T postgres \ +docker compose -p akademia -f docker-compose.prod.yml exec -T postgres \ psql -U webinar -d webinar -c "SELECT pg_size_pretty(pg_database_size('webinar'));" ``` @@ -602,7 +646,7 @@ oraz zaloguj sie do panelu i obejrzyj liste webinarow i zapisow. 2. Skopiuj `env.production.kopia` z kopii jako `.env.production` i ustaw `chmod 600`. Zachowaj **ten sam** `JWT_SECRET`, inaczej wszyscy zostana wylogowani, oraz to samo `DB_PASSWORD`, bo odtwarzana baza go uzywa. -3. Uruchom uslugi: `docker compose -f docker-compose.prod.yml --env-file .env.production up -d`. +3. Uruchom uslugi: `docker compose -p akademia -f docker-compose.prod.yml --env-file .env.production up -d`. 4. Odtworz dane: `./scripts/restore.sh `. 5. Przekieruj domene na nowy serwer i sprawdz `/api/health`. @@ -634,7 +678,7 @@ albo na Jitsi nie uruchamia niczego zbednego. ```bash # uzupelnij w .env: LIVEKIT_API_KEY, LIVEKIT_API_SECRET, TURN_PASSWORD -docker compose --profile livekit up -d +docker compose -p akademia --profile livekit -f docker-compose.prod.yml --env-file .env.production up -d ``` Ten sam klucz i sekret wpisz w panelu (`Ustawienia > Wideo`): tutaj konfiguruja @@ -859,7 +903,7 @@ kart testowych zmieniaja sie miedzy wersjami uslugi. - **Komunikat odrzucony tez jest zapisany**, z oznaczeniem `podpis niepoprawny`. Brak wpisu oznacza, ze notyfikacja w ogole nie dotarla: sprawdz adres w panelu operatora i czy domena ma wazny certyfikat. -- `docker compose logs -f backend | grep -i autopay` +- `docker compose -p akademia -f docker-compose.prod.yml logs -f backend | grep -i autopay` ### Reczne nadanie dostepu diff --git a/docs/instrukcja_nowy_webinar.html b/docs/instrukcja_nowy_webinar.html index 17e887a..67f4aa8 100644 --- a/docs/instrukcja_nowy_webinar.html +++ b/docs/instrukcja_nowy_webinar.html @@ -319,6 +319,7 @@ Rola: ADMIN lub MODERATOR 8 zakładek Zapis jako szkic + Stan na 4 września 2026 @@ -485,6 +486,12 @@ Po webinarze adres nagrania, adres materiałów

Oba adresy trafiają do maila podsumowującego wysyłanego automatycznie po zakończeniu. Uzupełnij je przed wysyłką podsumowania; jeśli nagranie pojawi się później, jest osobny przycisk do wysłania powiadomienia o nagraniu.

+ +
+ 💡 +

Linku do spotkania nie musisz nigdzie wpisywać ani wysyłać. Uczestnik dostaje go automatycznie: w mailu potwierdzającym zapis, w każdym przypomnieniu oraz na stronie swojego zgłoszenia. Sala otwiera się krótko przed startem — kto kliknie wcześniej, zobaczy odliczanie zamiast błędu.

+

Dotyczy to dostawców z własnym pokojem (LiveKit, Jitsi, Daily, Zoom). W trybie NONE obowiązuje link, który wkleisz w polu „Link do spotkania”.

+
Nagrania i materiały

Osobna zakładka na wgranie plików do własnego składu (po zapisaniu webinaru). Alternatywa dla wklejania adresów zewnętrznych.

@@ -514,7 +521,7 @@ DostawcaUprawnieniaNagrywanie - LIVEKIT
domyślnyEgzekwuje serwer (token). Uczestnik nie włączy kamery, nawet podmieniając stronę.Automatyczne (wymaga usług Egress + Redis). + LIVEKIT
domyślnyEgzekwuje serwer (token). Uczestnik nie włączy kamery, nawet podmieniając stronę.Prowadzący włącza przyciskiem w panelu prowadzenia. Plik trafia na Wasz serwer (wymaga usług Egress + Redis). DAILYEgzekwuje serwer.Pobierane od dostawcy po zakończeniu. ZOOMOsadzone w oknie. Role jak przy Jitsi.Po stronie Zoom. JITSIZ JWT: tylko rola moderatora. Bez JWT: pokój otwarty dla każdego, kto zna adres.Ręczne wgranie pliku. @@ -526,6 +533,19 @@ 💡

Wideo uczestników jest zawsze wyłączone na poziomie uprawnień. Prowadzący może w trakcie webinaru włączyć głos wybranej osobie lub — świadomie — kamerę dla wszystkich.

+ +

Co prowadzący ma pod ręką w trakcie spotkania (Panel → Webinary → „Prowadzenie”):

+
    +
  • Otwarty mikrofon — każdy mówi bez proszenia o głos. Włączenie pyta o potwierdzenie, bo otwiera mikrofony całej sali naraz.
  • +
  • Wideo dla wszystkich — każdy uczestnik może włączyć kamerę.
  • +
  • Nagraj spotkanie — start i stop nagrywania. Nagranie nie startuje samo: to świadoma decyzja organizatora, a zgoda uczestników na nagranie pozostaje jego sprawą. Gotowy plik pojawia się w zakładce „Nagrania i materiały” w ciągu minuty od zatrzymania.
  • +
  • Kolejka zgłoszeń do głosu, moderacja pytań i ogłaszanie zadań.
  • +
+ +
+ 💡 +

Rozmycie tła. Każdy, kto ma włączoną kamerę — prowadzący i prelegenci — może rozmyć swoje tło przyciskiem w lewym górnym rogu okna wideo. Włącza się i wyłącza w dowolnym momencie, bez przerywania obrazu. Przydaje się przy prowadzeniu z domu.

+
diff --git a/scripts/deploy-prod.sh b/scripts/deploy-prod.sh new file mode 100644 index 0000000..0e3ade5 --- /dev/null +++ b/scripts/deploy-prod.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# +# Wdrozenie na serwer produkcyjny. +# +# Wolane przez Gitea Actions (`.gitea/workflows/deploy.yml`) po kazdym pushu +# na `main`, ale dziala takze recznie. Kopia na serwerze lezy w +# `/opt/akademia/deploy-prod.sh` i musi byc zgodna z ta wersja. +# +# NAZWA PROJEKTU JEST OBOWIAZKOWA +# ----------------------------------------------------------------------------- +# `-p akademia` NIE jest ozdobnikiem. Katalog aplikacji nazywa sie +# `/opt/akademia/app`, wiec bez tej flagi compose bierze nazwe projektu +# z katalogu i staje sie projektem `app`. Na tym serwerze `app` to **inna +# aplikacja tego samego wlasciciela**, a komenda bez `-p` tworzy w niej obce +# kontenery albo restartuje cudze uslugi. Zdarzylo sie to realnie. +# +set -euo pipefail + +APP_DIR=/opt/akademia/app +LOG=/opt/akademia/deploy.log +COMPOSE="docker compose -p akademia -f docker-compose.prod.yml --env-file .env.production" + +# Dwa deploye naraz przebudowywalyby ten sam obraz i restartowaly kontener +# w polowie pracy drugiego. Blokada jest nieblokujaca: drugi przebieg konczy +# sie od razu, zamiast czekac w kolejce z nieaktualnym juz kodem. +exec 9>/opt/akademia/deploy.lock +flock -n 9 || { echo "$(date '+%F %T') inny deploy trwa - pomijam"; exit 0; } + +exec >>"$LOG" 2>&1 +echo "===== $(date '+%F %T') deploy start =====" + +cd "$APP_DIR" + +# Lokalne zmiany zatrzymuja wdrozenie, i to jest swiadome: `--ff-only` nizej +# i tak by je odrzucil, ale robil to CICHO. Deploy konczyl sie wtedy statusem +# "sukces", a kod nie wchodzil na serwer: rozjazd wykrywalo sie dopiero +# recznym `git log` na produkcji. Lepiej zaswiecic na czerwono i nazwac pliki. +if [ -n "$(git status --porcelain)" ]; then + echo "PRZERWANE: repozytorium na serwerze ma lokalne zmiany." + echo "Wdrozenie nie ruszy, dopoki ktos ich nie przeniesie albo nie odrzuci:" + git status --short + echo + echo "Zmiana warta zachowania: przenies ja do repozytorium." + echo "Zmiana do wyrzucenia: git checkout -- " + exit 1 +fi + +git fetch --prune origin +LOCAL=$(git rev-parse @) +REMOTE=$(git rev-parse '@{u}') + +if [ "$LOCAL" = "$REMOTE" ]; then + echo "Brak zmian ($LOCAL) - koniec." + exit 0 +fi + +echo "Aktualizacja $LOCAL -> $REMOTE" +# --ff-only: nigdy nie scalamy na produkcji. Merge z konfliktem zostawilby +# repozytorium w stanie polowicznym, a rozwiazywanie go przez SSH na zywej +# instancji jest ostatnia rzecza, ktora ktokolwiek chce robic. +git merge --ff-only origin/main + +$COMPOSE up -d --build + +echo "===== $(date '+%F %T') deploy OK (v $REMOTE) ====="