2026-09-17 21:19:52 +03:00

LMS

Микросервисная система для проведения практических занятий (Kotlin + Spring Boot + PostgreSQL 18).

Документация

UML- и архитектурные диаграммы (Mermaid) — в каталоге docs/:

Модули

  • libs/common-http — общий формат ошибок и обработчик исключений.
  • libs/security-jwt — выпуск/проверка JWT (RSA) и PasswordEncoder.
  • libs/markdown — серверный рендер markdown со спойлерами и санитизацией.
  • libs/test-support — общая тестовая инфраструктура (Testcontainers, postgres:18).
  • services/identity-service — аккаунты, студенты, группы, зачисления (схема identity).
  • services/academic-service — курсы, статьи, лабораторные, уровни, варианты (схема academic).
  • services/progress-service — уровни студентов, распределение вариантов, работы, оценки (схема progress).
  • services/gateway — Spring Cloud Gateway, маршрутизация и проверка JWT.
  • web — серверный веб-интерфейс (Thymeleaf + htmx), BFF.

Требования

  • JDK 21 (runtime) + Docker (для PostgreSQL 18). Gradle сам скачает toolchain 21 для сборки.
  • Не запускать сервисы на JAVA_HOME=Java 25: на 25 ломается logback внутри fat-jar (NoClassDefFoundError ThrowableProxy), что роняет потоки Tomcat. Скрипт run.sh использует JDK 21 автоматически.

Запуск

./gradlew bootJar -x test    # собрать все сервисы
./run.sh start               # поднять БД + все 5 сервисов на JDK 21
./run.sh status              # проверить здоровье
./run.sh logs web            # посмотреть лог сервиса
./run.sh stop | restart

Можно и без скрипта: docker compose up -d postgres, затем /usr/lib/jvm/java-21-openjdk/bin/java -jar <jar> для каждого сервиса, или ./gradlew :services:identity-service:bootRun и т.д. по одному.

Порты по умолчанию: gateway :8080, identity :8081, academic :8082, progress :8083, web :8090. Интерфейс: http://localhost:8090.

Запуск в Docker (для сервера)

На сервере Java не нужна: всё собирается и работает в контейнерах (multi-stage Dockerfile, сборка на JDK 21, запуск на JRE 21).

cp .env.prod.example .env.prod     # заполнить LMS_DB_PASSWORD
./deploy/deploy.sh up              # сборка + запуск (postgres, 5 сервисов)
./deploy/deploy.sh logs web
./deploy/deploy.sh ps
./deploy/deploy.sh down

Наружу публикуется только web на 127.0.0.1:8090; reverse proxy — nginx (конфиг и полная инструкция по деплою на lms.our-place.ru в deploy/DEPLOY.md и deploy/nginx/lms.our-place.ru.conf).

Наполнение данными

Порядок ввода данных (всё через админку): /setup → администратор; импорт групп (.xls, логин = студенческий номер, временный пароль со сменой при первом входе); импорт лабораторной ZIP'ом (lab.yaml + README.md + variants/*.md); «Распределить» — случайная раздача по кругам в рамках уровня; оценки/защита — на странице группы; учебные материалы — статьи или ZIP (chapters/…, доступны гостям).

Подробный пошаговый порядок — в deploy/IMPORT.md.

Текущий функционал

Открыть http://localhost:8090. При первом запуске создаётся аккаунт администратора (/setup); далее — вход по логину и паролю.

  • Доступ: JWT (RSA, ключи identity генерирует при первом старте и хранит в БД), проверка токена на gateway, роли ADMIN/STUDENT, CSRF на всех POST-формах, session fixation-защита; вход студента с обязательной сменой пароля.
  • Группы и студенты: создание групп, импорт .xls/.xlsx/.csv, ручное добавление студента (генерация пароля) и «Добавить существующего» (перевод из другой группы), «Отчислить из группы», выгрузка логинов XLSX/CSV, сброс пароля.
  • Лабораторные: импорт ZIP (lab.yaml + README.md + variants/*.md + assets/); создание через UI с произвольным числом уровней; единая форма настроек с одной кнопкой «Сохранить всё»; статусы draft/published (клик по бейджу меняет); даты в московском времени.
  • Распределение вариантов (только на странице лабы): «Распределить» с выбором групп; раздача случайная и равномерная — студенты выбранных групп идут общей очередью, вариант берётся со своего уровня, пока в уровне есть невыданные — без повторов, затем выдаются наименее занятые; ручные закрепления сохраняются всегда. На странице лабы — список назначений по группам (уровень/вариант каждому), массовые операции чекбоксами, индикаторы «выдано/всего». Матрица на странице группы — просмотр (вариант, сдача) + оценки/защита, управление раздачей оттуда убрано.
  • Индивидуальные задания (защита): на странице группы — назначение/изменение задания, импорт CSV (2 или 3 колонки, с кавычками и \n), выдача кода, «доп. задание: начать заново» после завершения защиты.
  • Материалы (/learn, публичная читалка): главы → разделы → статьи в markdown (код, спойлеры, картинки; санитизация OWASP); импорт ZIP (chapters/N-глава/N-раздел/N-статья.md + assets/); создание/переименование/ удаление глав, разделов, статей; перенос статей между разделами.
  • Студент: дашборд успеваемости (сдано, средний балл, лучшая/слабая оценка), карточки опубликованных лаб, страница лабы: условие, свой вариант, сдача файлов (zip/.c, переотправка до дедлайна, история, скачивание последней), оценка и комментарии отдельным блоком, защита по одноразовому коду (30 мин).
  • Публикация оценок: в группе по каждой лабе — кнопка «опубликовать»; студент видит оценку и комментарии только после публикации (публикуется вся группа).
  • Комментарии к оценкам: тональность (positive/negative/neutral), на странице студента — ненумерованными списками в трёх колонках, хранятся в БД, автоподсказки при вводе ранжируются по частоте использования.
  • Архив сдач группы: одна кнопка «Скачать все работы» — ZIP со всеми последними решениями группы, разложенными по лабам и студентам.
  • Материалы /learn доступны без входа; аккордеон глав: свёрнуты все, открыта последняя.
  • UI: светлая/тёмная тема (сохраняется в localStorage), модальные окна вместо отдельных страниц, аккордеоны глав/разделов в /learn, фавиконка, адаптивная вёрстка без inline-стилей.

Примечания

  • JDK 21 обязателен для runtime. В связке Spring Boot 4.0.x + fat-jar logback не находит ThrowableProxy, и это падает при любом исключении в потоке ответа. Если после деплоя сервис вдруг «висит» на страницах (а /actuator/health даёт 200) — лечится перезапуском (./run.sh restart).
  • Даты хранятся в UTC (instant), а в UI конвертируются в Europe/Moscow (DateUtils); формы datetime-local отправляются в московском времени.
  • Капча/rate-limit на входе отсутствует — для учебного стендома оставлено; пароли студентов показываются один раз при выдаче.
  • lms.submissions.dir (default ./data/submissions) и lms.content.assets-dir держите на постоянном томе при деплое.

Сборка и тесты

./gradlew build       # сборка всех модулей
./gradlew check       # тесты и проверки

Переменные окружения для БД: LMS_DB_URL, LMS_DB_USER, LMS_DB_PASSWORD.

Description
No description provided
Readme 5.9 MiB
Languages
Kotlin 55.2%
HTML 26.4%
JavaScript 12.5%
CSS 5.2%
Shell 0.5%
Other 0.2%