новый лендинг
This commit is contained in:
Executable
+300
@@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
|
||||
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-${ROOT_DIR}/storage/backups}"
|
||||
MYSQL_CLIENT_IMAGE="${MYSQL_CLIENT_IMAGE:-mysql:8.4}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Использование:
|
||||
./scripts/db-backup.sh export [--target=all|app|auth|characters] [--out=DIR]
|
||||
./scripts/db-backup.sh import <файл.sql[.gz]> --target=app|auth|characters
|
||||
./scripts/db-backup.sh list
|
||||
|
||||
Опции:
|
||||
--env=PATH путь к .env (по умолчанию ./.env)
|
||||
--target=NAME какая БД: app (laravel), auth (acore_auth),
|
||||
characters (acore_characters), all (по умолчанию для export)
|
||||
--out=DIR куда писать дампы (по умолчанию storage/backups)
|
||||
--gzip сжимать дамп gzip-ом (по умолчанию включено для export)
|
||||
--no-gzip не сжимать
|
||||
--force для import: не спрашивать подтверждение
|
||||
--create-db перед импортом создать БД, если её нет
|
||||
-h, --help показать справку
|
||||
|
||||
Переменные окружения:
|
||||
ENV_FILE, BACKUP_DIR — переопределяют значения по умолчанию
|
||||
MYSQL_CLIENT_IMAGE — образ для фолбэка (по умолчанию mysql:8.4)
|
||||
|
||||
Зависимости:
|
||||
Если на хосте установлены mysqldump/mysql — используются они.
|
||||
Иначе скрипт запускает их в одноразовом контейнере MYSQL_CLIENT_IMAGE.
|
||||
|
||||
Примеры:
|
||||
./scripts/db-backup.sh export
|
||||
./scripts/db-backup.sh export --target=auth --out=/tmp
|
||||
./scripts/db-backup.sh import /tmp/acore_auth_2026-04-26.sql.gz --target=auth
|
||||
./scripts/db-backup.sh import dump.sql.gz --target=app --create-db --force
|
||||
EOF
|
||||
}
|
||||
|
||||
log() { printf '\033[1;34m[db-backup]\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33m[db-backup]\033[0m %s\n' "$*" >&2; }
|
||||
err() { printf '\033[1;31m[db-backup]\033[0m %s\n' "$*" >&2; }
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || { err "Не найдена команда: $1"; exit 1; }
|
||||
}
|
||||
|
||||
# Безопасное чтение значения из .env (поддержка кавычек)
|
||||
env_get() {
|
||||
local key="$1" file="$2"
|
||||
local line value
|
||||
line="$(grep -E "^${key}=" "$file" | tail -n1 || true)"
|
||||
[[ -z "$line" ]] && return 0
|
||||
value="${line#${key}=}"
|
||||
# снять обрамляющие кавычки
|
||||
if [[ "$value" =~ ^\".*\"$ ]] || [[ "$value" =~ ^\'.*\'$ ]]; then
|
||||
value="${value:1:${#value}-2}"
|
||||
fi
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
load_target() {
|
||||
local target="$1"
|
||||
case "$target" in
|
||||
app)
|
||||
DB_HOST="$(env_get DB_HOST "$ENV_FILE")"
|
||||
DB_PORT="$(env_get DB_PORT "$ENV_FILE")"
|
||||
DB_NAME="$(env_get DB_DATABASE "$ENV_FILE")"
|
||||
DB_USER="$(env_get DB_USERNAME "$ENV_FILE")"
|
||||
DB_PASS="$(env_get DB_PASSWORD "$ENV_FILE")"
|
||||
;;
|
||||
auth)
|
||||
DB_HOST="$(env_get AZEROTHCORE_AUTH_DB_HOST "$ENV_FILE")"
|
||||
DB_PORT="$(env_get AZEROTHCORE_AUTH_DB_PORT "$ENV_FILE")"
|
||||
DB_NAME="$(env_get AZEROTHCORE_AUTH_DB_DATABASE "$ENV_FILE")"
|
||||
DB_USER="$(env_get AZEROTHCORE_AUTH_DB_USERNAME "$ENV_FILE")"
|
||||
DB_PASS="$(env_get AZEROTHCORE_AUTH_DB_PASSWORD "$ENV_FILE")"
|
||||
;;
|
||||
characters)
|
||||
DB_HOST="$(env_get AZEROTHCORE_CHARACTERS_DB_HOST "$ENV_FILE")"
|
||||
DB_PORT="$(env_get AZEROTHCORE_CHARACTERS_DB_PORT "$ENV_FILE")"
|
||||
DB_NAME="$(env_get AZEROTHCORE_CHARACTERS_DB_DATABASE "$ENV_FILE")"
|
||||
DB_USER="$(env_get AZEROTHCORE_CHARACTERS_DB_USERNAME "$ENV_FILE")"
|
||||
DB_PASS="$(env_get AZEROTHCORE_CHARACTERS_DB_PASSWORD "$ENV_FILE")"
|
||||
;;
|
||||
*)
|
||||
err "Неизвестная цель: $target"; exit 2;;
|
||||
esac
|
||||
|
||||
# docker-only хосты заменяем на localhost при запуске вне контейнера
|
||||
if [[ "$DB_HOST" == "mysql" || "$DB_HOST" == "host.docker.internal" ]]; then
|
||||
local fwd
|
||||
fwd="$(env_get FORWARD_DB_PORT "$ENV_FILE")"
|
||||
if [[ -n "$fwd" && "$target" == "app" ]]; then
|
||||
DB_PORT="$fwd"
|
||||
fi
|
||||
DB_HOST="127.0.0.1"
|
||||
fi
|
||||
|
||||
if [[ -z "$DB_NAME" || -z "$DB_USER" ]]; then
|
||||
err "Не удалось определить параметры подключения для target=$target"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
have_native_mysql() {
|
||||
command -v mysqldump >/dev/null 2>&1 && command -v mysql >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Запуск mysqldump/mysql либо на хосте, либо в одноразовом docker-контейнере.
|
||||
# Стдин/стдаут пробрасываются — годится и для дампа, и для импорта по pipe.
|
||||
# $1 — "mysqldump" или "mysql"; остальные — доп.аргументы тулзе.
|
||||
# Подключение читается из глобалей DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME.
|
||||
run_mysql_tool() {
|
||||
local tool="$1"; shift
|
||||
|
||||
if have_native_mysql; then
|
||||
local defaults
|
||||
defaults="$(mktemp)"
|
||||
chmod 600 "$defaults"
|
||||
{
|
||||
printf '[client]\n'
|
||||
printf 'host=%s\n' "$DB_HOST"
|
||||
printf 'port=%s\n' "${DB_PORT:-3306}"
|
||||
printf 'user=%s\n' "$DB_USER"
|
||||
if [[ -n "${DB_PASS:-}" && "$DB_PASS" != "null" ]]; then
|
||||
printf 'password=%s\n' "$DB_PASS"
|
||||
fi
|
||||
} > "$defaults"
|
||||
local rc=0
|
||||
"$tool" --defaults-extra-file="$defaults" "$@" || rc=$?
|
||||
rm -f "$defaults"
|
||||
return $rc
|
||||
fi
|
||||
|
||||
require_cmd docker
|
||||
|
||||
local target_host="$DB_HOST"
|
||||
if [[ "$target_host" == "127.0.0.1" || "$target_host" == "localhost" ]]; then
|
||||
target_host="host.docker.internal"
|
||||
fi
|
||||
|
||||
local pwd_value=""
|
||||
if [[ -n "${DB_PASS:-}" && "$DB_PASS" != "null" ]]; then
|
||||
pwd_value="$DB_PASS"
|
||||
fi
|
||||
|
||||
docker run --rm -i \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
-e MYSQL_PWD="$pwd_value" \
|
||||
"$MYSQL_CLIENT_IMAGE" \
|
||||
"$tool" \
|
||||
-h"$target_host" \
|
||||
-P"${DB_PORT:-3306}" \
|
||||
-u"$DB_USER" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
do_export_one() {
|
||||
local target="$1" out_dir="$2" use_gzip="$3"
|
||||
load_target "$target"
|
||||
|
||||
local ts dump_path
|
||||
ts="$(date +%Y-%m-%d_%H-%M-%S)"
|
||||
dump_path="${out_dir}/${DB_NAME}_${ts}.sql"
|
||||
|
||||
log "Экспорт ${target} (${DB_NAME}@${DB_HOST}:${DB_PORT:-3306}) → ${dump_path}$([[ $use_gzip -eq 1 ]] && echo .gz)"
|
||||
|
||||
local -a dump_args=(
|
||||
--single-transaction
|
||||
--quick
|
||||
--routines
|
||||
--triggers
|
||||
--events
|
||||
--set-gtid-purged=OFF
|
||||
--hex-blob
|
||||
--default-character-set=utf8mb4
|
||||
--no-tablespaces
|
||||
"$DB_NAME"
|
||||
)
|
||||
|
||||
if [[ "$use_gzip" -eq 1 ]]; then
|
||||
require_cmd gzip
|
||||
run_mysql_tool mysqldump "${dump_args[@]}" | gzip -c > "${dump_path}.gz"
|
||||
log "Готово: ${dump_path}.gz ($(du -h "${dump_path}.gz" | cut -f1))"
|
||||
else
|
||||
run_mysql_tool mysqldump "${dump_args[@]}" > "$dump_path"
|
||||
log "Готово: ${dump_path} ($(du -h "$dump_path" | cut -f1))"
|
||||
fi
|
||||
}
|
||||
|
||||
do_export() {
|
||||
local target="$1" out_dir="$2" use_gzip="$3"
|
||||
mkdir -p "$out_dir"
|
||||
if [[ "$target" == "all" ]]; then
|
||||
for t in app auth characters; do
|
||||
do_export_one "$t" "$out_dir" "$use_gzip"
|
||||
done
|
||||
else
|
||||
do_export_one "$target" "$out_dir" "$use_gzip"
|
||||
fi
|
||||
}
|
||||
|
||||
do_import() {
|
||||
local file="$1" target="$2" force="$3" create_db="$4"
|
||||
[[ -f "$file" ]] || { err "Файл не найден: $file"; exit 1; }
|
||||
[[ -z "$target" ]] && { err "Для import обязателен --target"; exit 2; }
|
||||
|
||||
load_target "$target"
|
||||
|
||||
if [[ "$force" -ne 1 ]]; then
|
||||
printf '\033[1;33m[!] Импорт перезапишет данные в %s@%s:%s. Продолжить? [y/N]: \033[0m' \
|
||||
"$DB_NAME" "$DB_HOST" "${DB_PORT:-3306}"
|
||||
read -r answer
|
||||
[[ "$answer" =~ ^[yYдД]$ ]] || { warn "Отменено"; exit 0; }
|
||||
fi
|
||||
|
||||
if [[ "$create_db" -eq 1 ]]; then
|
||||
log "Создаю БД ${DB_NAME} при отсутствии"
|
||||
local saved_name="$DB_NAME"
|
||||
DB_NAME=""
|
||||
run_mysql_tool mysql -e \
|
||||
"CREATE DATABASE IF NOT EXISTS \`$saved_name\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
|
||||
DB_NAME="$saved_name"
|
||||
fi
|
||||
|
||||
log "Импорт ${file} → ${target} (${DB_NAME}@${DB_HOST}:${DB_PORT:-3306})"
|
||||
|
||||
if [[ "$file" == *.gz ]]; then
|
||||
require_cmd gzip
|
||||
gzip -dc "$file" | run_mysql_tool mysql "$DB_NAME"
|
||||
else
|
||||
run_mysql_tool mysql "$DB_NAME" < "$file"
|
||||
fi
|
||||
|
||||
log "Импорт завершён"
|
||||
}
|
||||
|
||||
do_list() {
|
||||
if [[ ! -d "$BACKUP_DIR" ]]; then
|
||||
warn "Директория бэкапов отсутствует: $BACKUP_DIR"
|
||||
return 0
|
||||
fi
|
||||
log "Бэкапы в $BACKUP_DIR:"
|
||||
ls -lh "$BACKUP_DIR" 2>/dev/null | awk 'NR>1 {print " " $9 " (" $5 ", " $6, $7, $8 ")"}'
|
||||
}
|
||||
|
||||
main() {
|
||||
[[ $# -lt 1 ]] && { usage; exit 2; }
|
||||
local cmd="$1"; shift
|
||||
|
||||
local target="" out_dir="$BACKUP_DIR" use_gzip=1 force=0 create_db=0 import_file=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--env=*) ENV_FILE="${1#*=}";;
|
||||
--target=*) target="${1#*=}";;
|
||||
--out=*) out_dir="${1#*=}";;
|
||||
--gzip) use_gzip=1;;
|
||||
--no-gzip) use_gzip=0;;
|
||||
--force) force=1;;
|
||||
--create-db) create_db=1;;
|
||||
-h|--help) usage; exit 0;;
|
||||
-*) err "Неизвестная опция: $1"; usage; exit 2;;
|
||||
*) import_file="$1";;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[[ -f "$ENV_FILE" ]] || { err ".env не найден: $ENV_FILE"; exit 1; }
|
||||
|
||||
case "$cmd" in
|
||||
export)
|
||||
[[ -z "$target" ]] && target="all"
|
||||
do_export "$target" "$out_dir" "$use_gzip"
|
||||
;;
|
||||
import)
|
||||
[[ -z "$import_file" ]] && { err "Укажите путь к дампу"; usage; exit 2; }
|
||||
do_import "$import_file" "$target" "$force" "$create_db"
|
||||
;;
|
||||
list)
|
||||
do_list
|
||||
;;
|
||||
-h|--help|help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
err "Неизвестная команда: $cmd"
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user