Использование

Работа в админ-панели: посты и Markdown, альбомы и загрузка файлов, авторы и теги, SEO, короткие ссылки, режим защиты сайта, журнал аудита и роли.

Вся работа с контентом идёт через /dashboard. Кода касаться не нужно ни для чего, кроме статических страниц («Летопись», «О нас», «Проекты», «Образовательные программы») — их вёрстка живёт в шаблонах.

Вход и роли#

Панель открывается по адресу /auth. После входа пользователь попадает на /dashboard.

РольЗначение в базеЧто доступно
МодераторROLE_MODERATORпосты, медиа, авторы, теги
АдминистраторROLE_ADMINвсё то же плюс пользователи, SEO, настройки сайта, журнал, режим защиты, очистка кеша

Разделение сделано на уровне контроллеров: «контентные» операции лежат в StaffController и требуют любую из двух ролей, «административные» — в AdminController с проверкой строго на ROLE_ADMIN.

Заметка

Роль задаётся строкой при создании пользователя, и вводить нужно полное значение — ROLE_ADMIN или ROLE_MODERATOR. Опечатка не вызовет ошибку: пользователь создастся, сможет войти, но получит 403 на любой странице панели.

Пароль обязан содержать не меньше 8 символов, хотя бы одну латинскую букву и хотя бы одну цифру; логин — не короче 4 символов. При редактировании пользователя пустое поле пароля означает «оставить прежний», а не «стереть».

Последнего администратора удалить нельзя — сервер вернёт 406 и объяснит, почему.

Порядок наполнения#

Зависимости между сущностями строгие: у поста и у альбома обязательно есть автор, поэтому сначала авторы, потом всё остальное.

1. Авторы обязательны, без них никак 2. Теги необязательны 3. Посты и альбомы ровно один автор у каждого 4. SEO заголовки страниц 5. Публикация снять защиту

Авторы#

/dashboard/authors. У автора есть имя (2–30 символов) и необязательное описание «о себе». На сайте появляется страница /author?id=N со всеми его постами и альбомами, отсортированными от новых к старым.

Осторожно

Удаление автора удаляет весь его контент. Связь настроена с orphanRemoval, поэтому вместе с автором из базы уйдут все его посты и все записи об альбомах. Каталоги с фотографиями при этом останутся на диске «сиротами» — их придётся удалять вручную. Если нужно просто переименовать человека, редактируйте автора, а не создавайте нового.

Теги#

/dashboard/tags. Имя тега — 2–30 символов и должно быть уникальным; описание — от 4 символов. Тег общий для постов и альбомов, страница — /tag?id=N.

Удаление тега безопасно: перед удалением связи с постами и альбомами разрываются автоматически, сам контент остаётся на месте.

Посты#

/dashboard/posts. Поля:

ПолеОграничениеГде видно
Заголовок4–50 символовв карточке, в title страницы
Введениеот 4 символовв карточке ленты и в описании для соцсетей
ТекстMarkdown, обязателенна странице /records/read?id=N
ТипNEWS, STORY, ANNOUNCEMENTкак «НОВОСТЬ», «РЕПОРТАЖ», «ОБЪЯВЛЕНИЕ»
Авторровно один, обязателенссылка под заголовком
Тегисколько угодно, необязательныв карточке и в фильтрах

Дата ставится автоматически в момент создания и не редактируется. Поле «опубликовал» заполняется именем вошедшего сотрудника — это не то же самое, что автор: автор — тот, чьё имя показывается на сайте, «опубликовал» — тот, кто нажал кнопку. Второе видно только в панели.

Текст поста#

Текст пишется в Markdown в редакторе EasyMDE. Важная особенность: Markdown превращается в HTML в браузере читателя, а не на сервере. В базе лежит исходный Markdown, страница /records/read отдаёт его как есть, а marked.js на клиенте рендерит.

Из этого следуют два практических вывода:

  • HTML внутри Markdown работает — можно вставить таблицу, <details> или ролик с YouTube. Разрешённые источники для встраивания ограничены политикой безопасности: YouTube, Rutube, Instagram, Telegram, MAX;
  • опасные теги вырезаются — при включённой защите клиентский санитайзер выбрасывает script, iframe, form, object, svg, обработчики событий вида onclick и ссылки со схемами javascript: и data:.

Внимание

iframe попадает в список вырезаемых тегов. Это значит, что вставить видео прямо в текст поста при включённой защите не получится — санитайзер уберёт вставку. Отключать защиту ради этого не стоит: без неё любой, кто может редактировать посты, получает возможность выполнить произвольный скрипт в браузере каждого посетителя.

При редактировании поста меняются заголовок, введение, текст и тип. Автора и теги существующего поста через форму редактирования не изменить — эти поля обработчик правки игнорирует.

Альбомы: фото и видео#

/dashboard/media. Здесь логика самая непривычная, поэтому по шагам.

Открыли форму браузер создал UUID batchId Перетащили файлы каждый уходит отдельным запросом POST /dashboard/media/upload Файлы на диске media/batchId/preview.jpg media/batchId/01.jpg ... только теперь — «Создать» Запись в базе: название, тип, автор, теги, batchId сама галерея не хранится в базе — она читается из каталога при открытии страницы
  1. Откройте форму создания. Браузер сразу генерирует batchId — случайный UUID. Это будущее имя каталога на диске и единственная связь между записью в базе и файлами.
  2. Загрузите превью. Отдельная зона, принимает ровно один файл и только .jpg, не больше 15 МБ. Файл всегда сохраняется под именем preview.jpg, как бы он ни назывался у вас. Это обложка альбома в лентах.
  3. Загрузите содержимое. Вторая зона принимает изображения и видео. Файлы уходят по одному, имена сохраняются.
  4. Заполните поля и нажмите «Создать». Название — 4–50 символов, тип — PHOTO или VIDEO, автор — ровно один, теги — по желанию.

Важно

Порядок именно такой: файлы физически уже лежат на диске к моменту создания записи. Если закрыть форму, не нажав «Создать», каталог с загруженными файлами останется на сервере, а записи о нём не будет — получится мусор, который удаляется только вручную из файловой системы.

Что нужно знать про галерею:

  • порядок файлов — алфавитный. Управлять им можно только именами, поэтому нумеруйте: 01-..., 02-.... Иначе 10 окажется раньше 2;
  • preview.jpg в галерею не попадает — он всегда исключается из списка;
  • обложка не обязательна. Без preview.jpg подставится картинка по умолчанию из web.storage.default-preview, но альбом будет выглядеть безлико;
  • тип определяет шаблон, а не содержимое. Альбом с типом VIDEO открывается видеоплеером; при этом запрос /media/album?id=N для видеоальбома сам перенаправится на нужный шаблон, так что ошибиться ссылкой нельзя;
  • при редактировании альбома меняются только название и тип. Автора, теги и состав файлов форма правки не трогает.

Удаление альбома удаляет файлы с диска — весь каталог batchId целиком и безвозвратно. Это единственная операция в панели, которая физически стирает данные.

SEO#

/dashboard/seo. Для каждого адреса хранятся title и meta description. Записи двух видов:

Обычные страницы/, /records, /media, /history, /about, /education, /projects. Заголовок и описание используются как есть.

Шаблоны динамических страниц/media/album, /media/video_album, /media/read, /author, /tag. В них работают подстановки, которые заменяются на данные конкретного материала:

ПодстановкаЗначениеГде доступна
<title>название поста или альбомаальбомы, посты
<author>имя автораальбомы, посты
<date>дата публикацииальбомы, посты
<type>тип постапосты
<name>имя автора или название тегастраницы автора и тега

Например, шаблон <title> | ФОТО | Сайт отряда для альбома «Поездка в Суздаль» даст Поездка в Суздаль | ФОТО | Сайт отряда.

Заметка

Через панель правятся только title и description. Ключевые слова, тип Schema.org и родительская страница для «хлебных крошек» задаются в коде при первом запуске и меняются правкой properties.json вручную (с последующим перезапуском) либо в исходниках.

sitemap.xml собирается сам и включает все статические страницы, всех авторов, все теги, все посты и все альбомы. Он кешируется на час — новый материал появится в карте сайта не мгновенно, либо сразу после очистки кеша.

Настройки сайта#

Всё это на главной странице панели, /dashboard, и доступно только администратору.

Контакты — телефон, адрес, e-mail. Показываются в подвале и попадают в разметку Organization для поисковиков.

Соцсети — список ссылок с названием, описанием и иконкой. Иконка задаётся путём к файлу, например /assets/icons/telegram.svg. Удаление ищет соцсеть по названию, поэтому два одинаковых названия делать не стоит.

Баннер-уведомление — полоса с текстом на всех страницах. Включается и выключается флажком, текст произвольный. Удобно для «Запись на смену открыта до 15 августа».

Короткие ссылки — пары «откуда → куда». Добавив /lager → /records/read?id=42, вы получаете рабочий адрес example.ru/go/lager. Регистр не важен: и ключ, и запрос приводятся к нижнему регистру. Технически это не редирект, а внутренняя переадресация — адрес в строке браузера останется /go/lager, что удобно для печатных материалов и QR-кодов.

Очистка кеша — одна кнопка, сбрасывает кеши постов, альбомов, авторов, тегов и карты сайта. Нужна, если правили properties.json мимо панели или хотите увидеть изменения немедленно.

Режим защиты#

Пока сайт наполняется, публичную часть можно закрыть. Включается флажком в /dashboard, там же лежит готовая ссылка с токеном — её можно отправить заказчику для приёмки.

запрос Защита включена? Сотрудник? Служебный адрес? Отметка в сессии? да нет сайт открывается ?token=... верный? токен запомнится в сессии да пускаем locked .html

Что важно:

  • токен запоминается в сессии. Открыв ссылку с токеном один раз, человек дальше ходит по сайту без него. Сам токен убирается из адресной строки редиректом, чтобы не утёк через историю браузера или чужой скриншот;
  • сотрудники проходят всегда, поэтому запереть себя снаружи невозможно;
  • вход в панель остаётся доступен/auth, /process_login, /dashboard, страница ошибки, robots.txt и иконки в список закрываемого не входят;
  • токен можно перевыпустить кнопкой в панели — прежние ссылки перестанут работать, но уже открытые сессии сохранят доступ до истечения;
  • защита не предназначена для хранения секретов. Это ширма от случайных посетителей и поисковиков, а не контроль доступа.

Журнал аудита#

/dashboard/journal, только для администратора. Пишутся все значимые операции панели: кто, что, когда и с какими аргументами. Отдельно логируются успешные и неудачные входы.

Пароли в журнал не попадают — вместо значения записывается ******. Текст поста тоже не пишется целиком, чтобы журнал не распухал.

Записи журнала не имеют срока жизни и накапливаются бесконечно. Для сайта с несколькими редакторами это не проблема, но чистить таблицу action_logs время от времени стоит.

Что через панель не меняется#

Честный список того, для чего придётся идти в код и пересобирать:

  • текст и вёрстка страниц «Летопись», «О нас», «Проекты», «Образовательные программы» — это шаблоны Thymeleaf, содержимое вписано в разметку;
  • домен в robots.txt;
  • ключевые слова, типы Schema.org и структура «хлебных крошек»;
  • дизайн, цвета, шрифты — обычный CSS в src/main/resources/static/assets/;
  • размер страницы в лентах (page-size, по умолчанию 6) и время жизни кеша.

Совет

Правки в шаблоны можно вносить и без пересборки: если раскомментировать spring.thymeleaf.prefix=file:/opt/OtryadWebsite/templates/, приложение начнёт читать шаблоны из каталога на диске. Удобно для быстрых правок текстов, но следите, чтобы каталог не разошёлся с версией кода.