Files
ds-galo/Отчёты об изменениях/2026-10-21-описание-релиза.md
T
iu 723b19bbe5 docs: описание релиза Galo за 21 октября 2026
Описание изменений с main (84ce4d1) до develop (afe85f7): локальный
режим на SQLite и файловой системе, слой объектов, редактирование фото,
новый интерфейс рабочего пространства, видео, Android-клиент, профиль,
офлайн, живое обновление, исправления и инструкция по обновлению.
2026-10-21 21:15:59 +03:00

22 KiB
Raw Blame History

Описание релиза Galo — 21 октября 2026

База релиза: main (84ce4d1, 10 октября 2026) → develop (afe85f7, 21 октября 2026) Объём: 138 коммитов, 4 спецификации обновлены, 1 изменение реализовано


Коротко о главном

  1. Локальный режим без внешних сервисов. Слой метаданных переключается на встроенный SQLite, хранилище блобов — на локальную файловую систему. По умолчанию приложение работает полностью на одной машине: без Postgres и без S3.
  2. Редактирование фотографий в браузере. Вкладка «Редактирование» с инструментом «Свет»: экспозиция, контраст, света, тени, белые и чёрные, тональная кривая по каналам с живой гистограммой. Правки видны сразу, без ожидания сервера.
  3. Новый интерфейс рабочего пространства. Альбомы с обложками, тёмная, светлая и нейтральная темы, единая система векторных иконок, гало-эмблема и фавикон.
  4. Нативный Android-клиент. Отдельное приложение: просмотр альбомов, загрузка с устройства, двусторонняя синхронизация с сервером, безопасная разгрузка превью.
  5. Видео и офлайн. Видео в общих альбомах с фоновой подготовкой и перемоткой; офлайн-кеш оболочки, списков и превью.

Новые возможности

Локальный режим: SQLite и файловая система

  • Слой метаданных выбирается конфигурацией: внешний PostgreSQL или встроенный SQLite с тем же поведением для одного экземпляра приложения.
  • Хранилище блобов выбирается отдельно: S3-совместимое (Garage) или локальная файловая система. Клиент никогда не видит хранилище — блобы всегда отдаются через бэкенд по подписанному URL.
  • По умолчанию (DB_BACKEND=sqlite, STORAGE_BACKEND=fs) приложение поднимается без Postgres и S3: данные лежат в galo.db и каталоге data/.
  • Секрет подписи JWT и URL объектов генерируются при первом старте и хранятся в слое метаданных — переживают перезапуск и рестарт SQLite.
  • Реконсиляция хранилища с журналом объектов: make reconcile (отчёт), make reconcile-delete (удаление осиротевших блобов). Сборку мусора ведёт журнал, хранилище не сканируется.

Слой объектов

  • Физическое объектное хранилище с непрозрачными ключами: идентификаторы альбомов и фото в ключах не участвуют, сопоставление живёт только в метаданных.
  • Журнал объектов: у каждого объекта ровно одна запись с ключом, MIME-типом, размером, габаритами и состоянием; фото ссылается на объекты по ролям (original, thumb, medium, preview, playback, edit, edit_thumb, edit_medium, edit_preview).
  • Один физический объект может разделяться несколькими фото: перенос и копирование меняют только ссылки, хранилище не трогается.
  • Сборка мусора по ссылкам: объект удаляется, когда на него не ссылается ни одно фото и ни один аватар.

Редактирование фотографий

  • Вкладка «Редактирование» в правой панели фото, инструмент «Свет»: экспозиция, контраст, света, тени, белые, чёрные.
  • Тональная кривая: выбор канала (R/G/B/мастер), точки добавляются кликом по полю графика, перетаскиваются и двигаются стрелками с клавиатуры.
  • Гистограмма каналов пересчитывается на лету с учётом текущих правок.
  • Блок «Кривая» сворачивается (по умолчанию свёрнут), кнопки выбора канала остаются доступными.
  • Предпросмотр правок рисуется в браузере — WASM-модулем на Rust, с резервной JS-реализацией, если модуль недоступен или считает иначе.
  • Правки применяются сразу; на сервер уходят через 10 секунд без изменений или при переключении фото. Сервер рендерит отредактированную версию в фоне отдельной очередью и сообщает о готовности.
  • Переключатель «Сброс» удаляет и параметры правок, и отредактированную версию.
  • Редактирование неразрушающее: оригинал остаётся неизменным, скачивание отдаёт исходник. Рендер идёт по исходному объекту, а не по уменьшенной копии.

Интерфейс рабочего пространства

  • Альбомы с обложками, «лайтрумовская» компоновка: рельс альбомов, сетка, единая панель фотографии.
  • Три темы — тёмная, светлая, нейтральная — с семантическими токенами; переключатель тем переехал в шапку, нейтральная тема затемнена.
  • Единая система иконок: встроенная векторная графика (Material Symbols) вместо эмодзи и символов системного шрифта — иконки наследуют цвет темы и не тянут внешние ресурсы.
  • Гало-эмблема (диск солнца в отделённом кольце) и фирменный фавикон: отдаются самим приложением, наследуют тему, читаются в размере 16–24 px.
  • Панель сведений о фото со сворачиваемыми секциями и настраиваемым набором атрибутов: состав EXIF хранится в профиле пользователя.
  • Полноэкранный просмотр: своя панель, кнопка «на весь экран», возврат по Esc.

Просмотр и навигация

  • Зум колесом мыши (с привязкой к курсору, до 200%), клавишами + и -; Space возвращает во вписанный вид, а из вписанного — в 100% от оригинала по центру кадра.
  • В зуме стрелки клавиатуры панорамируют фото, переключение соседних снимков остаётся за мышью и кнопками у краёв.
  • Скрытие боковых панелей альбома: кнопка в панели сетки и хоткей Tab. Панели уезжают за края, содержимое занимает всю ширину, панели возвращаются при наведении на кромку окна. Размер миниатюр при этом сохраняется — в строке помещается больше фото.
  • Лента миниатюр в просмотре следует за текущим фото; на превью работает наведение- зум.
  • Полноэкранный режим приложения по Ctrl+F, навигация с клавиатуры в сетке с возвратом фокуса, открытие фото тапом на тач-экранах.
  • Просмотр 360-панорам по тегу pano360, корректное направление перетаскивания.
  • Пока грузится оригинал для зума — спиннер; на Android — щипок-зум.

Работа с фотографиями

  • Мультивыбор в сетке, контекстные меню, перенос и копирование снимков, корзина на уровне альбома и общие счётчики.
  • Теги фото: правки расходятся по сетке и просмотру.
  • Переименование фото и удаление из просмотра с подтверждением.
  • EXIF извлекается полностью при загрузке; для старых снимков — дозаполнение (cmd/backfill-exif).

Офлайн-режим

  • Service worker кеширует оболочку приложения, списки и эскизы/превью фотографий: открытые альбомы и ранее загруженные снимки доступны без сети.
  • Модуль фильтров в офлайне не кешируется — он всегда подтягивается свежим.

Видео

  • Загрузка видео в те же альбомы, что и фото (kind=video).
  • Фоновая подготовка: постер (кадр кадра → обычные варианты сетки) и web-совместимый вариант для воспроизведения (ремукс без перекодирования либо транскод в h264/yuv420p/aac с +faststart), с поддержкой перемотки (Range).
  • Транскод идёт в отдельном малом пуле и не вытесняет генерацию вариантов изображений; готовность транслируется подписчикам альбома.
  • Без ffmpeg/ffprobe приём видео отклоняется, /api/health сообщает "video": "unavailable", изображения работают как прежде.

Android-клиент

  • Отдельный Gradle-модуль, работает с существующим HTTP API, сервер и веб не трогает. Kotlin + Jetpack Compose, minSdk 28, targetSdk 35.
  • Двусторонняя синхронизация альбома устройства с серверным альбомом: загрузки, изменения состава, удаления.
  • Безопасная разгрузка: с устройства уходит превью, оригинал остаётся на сервере; потеря серверной копии распознаётся и показывается в интерфейсе.
  • Статус синхронизации на карточке альбома, ход восстановления оригиналов, сортировка списка, обновление списка альбомов в реальном времени.
  • Установка на устройство одной командой: make install-android-app (с выбором серийного номера при нескольких подключённых устройствах).

Профиль и учётная запись

  • Профиль пользователя: круглый аватар с загрузкой и кадрированием, публичная отдача аватара по версионированной ссылке.
  • Вход и регистрация по телефону (E.164) в дополнение к email, управление идентификаторами, удаление аккаунта.
  • Предпочтения пользователя (в том числе состав панели сведений) хранятся на сервере.

Альбомы и живое обновление

  • Обложки альбомов: отдельный жизненный цикл, обложка доводится до готовности и доставляется подписчикам.
  • Живое обновление: события жизненного цикла альбома и изменения состава фото через SSE, подписчики видят изменения без перезагрузки.
  • Квадратный вариант medium — резкие миниатюры в сетке; шаг переключения размера миниатюр и направление ползунка приведены в порядок.
  • Быстрая загрузка больших альбомов.

Наблюдаемость и эксплуатация

  • Настраиваемое логирование: LOG_LEVEL (debug включает access-логи и ожидаемые 4xx), LOG_FORMAT (json/text), request id в ответах и логах.
  • scripts/seed.py и make testdata — наполнение запущенного инстанса тестовыми данными через публичный API.
  • Release-сборка: make build-release — статичный бинарник с вшитой версией и UPX-сжатием, если он установлен.

Исправления

  • Копия фото больше не наследует отредактированную версию. Раньше копирование переносило все привязки объектов, включая edit*: копия показывала отредактированную картинку, но не наследовала параметры правок, и первая же новая правка перезаписывала обработку с нуля. Теперь правка принадлежит своему фото, а копия показывает исходник. Разовая миграция убирает привязки у копий, созданных до фикса.
  • Предпросмотр правок работает и без зума, а не только в увеличенном виде.
  • Устранено искажение цветов в предпросмотре (WASM-фильтры считались по преобразованным каналам).
  • Гистограмма пересчитывается при правке, а не остаётся от исходного снимка.
  • Линия тональной кривой видима в тёмной теме.
  • Починен зум в просмотре: контейнер прокрутки и центрирование.
  • Android: разгруженное превью больше не считается новой версией элемента.
  • Сервер отдаёт контрольную сумму оригинала в ответе на загрузку — клиент может сверить файл.
  • Счётчик корзины и правки тегов синхронизируются с сеткой и просмотром.
  • Ошибка загрузки крупного файла показывается явно, лимит поднят.
  • make run пересобирает фронтенд, если он изменился.

Технические изменения

API

Новые эндпоинты:

  • PATCH /api/photos/:id/edit — сохранить параметры правок (тело: {"light": {...}}).
  • DELETE /api/photos/:id/edit — сбросить правки вместе с отредактированной версией.
  • GET|PATCH|DELETE /api/auth/me, POST /api/auth/me/password, POST /api/auth/me/identifiers, PATCH /api/auth/me/preferences, POST|DELETE /api/auth/me/avatar, GET /api/avatars/:userID/:version?size=large.
  • POST /api/photo-batch/copy, POST /api/photo-batch/move — пакетные операции над снимками.

Изменившиеся ответы: у фото появились поля edit_params, edit_url, edit_thumb_url, edit_medium_url, edit_preview_url; у видео — playback_url.

Схема данных и миграции

  • Postgres: миграции до 000016; SQLite: до 000008 (нумерация диалектов независимая, миграции вшиты в бинарник и применяются автоматически при старте; make migrate применяет только Postgres).
  • Новые роли объектов edit, edit_thumb, edit_medium, edit_preview — рядом с обычными original, thumb, medium, preview, playback.
  • Новая колонка photos.edit_params — параметры неразрушающих правок.

Сборка

  • Фильтры правок считает WASM-модуль из frontend/wasm/light (Rust, no_std). Пересборка нужна только при правке самого модуля; в репозитории лежит готовый light.wasm, поэтому фронтенд собирается без Rust-инструментов.
  • npm run wasm — отдельная пересборка модуля; обычная сборка фронтенда обновляет его сама.

Как обновиться

  1. Собрать бинарник: make build (фронтенд → backend/static → go:embed → bin/galo) или make build-release — с вшитой версией.
  2. Остановить сервис, заменить бинарник. Фронтенк вшит в бинарник, отдельно выкладывать нечего.
  3. Запустить: миграции применятся автоматически при старте для выбранного бэкенда. Для Postgres можно отдельно прогнать make migrate.
  4. Для видео нужен ffmpeg/ffprobe на хосте; без них приём видео отключается.
  5. Android-клиент ставится отдельно: make install-android-app.

Откат: бинарник возвращается предыдущей версии; миграции down не восстанавливают удалённые привязки копий — это безопасно, отредактированные версии исходных фото остаются на месте.


Ограничения

  • Редактирование — только для изображений; видео не редактируется.
  • Правки не порождают отдельной записи в истории: параметры хранятся в метаданных фото, отредактированная версия — как отдельный объект.
  • Офлайн-кеш ограничен оболочкой, списками и эскизами/превью: оригиналы для зума и скачивания требуют сети.
  • Android-клиент требует JDK 17 и Android SDK (platforms;android-35, build-tools;35.0.0).
  • Номер версии берётся из git describe --tags; в репозитории пока нет тегов, поэтому версия в сборке — короткий SHA или dev. Для осмысленной нумерации нужен тег.

Для разработчиков

  • Спецификации OpenSpec синхронизированы с поведением: обновлены ui-workspace (редактирование, кривая, зум и навигация), media-objects (роли edit*, раздельное владение правками, отдельная очередь рендера), photos (рендер по оригиналу), dev-make-workflow (сборка WASM-модуля).
  • Изменение fix-copy-edit-variant-sharing реализовано (9/9 задач) и ждёт ревью и архивации: openspec list показывает его как complete.
  • Контрактные тесты репозиториев (internal/repo/contract) гоняют один набор проверок на SQLite и Postgres — новый кейс Clone проверяет, что копия не наследует отредактированную версию, в обоих хранилищах.
  • Интеграционные тесты Postgres требуют TEST_DATABASE_URL; без них они пропускаются. Тесты e2e поднимают SQLite в t.TempDir() и внешних сервисов не требуют.
  • Сервис переименован в galo, бренд — «гало»; сборка make build-release пишет bin/galo-$(VERSION).