cryptofoundry

Связаться с cryptofoundry

Расскажите, что вы хотите создать или автоматизировать.

Электронная почта
[email protected]
ADAMANT Messenger
Открыть в ADAMANT
currencyinfo

Currencyinfo v4.2.0

Версия Currencyinfo 4.2.0 перестраивает слой источников курсов вокруг провайдеров, не требующих ключей, после того как два бесплатных вышестоящих сервиса прекратили работу. Также были усилены среда выполнения и контейнер, добавлены сайт с документацией и полноценный набор тестов.

Аудит безопасности проведен cryptofoundry.

Обновление с версии 4.1.2 или старше

Для потребителей API изменений не требуется: структура ответов /get, /getHistory и /status осталась прежней. Операторам необходимо выполнить два обязательных шага.

Стандартный файл config.jsonc версии 4.1.2 не запустится в 4.2.0. Сервисы CryptoCompare и CoinGecko теперь требуют API-ключ, и оба включены в шаблоне 4.1.2, поэтому исходная конфигурация не пройдет проверку перед открытием HTTP-порта. Отключите их или укажите учетные данные, добавьте альтернативные источники без ключей для восстановления покрытия и обновите priorities.

Индексы tickers были перестроены. Три индекса, упорядоченных по дате, заменили три старых. Mongoose создает новые индексы при подключении, но не удаляет старые, поэтому прямое обновление приведет к их созданию при запуске, что может задержать готовность системы. Рекомендуется создать их заранее.

Замеры на двух производственных развертываниях, содержащих ~238 миллионов документов тикеров: на NVMe с 12 ядрами и 64 ГБ ОЗУ создание всех трех индексов заняло 17 минут при росте indexSize с 9,1 до 19,3 ГБ; на SATA с 4 ядрами и 16 ГБ ОЗУ сборка заняла 50 минут при росте indexSize с 8,7 до 18,9 ГБ.

Теперь требуется Node.js версии 22.12 или новее. Откат к версии 4.1.2 безопасен: структура хранимых документов не изменилась.

Источники курсов

Набор провайдеров был переработан после того, как CryptoCompare прекратил поддержку бесплатного тарифа 21 мая 2026 года, а безключевой план CoinGecko стал непригодным для использования. Добавлены четыре новых коннектора, все они не требуют ключей.

CoinPaprika выполняет один пакетный запрос на цикл плюс ограниченное количество запросов по отдельным монетам, выходящим за рамки пакетного диапазона. Параметры bulk_limit и max_individual_requests ограничивают бюджет запросов, а монеты вне диапазона исключаются при запуске с предупреждением, чтобы не расходовать квоту в каждом цикле.

CoinLore возвращает весь набор монет за один мульти-ID запрос. Числовые идентификаторы CoinLore переназначаются между листингами, поэтому ответ, чей символ не совпадает с настроенным ID, отклоняется во время выполнения, а не принимается на веру.

Binance предоставляет публичные данные спотового рынка напрямую с биржи, а не через агрегатор, что обеспечивает независимость от четырех агрегаторов, частично использующих общие данные. У Binance нет прямых пар с USD, поэтому курсы запрашиваются относительно настраиваемого quote_asset (по умолчанию USDT) и предоставляются как USD; при отвязке курса (depeg) затронутые курсы выделяются в отдельную группу расхождения вместо скрытия через маппинг. Ошибка HTTP 451 (геоблокировка) отключает коннектор и выдает предупреждение один раз, вместо того чтобы приводить к сбою в каждом цикле.

ExchangeRate-API предоставляет фиатные курсы без ключей для 166 валют с ежедневным обновлением.

CryptoCompare помечен как устаревший и отключен по умолчанию, удален из priorities и будет полностью убран в следующем мажорном релизе. При его включении API-ключ обязателен. CoinGecko отключен по умолчанию и теперь требует бесплатный Demo-ключ, так как безключевой публичный план ограничивает количество запросов до 5–15 в минуту с непредсказуемым лимитированием.

Пять источников теперь включены по умолчанию без каких-либо учетных данных, что делает config.default.jsonc рабочим «из коробки»: три крипто-источника и два фиатных источника позволяют удовлетворить условие minSources: 2 без дополнительных настроек.

Доступ без ключа не означает разрешение на републикацию. CoinPaprika и ExchangeRate-API ограничивают перераспределение данных публичными или коммерческими инстансами. Ознакомьтесь с условиями использования источников и документацией по перераспределению перед публикацией этих курсов.

Расчет курсов и корректность API

Индексы tickers теперь упорядочены по дате ({ base: 1, date: -1 }, { quote: 1, date: -1 }, { base: 1, quote: 1, date: -1 }), поэтому сортировка в /getHistory выполняется на уровне индекса, а не в оперативной памяти. На коллекции из 238 миллионов документов тот же запрос по паре и диапазону ускорился с 22,8 с до 8 мс.

Исторические фильтры coin теперь используют документированный порядок BASE/QUOTE. В версии 4.1.x фильтр пар был инвертирован, поэтому coin=ADM/USD ничего не находил. Развертывания, использующие обходные пути с инверсией на стороне клиента, должны их удалить.

minSources теперь рассчитывается с учетом источников, актуальных для запрашиваемого периода, поэтому устаревший провайдер больше не может удовлетворить условие минимального количества источников. Эффективный порог равен min(minSources, coverage), что позволяет обслуживать пару с одним провайдером, сообщая об этом в предупреждении при запуске.

Триангуляция отклоняет кросс-курсы, которые округляются до нуля или являются нечисловыми (non-finite). Схемы /get и /getHistory теперь .strict(), поэтому неизвестный параметр запроса возвращает 400, а не игнорируется. Ошибки валидации теперь возвращают 400 вместо 500.

Символы монет, принимаемые от провайдеров и в фильтрах запросов, теперь поддерживают расширенный формат Unicode, поэтому любая хранимая пара доступна — $CWIF является реальным примером — в то время как опечатки в base_coins и mappings по-прежнему вызывают ошибку при запуске.

/status сообщает статус updating на основе реального состояния обновления, а не на сравнении временных меток; параллельные циклы обновления пропускаются, а не накладываются друг на друга.

Безопасность

URL вебхуков, API-ключи и парольные фразы удаляются из логов, файлов журналов и уведомлений. Это устраняет реальную уязвимость: ответ с ошибкой от вышестоящего сервиса мог поместить API-ключ в лог через URL ошибки Axios.

Директория logs создается с правами 0o750, а файлы логов — 0o600 (по мере возможности), чтобы примонтированный том, принадлежащий другому пользователю, не блокировал запуск. Имена файлов логов больше не содержат двоеточий, что делало их недоступными в некоторых файловых системах.

Контейнер работает от имени непривилегированного пользователя node, npm/pnpm/yarn удалены из образа среды выполнения, apk upgrade применяет обновления Alpine во время сборки, а защитный механизм прерывает сборку, если производственная зависимость содержит нативные биндинги. Файл config.jsonc намеренно отсутствует в образе. Заголовок x-powered-by отключен, а валидация конфигурации отклоняет неизвестные ключи и значения с неверным регистром.

Сканирование Trivy выполняется в CI, политика уязвимостей задокументирована. Пакеты multer и js-yaml зафиксированы через overrides, поэтому pnpm audit и pnpm audit --prod не показывают уязвимостей.

Платформа и зависимости

Требуется Node.js >= 22.12.0, pnpm зафиксирован на версии 12.3.4 через packageManager. NestJS обновлен с 10 до 12, Mongoose с 8 до 9, Zod с 3 до 4, adamant-api со 2 до 3, chalk с 4 до 6. ESLint обновлен с 8 до 10 с плоской конфигурацией (flat config), TypeScript с 5 до 6, Jest с 29 до 30, Prettier до 3.9.

pnpm-workspace.yaml содержит список разрешенных скриптов установки, от которых зависит pnpm run deps:setup. Ошибки подключения к MongoDB теперь логируются, а не подавляются, и процесс завершается с ненулевым кодом и очищенным сообщением об ошибке.

Распространение

publish-docker.yml выполняет публикацию мультиплатформенных образов в ghcr.io/adamant-im/currencyinfo для linux/amd64 и linux/arm64 с OCI-метками, SBOM и аттестацией происхождения сборки. Задача прерывается, если тег релиза не является предком master, а тег latest обновляется только для новейших не-пререлизных версий.

docker-ci.yml собирает производственный образ при создании pull-реквестов и выполняет smoke-тесты с использованием config.default.jsonc, подтверждая, что конфигурация по умолчанию запускается и возвращает курсы без API-ключа. Проверяются OCI-метки, отсутствие конфигурации или секретов в образе, а также запуск контейнера от имени не-root пользователя. Публикация не выполняется.

docker-compose.prod.yaml использует опубликованный образ, монтирует конфигурацию только для чтения и фиксирует mongo:8.0. Файл Compose для разработки привязывает MongoDB к 127.0.0.1 вместо всех интерфейсов.

docker pull ghcr.io/adamant-im/currencyinfo:4.2.0

Документация и тесты

Сайт документации доступен по адресу https://currencyinfo.docs.adamant.im и охватывает темы быстрого старта, установки, архитектуры, расчета курсов, истории, уведомлений, операций, безопасности, устранения неполадок, обновления, а также содержит полный справочник REST и конфигурации, и страницу для каждого источника курсов с описанием квот, форматов идентификаторов, режимов сбоя и условий перераспределения.

Впервые добавлен файл LICENSE (GPL-3.0), а также CONTRIBUTING.md и AGENTS.md.

Набор тестов вырос с 3 файлов спецификаций до 28, охватывая каждый коннектор источника, объединитель и его стратегии, менеджер источников, загрузку конфигурации, миграцию и валидацию схем, логгер, уведомитель, обе схемы запросов, фильтр исключений, пайп валидации, интерцептор, контроллер и общие утилиты. pnpm test выполняет 266 тестов.

Критические изменения

Стандартный файл config.jsonc версии 4.1.2 не запускается в 4.2.0, так как CryptoCompare и CoinGecko теперь требуют API-ключ, а оба включены в шаблоне 4.1.2. Операторы должны отключить их или предоставить учетные данные, добавить альтернативные источники без ключей и обновить priorities. Три индекса tickers, упорядоченных по дате, заменили три старых; Mongoose создает новые при подключении, но не удаляет старые, поэтому создайте их заранее, чтобы избежать задержки запуска. Требуется Node.js 22.12 или новее. Исторические фильтры coin теперь используют документированный порядок BASE/QUOTE; в 4.1.x фильтр пар был инвертирован, поэтому развертывания с обходными путями на стороне клиента должны их удалить. Схемы /get и /getHistory теперь .strict(), поэтому неизвестный параметр запроса возвращает 400 вместо игнорирования, а ошибки валидации возвращают 400 вместо 500.