feat: закрыть остаточный backlog

- keyset-пагинация аудита с курсором (occurred_at, id) и UI «Показать ещё»
- пакетный /api/v1/labs/details и переход progress на один запрос
- абсолютный потолок сессии 30 дней (identity V12 + web), очистка refresh
- админская инвентаризация legacy-активов (/admin/legacy-assets, скрипт)
- E2E в Chromium/Firefox/WebKit (18/18), CI preflight
- LDD: 51 claim, 346 тестов, docs и handoff обновлены
This commit is contained in:
2026-10-28 19:29:23 +03:00
parent d45137220c
commit 1e1e9ae0e4
64 changed files with 2124 additions and 372 deletions

View File

@@ -211,11 +211,11 @@
Номера уточняются при реализации, чтобы не конфликтовать с максимумами master
(identity V3, **academic V5**, progress V12). Максимумы этой ветки:
identity V11, **academic V12**, progress V13.
identity V13, **academic V12**, progress V13.
| Сервис | Миграции | Содержание |
|---|---|---|
| identity | V4–V11 | роли, поля групп, статусы студентов, `membership_history`, `promotion_runs/items`, `accounts.status`; `V10` — `audit_events` без внешних ключей и индексы по `occurred_at`/`action`; `V11` — `refresh_tokens` (хеш, срок, отзыв, цепочка замен) |
| identity | V4–V13 | роли, поля групп, статусы студентов, `membership_history`, `promotion_runs/items`, `accounts.status`; `V10` — `audit_events`; `V11` — `refresh_tokens` (хеш, срок, отзыв, цепочка замен); `V12` — `absolute_expires_at`; `V13` — keyset-индексы аудита |
| academic | V6–V12 + post-bootstrap step | курсы, преподаватели, группы курса, `labs.course_id` (nullable), материалы курса/аудитории, частичные индексы, активы; `V10` — уникальный индекс `lower(title)` по курсам; `V11` — backfill `audience='ALL'` для глобальных глав; `V12` — backfill связей `article_assets` из markdown-ссылок; `SET NOT NULL` — post-bootstrap шаг вне Flyway |
| progress | V13 | `grade_comments.author_account_id`, `lab_grades.updated_by_account_id`, индекс `lab_grades(lab_id)`; аудит реализован в identity `V10` |
@@ -297,13 +297,15 @@ identity V11, **academic V12**, progress V13.
- **Legacy-активы.** Закрыто: связанные активы наследуют видимость статьи
(миграция `V12`), а файлы без markdown-ссылки по умолчанию отдают `404`
анониму/студенту/преподавателю. Админ перечисляет их через
`GET /api/v1/content/legacy-assets` для привязки или удаления.
`GET /api/v1/content/legacy-assets`, страницу `/admin/legacy-assets` или
`scripts/legacy-assets.sh` для привязки или удаления.
- **JWT и сессии.** Закрыто: access-токен 15 минут, refresh 7 дней с ротацией
и отзывом, web-сессия 7 дней sliding. Прямые API-клиенты используют
`POST /api/v1/auth/refresh`; logout и смена пароля отзывают refresh.
- **Оставшаяся производительность.** Аудит постраничный и группы курса
отдаются одним вызовом, но N+1 в отдельных лабораторных списках и
offset-пагинация аудита остаются лабораторными деталями.
и отзывом, web-сессия 7 дней sliding с абсолютным потолком 30 дней.
Прямые API-клиенты используют `POST /api/v1/auth/refresh`; logout и смена
пароля отзывают refresh; истёкшие токены чистит ночная задача.
- **Производительность.** Аудит продолжается keyset-курсором, детали всех
лабораторных курса и группы курса отдаются пакетно. Отдельный запрос сдач
по выбранной лабораторной в ведомости остаётся, но это не N+1 по студентам.
- **Комментарии без автора.** Старые записи останутся с «автор не указан».
- **Архивные студенты.** Открывать ли профиль после отвязки группы — уточнить
на этапе 3; по умолчанию история курса доступна, если студент был в курсе.
@@ -334,25 +336,31 @@ identity V11, **academic V12**, progress V13.
- **Этап 6 — выполнен.** Права преподавателя на запись (лабораторные,
ведомость, группы, материалы), центральный аудит `identity.audit_events`
с `/admin/audit`, CI merge gate и полный hardening. Проверены прогон
тестов/линтеров (**319**, 0 failures), upgrade миграций с master-схемы
(identity `V4–V11`, academic `V6–V12`, progress `V13`), security-матрица,
gateway, видимость активов и deploy-runbook. LDD baseline: **46 claims
(25 P0 / 21 P1), 46/46 proven**, browser E2E 4/4. Отчёт:
тестов/линтеров (**346**, 0 failures), upgrade миграций с master-схемы
(identity `V4–V13`, academic `V6–V12`, progress `V13`), security-матрица,
gateway, видимость активов и deploy-runbook. LDD baseline: **51 claims
(25 P0 / 26 P1), 51/51 proven**, browser E2E 18/18 (3 браузера). Отчёт:
[16-stage6-report.md](16-stage6-report.md).
### 9.1. Известные пробелы после этапа 6
- **Импорт.** Старый формат не поддерживается, принимается только шаблон v2.
Номер группы — числовой; отдельного требования к ведущим нулям нет.
- **Пагинация и N+1.** `GET /api/v1/audit` поддерживает `page` + `limit`,
в UI есть «Показать ещё»; `GET /api/v1/courses/{id}/groups` отдаёт
привязанные группы с именами одним вызовом. Остаются offset-пагинация
аудита и N+1 в отдельных лабораторных списках/ведомости на больших курсах.
- **Абсолютный лимит сессии.** Web-сессия скользящая (7 дней) и отзывается
при выходе/смене пароля/закрытии доступа, но при непрерывной активности
может жить бессрочно; при необходимости нужен абсолютный `max-age`.
- **Внешняя интеграция.** «Контингент»/`next.uniyar.ac.ru` остаётся заделом:
отдельный anti-corruption слой и внешние идентификаторы — позже.
- **Пагинация и N+1.** Аудит продолжается keyset-курсором `(occurred_at, id)`
с offset-fallback для старых ссылок; `GET /api/v1/labs/details` отдаёт все
лабораторные курса с уровнями и вариантами одним запросом; группы курса —
одним вызовом. Ведомость по-прежнему запрашивает сдачи по выбранной
лабораторной — это не N+1 по студентам.
- **Legacy-активы.** Файлы без статьи уже не публичны; админская страница
`/admin/legacy-assets`, API и `scripts/legacy-assets.sh` готовы. Остаётся
прогнать инвентаризацию на production/staging и привязать или удалить файлы.
- **Сессии.** Web-сессия скользящая 7 дней, абсолютный потолок 30 дней
(`LMS_SESSION_ABSOLUTE_TIMEOUT`); истёкшие refresh-токены чистит ночная
задача (`LMS_REFRESH_TOKEN_RETENTION`).
- **CI.** Конфигурация и `ci-preflight.sh` готовы; первый реальный pipeline
на GitLab shell-runners ещё нужно подтвердить.
- **Внешняя интеграция «Контингент»/`next.uniyar.ac.ru` исключена из плана**
до появления нового ТЗ и доступа к API.
## 10. Definition of Done
@@ -363,12 +371,13 @@ identity V11, **academic V12**, progress V13.
- Профиль показывает статистику, комментарии и все попытки; файлы скачиваются.
- Повышение, академотпуск, выпуск и архив работают по ТЗ.
- Аудит чувствительных действий пишется в `identity.audit_events` и доступен
админу на `/admin/audit` с пагинацией.
админу на `/admin/audit` с keyset-пагинацией.
- Активы наследуют видимость материала; файлы без привязки не публичны и
доступны только админу через инвентаризацию `legacy-assets`.
доступны только админу через страницу `/admin/legacy-assets`.
- Access/refresh-токены и web-сессия работают по срокам 15 минут / 7 дней /
7 дней; logout, смена пароля и закрытие доступа отзывают refresh.
- Security-матрица и миграционный upgrade-тест зелёные (identity до `V11`).
7 дней sliding с абсолютным потолком 30 дней; logout, смена пароля и
закрытие доступа отзывают refresh.
- Security-матрица и миграционный upgrade-тест зелёные (identity до `V13`).
- CI на каждом push/MR прогоняет `test ktlintCheck detekt evidenceCheck` и
smoke полного стека; полный release-evidence обязателен для тегов.
- Backup перед деплоем обязателен; restore проверен на staging.