chore(home): mirror from kalinamall (9883e6a) with papatramp URLs

This commit is contained in:
2026-07-14 20:43:52 +10:00
commit ed4e78f6c3
312 changed files with 42790 additions and 0 deletions
+198
View File
@@ -0,0 +1,198 @@
#!/usr/bin/env bash
# Полное обновление SAC на сервере (native).
# Установка: sudo cp /opt/security-alert-center/deploy/sac-deploy.sh /opt/sac-deploy.sh
# sudo chmod 755 /opt/sac-deploy.sh
# Запуск: sudo /opt/sac-deploy.sh
#
# Выполняет от пользователя sac: git pull, pip, alembic, npm build.
# От root: systemctl restart sac-api.
set -euo pipefail
APP_ROOT="${SAC_APP_ROOT:-/opt/security-alert-center}"
APP_USER="${SAC_APP_USER:-sac}"
SERVICE_NAME="${SAC_SERVICE:-sac-api}"
CONFIG_FILE="${SAC_CONFIG_FILE:-${APP_ROOT}/config/sac-api.env}"
log() { printf '[sac-deploy] %s\n' "$*"; }
die() { printf '[sac-deploy] ERROR: %s\n' "$*" >&2; exit 1; }
if [ "$(id -u)" -ne 0 ]; then
die "Запускайте от root: sudo /opt/sac-deploy.sh"
fi
id "$APP_USER" &>/dev/null || die "Пользователь ${APP_USER} не существует"
if [ ! -d "${APP_ROOT}/.git" ]; then
die "Не найден git-репозиторий: ${APP_ROOT}"
fi
log "Выравнивание владельца ${APP_ROOT}${APP_USER}:${APP_USER}"
chown -R "${APP_USER}:${APP_USER}" "${APP_ROOT}"
log "git fetch + sync"
sudo -u "${APP_USER}" git -C "${APP_ROOT}" fetch --prune
if sudo -u "${APP_USER}" git -C "${APP_ROOT}" pull --ff-only 2>/dev/null; then
log "git: fast-forward OK"
else
UPSTREAM="$(sudo -u "${APP_USER}" git -C "${APP_ROOT}" rev-parse --abbrev-ref '@{u}' 2>/dev/null || true)"
if [ -z "${UPSTREAM}" ]; then
UPSTREAM="origin/main"
fi
log "WARN: fast-forward невозможен (часто после force-push на kalinamall) — reset --hard ${UPSTREAM}"
sudo -u "${APP_USER}" git -C "${APP_ROOT}" reset --hard "${UPSTREAM}"
fi
if [ ! -f "${CONFIG_FILE}" ]; then
die "Нет конфига: ${CONFIG_FILE}"
fi
VENV="${APP_ROOT}/backend/.venv"
if [ ! -x "${VENV}/bin/pip" ]; then
die "Нет venv: ${VENV} — сначала установите по install-ubuntu-24.04-native.md"
fi
log "pip install -r requirements.txt"
sudo -u "${APP_USER}" "${VENV}/bin/pip" install -q -r "${APP_ROOT}/backend/requirements.txt"
log "Проверка ${CONFIG_FILE} (pydantic; без bash source — inline-комментарии в значениях ломают deploy)"
sudo -u "${APP_USER}" bash -c "
export SAC_CONFIG_FILE='${CONFIG_FILE}'
cd '${APP_ROOT}/backend'
'${VENV}/bin/python' -c 'from app.config import get_settings; get_settings(); print(\"config: OK\")'
" || die "Неверный ${CONFIG_FILE}: одна переменная = одна строка, комментарии только отдельной строкой с # (не «false ← …» после значения)"
log "alembic upgrade head"
sudo -u "${APP_USER}" bash -c "
export SAC_CONFIG_FILE='${CONFIG_FILE}'
cd '${APP_ROOT}/backend' && '${VENV}/bin/alembic' upgrade head
"
if [ -f "${APP_ROOT}/frontend/package.json" ]; then
if ! command -v npm >/dev/null 2>&1; then
log "npm не найден — пропуск сборки UI (apt install nodejs npm)"
else
log "frontend: npm ci / build"
sudo -u "${APP_USER}" bash -c "
cd '${APP_ROOT}/frontend'
if [ -f package-lock.json ]; then npm ci; else npm install; fi
npm run build
"
if [ ! -f "${APP_ROOT}/frontend/dist/index.html" ]; then
die "Сборка UI не создала frontend/dist/index.html"
fi
fi
fi
if [ -f "${APP_ROOT}/deploy/systemd/${SERVICE_NAME}.service" ]; then
SYSTEMD_DST="/etc/systemd/system/${SERVICE_NAME}.service"
if [ ! -f "${SYSTEMD_DST}" ] || ! cmp -s "${APP_ROOT}/deploy/systemd/${SERVICE_NAME}.service" "${SYSTEMD_DST}"; then
log "Обновление systemd unit → ${SYSTEMD_DST}"
cp "${APP_ROOT}/deploy/systemd/${SERVICE_NAME}.service" "${SYSTEMD_DST}"
systemctl daemon-reload
fi
fi
START_SH="${APP_ROOT}/deploy/systemd/sac-api-start.sh"
if [ -f "${START_SH}" ]; then
sed -i 's/\r$//' "${START_SH}"
chmod 755 "${START_SH}"
fi
for _sh in "${APP_ROOT}"/deploy/sac-deploy.sh "${APP_ROOT}"/deploy/systemd/*.sh; do
[ -f "${_sh}" ] || continue
sed -i 's/\r$//' "${_sh}"
chmod 755 "${_sh}" 2>/dev/null || true
done
log "Проверка DATABASE_URL (как у uvicorn через SAC_CONFIG_FILE)"
sudo -u "${APP_USER}" bash -c "
export SAC_CONFIG_FILE='${CONFIG_FILE}'
cd '${APP_ROOT}/backend'
'${VENV}/bin/python' -c \"
from sqlalchemy import create_engine, text
from app.config import get_settings
engine = create_engine(get_settings().database_url, pool_pre_ping=True)
with engine.connect() as conn:
conn.execute(text('SELECT 1'))
print('db: OK')
\"
" || die "PostgreSQL: проверьте DATABASE_URL в ${CONFIG_FILE} и systemctl status postgresql"
log "systemctl restart ${SERVICE_NAME} (краткий 502 в UI возможен ~10 с)"
log "Сброс зависших remote_action (running без worker после restart)"
sudo -u "${APP_USER}" bash -c "
export SAC_CONFIG_FILE='${CONFIG_FILE}'
cd '${APP_ROOT}/backend'
'${VENV}/bin/python' -c \"
from app.database import SessionLocal
from app.services.host_remote_actions import clear_stale_running_remote_actions
session = SessionLocal()
try:
names = clear_stale_running_remote_actions(session)
for name in names:
print(f'stale remote_action reset: {name}')
finally:
session.close()
\"
"
systemctl restart "${SERVICE_NAME}"
sleep 2
for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
if systemctl is-active --quiet "${SERVICE_NAME}"; then
break
fi
sleep 1
done
systemctl is-active --quiet "${SERVICE_NAME}" || die "${SERVICE_NAME} не active — journalctl -u ${SERVICE_NAME} -n 40 --no-pager"
HEALTH_OK=0
for _ in 1 2 3 4 5 6; do
sleep 2
if curl -sf "http://127.0.0.1:8000/health" >/dev/null 2>&1; then
HEALTH_OK=1
break
fi
done
if [ "${HEALTH_OK}" -eq 1 ]; then
log "health: OK (http://127.0.0.1:8000/health)"
else
die "${SERVICE_NAME} запущен, но /health не ответил — journalctl -u ${SERVICE_NAME} -n 80 --no-pager"
fi
RENDER_DAILY_TIMER="${APP_ROOT}/deploy/systemd/render-sac-daily-report-timer.sh"
if [ -f "${RENDER_DAILY_TIMER}" ]; then
chmod +x "${RENDER_DAILY_TIMER}" 2>/dev/null || true
fi
for aux in sac-retention sac-daily-report; do
for suffix in service timer; do
src="${APP_ROOT}/deploy/systemd/${aux}.${suffix}"
dst="/etc/systemd/system/${aux}.${suffix}"
if [ "${aux}" = "sac-daily-report" ] && [ "${suffix}" = "timer" ]; then
if [ -x "${RENDER_DAILY_TIMER}" ]; then
log "Генерация ${dst} из ${CONFIG_FILE} (SAC_DAILY_REPORT_HOUR/TIMEZONE)"
bash "${RENDER_DAILY_TIMER}" "${CONFIG_FILE}" "${dst}"
systemctl daemon-reload
systemctl restart "${aux}.timer" 2>/dev/null || systemctl start "${aux}.timer" 2>/dev/null || true
elif [ -f "${src}" ]; then
log "WARN: ${RENDER_DAILY_TIMER} не найден — копируем статический timer"
cp "${src}" "${dst}"
systemctl daemon-reload
fi
continue
fi
if [ -f "${src}" ]; then
if [ ! -f "${dst}" ] || ! cmp -s "${src}" "${dst}"; then
log "Обновление ${dst}"
cp "${src}" "${dst}"
systemctl daemon-reload
fi
fi
done
if [ -f "/etc/systemd/system/${aux}.timer" ]; then
systemctl enable "${aux}.timer" 2>/dev/null || true
systemctl start "${aux}.timer" 2>/dev/null || true
fi
done
log "Готово."