Skip to content

Сервис маркетплейса расширений

Отдельный backend-сервис каталога (REST + Postgres), не часть npm-пакета расширения.

Стек: Fastify, Postgres, node-pg-migrate, Zod, типы из @rynt/sdk/extension-marketplace.

Отличие от лаунчера: уникальность слота

СредаПолитика (registryId, key)
Лаунчер (runtime store)Несколько расширений могут записать один слот; UI/политика хоста не навязывает глобальную уникальность
Сервис каталогаПри публикации каждая пара (registry_id, registry_key) глобально уникальна в каталоге

При POST /api/v1/extensions сервис:

  1. Парсит и валидирует manifest (contributes.rynt без requiresRegistries, extensionApi: "1" — см. 08-extension-api-versioning.md).
  2. Разворачивает extendsRegistries в строки (registry_id, registry_key).
  3. Для каждой строки проверяет отсутствие записи в registry_slot_contributions с другим extension_id.
  4. При конфликте → 409 + MarketplaceApiError с code: SLOT_ALREADY_CLAIMED.

Обновление версии того же manifest.id: слоты предыдущей версии снимаются и записываются заново из нового manifest (транзакция).

Повторная публикация той же версии → DUPLICATE_VERSION.

Схема БД

extensions

КолонкаОписание
id, versionPK составной (id, version)
manifestполный JSON
download_urlURL .ryntextension
is_latestboolean, одна актуальная версия на id для каталога
author_user_idKeystone user id (текст), nullable для системных пакетов

Индекс: GIN по manifest или tsvector по name + description.

registry_declarations

Денормализация declaresRegistries. PK (extension_id, registry_id).

Уникальность: один registry_id в каталоге может быть объявлен только одним extension_id (constraint на registry_id UNIQUE) — зеркало правила лаунчера «один владелец реестра».

registry_slot_contributions

Денормализация extendsRegistries (развернуть все keys).

ConstraintНазначение
UNIQUE (registry_id, registry_key)один слот — одно расширение в каталоге
PK (extension_id, registry_id, registry_key)все слоты пакета

Индекс для поиска: (registry_id, registry_key) — основной запрос кнопки слота.

REST API

Контракт типов: 04-sdk-shared-types.md.

Публичные

MethodPathОписание
GET/api/v1/extensions/slots?registryId=&key=MarketplaceSlotLookupResult
GET/api/v1/extensions/search?q=&registryId=&limit=&offset=поиск
GET/api/v1/extensions/:idlatest или список версий
GET/api/v1/extensions/:id/versions/:versionдеталь
GET/api/v1/registries/:registryIdкто объявил реестр + метаданные

Защищённые

Authorization: Bearer <sessionToken> (сессия Keystone). Чтение каталога без токена.

MethodPathОписание
POST/api/v1/extensionsпубликация
PUT/api/v1/extensions/:id/versions/:versionобновить url / manifest
DELETE/api/v1/extensions/:id/versions/:versionснять с каталога

Публикация: алгоритм проверки слотов

text
FOR each (registryId, key) IN flatten(manifest.contributes.rynt.extendsRegistries):
  IF EXISTS registry_slot_contributions
     WHERE registry_id = registryId AND registry_key = key
       AND extension_id <> manifest.id:
    RAISE SLOT_ALREADY_CLAIMED
INSERT extensions ...
INSERT registry_declarations ...
INSERT registry_slot_contributions ...

declaresRegistries:

text
FOR each registryId IN declares:
  IF EXISTS registry_declarations WHERE registry_id = registryId AND extension_id <> manifest.id:
    RAISE REGISTRY_ALREADY_DECLARED

Поиск

  1. Точный слотregistry_slot_contributions JOIN extensions WHERE is_latest = true.
  2. По реестру — все contributions с registry_id = $1.
  3. Текстsearch?q= по name/description + опционально filter registryId.

Redis — post-MVP.

Переменные окружения

EnvОписание
DATABASE_URLPostgres
PORTHTTP
JWT_SECRET или AUTH_INTROSPECT_URLпроверка Bearer для POST

Миграции

src/db/migrations/ + скрипты migrate:up / migrate:create как в chat-service.

Первая миграция: таблицы выше + constraints UNIQUE.

Связь с лаунчером

Лаунчер не вызывает сервис при обычной загрузке расширений с диска. Сервис нужен для:

  • кнопки / диалога установки слота;
  • глобального диалога каталога;
  • будущего publish CLI / CI.

Установка: downloadUrl → скачивание файла → установка .ryntextension в каталог расширений → перезагрузка реестра в лаунчере.