# realtime Specification ## Purpose Поток событий альбома и персональный поток через Server-Sent Events. ## Requirements ### Requirement: Подписка на события альбома Клиент SHALL иметь возможность подписаться на события конкретного альбома через SSE. #### Scenario: Подписка на альбом - **WHEN** участник подключается к SSE-потоку альбома - **THEN** система устанавливает text/event-stream и отправляет heartbeat ### Requirement: События альбома Система SHALL публиковать события photo.created, photo.updated, photo.deleted, comment.created, like.added, like.removed, member.joined, member.left, album.updated. Событие photo.created SHALL обозначать появление фото в альбоме, а готовность вариантов существующего фото SHALL публиковаться как photo.updated. #### Scenario: Новое фото - **WHEN** в альбоме появляется фото - **THEN** подписчики получают событие photo.created с метаданными и URL превью #### Scenario: Готовы варианты - **WHEN** для существующего фото сгенерированы thumb и preview - **THEN** подписчики получают событие photo.updated с этим фото ### Requirement: Персональный поток Клиент SHALL иметь возможность подписаться на персональный поток (приглашения, уведомления, изменения его альбомов). #### Scenario: Подписка на персональный поток - **WHEN** пользователь подключается к персональному потоку - **THEN** система отправляет ему персональные события #### Scenario: Изменение списка альбомов - **WHEN** альбом, доступный пользователю, создан, изменён или удалён - **THEN** пользователь получает соответствующее событие в персональном потоке ### Requirement: Авторизация и область доступа Клиент SHALL подписываться только на доступные ему альбомы; токен передаётся в query-параметре. #### Scenario: Подписка на недоступный альбом - **WHEN** пользователь подписывается на альбом, к которому нет доступа - **THEN** система отклоняет подключение ### Requirement: Устойчивость соединения Система SHALL отправлять heartbeat каждые 25 секунд и отключать медленных клиентов при переполнении буфера. #### Scenario: Переполнение буфера клиента - **WHEN** буфер клиента переполняется - **THEN** система отключает клиента ### Requirement: Догон пропущенного Клиент SHALL иметь возможность догнать пропущенные события через REST. #### Scenario: Переподключение - **WHEN** клиент переподключается после разрыва - **THEN** клиент догоняет события через запрос фото с параметром since ### Requirement: Различение добавления и обновления фото Клиент SHALL различать добавление нового фото в альбом и обновление уже показанного: обновление SHALL изменять фото на месте, не меняя порядок сетки. #### Scenario: Обновление отображаемого фото - **WHEN** приходит событие обновления для фото, которое уже показано - **THEN** клиент обновляет его на месте, порядок сетки не меняется #### Scenario: Обновление отсутствующего фото - **WHEN** приходит событие обновления для фото, которого нет в текущем списке - **THEN** клиент не добавляет его в сетку ### Requirement: События жизненного цикла альбома Система SHALL публиковать в персональные потоки события `album.created` и `album.updated` с объектом альбома и `album.deleted` с идентификатором альбома. #### Scenario: Создание альбома - **WHEN** пользователь создаёт альбом - **THEN** владелец получает событие `album.created` с объектом альбома #### Scenario: Изменение метаданных альбома - **WHEN** участник с правом редактирования меняет название, описание или обложку - **THEN** владелец и участники получают событие `album.updated` с актуальным объектом #### Scenario: Удаление альбома - **WHEN** владелец удаляет альбом - **THEN** владелец и участники получают событие `album.deleted` с идентификатором ### Requirement: Получатели событий об альбоме Система SHALL доставлять события жизненного цикла альбома владельцу и всем участникам альбома, для которых он доступен. #### Scenario: Веер по участникам - **WHEN** альбом изменён - **THEN** событие получает владелец и каждый участник альбома, включая инициатора изменения #### Scenario: Принятие приглашения - **WHEN** пользователь принимает приглашение в альбом - **THEN** приглашённый получает событие о появлении альбома в своём списке, а владелец — обновлённый объект альбома с новым числом участников ### Requirement: Единая форма объекта альбома в событиях Объект альбома в событиях SHALL совпадать по форме с элементом ответа списка альбомов, включая URL обложки и счётчики, чтобы клиент применял событие без дополнительных запросов. #### Scenario: Объект события совпадает со списком - **WHEN** клиент получает `album.created` или `album.updated` - **THEN** объект содержит те же поля, что элемент ответа `GET /api/albums` ### Requirement: Применение события без повторного запроса Клиент SHALL обновлять локальный список альбомов из payload'а события: создавать или заменять альбом по полученному объекту и удалять его по идентификатору, не выполняя дополнительный запрос списка. #### Scenario: Обновление альбома из списка - **WHEN** приходит событие обновления для альбома, который уже есть в списке клиента - **THEN** клиент заменяет его на месте, не меняя порядок списка #### Scenario: Удаление альбома из списка - **WHEN** приходит событие удаления для альбома, который есть в списке клиента - **THEN** клиент убирает альбом из списка ### Requirement: Догон состояния альбомов при переподключении Клиент SHALL после восстановления персонального потока синхронизировать список альбомов через REST, чтобы отразить изменения, пропущенные во время разрыва соединения. #### Scenario: Переподключение персонального потока - **WHEN** персональный поток восстанавливается после разрыва - **THEN** клиент перезапрашивает список альбомов и приводит локальное состояние к серверному ### Requirement: Потеря доступа к альбому Клиент SHALL переставать отображать альбом, который удалён или к которому потерян доступ, при получении соответствующего персонального события, в том числе на открытой странице альбома. #### Scenario: Альбом удалён во время просмотра - **WHEN** владелец удаляет альбом, пока участник просматривает его страницу - **THEN** участник получает событие и страница перестаёт отображать удалённый альбом #### Scenario: Участник исключён из альбома - **WHEN** участника исключают из альбома - **THEN** исключённый получает событие, и альбом убирается из его списка ### Requirement: Обновление альбома при изменении состава фото Система SHALL публиковать `album.updated` с актуальным объектом альбома в персональные потоки владельца и участников, когда состав фото альбома меняется: загрузка, копирование, перемещение, восстановление, перевод в корзину и окончательное удаление. При загрузке система также SHALL публиковать `photo.created` в поток альбома, чтобы открытая страница добавляла новое фото. #### Scenario: Загрузка фото - **WHEN** пользователь загружает фото в альбом - **THEN** владелец и участники получают `album.updated` с увеличенным `photos_count` #### Scenario: Перемещение фото - **WHEN** фото перемещается из одного альбома в другой - **THEN** владелец и участники обоих альбомов получают `album.updated` с актуальными счётчиками #### Scenario: Фото в корзине - **WHEN** фото переводится в корзину или восстанавливается - **THEN** владелец и участники получают `album.updated` с обновлённым `photos_count` #### Scenario: Окончательное удаление фото - **WHEN** фото удаляется окончательно - **THEN** владелец и участники получают `album.updated` с уменьшенным `photos_count`