# AGENTS.md Инструкции для агентов, работающих в этом репозитории. Читай вместе с `README.md` (актуальный источник правды) и `project.md` (исходное ТЗ). ## Что это Self-hosted фотоальбом: Go-бэкенд с вшитой через `go:embed` статикой SvelteKit, один статический бинарник (`CGO_ENABLED=0`), без Docker. Плюс отдельный Android-клиент. По умолчанию (`DB_BACKEND=sqlite`, `STORAGE_BACKEND=fs`) работает полностью локально, без Postgres и S3. Postgres/Garage (`pg.localnet`, `s3.localnet`) — опциональные слои. ## Команды Все команды — из корня репозитория. Голый `make` только печатает список целей. ```bash make build # frontend -> backend/static -> go:embed -> bin/galo make frontend # npm run build (инкрементально), копирует статику в backend/static make backend # CGO_ENABLED=0 go build -tags nodynamic -o ../bin/galo make run # frontend + backend + запуск ./bin/galo make migrate # применяет только Postgres-миграции (cmd/migrate) make test # cd backend && go test ./... (только бэкенд!) make testdata # наполняет запущенный инстанс через scripts/seed.py make android # cd android && ./gradlew :app:assembleDebug make install-android-app # сборка + установка debug-APK на устройство ``` - `-tags nodynamic` обязателен: без него `gen2brain/webp` тянет динамический libwebp и бинарник перестаёт быть статическим. - Порядок сборки важен: `frontend` должен отработать до `backend`, иначе `go:embed all:static` вшивает только `backend/static/.gitkeep` и бинарник выходит без UI. - `make test` не запускает фронтенд-тесты; отдельных make-целей для них нет: `cd frontend && npm run test` (vitest, окружение `node`, файлы `src/**/*.test.ts`) и `cd frontend && npm run check` (svelte-check/typecheck). - Один Go-тест: `cd backend && go test ./internal/photos/ -run TestName -v`. - Интеграционные тесты Postgres-репозиториев пропускаются без `TEST_DATABASE_URL`: `TEST_DATABASE_URL='postgres://user:pass@pg.localnet:5432/galo?sslmode=disable' \ cd backend && go test ./internal/repo/ -v` - Тесты видео (`internal/e2e`) сами пропускаются, если нет `ffmpeg`/`ffprobe`. Тесты e2e репозиториев поднимают SQLite в `t.TempDir()` и внешних сервисов не требуют. ## Тесты на эмуляторе Instrumented-прогоны (`:app:connectedDebugAndroidTest`) и сам запуск AVD **могут зависнуть** — не дожидайся их молча. Пока идёт прогон, проверяй прогресс **примерно раз в минуту**: жив ли процесс, обновляются ли результаты, отвечает ли устройство: ```bash pgrep -fa "GradleWrapperMain|Gradle Test Executor" ls -la android/app/build/outputs/androidTest-results/connected/debug/ adb devices ``` Если прогресса нет дольше пары минут — снимай прогон (`kill`), а не жди дальше, и разбирайся по дампу потоков (`jstack ` JVM тестов). То же относится к обычным unit-тестам: они тоже идут в отдельной JVM, и её зависание видно только по тому, что результаты перестали обновляться. ## Архитектура - Go-модуль `galo` (`backend/go.mod`, Go 1.26). Точки входа: `backend/cmd/server`, `cmd/migrate`, `cmd/reconcile`, `cmd/backfill-exif`. - Доменные пакеты в `backend/internal/` (albums, auth, members, photos, social, notifications, sharelinks, storage, sse, settings, video). Каждый содержит сервис, HTTP-обработчики и `Repository`-интерфейс. - `internal/store/store.go` — единственное место, которое знает конкретные реализации. При добавлении метода в `Repository` нужно реализовать его в **обоих** вариантах: Postgres (`NewStore/NewPostgres`) и SQLite (`NewSQLiteStore/NewSQLite`). - `backend/internal/app` — маршруты/статика; `internal/repo/contract` — контрактные тесты, которые гоняют одинаковый набор проверок для SQLite и Postgres. - `backend/embed.go` вшивает `static` (фронтенд) и `migrations/**`. Паттерны эмбеда — `migrations/*.sql migrations/sqlite/*.sql`: новые файлы должны им соответствовать. - Права проверяются на уровне сервиса, не только в UI. Блобы наружу отдаются только через бэкенд: `GET /api/objects/` с HMAC-подписью, один маршрут для авторизованных и для share-ссылок. Ключи объектов непрозрачны: `objects/{object_id}/{role}`. - SSE (`/api/sse/...`) авторизуется через `?token=`, т.к. `EventSource` не шлёт заголовки. Для nginx критичен `proxy_buffering off`. - `android/` — самостоятельный Gradle-модуль, серверный и веб-код не трогает; использует уже существующий HTTP API. Тесты: `cd android && ./gradlew test`. ## Миграции (частый источник ошибок) Схема поддерживается в **двух диалектах** с независимой нумерацией: - Postgres: `backend/migrations/NNNNNN_*.{up,down}.sql` - SQLite: `backend/migrations/sqlite/NNNNNN_*.{up,down}.sql` Любое изменение схемы нужно добавить в оба каталога, номера версий не обязаны совпадать (сейчас они разошлись). Миграции вшиты в бинарник и применяются автоматически при старте сервера для выбранного бэкенда. `make migrate` применяет только Postgres. `cmd/reconcile` существует для сверки хранилища с журналом `objects` (сборка мусора журналом управляется и хранилище не сканирует). ## Конфигурация и данные - `.env` читается вручную в `cmd/*/main.go` из `.env`, затем `../.env` (без dotenv-библиотеки). - Секреты подписи JWT и URL объектов не задаются в env: генерируются при первом старте и лежат в слое метаданных (`app_settings`), поэтому переживают перезапуск и рестарт SQLite. - Локальные артефакты в корне (`galo.db*`, `/data/`, `bin/`, `backend/static/*`, `frontend/build/`) в git не попадают — не коммить их. ## Фронтенд - SvelteKit 2 + Svelte 5 (runes), `adapter-static` с `fallback: index.html`, Tailwind v4. - Тёмная/светлая/нейтральная темы через `data-theme` на `` и семантические CSS-токены. В компонентах — только семантические классы (`bg-bg-elevated`, `text-muted` и т.п.), **никаких `dark:` префиксов**. - Vite dev-сервер проксирует `/api` на `localhost:8080` (см. `vite.config.ts`). ## OpenSpec Спецификации — в `openspec/specs//spec.md`; завершённые изменения — в `openspec/changes/archive/`. Активных изменений сейчас нет. Workflow-команды и скиллы лежат в `.opencode/` и требуют CLI `openspec` (в этом окружении он может быть не установлен — проверь `command -v openspec`). При изменении поведения сначала обновляй/создавай спеку, а не только код. ## Конвенции репозитория - Ветка `develop`; коммиты — Conventional Commits с русским описанием (`feat(realtime): ...`, `test(android): ...`, `chore: ...`). Функциональные ветки вливаются в `develop` через merge-коммиты. - `project.md` — исходное ТЗ и местами устарел (например, называет S3 бэкендом по умолчанию и старую раскладку ключей `albums/...`). Ориентируйся на `README.md` и код.