diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 36e2daa..e75fa5b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -21,6 +21,7 @@ import { PatientSupportsModule } from './http/patient-supports/patient-supports. import { PatientsModule } from './http/patients/patients.module'; import { ReferralsModule } from './http/referrals/referrals.module'; import { StatisticsModule } from './http/statistics/statistics.module'; +import { StatusModule } from './http/status/status.module'; import { UsersModule } from './http/users/users.module'; import { StorageModule } from './storage/storage.module'; @@ -71,6 +72,7 @@ import { StorageModule } from './storage/storage.module'; PatientRequirementsModule, PatientSupportsModule, StorageModule, + StatusModule, ], providers: [ { provide: APP_GUARD, useClass: LogGuard }, diff --git a/src/app/http/status/status.controller.ts b/src/app/http/status/status.controller.ts new file mode 100644 index 0000000..091cac7 --- /dev/null +++ b/src/app/http/status/status.controller.ts @@ -0,0 +1,31 @@ +import { Controller, Get, Res } from '@nestjs/common'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { Response } from 'express'; + +import { Public } from '@/common/decorators/public.decorator'; +import { Log } from '@/common/log/log.decorator'; + +import { GetStatusResponse } from './status.dtos'; +import { GetStatusUseCase } from './use-cases/get-status.use-case'; + +@ApiTags('Status') +@Controller() +export class StatusController { + constructor(private readonly getStatusUseCase: GetStatusUseCase) {} + + @Public() + @Get('status') + @Log('get_status') + @ApiOperation({ summary: 'Verifica o status do sistema e do banco de dados' }) + @ApiResponse({ type: GetStatusResponse }) + async getStatus(@Res() res: Response): Promise { + const result = await this.getStatusUseCase.execute(); + + if (!result.success) { + res.status(503).json(result); + return; + } + + res.json(result); + } +} diff --git a/src/app/http/status/status.dtos.ts b/src/app/http/status/status.dtos.ts new file mode 100644 index 0000000..aacea54 --- /dev/null +++ b/src/app/http/status/status.dtos.ts @@ -0,0 +1,5 @@ +import { createZodDto } from 'nestjs-zod'; + +import { getStatusResponseSchema } from '@/domain/schemas/status/responses'; + +export class GetStatusResponse extends createZodDto(getStatusResponseSchema) {} diff --git a/src/app/http/status/status.module.ts b/src/app/http/status/status.module.ts new file mode 100644 index 0000000..e159765 --- /dev/null +++ b/src/app/http/status/status.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; + +import { StatusController } from './status.controller'; +import { GetStatusUseCase } from './use-cases/get-status.use-case'; + +@Module({ + controllers: [StatusController], + providers: [GetStatusUseCase], +}) +export class StatusModule {} diff --git a/src/app/http/status/use-cases/get-status.use-case.ts b/src/app/http/status/use-cases/get-status.use-case.ts new file mode 100644 index 0000000..d3194d7 --- /dev/null +++ b/src/app/http/status/use-cases/get-status.use-case.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@nestjs/common'; +import { InjectDataSource } from '@nestjs/typeorm'; +import { DataSource } from 'typeorm'; + +import { Log } from '@/common/log/log.decorator'; +import { LogService } from '@/common/log/log.service'; + +import { GetStatusResponse } from '../status.dtos'; + +@Log() +@Injectable() +export class GetStatusUseCase { + constructor( + @InjectDataSource() + private readonly dataSource: DataSource, + private readonly logger: LogService, + ) {} + + async execute(): Promise { + let success = true; + let message = 'Sistema está operacional.'; + + const data: GetStatusResponse['data'] = { + api: { status: 'ok' }, + database: { status: 'ok' }, + }; + + try { + await this.dataSource.query('SELECT 1'); + } catch { + success = false; + message = 'O banco de dados está indisponível.'; + data.database.status = 'error'; + this.logger.error('Database health check failed'); + } + + return { success, message, data }; + } +} diff --git a/src/common/types.d.ts b/src/common/types.d.ts index 3583b95..2bffc41 100644 --- a/src/common/types.d.ts +++ b/src/common/types.d.ts @@ -42,6 +42,8 @@ type UsersEvent = | 'deactivate_user' | 'update_user'; +type StatusEvent = 'get_status'; + export type Event = | AppointmentsEvent | AuthEvent @@ -49,4 +51,5 @@ export type Event = | PatientSupportsEvent | PatientsEvent | ReferralsEvent + | StatusEvent | UsersEvent; diff --git a/src/domain/schemas/status/responses.ts b/src/domain/schemas/status/responses.ts new file mode 100644 index 0000000..85b01fa --- /dev/null +++ b/src/domain/schemas/status/responses.ts @@ -0,0 +1,12 @@ +import { z } from 'zod'; + +import { baseResponseSchema } from '../base'; + +const statusSchema = z.enum(['ok', 'error']); + +export const getStatusResponseSchema = baseResponseSchema.extend({ + data: z.object({ + api: z.object({ status: statusSchema }), + database: z.object({ status: statusSchema }), + }), +});