fix preprod
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
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
This commit is contained in:
parent
850c23c164
commit
bbf059cce9
@ -19,6 +19,7 @@ import { WebhooksModule } from './application/webhooks/webhooks.module';
|
||||
import { GDPRModule } from './application/gdpr/gdpr.module';
|
||||
import { CsvBookingsModule } from './application/csv-bookings.module';
|
||||
import { AdminModule } from './application/admin/admin.module';
|
||||
import { LogsModule } from './application/logs/logs.module';
|
||||
import { SubscriptionsModule } from './application/subscriptions/subscriptions.module';
|
||||
import { ApiKeysModule } from './application/api-keys/api-keys.module';
|
||||
import { CacheModule } from './infrastructure/cache/cache.module';
|
||||
@ -67,6 +68,7 @@ import { CustomThrottlerGuard } from './application/guards/throttle.guard';
|
||||
STRIPE_GOLD_YEARLY_PRICE_ID: Joi.string().optional(),
|
||||
STRIPE_PLATINIUM_MONTHLY_PRICE_ID: Joi.string().optional(),
|
||||
STRIPE_PLATINIUM_YEARLY_PRICE_ID: Joi.string().optional(),
|
||||
LOG_EXPORTER_URL: Joi.string().uri().default('http://xpeditis-log-exporter:3200'),
|
||||
}),
|
||||
}),
|
||||
|
||||
@ -147,6 +149,7 @@ import { CustomThrottlerGuard } from './application/guards/throttle.guard';
|
||||
AdminModule,
|
||||
SubscriptionsModule,
|
||||
ApiKeysModule,
|
||||
LogsModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [
|
||||
|
||||
98
apps/backend/src/application/logs/logs.controller.ts
Normal file
98
apps/backend/src/application/logs/logs.controller.ts
Normal file
@ -0,0 +1,98 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
apps/backend/src/application/logs/logs.module.ts
Normal file
9
apps/backend/src/application/logs/logs.module.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { LogsController } from './logs.controller';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
controllers: [LogsController],
|
||||
})
|
||||
export class LogsModule {}
|
||||
@ -12,8 +12,8 @@ import {
|
||||
Server,
|
||||
} from 'lucide-react';
|
||||
|
||||
const LOG_EXPORTER_URL =
|
||||
process.env.NEXT_PUBLIC_LOG_EXPORTER_URL || 'http://localhost:3200';
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000';
|
||||
const LOGS_API_URL = `${API_URL}/api/v1/logs`;
|
||||
|
||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
@ -125,7 +125,7 @@ export default function AdminLogsPage() {
|
||||
|
||||
// Load available services
|
||||
useEffect(() => {
|
||||
fetch(`${LOG_EXPORTER_URL}/api/logs/services`)
|
||||
fetch(`${LOGS_API_URL}/services`)
|
||||
.then(r => r.json())
|
||||
.then(d => setServices(d.services || []))
|
||||
.catch(() => {});
|
||||
@ -151,7 +151,7 @@ export default function AdminLogsPage() {
|
||||
setError(null);
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${LOG_EXPORTER_URL}/api/logs/export?${buildQueryString('json')}`,
|
||||
`${LOGS_API_URL}/export?${buildQueryString('json')}`,
|
||||
);
|
||||
if (!res.ok) {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
@ -175,7 +175,7 @@ export default function AdminLogsPage() {
|
||||
setExportLoading(true);
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${LOG_EXPORTER_URL}/api/logs/export?${buildQueryString(format)}`,
|
||||
`${LOGS_API_URL}/export?${buildQueryString(format)}`,
|
||||
);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const blob = await res.blob();
|
||||
@ -384,8 +384,7 @@ export default function AdminLogsPage() {
|
||||
Impossible de contacter le log-exporter : <strong>{error}</strong>
|
||||
<br />
|
||||
<span className="text-xs text-red-500">
|
||||
Vérifiez que le container log-exporter est démarré sur{' '}
|
||||
<code className="font-mono">{LOG_EXPORTER_URL}</code>
|
||||
Vérifiez que le backend et le log-exporter sont démarrés.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -1,53 +1,43 @@
|
||||
server:
|
||||
http_listen_port: 9080
|
||||
grpc_listen_port: 0
|
||||
log_level: warn
|
||||
|
||||
positions:
|
||||
filename: /tmp/positions.yaml
|
||||
|
||||
clients:
|
||||
- url: http://loki:3100/loki/api/v1/push
|
||||
- url: http://xpeditis-loki:3100/loki/api/v1/push
|
||||
batchwait: 1s
|
||||
batchsize: 1048576
|
||||
timeout: 10s
|
||||
|
||||
scrape_configs:
|
||||
# ─── Docker container log collection (Mac-compatible via Docker socket API) ─
|
||||
- job_name: docker
|
||||
docker_sd_configs:
|
||||
- host: unix:///var/run/docker.sock
|
||||
refresh_interval: 5s
|
||||
filters:
|
||||
# Only collect containers with label: logging=promtail
|
||||
# Add this label to backend and frontend in docker-compose.dev.yml
|
||||
- name: label
|
||||
values: ['logging=promtail']
|
||||
|
||||
relabel_configs:
|
||||
# Use docker-compose service name as the "service" label
|
||||
- source_labels: ['__meta_docker_container_label_com_docker_compose_service']
|
||||
target_label: service
|
||||
# Keep container name for context
|
||||
- source_labels: ['__meta_docker_container_name']
|
||||
regex: '/?(.*)'
|
||||
replacement: '${1}'
|
||||
target_label: container
|
||||
# Log stream (stdout / stderr)
|
||||
- source_labels: ['__meta_docker_container_log_stream']
|
||||
target_label: stream
|
||||
|
||||
pipeline_stages:
|
||||
# Drop entries older than 15 min to avoid replaying full container log history
|
||||
- drop:
|
||||
older_than: 15m
|
||||
drop_counter_reason: entry_too_old
|
||||
|
||||
# Drop noisy health-check / ping lines
|
||||
- drop:
|
||||
expression: 'GET /(health|metrics|minio/health)'
|
||||
|
||||
# Try to parse JSON (NestJS/pino output)
|
||||
- json:
|
||||
expressions:
|
||||
level: level
|
||||
@ -55,12 +45,10 @@ scrape_configs:
|
||||
context: context
|
||||
reqId: reqId
|
||||
|
||||
# Promote parsed fields as Loki labels
|
||||
- labels:
|
||||
level:
|
||||
context:
|
||||
|
||||
# Map pino numeric levels to strings
|
||||
- template:
|
||||
source: level
|
||||
template: >-
|
||||
|
||||
Loading…
Reference in New Issue
Block a user