• v3.6.0 992f71c0e4

    Release v3.6.0 — Портфолио прошедших игр
    Deploy Telegram Bot / build-and-push (push) Successful in 5m36s
    Deploy Telegram Bot / scan-images (push) Successful in 1m44s
    Deploy Telegram Bot / deploy (push) Successful in 39s
    Stable

    Toutsu 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 trigger validate_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 lock pg_advisory_xact_lock(20260530, 108), friendly error mapping на UniqueViolation (slug), publish-валидация (slug + description + cover + ≥1 past session + ≥1 master) перед is_public = true
    • src/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 second
    • src/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 action
    • src/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 action
    • src/GmRelay.Web/Services/Portfolio/PortfolioCoverStorageExtensions.cs + Program.cs — регистрация IPortfolioCoverStorage и AuthorizedPortfolioService в DI
    • compose.yaml — Docker volume portfolio_covers для Bot и Web сервисов
    • .env.example — добавлен PORTFOLIO_COVERS_VOLUME_NAME=gmrelay_portfolio_covers
    • README.md — новая секция "📚 Портфолио завершённых приключений" + portfolio bullets в Web Dashboard
    • docs/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 (env PORTFOLIO_COVERS_VOLUME_NAME); будущая замена на S3 — через IPortfolioCoverStorage без изменений в PortfolioService

    📦 Версия и деплой

    • версия обновлена до 3.6.0
    • Docker-образы используют тег 3.6.0
    • PR #118 интегрирован в main через merge commit 992f71c0e471e8250a717392c1a4f81f41ae8e95
    • Gitea Actions run #284 (PR checks) — success
    • Gitea Actions run #285 (deploy) — success
    • Issue #108 закрыт
    Downloads