Ask the assistant
0/400
Your question is processed by an AI service and kept to improve answers. Do not enter personal data.

    Синхронизация и сборка

    This page has not been translated into English yet. The Russian original is below.
    Статус: реализовано (backend развёрнут на *.reeel.dev ; клиент — Dart-пакет reeel_platform_client , интеграция в редактор — PlatformSyncService ). Полный контракт API — источник истины — в platform-docs/reeel-editor-api.md . Ниже — как этим пользуется редактор и как устроены потоки.

    Роли

    Роль Кто это Ответственность
    Editor Редактор REEEL (десктоп) Вход по email/паролю, привязка проекта, синхронизация снапшота, постановка сборки. Использует reeel_platform_client .
    Platform API https://api.reeel.dev Единственная точка входа: auth, организации/проекты, ревизии снапшотов, очередь сборок, выдача контента.
    Storage https://storage.reeel.dev Объектное хранилище blob'ов. Доступ только по presigned-URL из ответов API (авторизация в самом URL).
    Build Worker Отдельный исполнитель (пока не реализован) Берёт задачу + снимок ревизии, собирает конфигурацию ( config_builder / болванки), заливает артефакт и статус.

    Клиент платформы ( reeel_platform_client ) fs-агностичен и рассчитан на переиспользование будущим worker'ом (тот тоже читает ревизии и blob'ы).

    Аутентификация

    Email/пароль → JWT. OTP нет (не путать с runtime-клиентом reeel_api_client , у которого свой контракт reeel.dev/reeel/api ).

    • POST /auth/signup → 202 (пользователь PENDING_VERIFICATION ); на почту — ссылка /auth/verify-email?token=… (TTL 24 ч, одноразовая). POST /auth/verify-email .
    • POST /auth/login → { access_token, refresh_token, token_type, expires_in } .
    • access — JWT RS256, TTL 15 мин ; шлётся как Authorization: Bearer <jwt> .
    • refresh — opaque, TTL 30 дней , ротируется на каждом обмене. ⚠️ Повторное предъявление ротированного refresh = компрометация → аннулируется вся семья токенов. Клиент хранит только последнюю пару и не ретраит старый refresh.
    • POST /auth/refresh — обновление пары; POST /auth/logout — отзыв предъявленного refresh; GET /auth/me — текущий пользователь.
    • OIDC device flow (браузерный вход) поддержан в пакете ( DeviceFlowApi , публичный клиент reeel-cloud-cli , PKCE, эндпоинты из discovery); в UI редактора сейчас выведен только вход по паролю.
    • Чего в API нет : resend письма, forgot/reset и смены пароля.

    Роли ( owner > admin > developer > viewer ) привязаны к организации и действуют на все её проекты (project-scoped ролей нет).

    Ресурсная модель

    organization (org_…)
      └── project (prj_…)   slug, region, tier, defaultDomain=<slug>.apps.reeel.dev
            ├── snapshot revision (snp_…)   контент проекта, content-addressed
            └── build task (rbt_…)          сборка ревизии под конфигурацию

    Проект в редакторе (папка с project.json ) привязывается к удалённому prj_… ; привязка ( orgId + projectId + последняя ревизия) хранится локально в .reeel/state.json .

    Поток 1 — синхронизация проекта (Editor → Platform)

    Трёхфазная, content-addressed по SHA-256: negotiate → presigned PUT → commit . Оркестрируется SnapshotSync.push(projectId, files) ; в редакторе — PlatformSyncService.sync() .

    Editor                         api.reeel.dev            storage.reeel.dev
      │  1. sha256+size каждого файла                              │
      │  2. POST …/snapshots:negotiate ─────►│                     │
      │◄──── { revisionId, missing[], upload[] (presigned PUT) }   │
      │  3. PUT сырые байты (для каждого missing) ─────────────────►│  (без Authorization)
      │◄──────────────────────────────────────────── 200/204 ──────┤
      │  4. POST …/snapshots/{revisionId}:commit ─►│               │
      │◄──── { status: COMMITTED, contentHash, revision, … }       │
    • Что синхронизируется: всё содержимое проекта — project.json , reeel/ , data/ , произвольные ассеты, .well-known/ — кроме любого пути, где сегмент начинается с . ( .reeel/ , .git/ , .claude/ , .env , …). Правило зашито в PlatformSyncService.isSyncablePath (исключение — точный сегмент .well-known ). ⚠️ При выключенном content-gating всё это публично читаемо — не кладите секреты в синкаемые файлы.
    • Идемпотентность по контенту: повторный commit идентичного контента возвращает ту же ревизию без создания новой; неизменные файлы не перезаливаются (пустой missing ).
    • Ошибки: 403 forbidden_path (недопустимый путь в negotiate), 409 incomplete_snapshot (залиты не все blob'ы — клиент ре-negotiate'ит и повторяет commit один раз), 422 manifest_invalid (невалидный project.json , с errors[] ).
    • Dry-run: перед push редактор прогоняет локальную валидацию манифеста ( ProjectService.validate() ); на сервере есть POST …/snapshots:validate (всегда 200 ).
    • Pull / история: GET …/snapshots/current , …/snapshots/{revisionId} , GET …/snapshots?cursor=…&limit=… (курсорная пагинация), blob — GET …/blobs/{sha256} (302).

    Поток 2 — постановка сборки в очередь (Editor → Platform)

    Editor                         api.reeel.dev
      │  POST /api/v1/reeel/builds                     │
      │  { projectId, revisionId, configurationName }  │  (+ опц. Idempotency-Key)
      │ ──────────────────────────────────────────────►│  201 { taskId: rbt_…, status: queued }
      │  GET /api/v1/reeel/builds/{taskId}  (poll 2–5с)─►│  queued → running → succeeded/failed
      │◄──── { status, artifactRef | errorCode }───────┤
    • configurationName — имя конфигурации из project.json (выбранной в редакторе). Сервер не валидирует его против манифеста при постановке — ошибка всплывёт при сборке.
    • Опрос: BuildsApi.poll (в редакторе — PlatformSyncService.build() ); backoff на 429 . Есть SSE-стрим логов ( Accept: text/event-stream , BuildsApi.streamLogs ).
    • Идемпотентность: опциональный Idempotency-Key (тот же ключ+тело = та же задача).
    • Списка сборок нет — трекинг по taskId . Не путать с legacy /api/v1/builds .

    Поток 3 — исполнение сборки (Worker → Platform) — планируется

    Worker (ещё не реализован) заберёт задачу, скачает ревизию ( snapshots/{id} + blobs/{sha256} ), соберёт конфигурацию через существующий пайплайн ( config_builder , make build ) или быстрый путь через болванки ( TemplateFlavorResolver → перепаковка flutter_assets без перекомпиляции; см. template builds.md) и зальёт артефакт + финальный статус. Тот же reeel_platform_client предназначен и для worker'а.

    Runtime-контент и gating (вне этого майлстоуна)

    Собранное приложение тянет контент через GET /content/{projectId}/{path} (302 на presigned GET). ⚠️ У новых проектов content-gating включён по умолчанию — runtime требует content-grant ( enroll install → обмен на grant, TTL 15 мин). Пакет реализует этот lifecycle ( ContentApi ), но редактор его не использует; включение grant-flow в собранные приложения — отдельная задача.

    Обработка ошибок

    Формат — RFC 7807 ProblemDetail. Машинное поле code есть только у бизнес-ошибок /api/v1/** и /content/** ; у /auth/** и входных 401/403/429 его нет — там ветвление по HTTP-статусу. Клиент маппит всё это в PlatformApiException { statusCode, code?, detail, errors[], warnings[], missing[] } и игнорирует неизвестные поля ответов. Полный индекс кодов — в platform-docs .

    Поверхность API (что использует редактор)

    Операция Метод / путь Клиент ( reeel_platform_client )
    Вход / сессия POST /auth/login , /auth/refresh , GET /auth/me AuthApi
    Список организаций / проектов GET /api/v1/organizations , …/{orgId}/projects OrganizationsApi , ProjectsApi
    Создать проект POST /api/v1/organizations/{orgId}/projects ProjectsApi.create
    Negotiate снапшота POST …/snapshots:negotiate SnapshotsApi / SnapshotSync
    Upload blob'а PUT <presigned> (storage) AuthedHttp.putToStorage
    Commit ревизии POST …/snapshots/{revisionId}:commit SnapshotSync.push
    Текущая ревизия / история GET …/snapshots/current , …/snapshots?cursor= SnapshotsApi
    Поставить сборку POST /api/v1/reeel/builds BuildsApi.enqueue
    Статус / логи сборки GET /api/v1/reeel/builds/{taskId} (poll / SSE) BuildsApi.poll / streamLogs

    Реализация — статус

    Готово

    • Dart-клиент reeel_platform_client на все группы эндпоинтов (auth + device flow, организации, проекты, участники, снапшоты + SnapshotSync , сборки, content).
    • Транспорт: инъекция Bearer, single-flight refresh на 401 , presigned PUT, разбор ProblemDetail.
    • Интеграция в редактор: PlatformSyncService (вход email/пароль, привязка проекта в .reeel/state.json , sync() , build() ), персистентные токены, конфиг platform_api_url .
    • UI: форма входа, кнопки Sync/Build и меню аккаунта в тулбаре, диалог привязки.

    Планируется

    • Build worker (протокол claim/artifact; переиспользует reeel_platform_client ).
    • Runtime content-grant flow в собранных приложениях (gating включён по умолчанию).
    • Device-flow вход в UI редактора (в пакете реализован).
    • Push-уведомления о статусе сборки вместо поллинга.
    DOCS
    dark_mode_baseline
    RU/EN
    menu_baseline