Commit Graph

4 Commits

Author SHA1 Message Date
163278c146 Шапка одной высоты с админкой, ховер кнопок как у остальных
- на сайте логотип лежит внутри ссылки и как строчный элемент добавлял 7px:
  шапка была 70px против 63px в админке. Теперь логотип блочный, а ссылка —
  inline-flex: обе шапки 63px (и min-height layout сходится точно);
- выход и «Админка» при наведении только меняют цвет (тёмно-красный и
  синий соответственно), без подъёма, тени и transform — как остальные
  кнопки проекта.
2026-10-10 13:02:43 +03:00
34a46b6832 Админка: кнопки в шапке сайта, красный выход и путь, который нигде не утекает
Интерфейс:
- после входа на главной появляются «Админка» и «Выйти» — в шапке, перед
  переключателем темы и после него соответственно (порядок: Админка, тема,
  Выйти);
- «Выйти» теперь красная и заметно реагирует на наведение (темнеет,
  приподнимается, тень) — и на сайте, и в админке (одна кнопка в шапке,
  из карточки убрана);
- в админке убраны «осталось попыток», путь к файлу настроек и карточка
  «Данные расписания» (дублировала шапку); блокировка показывается отдельной
  плашкой только когда действительно активна.

Безопасность пути:
- путь к панели не отдаётся ни в HTML, ни в JSON (из /api/admin/state убран),
  ни в JS: на главной для вошедшего только флаг, а кнопка «Админка» ведёт на
  служебный /api/admin/goto, который отвечает 302 на секретный путь;
- без сессии /api/admin/goto и /api/admin/state отвечают 401, неизвестные пути
  дают 404 — перебором путь не находится;
- страница панели: noindex/nofollow/noarchive, Referrer-Policy: no-referrer,
  X-Frame-Options: DENY, Cache-Control: no-store;
- robots.txt без пути (только Disallow: /api/), внешние ссылки в панели —
  rel=noopener noreferrer.

Тесты: 161 (состояние без пути, /goto: 401 без входа и 302 с ним).
2026-10-10 12:54:54 +03:00
71f83a27dd Админка: страница переделана — понятнее и в стиле сайта
- шапка как на сайте (логотип факультета, «на сайт», переключатель светлой/тёмной
  темы — тема общая с сайтом и не мигает при загрузке);
- крупная шапка состояния: «Работает / Ожу данные / Ошибка загрузки», сколько
  обновлено назад, число групп и текущий интервал; рядом главная кнопка
  «Обновить сейчас»;
- вместо россыпи чипов — карточки: «Ссылка на расписание» (с кнопкой
  «открыть таблицу»), «Обновление» (интервал + факты: изменился ли файл, дата,
  отпечаток), «Входы в админку» (последняя попытка, последний успешный вход,
  остаток попыток или блокировка), «Данные расписания»;
- предупреждения (ошибка загрузки, невозможность сохранить настройки) вынесены
  отдельной плашкой под шапкой, а не спрятаны среди полей;
- верстка адаптивная: сетка карточек, на телефоне одна колонка и подписи
  под значениями; формы с подписями и подсказками, кнопки с ясной иерархией;
- уведомления и вся логика прежние; тесты 160.
2026-10-10 12:42:44 +03:00
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