#!/usr/bin/env bash # Интерактивное управление: остановка, запуск, перезапуск Docker-сервисов проекта. # Запуск: chmod +x manage.sh && ./manage.sh set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' ENV_FILE="$SCRIPT_DIR/.env" PROFILE_PLACEHOLDER=(--profile placeholder) # Имена из docker-compose.yml (для надёжной проверки состояния) PG_CONTAINER="l2_postgres" L2_CONTAINER="l2_server_placeholder" read_env_val() { local key="$1" grep -m1 "^${key}=" "$ENV_FILE" | sed "s/^${key}=//" } ask_yes_no() { local prompt="$1" local default="${2:-n}" local hint="[y/N]" [[ "$default" == "y" ]] && hint="[Y/n]" while true; do read -r -p "$prompt $hint: " ans ans="${ans:-$default}" case "$ans" in [Yy]|[Yy][Ee][Ss]) return 0 ;; [Nn]|[Nn][Oo]) return 1 ;; *) echo "Введите y или n." ;; esac done } info() { echo -e "${GREEN}[*]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } err() { echo -e "${RED}[x]${NC} $*" >&2; } step_header() { echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " $1" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" } require_cmd() { command -v "$1" >/dev/null 2>&1 } ensure_docker() { if ! require_cmd docker || ! docker compose version >/dev/null 2>&1; then err "Нужны Docker и плагин «docker compose». Установите через ./install.sh" exit 1 fi if ! docker info >/dev/null 2>&1; then err "Docker недоступен (sudo или перелогин после группы docker)." exit 1 fi } ensure_env() { if [[ ! -f "$ENV_FILE" ]]; then err "Нет файла .env. Сначала выполните ./install.sh" exit 1 fi } postgres_running() { [[ "$(docker inspect -f '{{.State.Running}}' "$PG_CONTAINER" 2>/dev/null || echo false)" == "true" ]] } l2_running() { [[ "$(docker inspect -f '{{.State.Running}}' "$L2_CONTAINER" 2>/dev/null || echo false)" == "true" ]] } # --- Шаг 0 --- step_header "Шаг 0 — Управление сервером (Docker Compose)" echo "Проект: PostgreSQL 17 + опционально l2-server (profile placeholder)." if ! ask_yes_no "Продолжить?" "y"; then info "Выход." exit 0 fi ensure_docker ensure_env # --- Шаг 1: статус --- step_header "Шаг 1 — Текущий статус" if ask_yes_no "Показать docker compose ps?" "y"; then docker compose "${PROFILE_PLACEHOLDER[@]}" ps -a || true fi # --- Шаг 2: остановка --- step_header "Шаг 2 — Остановка" if ask_yes_no "Остановить контейнер l2-server (если был запущен с profile placeholder)?" "n"; then docker compose "${PROFILE_PLACEHOLDER[@]}" stop l2-server 2>/dev/null || warn "l2-server не остановлен (возможно не запускался)." fi if ask_yes_no "Остановить только PostgreSQL (docker compose stop postgres)?" "n"; then docker compose stop postgres 2>/dev/null || warn "PostgreSQL уже остановлен или не создавался." fi if ask_yes_no "Полная остановка проекта: docker compose down (все сервисы, сеть; тома БД не удаляются)?" "n"; then docker compose "${PROFILE_PLACEHOLDER[@]}" down info "Контейнеры остановлены. Данные БД в томе postgres_data сохранены." fi if ask_yes_no "Удалить том с данными PostgreSQL (docker compose down -v)? ОПАСНО: потеря БД." "n"; then warn "Будет выполнено: docker compose --profile placeholder down -v" if ask_yes_no "Точно удалить тома?" "n"; then docker compose "${PROFILE_PLACEHOLDER[@]}" down -v info "Тома удалены." else info "Отмена удаления томов." fi fi # --- Шаг 3: запуск --- step_header "Шаг 3 — Запуск" if ask_yes_no "Запустить / обновить PostgreSQL (up -d postgres)?" "n"; then docker compose up -d postgres info "Ожидание pg_isready..." EV_USER="$(read_env_val POSTGRES_USER)" EV_DB="$(read_env_val POSTGRES_DB)" for _ in $(seq 1 60); do if docker compose exec -T postgres pg_isready -U "$EV_USER" -d "$EV_DB" >/dev/null 2>&1; then break fi sleep 1 done fi if ask_yes_no "Запустить l2-server (placeholder, нужен здоровый PostgreSQL)?" "n"; then if ! postgres_running; then warn "PostgreSQL не запущен. Сначала поднимите postgres." else docker compose "${PROFILE_PLACEHOLDER[@]}" up -d l2-server fi fi # --- Шаг 4: перезапуск --- step_header "Шаг 4 — Перезапуск" if ask_yes_no "Перезапустить PostgreSQL (restart postgres)?" "n"; then if postgres_running; then docker compose restart postgres info "PostgreSQL перезапущен." else warn "PostgreSQL не в состоянии running. Выполняю up -d postgres." docker compose up -d postgres fi fi if ask_yes_no "Перезапустить l2-server (restart l2-server)?" "n"; then if l2_running; then docker compose "${PROFILE_PLACEHOLDER[@]}" restart l2-server info "l2-server перезапущен." else warn "l2-server не запущен. Пропуск." fi fi if ask_yes_no "Перезапустить весь стек: сначала down, затем up postgres (+ опционально l2)?" "n"; then docker compose "${PROFILE_PLACEHOLDER[@]}" down docker compose up -d postgres EV_USER="$(read_env_val POSTGRES_USER)" EV_DB="$(read_env_val POSTGRES_DB)" for _ in $(seq 1 60); do if docker compose exec -T postgres pg_isready -U "$EV_USER" -d "$EV_DB" >/dev/null 2>&1; then break fi sleep 1 done if ask_yes_no "Сразу поднять l2-server (placeholder)?" "n"; then docker compose "${PROFILE_PLACEHOLDER[@]}" up -d l2-server fi info "Стек перезапущен." fi # --- Итог --- step_header "Готово" if ask_yes_no "Показать итоговый статус?" "y"; then docker compose "${PROFILE_PLACEHOLDER[@]}" ps -a || true fi info "Логи: docker compose logs -f postgres" info "Выход из проекта без удаления томов: docker compose --profile placeholder down"