feature #13
|
|
@ -33,7 +33,10 @@
|
|||
"pg": "^8.14.1",
|
||||
"typeorm": "^0.3.21",
|
||||
"bcrypt": "^5.1.1",
|
||||
"@types/bcrypt": "^5.0.2"
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"socket.io": "^4.8.1",
|
||||
"@nestjs/websockets": "11.0.12",
|
||||
"@nestjs/platform-socket.io": "11.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { PrometheusService } from './prometheus.service';
|
|||
import { MetricsController } from './metrics.controller';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { MetricsGateway } from './metrics.gateway';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -27,6 +28,10 @@ import { AuthModule } from './auth/auth.module';
|
|||
AuthModule,
|
||||
],
|
||||
controllers: [MetricsController],
|
||||
providers: [PrometheusService],
|
||||
providers: [
|
||||
PrometheusService,
|
||||
MetricsGateway,
|
||||
],
|
||||
exports: [MetricsGateway],
|
||||
})
|
||||
export class AppModule { }
|
||||
11
src/main.ts
11
src/main.ts
|
|
@ -3,11 +3,18 @@ import { AppModule } from './app.module';
|
|||
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule, { cors: false });
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
// Установка глобального префикса для всех маршрутов
|
||||
app.setGlobalPrefix('api');
|
||||
|
||||
//настройка CORS
|
||||
|
||||
|
||||
app.enableCors({
|
||||
origin: '*',
|
||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
|
||||
allowedHeaders: 'Content-Type, Authorization',
|
||||
});
|
||||
await app.listen(process.env.PORT ?? 3000);
|
||||
}
|
||||
bootstrap();
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
import { WebSocketGateway, WebSocketServer, OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect, SubscribeMessage, } from '@nestjs/websockets';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import { PrometheusService } from './prometheus.service';
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
@WebSocketGateway({
|
||||
/*
|
||||
cors: {
|
||||
origin: '*', // В production укажите конкретные домены
|
||||
methods: ['GET', 'POST'],
|
||||
credentials: true
|
||||
}, */
|
||||
namespace: '/api/metrics-ws'
|
||||
})
|
||||
export class MetricsGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
|
||||
@WebSocketServer() server: Server;
|
||||
private readonly logger = new Logger(MetricsGateway.name);
|
||||
private activeSockets: Map<string, Socket> = new Map();
|
||||
|
||||
constructor(private readonly prometheusService: PrometheusService) { }
|
||||
|
||||
afterInit(server: Server) {
|
||||
this.logger.log('WebSocket Gateway initialized');
|
||||
this.logger.log('WebSocket server initialized successfully');
|
||||
}
|
||||
|
||||
handleConnection(client: Socket) {
|
||||
this.logger.log(`Client connected: ${client.id}`);
|
||||
this.logger.log(`New client connected: ${client.id} from ${client.handshake.address}`);
|
||||
this.activeSockets.set(client.id, client);
|
||||
}
|
||||
|
||||
handleDisconnect(client: Socket) {
|
||||
this.logger.log(`Client disconnected: ${client.id}`);
|
||||
this.activeSockets.delete(client.id);
|
||||
}
|
||||
|
||||
@SubscribeMessage('get-metrics')
|
||||
async handleGetMetrics(client: Socket, payload: any) {
|
||||
const { metric, start, end, step, _t } = payload;
|
||||
this.logger.log(`Received metrics request: ${metric}, start: ${start}, end: ${end}, step: ${step}`);
|
||||
|
||||
try {
|
||||
// Для запросов с диапазоном - просто возвращаем данные без подписки
|
||||
if (start && end) {
|
||||
const data = await this.prometheusService.fetchMetricsRange(metric, start, end, step);
|
||||
client.emit('metrics-data', { metric, data });
|
||||
return;
|
||||
}
|
||||
|
||||
// Для запросов без диапазона (realtime) - запускаем подписку
|
||||
const stopUpdates = await this.sendPeriodicUpdates(
|
||||
metric,
|
||||
step || 5000, // Используем переданный шаг или дефолтный
|
||||
client
|
||||
);
|
||||
|
||||
client.on('disconnect', () => stopUpdates());
|
||||
client.on('unsubscribe-metric', () => stopUpdates());
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error(`Error fetching metrics: ${error.message}`);
|
||||
client.emit('metrics-error', {
|
||||
metric,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeMessage('get-metric-types')
|
||||
async handleGetMetricTypes(client: Socket, payload: { metric: string }) {
|
||||
try {
|
||||
const type = await this.prometheusService.fetchMetricType(payload.metric);
|
||||
const description = await this.prometheusService.fetchMetricDescription(payload.metric);
|
||||
client.emit('metric-types', {
|
||||
metric: payload.metric,
|
||||
type,
|
||||
description
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.log(`Error fetching metric types: ${error.message}`);
|
||||
client.emit('metrics-error', {
|
||||
metric: payload.metric,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeMessage('get-all-metrics')
|
||||
async handleGetAllMetrics(client: Socket) {
|
||||
try {
|
||||
const metrics = await this.prometheusService.fetchAllMetrics();
|
||||
client.emit('all-metrics', metrics);
|
||||
} catch (error) {
|
||||
this.logger.log(`Error fetching all metrics: ${error.message}`);
|
||||
client.emit('metrics-error', {
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SubscribeMessage('subscribe-metric')
|
||||
async handleSubscribeMetric(client: Socket, payload: { metric: string, interval?: number }) {
|
||||
const stopUpdates = await this.sendPeriodicUpdates(
|
||||
payload.metric,
|
||||
payload.interval || 5000, // Добавляем значение по умолчанию
|
||||
client // Передаем клиента
|
||||
);
|
||||
|
||||
// Сохраняем функцию остановки для этого клиента
|
||||
client.on('disconnect', () => stopUpdates());
|
||||
client.on('unsubscribe-metric', () => stopUpdates());
|
||||
}
|
||||
|
||||
// Метод для периодической отправки обновлений
|
||||
async sendPeriodicUpdates(metric: string, interval: number, client: Socket) {
|
||||
const timer = setInterval(async () => {
|
||||
try {
|
||||
const data = await this.prometheusService.fetchMetrics(metric);
|
||||
client.emit('metrics-data', { metric, data });
|
||||
} catch (error) {
|
||||
this.logger.error(`Error in periodic update for ${metric}: ${error.message}`);
|
||||
}
|
||||
}, interval);
|
||||
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
this.logger.log(`Stopped updates for ${metric}`);
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue