#!/usr/bin/env bash # dump_project.sh — собирает всё нужное для аудита проекта в один файл # Запуск: sudo bash dump_project.sh # Результат: /root/morozovka_dump_YYYY-MM-DD_HH-MM.txt set -uo pipefail PROJECT_DIR="/opt/morozovka-portal" OUT="/root/morozovka_dump_$(date +%F_%H-%M).txt" MAX_FILE_SIZE=$((512 * 1024)) # файлы больше 512 КБ пропускаем (обычно мусор) # ----- исключения ----- EXCLUDES=( "*/venv/*" "*/__pycache__/*" "*.pyc" "*/.git/*" "*/node_modules/*" "*.log" "*.sqlite" "*.db" "google_credentials.json" # СЕКРЕТ — не включаем "*.png" "*.jpg" "*.jpeg" "*.gif" "*.ico" "*.pdf" "*.zip" "*.tar" "*.gz" "*.woff" "*.woff2" "*.ttf" ) should_skip() { local f="$1" for pat in "${EXCLUDES[@]}"; do # shellcheck disable=SC2053 [[ "$f" == $pat ]] && return 0 done return 1 } section() { echo "" >> "$OUT" echo "================================================================================" >> "$OUT" echo "== $*" >> "$OUT" echo "================================================================================" >> "$OUT" } dump_file() { local f="$1" [[ -f "$f" ]] || { echo "[skip: not a file] $f" >> "$OUT"; return; } local size size=$(stat -c%s "$f" 2>/dev/null || echo 0) if (( size > MAX_FILE_SIZE )); then echo "" >> "$OUT" echo "---- FILE: $f (SKIPPED, size=${size}B > ${MAX_FILE_SIZE}B) ----" >> "$OUT" return fi echo "" >> "$OUT" echo "---- FILE: $f (size=${size}B) ----" >> "$OUT" cat "$f" >> "$OUT" echo "" >> "$OUT" echo "---- END: $f ----" >> "$OUT" } # ----- начинаем ----- : > "$OUT" echo "DUMP GENERATED: $(date -Is)" >> "$OUT" echo "HOSTNAME: $(hostname -f 2>/dev/null || hostname)" >> "$OUT" echo "UPTIME: $(uptime -p 2>/dev/null || uptime)" >> "$OUT" # 1) Дерево файлов проекта section "TREE: $PROJECT_DIR" if command -v tree >/dev/null 2>&1; then tree -a -I 'venv|__pycache__|.git|node_modules' -L 4 "$PROJECT_DIR" >> "$OUT" 2>&1 else find "$PROJECT_DIR" \ \( -path '*/venv' -o -path '*/__pycache__' -o -path '*/.git' -o -path '*/node_modules' \) -prune -o \ -type f -print | sort >> "$OUT" fi # 2) requirements.txt + версия Python section "ENVIRONMENT" echo "python: $(command -v python3) $(python3 --version 2>&1)" >> "$OUT" if [[ -f "$PROJECT_DIR/requirements.txt" ]]; then echo "" >> "$OUT" echo "--- pip freeze (из venv) ---" >> "$OUT" if [[ -x "$PROJECT_DIR/venv/bin/pip" ]]; then "$PROJECT_DIR/venv/bin/pip" freeze >> "$OUT" 2>&1 else cat "$PROJECT_DIR/requirements.txt" >> "$OUT" fi fi # 3) Все текстовые файлы проекта section "PROJECT FILES ($PROJECT_DIR)" while IFS= read -r -d '' f; do should_skip "$f" && continue # бинарные — пропускаем по mime if command -v file >/dev/null 2>&1; then case "$(file -b --mime-encoding "$f" 2>/dev/null)" in binary) echo "" >> "$OUT"; echo "---- BINARY SKIPPED: $f ----" >> "$OUT"; continue ;; esac fi dump_file "$f" done < <(find "$PROJECT_DIR" \ \( -path '*/venv' -o -path '*/__pycache__' -o -path '*/.git' -o -path '*/node_modules' \) -prune -o \ -type f -print0 | sort -z) # 4) systemd-юниты и таймеры проекта section "SYSTEMD UNITS" for u in \ /etc/systemd/system/morozovka-portal.service \ /etc/systemd/system/morozovka-sync.service \ /etc/systemd/system/morozovka-sync.timer \ /etc/systemd/system/gitea.service \ /etc/systemd/system/bridge-webui.service; do [[ -f "$u" ]] && dump_file "$u" || echo "[missing] $u" >> "$OUT" done echo "" >> "$OUT" echo "--- systemctl list-units (morozovka*) ---" >> "$OUT" systemctl list-units --type=service --type=timer --no-pager --all --no-legend 2>/dev/null \ | grep -Ei 'morozovka|gitea|bridge' >> "$OUT" || true echo "" >> "$OUT" echo "--- systemctl status (кратко) ---" >> "$OUT" for s in morozovka-portal morozovka-sync.timer gitea; do echo "" >> "$OUT" echo "### $s" >> "$OUT" systemctl is-enabled "$s" 2>&1 >> "$OUT" || true systemctl is-active "$s" 2>&1 >> "$OUT" || true done # 5) Nginx-конфиги (сервер 3) section "NGINX CONFIGS (server 3)" for d in /etc/nginx/sites-available /etc/nginx/sites-enabled /etc/nginx/conf.d; do [[ -d "$d" ]] || continue while IFS= read -r -d '' f; do dump_file "$f" done < <(find "$d" -maxdepth 1 -type f -o -type l -print0 2>/dev/null | sort -z) done section "NGINX TEST OUTPUT" nginx -t 2>&1 >> "$OUT" || true # 6) PostgreSQL: список таблиц и схема (без данных) section "POSTGRESQL SCHEMA (morozovka_portal)" if command -v psql >/dev/null 2>&1 || [[ -x /usr/bin/sudo ]]; then sudo -u postgres psql -d morozovka_portal -c "\dt" >> "$OUT" 2>&1 || true sudo -u postgres psql -d morozovka_portal -c "\d+" >> "$OUT" 2>&1 || true echo "" >> "$OUT" echo "--- pg_dump --schema-only ---" >> "$OUT" sudo -u postgres pg_dump --schema-only --no-owner --no-privileges morozovka_portal >> "$OUT" 2>&1 || true else echo "[psql not found]" >> "$OUT" fi # 7) Проверка, что портал отвечает section "PORTAL HEALTH" curl -sS -m 5 http://127.0.0.1:8000/healthz >> "$OUT" 2>&1 || echo "[healthz failed]" >> "$OUT" echo "" >> "$OUT" curl -sS -m 5 -o /dev/null -w "HTTP %{http_code} time=%{time_total}s\n" http://127.0.0.1:8000/ >> "$OUT" 2>&1 || true # 8) Последние логи (обрезанные) section "RECENT LOGS (last 40 lines each)" for u in morozovka-portal morozovka-sync; do echo "" >> "$OUT" echo "### journalctl -u $u -n 40 --no-pager" >> "$OUT" journalctl -u "$u" -n 40 --no-pager >> "$OUT" 2>&1 || true done # 9) Проверка VPN/маршрутов на всякий section "NETWORK (server 3)" ip -brief addr >> "$OUT" 2>&1 || true echo "" >> "$OUT" ip route >> "$OUT" 2>&1 || true echo "" >> "$OUT" echo "DUMP COMPLETE: $(date -Is)" >> "$OUT" echo "SIZE: $(du -h "$OUT" | cut -f1)" >> "$OUT" echo "Готово: $OUT" echo "Размер: $(du -h "$OUT" | cut -f1)"