Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ, инструкция пользователя, техдокументация, код-стайл), бэклог, скрипты развёртывания и архив прототипа LeadRadar.
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# deal-backup-lib.sh — общие для scripts/backup.sh и scripts/restore.sh env-дефолты и хелперы
|
||||
# (этап 7 Task 15, Ruling 8). Скрипт НЕ исполняется напрямую: подключается из backup.sh/restore.sh
|
||||
# . "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/deal-backup-lib.sh"
|
||||
#
|
||||
# Параметры — окружение (секреты скрипты НЕ логируют; в лог — только имена файлов и размеры):
|
||||
#
|
||||
# BACKUP_DIR / DEAL_BACKUP_DIR корень бэкапов (по умолчанию <репозиторий>/data/backups)
|
||||
# RETENTION_DAYS / DEAL_RETENTION_DAYS
|
||||
# сколько дней хранить снапшоты (удаление по дате YYYYMMDD в имени; 14)
|
||||
# DEAL_DATA_DIR корень host-каталогов данных (по умолчанию <репозиторий>/data)
|
||||
# DEAL_TAR_DIRS относительные к DEAL_DATA_DIR каталоги для тара (по умолчанию
|
||||
# "attachments ml telegram_sessions"; контейнерный путь каталога
|
||||
# сессий telegram-service — /data/sessions)
|
||||
# DEAL_TAR_VOLUMES если задан (список docker volume'ов через пробел) — файловые данные
|
||||
# берутся из томов busybox-контейнером (Ruling 8), DEAL_TAR_DIRS
|
||||
# игнорируется
|
||||
#
|
||||
# Postgres (docker-режим по умолчанию — exec в контейнер; прямой pg_dump — при заданном DEAL_PG_HOST):
|
||||
# DEAL_PG_HOST / DEAL_PG_PORT (5432) / DEAL_PG_USER (deal) / DEAL_PG_PASSWORD / DEAL_PG_DB (deal)
|
||||
# DEAL_PG_CONTAINER имя контейнера (авто: deal-postgres → compose-сервис 'postgres')
|
||||
#
|
||||
# MinIO / mc (бэкап = выгрузка ИЗ MinIO в BACKUP_DIR; restore — обратно):
|
||||
# DEAL_MINIO_ENDPOINT пусто → host-mc: http://localhost:9000 (dev: опубликованный порт);
|
||||
# docker-mc: http://minio:9000 (алиас compose-сервиса — работает и в
|
||||
# compose.dev, и в compose.prod). prod (compose.prod) порты MinIO НЕ
|
||||
# публикует — хостовый mc его не достанет: нужен docker-режим (дефолт,
|
||||
# endpoint minio:9000) либо достижимый DEAL_MINIO_ENDPOINT.
|
||||
# DEAL_MINIO_ACCESS_KEY (fallback: MINIO_ROOT_USER; иначе deal_minio)
|
||||
# DEAL_MINIO_SECRET_KEY (fallback: MINIO_ROOT_PASSWORD; иначе deal_minio_secret)
|
||||
# DEAL_MINIO_BUCKET (deal-files)
|
||||
# DEAL_MINIO_SKIP 1 → шаг MinIO пропускается (Local-хранилище без MinIO)
|
||||
# DEAL_MINIO_MIRROR_REMOVE restore: 1 → mc mirror --remove (бакет = снимок 1:1)
|
||||
# DEAL_MC_IMAGE docker-режим mc (minio/mc; в проде зафиксируйте тег, напр.
|
||||
# minio/mc:RELEASE.2025-04-16T15-23-31Z)
|
||||
# DEAL_MC_CONTAINER контейнер MinIO для сети (авто: deal-minio → compose-сервис 'minio')
|
||||
set -u
|
||||
|
||||
# --- Корень репозитория и каталоги -----------------------------------------------
|
||||
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
REPO_DIR=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
|
||||
|
||||
BACKUP_DIR=${BACKUP_DIR:-${DEAL_BACKUP_DIR:-"$REPO_DIR/data/backups"}}
|
||||
RETENTION_DAYS=${RETENTION_DAYS:-${DEAL_RETENTION_DAYS:-14}}
|
||||
DEAL_DATA_DIR=${DEAL_DATA_DIR:-"$REPO_DIR/data"}
|
||||
DEAL_TAR_DIRS=${DEAL_TAR_DIRS:-"attachments ml telegram_sessions"}
|
||||
DEAL_TAR_VOLUMES=${DEAL_TAR_VOLUMES:-""}
|
||||
|
||||
# --- Postgres ----------------------------------------------------------------------
|
||||
DEAL_PG_DB=${DEAL_PG_DB:-deal}
|
||||
DEAL_PG_USER=${DEAL_PG_USER:-deal}
|
||||
DEAL_PG_HOST=${DEAL_PG_HOST:-""}
|
||||
DEAL_PG_PORT=${DEAL_PG_PORT:-5432}
|
||||
DEAL_PG_PASSWORD=${DEAL_PG_PASSWORD:-""}
|
||||
DEAL_PG_CONTAINER=${DEAL_PG_CONTAINER:-""}
|
||||
|
||||
# --- MinIO / mc --------------------------------------------------------------------
|
||||
DEAL_MINIO_BUCKET=${DEAL_MINIO_BUCKET:-deal-files}
|
||||
DEAL_MINIO_ENDPOINT=${DEAL_MINIO_ENDPOINT:-""}
|
||||
DEAL_MINIO_ACCESS_KEY=${DEAL_MINIO_ACCESS_KEY:-${MINIO_ROOT_USER:-deal_minio}}
|
||||
DEAL_MINIO_SECRET_KEY=${DEAL_MINIO_SECRET_KEY:-${MINIO_ROOT_PASSWORD:-deal_minio_secret}}
|
||||
DEAL_MINIO_SKIP=${DEAL_MINIO_SKIP:-0}
|
||||
DEAL_MINIO_MIRROR_REMOVE=${DEAL_MINIO_MIRROR_REMOVE:-0}
|
||||
DEAL_MC_IMAGE=${DEAL_MC_IMAGE:-minio/mc}
|
||||
DEAL_MC_CONTAINER=${DEAL_MC_CONTAINER:-""}
|
||||
|
||||
# Найденные рантайм-значения (заполняют функции ниже)
|
||||
PG_CONTAINER=""
|
||||
MINIO_CONTAINER=""
|
||||
MC_MODE="" # skip | host (клиент mc) | docker (разовый контейнер minio/mc)
|
||||
MC_ENDPOINT="$DEAL_MINIO_ENDPOINT"
|
||||
|
||||
# --- Утилиты -----------------------------------------------------------------------
|
||||
say() { printf '%s\n' "$*"; }
|
||||
log() { printf '[%s] %s\n' "$(date '+%F %T')" "$*"; }
|
||||
die() { log "ОШИБКА: $*"; exit 1; }
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
docker_ok() { docker info >/dev/null 2>&1; }
|
||||
|
||||
# --- Windows (Git Bash/MSYS): docker не понимает /c/... пути и ломает контейнерные
|
||||
# /out, /in (MSYS конвертирует их в C:/Program Files/Git/...). Отключаем конвертацию
|
||||
# аргументов и переводим host-пути в Windows-вид (C:/...) для -v/-cp. На Linux хелпер
|
||||
# возвращает путь как есть — поведение не меняется.
|
||||
if uname -s 2>/dev/null | grep -qiE 'mingw|msys|cygwin'; then
|
||||
export MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*'
|
||||
host_docker_path() { cygpath -m "$1"; }
|
||||
else
|
||||
host_docker_path() { printf '%s' "$1"; }
|
||||
fi
|
||||
|
||||
container_running() { # имя контейнера → 0, если запущен
|
||||
[ -n "${1:-}" ] && [ "$(docker inspect -f '{{.State.Running}}' "$1" 2>/dev/null)" = "true" ]
|
||||
}
|
||||
|
||||
compose_container() { # метка com.docker.compose.service → имя running-контейнера (или пусто)
|
||||
docker ps --filter "label=com.docker.compose.service=$1" --format '{{.Names}}' 2>/dev/null | head -n 1 || true
|
||||
}
|
||||
|
||||
# --- Поиск контейнеров Postgres / MinIO --------------------------------------------
|
||||
resolve_pg_container() {
|
||||
local c
|
||||
if [ -n "$DEAL_PG_CONTAINER" ]; then
|
||||
container_running "$DEAL_PG_CONTAINER" \
|
||||
|| die "контейнер DEAL_PG_CONTAINER='$DEAL_PG_CONTAINER' не найден/не запущен"
|
||||
PG_CONTAINER="$DEAL_PG_CONTAINER"
|
||||
return 0
|
||||
fi
|
||||
if container_running deal-postgres; then PG_CONTAINER=deal-postgres; return 0; fi
|
||||
c=$(compose_container postgres)
|
||||
if [ -n "$c" ] && container_running "$c"; then PG_CONTAINER="$c"; return 0; fi
|
||||
return 1
|
||||
}
|
||||
|
||||
resolve_minio_container() {
|
||||
local c
|
||||
if [ -n "$DEAL_MC_CONTAINER" ]; then
|
||||
container_running "$DEAL_MC_CONTAINER" \
|
||||
|| die "контейнер DEAL_MC_CONTAINER='$DEAL_MC_CONTAINER' не найден/не запущен"
|
||||
MINIO_CONTAINER="$DEAL_MC_CONTAINER"
|
||||
return 0
|
||||
fi
|
||||
if container_running deal-minio; then MINIO_CONTAINER=deal-minio; return 0; fi
|
||||
c=$(compose_container minio)
|
||||
if [ -n "$c" ] && container_running "$c"; then MINIO_CONTAINER="$c"; return 0; fi
|
||||
return 1
|
||||
}
|
||||
|
||||
minio_network() { # первая docker-сеть контейнера MinIO (для разового контейнера mc)
|
||||
docker inspect -f '{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}' \
|
||||
"$MINIO_CONTAINER" 2>/dev/null | awk '{ print $1 }'
|
||||
}
|
||||
|
||||
# --- mc: выбор режима и запуск -------------------------------------------------------
|
||||
select_mc_mode() { # заполняет MC_MODE / MC_ENDPOINT
|
||||
if [ "$DEAL_MINIO_SKIP" = "1" ] || [ "$DEAL_MINIO_SKIP" = "true" ]; then
|
||||
MC_MODE=skip
|
||||
elif have mc; then
|
||||
MC_MODE=host
|
||||
[ -n "$MC_ENDPOINT" ] || MC_ENDPOINT=http://localhost:9000
|
||||
elif docker_ok; then
|
||||
MC_MODE=docker
|
||||
# minio — алиас compose-сервиса: резолвится и в compose.dev (container_name deal-minio), и в
|
||||
# compose.prod (container_name нет, сервис 'minio'); deal-minio:9000 в prod-сети не существует.
|
||||
[ -n "$MC_ENDPOINT" ] || MC_ENDPOINT=http://minio:9000
|
||||
else
|
||||
die "MinIO: клиент mc не найден и docker недоступен — установите mc или задайте DEAL_MINIO_SKIP=1"
|
||||
fi
|
||||
}
|
||||
|
||||
mc_cmd() { # выполняет «mc <аргументы…>» в выбранном режиме; alias 'deal' — только env MC_HOST_deal
|
||||
# (секрет в конфиг mc на диск НЕ пишется; значение env нигде не логируется)
|
||||
# MC_HOST_deal — единый URL http(s)://user:pass@host:port: схему берём из MC_ENDPOINT (если задана),
|
||||
# иначе http. Убираем схему из host:port — иначе URL получает двойную схему и mc его отвергает.
|
||||
local host_value endpoint scheme
|
||||
endpoint="$MC_ENDPOINT"
|
||||
scheme=http
|
||||
case "$endpoint" in
|
||||
https://*) scheme=https; endpoint=${endpoint#https://} ;;
|
||||
http://*) endpoint=${endpoint#http://} ;;
|
||||
esac
|
||||
host_value="$scheme://$DEAL_MINIO_ACCESS_KEY:$DEAL_MINIO_SECRET_KEY@$endpoint"
|
||||
case "$MC_MODE" in
|
||||
skip)
|
||||
log "MinIO: шаг пропущен (DEAL_MINIO_SKIP=1)"
|
||||
return 0
|
||||
;;
|
||||
host)
|
||||
MC_HOST_deal="$host_value" mc "$@"
|
||||
;;
|
||||
docker)
|
||||
resolve_minio_container \
|
||||
|| die "MinIO: контейнер не найден (искал deal-minio и compose-сервис 'minio') — поднимите стек, задайте DEAL_MINIO_ENDPOINT или DEAL_MINIO_SKIP=1"
|
||||
log "MinIO: разовый контейнер $DEAL_MC_IMAGE (сеть: $(minio_network), бакет $DEAL_MINIO_BUCKET)"
|
||||
docker run --rm -i --network "$(minio_network)" \
|
||||
-e "MC_HOST_deal=$host_value" \
|
||||
-v "$(host_docker_path "$BACKUP_DIR"):$BACKUP_DIR" \
|
||||
"$DEAL_MC_IMAGE" "$@"
|
||||
;;
|
||||
*)
|
||||
die "MinIO: режим mc не выбран (MC_MODE='$MC_MODE') — вызовите select_mc_mode"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
Reference in New Issue
Block a user