diff --git a/.env.example b/.env.example index bee770e..4dfcfec 100644 --- a/.env.example +++ b/.env.example @@ -2,13 +2,4 @@ APPLICATION_PORT=3001 APPLICATION_NAME=Service Prices API APPLICATION_DESCRIPTION=Murray Rothbot Service Prices API. -APPLICATION_VERSION=1.0.0 - -POSTGRES_HOST=localhost -POSTGRES_PORT=5432 -POSTGRES_USER=murray -POSTGRES_PASSWORD=1q2w3e4r -POSTGRES_DB=service-murray - -SERVICE_PRICES_URL=http://localhost:3001 -SERVICE_MURRAY_URL=http://localhost:3004 \ No newline at end of file +APPLICATION_VERSION=1.0.0 \ No newline at end of file diff --git a/src/app.controller.ts b/src/app.controller.ts index d3efc45..f781ea4 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -7,7 +7,7 @@ export class AppController { constructor(private readonly appService: AppService) {} @ApiOperation({ - summary: 'This is the health check endpoint.', + summary: 'Checks the health and availability of the API service.', }) @ApiOkResponse({ description: 'The service is healthy.', diff --git a/src/app.module.ts b/src/app.module.ts index 50e9472..37132df 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,9 +7,6 @@ import { AppController } from './app.controller' import { AppService } from './app.service' import { TickerModule } from './domain/ticker/ticker.module' import { ConvertModule } from './domain/convert/convert.module' -import { AlertPriceModule } from './domain/alert-price/alert-price.module' -import { SequelizeModule } from '@nestjs/sequelize' -import { AlertPrice } from './domain/alert-price/alert-price.model' @Module({ imports: [ @@ -18,20 +15,8 @@ import { AlertPrice } from './domain/alert-price/alert-price.model' load: [config], }), ScheduleModule.forRoot(), - SequelizeModule.forRoot({ - dialect: 'postgres', - host: process.env.POSTGRES_HOST, - port: parseInt(process.env.POSTGRES_PORT, 10), - username: process.env.POSTGRES_USER, - password: process.env.POSTGRES_PASSWORD, - database: process.env.POSTGRES_DB, - models: [AlertPrice], - autoLoadModels: true, - logging: false, - }), TickerModule, ConvertModule, - AlertPriceModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/config/swagger.config.ts b/src/config/swagger.config.ts index 5ffbd4b..ec98637 100644 --- a/src/config/swagger.config.ts +++ b/src/config/swagger.config.ts @@ -8,9 +8,9 @@ export const swaggerConfig = async function conf(app: INestApplication): Promise .setTitle(cfgService.get('APPLICATION_NAME', '')) .setDescription(cfgService.get('APPLICATION_DESCRIPTION', '')) .setVersion(cfgService.get('APPLICATION_VERSION', '')) - .addTag('default', 'Default Routes') - .addTag('Tickers', 'Ticker Routes') - .setExternalDoc('Discord Server', 'https://discord.gg/6BfSApvh') + .addTag('default', 'Essential endpoints for monitoring and checking the APIs status.') + .addTag('Tickers', 'Endpoints for financial information and exchange rates.') + .setExternalDoc('Github Repository', 'https://github.com/Murray-Rothbot/service-prices') .setLicense('MIT', 'https://opensource.org/licenses/MIT') .build() diff --git a/src/domain/alert-price/alert-price.controller.ts b/src/domain/alert-price/alert-price.controller.ts deleted file mode 100644 index d6e37d9..0000000 --- a/src/domain/alert-price/alert-price.controller.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Controller, Post, Body, Get, Query } from '@nestjs/common' -import { AlertPriceService } from './alert-price.service' -import { CreateAlertPriceDto, ListAlertPricesDto } from './dto' - -@Controller('alert-price') -export class AlertPriceController { - constructor(private readonly alertPriceService: AlertPriceService) {} - - @Post() - create(@Body() createAlertPriceDto: CreateAlertPriceDto) { - return this.alertPriceService.create(createAlertPriceDto) - } - - @Get() - list(@Query() listAlertPricesDto: ListAlertPricesDto) { - return this.alertPriceService.list(listAlertPricesDto) - } -} diff --git a/src/domain/alert-price/alert-price.model.ts b/src/domain/alert-price/alert-price.model.ts deleted file mode 100644 index 9521b89..0000000 --- a/src/domain/alert-price/alert-price.model.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Column, Model, Table, DataType } from 'sequelize-typescript' - -@Table -export class AlertPrice extends Model { - @Column(DataType.TEXT({ length: 'medium' })) - webhookUrl: string - - @Column - currency: string - - @Column(DataType.FLOAT()) - currentPrice: number - - @Column(DataType.FLOAT()) - price: number - - @Column - above: boolean - - @Column - active: boolean -} diff --git a/src/domain/alert-price/alert-price.module.ts b/src/domain/alert-price/alert-price.module.ts deleted file mode 100644 index 5187389..0000000 --- a/src/domain/alert-price/alert-price.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { AlertPriceController } from './alert-price.controller' -import { Module } from '@nestjs/common' -import { SequelizeModule } from '@nestjs/sequelize' -import { AlertPrice } from './alert-price.model' -import { AlertPriceService } from './alert-price.service' -import { TickerModule } from '../ticker/ticker.module' -import { HttpModule } from '@nestjs/axios' - -@Module({ - imports: [SequelizeModule.forFeature([AlertPrice]), HttpModule, TickerModule], - providers: [AlertPriceService], - controllers: [AlertPriceController], -}) -export class AlertPriceModule {} diff --git a/src/domain/alert-price/alert-price.service.ts b/src/domain/alert-price/alert-price.service.ts deleted file mode 100644 index e5bec8a..0000000 --- a/src/domain/alert-price/alert-price.service.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common' -import { Cron } from '@nestjs/schedule' -import { InjectModel } from '@nestjs/sequelize' -import { Op } from 'sequelize' -import { TickerService } from '../ticker/ticker.service' -import { AlertPrice } from './alert-price.model' -import { CreateAlertPriceDto, ListAlertPricesDto } from './dto' -import { HttpService } from '@nestjs/axios' -import { catchError, lastValueFrom, map } from 'rxjs' - -@Injectable() -export class AlertPriceService { - private readonly logger = new Logger(AlertPriceService.name) - - constructor( - @InjectModel(AlertPrice) - private alertPriceModel: typeof AlertPrice, - private readonly tickerService: TickerService, - protected readonly httpService: HttpService, - ) {} - - async create(data: CreateAlertPriceDto): Promise { - const ticker = await this.tickerService.getTicker({ symbol: `BTC${data.currency}` }) - const priceDifference = data.price - parseFloat(ticker.price) - - //check if alert is already created - const alert = await this.alertPriceModel.findOne({ - where: { - webhookUrl: data.webhookUrl, - currency: data.currency, - price: data.price, - above: priceDifference > 0, - }, - }) - if (alert) return alert - - // create new alert - const newAlertPrice = await this.alertPriceModel.create({ - webhookUrl: data.webhookUrl, - currency: data.currency, - currentPrice: +ticker.price, - price: data.price, - above: priceDifference > 0, - active: true, - }) - return newAlertPrice - } - - async list(data: ListAlertPricesDto): Promise { - return this.alertPriceModel.findAll({ - where: { webhookUrl: data.webhookUrl, active: true }, - order: ['currency', 'price'], - }) - } - - @Cron('*/15 * * * * *') - async checkAlertPrices() { - // get current prices - const currentPrices = await Promise.all([ - this.tickerService.getTicker({ symbol: 'btcusd' }), - this.tickerService.getTicker({ symbol: 'btcbrl' }), - ]) - - // check if any alert is triggered - const triggeredAlerts: AlertPrice[] = [] - for (const currentPrice of currentPrices) { - const aboveAlerts = await this.alertPriceModel.findAll({ - where: { - active: true, - currency: currentPrice.symbol.slice(3), - above: true, - price: { - [Op.lte]: +currentPrice.price, - }, - }, - }) - - const belowAlerts = await this.alertPriceModel.findAll({ - where: { - active: true, - currency: currentPrice.symbol.slice(3), - above: false, - price: { - [Op.gte]: +currentPrice.price, - }, - }, - }) - - if (belowAlerts.length > 0) triggeredAlerts.push(...belowAlerts) - if (aboveAlerts.length > 0) triggeredAlerts.push(...aboveAlerts) - } - - // post triggered alerts to their webhooks - if (triggeredAlerts.length > 0) { - triggeredAlerts.map(async (alert) => { - await lastValueFrom( - this.httpService.post(alert.webhookUrl, alert).pipe( - map(async (response: any) => { - // deactivate triggered alerts - if (response.data.data.message === 'OK') { - await this.deactivateAlertPrice(alert.id) - } - }), - catchError(async () => { - this.logger.error(`ERROR POST ${alert.webhookUrl}`) - return null - }), - ), - ) - }) - } - } - - async deactivateAlertPrice(id: number): Promise { - await this.alertPriceModel.update( - { - active: false, - }, - { - where: { - id, - }, - }, - ) - } -} diff --git a/src/domain/alert-price/dto/create-alert-price.dto.ts b/src/domain/alert-price/dto/create-alert-price.dto.ts deleted file mode 100644 index 246be51..0000000 --- a/src/domain/alert-price/dto/create-alert-price.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IsNotEmpty, IsNumber, IsString, IsUrl } from 'class-validator' - -export class CreateAlertPriceDto { - @IsString() - @IsNotEmpty() - @IsUrl({ require_tld: false }) - webhookUrl: string - - @IsNumber() - @IsNotEmpty() - price: number - - @IsString() - @IsNotEmpty() - currency: string -} diff --git a/src/domain/alert-price/dto/index.ts b/src/domain/alert-price/dto/index.ts deleted file mode 100644 index a76cd55..0000000 --- a/src/domain/alert-price/dto/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './create-alert-price.dto' -export * from './list-alert-prices.dto' diff --git a/src/domain/alert-price/dto/list-alert-prices.dto.ts b/src/domain/alert-price/dto/list-alert-prices.dto.ts deleted file mode 100644 index 5f9b4df..0000000 --- a/src/domain/alert-price/dto/list-alert-prices.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { IsNotEmpty, IsNumber, IsString, IsUrl } from 'class-validator' - -export class ListAlertPricesDto { - @IsString() - @IsNotEmpty() - @IsUrl({ require_tld: false }) - webhookUrl: string -} diff --git a/src/domain/convert/convert.controller.ts b/src/domain/convert/convert.controller.ts index 09840b5..83882d1 100644 --- a/src/domain/convert/convert.controller.ts +++ b/src/domain/convert/convert.controller.ts @@ -8,7 +8,7 @@ export class ConvertController { constructor(private readonly convertService: ConvertService) {} @ApiOperation({ - summary: 'Converts a value from a currency to another.', + summary: ' Converts values between different currencies.', }) @ApiQuery({ name: 'currency', diff --git a/src/domain/ticker/ticker.controller.ts b/src/domain/ticker/ticker.controller.ts index 32d2d1b..2fda5cf 100644 --- a/src/domain/ticker/ticker.controller.ts +++ b/src/domain/ticker/ticker.controller.ts @@ -9,7 +9,7 @@ export class TickerController { constructor(private readonly tickerService: TickerService) {} @ApiOperation({ - summary: 'Get the ticker for a specific pair.', + summary: 'Provides data for a specific currency pair.', }) @ApiQuery({ name: 'symbol', @@ -34,7 +34,7 @@ export class TickerController { } @ApiOperation({ - summary: 'Get the tickers for a list of pairs.', + summary: 'Returns information on multiple currency pairs.', }) @ApiQuery({ name: 'symbol', diff --git a/src/domain/ticker/ticker.service.ts b/src/domain/ticker/ticker.service.ts index 4b50585..0878fe2 100644 --- a/src/domain/ticker/ticker.service.ts +++ b/src/domain/ticker/ticker.service.ts @@ -22,7 +22,6 @@ import { export class TickerService { repositories: Array private readonly logger = new Logger(TickerService.name) - serviceMurrayUrl: string = this.cfgService.get('SERVICE_MURRAY_URL') constructor( protected readonly httpService: HttpService,