Files
iu 6e68242ec2 chore(openspec): архивировать album-live-sync и синхронизировать основные спеки
- realtime: личный поток несёт события жизненного цикла альбома и изменения
  счётчика фото; добавлены требования о жизненном цикле, получателях, форме
  payload'а, догоне и потере доступа
- albums: photos_count считает только фото вне корзины
2026-10-15 23:25:07 +03:00

8.3 KiB
Raw Permalink Blame History

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 проверяется только при подключении, поэтому исключённый участник продолжает получать события альбома до разрыва соединения.