# Proposal ## Why Real-time сейчас работает только для открытого альбома: события изменений альбома публикуются в канал конкретного альбома, поэтому у пользователя, который не смотрит этот альбом, список альбомов (рейл, главная) остаётся устаревшим до перезагрузки страницы. Персональный поток `GET /api/sse/me` существует, но не несёт событий жизненного цикла альбома, и ни один клиент в него не подписан — то есть создание, переименование, смена обложки, изменение состава участников и удаление альбома не доходят до тех, кого они касаются. Отдельно устаревает состав фото: загрузка, перемещение, копирование, восстановление и удаление фото не публикуют событие альбома, поэтому счётчик `photos_count` и обложка на карточках не обновляются, а открытая страница альбома у других клиентов не показывает загруженное фото (`Upload` не публикует `photo.created`, хотя способность `realtime` это требует). ## What Changes - Персональный поток `GET /api/sse/me` начинает нести события жизненного цикла альбома: `album.created` и `album.updated` с полным объектом альбома в той же форме, что отдаёт `GET /api/albums`, и `album.deleted` с `{id}`. - Получатели события — пользователи, для которых альбом входит в список доступных: владелец (`albums.owner_id`) и участники (`album_members`); событие доставляется веером по личным каналам. - Публикации добавляются в: создание альбома, принятие инвайта, изменение названия/описания/ обложки, смену роли, исключение участника и выход, удаление альбома. - Список альбомов обновляется и при изменении состава фото: загрузка, перемещение, копирование, восстановление, перевод в корзину и окончательное удаление публикуют `album.updated` в личные потоки владельца и участников, поэтому `photos_count` и обложка на карточках становятся актуальными без перезагрузки. - Загрузка фото публикует `photo.created` в канал альбома, поэтому открытая страница альбома у других клиентов добавляет новое фото; готовность вариантов по-прежнему приходит как `photo.updated` и обновляет фото на месте. - Web-клиент подписывается на `/api/sse/me` на уровне layout'а приложения и применяет события к стору списка альбомов (`upsert` / `remove`) без повторного запроса; при переподключении догоняет состояние через `GET /api/albums`. - Открытая страница альбома начинает обрабатывать `album.deleted` (сейчас обрабатывается только `album.updated`, поэтому удалённый альбом продолжает отображаться у участников). - Контракт существующих пер-альбомных событий (`photo.*`, `like.*`, `comment.created`, `member.*`, `album.updated`) и REST не меняется: `Upload` начинает публиковать уже предусмотренный контрактом `photo.created`, остальное — аддитивно. **Не входит в этап** (осознанно отложено): персистентный центр уведомлений (колокольчик, список непрочитанных, новые типы в таблице `notifications`); Android-клиент (личный поток остаётся совместимым, подключение мобильного клиента — отдельный шаг); принудительный обрыв пер-альбомного SSE у исключённого участника. ## Capabilities ### New Capabilities _Нет — поведение описывает расширение существующей способности `realtime`, отдельная способность не вводится._ ### Modified Capabilities - `realtime`: персональный поток обязан нести события жизненного цикла альбома с полным payload'ом; определяются получатели события, поведение клиента при получении (обновление или удаление альбома в списке без запроса) и догон состояния при переподключении. Дополнительно определяются события при изменении состава фото: список альбомов обновляется в реальном времени, а загрузка фото публикует `photo.created`. - `albums`: уточняется счётчик `photos_count` — он считает только доступные в альбоме фото (вне корзины), чтобы совпадать с сеткой и корректно меняться при переводе в корзину и восстановлении. ## Impact - **Код (backend):** `internal/sse` (веер по личным каналам), `internal/albums/service.go` (Create/Update/Delete, рассылка `album.updated` получателям), `internal/members/service.go` (Accept/ChangeRole/Remove), `internal/photos/service.go` (загрузка/удаление/перемещение/ копирование/восстановление фото публикуют `photo.created` и `album.updated`), точка сборки `internal/app/app.go` и `cmd/server/main.go` — если для веера потребуется новый интерфейс-резолвер аудитории альбома. - **Код (frontend):** `routes/(app)/+layout.svelte` (единая подписка на `/api/sse/me`), `lib/stores/albums.svelte.ts` (upsert/remove/load как обработчики событий), `routes/(app)/albums/[id]/+page.svelte` (обработка `album.deleted`), `lib/sse.ts` — список известных событий. - **API:** новых HTTP-эндпоинтов нет; существующий `GET /api/sse/me` начинает отдавать дополнительные типы событий. Для старых клиентов изменение аддитивное. - **Не затрагивается:** `notifications` (персистентные уведомления остаются как есть), `android-client` (требование live-обновлений там и так ограничено активным экраном альбома), схема БД, миграции, зависимости. - **Известное ограничение, фиксируемое отдельной задачей:** доступ к пер-альбомному SSE проверяется только при подключении, поэтому исключённый участник продолжает получать события альбома до разрыва соединения.