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

281 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Описание релиза 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)`.