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:
@@ -227,9 +227,10 @@ CREATE INDEX audit_events_action_idx ON identity.audit_events (action);
|
||||
```
|
||||
Внешних ключей нет: событие должно переживать удаление субъекта. Запись идёт
|
||||
из сервисов через `POST /api/v1/internal/audit` (`X-Internal-Token`), чтение —
|
||||
`GET /api/v1/audit?page=&limit=&action=` и `/admin/audit` только для админа.
|
||||
Страница аудита поддерживает `page` (с нуля) и `limit` (максимум 500), в UI —
|
||||
переход «Показать ещё».
|
||||
`GET /api/v1/audit?limit=&action=&cursor=` и `/admin/audit` только для админа.
|
||||
Страница аудита продолжается keyset-курсором `(occurred_at, id)`; offset `page`
|
||||
остаётся для совместимости, `limit` — максимум 500. В UI — «Показать ещё» и
|
||||
«В начало»; битый курсор даёт `400`.
|
||||
|
||||
**V11 — refresh-токены (добавлена при закрытии рисков аутентификации)**
|
||||
```sql
|
||||
@@ -422,7 +423,8 @@ V13 идемпотентна: если старая feature-миграция V10
|
||||
и защиты. Свой профиль студент открывает без записи в аудит, чтобы не
|
||||
засорять журнал и не нагружать identity на горячем пути. Админ смотрит
|
||||
журнал на `/admin/audit` с фильтром по действию; время отображается в МСК.
|
||||
Журнал читается страницами (`page`/`limit`, «Показать ещё»), а не только
|
||||
Журнал читается keyset-курсором (`cursor`/`limit`, «Показать ещё»; offset
|
||||
`page` — только совместимость), а не только
|
||||
последними N событиями. `membership_history` и `promotion_runs/items`
|
||||
остаются доменной историей и дополняют общий журнал.
|
||||
|
||||
@@ -433,19 +435,20 @@ V13 идемпотентна: если старая feature-миграция V10
|
||||
сервисами на каждом запросе).
|
||||
- **Refresh-токен** — 7 дней (`LMS_REFRESH_TOKEN_TTL`), хранится хешем в
|
||||
`identity.refresh_tokens` (`V11`), ротируется при каждом обновлении.
|
||||
Повторное использование старого токена отзывает все активные сессии
|
||||
аккаунта; выход, смена пароля, отчисление/выпуск/архив и закрытая группа
|
||||
обновление запрещают.
|
||||
Абсолютный потолок сессии — 30 дней (`LMS_SESSION_ABSOLUTE_TIMEOUT`, `V12`):
|
||||
ротация не продлевает `absolute_expires_at`. Повторное использование старого
|
||||
токена отзывает все активные сессии аккаунта; выход, смена пароля,
|
||||
отчисление/выпуск/архив и закрытая группа обновление запрещают. Истёкшие
|
||||
токены старше retention удаляет ночная задача.
|
||||
- **Прямой API-клиент** обязан обновлять пару через
|
||||
`POST /api/v1/auth/refresh`; `POST /api/v1/auth/logout` отзывает refresh.
|
||||
- **Web-сессия** — 7 дней sliding (`LMS_SESSION_TIMEOUT`), cookie `LMS_SESSION`
|
||||
(`HttpOnly`, `SameSite=Lax`, `Max-Age` обновляется на каждом ответе).
|
||||
Access-токен автоматически обновляется за 2 минуты до истечения, поэтому
|
||||
активная вкладка не требует повторного входа каждые 15 минут; при выходе
|
||||
refresh отзывается.
|
||||
- **Ограничение:** абсолютного лимита жизни сессии нет — при непрерывной
|
||||
активности она продлевается. Если политика потребует принудительный
|
||||
повторный вход раз в N дней, добавляется отдельный абсолютный `max-age`.
|
||||
(`HttpOnly`, `SameSite=Lax`, `Max-Age` обновляется на каждом ответе), но не
|
||||
дольше 30 дней с момента входа (`LMS_SESSION_ABSOLUTE_TIMEOUT`). После
|
||||
абсолютного срока cookie получает `Max-Age=0`, а пользователь — повторный
|
||||
вход. Access-токен автоматически обновляется за 2 минуты до истечения,
|
||||
поэтому активная вкладка не требует повторного входа каждые 15 минут; при
|
||||
выходе refresh отзывается.
|
||||
|
||||
---
|
||||
|
||||
@@ -457,8 +460,9 @@ V13 идемпотентна: если старая feature-миграция V10
|
||||
- **Активы (реализовано):** видимость актива наследуется от статьи → раздела →
|
||||
главы, маршрут `/api/v1/content/assets/{name}` проверяет доступ. Файл без
|
||||
связи со статьёй не публичен: аноним/студент/преподаватель получают `404`,
|
||||
админ может скачать файл и получить список таких файлов через
|
||||
`GET /api/v1/content/legacy-assets` для привязки или удаления. Старые
|
||||
админ может скачать файл, открыть страницу `/admin/legacy-assets`, вызвать
|
||||
`GET /api/v1/content/legacy-assets` или `scripts/legacy-assets.sh` для
|
||||
привязки или удаления. Старые
|
||||
внешние ссылки на несвязанные файлы у обычных пользователей перестают
|
||||
работать — это осознанное secure-by-default поведение.
|
||||
- Поисковая индексация: для `ALL` — по умолчанию разрешена; для остального — закрыть (robots/noindex).
|
||||
@@ -527,13 +531,16 @@ V13 идемпотентна: если старая feature-миграция V10
|
||||
|
||||
---
|
||||
|
||||
## 12. Интеграция «Контингент» / next.uniyar.ac.ru (задел)
|
||||
## 12. Интеграция «Контингент» / next.uniyar.ac.ru — исключена из плана
|
||||
|
||||
- Позже — **отдельный сервис-адаптер** (anti-corruption layer); внешняя система — источник истины по
|
||||
курсу/группе/статусу; LMS — зеркало; синхронизация односторонняя + отчёт дрифта.
|
||||
- Сейчас закладываем: `student_no` уникальный и стабильный (ключ склейки); внешние id добавим миграцией.
|
||||
- Обязательно: идемпотентность, журнал синхронизаций, отчёт расхождений, обработка академа/переводов/отчислений,
|
||||
устойчивость к недоступности внешней системы, ПДн.
|
||||
Решением от 2026-10-28 интеграция **не входит в объём работ**: нет API-спецификации
|
||||
и доступа к внешней системе, а без них можно сделать только непроверяемую заглушку.
|
||||
`student_no` остаётся уникальным и стабильным, поэтому при появлении нового ТЗ
|
||||
адаптер можно добавить без переделки модели.
|
||||
|
||||
Если интеграция понадобится позже: отдельный anti-corruption layer, идемпотентная
|
||||
синхронизация, журнал, отчёт дрифта, обработка академа/переводов/отчислений,
|
||||
устойчивость к недоступности внешней системы и ПДн.
|
||||
|
||||
---
|
||||
|
||||
@@ -573,6 +580,6 @@ V13 идемпотентна: если старая feature-миграция V10
|
||||
4. Запись/снятие групп на курс.
|
||||
5. Повышение курса: dry-run, журнал, идемпотентность, откат, выпуск.
|
||||
6. Академический отпуск/возврат, отчисление, выпуск, архив; усиленное удаление.
|
||||
7. (Позже) сервис-адаптер «Контингент»/`next.uniyar.ac.ru`.
|
||||
7. ~~Сервис-адаптер «Контингент»/`next.uniyar.ac.ru`~~ — исключён из плана.
|
||||
|
||||
Открытых вопросов нет: все решения зафиксированы в §1.
|
||||
|
||||
Reference in New Issue
Block a user