semantics
This commit is contained in:
@@ -1,19 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# [DEF:docker.backend.entrypoint:Module]
|
||||
# @TIER: STANDARD
|
||||
# @SEMANTICS: docker, entrypoint, admin-bootstrap, runtime, backend
|
||||
# @PURPOSE: Container entrypoint that performs optional idempotent admin bootstrap before starting backend runtime.
|
||||
# @LAYER: Infra
|
||||
# @RELATION: DEPENDS_ON -> backend/src/scripts/create_admin.py
|
||||
# @INVARIANT: Existing admin account must never be overwritten during container restarts.
|
||||
# [/DEF:docker.backend.entrypoint:Module]
|
||||
# #region docker.backend.entrypoint [C:4] [TYPE Module] [SEMANTICS docker,entrypoint,enterprise,certs]
|
||||
# @BRIEF Container entrypoint — установка корп. сертификатов, Playwright (lazy), bootstrap admin,
|
||||
# затем запуск backend (exec "$@").
|
||||
# @PRE Python runtime и backend source доступны в /app/backend.
|
||||
# Переменная CERTS_PATH указывает на директорию с .crt файлами (или пусто).
|
||||
# @POST Сертификаты установлены в системное хранилище. Playwright Chromium установлен (lazy).
|
||||
# Admin создан (если INITIAL_ADMIN_CREATE=true). Backend запущен через exec "$@".
|
||||
# @SIDE_EFFECT Модифицирует /usr/local/share/ca-certificates/ и вызывает update-ca-certificates.
|
||||
# Скачивает Chromium ~377MB при первом запуске.
|
||||
# @LAYER Infrastructure
|
||||
# @RELATION DEPENDS_ON -> [backend/src/scripts/create_admin.py]
|
||||
# @RELATION DEPENDS_ON -> [backend/src/scripts/init_auth_db.py]
|
||||
# @INVARIANT Если CERTS_PATH пуст или нет .crt файлов — install_certificates не падает.
|
||||
# @INVARIANT Существующий admin аккаунт НЕ перезаписывается при перезапуске контейнера
|
||||
# (create_admin.py идемпотентен).
|
||||
# @INVARIANT Playwright Chromium устанавливается только если отсутствует в кеше.
|
||||
# #endregion docker.backend.entrypoint
|
||||
|
||||
# [DEF:docker.backend.entrypoint.bootstrap_admin:Function]
|
||||
# @PURPOSE: Execute optional initial admin bootstrap from runtime environment variables.
|
||||
# @PRE: Python runtime and backend sources are available inside /app/backend.
|
||||
# @POST: Admin is created only when INITIAL_ADMIN_CREATE=true and required credentials are present.
|
||||
# ======================================================================
|
||||
# install_certificates — корпоративные CA-сертификаты
|
||||
# ======================================================================
|
||||
# #region docker.backend.entrypoint.install_certificates [C:3] [TYPE Function]
|
||||
# @BRIEF Устанавливает корпоративные .crt сертификаты из CERTS_PATH в системное хранилище Debian.
|
||||
# @PRE CERTS_PATH указывает на директорию (может быть пуста или не существовать).
|
||||
# В системе установлен пакет ca-certificates.
|
||||
# @POST .crt файлы скопированы в /usr/local/share/ca-certificates/custom/ и
|
||||
# добавлены в системное хранилище через update-ca-certificates.
|
||||
# @SIDE_EFFECT Запускает update-ca-certificates — модифицирует /etc/ssl/certs/.
|
||||
# @LAYER Infrastructure
|
||||
# @RELATION DEPENDS_ON -> [ca-certificates (system package)]
|
||||
# @EXAMPLE:
|
||||
# CERTS_PATH=/opt/certs docker run ... # монтируем ./certs в /opt/certs
|
||||
# В ./certs кладём: my-corporate-ca.crt, another-ca.crt
|
||||
# entrypoint установит оба в доверенные.
|
||||
install_certificates() {
|
||||
local certs_dir="${CERTS_PATH:-/opt/certs}"
|
||||
|
||||
if [ ! -d "$certs_dir" ]; then
|
||||
echo "[entrypoint] CERTS_PATH=${certs_dir} не существует — пропускаем установку сертификатов"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local cert_count
|
||||
cert_count="$(find "$certs_dir" -maxdepth 1 -name '*.crt' -o -name '*.pem' 2>/dev/null | wc -l)"
|
||||
|
||||
if [ "$cert_count" -eq 0 ]; then
|
||||
echo "[entrypoint] Нет .crt/.pem файлов в ${certs_dir} — пропускаем установку сертификатов"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local target="/usr/local/share/ca-certificates/custom"
|
||||
mkdir -p "$target"
|
||||
|
||||
echo "[entrypoint] Устанавливаем корпоративные сертификаты из ${certs_dir}..."
|
||||
local copied=0
|
||||
for cert in "$certs_dir"/*.crt "$certs_dir"/*.pem; do
|
||||
if [ -f "$cert" ]; then
|
||||
cp -v "$cert" "$target/" 2>&1 | sed 's/^/[entrypoint] /'
|
||||
copied=$((copied + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$copied" -eq 0 ]; then
|
||||
echo "[entrypoint] Нет файлов для копирования — пропускаем"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "[entrypoint] Обновляем системное хранилище CA-сертификатов..."
|
||||
update-ca-certificates --fresh 2>&1 | sed 's/^/[entrypoint] /'
|
||||
|
||||
echo "[entrypoint] ✅ Установлено ${copied} корпоративных сертификатов"
|
||||
}
|
||||
# #endregion docker.backend.entrypoint.install_certificates
|
||||
|
||||
# ======================================================================
|
||||
# bootstrap_admin — создание admin пользователя
|
||||
# ======================================================================
|
||||
# #region docker.backend.entrypoint.bootstrap_admin [C:2] [TYPE Function]
|
||||
# @BRIEF Создание admin пользователя из переменных окружения (идемпотентно).
|
||||
# @PRE INITIAL_ADMIN_CREATE=true, INITIAL_ADMIN_USERNAME и INITIAL_ADMIN_PASSWORD заданы.
|
||||
# @POST Admin пользователь создан в БД (если ещё не существовал).
|
||||
# @LAYER Infrastructure
|
||||
bootstrap_admin() {
|
||||
local create_flag="${INITIAL_ADMIN_CREATE:-false}"
|
||||
local username="${INITIAL_ADMIN_USERNAME:-}"
|
||||
@@ -24,55 +93,64 @@ bootstrap_admin() {
|
||||
true|1|yes|y)
|
||||
;;
|
||||
*)
|
||||
echo "[entrypoint] INITIAL_ADMIN_CREATE is disabled; skipping admin bootstrap"
|
||||
echo "[entrypoint] INITIAL_ADMIN_CREATE disabled — пропускаем bootstrap admin"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -z "${username}" ]]; then
|
||||
echo "[entrypoint] INITIAL_ADMIN_USERNAME is required when INITIAL_ADMIN_CREATE=true" >&2
|
||||
echo "[entrypoint] INITIAL_ADMIN_USERNAME required when INITIAL_ADMIN_CREATE=true" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -z "${password}" ]]; then
|
||||
echo "[entrypoint] INITIAL_ADMIN_PASSWORD is required when INITIAL_ADMIN_CREATE=true" >&2
|
||||
echo "[entrypoint] INITIAL_ADMIN_PASSWORD required when INITIAL_ADMIN_CREATE=true" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "[entrypoint] initializing auth database"
|
||||
echo "[entrypoint] Initializing auth database..."
|
||||
python3 src/scripts/init_auth_db.py
|
||||
|
||||
echo "[entrypoint] running idempotent admin bootstrap for user '${username}'"
|
||||
echo "[entrypoint] Creating admin user '${username}' (idempotent)..."
|
||||
if [[ -n "${email}" ]]; then
|
||||
python3 src/scripts/create_admin.py --username "${username}" --password "${password}" --email "${email}"
|
||||
else
|
||||
python3 src/scripts/create_admin.py --username "${username}" --password "${password}"
|
||||
fi
|
||||
}
|
||||
# [/DEF:docker.backend.entrypoint.bootstrap_admin:Function]
|
||||
|
||||
# [DEF:docker.backend.entrypoint.install_playwright:Function]
|
||||
# @PURPOSE: Lazily install Playwright Chromium browser on first container start.
|
||||
# Browser binaries (~377 MB) are downloaded only once and cached in
|
||||
# PLAYWRIGHT_BROWSERS_PATH (default: ~/.cache/ms-playwright).
|
||||
# Use a Docker volume or bind mount to persist across restarts:
|
||||
# -v playwright_cache:/root/.cache/ms-playwright
|
||||
echo "[entrypoint] ✅ Admin bootstrap complete"
|
||||
}
|
||||
# #endregion docker.backend.entrypoint.bootstrap_admin
|
||||
|
||||
# ======================================================================
|
||||
# install_playwright — lazy установка Chromium
|
||||
# ======================================================================
|
||||
# #region docker.backend.entrypoint.install_playwright [C:2] [TYPE Function]
|
||||
# @BRIEF Lazy установка Playwright Chromium (~377 MB) при первом запуске.
|
||||
# @PRE ~/.cache/ms-playwright доступен (можно закешировать volume).
|
||||
# @POST Chromium установлен в PLAYWRIGHT_BROWSERS_PATH.
|
||||
# @LAYER Infrastructure
|
||||
install_playwright() {
|
||||
local pw_dir="${PLAYWRIGHT_BROWSERS_PATH:-$HOME/.cache/ms-playwright}"
|
||||
if [ -f "${pw_dir}/chromium-*/chrome-linux64/chrome" ]; then
|
||||
if ls "${pw_dir}"/chromium-*/chrome-linux64/chrome 1>/dev/null 2>&1; then
|
||||
echo "[entrypoint] Playwright Chromium already installed at ${pw_dir}"
|
||||
return 0
|
||||
fi
|
||||
echo "[entrypoint] Installing Playwright Chromium browser (first start only)..."
|
||||
echo "[entrypoint] This downloads ~377 MB. Use a volume to cache:"
|
||||
echo " -v playwright_cache:/root/.cache/ms-playwright"
|
||||
python3 -m playwright install chromium --with-deps 2>&1
|
||||
echo "[entrypoint] Playwright Chromium installed successfully"
|
||||
}
|
||||
# [/DEF:docker.backend.entrypoint.install_playwright:Function]
|
||||
|
||||
echo "[entrypoint] Installing Playwright Chromium (first start only, ~377 MB)..."
|
||||
echo "[entrypoint] Cache with: -v playwright_cache:/root/.cache/ms-playwright"
|
||||
python3 -m playwright install chromium --with-deps 2>&1
|
||||
echo "[entrypoint] ✅ Playwright Chromium installed"
|
||||
}
|
||||
# #endregion docker.backend.entrypoint.install_playwright
|
||||
|
||||
# ======================================================================
|
||||
# MAIN
|
||||
# ======================================================================
|
||||
|
||||
install_certificates
|
||||
install_playwright
|
||||
bootstrap_admin
|
||||
|
||||
echo "[entrypoint] starting backend: $*"
|
||||
echo "[entrypoint] Starting backend: $*"
|
||||
exec "$@"
|
||||
|
||||
Reference in New Issue
Block a user