Новый алгоритм редактирования: обработка идёт только на клиенте.
- Фильтры «Свет» считает WASM-модуль на Rust (frontend/wasm/light, 1.5 КБ):
он строит тональные кривые и обрабатывает пиксели. При недоступности модуля
используется JS-реализация с тем же результатом — расхождение проверяется
тестом.
- Просмотр рисует видимую область фото с правками на canvas поверх
исходной версии, поэтому изменения видны сразу, без ожидания рендера.
- Правки уходят на сервер не сразу: через десять секунд без изменений или при
переключении фото. Сервер сохраняет параметры и готовит варианты сетки и
полноразмерную версию в фоне, о готовности сообщает через SSE — клиент
подтягивает готовую версию.
Добавлен service worker: оболочка приложения, ответы API и варианты
изображений (эскиз, превью, средний) сохраняются в офлайн-кеше браузера.
Ключ кеша картинки — путь объекта без подписи, поэтому обновление подписи
не плодит записи; оригиналы и видео не кешируются. Кеш ограничен по размеру
и вытесняет старые записи, при выходе из аккаунта очищается, а в верхней
панели появляется индикатор «Офлайн».
Добавлен манифест PWA; манифест отдаётся с типом application/manifest+json.
Цель run зависела только от backend, а статика фронтенда попадает в бинарник
через go:embed из backend/static, который обновляла лишь цель frontend. Поэтому
make run пересобирал Go-код со старым UI.
- run теперь зависит от frontend backend;
- frontend стала инкрементальной: npm ci только при изменении package.json/
package-lock.json, npm run build и копирование в backend/static — только когда
исходники или конфиги новее, чем backend/static/index.html (он же stamp);
- проверки вынесены в shell-recipe, т.к. имена SvelteKit-роутов вида (app)
ломают разбор имён файлов в make.
Build signed object URLs as path-only (/api/objects/...) instead of
prefixing APP_BASE_URL, so media loads from whatever host or reverse
proxy served the app. Remove the now-unused base URL from the signer and
drop the APP_BASE_URL / Server.BaseURL config.
Add video as a media kind in photos: streaming upload with per-kind
limits, ffprobe metadata, poster frame reusing the image variant
pipeline, and a playback object (remux when web-compatible, else
h264/yuv420p/aac transcode). Video jobs run on a separate pool with a
long lease. Frontend gains a video viewer, grid badge/duration and
video upload.
- rename Go module, binary, systemd unit, PG_DATABASE default, frontend package and docs from shared-album to galo
- add theme-aware halo emblem and favicon (new ui-branding capability)
- add make build-release (versioned, stripped, static binary) and run the binary from the repo root
- app_settings table (PostgreSQL migration 000010, SQLite 000002)
- internal/settings repository (Get/Set/Ensure) wired into store.Repos
- secret generated with crypto/rand on first start and persisted; reused on restart
- drop required JWT_SECRET from env/config; app.New takes a TokenManager and
storage.Open takes the resolved secret (URL_SECRET still overrides)
- docs updated
- introduce storage.Backend with S3 and local file-system implementations
- deliver all blobs via signed GET/HEAD /api/objects/<key> (Range, Content-Type)
- stream S3 objects through the backend; remove presigned URL path
- share shared HMAC Signer between backends
- remove S3_PUBLIC_URL; rename S3_PRESIGN_TTL to OBJECT_URL_TTL
- make storage label in health dynamic; nginx proxy_buffering off for objects
Backend (Go/Gin/pgx): env config, pgx pool with retries, Garage S3 client
(path-style, region garage), embedded golang-migrate migrations, JWT auth with
refresh rotation, albums CRUD, members/roles/invites with no-escalation matrix,
photo upload + async imaging/goexif WebP variants, cursor pagination, presigned
URLs, likes/comments, SSE hub (album + personal), notifications, share links.
Frontend (SvelteKit 2 + Svelte 5 + Tailwind v4): API wrapper with 401 refresh,
auth store, all screens, Uploader/PhotoGrid/Lightbox/Comments, SSE client with
reconnect + REST catch-up, three themes + system via data-theme.
Deploy without Docker: Makefile builds the frontend into backend/static and
embeds it (go:embed) into a single statically linked binary; systemd unit and
nginx SSE example included.
Use gen2brain/webp (-tags nodynamic) instead of cgo kolesa-team/go-webp to keep
CGO_ENABLED=0 static builds; design.md/proposal.md updated accordingly.