Памятка для backend-разработчика. Активность ученика (мышь, тесты, задания) шлёт browser SDK — здесь только IP-сессии.

1. Эндпоинт

POST https://<anomaly-host>/api/lms/session-ip-events
Authorization: Bearer <LMS_TO_ANOMALY_API_KEY>
Content-Type: application/json

Ключ LMS_TO_ANOMALY_API_KEY выдаёт команда anomaly-service. GeoIP (страна, регион, город, ASN, ISP) считается на стороне anomaly-service.

2. Когда вызывать

Момент в LMS eventType Зачем
Успешный логин / создание сессии session_started зафиксировать IP входа
Периодический ping (опционально) session_ping актуальный IP с сервера на каждый запрос
Если eventType не передать — по умолчанию session_started.
Смену IP и «разные регионы за 10 мин» детектит сам anomaly-service — правило сравнивает сохранённые session_ip_event. LMS не должен определять смену IP и слать отдельное событие.

3. Payload

Обязательные required

Поле Описание
studentId ID ученика в LMS
ipAddress IPv4 или IPv6 — только с сервера, не с фронта

Желательные

Поле Описание
lmsSessionId ID сессии LMS — для dedupe и правила multi-region
occurredAt ISO 8601, например 2026-07-10T10:15:00.000Z
externalEventId уникальный ID — идемпотентность (повтор не создаст дубль)
eventType см. таблицу выше

Опциональные

studentName, role, userAgent, courseId, courseTitle, groupId, groupTitle, moduleId, moduleTitle, teacherId, teacherName.

Контекст курса обычно уже приходит из browser SDK — на backend можно не дублировать.

4. Примеры

Минимальный (логин)

{
  "studentId": "12345",
  "ipAddress": "95.24.10.15"
}

Рекомендуемый (логин)

{
  "studentId": "12345",
  "studentName": "Иванов Иван",
  "ipAddress": "95.24.10.15",
  "lmsSessionId": "lms-session-abc",
  "occurredAt": "2026-07-10T10:15:00.000Z",
  "eventType": "session_started",
  "externalEventId": "login-12345-20260710101500"
}

Ping с актуальным IP (опционально)

Если LMS шлёт ping на каждый авторизованный запрос — сервис сам увидит смену IP. Отдельный eventType для смены IP не нужен.

{
  "studentId": "12345",
  "ipAddress": "95.24.10.15",
  "lmsSessionId": "lms-session-abc",
  "occurredAt": "2026-07-10T10:22:00.000Z",
  "eventType": "session_ping",
  "externalEventId": "ping-lms-session-abc-20260710102200"
}

curl

curl -X POST "https://anomaly.maximumtest.ru/api/lms/session-ip-events" \
  -H "Authorization: Bearer <LMS_TO_ANOMALY_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "studentId": "12345",
    "ipAddress": "95.24.10.15",
    "lmsSessionId": "lms-session-abc",
    "eventType": "session_started"
  }'

Успешный ответ: { "ok": true }.

5. Как получить IP на backend LMS

Не доверяй IP из тела запроса от браузера. Бери на сервере: за reverse proxy — первый адрес из X-Forwarded-For (если доверяешь прокси), иначе remoteAddress.
// псевдокод
function clientIp(req) {
  const forwarded = req.headers['x-forwarded-for'];
  if (forwarded) {
    return forwarded.split(',')[0].trim();
  }
  return req.socket.remoteAddress;
}

6. Ошибки и feature flag

Код Причина Что делать на LMS
401 неверный Bearer token проверить ключ
400 невалидный payload исправить запрос
403 KB2035_ANOMALY_ENABLED=false не слать или глотать ошибку
Рекомендация: вызов anomaly-service — fire-and-forget (async, без блокировки логина). Ошибку логировать, пользователю не показывать.

7. Чеклист

8. Проверка

  1. Отправить тестовый POST (curl выше).
  2. На стенде: «Наполнить БД» → сценарий multi-region-login — пример IP-событий в БД.
  3. POST /api/admin/rules/run — правило multi_region_login_10min ищет разные регионы по session_ip_event.