fix: setup.sh doctor/update crash fixes
- Remove set -e (((issues++)) returns exit 1 when 0 → kills script) - Fix .env.example parser: skip comments, validate var names with regex - Fix update: skip git pull when user declines stash (was running anyway) - Add --accept-data-loss to prisma db push (prevents interactive hang) - Replace ((var++)) with var=$((var + 1)) for safety with pipefail Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
101
setup.sh
101
setup.sh
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# LiveServer-M1 — Universal Setup / Update / Doctor Script
|
# LiveServer-M1 — Universal Setup / Update / Doctor Script
|
||||||
@@ -325,8 +325,8 @@ cmd_doctor() {
|
|||||||
log_ok "$cmd: $(command -v "$cmd")"
|
log_ok "$cmd: $(command -v "$cmd")"
|
||||||
else
|
else
|
||||||
log_err "$cmd не найден"
|
log_err "$cmd не найден"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
install_if_missing "$cmd" "см. документацию" && ((fixed++)) || true
|
install_if_missing "$cmd" "см. документацию" && fixed=$((fixed + 1)) || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -335,16 +335,16 @@ cmd_doctor() {
|
|||||||
log_ok "docker compose: $(docker compose version --short 2>/dev/null)"
|
log_ok "docker compose: $(docker compose version --short 2>/dev/null)"
|
||||||
else
|
else
|
||||||
log_err "docker compose V2 не найден"
|
log_err "docker compose V2 не найден"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if docker info &>/dev/null; then
|
if docker info &>/dev/null; then
|
||||||
log_ok "Docker daemon запущен"
|
log_ok "Docker daemon запущен"
|
||||||
else
|
else
|
||||||
log_err "Docker daemon не запущен"
|
log_err "Docker daemon не запущен"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
log_fix "Запускаю Docker..."
|
log_fix "Запускаю Docker..."
|
||||||
sudo systemctl start docker 2>/dev/null && ((fixed++)) || log_warn "Не удалось запустить Docker"
|
sudo systemctl start docker 2>/dev/null && fixed=$((fixed + 1)) || log_warn "Не удалось запустить Docker"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -352,7 +352,7 @@ cmd_doctor() {
|
|||||||
log_step "2/8 — Файл .env"
|
log_step "2/8 — Файл .env"
|
||||||
if [[ ! -f .env ]]; then
|
if [[ ! -f .env ]]; then
|
||||||
log_err ".env не найден"
|
log_err ".env не найден"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
if ask_yn "Запустить полную установку?" "y"; then
|
if ask_yn "Запустить полную установку?" "y"; then
|
||||||
cmd_install
|
cmd_install
|
||||||
return
|
return
|
||||||
@@ -367,7 +367,7 @@ cmd_doctor() {
|
|||||||
val=$(env_get "$var")
|
val=$(env_get "$var")
|
||||||
if [[ -z "$val" ]]; then
|
if [[ -z "$val" ]]; then
|
||||||
log_err "${var} не задан в .env"
|
log_err "${var} не задан в .env"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -377,22 +377,27 @@ cmd_doctor() {
|
|||||||
pg_pass=$(env_get POSTGRES_PASSWORD)
|
pg_pass=$(env_get POSTGRES_PASSWORD)
|
||||||
if [[ "$pg_pass" == "postgres" ]]; then
|
if [[ "$pg_pass" == "postgres" ]]; then
|
||||||
log_warn "POSTGRES_PASSWORD = 'postgres' в production — небезопасно!"
|
log_warn "POSTGRES_PASSWORD = 'postgres' в production — небезопасно!"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
local minio_pass
|
local minio_pass
|
||||||
minio_pass=$(env_get MINIO_ROOT_PASSWORD)
|
minio_pass=$(env_get MINIO_ROOT_PASSWORD)
|
||||||
if [[ "$minio_pass" == "minioadmin" ]]; then
|
if [[ "$minio_pass" == "minioadmin" ]]; then
|
||||||
log_warn "MINIO_ROOT_PASSWORD = 'minioadmin' в production — небезопасно!"
|
log_warn "MINIO_ROOT_PASSWORD = 'minioadmin' в production — небезопасно!"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Проверить что .env.example не содержит новых переменных
|
# Проверить что .env.example не содержит новых переменных
|
||||||
if [[ -f .env.example ]]; then
|
if [[ -f .env.example ]]; then
|
||||||
local missing_in_env=0
|
local missing_in_env=0
|
||||||
while IFS='=' read -r key _; do
|
while IFS= read -r line; do
|
||||||
key=$(echo "$key" | sed 's/^#\s*//' | xargs)
|
# Пропустить пустые строки и комментарии
|
||||||
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
|
||||||
|
# Извлечь ключ (до первого =)
|
||||||
|
local key="${line%%=*}"
|
||||||
|
# Убрать пробелы и проверить что это валидное имя переменной
|
||||||
|
key=$(echo "$key" | tr -d '[:space:]')
|
||||||
|
[[ -z "$key" || ! "$key" =~ ^[A-Z_][A-Z0-9_]*$ ]] && continue
|
||||||
if ! grep -qE "^${key}=" .env 2>/dev/null; then
|
if ! grep -qE "^${key}=" .env 2>/dev/null; then
|
||||||
if [[ "$missing_in_env" -eq 0 ]]; then
|
if [[ "$missing_in_env" -eq 0 ]]; then
|
||||||
log_warn "Новые переменные из .env.example отсутствуют в .env:"
|
log_warn "Новые переменные из .env.example отсутствуют в .env:"
|
||||||
@@ -401,7 +406,7 @@ cmd_doctor() {
|
|||||||
missing_in_env=1
|
missing_in_env=1
|
||||||
fi
|
fi
|
||||||
done < .env.example
|
done < .env.example
|
||||||
[[ "$missing_in_env" -eq 1 ]] && ((issues++))
|
[[ "$missing_in_env" -eq 1 ]] && issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -414,14 +419,14 @@ cmd_doctor() {
|
|||||||
type_val=$(node -e "console.log(require('./package.json').type || '')" 2>/dev/null || echo "")
|
type_val=$(node -e "console.log(require('./package.json').type || '')" 2>/dev/null || echo "")
|
||||||
if [[ -n "$type_val" ]]; then
|
if [[ -n "$type_val" ]]; then
|
||||||
log_err "package.json содержит \"type\": \"${type_val}\" — вызывает ESM/CJS конфликт с Turbopack"
|
log_err "package.json содержит \"type\": \"${type_val}\" — вызывает ESM/CJS конфликт с Turbopack"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
log_fix "Удаляю поле \"type\" из package.json..."
|
log_fix "Удаляю поле \"type\" из package.json..."
|
||||||
node -e "
|
node -e "
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const pkg = JSON.parse(fs.readFileSync('package.json','utf8'));
|
const pkg = JSON.parse(fs.readFileSync('package.json','utf8'));
|
||||||
delete pkg.type;
|
delete pkg.type;
|
||||||
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
||||||
" 2>/dev/null && { log_ok "\"type\" удалён"; ((fixed++)); } || log_warn "Не удалось автоисправить"
|
" 2>/dev/null && { log_ok "\"type\" удалён"; fixed=$((fixed + 1)); } || log_warn "Не удалось автоисправить"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
log_ok "package.json: нет конфликтного поля \"type\""
|
log_ok "package.json: нет конфликтного поля \"type\""
|
||||||
@@ -430,18 +435,18 @@ cmd_doctor() {
|
|||||||
# node_modules
|
# node_modules
|
||||||
if [[ ! -d "node_modules" ]]; then
|
if [[ ! -d "node_modules" ]]; then
|
||||||
log_warn "node_modules отсутствует"
|
log_warn "node_modules отсутствует"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
log_fix "Запускаю npm install..."
|
log_fix "Запускаю npm install..."
|
||||||
npm install 2>&1 | tail -5
|
npm install 2>&1 | tail -5
|
||||||
log_ok "npm install завершён"
|
log_ok "npm install завершён"
|
||||||
((fixed++))
|
fixed=$((fixed + 1))
|
||||||
elif [[ "package.json" -nt "node_modules/.package-lock.json" ]] 2>/dev/null; then
|
elif [[ "package.json" -nt "node_modules/.package-lock.json" ]] 2>/dev/null; then
|
||||||
log_warn "node_modules устарел (package.json новее)"
|
log_warn "node_modules устарел (package.json новее)"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
log_fix "Запускаю npm install..."
|
log_fix "Запускаю npm install..."
|
||||||
npm install 2>&1 | tail -5
|
npm install 2>&1 | tail -5
|
||||||
log_ok "npm install обновлён"
|
log_ok "npm install обновлён"
|
||||||
((fixed++))
|
fixed=$((fixed + 1))
|
||||||
else
|
else
|
||||||
log_ok "node_modules актуален"
|
log_ok "node_modules актуален"
|
||||||
fi
|
fi
|
||||||
@@ -451,14 +456,14 @@ cmd_doctor() {
|
|||||||
log_ok "Prisma Client сгенерирован"
|
log_ok "Prisma Client сгенерирован"
|
||||||
else
|
else
|
||||||
log_warn "Prisma Client не найден"
|
log_warn "Prisma Client не найден"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
log_fix "Генерирую Prisma Client..."
|
log_fix "Генерирую Prisma Client..."
|
||||||
npx prisma generate 2>&1 | tail -2
|
npx prisma generate 2>&1 | tail -2
|
||||||
((fixed++))
|
fixed=$((fixed + 1))
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
log_err "package.json не найден — это не корень проекта?"
|
log_err "package.json не найден — это не корень проекта?"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 4. Docker контейнеры ---
|
# --- 4. Docker контейнеры ---
|
||||||
@@ -475,7 +480,7 @@ cmd_doctor() {
|
|||||||
;;
|
;;
|
||||||
unhealthy)
|
unhealthy)
|
||||||
log_warn "${svc}: unhealthy"
|
log_warn "${svc}: unhealthy"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
log_fix "Перезапускаю ${svc}..."
|
log_fix "Перезапускаю ${svc}..."
|
||||||
docker compose restart "$svc" 2>/dev/null
|
docker compose restart "$svc" 2>/dev/null
|
||||||
sleep 3
|
sleep 3
|
||||||
@@ -483,14 +488,14 @@ cmd_doctor() {
|
|||||||
new_state=$(check_service "$svc")
|
new_state=$(check_service "$svc")
|
||||||
if [[ "$new_state" == "ok" ]]; then
|
if [[ "$new_state" == "ok" ]]; then
|
||||||
log_ok "${svc}: восстановлен"
|
log_ok "${svc}: восстановлен"
|
||||||
((fixed++))
|
fixed=$((fixed + 1))
|
||||||
else
|
else
|
||||||
log_err "${svc}: всё ещё проблема после рестарта"
|
log_err "${svc}: всё ещё проблема после рестарта"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
log_err "${svc}: ${state}"
|
log_err "${svc}: ${state}"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
stopped_services+=("$svc")
|
stopped_services+=("$svc")
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -505,7 +510,7 @@ cmd_doctor() {
|
|||||||
new_state=$(check_service "$svc")
|
new_state=$(check_service "$svc")
|
||||||
if [[ "$new_state" == "ok" ]]; then
|
if [[ "$new_state" == "ok" ]]; then
|
||||||
log_ok "${svc}: запущен"
|
log_ok "${svc}: запущен"
|
||||||
((fixed++))
|
fixed=$((fixed + 1))
|
||||||
else
|
else
|
||||||
log_err "${svc}: не удалось запустить"
|
log_err "${svc}: не удалось запустить"
|
||||||
fi
|
fi
|
||||||
@@ -520,7 +525,7 @@ cmd_doctor() {
|
|||||||
log_ok "PostgreSQL: pg_isready OK"
|
log_ok "PostgreSQL: pg_isready OK"
|
||||||
else
|
else
|
||||||
log_err "PostgreSQL: pg_isready FAIL"
|
log_err "PostgreSQL: pg_isready FAIL"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Проверить что БД liveserver существует
|
# Проверить что БД liveserver существует
|
||||||
@@ -528,9 +533,9 @@ cmd_doctor() {
|
|||||||
log_ok "БД 'liveserver' существует"
|
log_ok "БД 'liveserver' существует"
|
||||||
else
|
else
|
||||||
log_warn "БД 'liveserver' не найдена"
|
log_warn "БД 'liveserver' не найдена"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
log_fix "Создаю БД..."
|
log_fix "Создаю БД..."
|
||||||
docker compose exec -T postgres createdb -U postgres liveserver 2>/dev/null && ((fixed++)) || true
|
docker compose exec -T postgres createdb -U postgres liveserver 2>/dev/null && fixed=$((fixed + 1)) || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Проверить схему (есть ли таблицы)
|
# Проверить схему (есть ли таблицы)
|
||||||
@@ -540,10 +545,10 @@ cmd_doctor() {
|
|||||||
log_ok "Схема БД: ${table_count} таблиц"
|
log_ok "Схема БД: ${table_count} таблиц"
|
||||||
else
|
else
|
||||||
log_warn "Схема БД пуста"
|
log_warn "Схема БД пуста"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
log_fix "Применяю Prisma schema..."
|
log_fix "Применяю Prisma schema..."
|
||||||
npx prisma db push --skip-generate 2>&1 | tail -3
|
npx prisma db push --skip-generate --accept-data-loss 2>&1 | tail -3
|
||||||
((fixed++))
|
fixed=$((fixed + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# PgBouncer
|
# PgBouncer
|
||||||
@@ -552,7 +557,7 @@ cmd_doctor() {
|
|||||||
log_ok "PgBouncer: подключение OK"
|
log_ok "PgBouncer: подключение OK"
|
||||||
else
|
else
|
||||||
log_warn "PgBouncer: не отвечает через pg_isready"
|
log_warn "PgBouncer: не отвечает через pg_isready"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@@ -566,7 +571,7 @@ cmd_doctor() {
|
|||||||
log_ok "Redis PING → PONG"
|
log_ok "Redis PING → PONG"
|
||||||
else
|
else
|
||||||
log_err "Redis не отвечает на PING"
|
log_err "Redis не отвечает на PING"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local redis_mem
|
local redis_mem
|
||||||
@@ -599,7 +604,7 @@ cmd_doctor() {
|
|||||||
log_ok "MinIO health: live"
|
log_ok "MinIO health: live"
|
||||||
else
|
else
|
||||||
log_warn "MinIO health endpoint не отвечает (может быть закрыт порт)"
|
log_warn "MinIO health endpoint не отвечает (может быть закрыт порт)"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Проверить bucket
|
# Проверить bucket
|
||||||
@@ -610,14 +615,14 @@ cmd_doctor() {
|
|||||||
log_ok "MinIO: bucket '${bucket}' доступен"
|
log_ok "MinIO: bucket '${bucket}' доступен"
|
||||||
else
|
else
|
||||||
log_warn "MinIO: bucket '${bucket}' недоступен (HTTP ${bucket_check})"
|
log_warn "MinIO: bucket '${bucket}' недоступен (HTTP ${bucket_check})"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
log_fix "Создаю bucket..."
|
log_fix "Создаю bucket..."
|
||||||
if check_command mc; then
|
if check_command mc; then
|
||||||
mc alias set local "${s3_endpoint}" "$minio_user" "$minio_pass" 2>/dev/null || true
|
mc alias set local "${s3_endpoint}" "$minio_user" "$minio_pass" 2>/dev/null || true
|
||||||
mc mb "local/${bucket}" 2>/dev/null || true
|
mc mb "local/${bucket}" 2>/dev/null || true
|
||||||
((fixed++))
|
fixed=$((fixed + 1))
|
||||||
else
|
else
|
||||||
curl -sf -o /dev/null -X PUT "${s3_endpoint}/${bucket}" -u "${minio_user}:${minio_pass}" 2>/dev/null && ((fixed++)) || \
|
curl -sf -o /dev/null -X PUT "${s3_endpoint}/${bucket}" -u "${minio_user}:${minio_pass}" 2>/dev/null && fixed=$((fixed + 1)) || \
|
||||||
log_warn "Создай вручную: http://localhost:9001"
|
log_warn "Создай вручную: http://localhost:9001"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -637,11 +642,11 @@ cmd_doctor() {
|
|||||||
log_ok "BUILD_ID: $(cat .next/BUILD_ID)"
|
log_ok "BUILD_ID: $(cat .next/BUILD_ID)"
|
||||||
else
|
else
|
||||||
log_warn ".next может быть повреждён (нет BUILD_ID)"
|
log_warn ".next может быть повреждён (нет BUILD_ID)"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
if ask_yn "Очистить .next кеш?" "y"; then
|
if ask_yn "Очистить .next кеш?" "y"; then
|
||||||
rm -rf .next
|
rm -rf .next
|
||||||
log_fix "Кеш очищен"
|
log_fix "Кеш очищен"
|
||||||
((fixed++))
|
fixed=$((fixed + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@@ -651,7 +656,7 @@ cmd_doctor() {
|
|||||||
# Проверить lock file конфликт
|
# Проверить lock file конфликт
|
||||||
if [[ -f "package-lock.json" ]] && [[ -f "yarn.lock" ]]; then
|
if [[ -f "package-lock.json" ]] && [[ -f "yarn.lock" ]]; then
|
||||||
log_warn "Найдены И package-lock.json И yarn.lock — выбери один менеджер"
|
log_warn "Найдены И package-lock.json И yarn.lock — выбери один менеджер"
|
||||||
((issues++))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Итого ---
|
# --- Итого ---
|
||||||
@@ -690,6 +695,7 @@ cmd_update() {
|
|||||||
log_info "Текущая ветка: ${branch}"
|
log_info "Текущая ветка: ${branch}"
|
||||||
|
|
||||||
# Проверить uncommitted changes
|
# Проверить uncommitted changes
|
||||||
|
local skip_pull=false
|
||||||
if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
|
if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
|
||||||
log_warn "Есть незакоммиченные изменения"
|
log_warn "Есть незакоммиченные изменения"
|
||||||
if ask_yn "Сохранить через git stash и продолжить?" "y"; then
|
if ask_yn "Сохранить через git stash и продолжить?" "y"; then
|
||||||
@@ -697,11 +703,14 @@ cmd_update() {
|
|||||||
log_ok "Изменения сохранены в stash"
|
log_ok "Изменения сохранены в stash"
|
||||||
else
|
else
|
||||||
log_warn "Пропускаю git pull"
|
log_warn "Пропускаю git pull"
|
||||||
|
skip_pull=true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git pull --rebase 2>&1 | tail -5
|
if [[ "$skip_pull" == "false" ]]; then
|
||||||
log_ok "git pull завершён"
|
git pull --rebase 2>&1 | tail -5
|
||||||
|
log_ok "git pull завершён"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
log_warn "Не git-репозиторий — пропускаю git pull"
|
log_warn "Не git-репозиторий — пропускаю git pull"
|
||||||
fi
|
fi
|
||||||
@@ -719,7 +728,7 @@ cmd_update() {
|
|||||||
# Проверить нужна ли миграция
|
# Проверить нужна ли миграция
|
||||||
if docker compose exec -T postgres pg_isready -U postgres &>/dev/null; then
|
if docker compose exec -T postgres pg_isready -U postgres &>/dev/null; then
|
||||||
log_info "Применяю schema changes..."
|
log_info "Применяю schema changes..."
|
||||||
npx prisma db push --skip-generate 2>&1 | tail -3
|
npx prisma db push --skip-generate --accept-data-loss 2>&1 | tail -3
|
||||||
log_ok "Схема БД синхронизирована"
|
log_ok "Схема БД синхронизирована"
|
||||||
else
|
else
|
||||||
log_warn "PostgreSQL недоступен — миграция пропущена"
|
log_warn "PostgreSQL недоступен — миграция пропущена"
|
||||||
@@ -980,7 +989,7 @@ ENVEOF
|
|||||||
log_warn "PgBouncer не отвечает — продолжаем без него"
|
log_warn "PgBouncer не отвечает — продолжаем без него"
|
||||||
|
|
||||||
echo -e " Применение миграций..."
|
echo -e " Применение миграций..."
|
||||||
npx prisma db push --skip-generate 2>&1 | tail -3
|
npx prisma db push --skip-generate --accept-data-loss 2>&1 | tail -3
|
||||||
log_ok "Схема БД синхронизирована"
|
log_ok "Схема БД синхронизирована"
|
||||||
|
|
||||||
echo -e " Создание S3 bucket..."
|
echo -e " Создание S3 bucket..."
|
||||||
|
|||||||
Reference in New Issue
Block a user