diff --git a/harvest-finance/backend/src/farm-vaults/farm-vaults.controller.ts b/harvest-finance/backend/src/farm-vaults/farm-vaults.controller.ts index 8a89014f7..d16cea6ee 100644 --- a/harvest-finance/backend/src/farm-vaults/farm-vaults.controller.ts +++ b/harvest-finance/backend/src/farm-vaults/farm-vaults.controller.ts @@ -7,25 +7,29 @@ import { UseGuards, Request, } from '@nestjs/common'; -import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; +import { ApiTags, ApiOperation, ApiBearerAuth, ApiProperty } from '@nestjs/swagger'; import { IsNumber, IsString, IsUUID, Min } from 'class-validator'; import { Throttle } from '@nestjs/throttler'; import { FarmVaultsService } from './farm-vaults.service'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; class CreateFarmVaultDto { + @ApiProperty({ description: 'Vault name' }) @IsString() name: string; + @ApiProperty({ description: 'Crop cycle ID' }) @IsUUID() cropCycleId: string; + @ApiProperty({ description: 'Target amount' }) @IsNumber() @Min(0) targetAmount: number; } class FarmVaultAmountDto { + @ApiProperty({ description: 'Amount to deposit/withdraw' }) @IsNumber() @Min(0.01) amount: number; diff --git a/harvest-finance/backend/src/health/health.controller.ts b/harvest-finance/backend/src/health/health.controller.ts index 309745dc0..c213de89e 100644 --- a/harvest-finance/backend/src/health/health.controller.ts +++ b/harvest-finance/backend/src/health/health.controller.ts @@ -3,11 +3,17 @@ import { HealthCheck, HealthCheckService, TypeOrmHealthIndicator, - HealthCheckError, + HealthIndicator, HealthIndicatorResult, } from '@nestjs/terminus'; import { SkipThrottle } from '@nestjs/throttler'; +import { + ApiTags, + ApiOperation, + ApiResponse, +} from '@nestjs/swagger'; import { StellarClientService } from '../stellar/services/stellar-client.service'; +import { RedisHealthIndicator } from './redis.health'; @SkipThrottle() @Controller('health') @@ -16,6 +22,7 @@ export class HealthController { private health: HealthCheckService, private db: TypeOrmHealthIndicator, private stellarClient: StellarClientService, + private redis: RedisHealthIndicator, ) {} @Get() @@ -23,16 +30,14 @@ export class HealthController { check() { return this.health.check([ () => this.db.pingCheck('database', { timeout: 1500 }), + () => this.redis.isHealthy('redis', 3000), async (): Promise => { - const streamHealth = this.stellarClient.getStreamHealth(); - if (!streamHealth.isConnected) { - throw new HealthCheckError('Stellar stream is disconnected', { - 'stellar-payment-stream': streamHealth, - }); + try { + await this.stellarClient.checkHorizon(3000); + return { 'stellar-horizon': { status: 'up' } }; + } catch { + return { 'stellar-horizon': { status: 'down' } }; } - return { - 'stellar-payment-stream': streamHealth, - }; }, ]); } diff --git a/harvest-finance/backend/src/health/redis.health.ts b/harvest-finance/backend/src/health/redis.health.ts new file mode 100644 index 000000000..588327391 --- /dev/null +++ b/harvest-finance/backend/src/health/redis.health.ts @@ -0,0 +1,21 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { HealthIndicator, HealthIndicatorResult, HealthCheckError } from '@nestjs/terminus'; +import Redis from 'ioredis'; + +@Injectable() +export class RedisHealthIndicator extends HealthIndicator { + constructor(@Inject('REDIS_CLIENT') private readonly client: Redis) {} + + async isHealthy(key = 'redis', timeout = 3000): Promise { + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Redis timeout')), timeout), + ); + const pingPromise = this.client.ping(); + try { + await Promise.race([pingPromise, timeoutPromise]); + return this.getStatus(key, true); + } catch { + throw new HealthCheckError('Redis is unavailable', { [key]: { status: 'down' } }); + } + } +} \ No newline at end of file diff --git a/harvest-finance/backend/src/main.ts b/harvest-finance/backend/src/main.ts index 1f62c9d1a..6eced96d1 100644 --- a/harvest-finance/backend/src/main.ts +++ b/harvest-finance/backend/src/main.ts @@ -134,8 +134,21 @@ async function bootstrap() { customSiteTitle: 'Harvest Finance API Docs', }); + // Serve raw OpenAPI JSON at /api/docs-json + app.use('/api/docs-json', (req, res) => { + res.json(document); + }); + const configService = app.get(ConfigService); const port = configService.get('PORT') || 5000; + const isProduction = configService.get('NODE_ENV') === 'production'; + + // Serve raw OpenAPI JSON and Swagger UI in non-production environments + if (!isProduction) { + app.getHttp().getRouter().get('/api/docs-json', (req, res) => { + res.json(document); + }); + } const server = await app.listen(port); console.log(`Application is running on: http://localhost:${port}`);