# user-profile Specification ## Purpose Управление аккаунтом пользователя: просмотр и изменение профиля, смена пароля с отзывом сессий, добавление второго идентификатора входа и удаление аккаунта со всем содержимым — единообразно в веб-интерфейсе и Android-клиенте. ## Requirements ### Requirement: Просмотр собственного профиля Пользователь SHALL получать свой профиль с именем, email, телефоном и аватаром. Телефон SHALL быть виден только владельцу и SHALL NOT попадать в публичные представления пользователя (участники альбома, загрузчики, комментарии). #### Scenario: Получение профиля - **WHEN** аутентифицированный пользователь запрашивает свой профиль - **THEN** ответ содержит его имя, email (если задан), телефон (если задан) и аватар #### Scenario: Телефон не утекает - **WHEN** пользователь просматривает участников альбома - **THEN** представления других пользователей не содержат телефон ### Requirement: Изменение имени Пользователь SHALL изменять своё имя. Пустое имя SHALL отклоняться. Смена имени SHALL применяться немедленно и отражаться при следующем чтении профиля. #### Scenario: Успешное изменение - **WHEN** пользователь отправляет непустое имя - **THEN** система сохраняет его и возвращает обновлённый профиль #### Scenario: Пустое имя - **WHEN** пользователь отправляет пустое имя - **THEN** система отклоняет запрос и не меняет профиль ### Requirement: Смена пароля с отзывом сессий Пользователь SHALL менять пароль, подтвердив текущий пароль. Система SHALL отклонять неверный текущий пароль и новый пароль короче 8 символов. Успешная смена SHALL отзывать все refresh-токены пользователя на всех устройствах. #### Scenario: Успешная смена - **WHEN** пользователь вводит верный текущий пароль и новый пароль допустимой длины - **THEN** система сохраняет новый хэш и отзывает все refresh-токены пользователя #### Scenario: Неверный текущий пароль - **WHEN** пользователь вводит неверный текущий пароль - **THEN** система возвращает ошибку и не меняет пароль #### Scenario: Слабый новый пароль - **WHEN** новый пароль короче 8 символов - **THEN** система отклоняет запрос и сохраняет старый пароль ### Requirement: Добавление недостающего идентификатора Пользователь с одним заданным идентификатором (email или телефон) SHALL добавить недостающий один раз после проверки уникальности. Система SHALL отклонять добавление, когда оба идентификатора уже заданы, а также изменение или удаление заданного. После добавления оба идентификатора SHALL работать для входа. #### Scenario: Добавление телефона - **WHEN** пользователь без телефона добавляет свободный номер в формате E.164 - **THEN** система сохраняет номер, и вход по нему становится возможен #### Scenario: Занятый идентификатор - **WHEN** добавляемый email или телефон уже принадлежит другому пользователю - **THEN** система возвращает ошибку конфликта и не меняет профиль #### Scenario: Оба уже заданы - **WHEN** у пользователя уже заданы и email, и телефон, и он пытается добавить третий - **THEN** система отклоняет запрос ### Requirement: Удаление аккаунта со всем содержимым Пользователь SHALL удалять свой аккаунт, подтвердив текущий пароль. Неверный пароль SHALL отклоняться. Успешное удаление SHALL быть немедленным и необратимым: SHALL каскадно удаляться альбомы пользователя (вместе с доступом других участников), его фото, комментарии, лайки, метки, уведомления, share-ссылки и приглашения. Блобы SHALL освобождаться фоновой сверкой хранилища, а не в транзакции удаления. #### Scenario: Успешное удаление - **WHEN** пользователь подтверждает удаление верным текущим паролем - **THEN** учётная запись и связанное содержимое удаляются, и вход больше невозможен #### Scenario: Общие альбомы - **WHEN** удаляемый пользователь владеет альбомом, в котором есть другие участники - **THEN** альбом удаляется вместе с ними #### Scenario: Неверный пароль - **WHEN** пользователь подтверждает удаление неверным паролем - **THEN** система возвращает ошибку и не удаляет аккаунт ### Requirement: Страница профиля в веб-интерфейсе Веб-приложение SHALL предоставлять отдельную страницу профиля, доступную из шапки, с показом идентификаторов и формами изменения имени, смены пароля и удаления аккаунта. #### Scenario: Открытие страницы - **WHEN** аутентифицированный пользователь открывает профиль из шапки - **THEN** отображаются его идентификаторы и формы редактирования #### Scenario: Выход после смены пароля - **WHEN** смена пароля завершилась успешно - **THEN** клиент очищает токены и перенаправляет на страницу входа #### Scenario: Выход после удаления - **WHEN** удаление аккаунта завершилось успешно - **THEN** клиент очищает токены и перенаправляет на страницу входа ### Requirement: Экран профиля в Android-клиенте Android-приложение SHALL предоставлять отдельный экран профиля, доступный из списка альбомов, с теми же возможностями, что и веб-страница. #### Scenario: Открытие экрана - **WHEN** пользователь открывает профиль из списка альбомов - **THEN** отображаются идентификаторы и формы редактирования #### Scenario: Возврат после смены пароля - **WHEN** смена пароля завершилась успешно - **THEN** приложение очищает токены и возвращает на экран входа #### Scenario: Возврат после удаления - **WHEN** удаление аккаунта завершилось успешно - **THEN** приложение очищает токены и возвращает на экран входа ### Requirement: Загрузка фото профиля с выбором области Пользователь SHALL загружать собственное фото в профиль, выбирая видимую квадратную область. Сохранённый аватар SHALL соответствовать выбранной области. Загрузка SHALL быть доступна только владельцу профиля. #### Scenario: Успешная загрузка - **WHEN** пользователь выбирает изображение и подтверждает квадратную область - **THEN** аватар обновляется и возвращается в профиле #### Scenario: Выбранная область учтена - **WHEN** пользователь сдвигает или масштабирует область перед подтверждением - **THEN** сохранённый аватар показывает именно выбранную область #### Scenario: Не изображение - **WHEN** загруженный файл не является декодируемым изображением - **THEN** система отклоняет запрос и не меняет аватар ### Requirement: Нормализация аватара и снятие метаданных Система SHALL сохранять аватар в виде квадратных изображений фиксированных размеров и SHALL NOT сохранять EXIF-метаданные загруженного файла. #### Scenario: Метаданные не сохраняются - **WHEN** загружено изображение с EXIF, включая GPS-координаты - **THEN** сохранённый аватар не содержит EXIF и GPS #### Scenario: Слишком большой файл - **WHEN** размер загруженного файла превышает допустимый предел - **THEN** система отклоняет запрос и не меняет аватар ### Requirement: Отдача аватара по стабильному URL Профиль и публичные представления пользователя SHALL содержать URL аватара, когда он задан. URL SHALL быть стабильным и SHALL изменяться при обновлении аватара, чтобы клиентские кэши инвалидировались. Изображение SHALL отдаваться по публичной ссылке без аутентификации, чтобы его могли загружать теги изображений и клиентские загрузчики. #### Scenario: Ссылка в представлениях пользователя - **WHEN** аутентифицированный клиент запрашивает профиль, участников альбома, комментарии или автора фото - **THEN** представления содержат URL аватара, если он задан #### Scenario: Кэш инвалидируется при замене - **WHEN** пользователь заменяет аватар - **THEN** URL аватара изменяется #### Scenario: Загрузка изображения без заголовка авторизации - **WHEN** браузер или клиентский загрузчик изображений запрашивает изображение аватара по URL без заголовка авторизации - **THEN** система отдаёт изображение ### Requirement: Круглое отображение аватара в веб-интерфейсе Веб-приложение SHALL отображать аватар пользователя круглым в шапке, на странице профиля, у участников альбома, в комментариях и у автора фото. #### Scenario: Аватар в шапке - **WHEN** аутентифицированный пользователь открывает любое место приложения - **THEN** в шапке отображается его круглый аватар #### Scenario: Аватар рядом с другими пользователями - **WHEN** пользователь просматривает участников, комментарии или автора фото - **THEN** аватары этих пользователей отображаются круглыми ### Requirement: Круглое отображение аватара в Android-клиенте Android-приложение SHALL отображать аватар пользователя круглым на экране профиля и в шапке списка альбомов. #### Scenario: Аватар на экране профиля - **WHEN** пользователь открывает экран профиля - **THEN** отображается его круглый аватар #### Scenario: Аватар в шапке списка альбомов - **WHEN** пользователь открывает список альбомов - **THEN** в шапке отображается его круглый аватар ### Requirement: Заглушка при отсутствии аватара Когда аватар не задан, система SHALL показывать нейтральную заглушку-силуэт вместо изображения. #### Scenario: Аватар не задан - **WHEN** у пользователя нет аватара и его представление отображается в вебе или Android - **THEN** вместо изображения показывается нейтральная круглая заглушка-силуэт ### Requirement: Удаление аватара Пользователь SHALL удалять свой аватар; после удаления SHALL показываться заглушка, а связанные объекты хранилища SHALL освобождаться. #### Scenario: Сброс аватара - **WHEN** пользователь удаляет аватар - **THEN** представления возвращаются к заглушке, а объекты старого аватара освобождаются