239 lines
8.8 KiB
Bash
Executable File
239 lines
8.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Sprawdzenie konfiguracji przed uruchomieniem instancji u klienta.
|
|
#
|
|
# ./scripts/check-config.sh # sprawdza .env.production
|
|
# ENV_FILE=.env ./scripts/check-config.sh # inny plik
|
|
# ./scripts/check-config.sh --skip-db # bez pytania bazy (przed pierwszym startem)
|
|
#
|
|
# ODMAWIA STARTU, ZAMIAST OSTRZEC I PRZEPUSCIC.
|
|
# ---------------------------------------------------------------------------
|
|
# Kazdy sprawdzany warunek to blad, ktory na produkcji kosztuje wiecej niz
|
|
# nieudany start: sekret z przykladu oznacza, ze kazdy moze podrobic token,
|
|
# adres z "localhost" oznacza martwe linki w kazdym wyslanym mailu, a haslo
|
|
# przykladowe oznacza konto administratora otwarte dla kogokolwiek, kto
|
|
# widzial repozytorium.
|
|
#
|
|
# Kod wyjscia: 0 gdy wszystko w porzadku, 1 gdy cokolwiek blokuje start.
|
|
|
|
set -uo pipefail
|
|
|
|
ENV_FILE="${ENV_FILE:-.env.production}"
|
|
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.prod.yml}"
|
|
EXAMPLE_FILE="${EXAMPLE_FILE:-.env.production.example}"
|
|
SKIP_DB=0
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--skip-db) SKIP_DB=1 ;;
|
|
-h|--help) sed -n '2,20p' "$0"; exit 0 ;;
|
|
esac
|
|
done
|
|
|
|
ERRORS=0
|
|
WARNINGS=0
|
|
|
|
red() { printf '\033[31m%s\033[0m\n' "$*"; }
|
|
yellow() { printf '\033[33m%s\033[0m\n' "$*"; }
|
|
green() { printf '\033[32m%s\033[0m\n' "$*"; }
|
|
|
|
fail() {
|
|
red " BLAD: $1"
|
|
[ $# -gt 1 ] && echo " $2"
|
|
ERRORS=$((ERRORS + 1))
|
|
}
|
|
|
|
warn() {
|
|
yellow " UWAGA: $1"
|
|
[ $# -gt 1 ] && echo " $2"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
}
|
|
|
|
ok() { echo " ok: $1"; }
|
|
|
|
# Wartosc zmiennej z pliku .env. Obsluguje wartosci w cudzyslowach.
|
|
value_of() {
|
|
local key="$1" file="$2"
|
|
grep -E "^${key}=" "$file" 2>/dev/null \
|
|
| tail -n1 \
|
|
| cut -d= -f2- \
|
|
| sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//"
|
|
}
|
|
|
|
echo "=========================================================="
|
|
echo " Sprawdzenie konfiguracji: ${ENV_FILE}"
|
|
echo "=========================================================="
|
|
|
|
if [ ! -f "${ENV_FILE}" ]; then
|
|
red "BLAD: brak pliku ${ENV_FILE}"
|
|
echo
|
|
echo "Skopiuj wzor i uzupelnij:"
|
|
echo " cp ${EXAMPLE_FILE} ${ENV_FILE}"
|
|
echo " chmod 600 ${ENV_FILE}"
|
|
exit 1
|
|
fi
|
|
|
|
# --- uprawnienia pliku z sekretami ---
|
|
echo
|
|
echo "[1/5] Plik konfiguracji"
|
|
if [ "$(uname -s)" != "MINGW64_NT"* ] && command -v stat >/dev/null 2>&1; then
|
|
PERMS="$(stat -c '%a' "${ENV_FILE}" 2>/dev/null || echo '')"
|
|
case "${PERMS}" in
|
|
600|400) ok "uprawnienia ${PERMS}" ;;
|
|
"") ;; # systemy bez stat -c, pomijamy
|
|
*) warn "plik ma uprawnienia ${PERMS}, zalecane 600" \
|
|
"chmod 600 ${ENV_FILE}" ;;
|
|
esac
|
|
fi
|
|
|
|
# --- JWT_SECRET ---
|
|
echo
|
|
echo "[2/5] Sekret podpisywania tokenow"
|
|
JWT_SECRET="$(value_of JWT_SECRET "${ENV_FILE}")"
|
|
JWT_EXAMPLE="$(value_of JWT_SECRET "${EXAMPLE_FILE}" 2>/dev/null || echo '')"
|
|
|
|
if [ -z "${JWT_SECRET}" ]; then
|
|
fail "JWT_SECRET jest pusty" \
|
|
"Wygeneruj: openssl rand -base64 48"
|
|
elif [ "${#JWT_SECRET}" -lt 32 ]; then
|
|
fail "JWT_SECRET ma ${#JWT_SECRET} znakow, wymagane minimum 32" \
|
|
"Krotszy klucz da sie zlamac. Wygeneruj: openssl rand -base64 48"
|
|
elif [ -n "${JWT_EXAMPLE}" ] && [ "${JWT_SECRET}" = "${JWT_EXAMPLE}" ]; then
|
|
fail "JWT_SECRET jest rowny wartosci z ${EXAMPLE_FILE}" \
|
|
"Kazdy, kto widzial repozytorium, moze podrobic token administratora."
|
|
else
|
|
ok "JWT_SECRET ustawiony (${#JWT_SECRET} znakow)"
|
|
fi
|
|
|
|
# --- ADMIN_PASSWORD ---
|
|
echo
|
|
echo "[3/5] Konto administratora"
|
|
ADMIN_PASSWORD="$(value_of ADMIN_PASSWORD "${ENV_FILE}")"
|
|
ADMIN_EXAMPLE="$(value_of ADMIN_PASSWORD "${EXAMPLE_FILE}" 2>/dev/null || echo '')"
|
|
ADMIN_EMAIL="$(value_of ADMIN_EMAIL "${ENV_FILE}")"
|
|
|
|
if [ -z "${ADMIN_EMAIL}" ]; then
|
|
fail "ADMIN_EMAIL jest pusty" "Bez adresu nie powstanie pierwsze konto."
|
|
else
|
|
ok "ADMIN_EMAIL = ${ADMIN_EMAIL}"
|
|
fi
|
|
|
|
if [ -z "${ADMIN_PASSWORD}" ]; then
|
|
# Puste haslo jest tu POPRAWNE i zalecane: aplikacja losuje je przy
|
|
# pierwszym starcie i wypisuje raz w logu. To bezpieczniejsze niz haslo
|
|
# przechowywane w pliku na serwerze.
|
|
ok "ADMIN_PASSWORD pusty: haslo zostanie wylosowane i wypisane w logu"
|
|
echo " Po starcie: docker compose logs backend | grep -i haslo"
|
|
elif [ -n "${ADMIN_EXAMPLE}" ] && [ "${ADMIN_PASSWORD}" = "${ADMIN_EXAMPLE}" ]; then
|
|
fail "ADMIN_PASSWORD jest rowny wartosci z ${EXAMPLE_FILE}" \
|
|
"Konto administratora byloby otwarte dla kazdego, kto widzial repozytorium."
|
|
elif [ "${#ADMIN_PASSWORD}" -lt 12 ]; then
|
|
warn "ADMIN_PASSWORD ma ${#ADMIN_PASSWORD} znakow, zalecane minimum 12" \
|
|
"Albo zostaw puste, a aplikacja wylosuje haslo."
|
|
else
|
|
ok "ADMIN_PASSWORD ustawiony"
|
|
fi
|
|
|
|
# --- APP_PUBLIC_URL ---
|
|
echo
|
|
echo "[4/5] Adres publiczny instancji"
|
|
APP_PUBLIC_URL="$(value_of APP_PUBLIC_URL "${ENV_FILE}")"
|
|
|
|
if [ -z "${APP_PUBLIC_URL}" ]; then
|
|
fail "APP_PUBLIC_URL jest pusty" \
|
|
"Z tego adresu powstaja WSZYSTKIE linki w mailach: potwierdzenia, przypomnienia i wypisanie."
|
|
elif echo "${APP_PUBLIC_URL}" | grep -qiE 'localhost|127\.0\.0\.1|0\.0\.0\.0'; then
|
|
fail "APP_PUBLIC_URL zawiera adres lokalny: ${APP_PUBLIC_URL}" \
|
|
"Uczestnik dostanie link prowadzacy do jego wlasnego komputera."
|
|
elif ! echo "${APP_PUBLIC_URL}" | grep -qE '^https?://'; then
|
|
fail "APP_PUBLIC_URL nie zaczyna sie od http:// ani https://: ${APP_PUBLIC_URL}"
|
|
else
|
|
case "${APP_PUBLIC_URL}" in
|
|
*/) warn "APP_PUBLIC_URL konczy sie ukosnikiem" "Usun go: ${APP_PUBLIC_URL%/}" ;;
|
|
esac
|
|
echo "${APP_PUBLIC_URL}" | grep -qE '^http://' \
|
|
&& warn "APP_PUBLIC_URL uzywa http, nie https" \
|
|
"Tokeny zapisu i sesji poszlyby przez siec otwartym tekstem."
|
|
ok "APP_PUBLIC_URL = ${APP_PUBLIC_URL}"
|
|
fi
|
|
|
|
# pozostale sekrety infrastruktury
|
|
for key in DB_PASSWORD MINIO_SECRET_KEY MINIO_ACCESS_KEY DB_NAME DB_USER MINIO_BUCKET; do
|
|
v="$(value_of "${key}" "${ENV_FILE}")"
|
|
ex="$(value_of "${key}" "${EXAMPLE_FILE}" 2>/dev/null || echo '')"
|
|
if [ -z "${v}" ]; then
|
|
fail "${key} jest pusty"
|
|
elif [ -n "${ex}" ] && [ "${v}" = "${ex}" ] && echo "${key}" | grep -q "PASSWORD\|SECRET"; then
|
|
fail "${key} jest rowny wartosci z ${EXAMPLE_FILE}"
|
|
fi
|
|
done
|
|
|
|
# TURN_PASSWORD wymagany tylko przy wlaczonym profilu wideo
|
|
VIDEO_PROVIDER="$(value_of VIDEO_PROVIDER "${ENV_FILE}")"
|
|
if [ "${VIDEO_PROVIDER^^}" = "LIVEKIT" ]; then
|
|
[ -z "$(value_of TURN_PASSWORD "${ENV_FILE}")" ] \
|
|
&& fail "TURN_PASSWORD jest pusty przy VIDEO_PROVIDER=LIVEKIT" \
|
|
"Bez TURN czesc uczestnikow za firewallem firmowym nikogo nie uslyszy."
|
|
[ -z "$(value_of LIVEKIT_NODE_IP "${ENV_FILE}")" ] \
|
|
&& warn "LIVEKIT_NODE_IP jest pusty" \
|
|
"Na serwerze publicznym ustaw tu jego adres IP, inaczej media moga sie nie zestawic."
|
|
fi
|
|
|
|
# --- konfiguracja wymagana, prosto z katalogu ustawien ---
|
|
echo
|
|
echo "[5/5] Konfiguracja wymagana (katalog SettingKey)"
|
|
|
|
if [ "${SKIP_DB}" -eq 1 ]; then
|
|
echo " pominieto (--skip-db)"
|
|
elif ! command -v docker >/dev/null 2>&1; then
|
|
warn "brak polecenia docker, pomijam sprawdzenie w bazie"
|
|
else
|
|
PORT="$(value_of FRONTEND_PORT_EXPOSED "${ENV_FILE}")"
|
|
PORT="${PORT:-8081}"
|
|
|
|
# Zrodlem prawdy o tym, ktore klucze sa wymagane, jest katalog SettingKey
|
|
# w backendzie, a nie lista przepisana do tego skryptu: przepisana
|
|
# rozjechalaby sie z katalogiem przy pierwszym nowym kluczu.
|
|
RESPONSE="$(curl -fsS --max-time 5 "http://127.0.0.1:${PORT}/api/health/config" 2>/dev/null || echo '')"
|
|
|
|
if [ -z "${RESPONSE}" ]; then
|
|
echo " instancja nie odpowiada, pomijam"
|
|
echo " To normalne PRZED pierwszym uruchomieniem."
|
|
echo " Po starcie uruchom skrypt ponownie, zeby sprawdzic klucze"
|
|
echo " ustawiane w panelu (SMTP, bramka SMS, dostawca wideo)."
|
|
elif echo "${RESPONSE}" | grep -q '"ready":true'; then
|
|
ok "wszystkie wymagane klucze maja wartosc"
|
|
else
|
|
echo "${RESPONSE}" \
|
|
| tr '{' '\n' \
|
|
| grep -o '"key":"[A-Z_]*"' \
|
|
| cut -d'"' -f4 \
|
|
| while read -r key; do
|
|
fail "brak wymaganej wartosci: ${key}" \
|
|
"Ustaw w panelu albo w ${ENV_FILE}"
|
|
done
|
|
# petla `while` biegnie w podpowloce, wiec licznik trzeba podniesc tutaj
|
|
MISSING_COUNT="$(echo "${RESPONSE}" | grep -o '"key":"[A-Z_]*"' | wc -l)"
|
|
ERRORS=$((ERRORS + MISSING_COUNT))
|
|
fi
|
|
fi
|
|
|
|
# --- podsumowanie ---
|
|
echo
|
|
echo "=========================================================="
|
|
if [ "${ERRORS}" -gt 0 ]; then
|
|
red " NIE URUCHAMIAJ: ${ERRORS} bledow, ${WARNINGS} ostrzezen"
|
|
echo "=========================================================="
|
|
echo
|
|
echo "Popraw ${ENV_FILE} i uruchom skrypt ponownie."
|
|
exit 1
|
|
fi
|
|
|
|
if [ "${WARNINGS}" -gt 0 ]; then
|
|
yellow " Mozna uruchomic: 0 bledow, ${WARNINGS} ostrzezen do przejrzenia"
|
|
else
|
|
green " Konfiguracja w porzadku"
|
|
fi
|
|
echo "=========================================================="
|
|
exit 0
|