chore(openspec): архивировать album-live-sync и синхронизировать основные спеки
- realtime: личный поток несёт события жизненного цикла альбома и изменения счётчика фото; добавлены требования о жизненном цикле, получателях, форме payload'а, догоне и потере доступа - albums: photos_count считает только фото вне корзины
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# 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
|
||||
проверяется только при подключении, поэтому исключённый участник продолжает получать
|
||||
события альбома до разрыва соединения.
|
||||
Reference in New Issue
Block a user