Комментарии про методы входа приведены к факту (логин/пароль и Telegram везде, stub только в development): config.py, .env.example, telegram.py, dev_stub.py, dev_auth.py, login.py. admin_login.py больше не обещает «строже игроцкого» — описаны реальные лимиты; client_ip — адрес за прокси уже даёт uvicorn. Подсказки деплоя: run.ps1/run.sh (build-push на ПК, `up -d` на Pi), compose, .env.example (build-push.ps1), Caddyfile (туннель-контейнер вместо autossh); ratelimit.py ссылается на core/events.py вместо CLAUDE.md. Удалены неиспользуемые scripts/export-prod.sh (с блоком export-ignore в .gitattributes и упоминаниями в README), db/init_db.py и случайно закоммиченный файл «h -u origin dev…». #70 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
96 lines
5.8 KiB
Bash
96 lines
5.8 KiB
Bash
#!/usr/bin/env bash
|
||
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
||
# ║ Единый лаунчер dev. Что запускать — решает APP_ENV из корневого .env ║
|
||
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
||
# development → uvicorn --reload (бэк) + vite (фронт), нативно (Ctrl+C останавливает оба)
|
||
# production → лаунчером НЕ запускается (прод обособлен: docker compose up -d на Pi)
|
||
#
|
||
# LOCAL_PUBLIC=vps поднимает SSH-туннель на VPS → https://forbidden-stars.ru
|
||
# Прод выставляет себя сам через туннель-контейнер (см. docker-compose.yml).
|
||
#
|
||
# Запуск: ./run.sh (при необходимости: chmod +x run.sh)
|
||
set -euo pipefail
|
||
root="$(cd "$(dirname "$0")" && pwd)"
|
||
|
||
envf="$root/.env"
|
||
[ -f "$envf" ] || { echo "Нет .env в корне. Скопируйте: cp .env.example .env"; exit 1; }
|
||
|
||
# Достаём значение ключа из .env (последнее присвоение, без комментариев/пробелов).
|
||
env_val() {
|
||
grep -E "^[[:space:]]*$1[[:space:]]*=" "$envf" | tail -n1 \
|
||
| sed -E 's/^[^=]*=[[:space:]]*//; s/[[:space:]#].*$//'
|
||
}
|
||
|
||
app_env="$(env_val APP_ENV | tr 'A-Z' 'a-z')"; app_env="${app_env:-development}"
|
||
local_public="$(env_val LOCAL_PUBLIC | tr 'A-Z' 'a-z')"; local_public="${local_public:-local}"
|
||
vps_host="$(env_val VPS_TUNNEL_HOST)"
|
||
vps_user="$(env_val VPS_TUNNEL_USER)"; vps_user="${vps_user:-tunnel}"
|
||
vps_port="$(env_val VPS_TUNNEL_PORT)"; vps_port="${vps_port:-9001}"
|
||
echo "APP_ENV = $app_env LOCAL_PUBLIC = $local_public"
|
||
|
||
# Поднимает SSH reverse-tunnel в фоне: VPS:$vps_port → localhost:$1 (если LOCAL_PUBLIC=vps).
|
||
start_tunnel() {
|
||
local local_port="$1"
|
||
if [ -z "$vps_host" ]; then
|
||
echo "LOCAL_PUBLIC=vps, но VPS_TUNNEL_HOST не задан — туннель пропущен."; return
|
||
fi
|
||
echo "SSH-туннель: forbidden-stars.ru (VPS:$vps_port) → localhost:$local_port …"
|
||
ssh -N -R "${vps_port}:localhost:${local_port}" \
|
||
-o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes \
|
||
"${vps_user}@${vps_host}" &
|
||
tunnel=$!
|
||
trap 'kill "$tunnel" 2>/dev/null || true' EXIT INT TERM
|
||
echo " Публично: https://forbidden-stars.ru"
|
||
}
|
||
|
||
case "$app_env" in
|
||
development)
|
||
py="$root/backend/.venv/bin/python"
|
||
[ -x "$py" ] || py="$root/backend/.venv/Scripts/python.exe" # Git Bash на Windows
|
||
[ -x "$py" ] || { echo "Нет venv. Сначала: cd backend && python -m venv .venv && pip install -e '.[dev]'"; exit 1; }
|
||
[ -d "$root/frontend/node_modules" ] || { echo "Нет node_modules. Сначала: cd frontend && npm install"; exit 1; }
|
||
# Сначала схема: ветка могла принести миграцию, и без неё бэк поднимется на
|
||
# старой схеме и упадёт 500 на первом же обращении к новой таблице.
|
||
echo "Применяю миграции (alembic upgrade head)…"
|
||
mig_log="$( cd "$root/backend" && "$py" -m alembic upgrade head 2>&1 )" && mig_code=0 || mig_code=$?
|
||
if [ "$mig_code" -ne 0 ]; then
|
||
echo "$mig_log"
|
||
case "$mig_log" in
|
||
*"locate revision"*)
|
||
# В базе миграция, которой нет в этой ветке (вернулись с ветки задачи).
|
||
# Лишние таблицы старому коду не мешают — продолжаем запуск.
|
||
echo "База новее ветки — миграции пропущены." ;;
|
||
*)
|
||
echo "Миграции не применились — не запускаю. Исправьте ошибку выше и повторите."
|
||
exit 1 ;;
|
||
esac
|
||
fi
|
||
|
||
echo "Запуск бэка (uvicorn --reload) и фронта (vite)… Ctrl+C — остановить всё."
|
||
# --timeout-graceful-shutdown: открытая вкладка держит SSE-поток /api/events вечно, а при
|
||
# reload старый процесс ждёт закрытия ВСЕХ соединений — без лимита перезапуск не
|
||
# завершается, и сайт висит на загрузке без единой ошибки в логе.
|
||
( cd "$root/backend" && exec "$py" -m uvicorn app.main:app --reload --timeout-graceful-shutdown 2 ) &
|
||
back=$!
|
||
trap 'kill "$back" 2>/dev/null || true' EXIT INT TERM
|
||
echo " Бэк: http://127.0.0.1:8000 (Swagger: /api/docs)"
|
||
echo " Фронт: http://127.0.0.1:5173"
|
||
if [ "$local_public" = "vps" ]; then
|
||
start_tunnel 5173
|
||
echo " ВНИМАНИЕ: dev опубликован. Любой посетитель может войти по нику без пароля,"
|
||
echo " смотреть и создавать игроков, жёстко удалять аккаунты и читать Swagger."
|
||
echo " Не держите в dev-базе копию прод-данных."
|
||
fi
|
||
( cd "$root/frontend" && npm run dev )
|
||
;;
|
||
production)
|
||
echo "production лаунчером не запускается — прод обособлен."
|
||
echo "Деплой: на ПК (ветка main) scripts/build-push.sh, затем на Pi: docker compose up -d"
|
||
exit 1
|
||
;;
|
||
*)
|
||
echo "Неизвестный APP_ENV='$app_env'. Допустимо: development | production."
|
||
exit 1
|
||
;;
|
||
esac
|