diff --git a/AGENTS.md b/AGENTS.md index 7029eb4..2d3d4b2 100755 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,3 +86,4 @@ docker compose logs -f backend | [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) | Code Style, соглашения, пошаговое создание нового эндпоинта | | [`docs/FRONTEND.md`](docs/FRONTEND.md) | Frontend архитектура, SPA-маршрутизация, CSS, адаптивность | | [`docs/LOGGING.md`](docs/LOGGING.md) | Логирование: SLF4J + Logback, MDC, OpenTelemetry → SigNoz | +| [`docs/UI_COMPONENTS.md`](docs/UI_COMPONENTS.md) | Использование дизайн-системы (кастомные селекты, чекбоксы и др.) | diff --git a/backend/README.md b/backend/README.md deleted file mode 100644 index cdcdc0f..0000000 --- a/backend/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Руководство Backend-разработчика Magistr - -Добро пожаловать в проект Magistr! Этот бэкенд построен на **Spring Boot** и имеет сложную **мультитенантную архитектуру**, где одно приложение обслуживает множество независимых университетов, каждый со своей базой данных. В проекте также есть интеграция с Kubernetes для "горячего" управления этими тенантами. - -Здесь описано, как тут всё устроено, чтобы вы ничего не сломали. - ---- - -## 1. Архитектура мультитенантности - -Мы используем подход **Separate Database per Tenant** (Отдельная БД для каждого клиента). - -- **Как приложение понимает, к какой базе обращаться?** - Все запросы с фронтенда приходят с заголовком `Host` (например, `swsu.zuev.company`). - В классе `TenantInterceptor` (находится в `config/tenant/TenantInterceptor.java`) мы перехватываем этот запрос ДО того, как он дойдёт до контроллеров, вытаскиваем поддомен (`swsu`) и сохраняем его в `ThreadLocal` переменную через класс `TenantContext`. - -- **Как переключаются базы данных?** - Класс `TenantRoutingDataSource` наследуется от спринговского `AbstractRoutingDataSource`. Перед каждым запросом в базу (любой `findById` или `save` из репозитория) Spring спрашивает этот класс: *"Какой сейчас ключ тенанта?"*. Класс берёт имя из `TenantContext` и переключает коннект на нужную БД на лету. - -> **Важно:** Вся логика переключения абсолютно прозрачна для бизнес-кода. В контроллерах и сервисах вы пишете обычный код (`userRepository.findAll()`), и он сам выполнится в нужной базе. - ---- - -## 2. Динамическое управление тенантами (Kubernetes / ConfigMap) - -Бэкенд спроектирован для работы в **Kubernetes с несколькими репликами (replicas: 2+)**. - -Список тенантов не зашит в код: -- В K8s он лежит в специальном `ConfigMap`, который монтируется внутрь пода как файл `tenants.json`. -- В классе `DatabaseController` находится API для добавления нового тенанта из админки. -- Чтобы изменения применились ко **всем подам** без перезагрузки, `DatabaseController` вызывает `ConfigMapUpdater`. Этот класс обращается напрямую к **Kubernetes API** (используя ServiceAccount токен пода) и патчит `ConfigMap`. -- В фоне работает планировщик `TenantConfigWatcher` (каждые 30 секунд). Он следит за изменениями `tenants.json` и, если видит нового тенанта, на лету поднимает для него новый `HikariCP` пул соединений и добавляет в маршрутизатор баз данных. - ---- - -## 3. Базы данных и Миграции (Flyway) - -Мы **НЕ используем** автоматическую генерацию таблиц через Hibernate (`spring.jpa.hibernate.ddl-auto=none`). -Структурой баз данных правит **Flyway**. - -Поскольку баз данных много (они создаются динамически), стандартный Spring Boot Flyway отключён. Вместо этого `TenantConfigWatcher` вызывает Flyway **программно** в момент первого подключения нового тенанта. - -### 🛑 ПРАВИЛА ИЗМЕНЕНИЯ СТРУКТУРЫ БД: - -Если вам нужно добавить новую таблицу, колонку или изменить тип поля: - -1. **Запрещено трогать старые файлы миграций!** - Запомните: файл `V1__init.sql` (и любые другие V-файлы, которые уже попали в коммит) — **СВЯЩЕНЕН**. Если вы его измените, бэкенд не запустится на сервере с ошибкой `Migration checksum mismatch`. - -2. **Как правильно добавить таблицу?** - - Зайдите в папку `src/main/resources/db/migration/`. - - Создайте новый файл. Название **строго** по формату: `V<Номер>__<Описание>.sql`. Например: `V2__add_student_rating_table.sql`. - - Напишите в нём ваш SQL (`CREATE TABLE ...`, `ALTER TABLE ...`). - - Сохраните и запустите проект. Flyway **сам** пройдёт по всем базам данных тенантов и накатит этот скрипт. - -3. **Что если локально я накосячил в V2?** - Пока файл `V2_...` не залит в Git и крутится только у вас на локалке, вы можете его переписывать. Но для этого вам нужно зайти в вашу локальную БД (через DBeaver/pgAdmin), вручную откатить свои кривые изменения (удалить таблицу) и **удалить запись из истории Flyway**: - `DELETE FROM flyway_schema_history WHERE version = '2';` - Либо, что проще: удалите контейнер с локальной БД (`docker compose down -v`) и поднимите заново пустую. - ---- - -## 4. Как запускать проект локально - -В корневой папке репозитория (где лежит `docker-compose.yaml`) поднимите инфраструктуру: -```bash -docker compose up -d -``` -Соберется и запустится: -- Фронтенд -- Бэкенд -- Ваша локальная тестовая PostgreSQL-база данных (на порту 5432, имя базы `app_db`, юзер `myuser`, логин/пароль см. в compose файле). - -Файл `backend/tenants.json` нужен для локальной разработки. Если вы запускаете бэкенд в Docker Compose, вы можете указать URL `jdbc:postgresql://db:5432/app_db` (где `db` — имя контейнера в compose сети). -Либо, если вы тестируете взаимодействие бэкенда с вашим текущим IP-адресом (например, `192.168.1.87`), вы можете использовать этот IP. Оба варианта рабочие! Проект сразу подхватит настройки и накатит таблицы через Flyway. - -Контроллеры и бизнес-логику пишите как в обычном Spring Boot проекте. Главное, помните: у каждого тенанта — своё изолированное хранилище! diff --git a/docs/UI_COMPONENTS.md b/docs/UI_COMPONENTS.md new file mode 100644 index 0000000..3c440b1 --- /dev/null +++ b/docs/UI_COMPONENTS.md @@ -0,0 +1,115 @@ +# 🎨 Использование UI компонентов: Выпадающие списки (Dropdowns) + +В проекте Magistr используется **премиальная кастомная дизайн-система** выпадающих списков. В связи с ограничениями браузеров на стилизацию стандартных элементов `` на всём сайте, превращая их в красивые выпадающие меню. Вам **не нужно** писать сложный HTML, всё работает автоматически! + +### Как добавить новый одинарный список: + +Просто добавьте обычный тег ` + + + + + +``` + +### Как это работает: +1. В файле `frontend/admin/js/dropdown.js` инициализируется глобальный **`MutationObserver`**. +2. Как только любой скрипт или загрузка страницы добавляет `` (но оставляет его доступным из JS!). + - Рисует поверх него красивый `div.custom-select-wrapper` с нужным текстом, иконкой-шевроном и эффектом размытия фона. + - Синхронизирует состояния (если вы выберете элемент в кастомном UI, он автоматически изменит `select.value` и кинет событие `change`). + +### Динамическое обновление списка (через JS): +Если вы подгружаете список с API, просто обновите `innerHTML` **нативного селекта**, как обычно: + +```javascript +const select = document.getElementById('my-new-select'); +select.innerHTML = ''; +``` +**Магия!** Экземпляр `CustomSelect` использует свой собственный внутренний `MutationObserver` для отслеживания изменений `