Media Gallery
HR bot (Telegram, StackHiringBot) — медіатека для HR/Recruiter: завантаження й модерація файлів (фото/відео/pdf/docs) з іменем і описом, які згодом відправляються кандидатам HR-бота як шаблони в Telegram. За задумом — аналог сусіднього модуля emails/email-media (той самий upload → moderation → status → send), але без прив’язки до “леді” (тут прив’язки до сутності взагалі немає — плоский спільний список HR+Recruiter).
Документ — аналіз задачі до реалізації. Верифіковані факти — у розділах нижче; невідоме — у “Відкриті питання” вверху, на явний запит.
Відкриті питання
1. Відправка в Telegram файлу, що зберігається по URL (uploadFile)
З’ясовано аналізом коду:
FileManagerService.uploadFile(src/stack/components/FileManger/FileManagerService.ts) заливає файл у Cloudflare R2 (S3-сумісний) і повертає публічний URL (${R2_PUBLIC_URL}/${key}) — те саме джерело, яким користуєтьсяemail-media(fileUrlвemail-media.service.ts).- HR bot вже вміє відправляти вкладення просто по URL, без проміжного завантаження на свій бек:
hiring-telegraf-bot.service.ts → _handleStandardMessageвикликаєthis.bot.telegram.sendPhoto/sendDocument/sendAudio/sendVoice(telegramId, attachment.url, extra)— Telegraf/Telegram Bot API приймає HTTP(S)-посилання якsourceі сам його стягує на своїй стороні. Тобто для Media Gallery принципово нової механіки не треба — той самий підхід підходить і для шаблонів. - Проблема, яку треба закрити: у поточному switch немає кейсу
video(sendVideo) — є тількиphoto | document | audio | voice. Задача явно вимагає тип “відео”, тому кейс треба додати. - Відкрито (потребує перевірки на реальних файлах): Telegram Bot API обмежує розмір файлу, який приймає саме по URL (менше, ніж при прямому мультипарт-аплоаді на
sendX). Для відео/pdf з рекрутингу файли можуть бути більшими — треба перевірити на практиці, чи всі кандидатські медіа проходять по URL, чи для важких файлів знадобиться прогрузка черезmultipart(тобто спершу скачати з R2 на бек, потім віддати в Telegram як buffer/stream). - Відкрито: чи
pdf/docsвзагалі коректно рендеряться Telegram-клієнтом якsendDocumentпо URL (технічно так, Telegram сам віддає файл як документ) — здається безпроблемним, але не перевірено на цьому боті.
2. Доступи HR / Recruiter (чернетка від задачі, потребує підтвердження)
| Дія | HR | Recruiter |
|---|---|---|
| Завантажити медіа | ✓ — тільки в статус on_moderation | ✓ — тільки в статус on_moderation |
| Модерація (approve/reject) | ✓ | ✗ |
| Авто-видалення rejected 3 дня з коментарем | ||
| Редагування (ім’я/опис) | ✓ | своїх on_moderation |
- Це буквально дзеркалить
email-media: тамtopManager/supervisor= повний доступ (create+moderate+delete),operator= тільки create →on_moderation. HR ~ supervisor/topManager, Recruiter ~ operator. - Відкрито: Recruiter бачить весь список медіа.
- Відкрито: Для Media Gallery йти шляхом - декларативний
MiddlevareGuard([...roles])
3. На майбутнє: прив’язка медіа до команди
- Наразі (як і в чернетці задачі) немає сутності “команда” для HR/Recruiter в контексті медіа — це буде плоский спільний список на всіх HR+Recruiter.
- Питання на майбутнє (не блокує MVP): чи прив’язувати медіа до
hrId-власника (recruiter бачить тільки галерею “свого” HR), чи вводити окрему сутність команди. Поки що рішення відкладено — фіксуємо тут, щоб не загубилось.
Суть
Спільна медіатека для HR і Recruiter HR-бота (Telegram, user/StackHiringBot): завантаження файлів (фото, відео, pdf, docs) з ім’ям і описом, модерація перед використанням, і подальша відправка як шаблон повідомлення кандидату в Telegram.
Дизайн за аналогією з Email Media
| Email Media (сусідній модуль, є) | Media Gallery (HR bot, план) | |
|---|---|---|
| Прив’язка | до stackLadyId (конкретна анкета) | без прив’язки до конкретної сутності (спільний список) — див. “Відкрите питання 3” |
| Типи | photo, video, audio | photo, video, pdf, docs (== “документ”) |
| Статуси | approved / rejected / on_moderation | той самий набір, за задачею |
| Ім’я/опис | name (з файлу), опису нема | name — можна вказати вручну; новий description |
| Хто завантажує з фінальним статусом | topManager/supervisor | HR |
| Хто завантажує тільки в модерацію | operator | Recruiter |
| Видалення файлу | видаляє і оригінал, і previewUrl (прев’ю з відео) з R2 | той самий підхід, якщо буде preview для відео |
| Використання | прикріплюється до email через emailMediaIds | відправляється як шаблон в Telegram кандидату HR bot |
Що інакше в типах медіа
Поточний EmailMediaType = photo | video | audio (без документів). Для Media Gallery знадобиться новий enum/мапа з підтримкою pdf, docs.
Структура файлів (план)
Бек — папка media-gallery/ розміщена всередині user/StackHiringBot/ (не поруч з emails/) — бо це не крос-фічева бібліотека, а частина домену одного конкретного бота (HR/Recruiter, один hiring-bot). Внутрішня структура папки один-в-один копіює скелет email-media, тільки без stackLadyId і з новими полями/типами.
user/StackHiringBot/media-gallery/
├── dto/
│ └── index.ts # GetMediaGalleryDto, MediaGalleryIdDto, UpdateMediaGalleryStatusDto, UpdateMediaGalleryInfoDto
├── media-gallery.model.ts # MediaGallery, MediaGalleryStatus (approved/rejected/on_moderation), MediaGalleryType (photo/video/pdf/docs)
├── media-gallery.repository.ts # getMedia (cursor+filters), createMedia, findById, deleteById, updateStatus, updateInfo (name/description)
├── media-gallery.service.ts # роль → стартовий статус (HR=approved, Recruiter=on_moderation), правила видалення/редагування, прев'ю відео
├── media-gallery.controller.ts # MiddlevareGuard([StackRoles.hr, StackRoles.recruiter]) — за рішенням у "Відкриті питання → 2"
└── utils.ts # MIME_TYPE_MAP (+ pdf/docs), convertUserToMediaGalleryUserInfoResponse, checkMediaGalleryEditable(media, user)
Точки дотику в існуючому коді (не нові файли, а правки):
src/TYPES.ts— символиMediaGalleryController/MediaGalleryService/MediaGalleryRepository, за зразкомEmailMedia*.src/stack/StackModule.ts— імпорт з./components/user/StackHiringBot/media-gallery/media-gallery.controller,bind(...).inSingletonScope(),this.app.use('/media-gallery', ...).user/StackHiringBot/interfaces.ts—StandardMessagePayload.attachment.typeрозширити з'photo' | 'document' | 'voice' | 'audio'до+ 'video'.user/StackHiringBot/hiring-telegraf-bot.service.ts(_handleStandardMessage) — додатиcase 'video': sendVideo(...).user/StackHiringBot/hiring-workspace.service.ts+hiring-workspace.controller.ts— новий метод/routesendMediaTemplate(hireId+mediaGalleryId, без реаплоаду файлу — бере готовийurl/typeз галереї, на відміну відsendMessage, який завжди приймає свіжий файл).
UI (репо stack-client) — за зразком переюзаного common_stack_components/email_media, але без page_users (нема дерева supervisor→operator→lady — див. “Відкрите питання 3”: список поки що плоский):
stack-client/src/components/common_stack_components/media_gallery/
├── Index.tsx # role-обгортка (hr | recruiter), allowedActionsByRole — як в email_media
├── center_section/
│ └── Index.tsx # список approved/rejected + upload + фільтри по типу
├── moderation_section/
│ └── Index.tsx # черга on_moderation — approve/reject (HR), перегляд своїх (Recruiter)
├── types.ts # MediaGalleryType, MediaGalleryStatus, MediaFile, MediaGalleryAllowedActions
└── utils/
└── useLoadMediaGallery.ts
Плюс:
stack-client/src/utils/mediaGallery.js— константи ендпоінтів (MEDIA_GALLERY_GET_BY_OPTIONS,_CREATE_FILE,_DELETE_FILE,_UPDATE_STATUS,_UPDATE_INFO), за зразкомutils/emailMedia.js.- Точка входу сторінки — новий пункт в лівому меню HR/Recruiter, ймовірно
src/components/recruiter/media_gallery/Index.tsx, що рендерить<MediaGallery role={...} />(конкретне місце в меню/роутингу — TBD, не досліджено). - Відправка шаблону з чату кандидата — picker в footer
workspace/live_chat(за зразком goldenemails_footer/media/MediaPicker.tsx) — конкретний файл/шлях TBD.
Зв’язки
- Аналог:
emails/email-media(src/stack/components/emails/email-media/, в тому ж репо, але інший бounded-context) — той самий патерн upload → moderation → status → send. - Домен:
user/StackHiringBot/— сусідні файлиhiring-workspace.*,hiring-telegraf-bot.service.tsтощо;media-gallery/— єдина папка-виняток серед переважно пласкихhiring-*файлів цієї директорії. - Telegram-відправка:
hiring-telegraf-bot.service.ts(_handleStandardMessage) — точка, куди додається кейсvideoі шаблонна відправка медіа-галереї. - UI:
common_stack_components/email_media(репоstack-client, той самий компонент з рольовимиallowedActionsвже параметризований поrole/family) — джерело патерну дляmedia_gallery.
Сторінки
- UI — TBD, план шляху:
stack-client/src/components/common_stack_components/media_gallery/(див. “Структура файлів (план)”) - Backend — TBD, план шляху:
user/StackHiringBot/media-gallery/(див. “Структура файлів (план)“)