Админка: секретный путь, смена ссылки, интервал и принудительное обновление
Панель без публичного входа: страница живёт по ADMIN_PATH, логин/пароль — ADMIN_LOGIN/ADMIN_PASSWORD из .env (пусто = админка выключена и маршруты /api/admin/* не регистрируются, прод можно не трогать). Умеет: посмотреть текущую ссылку и состояние кэша, вставить новую ссылку (проверяется скачиванием и разбором до сохранения, при ошибке откат), принудительно обновить таблицу вне интервала и без проверки modified, задать интервал в минутах (1..1440). Реализация: - backend/admin.py — cookie-сессия на HMAC (ключ = пароль, 12 часов, HttpOnly/SameSite/Secure-за-https), сравнение за постоянное время; compare_digest по байтам — кириллица в логине не роняет сервер; - schedule/settings.py — ссылка и интервал в JSON (SETTINGS_FILE), атомарная запись, терпимость к битому/недоступному файлу (тогда живёт в памяти); - schedule/cache.py — интервал вместо TTL, apply_link с откатом, force_reload, state() для панели; кэш без ссылки больше не роняет старт; - frontend: страница admin.html (вне static, прямой /admin.html закрыт) и прокси /api/admin/* с телом и Set-Cookie; - docker-compose: том schedule_data для настроек, ADMIN_* в окружении; - deploy/: nginx-конфиг и инструкция для стенда dev.schedule.our-place.ru. Тесты: 138 (новые — админ-API, сессии, ссылка/интервал, force, хранилище).
This commit is contained in:
@@ -196,12 +196,17 @@ def test_cache_failure_without_data_raises(cache):
|
||||
assert cache.status()["last_updated"] is None
|
||||
|
||||
|
||||
def test_broken_cache_http(client=None):
|
||||
def test_cache_without_link():
|
||||
"""Без ссылки приложение живёт: /healthz есть, данные отвечают понятной ошибкой."""
|
||||
from fastapi.testclient import TestClient as TC
|
||||
|
||||
app = main.create_app(main._BrokenCache())
|
||||
from schedule import ScheduleCache
|
||||
from schedule.settings import SettingsStore
|
||||
|
||||
cache = ScheduleCache(settings=SettingsStore(link=""), retry_after=60)
|
||||
app = main.create_app(cache)
|
||||
with TC(app) as c:
|
||||
assert c.get("/healthz").status_code == 200
|
||||
assert c.get("/groups").status_code == 502
|
||||
body = c.get("/status").json()
|
||||
assert "SCHEDULE_LINK" in body["error"]
|
||||
assert "Ссылка" in body["error"]
|
||||
|
||||
Reference in New Issue
Block a user