Some checks failed
CD Preprod / Backend — Lint (push) Successful in 10m22s
CD Preprod / Frontend — Lint & Type-check (push) Successful in 10m55s
CD Preprod / Backend — Unit Tests (push) Successful in 10m12s
CD Preprod / Frontend — Unit Tests (push) Successful in 10m33s
CD Preprod / Backend — Integration Tests (push) Successful in 9m57s
CD Preprod / Build Backend (push) Successful in 7m51s
CD Preprod / Build Log Exporter (push) Successful in 34s
CD Preprod / Build Frontend (push) Successful in 19m46s
CD Preprod / Deploy to Preprod (push) Failing after 1s
CD Preprod / Notify Failure (push) Has been skipped
CD Preprod / Smoke Tests (push) Has been skipped
CD Preprod / Notify Success (push) Has been skipped
99 lines
2.7 KiB
TypeScript
99 lines
2.7 KiB
TypeScript
import {
|
|
Controller,
|
|
Get,
|
|
Query,
|
|
Res,
|
|
UseGuards,
|
|
HttpException,
|
|
HttpStatus,
|
|
} from '@nestjs/common';
|
|
import { Response } from 'express';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
|
|
import { RolesGuard } from '../guards/roles.guard';
|
|
import { Roles } from '../decorators/roles.decorator';
|
|
|
|
@Controller('logs')
|
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
@Roles('admin')
|
|
export class LogsController {
|
|
private readonly logExporterUrl: string;
|
|
|
|
constructor(private readonly configService: ConfigService) {
|
|
this.logExporterUrl = this.configService.get<string>(
|
|
'LOG_EXPORTER_URL',
|
|
'http://xpeditis-log-exporter:3200',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/logs/services
|
|
* Proxy → log-exporter /api/logs/services
|
|
*/
|
|
@Get('services')
|
|
async getServices() {
|
|
try {
|
|
const res = await fetch(`${this.logExporterUrl}/api/logs/services`, {
|
|
signal: AbortSignal.timeout(5000),
|
|
});
|
|
if (!res.ok) throw new Error(`log-exporter error: ${res.status}`);
|
|
return res.json();
|
|
} catch (err: any) {
|
|
throw new HttpException(
|
|
{ error: err.message },
|
|
HttpStatus.BAD_GATEWAY,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/logs/export
|
|
* Proxy → log-exporter /api/logs/export (JSON or CSV)
|
|
*/
|
|
@Get('export')
|
|
async exportLogs(
|
|
@Query('service') service: string,
|
|
@Query('level') level: string,
|
|
@Query('search') search: string,
|
|
@Query('start') start: string,
|
|
@Query('end') end: string,
|
|
@Query('limit') limit: string,
|
|
@Query('format') format: string = 'json',
|
|
@Res() res: Response,
|
|
) {
|
|
try {
|
|
const params = new URLSearchParams();
|
|
if (service) params.set('service', service);
|
|
if (level) params.set('level', level);
|
|
if (search) params.set('search', search);
|
|
if (start) params.set('start', start);
|
|
if (end) params.set('end', end);
|
|
if (limit) params.set('limit', limit);
|
|
params.set('format', format);
|
|
|
|
const upstream = await fetch(
|
|
`${this.logExporterUrl}/api/logs/export?${params}`,
|
|
{ signal: AbortSignal.timeout(30000) },
|
|
);
|
|
|
|
if (!upstream.ok) {
|
|
const body = await upstream.json().catch(() => ({}));
|
|
throw new HttpException(body, upstream.status);
|
|
}
|
|
|
|
res.status(upstream.status);
|
|
upstream.headers.forEach((value, key) => {
|
|
if (['content-type', 'content-disposition'].includes(key.toLowerCase())) {
|
|
res.setHeader(key, value);
|
|
}
|
|
});
|
|
|
|
const buffer = await upstream.arrayBuffer();
|
|
res.send(Buffer.from(buffer));
|
|
} catch (err: any) {
|
|
if (err instanceof HttpException) throw err;
|
|
throw new HttpException({ error: err.message }, HttpStatus.BAD_GATEWAY);
|
|
}
|
|
}
|
|
}
|