-
released this
2026-06-02 18:38:48 +03:00 | 60 commits to main since this release🛠 Feature 3.6.0 — Портфолио прошедших игр
Добавлено публичное портфолио завершённых приключений на витрине мастера: мастера могут оформлять проведённые игры с обложкой, описанием, составом ГМов и модерируемыми отзывами игроков. Публичная страница
/portfolio/{slug}дополняет каталог будущих игр из/showcase(3.4.0) и публичные профили мастеров (3.5.0).🧩 Что вошло в релиз
src/GmRelay.Bot/Migrations/V029__add_completed_game_portfolios_and_reviews.sql— таблицыportfolio_games,portfolio_game_sessions,portfolio_game_masters,portfolio_game_reviews+ statement-level триггеры + deferred constraint triggervalidate_public_portfolio_game_required_links, атомарно проверяющий обязательные связи опубликованной игрыsrc/GmRelay.Web/Services/Portfolio/PortfolioContracts.cs— sanitized public DTOs (PublicPortfolioCard,PublicPortfolioGame,PublicPortfolioMaster,PublicPortfolioReview) без приватных UUID / storage keys / platform info; protected DTOs для редактора и модерацииsrc/GmRelay.Web/Services/Portfolio/IPortfolioStore.cs— 16 методов: 3 публичных чтения, 11 protected CRUD, 2 метода модерации отзывовsrc/GmRelay.Web/Services/Portfolio/PortfolioValidation.cs— slug regex + trim/collapse, title/description/format/review body нормализацияsrc/GmRelay.Web/Services/Portfolio/Covers/LocalPortfolioCoverStorage.cs— JPEG/PNG/WebP signature validation (не только content-type), 5 MB cap,SafeKeyPatternот path traversal, temp-file cleanup на ошибкеsrc/GmRelay.Web/Services/Portfolio/PortfolioService.cs— публичные чтения мастер/клуб/detail, protected CRUD с advisory lockpg_advisory_xact_lock(20260530, 108), friendly error mapping наUniqueViolation(slug), publish-валидация (slug + description + cover + ≥1 past session + ≥1 master) передis_public = truesrc/GmRelay.Web/Services/Portfolio/AuthorizedPortfolioService.cs— все 9 management-методов подIsGroupManagerAsync; cross-club →SessionAccessDeniedException; review submission требуетpublicationConsent = true; cover replace: new → DB → old с cleanup новой обложки на ошибке; delete: row first, cover secondsrc/GmRelay.Web/Components/Pages/PortfolioEditor.razor—/portfolio/manage/{PortfolioGameId:guid}(Authorize) с cover upload, draft/publish, delete, модерация отзывовsrc/GmRelay.Web/Components/Pages/GroupCompletedSessions.razor—/group/{GroupId:guid}/completedсо списком прошедших сессий иДобавить в портфолиоquick actionsrc/GmRelay.Web/Components/Portfolio/PortfolioCardGrid.razor— переиспользуемая сетка карточек для публичных страницsrc/GmRelay.Web/Components/Pages/PublicPortfolio.razor—/portfolio/{slug}(PublicLayout, без[Authorize]) с обложкой, описанием, составом ГМов, одобренными отзывами, формой отзыва с consent-чекбоксомsrc/GmRelay.Web/Components/Pages/PublicMasterProfile.razor,PublicClub.razor— portfolio card grid под существующим upcoming-session контентомsrc/GmRelay.Web/Components/Pages/GroupDetails.razor,SessionHistory.razor— секцияПроведённые приключенияс badges и quick actionsrc/GmRelay.Web/Services/Portfolio/PortfolioCoverStorageExtensions.cs+Program.cs— регистрацияIPortfolioCoverStorageиAuthorizedPortfolioServiceв DIcompose.yaml— Docker volumeportfolio_coversдля Bot и Web сервисов.env.example— добавленPORTFOLIO_COVERS_VOLUME_NAME=gmrelay_portfolio_coversREADME.md— новая секция "📚 Портфолио завершённых приключений" + portfolio bullets в Web Dashboarddocs/c4-system-context.md— обновлены System Context и Container диаграммы, новая Level 3 Component диаграмма для портфолио с границейIPortfolioCoverStorage(future S3 replacement)- Тесты:
PortfolioContractsTests,PortfolioValidationTests,LocalPortfolioCoverStorageTests,PortfolioCoverRuntimeWiringTests,PortfolioServiceSourceTests(включая regression assertion на неизменённыйinterval '4 hours'для showcase),AuthorizedPortfolioServiceTests,PortfolioPagesTests(Tasks 6+7) - Синхронизированы версии:
Directory.Build.props,NavMenu.razor,compose.yaml(3 образа),deploy.yml,README.md→ 3.6.0
🗡 Что это даёт
- Мастера могут публиковать проведённые приключения с обложкой, описанием, системой, форматом, составом ГМов
- Публичная страница
/portfolio/{slug}показывает карточку с обложкой, описанием, составом и одобренными отзывами — без раскрытия приватных идентификаторов - Публичные профили мастеров
/gm/{slug}и публичные страницы клубов/club/{slug}получают секциюЗавершённые приключенияс карточками - Игроки, участвовавшие в сессии (non-GM, Active), могут оставить модерируемый отзыв — без consent-чекбокса отзыв не сохраняется
- Мастер/co-GM модерируют отзывы (Approve/Reject/Hide) перед публикацией
- Cross-club изоляция: ни одна управляющая операция не доступна менеджеру чужого клуба
- Обложки хранятся в persistent volume
portfolio_covers(envPORTFOLIO_COVERS_VOLUME_NAME); будущая замена на S3 — черезIPortfolioCoverStorageбез изменений вPortfolioService
📦 Версия и деплой
Downloads