From d91f947ab0980fb275e61ec9bdd4150b0de12f2b Mon Sep 17 00:00:00 2001 From: Tanguy Herbron Date: Sat, 6 Jan 2024 19:06:07 +0100 Subject: [PATCH] Feature/Add Coingecko api keys support (#2827) * Add CoinGecko API keys support * Update changelog --- CHANGELOG.md | 5 +++ README.md | 30 +++++++++-------- .../configuration/configuration.service.ts | 2 ++ .../coingecko/coingecko.service.ts | 33 +++++++++++++++---- .../interfaces/environment.interface.ts | 2 ++ 5 files changed, 51 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 668e0efd..9bea61f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added support to set a _CoinGecko_ Demo API key via environment variable (`API_KEY_COINGECKO_DEMO`) +- Added support to set a _CoinGecko_ Pro API key via environment variable (`API_KEY_COINGECKO_PRO`) + ### Changed - Removed the `AccountType` enum diff --git a/README.md b/README.md index 0ea5eef2..af5a7a8c 100644 --- a/README.md +++ b/README.md @@ -87,20 +87,22 @@ We provide official container images hosted on [Docker Hub](https://hub.docker.c ### Supported Environment Variables -| Name | Default Value | Description | -| ------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| `ACCESS_TOKEN_SALT` | | A random string used as salt for access tokens | -| `DATABASE_URL` | | The database connection URL, e.g. `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?sslmode=prefer` | -| `HOST` | `0.0.0.0` | The host where the Ghostfolio application will run on | -| `JWT_SECRET_KEY` | | A random string used for _JSON Web Tokens_ (JWT) | -| `PORT` | `3333` | The port where the Ghostfolio application will run on | -| `POSTGRES_DB` | | The name of the _PostgreSQL_ database | -| `POSTGRES_PASSWORD` | | The password of the _PostgreSQL_ database | -| `POSTGRES_USER` | | The user of the _PostgreSQL_ database | -| `REDIS_HOST` | | The host where _Redis_ is running | -| `REDIS_PASSWORD` | | The password of _Redis_ | -| `REDIS_PORT` | | The port where _Redis_ is running | -| `REQUEST_TIMEOUT` | `2000` | The timeout of network requests to data providers in milliseconds | +| Name | Default Value | Description | +| ------------------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `ACCESS_TOKEN_SALT` | | A random string used as salt for access tokens | +| `API_KEY_COINGECKO_DEMO` |   | The _CoinGecko_ Demo API key | +| `API_KEY_COINGECKO_PRO` |   | The _CoinGecko_ Pro API | +| `DATABASE_URL` | | The database connection URL, e.g. `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?sslmode=prefer` | +| `HOST` | `0.0.0.0` | The host where the Ghostfolio application will run on | +| `JWT_SECRET_KEY` | | A random string used for _JSON Web Tokens_ (JWT) | +| `PORT` | `3333` | The port where the Ghostfolio application will run on | +| `POSTGRES_DB` | | The name of the _PostgreSQL_ database | +| `POSTGRES_PASSWORD` | | The password of the _PostgreSQL_ database | +| `POSTGRES_USER` | | The user of the _PostgreSQL_ database | +| `REDIS_HOST` | | The host where _Redis_ is running | +| `REDIS_PASSWORD` | | The password of _Redis_ | +| `REDIS_PORT` | | The port where _Redis_ is running | +| `REQUEST_TIMEOUT` | `2000` | The timeout of network requests to data providers in milliseconds | ### Run with Docker Compose diff --git a/apps/api/src/services/configuration/configuration.service.ts b/apps/api/src/services/configuration/configuration.service.ts index 5b76055c..c9cf8625 100644 --- a/apps/api/src/services/configuration/configuration.service.ts +++ b/apps/api/src/services/configuration/configuration.service.ts @@ -12,6 +12,8 @@ export class ConfigurationService { this.environmentConfiguration = cleanEnv(process.env, { ACCESS_TOKEN_SALT: str(), ALPHA_VANTAGE_API_KEY: str({ default: '' }), + API_KEY_COINGECKO_DEMO: str({ default: '' }), + API_KEY_COINGECKO_PRO: str({ default: '' }), BETTER_UPTIME_API_KEY: str({ default: '' }), CACHE_QUOTES_TTL: num({ default: 1 }), CACHE_TTL: num({ default: 1 }), diff --git a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts index d2156ed3..a12c1bb2 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -17,15 +17,30 @@ import { SymbolProfile } from '@prisma/client'; import { format, fromUnixTime, getUnixTime } from 'date-fns'; -import got from 'got'; +import got, { Headers } from 'got'; @Injectable() export class CoinGeckoService implements DataProviderInterface { - private readonly URL = 'https://api.coingecko.com/api/v3'; + private readonly apiUrl: string; + private readonly headers: Headers = {}; public constructor( private readonly configurationService: ConfigurationService - ) {} + ) { + const apiKeyDemo = this.configurationService.get('API_KEY_COINGECKO_DEMO'); + const apiKeyPro = this.configurationService.get('API_KEY_COINGECKO_PRO'); + + this.apiUrl = 'https://api.coingecko.com/api/v3'; + + if (apiKeyDemo) { + this.headers['x-cg-demo-api-key'] = apiKeyDemo; + } + + if (apiKeyPro) { + this.apiUrl = 'https://pro-api.coingecko.com/api/v3'; + this.headers['x-cg-pro-api-key'] = apiKeyPro; + } + } public canHandle(symbol: string) { return true; @@ -49,7 +64,8 @@ export class CoinGeckoService implements DataProviderInterface { abortController.abort(); }, this.configurationService.get('REQUEST_TIMEOUT')); - const { name } = await got(`${this.URL}/coins/${aSymbol}`, { + const { name } = await got(`${this.apiUrl}/coins/${aSymbol}`, { + headers: this.headers, // @ts-ignore signal: abortController.signal }).json(); @@ -101,11 +117,12 @@ export class CoinGeckoService implements DataProviderInterface { const { prices } = await got( `${ - this.URL + this.apiUrl }/coins/${aSymbol}/market_chart/range?vs_currency=${DEFAULT_CURRENCY.toLowerCase()}&from=${getUnixTime( from )}&to=${getUnixTime(to)}`, { + headers: this.headers, // @ts-ignore signal: abortController.signal } @@ -163,10 +180,11 @@ export class CoinGeckoService implements DataProviderInterface { }, requestTimeout); const quotes = await got( - `${this.URL}/simple/price?ids=${symbols.join( + `${this.apiUrl}/simple/price?ids=${symbols.join( ',' )}&vs_currencies=${DEFAULT_CURRENCY.toLowerCase()}`, { + headers: this.headers, // @ts-ignore signal: abortController.signal } @@ -216,7 +234,8 @@ export class CoinGeckoService implements DataProviderInterface { abortController.abort(); }, this.configurationService.get('REQUEST_TIMEOUT')); - const { coins } = await got(`${this.URL}/search?query=${query}`, { + const { coins } = await got(`${this.apiUrl}/search?query=${query}`, { + headers: this.headers, // @ts-ignore signal: abortController.signal }).json(); diff --git a/apps/api/src/services/interfaces/environment.interface.ts b/apps/api/src/services/interfaces/environment.interface.ts index a410070a..c4baee3b 100644 --- a/apps/api/src/services/interfaces/environment.interface.ts +++ b/apps/api/src/services/interfaces/environment.interface.ts @@ -3,6 +3,8 @@ import { CleanedEnvAccessors } from 'envalid'; export interface Environment extends CleanedEnvAccessors { ACCESS_TOKEN_SALT: string; ALPHA_VANTAGE_API_KEY: string; + API_KEY_COINGECKO_DEMO: string; + API_KEY_COINGECKO_PRO: string; BETTER_UPTIME_API_KEY: string; CACHE_QUOTES_TTL: number; CACHE_TTL: number;