Files
ds-galo/openspec/changes/add-user-avatar/proposal.md
T
iu 04617d1cdb feat(profile): add round avatar with upload and cropping
- backend: user_avatar_objects binding (PG+SQLite), GC preserves avatar blobs,
  avatar service (EXIF-stripping WebP 128/512), public avatar read route
- web: avatar upload/crop on profile, round avatar in header/members/comments/author
- android: avatar upload/crop, round avatar on profile and albums header
- openspec: add-user-avatar proposal/design/specs/tasks
2026-10-12 16:16:47 +03:00

4.7 KiB

Proposal

Why

Аватар профиля задуман, но не реализован: колонка users.avatar_url, поля PublicUser.AvatarURL/SelfUser.AvatarURL и avatarUrl в клиентах существуют, однако никто их не пишет, изображение никто не отдаёт, а интерфейс везде показывает только имена. Пользователи не могут выделить себя визуально. Нужно замкнуть эту заготовку в рабочую круглую фотографию профиля с загрузкой и кадрированием на обеих платформах.

What Changes

  • Загрузка и удаление аватара: POST /api/auth/me/avatar (клиент присылает уже готовый квадрат после кадрирования) и DELETE /api/auth/me/avatar (сброс к дефолту).
  • Серверная нормализация загруженного изображения: отклонение не-изображений, снятие EXIF/GPS повторным энкодом, две квадратные WebP-нарезки — avatar_small (128) и avatar (512).
  • Аватар хранится как объект журнала, привязанный к пользователю; сборка мусора учитывает эту привязку, поэтому замена и сброс аватара сами освобождают старый блоб.
  • Стабильная отдача под авторизацией: GET /api/avatars/:userID/:version (версия в пути, чтобы клиентские и браузерные кэши инвалидировались при замене), ?size=large отдаёт крупную нарезку.
  • users.avatar_url заполняется стабильной ссылкой, поэтому существующие публичные представления пользователя (участники альбома, комментарии, авторы) начинают отдавать аватар без изменения своих запросов.
  • Веб-интерфейс: круглый аватар и загрузка с кадрированием на странице профиля; круглый аватар в шапке, у участников, в комментариях и у автора фото.
  • Android-клиент: круглый аватар и загрузка с кадрированием на экране профиля; круглый аватар в шапке списка альбомов.
  • Дефолт при отсутствии аватара — нейтральная иконка-силуэт; в веб-спрайт добавляется иконка person.

Capabilities

New Capabilities

Modified Capabilities

  • user-profile: добавляются требования к аватару — загрузка с кадрированием на клиенте, круглое отображение в вебе (шапка, страница профиля, участники, комментарии, автор) и в Android (экран профиля, шапка списка альбомов), дефолтная заглушка и удаление аватара.
  • media-objects: пользовательский аватар становится объектом журнала, привязанным к пользователю, и учитывается сборкой мусора наряду с привязками к фото.

Impact

  • Backend: internal/auth (хендлеры/сервис/сторы), новая привязка объектов аватара и её учёт в сборке мусора (internal/photos/objects.go, sqlite_objects.go или выделенный слой), маршрутизация и отдача (internal/app), миграции Postgres и SQLite, журнал объектов.
  • Frontend: lib/api.ts, lib/stores/auth.svelte.ts, lib/icons/registry.ts, страница routes/(app)/profile, AppShell.svelte, Comments.svelte, PhotoPanel.svelte, экран участников.
  • Android: ProfileScreen/ProfileViewModel/ProfileApi/GaloApi, AlbumsScreen, экран(ы) кадрирования; UserDto.avatarUrl уже есть.
  • API: новые маршруты /auth/me/avatar и /avatars/:userID/:version.
  • Спеки: user-profile, media-objects.