оптимизация 2 и добавлены логи

This commit is contained in:
2026-10-08 05:39:49 +03:00
parent 9fc06aa297
commit a96b18d6ae
9 changed files with 1339 additions and 325 deletions

112
update.sh Normal file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# Update an installed server from an unpacked release. Keeps .env, team and venv.
set -Eeuo pipefail
source_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
target_dir="${1:-/root/RFB}"
service_name="rfb-data.service"
systemd_root="${RFB_SYSTEMD_ROOT:-/etc/systemd/system}"
dropin="$systemd_root/$service_name.d/20-rfb-runtime.conf"
check_only="${2:-}"
files=(get_data.py rfb_polling.py rfb_runtime.py rfb_logs.html rfb_version.json polling_config.json deploy.sh README_RU.txt update.sh)
python_bin="$target_dir/.venv/bin/python"
if [[ ! -x "$python_bin" ]]; then python_bin="$(command -v python3)"; fi
"$python_bin" - "$source_dir" <<'PY'
from pathlib import Path
import hashlib, json, sys
root = Path(sys.argv[1]); version = json.loads((root/'rfb_version.json').read_text())
for name, expected in version['files'].items():
if hashlib.sha256((root/name).read_bytes()).hexdigest() != expected:
raise SystemExit('Release checksum mismatch: '+name)
for name in ('get_data.py','rfb_polling.py','rfb_runtime.py'):
compile((root/name).read_bytes(),str(root/name),'exec')
print('RFB '+version['version']+': release validated')
PY
if [[ "$check_only" == "--check" ]]; then exit 0; fi
if [[ "$source_dir" == "$(realpath -m "$target_dir")" ]]; then
echo 'Распакуйте архив в отдельную папку, затем запустите update.sh с путём рабочего проекта.' >&2
exit 1
fi
if [[ ! -d "$target_dir" ]]; then echo "Не найден установленный проект: $target_dir" >&2; exit 1; fi
"$python_bin" -c 'import requests, fastapi, uvicorn, pandas, PIL, dotenv, nasio'
backup_dir="${target_dir}-backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p -- "$backup_dir"
for name in "${files[@]}" .rfb_deployment.json; do
if [[ -f "$target_dir/$name" ]]; then cp -a -- "$target_dir/$name" "$backup_dir/$name"; fi
done
if [[ -f "$dropin" ]]; then cp -a -- "$dropin" "$backup_dir/systemd-rfb-runtime.conf"; fi
was_active=0
if systemctl is-active --quiet "$service_name"; then was_active=1; fi
changed=0
rollback() {
local result=$?
trap - ERR
if [[ "$changed" == 1 ]]; then
echo "Обновление не завершилось. Восстанавливаю файлы из $backup_dir" >&2
systemctl stop "$service_name" || true
mkdir -p -- "$backup_dir/failed-update"
for name in "${files[@]}" .rfb_deployment.json; do
if [[ -f "$backup_dir/$name" ]]; then
cp -a -- "$backup_dir/$name" "$target_dir/$name"
elif [[ -f "$target_dir/$name" ]]; then
mv -- "$target_dir/$name" "$backup_dir/failed-update/$name"
fi
done
if [[ -f "$backup_dir/systemd-rfb-runtime.conf" ]]; then
cp -a -- "$backup_dir/systemd-rfb-runtime.conf" "$dropin"
elif [[ -f "$dropin" ]]; then
mv -- "$dropin" "$backup_dir/failed-update/systemd-rfb-runtime.conf"
fi
systemctl daemon-reload || true
if [[ "$was_active" == 1 ]]; then systemctl start "$service_name" || true; fi
fi
exit "$result"
}
trap rollback ERR
systemctl stop "$service_name"
changed=1
for name in "${files[@]}"; do
if [[ "$name" != "polling_config.json" ]]; then cp -- "$source_dir/$name" "$target_dir/$name"; fi
done
"$python_bin" - "$source_dir" "$target_dir" <<'PY'
from pathlib import Path
import json, sys
source, target = map(Path, sys.argv[1:])
defaults = json.loads((source/'polling_config.json').read_text())
path = target/'polling_config.json'
previous = json.loads(path.read_text()) if path.exists() else {}
if not isinstance(previous,dict): raise SystemExit('polling_config.json must contain an object')
# Keep user-specific budgets and intervals. Migrate the old default preparation window.
if previous.get('prepare_minutes') == 70: previous['prepare_minutes'] = 90
defaults.update(previous)
path.write_text(json.dumps(defaults,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
PY
chmod +x "$target_dir/update.sh" "$target_dir/deploy.sh"
"$python_bin" "$target_dir/rfb_runtime.py" --mark-deployed "$target_dir"
mkdir -p -- "$(dirname -- "$dropin")"
cat > "$dropin" <<'UNIT'
[Unit]
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Environment=PYTHONUNBUFFERED=1
UNIT
systemctl daemon-reload
systemctl start "$service_name"
# HTTP readiness does not depend on the upstream API or Telegram being reachable.
"$python_bin" - "$source_dir/rfb_version.json" <<'PY'
import json, sys, time, urllib.request
expected = json.load(open(sys.argv[1]))['version']
last = None
for _ in range(30):
try:
with urllib.request.urlopen('http://127.0.0.1:8000/api_health',timeout=2) as response:
if response.status == 200 and json.load(response).get("version") == expected: break
except Exception as ex: last = ex
time.sleep(1)
else: raise SystemExit('Сервис не ответил после обновления: '+str(last))
PY
trap - ERR
echo "Обновление установлено. Резервная копия: $backup_dir"
echo 'Диагностика: /logs; состояние доставки Telegram и тест доступны на странице.'