Currencyinfo 4.2.0 在两个上游免费层级下线后,围绕无密钥提供商重构了汇率源层,加固了运行时与容器,并发布了文档站点及完整的测试套件。
该版本已通过 cryptofoundry 的安全审计。
从 4.1.2 或更早版本升级
API 使用者无需进行任何更改:/get、/getHistory 和 /status 的响应格式保持不变。但运维人员必须执行两个强制步骤。
标准的 4.1.2 config.jsonc 无法在 4.2.0 上启动。CryptoCompare 和 CoinGecko 现在均要求提供 API 密钥,且两者在 4.1.2 模板中均已启用,因此未经修改的配置会在 HTTP 端口打开前验证失败。请禁用它们或提供凭据,添加无密钥替代方案以恢复覆盖范围,并更新 priorities。
tickers 索引已重建。三个按日期排序的索引取代了旧的三个索引。Mongoose 会在连接时创建新索引,但不会删除旧索引,因此直接升级会在启动时构建所有三个索引,这可能会延迟就绪时间。建议先在带外(out of band)构建这些索引。
在两个各持有约 2.38 亿条 ticker 文档的生产部署中进行测量:在配备 12 核 CPU 和 64 GB RAM 的 NVMe 环境下,构建所有三个索引耗时 17 分钟,indexSize 从 9.1 GB 增长至 19.3 GB;在配备 4 核 CPU 和 16 GB RAM 的 SATA 环境下,同样的操作耗时 50 分钟,indexSize 从 8.7 GB 增长至 18.9 GB。
现在要求使用 Node.js 22.12 或更高版本。回滚至 4.1.2 是安全的:存储的文档布局未发生变化。
汇率源
在 CryptoCompare 于 2026 年 5 月 21 日停用其免费层级且 CoinGecko 的无密钥计划变得不可用后,提供商集合已重新规划。新增了四个连接器,且均为无密钥连接器。
CoinPaprika 每个周期进行一次排名批量调用,外加针对批量范围外币种的有限次单币种调用。bulk_limit 和 max_individual_requests 用于限制请求预算,超出范围的币种会在启动时被排除并发出警告,从而避免每个周期浪费配额。
CoinLore 在单次多 ID 请求中返回整个币种集。由于数字 CoinLore ID 会在不同列表中重新分配,因此若响应中的符号与配置的 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 排序由索引提供,不再阻塞内存中排序。在 2.38 亿条文档的集合上测量,相同的交易对与范围查询从 22.8 秒缩短至 8 毫秒。
历史 coin 过滤器现在使用记录的 BASE/QUOTE 顺序。在 4.1.x 版本中,交易对过滤器是反向的,因此 coin=ADM/USD 无法匹配任何内容。带有客户端反向处理方案的部署必须将其移除。
minSources 是根据请求生命周期内最新的源进行衡量的,因此过时的提供商不再能满足源计数门槛。有效阈值为 min(minSources, coverage),这既能保持单提供商交易对的正常服务,又能在启动警告中报告该情况。
三角测量会拒绝四舍五入为零或非有限值的交叉汇率。/get 和 /getHistory 模式现在为 .strict(),因此未知的查询参数会返回 400 而不是被静默忽略。验证错误现在返回 400 而不是 500。
提供商和查询过滤器接受的币种符号采用更广泛的 Unicode 感知格式,因此每个存储的交易对都是可寻址的(例如 $CWIF),而 base_coins 和 mappings 中的拼写错误仍会在启动时报错。
/status 根据实际更新状态报告 updating,而不是通过时间戳比较进行推断,且并发刷新周期会被跳过,而不是重叠。
安全性
Webhook URL、API 密钥和密码已从日志输出、日志文件和通知分发中脱敏。这修复了一个实际的泄漏问题:上游错误响应曾通过 Axios 错误 URL 将 API 密钥写入日志。
logs 目录创建权限为 0o750,日志文件为 0o600,这是尽力而为的设置,以确保由其他用户拥有的挂载卷不会阻塞启动。日志文件名不再包含冒号,这曾导致在某些文件系统上无法打开文件。
容器以非特权 node 用户身份运行,运行时镜像中移除了 npm/pnpm/yarn,apk upgrade 在构建时应用 Alpine 更新,且构建时防护机制会在生产依赖项包含原生绑定时中断构建。config.jsonc 被刻意排除在镜像之外,因此任何层都无法携带凭据。x-powered-by 已禁用,配置验证会拒绝未知键和大小写不匹配的值。
CI 中运行 Trivy 扫描,并记录了漏洞策略。multer 和 js-yaml 通过 overrides 固定版本,确保 pnpm audit 和 pnpm audit --prod 均返回无漏洞结果。
平台与依赖
要求 Node.js >= 22.12.0,并通过 packageManager 将 pnpm 固定为 12.3.4。NestJS 从 10 升级到 12,Mongoose 从 8 升级到 9,Zod 从 3 升级到 4,adamant-api 从 2 升级到 3,chalk 从 4 升级到 6。ESLint 升级到 10 并采用扁平配置,TypeScript 升级到 6,Jest 升级到 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 在拉取请求时构建生产镜像,并针对随附的 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 个测试。
重大变更
标准的 4.1.2 config.jsonc 无法在 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。