swagger #40
3
.env
3
.env
|
|
@ -28,6 +28,9 @@ COOKIE_SAME_SITE=lax
|
||||||
RANGES_API_URL=http://192.168.2.39:9999
|
RANGES_API_URL=http://192.168.2.39:9999
|
||||||
RANGES_API_ENDPOINT=/api/ranges/9999
|
RANGES_API_ENDPOINT=/api/ranges/9999
|
||||||
|
|
||||||
|
FORMULA_API_URL=http://192.168.2.39:9999
|
||||||
|
FORMULA_API_ENDPOINT=/api/integration/7777
|
||||||
|
|
||||||
# ClickHouse
|
# ClickHouse
|
||||||
CLICKHOUSE_HOST=http://192.168.2.37:8123
|
CLICKHOUSE_HOST=http://192.168.2.37:8123
|
||||||
CLICKHOUSE_USER=vlad
|
CLICKHOUSE_USER=vlad
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ async function bootstrap() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Настройка CORS
|
// Настройка CORS
|
||||||
app.enableCors({//ПОСТАВИТЬ ПРОКСИ, ЧТОБЫ КОРС НЕ РУГАЛСЯ, ИЗМЕНЕНИЕ ПОЛИТИКИ СЕТЕВЫХ ПАКЕТОВ. ПИШУ IP СВОЙ, А ПОРТ ПРОКСИ. REVERSE PROXY.
|
app.enableCors({
|
||||||
origin: [process.env.FRONTEND_URL, "http://dev.msf.enode"],
|
origin: [process.env.FRONTEND_URL, "http://dev.msf.enode"],
|
||||||
credentials: true,
|
credentials: true,
|
||||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
|
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// controllers/enriched-formula.controller.ts
|
||||||
|
import { Controller, Get, Param } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||||
|
import { FormulaEnrichmentService } from './formula-enrichment.service';
|
||||||
|
import { EnrichedFormulaMetric } from './formula.interface';
|
||||||
|
|
||||||
|
@ApiTags('Enriched Formulas')
|
||||||
|
@Controller('enriched-formulas')
|
||||||
|
export class EnrichedFormulaController {
|
||||||
|
constructor(
|
||||||
|
private readonly formulaEnrichmentService: FormulaEnrichmentService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@ApiOperation({ summary: 'Получить все формулы с обогащенными данными' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: 'Список формул с обогащенными метриками'
|
||||||
|
})
|
||||||
|
async getAllEnrichedFormulas(): Promise<EnrichedFormulaMetric[]> {
|
||||||
|
return this.formulaEnrichmentService.getAllEnrichedFormulas();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ summary: 'Получить конкретную формулу с обогащенными данными' })
|
||||||
|
@ApiParam({ name: 'id', description: 'ID формулы' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: 'Формула с обогащенными метриками'
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 404,
|
||||||
|
description: 'Формула не найдена'
|
||||||
|
})
|
||||||
|
async getEnrichedFormula(@Param('id') id: string): Promise<EnrichedFormulaMetric> {
|
||||||
|
return this.formulaEnrichmentService.getEnrichedFormula(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,307 @@
|
||||||
|
// services/formula-enrichment.service.ts
|
||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { PrometheusService } from '../prometheus/prometheus.service';
|
||||||
|
import { FormulaService } from './formula.service';
|
||||||
|
import {
|
||||||
|
FormulaMetric,
|
||||||
|
EnrichedFormulaMetric,
|
||||||
|
EnrichedMetric
|
||||||
|
} from './formula.interface';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FormulaEnrichmentService {
|
||||||
|
private readonly logger = new Logger(FormulaEnrichmentService.name);
|
||||||
|
|
||||||
|
// Конфигурируемые префиксы
|
||||||
|
private readonly metricPrefixes = ['zvks_', 'server_', 'application_'];
|
||||||
|
private readonly defaultPrefix = 'zvks_';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly prometheusService: PrometheusService,
|
||||||
|
private readonly formulaService: FormulaService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Автоматически определяет имя метрики в Prometheus
|
||||||
|
*/
|
||||||
|
private resolveMetricName(originalName: string): string {
|
||||||
|
// Если имя уже содержит префикс, используем как есть
|
||||||
|
if (this.metricPrefixes.some(prefix => originalName.startsWith(prefix))) {
|
||||||
|
return originalName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Иначе добавляем дефолтный префикс
|
||||||
|
return `${this.defaultPrefix}${originalName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ищет метрику в Prometheus с учетом различных вариантов имен
|
||||||
|
*/
|
||||||
|
private async findMetricInPrometheus(originalName: string): Promise<{
|
||||||
|
prometheusName: string;
|
||||||
|
metrics: any[];
|
||||||
|
found: boolean;
|
||||||
|
}> {
|
||||||
|
const possibleNames = [
|
||||||
|
originalName, // как есть
|
||||||
|
this.resolveMetricName(originalName), // с дефолтным префиксом
|
||||||
|
// Можно добавить другие варианты если нужно
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const metricName of possibleNames) {
|
||||||
|
try {
|
||||||
|
this.logger.debug(`Trying to find metric: ${metricName}`);
|
||||||
|
const metrics = await this.prometheusService.fetchMetrics(metricName);
|
||||||
|
|
||||||
|
if (metrics && metrics.length > 0) {
|
||||||
|
this.logger.debug(`Found metric: ${metricName} with ${metrics.length} entries`);
|
||||||
|
return {
|
||||||
|
prometheusName: metricName,
|
||||||
|
metrics,
|
||||||
|
found: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.debug(`Metric ${metricName} not found: ${error.message}`);
|
||||||
|
// Продолжаем поиск с следующим вариантом
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
prometheusName: this.resolveMetricName(originalName),
|
||||||
|
metrics: [],
|
||||||
|
found: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить обогащенные данные формулы по ID
|
||||||
|
*/
|
||||||
|
async getEnrichedFormula(id: string): Promise<EnrichedFormulaMetric> {
|
||||||
|
const formulaData = await this.formulaService.getFormulaData(id);
|
||||||
|
|
||||||
|
if (!formulaData || !formulaData.values) {
|
||||||
|
throw new Error(`Formula data not found for id: ${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const enrichedMetrics = await this.enrichMetrics(formulaData.values.statusarr);
|
||||||
|
const { parsedFormula, humanReadableFormula } = this.parseFormula(
|
||||||
|
formulaData.formula,
|
||||||
|
enrichedMetrics
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...formulaData,
|
||||||
|
enrichedMetrics,
|
||||||
|
parsedFormula,
|
||||||
|
humanReadableFormula,
|
||||||
|
metadata: {
|
||||||
|
totalMetrics: enrichedMetrics.length,
|
||||||
|
foundMetrics: enrichedMetrics.filter(m => m.found).length,
|
||||||
|
missingMetrics: enrichedMetrics.filter(m => !m.found).length
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обогащает массив метрик данными из Prometheus
|
||||||
|
*/
|
||||||
|
private async enrichMetrics(metricNames: string[]): Promise<EnrichedMetric[]> {
|
||||||
|
const enrichmentPromises = metricNames.map(async (originalName) => {
|
||||||
|
try {
|
||||||
|
const { prometheusName, metrics, found } = await this.findMetricInPrometheus(originalName);
|
||||||
|
|
||||||
|
if (found && metrics.length > 0) {
|
||||||
|
const metric = metrics[0]; // Берем первую метрику как пример
|
||||||
|
const description = metric.description || await this.getMetricDescription(prometheusName);
|
||||||
|
|
||||||
|
return {
|
||||||
|
originalName,
|
||||||
|
prometheusName,
|
||||||
|
description,
|
||||||
|
currentValue: metric.value,
|
||||||
|
device: metric.device,
|
||||||
|
source_id: metric.source_id,
|
||||||
|
timestamp: metric.timestamp,
|
||||||
|
type: metric.type,
|
||||||
|
found: true,
|
||||||
|
valuesCount: metrics.length
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Метрика не найдена
|
||||||
|
const description = await this.getMetricDescription(prometheusName);
|
||||||
|
return {
|
||||||
|
originalName,
|
||||||
|
prometheusName,
|
||||||
|
description: description || 'Метрика не найдена в Prometheus',
|
||||||
|
currentValue: undefined,
|
||||||
|
found: false,
|
||||||
|
valuesCount: 0,
|
||||||
|
error: `Метрика не найдена. Проверенные имена: ${prometheusName}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error enriching metric ${originalName}:`, error);
|
||||||
|
return {
|
||||||
|
originalName,
|
||||||
|
prometheusName: this.resolveMetricName(originalName),
|
||||||
|
description: `Ошибка при получении данных: ${error.message}`,
|
||||||
|
currentValue: undefined,
|
||||||
|
found: false,
|
||||||
|
valuesCount: 0,
|
||||||
|
error: error.message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(enrichmentPromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получает описание метрики
|
||||||
|
*/
|
||||||
|
private async getMetricDescription(metricName: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
const description = await this.prometheusService.fetchMetricDescription(metricName);
|
||||||
|
return description || 'Описание недоступно';
|
||||||
|
} catch (error) {
|
||||||
|
return 'Описание недоступно';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Парсит формулу для лучшего отображения
|
||||||
|
*/
|
||||||
|
private parseFormula(
|
||||||
|
formula: string,
|
||||||
|
enrichedMetrics: EnrichedMetric[]
|
||||||
|
): { parsedFormula: string; humanReadableFormula: string } {
|
||||||
|
let humanReadableFormula = formula;
|
||||||
|
|
||||||
|
// Заменяем statusarr[index] на описания метрик
|
||||||
|
enrichedMetrics.forEach((metric, index) => {
|
||||||
|
const arrayIndex = index + 1; // В формулах индексы с 1
|
||||||
|
const statusarrPattern = new RegExp(`statusarr\\[${arrayIndex}\\]`, 'g');
|
||||||
|
|
||||||
|
// Для humanReadableFormula используем описания метрик
|
||||||
|
if (metric.found) {
|
||||||
|
humanReadableFormula = humanReadableFormula.replace(
|
||||||
|
statusarrPattern,
|
||||||
|
metric.description
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
humanReadableFormula = humanReadableFormula.replace(
|
||||||
|
statusarrPattern,
|
||||||
|
`${metric.originalName} (НЕ НАЙДЕНА)`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Форматируем для лучшей читаемости
|
||||||
|
humanReadableFormula = humanReadableFormula
|
||||||
|
.replace(/\*/g, ' × ')
|
||||||
|
.replace(/\//g, ' ÷ ')
|
||||||
|
.replace(/\+/g, ' + ')
|
||||||
|
.replace(/-/g, ' - ')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
return {
|
||||||
|
parsedFormula: formula,
|
||||||
|
humanReadableFormula
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить все доступные формулы с обогащенными данными
|
||||||
|
*/
|
||||||
|
async getAllEnrichedFormulas(): Promise<EnrichedFormulaMetric[]> {
|
||||||
|
try {
|
||||||
|
const formulaOptions = await this.formulaService.getFormulaOptions('');
|
||||||
|
|
||||||
|
if (!Array.isArray(formulaOptions)) {
|
||||||
|
throw new Error('Invalid formula options response');
|
||||||
|
}
|
||||||
|
|
||||||
|
const enrichmentPromises = formulaOptions.map(async (formulaOption) => {
|
||||||
|
try {
|
||||||
|
return await this.getEnrichedFormula(formulaOption.id);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error enriching formula ${formulaOption.id}:`, error);
|
||||||
|
return {
|
||||||
|
...formulaOption,
|
||||||
|
enrichedMetrics: [],
|
||||||
|
parsedFormula: formulaOption.formula || '',
|
||||||
|
humanReadableFormula: formulaOption.formula || '',
|
||||||
|
metadata: {
|
||||||
|
totalMetrics: 0,
|
||||||
|
foundMetrics: 0,
|
||||||
|
missingMetrics: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(enrichmentPromises);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Error getting all enriched formulas:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Диагностика - проверка доступности метрик
|
||||||
|
*/
|
||||||
|
async diagnoseMetrics(metricNames: string[]): Promise<any> {
|
||||||
|
const results = await Promise.all(
|
||||||
|
metricNames.map(async (originalName) => {
|
||||||
|
const { prometheusName, metrics, found } = await this.findMetricInPrometheus(originalName);
|
||||||
|
|
||||||
|
return {
|
||||||
|
originalName,
|
||||||
|
prometheusName,
|
||||||
|
found,
|
||||||
|
availableNames: await this.findAvailableMetricNames(originalName),
|
||||||
|
metricsCount: metrics.length,
|
||||||
|
sampleValue: found && metrics[0] ? metrics[0].value : null
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
diagnosis: results,
|
||||||
|
summary: {
|
||||||
|
total: results.length,
|
||||||
|
found: results.filter(r => r.found).length,
|
||||||
|
notFound: results.filter(r => !r.found).length
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поиск доступных вариантов имен метрик
|
||||||
|
*/
|
||||||
|
private async findAvailableMetricNames(baseName: string): Promise<string[]> {
|
||||||
|
const possibleNames = [
|
||||||
|
baseName,
|
||||||
|
`${this.defaultPrefix}${baseName}`,
|
||||||
|
...this.metricPrefixes.map(prefix => `${prefix}${baseName}`)
|
||||||
|
];
|
||||||
|
|
||||||
|
const availableNames: string[] = [];
|
||||||
|
|
||||||
|
for (const name of possibleNames) {
|
||||||
|
try {
|
||||||
|
const metrics = await this.prometheusService.fetchMetrics(name);
|
||||||
|
if (metrics && metrics.length > 0) {
|
||||||
|
availableNames.push(name);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Игнорируем ошибки - метрика не найдена
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { Controller, Get, Post, Body, HttpException, HttpStatus, Param } from '@nestjs/common';
|
||||||
|
import { FormulaService } from './formula.service';
|
||||||
|
import { MenuService } from './menu.service';
|
||||||
|
import { FormulaEnrichmentService } from './formula-enrichment.service';
|
||||||
|
|
||||||
|
@Controller('formula')
|
||||||
|
export class FormulaController {
|
||||||
|
constructor(
|
||||||
|
private readonly FormulaService: FormulaService,
|
||||||
|
private readonly menuService: MenuService,
|
||||||
|
private readonly formulaEnrichmentService: FormulaEnrichmentService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
async getFormulaData(@Param('id') id: string) {
|
||||||
|
try {
|
||||||
|
return await this.FormulaService.getFormulaData(id);
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException('Failed to fetch Formula data', HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(':id/update')
|
||||||
|
async updateFormulaData(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Body() data: any
|
||||||
|
) {
|
||||||
|
if (!data) {
|
||||||
|
throw new HttpException('Invalid data format', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this.FormulaService.updateFormulaData(id, data);
|
||||||
|
this.menuService.invalidateCache();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OPTIONS метод для получения данных (как в вашем примере curl)
|
||||||
|
@Get(':id/options')
|
||||||
|
async getFormulaOptions(@Param('id') id: string) {
|
||||||
|
try {
|
||||||
|
return await this.FormulaService.getFormulaOptions(id);
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException('Failed to fetch Formula options', HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id/enriched')
|
||||||
|
async getEnrichedFormulaData(@Param('id') id: string) {
|
||||||
|
try {
|
||||||
|
return await this.formulaEnrichmentService.getEnrichedFormula(id);
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Failed to fetch enriched formula data',
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('all/enriched')
|
||||||
|
async getAllEnrichedFormulas() {
|
||||||
|
try {
|
||||||
|
return await this.formulaEnrichmentService.getAllEnrichedFormulas();
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Failed to fetch enriched formulas',
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// interfaces/formula.interface.ts
|
||||||
|
export interface FormulaMetric {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
values: {
|
||||||
|
statusarr: string[];
|
||||||
|
warr: string[];
|
||||||
|
};
|
||||||
|
formula: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnrichedFormulaMetric extends FormulaMetric {
|
||||||
|
enrichedMetrics: EnrichedMetric[];
|
||||||
|
parsedFormula: string;
|
||||||
|
humanReadableFormula: string;
|
||||||
|
metadata: {
|
||||||
|
totalMetrics: number;
|
||||||
|
foundMetrics: number;
|
||||||
|
missingMetrics: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnrichedMetric {
|
||||||
|
originalName: string;
|
||||||
|
prometheusName: string;
|
||||||
|
description: string;
|
||||||
|
currentValue?: number;
|
||||||
|
device?: string;
|
||||||
|
source_id?: string;
|
||||||
|
timestamp?: number;
|
||||||
|
type?: string;
|
||||||
|
found: boolean;
|
||||||
|
valuesCount?: number;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { HttpService } from '@nestjs/axios';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FormulaService {
|
||||||
|
private readonly FormulaApiUrl: string;
|
||||||
|
private readonly FormulaApiEndpoint: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly httpService: HttpService,
|
||||||
|
private readonly configService: ConfigService
|
||||||
|
) {
|
||||||
|
this.FormulaApiUrl = this.configService.get<string>('FORMULA_API_URL', 'http://192.168.2.39:9999');
|
||||||
|
this.FormulaApiEndpoint = this.configService.get<string>('FORMULA_API_ENDPOINT', '/api/integration/7777');
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFormulaData(id: string): Promise<any> {
|
||||||
|
try {
|
||||||
|
const response = await firstValueFrom(
|
||||||
|
this.httpService.get(`${this.FormulaApiUrl}${this.FormulaApiEndpoint}/${id}`, {
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch Formula data:', error);
|
||||||
|
this.handleError(error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFormulaOptions(id: string): Promise<any> {
|
||||||
|
try {
|
||||||
|
const url = `${this.FormulaApiUrl}${this.FormulaApiEndpoint}`;
|
||||||
|
console.log('Fetching Formula options via OPTIONS:', url);
|
||||||
|
|
||||||
|
const response = await firstValueFrom(
|
||||||
|
this.httpService.request({
|
||||||
|
method: 'OPTIONS',
|
||||||
|
url,
|
||||||
|
headers: { 'Accept': 'application/json' }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Response from Formula API:', response.data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch Formula options:', error);
|
||||||
|
this.handleError(error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateFormulaData(id: string, data: any) {
|
||||||
|
try {
|
||||||
|
const response = await firstValueFrom(
|
||||||
|
this.httpService.post(
|
||||||
|
`${this.FormulaApiUrl}${this.FormulaApiEndpoint}/${id}`,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update Formula data:', error);
|
||||||
|
this.handleError(error);
|
||||||
|
throw new Error('Failed to update Formula data');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleError(error: any): void {
|
||||||
|
if (error.response) {
|
||||||
|
console.error('Server responded with:', {
|
||||||
|
status: error.response.status,
|
||||||
|
data: error.response.data
|
||||||
|
});
|
||||||
|
} else if (error.request) {
|
||||||
|
console.error('No response received:', error.request);
|
||||||
|
} else {
|
||||||
|
console.error('Request setup error:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,10 +5,15 @@ import { MenuService } from './menu.service';
|
||||||
import { PrometheusModule } from '../prometheus/prometheus.module';
|
import { PrometheusModule } from '../prometheus/prometheus.module';
|
||||||
import { RangeService } from './range.service';
|
import { RangeService } from './range.service';
|
||||||
import { RangeController } from './range.controller';
|
import { RangeController } from './range.controller';
|
||||||
|
import { FormulaController } from './formula.controller';
|
||||||
|
import { FormulaService } from './formula.service';
|
||||||
|
import { FormulaEnrichmentService } from './formula-enrichment.service';
|
||||||
|
import { EnrichedFormulaController } from './enriched-formula.controller';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrometheusModule, HttpModule],
|
imports: [PrometheusModule, HttpModule],
|
||||||
controllers: [MenuController, RangeController],
|
controllers: [MenuController, RangeController, FormulaController, EnrichedFormulaController],
|
||||||
providers: [MenuService, RangeService]
|
providers: [MenuService, RangeService, FormulaService, FormulaEnrichmentService],
|
||||||
|
exports: [FormulaEnrichmentService]
|
||||||
})
|
})
|
||||||
export class MenuModule { }
|
export class MenuModule { }
|
||||||
|
|
@ -56,13 +56,14 @@ export class MetricsGateway implements OnModuleInit, OnModuleDestroy {
|
||||||
);
|
);
|
||||||
|
|
||||||
const wsPort = Number(this.configService.get('WS_PORT') || 3001);
|
const wsPort = Number(this.configService.get('WS_PORT') || 3001);
|
||||||
this.httpServer.listen(wsPort, () => {
|
const wsHost = this.configService.get('WS_HOST') || '0.0.0.0';
|
||||||
|
|
||||||
|
this.httpServer.listen(wsPort, wsHost, () => {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`WebSocket server running at ws://localhost:${wsPort}/metrics-ws`
|
`WebSocket server running at ws://${wsHost}:${wsPort}/metrics-ws`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onModuleDestroy() {
|
onModuleDestroy() {
|
||||||
// Очистка всех ресурсов
|
// Очистка всех ресурсов
|
||||||
this.clearAllSubscriptions();
|
this.clearAllSubscriptions();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue