/
zemars
/
it-dashboard
Обзор
Документация
Войти
/
zemars
/
it-dashboard
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
dev.sh
279 строк
11 KB
Дмитрий
fix(docs): актуализировать risk_log и docstring init_db
26 май 2026, 22:07
26 май 2026, 22:07
9981d43
Код
Авторство
О чём код?
#!/usr/bin/env bash # Автор: Максим Печенкин # Единый скрипт для локальной разработки IT Dashboard. # # Использование: # ./dev.sh — поднять всё (db в docker, backend uvicorn, frontend vite) # ./dev.sh stop — остановить всё, удалить контейнер db # ./dev.sh status — показать состояние процессов и портов # ./dev.sh logs — посмотреть логи backend+frontend (нужен tmux/iterm — здесь tail) # ./dev.sh reset — stop + удаление volume БД + .venv + node_modules # # Что делает по шагам: # 1. Поднимает postgres-контейнер (docker compose up -d db) # 2. Ждёт healthcheck # 3. Создаёт backend/.venv (если нет), ставит requirements # 4. Применяет alembic-миграции и создаёт тестовых пользователей # 5. Запускает uvicorn --reload на :8000 # 6. Делает npm install (если node_modules нет) # 7. Запускает vite на :3000 с VITE_API_URL=http://localhost:8000 # # Hot-reload: backend (.py) и frontend (.jsx/.css) перезагружаются автоматически. set -e cd "$(dirname "$0")" ROOT="$(pwd)" PIDFILE="$ROOT/.dev.pids" LOG_DIR="$ROOT/.dev-logs" # ----- цвета для вывода ----- if [ -t 1 ]; then C_RESET=$'\033[0m' C_BOLD=$'\033[1m' C_BLUE=$'\033[34m' C_GREEN=$'\033[32m' C_YELLOW=$'\033[33m' C_RED=$'\033[31m' C_DIM=$'\033[2m' else C_RESET=""; C_BOLD=""; C_BLUE=""; C_GREEN=""; C_YELLOW=""; C_RED=""; C_DIM="" fi log() { printf "%s[dev]%s %s\n" "$C_BOLD$C_BLUE" "$C_RESET" "$*"; } ok() { printf "%s ✓%s %s\n" "$C_GREEN" "$C_RESET" "$*"; } warn() { printf "%s ⚠%s %s\n" "$C_YELLOW" "$C_RESET" "$*"; } err() { printf "%s ✗%s %s\n" "$C_RED" "$C_RESET" "$*" >&2; } # ----- остановка процессов ----- stop_processes() { if [ -f "$PIDFILE" ]; then while IFS=' ' read -r pid name; do if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then log "kill $name (pid $pid)" # сначала вежливо kill "$pid" 2>/dev/null || true # дочерним процессам тоже (vite запускает node, uvicorn — детей-воркеров) pkill -P "$pid" 2>/dev/null || true fi done < "$PIDFILE" # дать процессам время выйти sleep 1 # добить если выжили while IFS=' ' read -r pid name; do if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then kill -9 "$pid" 2>/dev/null || true fi done < "$PIDFILE" rm -f "$PIDFILE" fi } cleanup_db() { if docker ps --format '{{.Names}}' | grep -q '^itdash-db$'; then log "останавливаю контейнер itdash-db" docker compose stop db >/dev/null 2>&1 || true docker compose rm -f db >/dev/null 2>&1 || true fi } # ----- проверка зависимостей ----- check_deps() { local missing=() command -v docker >/dev/null 2>&1 || missing+=("docker") command -v python3 >/dev/null 2>&1 || missing+=("python3") command -v node >/dev/null 2>&1 || missing+=("node") command -v npm >/dev/null 2>&1 || missing+=("npm") if [ ${#missing[@]} -gt 0 ]; then err "Не установлены: ${missing[*]}" exit 1 fi if ! docker info >/dev/null 2>&1; then err "Docker daemon не запущен. Запустите Docker Desktop и повторите." exit 1 fi } # ----- ожидание health postgres ----- wait_for_db() { log "жду healthy postgres..." for i in $(seq 1 30); do # healthcheck подсвечивается в `docker inspect` status=$(docker inspect --format '{{.State.Health.Status}}' itdash-db 2>/dev/null || echo "starting") if [ "$status" = "healthy" ]; then ok "postgres готов (через ${i}с)" return 0 fi sleep 1 done err "postgres не стал healthy за 30с" return 1 } # ----- subcommands ----- cmd_up() { check_deps mkdir -p "$LOG_DIR" : > "$PIDFILE" # .env (нужен docker compose) if [ ! -f .env ]; then log "создаю .env из .env.example" cp .env.example .env fi log "поднимаю postgres в docker..." docker compose up -d db >/dev/null wait_for_db || { cleanup_db; exit 1; } # ------- backend ------- if [ ! -x backend/.venv/bin/python ]; then log "создаю backend/.venv..." rm -rf backend/.venv python3 -m venv backend/.venv fi if [ ! -x backend/.venv/bin/alembic ] || [ backend/requirements.txt -nt backend/.venv/bin/alembic ]; then log "ставлю requirements.txt (может занять 1-2 мин при первом запуске)..." backend/.venv/bin/pip install -q --upgrade pip backend/.venv/bin/pip install -q -r backend/requirements.txt ok "зависимости backend готовы" fi export DATABASE_URL="postgresql+psycopg2://itdash:itdash_pwd@localhost:5432/itdash" export ALLOWED_ORIGINS="*" export JWT_SECRET_KEY="${JWT_SECRET_KEY:-dev-secret-change-me-please-0123456789abcdef}" log "alembic upgrade head" (cd backend && ../backend/.venv/bin/alembic upgrade head) | sed 's/^/ /' log "init_db (тестовые пользователи)" (cd backend && ../backend/.venv/bin/python -m app.init_db) | sed 's/^/ /' log "запускаю uvicorn на :8000 (логи -> .dev-logs/backend.log)" ( cd backend nohup ./.venv/bin/uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 \ > "$LOG_DIR/backend.log" 2>&1 & echo "$! backend" >> "$PIDFILE" ) # ------- frontend ------- if [ ! -d frontend/node_modules ]; then log "npm install (может занять 1-2 мин)..." (cd frontend && npm install --no-audit --no-fund --silent) ok "node_modules готов" fi log "запускаю vite на :3000 (логи -> .dev-logs/frontend.log)" ( cd frontend nohup env VITE_API_URL=http://localhost:8000 npm run dev \ > "$LOG_DIR/frontend.log" 2>&1 & echo "$! frontend" >> "$PIDFILE" ) # подождём пока vite/uvicorn реально откроют порты sleep 3 log "проверяю что сервисы отвечают..." if curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/health | grep -q 200; then ok "backend: http://localhost:8000/health → 200" else warn "backend не отвечает (см. .dev-logs/backend.log)" fi if curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 | grep -qE '^(200|304)$'; then ok "frontend: http://localhost:3000 → 200" else warn "frontend не отвечает (см. .dev-logs/frontend.log)" fi cat <<EOF ${C_BOLD}════════════════════════════════════════════════════════════${C_RESET} ${C_GREEN}Dev-окружение запущено${C_RESET} ${C_BOLD}Frontend:${C_RESET} http://localhost:3000 (Vite HMR) ${C_BOLD}Backend:${C_RESET} http://localhost:8000 (uvicorn --reload) ${C_BOLD}Swagger:${C_RESET} http://localhost:8000/docs ${C_BOLD}DB:${C_RESET} localhost:5432 (docker) ${C_BOLD}Логи:${C_RESET} tail -f .dev-logs/{backend,frontend}.log ${C_BOLD}Управление:${C_RESET} ./dev.sh status статус процессов и портов ./dev.sh logs показать последние строки логов ./dev.sh stop остановить всё ./dev.sh reset + удалить .venv, node_modules, volume БД ${C_DIM}Логины: analyst/analyst или operator/operator${C_RESET} ${C_BOLD}════════════════════════════════════════════════════════════${C_RESET} EOF } cmd_stop() { log "останавливаю dev-окружение..." stop_processes cleanup_db ok "остановлено" } cmd_status() { echo "${C_BOLD}Процессы:${C_RESET}" if [ -f "$PIDFILE" ]; then while IFS=' ' read -r pid name; do if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then ok "$name (pid $pid) — RUNNING" else warn "$name (pid $pid) — STOPPED" fi done < "$PIDFILE" else warn "PID-файл отсутствует (видимо, ничего не запущено)" fi echo "" echo "${C_BOLD}Порты:${C_RESET}" for port in 3000 8000 5432; do if lsof -i :"$port" -sTCP:LISTEN >/dev/null 2>&1; then who=$(lsof -i :"$port" -sTCP:LISTEN -P -n 2>/dev/null | awk 'NR==2{print $1}') ok ":$port — слушает ($who)" else warn ":$port — свободен" fi done echo "" echo "${C_BOLD}Docker:${C_RESET}" docker ps --filter "name=itdash-" --format 'table {{.Names}}\t{{.Status}}' 2>/dev/null || true } cmd_logs() { if [ ! -d "$LOG_DIR" ]; then warn "Логов ещё нет — запустите ./dev.sh" return fi echo "${C_BOLD}--- последние 20 строк backend.log ---${C_RESET}" tail -n 20 "$LOG_DIR/backend.log" 2>/dev/null || echo "(нет)" echo "" echo "${C_BOLD}--- последние 20 строк frontend.log ---${C_RESET}" tail -n 20 "$LOG_DIR/frontend.log" 2>/dev/null || echo "(нет)" echo "" echo "${C_DIM}Для непрерывного просмотра: tail -f .dev-logs/{backend,frontend}.log${C_RESET}" } cmd_reset() { log "RESET: остановка + удаление .venv, node_modules, volume БД" stop_processes cleanup_db docker volume rm -f it-dashboard_pgdata >/dev/null 2>&1 || true rm -rf backend/.venv frontend/node_modules "$LOG_DIR" "$PIDFILE" ok "всё очищено. Следующий запуск пересоздаст окружение с нуля." } # ----- main ----- case "${1:-up}" in up|start|"") cmd_up ;; stop|down) cmd_stop ;; status|ps) cmd_status ;; logs|log) cmd_logs ;; reset|clean) cmd_reset ;; *) echo "Usage: $0 {up|stop|status|logs|reset}" exit 1 ;; esac