Files
schedule/tests/test_deploy.py
Vladislav Kopnin 5b3c9dc00a Админка в master: секретный путь, ссылка, интервал, force, блокировка входа, история
Админка нужна на «голом» сервере, поэтому возвращаю её в основную ветку. Она
выключена, пока не заданы ADMIN_LOGIN/ADMIN_PASSWORD: маршруты /api/admin/* не
регистрируются, страницы нет — обычная установка ничего не замечает.

Панель (открывается по секретному пути ADMIN_PATH, кнопки входа на сайте нет,
вход по логину/паролю):
- состояние: группы, когда обновлялось, дата файла на Диске, изменился ли
  файл при последнем чтении (sha256), ошибки, путь файла настроек;
- новая ссылка на таблицу: проверяется скачиванием, при ошибке откат;
- интервал обновления (TTL) в минутах;
- «Обновить сейчас»: принудительно перечитывает таблицу (качает файл всегда,
  интервал — только если на Диске изменилась дата);
- история входов: последняя попытка (откуда, когда, успешна ли) и последний
  успешный вход — и в панели, и в логе службы.

Защита входа: ADMIN_MAX_ATTEMPTS неудач (по умолчанию 3), затем блокировка на
ADMIN_LOCKOUT_MINUTES минут (по умолчанию 15) — всё из .env.

Реализация:
- backend/admin.py: cookie-сессия на HMAC (ключ — пароль, 12 ч, HttpOnly,
  SameSite, Secure за https), сравнение за постоянное время, реальный IP из
  X-Forwarded-For, логирование каждой попытки;
- schedule/settings.py: ссылка, интервал и история входов в JSON; путь по
  умолчанию data/settings.json (в docker — том schedule_data), при
  недоступности файла настройки живут в памяти и панель предупреждает;
- cache.py: apply_link (с проверкой и откатом), force_reload, state();
- frontend: страница админки по ADMIN_PATH (вне static, noindex) и прокси
  /api/admin/* с телом и Set-Cookie;
- install.sh пишет переменные админки в .env, Dockerfile копирует admin.py.

Тесты: 156. Проверено локально (страница, 3 попытки -> блокировка, история) и
в собранном docker-образе (том настроек, запись истории).
2026-10-10 11:12:29 +03:00

52 lines
2.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Проверки сборки образов: все нужные файлы должны копироваться.
Ошибка «в Dockerfile забыли COPY какого-то модуля» роняет контейнер на импорте,
а тесты из исходников её не видят (они запускаются из рабочей копии). Ловим
статически, без docker.
"""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
DOCKERFILE = ROOT / "Dockerfile"
COMPOSE = ROOT / "docker-compose.yml"
def _stage(name: str) -> str:
dockerfile = DOCKERFILE.read_text(encoding="utf-8")
part = dockerfile.split(f"FROM base AS {name}", 1)[1]
for other in ("api", "frontend"):
if other != name:
part = part.split(f"FROM base AS {other}", 1)[0]
return part
def test_api_image_copies_every_backend_module():
stage = _stage("api")
for module in sorted((ROOT / "src" / "backend").glob("*.py")):
assert module.name in stage, f"{module.name} не копируется в образ api"
assert "src/backend/schedule" in stage, "пакет schedule не копируется в образ api"
def test_frontend_image_copies_page_and_static():
stage = _stage("frontend")
for path in ("src/frontend/main.py", "src/frontend/admin.html", "src/frontend/static"):
assert path in stage, f"{path} не копируется в образ frontend"
def test_compose_passes_required_env():
compose = COMPOSE.read_text(encoding="utf-8")
for var in (
"SCHEDULE_LINK",
"SCHEDULE_INTERVAL_MINUTES",
"SCHEDULE_API",
"ADMIN_PATH",
"ADMIN_LOGIN",
"ADMIN_PASSWORD",
"ADMIN_MAX_ATTEMPTS",
"ADMIN_LOCKOUT_MINUTES",
"SETTINGS_FILE",
):
assert var in compose, f"{var} не передаётся контейнерам"
assert "schedule_data:/data" in compose, "том для настроек админки не подключён"