diff --git a/BalanceServer/AGENTS.md b/BalanceServer/AGENTS.md new file mode 100644 index 0000000..3c9054b --- /dev/null +++ b/BalanceServer/AGENTS.md @@ -0,0 +1,125 @@ +# AGENTS.md + +## Назначение + +**BalanceServer** — серверная часть приложения Balance, используемая iOS/macOS/watchOS и Android-клиентами. + +Этот файл является operational guide для AI-агентов и разработчиков. При изменениях сначала изучай существующий код и сохраняй текущий API-контракт клиентов. + +## Быстрая карта проекта + +Проект написан на Go 1.25.0. + +Основные Go-файлы: + +```text +- internal/database/database.go +- internal/config/config.go +- internal/auth/tokens.go +- internal/auth/password.go +- internal/httpapi/auth_handlers.go +- internal/httpapi/server.go +- internal/httpapi/sync_handler.go +- internal/httpapi/web.go +- internal/httpapi/server_test.go +``` + +Количество Go-файлов: **9**. + +Обнаруженные технологии/интеграции: SQLite, Docker. + +## Правила изменений + +### 1. Не ломать клиентский контракт + +Сервер обслуживает несколько клиентов: +- Apple Balance; +- Android Balance. + +Изменение JSON-полей, HTTP methods, endpoint paths, auth semantics или sync semantics считается breaking change, даже если сервер компилируется. + +Перед изменением API ищи использование endpoint/field в server code и документации клиентов. + +### 2. Sync — центральный контракт + +Синхронизация должна оставаться: +- детерминированной; +- идемпотентной при повторной доставке; +- устойчивой к повторным запросам; +- корректной при нескольких устройствах одного пользователя. + +Не меняй правила cursor/sequence/update timestamps без обновления `docs/SYNC.md`. + +### 3. Аутентификация + +Никогда не: +- логируй пароль; +- логируй access/refresh token; +- возвращай секреты в ошибках; +- храни plaintext passwords. + +При изменении auth обязательно проверь: +- регистрацию; +- login; +- refresh; +- logout; +- истечение токенов; +- авторизацию sync endpoint. + +### 4. База данных + +Любое изменение schema должно иметь: +- migration strategy; +- обратную совместимость там, где её требуют старые клиенты; +- проверку индексов для user/entity/id/sequence/update timestamp. + +Не удаляй существующие поля только потому, что текущий клиент ими не пользуется. + +### 5. Конкурентность + +Go server должен быть безопасен при одновременных запросах от нескольких устройств. + +Особое внимание: +- генерации sequence; +- выдаче cursor; +- записи изменений; +- refresh-token rotation; +- race между push и pull. + +### 6. Ошибки + +HTTP API должен возвращать стабильную структуру ошибок. Не отдавай stack traces, SQL errors или внутренние пути клиенту. + +Внутренние детали логируй только в безопасном виде. + +### 7. Тесты + +Перед PR: + +```bash +go test ./... +go vet ./... +go build ./... +``` + +Если проект использует дополнительные проверки/линтеры, запускай их по Makefile/CI. + +### 8. Документация + +При изменении: +- endpoint → обновить `docs/API.md`; +- sync → `docs/SYNC.md`; +- DB model → `docs/DATA_MODEL.md`; +- deploy/config → `docs/DEPLOYMENT.md`; +- архитектуры → `docs/ARCHITECTURE.md`. + +## Checklist + +- [ ] Не изменён API случайно. +- [ ] Auth secrets не попадают в logs/errors. +- [ ] Sync остаётся идемпотентным. +- [ ] DB migration учтена. +- [ ] Concurrent requests безопасны. +- [ ] `go test ./...` проходит. +- [ ] `go vet ./...` проходит. +- [ ] Документация обновлена. diff --git a/BalanceServer/README.md b/BalanceServer/README.md index b8282d9..271e0c7 100644 --- a/BalanceServer/README.md +++ b/BalanceServer/README.md @@ -110,3 +110,17 @@ The sync endpoint accepts transactions, custom categories and monthly budgets. T - Back up the SQLite database and its `-wal`/`-shm` files together, or stop the server before copying the database. - Changing `BALANCE_JWT_SECRET` invalidates access tokens. Existing refresh sessions remain valid and will receive new access tokens after refresh. - The in-process authentication limiter allows 30 authentication requests per IP per minute. Use a reverse proxy for stronger public rate limiting and request logging. + +## Документация проекта + +- [`AGENTS.md`](AGENTS.md) — правила для AI-агентов и разработчиков. +- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — архитектура backend. +- [`docs/DATA_MODEL.md`](docs/DATA_MODEL.md) — модель данных. +- [`docs/API.md`](docs/API.md) — HTTP API. +- [`docs/SYNC.md`](docs/SYNC.md) — протокол синхронизации. +- [`docs/SECURITY.md`](docs/SECURITY.md) — требования безопасности. +- [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) — локальная разработка и проверки. +- [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md) — deployment/production. +- [`docs/PRODUCT.md`](docs/PRODUCT.md) — функциональная роль сервера. + +BalanceServer является общим backend для Apple- и Android-клиентов Balance. diff --git a/BalanceServer/docs/API.md b/BalanceServer/docs/API.md new file mode 100644 index 0000000..d32bddc --- /dev/null +++ b/BalanceServer/docs/API.md @@ -0,0 +1,151 @@ +# HTTP API + +## Общие правила + +API предназначен для iOS/macOS/watchOS и Android Balance. + +Базовый URL задаётся клиентом и зависит от deployment environment. + +JSON: + +```http +Content-Type: application/json +``` + +Защищённые endpoints используют: + +```http +Authorization: Bearer +``` + +## Authentication + +Основные операции: + +```text +POST /v1/auth/register +POST /v1/auth/login +POST /v1/auth/logout +POST /v1/auth/refresh +``` + +### Register + +Создаёт пользовательский аккаунт. + +Input должен валидироваться сервером. Password никогда не хранится plaintext. + +### Login + +Проверяет credentials и создаёт authenticated session. + +### Refresh + +Принимает refresh credential и выдаёт новый access credential согласно текущей server-side session policy. + +### Logout + +Инвалидирует текущую session/refresh state согласно текущей реализации. + +## Synchronization + +Основной endpoint: + +```text +POST /v1/sync +``` + +Логический request: + +```json +{ + "cursor": 123, + "deviceId": "UUID", + "changes": [], + "pull": true +} +``` + +Точная JSON schema должна оставаться совместимой с `ServerSync.swift` и Android `SyncRepository`. + +## Change + +Логическая форма: + +```json +{ + "entity": "transaction", + "id": "UUID", + "deleted": false, + "updatedAt": "2026-09-08T10:00:00Z", + "payload": {} +} +``` + +Поддерживаемые entity: + +```text +transaction +category +budget +``` + +## Push + +Client отправляет локальные изменения. + +Server должен: +1. аутентифицировать пользователя; +2. проверить ownership/ID; +3. валидировать payload; +4. применить изменения; +5. создать server change entries; +6. вернуть результат и актуальную sync metadata. + +Операция должна быть безопасна при retry. + +## Pull + +Client передаёт cursor. + +Server возвращает изменения после этого cursor, включая deletions. + +Если существует pagination, ответ должен содержать понятный признак продолжения и новый cursor/sequence. + +## Ошибки + +Рекомендуемый контракт: + +```json +{ + "error": { + "code": "invalid_request", + "message": "Human-readable safe message" + } +} +``` + +Не возвращать: +- SQL error; +- stack trace; +- password/token; +- filesystem paths; +- внутренние identifiers инфраструктуры. + +## HTTP status semantics + +Рекомендуемая семантика: + +```text +200 OK — успешный request +201 Created — создание ресурса +400 Bad Request — malformed/invalid input +401 Unauthorized — missing/expired auth +403 Forbidden — resource belongs to another user +404 Not Found — resource/endpoint unavailable +409 Conflict — semantic conflict, если endpoint использует такую семантику +429 Too Many Requests — rate limit +500 Internal Server Error — internal failure +``` + +Конкретные status codes текущей реализации имеют приоритет над этой общей рекомендацией. diff --git a/BalanceServer/docs/ARCHITECTURE.md b/BalanceServer/docs/ARCHITECTURE.md new file mode 100644 index 0000000..7a6ef0d --- /dev/null +++ b/BalanceServer/docs/ARCHITECTURE.md @@ -0,0 +1,119 @@ +# Архитектура BalanceServer + +## Роль сервера + +BalanceServer — backend для синхронизации личных финансов между устройствами. + +Поток данных: + +```text +iOS/macOS/watchOS ─┐ + ├── HTTPS/JSON API ──> BalanceServer ──> Database +Android ───────────┘ +``` + +Сервер отвечает за: +- регистрацию и авторизацию; +- пользовательские сессии; +- хранение финансовых изменений; +- push/pull synchronization; +- глобальную последовательность изменений; +- удаление через tombstones; +- выдачу изменений начиная с cursor. + +## Исходный код + +Текущая кодовая база содержит 9 Go-файлов. + +Пути: + +```text +- internal/auth +- internal/config +- internal/database +- internal/httpapi +``` + +Конкретные границы package/handler/service/repository нужно сохранять при рефакторинге: перенос кода между слоями не должен менять внешний API. + +## HTTP layer + +HTTP layer принимает JSON requests от мобильных клиентов. + +Каждый handler должен: +1. проверить method/content type; +2. аутентифицировать пользователя, если endpoint защищён; +3. валидировать input; +4. вызвать domain/storage logic; +5. вернуть стабильный JSON response. + +## Domain / sync layer + +Sync layer является наиболее критичной частью backend. + +Он должен разделять: +- изменения, созданные текущим клиентом; +- изменения, которые нужно вернуть клиенту; +- удалённые записи; +- cursor/sequence. + +Ключевая модель: + +```text +client cursor + │ + ▼ +server change log + │ + ├── sequence 101 + ├── sequence 102 + ├── sequence 103 + └── ... +``` + +Клиент может запросить изменения после своего cursor и затем сохранить новый cursor. + +## Persistence + +Хранилище должно сохранять как минимум: +- user/account identity; +- credentials/session metadata; +- financial records; +- change metadata; +- deletion/tombstone metadata; +- monotonically increasing sequence, если оно является частью текущего sync protocol. + +Конкретные entities и schema документированы в `docs/DATA_MODEL.md`. + +## Security boundaries + +```text +Internet + │ + ▼ +HTTP server + │ + ├── authentication + ├── authorization + ├── input validation + │ + ▼ +domain/sync + │ + ▼ +database +``` + +Нельзя позволять клиенту выбирать произвольный `user_id` для чужих records. + +## Совместимость клиентов + +Apple и Android реализации должны видеть одну и ту же семантику данных. + +Особенно важно, чтобы: +- enum values были стабильными; +- даты имели единый формат; +- UUID/string IDs не менялись; +- `deleted` semantics были одинаковыми; +- cursor/sequence были монотонными; +- conflict resolution совпадал с клиентской логикой. diff --git a/BalanceServer/docs/DATA_MODEL.md b/BalanceServer/docs/DATA_MODEL.md new file mode 100644 index 0000000..507ebee --- /dev/null +++ b/BalanceServer/docs/DATA_MODEL.md @@ -0,0 +1,119 @@ +# Модель данных BalanceServer + +Ниже описана серверная модель на основании исходного кода проекта. При изменении schema обновляйте этот документ. + +## Найденные определения моделей/schema + +- internal/database/database.go +- internal/database/database.go +- internal/database/database.go +- internal/config/config.go +- internal/auth/tokens.go +- internal/auth/tokens.go +- internal/httpapi/auth_handlers.go +- internal/httpapi/auth_handlers.go +- internal/httpapi/auth_handlers.go +- internal/httpapi/server.go +- internal/httpapi/server.go +- internal/httpapi/server.go +- internal/httpapi/sync_handler.go +- internal/httpapi/sync_handler.go +- internal/httpapi/sync_handler.go +- internal/httpapi/web.go +- internal/httpapi/server_test.go +- internal/httpapi/server_test.go +- internal/httpapi/server_test.go + +## Общие сущности + +Сервер должен различать как минимум следующие логические типы синхронизируемых данных, совместимые с клиентами Balance: + +```text +transaction +category +budget +``` + +### Transaction + +Логически содержит: +- стабильный ID; +- amount; +- date; +- note; +- category presentation data; +- transaction kind; +- balance-adjustment flag; +- update timestamp; +- принадлежность пользователю. + +### Category + +Логически содержит: +- стабильный ID; +- name; +- icon/emoji; +- color; +- operation kind; +- creation/update metadata; +- принадлежность пользователю. + +### Budget + +Логически содержит: +- стабильный ID; +- category information; +- monthly limit; +- month start; +- update timestamp; +- принадлежность пользователю. + +## Change metadata + +Для sync необходима метаинформация: + +```text +user +entity +record ID +deleted +updatedAt +sequence +payload +``` + +`sequence` используется как серверный порядок изменений, а `updatedAt` — как timestamp версии самой записи. + +## Tombstones + +Удаление нельзя реализовывать только физическим `DELETE`, если клиентам нужно узнать о нём при следующем pull. + +Используется логическое изменение: + +```json +{ + "entity": "transaction", + "id": "…", + "deleted": true, + "updatedAt": "…" +} +``` + +После того как tombstone перестанет быть нужен для поддерживаемого sync history window, сервер может применять retention policy. Такая политика должна быть согласована с cursor semantics. + +## Invariants + +1. Record ID стабилен между sync. +2. Record принадлежит одному пользователю. +3. Клиент не может читать/изменять чужую запись. +4. `sequence` не должен уменьшаться. +5. Повторная доставка одного change не должна повреждать данные. +6. Удаление должно быть видимо клиенту через sync. +7. Timestamps должны храниться в UTC. +8. Enum/string values должны быть совместимы с iOS и Android. + +## Миграции + +Schema changes должны быть backward-compatible с активными версиями клиентов либо сопровождаться versioned API/миграцией. + +Не переименовывайте JSON field без периода совместимости. diff --git a/BalanceServer/docs/DEPLOYMENT.md b/BalanceServer/docs/DEPLOYMENT.md new file mode 100644 index 0000000..cdc90d7 --- /dev/null +++ b/BalanceServer/docs/DEPLOYMENT.md @@ -0,0 +1,108 @@ +# Deployment + +## Компоненты + +```text +Internet + │ + HTTPS + │ + ▼ +Reverse proxy / Load Balancer + │ + ▼ +BalanceServer + │ + ▼ +Database +``` + +## Configuration + +Все environment variables и flags должны задаваться через deployment environment, а не hard-code в Go. + +Перед production deployment составьте список переменных, которые реально читает текущий код: + +```text +- docker-compose.yml +- docker-compose.yml +- docker-compose.yml +- go.mod +- README.md +- README.md +- README.md +- Dockerfile +- Dockerfile +- Dockerfile +- internal/database/database.go +- internal/database/database.go +- internal/database/database.go +- internal/config/config.go +- internal/config/config.go +- internal/config/config.go +- internal/auth/tokens.go +- internal/auth/tokens.go +- internal/auth/tokens.go +- internal/auth/password.go +``` + +## Docker + +Если репозиторий содержит Dockerfile/compose, используйте их как canonical deployment recipe. + +Не включайте secrets в Docker image. + +## Health/readiness + +Если проект предоставляет health endpoint, load balancer должен использовать его для readiness. + +Database connectivity желательно проверять отдельно от liveness. + +## Database migrations + +Migration должна выполняться контролируемо: + +```text +backup + ↓ +migration + ↓ +health check + ↓ +application rollout +``` + +Не запускайте destructive migration автоматически без backup/rollback plan. + +## Backups + +Финансовые данные — пользовательские данные высокой ценности. + +Минимум: +- регулярные DB backups; +- retention policy; +- периодическая проверка restore; +- encrypted backup storage. + +## Observability + +Рекомендуется мониторить: +- HTTP 5xx; +- auth failures; +- sync failures; +- latency; +- DB latency/errors; +- active users/devices; +- size/lag of sync change log; +- disk/storage usage. + +## Release checklist + +- [ ] Tests green. +- [ ] Database migrations проверены. +- [ ] Backup создан. +- [ ] Secrets injected через secure mechanism. +- [ ] HTTPS включён. +- [ ] Health checks работают. +- [ ] Sync tested with both mobile clients. +- [ ] Rollback plan готов. diff --git a/BalanceServer/docs/DEVELOPMENT.md b/BalanceServer/docs/DEVELOPMENT.md new file mode 100644 index 0000000..549be19 --- /dev/null +++ b/BalanceServer/docs/DEVELOPMENT.md @@ -0,0 +1,107 @@ +# Разработка и запуск + +## Требования + +Проект использует Go 1.25.0. + +Проверить локальную версию: + +```bash +go version +``` + +## Установка зависимостей + +```bash +go mod download +``` + +## Сборка + +```bash +go build ./... +``` + +## Тесты + +```bash +go test ./... +``` + +С race detector: + +```bash +go test -race ./... +``` + +## Static checks + +```bash +go vet ./... +``` + +Если в проекте есть Makefile/CI, его команды имеют приоритет как canonical build pipeline. + +## Локальный запуск + +Перед запуском изучите конфигурацию в коде и `.env`/deployment files. + +Не коммитьте реальные credentials. + +Пример общего workflow: + +```bash +go mod download +go test ./... +go build ./... +./ +``` + +Имя binary и обязательные env vars определяются текущей entrypoint/config реализацией. + +## Database + +Перед первым запуском: +1. поднять требуемую БД; +2. применить migrations, если проект их использует; +3. задать connection settings; +4. проверить health/startup logs. + +## Production + +Рекомендуемый pipeline: + +```text +source + ↓ +go test ./... + ↓ +go vet ./... + ↓ +go build ./... + ↓ +container/package + ↓ +deploy + ↓ +health check +``` + +Для production нужны: +- TLS termination; +- persistent database; +- backups; +- monitoring; +- log rotation; +- secrets management; +- migration procedure. + +## Совместимость + +При релизе server проверяйте минимум: +- старый iOS client + новый server; +- новый iOS client + server; +- Android client + server; +- concurrent sync с двух устройств; +- login/refresh после истечения access token; +- delete → pull на другом устройстве. diff --git a/BalanceServer/docs/PRODUCT.md b/BalanceServer/docs/PRODUCT.md new file mode 100644 index 0000000..f083274 --- /dev/null +++ b/BalanceServer/docs/PRODUCT.md @@ -0,0 +1,62 @@ +# Функциональная документация BalanceServer + +## Назначение + +BalanceServer является backend для приложения Balance — персонального финансового учёта. + +Он не реализует пользовательский UI. Его задача — безопасно хранить и синхронизировать данные между устройствами. + +## Пользовательские данные + +Синхронизируются: + +- финансовые операции; +- пользовательские категории; +- месячные бюджеты; +- удаления этих сущностей. + +## Multi-device + +Один пользователь может работать с несколькими устройствами. + +Пример: + +```text +iPhone добавил расход + ↓ + Server + ↓ +Android получил расход + ↓ +Mac получил расход +``` + +## Offline-first + +Мобильные приложения сохраняют изменения локально и затем отправляют их серверу. + +Поэтому сервер должен корректно принимать изменения спустя некоторое время после их создания. + +## Delete + +Удаление является syncable operation, а не только локальным DB delete. + +Другие устройства должны получить tombstone и удалить локальную запись. + +## Privacy + +Сервер хранит персональные финансовые данные. Поэтому production deployment должен обеспечивать: +- TLS; +- authentication; +- authorization; +- encrypted backups; +- controlled database access; +- минимальное логирование финансовых payloads. + +## Клиентская совместимость + +Backend должен рассматриваться как общий контракт для: +- Apple Balance; +- Android Balance. + +Любое изменение протокола требует проверки обеих реализаций. diff --git a/BalanceServer/docs/SECURITY.md b/BalanceServer/docs/SECURITY.md new file mode 100644 index 0000000..d2cc3d2 --- /dev/null +++ b/BalanceServer/docs/SECURITY.md @@ -0,0 +1,79 @@ +# Безопасность + +## Credentials + +Пароли пользователей должны храниться только в виде slow password hash с современным password hashing алгоритмом. + +Не хранить plaintext password. + +## Tokens + +Access/refresh tokens не должны попадать в: +- application logs; +- panic messages; +- error responses; +- analytics; +- database debug dumps. + +## Authorization + +Каждый sync mutation должен проверять ownership по authenticated user. + +Нельзя доверять `userId` из client payload, если authenticated identity уже известна из token. + +Правильная модель: + +```text +Authorization header + ↓ +authenticated user + ↓ +server-side ownership + ↓ +requested entity +``` + +## Transport + +Production API должен работать через HTTPS/TLS. + +HTTP следует оставлять только для controlled local development. + +## Input validation + +Проверяйте: +- UUID/ID; +- enum values; +- amount; +- timestamps; +- string lengths; +- batch size; +- JSON size; +- cursor/sequence bounds. + +## Logging + +Безопасно логировать: +- request ID; +- endpoint; +- status code; +- latency; +- batch size; +- non-sensitive user/record identifiers в допустимой для проекта форме. + +Нельзя логировать: +- passwords; +- Authorization headers; +- access tokens; +- refresh tokens; +- полные request bodies финансовых данных без явной необходимости. + +## Abuse protection + +Production deployment должен иметь: +- rate limiting для auth endpoints; +- reasonable request body limits; +- timeouts; +- graceful shutdown; +- database connection limits; +- monitoring. diff --git a/BalanceServer/docs/SYNC.md b/BalanceServer/docs/SYNC.md new file mode 100644 index 0000000..1205d90 --- /dev/null +++ b/BalanceServer/docs/SYNC.md @@ -0,0 +1,138 @@ +# Синхронизация + +## Цель + +Один аккаунт Balance может использоваться на нескольких устройствах: + +```text +iPhone + │ + ├── transaction A + ├── category B + └── budget C + │ + ▼ + BalanceServer + │ + ┌────┴────┐ + ▼ ▼ + Android Mac +``` + +## Cursor + +Каждый клиент хранит cursor последнего успешно применённого server change. + +Принцип: + +```text +cursor = 100 + +server: +101 A +102 B +103 C + +pull(cursor=100) +→ A, B, C +→ new cursor = 103 +``` + +Клиент должен сохранять cursor только после успешного применения соответствующего batch. + +## Sequence + +Server sequence задаёт глобальный порядок изменений в sync stream. + +Требования: +- монотонность; +- отсутствие уменьшения; +- однозначность порядка; +- корректность при concurrent requests. + +## Push + +Push передаёт изменения клиента: + +```text +local mutations + ↓ +POST /v1/sync + ↓ +validation + ↓ +ownership check + ↓ +persist + ↓ +append to change stream +``` + +Retry не должен создавать две разные логические записи из одного client change. + +## Pull + +Pull получает изменения после cursor. + +Сервер не должен возвращать изменения другого пользователя. + +При pagination клиент должен иметь возможность продолжить с нового cursor. + +## Conflict resolution + +Клиенты Balance используют timestamp-based versioning. + +Типовая проверка: + +```text +local.updatedAt < remote.updatedAt +``` + +Следовательно, server должен сохранять `updatedAt` достаточно точно и не подменять его произвольным временем каждого чтения. + +При конкурентных writes сервер должен иметь однозначное правило победителя. + +## Deletions + +Удаление создаёт tombstone/change: + +```json +{ + "entity": "transaction", + "id": "...", + "deleted": true, + "updatedAt": "..." +} +``` + +Tombstone должен попасть в change stream так же, как обычное изменение. + +## Idempotency + +Push может повториться из-за: +- timeout; +- network failure; +- app restart; +- retry after 401/token refresh. + +Поэтому сервер должен обрабатывать повторную отправку безопасно. + +## Batch + +Apple-клиент отправляет batches до 100 изменений и может делить крупные payloads. + +Android должен использовать совместимый protocol. + +Server не должен предполагать, что все изменения приходят одним запросом. + +## Reset/full sync + +Если client потерял cursor или требует reset, сервер должен поддерживать предусмотренный текущим API механизм полного pull либо новый cursor baseline. + +Нельзя молча трактовать неизвестный/просроченный cursor как актуальный. + +## Security + +Sync endpoint всегда ограничен текущим authenticated user. + +`deviceId` — идентификатор устройства, а не механизм авторизации.