From f3ebbc79d56e9eeab84d45e420ca4f2c567de6f8 Mon Sep 17 00:00:00 2001 From: SovietSpiderCat Date: Wed, 20 Aug 2025 00:15:56 +0300 Subject: [PATCH 1/3] fixed WS --- logs.txt | Bin 0 -> 126014 bytes src/prometheus/Subscription.interface.ts | 0 src/prometheus/index.ts | 5 + src/prometheus/metrics.gateway.ts | 594 +++++++++++--------- src/prometheus/prometheus-cache.service.ts | 49 ++ src/prometheus/prometheus-query.service.ts | 27 + src/prometheus/prometheus.module.ts | 20 +- src/prometheus/prometheus.service.ts | 610 +++++++++++++++------ 8 files changed, 881 insertions(+), 424 deletions(-) create mode 100644 logs.txt delete mode 100644 src/prometheus/Subscription.interface.ts create mode 100644 src/prometheus/index.ts create mode 100644 src/prometheus/prometheus-cache.service.ts create mode 100644 src/prometheus/prometheus-query.service.ts diff --git a/logs.txt b/logs.txt new file mode 100644 index 0000000000000000000000000000000000000000..c0969e7f9fc79999e570239463561a626b62a7cd GIT binary patch literal 126014 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 literal 0 HcmV?d00001 diff --git a/src/prometheus/Subscription.interface.ts b/src/prometheus/Subscription.interface.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/prometheus/index.ts b/src/prometheus/index.ts new file mode 100644 index 0000000..4213d99 --- /dev/null +++ b/src/prometheus/index.ts @@ -0,0 +1,5 @@ +// export * from './prometheus-metric.interface'; +// export * from './prometheus.service'; +// export * from './prometheus-cache.service'; +// export * from './prometheus-query.service'; +// export * from './prometheus-metadata.service'; \ No newline at end of file diff --git a/src/prometheus/metrics.gateway.ts b/src/prometheus/metrics.gateway.ts index 366b7c7..af8e1c7 100644 --- a/src/prometheus/metrics.gateway.ts +++ b/src/prometheus/metrics.gateway.ts @@ -1,23 +1,39 @@ -import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; +import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; import { Server, WebSocket } from 'ws'; import { createServer } from 'http'; import { PrometheusService } from './prometheus.service'; import { ConfigService } from '@nestjs/config'; +import { PrometheusMetric } from './prometheus-metric.interface'; type Filters = Record; +interface RealtimeSubscription { + clients: Set; + interval: NodeJS.Timeout; + metric: string; + filters: Filters; + lastData: PrometheusMetric[]; +} + +interface HistoricalRequest { + client: WebSocket; + requestId: string; +} + @Injectable() -export class MetricsGateway implements OnModuleInit { +export class MetricsGateway implements OnModuleInit, OnModuleDestroy { private readonly logger = new Logger(MetricsGateway.name); private wss: Server; + private httpServer: ReturnType; - private activeSockets = new Map(); - private metricSubscriptions = new Map< - string, - { stopUpdates: () => void; clients: Set } - >(); + // Real-time подписки (одна на метрику, много клиентов) + private realtimeSubscriptions = new Map(); - private lastSentData = new Map(); + // Активные клиенты + private activeClients = new Map(); + + // Исторические запросы (для отслеживания) + private historicalRequests = new Map(); constructor( private readonly prometheusService: PrometheusService, @@ -25,367 +41,429 @@ export class MetricsGateway implements OnModuleInit { ) { } onModuleInit() { - const httpServer = createServer(); + this.httpServer = createServer(); this.wss = new Server({ - server: httpServer, + server: this.httpServer, path: '/metrics-ws', }); this.wss.on('connection', (client, request) => this.handleConnection(client, request) ); + this.wss.on('error', (err) => this.logger.error('WebSocket server error:', err) ); const wsPort = Number(this.configService.get('WS_PORT') || 3001); - httpServer.listen(wsPort, () => { + this.httpServer.listen(wsPort, () => { this.logger.log( - `WebSocket server running at ws://localhost:${wsPort}/api/metrics-ws` + `WebSocket server running at ws://localhost:${wsPort}/metrics-ws` ); }); } + onModuleDestroy() { + // Очистка всех ресурсов + this.clearAllSubscriptions(); + this.wss?.close(); + this.httpServer?.close(); + } private handleConnection(client: WebSocket, request: any) { - let clientId = - this.getQueryParams(request?.url).clientId || - Math.random().toString(36).slice(2); + const clientId = this.getClientId(request?.url); + this.activeClients.set(clientId, client); - this.activeSockets.set(clientId, client); this.logger.log(`Client connected: ${clientId}`); + this.logger.debug(`Active clients: ${this.activeClients.size}, Subscriptions: ${this.realtimeSubscriptions.size}`); client.on('message', (raw) => { try { - const msg = JSON.parse(raw.toString()); - this.handleMessage(clientId, client, msg); + const message = JSON.parse(raw.toString()); + this.handleMessage(clientId, client, message); } catch (err) { - this.sendError(client, 'Invalid JSON'); + this.sendError(client, 'Invalid JSON format'); } }); - client.on('close', () => this.cleanupClient(clientId)); + client.on('close', () => this.handleClientDisconnect(clientId)); client.on('error', (err) => { this.logger.error(`Client ${clientId} error:`, err); - this.cleanupClient(clientId); + this.handleClientDisconnect(clientId); + }); + + // Отправляем приветственное сообщение + this.sendMessage(client, { + event: 'connected', + data: { clientId, timestamp: Date.now() } }); } private handleMessage(clientId: string, client: WebSocket, message: any) { - const { event, data } = message || {}; - if (!event) return this.sendError(client, 'Event type is required'); + const { event, data, requestId } = message; + + if (!event) { + return this.sendError(client, 'Event type is required', requestId); + } + + this.logger.debug(`Received event: ${event} from client: ${clientId}`); switch (event) { + case 'subscribe-realtime': + return this.handleSubscribeRealtime(clientId, client, data, requestId); + + case 'unsubscribe-realtime': + return this.handleUnsubscribeRealtime(clientId, data, requestId); + case 'unsubscribe-all': - return this.unsubscribeAllForClient(clientId); + return this.handleUnsubscribeAll(clientId, requestId); - case 'get-metrics': - return this.handleGetMetrics(client, data); + case 'get-historical': + return this.handleGetHistorical(client, data, requestId); - case 'subscribe-metric': - return this.handleSubscribeMetric(clientId, client, data); - - case 'unsubscribe-metric': - return this.handleUnsubscribeMetric(clientId, data); + case 'get-current': + return this.handleGetCurrent(client, data, requestId); default: - return this.sendError(client, `Unknown event type: ${event}`); + return this.sendError(client, `Unknown event type: ${event}`, requestId); } } - private cleanupClient(clientId: string) { - if (!this.activeSockets.has(clientId)) return; - this.logger.log(`Client disconnected: ${clientId}`); - this.activeSockets.delete(clientId); - - setTimeout(() => { - if (!this.activeSockets.has(clientId)) { - } - }, 5000); - - for (const [key, sub] of this.metricSubscriptions) { - sub.clients.delete(clientId); - if (sub.clients.size === 0) { - sub.stopUpdates(); - this.metricSubscriptions.delete(key); - this.lastSentData.delete(key); - } - } - } - - private unsubscribeAllForClient(clientId: string) { - for (const [key, sub] of this.metricSubscriptions) { - if (sub.clients.has(clientId)) sub.clients.delete(clientId); - if (sub.clients.size === 0) { - sub.stopUpdates(); - this.metricSubscriptions.delete(key); - this.lastSentData.delete(key); - } - } - } - - - private async handleGetMetrics(client: WebSocket, payload: any) { - const { - metric, - start, - end, - step, - isRangeQuery, - requestId, - filters = {}, - } = payload || {}; + private async handleSubscribeRealtime( + clientId: string, + client: WebSocket, + payload: any, + requestId?: string + ) { + const { metric, filters = {}, interval = 10000 } = payload || {}; if (!metric) { return this.sendError(client, 'Metric name is required', requestId); } - if (isRangeQuery) { - try { - const rangeData = await this.prometheusService.fetchMetricsRange( - metric, - start, - end, - step, - filters - ); - this.logger.debug('RangeQuery result', JSON.stringify(rangeData).slice(0, 200)); - - return this.sendMessage(client, { - event: 'metrics-data', - data: rangeData, - metric, - requestId, - }); - } catch (err: any) { - return this.sendError(client, err?.message || 'Range query error', requestId); - } - } + const subscriptionKey = this.getSubscriptionKey(metric, filters); try { - const subscriptionKey = this.getSubscriptionKey(metric, filters); + // Если подписка уже существует, просто добавляем клиента + if (this.realtimeSubscriptions.has(subscriptionKey)) { + const subscription = this.realtimeSubscriptions.get(subscriptionKey)!; + subscription.clients.add(clientId); - const initialData = - await this.prometheusService.fetchMetricsWithFilters(metric, filters); + this.logger.log(`Client ${clientId} added to existing subscription: ${subscriptionKey}`); + // Отправляем последние данные клиенту + if (subscription.lastData) { + this.sendMessage(client, { + event: 'realtime-data', + data: { + metric, + filters, + data: subscription.lastData, + type: 'initial' + }, + requestId + }); + } + return; + } + + // Создаем новую подписку + this.logger.log(`Creating new realtime subscription: ${subscriptionKey}`); + + // Первоначальная загрузка данных + const initialData = await this.prometheusService.fetchMetricsWithFilters(metric, filters); + + if (!Array.isArray(initialData)) { + throw new Error(`Expected array for metric ${metric}, got ${typeof initialData}`); + } + + // Создаем интервал для обновлений + const intervalId = setInterval( + () => this.updateRealtimeData(subscriptionKey), + interval + ); + + // Сохраняем подписку + const subscription: RealtimeSubscription = { + clients: new Set([clientId]), + interval: intervalId, + metric, + filters, + lastData: initialData + }; + + this.realtimeSubscriptions.set(subscriptionKey, subscription); + + // Отправляем данные клиенту this.sendMessage(client, { - event: 'metrics-data', - data: { metric, data: initialData, requestId }, + event: 'realtime-data', + data: { + metric, + filters, + data: initialData, + type: 'initial' + }, + requestId }); - let lastLocal = initialData; + this.logger.debug(`Subscription created for ${subscriptionKey} with ${interval}ms interval`); - const jitter = Math.floor(Math.random() * 5000); - setTimeout(() => { - const timer = setInterval(async () => { - try { - const fresh = - await this.prometheusService.fetchMetricsWithFilters( - metric, - filters - ); - if (!this.isDataEqual(lastLocal, fresh)) { - this.sendMessage(client, { - event: 'metrics-data', - data: { metric, data: fresh, requestId }, - }); - lastLocal = fresh; - } - } catch (e) { - this.logger.error( - `Error in on-demand periodic update for ${subscriptionKey}:`, - (e as any)?.message - ); - } - }, step || 5000); - - const closeOnce = () => clearInterval(timer); - client.once('close', closeOnce); - client.once('error', closeOnce); - }, jitter); - } catch (err: any) { - return this.sendError(client, err?.message || 'get-metrics error', requestId); + } catch (error) { + this.logger.error(`Subscribe error for ${subscriptionKey}:`, error); + this.sendError(client, error.message, requestId); } } - private async handleSubscribeMetric(clientId: string, client: WebSocket, payload: any) { - const { metric, interval = 60000, filters = {} } = payload || {}; + private async updateRealtimeData(subscriptionKey: string) { + const subscription = this.realtimeSubscriptions.get(subscriptionKey); + if (!subscription) return; + + try { + const freshData = await this.prometheusService.fetchMetricsWithFilters( + subscription.metric, + subscription.filters + ); + + if (!this.isDataEqual(subscription.lastData, freshData)) { + subscription.lastData = freshData; + + // Рассылаем обновление всем подписанным клиентам + this.broadcastToClients(Array.from(subscription.clients), { + event: 'realtime-data', + data: { + metric: subscription.metric, + filters: subscription.filters, + data: freshData, + type: 'update' + } + }); + + this.logger.debug(`Data updated for subscription: ${subscriptionKey}`); + } + } catch (error) { + this.logger.error(`Update error for ${subscriptionKey}:`, error); + } + } + + private handleUnsubscribeRealtime( + clientId: string, + payload: any, + requestId?: string + ) { + const { metric, filters = {} } = payload || {}; + if (!metric) { - this.sendError(client, 'Metric name is required'); return; } - const key = this.getSubscriptionKey(metric, filters); + const subscriptionKey = this.getSubscriptionKey(metric, filters); + const subscription = this.realtimeSubscriptions.get(subscriptionKey); + + if (!subscription) { + this.logger.debug(`No subscription found for: ${subscriptionKey}`); + return; + } + + // Удаляем клиента из подписки + subscription.clients.delete(clientId); + this.logger.log(`Client ${clientId} unsubscribed from: ${subscriptionKey}`); + + // Если больше нет клиентов, очищаем подписку + if (subscription.clients.size === 0) { + clearInterval(subscription.interval); + this.realtimeSubscriptions.delete(subscriptionKey); + this.logger.log(`Subscription removed: ${subscriptionKey}`); + } + } + + private handleUnsubscribeAll(clientId: string, requestId?: string) { + let unsubscribedCount = 0; + + for (const [key, subscription] of this.realtimeSubscriptions) { + if (subscription.clients.has(clientId)) { + subscription.clients.delete(clientId); + unsubscribedCount++; + + if (subscription.clients.size === 0) { + clearInterval(subscription.interval); + this.realtimeSubscriptions.delete(key); + } + } + } + + this.logger.log(`Client ${clientId} unsubscribed from ${unsubscribedCount} subscriptions`); + } + + private async handleGetHistorical( + client: WebSocket, + payload: any, + requestId?: string + ) { + const { metric, start, end, step = 60, filters = {} } = payload || {}; + + if (!metric) { + return this.sendError(client, 'Metric name is required', requestId); + } + + if (!start || !end) { + return this.sendError(client, 'Start and end time are required', requestId); + } + + const requestKey = `${requestId || Date.now()}-${metric}`; try { - const initial = await this.prometheusService.fetchMetricsWithFilters(metric, filters); + this.logger.debug(`Fetching historical data for: ${metric}, from ${new Date(start).toISOString()} to ${new Date(end).toISOString()}`); - if (!Array.isArray(initial)) { - throw new Error(`Expected array for metric ${metric}, got ${typeof initial}`); - } - - this.sendMessage(client, { - event: 'metrics-data', - data: { - metric: key, - data: initial, - type: 'initial' - } - }); - - this.lastSentData.set(key, initial); - - const stopUpdates = await this.sendPeriodicUpdates( + const historicalData = await this.prometheusService.fetchMetricsRange( metric, - interval, - (freshData) => { - if (!Array.isArray(freshData)) { - this.logger.error(`Periodic update: expected array for ${key}, got ${typeof freshData}`); - return; - } - - const lastData = this.lastSentData.get(key); - if (!this.isDataEqual(lastData, freshData)) { - this.broadcast({ - event: 'metrics-data', - data: { - metric: key, - data: freshData, - type: 'update' - } - }); - this.lastSentData.set(key, freshData); - } - }, + Math.floor(start / 1000), // Convert to seconds + Math.floor(end / 1000), // Convert to seconds + step, filters ); - this.metricSubscriptions.set(key, { - stopUpdates, - clients: new Set([clientId]), + this.sendMessage(client, { + event: 'historical-data', + data: { + metric, + filters, + data: historicalData, + start, + end, + step + }, + requestId }); - const unsubscribe = () => { - this.logger.log(`Unsubscribing client ${clientId} from ${key}`); - const subscription = this.metricSubscriptions.get(key); - if (subscription) { - subscription.clients.delete(clientId); - if (subscription.clients.size === 0) { - subscription.stopUpdates(); - this.metricSubscriptions.delete(key); - this.lastSentData.delete(key); - } - } - }; - - client.on('close', unsubscribe); - client.on('error', unsubscribe); + this.logger.debug(`Historical data sent for: ${metric}, points: ${historicalData.length}`); } catch (error) { - this.logger.error(`Subscription error for ${key}:`, error); - this.sendError(client, error.message); - - if (!this.metricSubscriptions.has(key)) { - this.lastSentData.delete(key); - } + this.logger.error(`Historical data error for ${metric}:`, error); + this.sendError(client, error.message, requestId); } } - private handleUnsubscribeMetric( - clientId: string, - payload: { metric: string; filters?: Filters } + private async handleGetCurrent( + client: WebSocket, + payload: any, + requestId?: string ) { const { metric, filters = {} } = payload || {}; - if (!metric) return; - const key = this.getSubscriptionKey(metric, filters); - const sub = this.metricSubscriptions.get(key); - if (!sub) return; + if (!metric) { + return this.sendError(client, 'Metric name is required', requestId); + } - sub.clients.delete(clientId); - if (sub.clients.size === 0) { - sub.stopUpdates(); - this.metricSubscriptions.delete(key); - this.lastSentData.delete(key); + try { + const currentData = await this.prometheusService.fetchMetricsWithFilters(metric, filters); + + this.sendMessage(client, { + event: 'current-data', + data: { + metric, + filters, + data: currentData, + timestamp: Date.now() + }, + requestId + }); + + } catch (error) { + this.logger.error(`Current data error for ${metric}:`, error); + this.sendError(client, error.message, requestId); } } + private handleClientDisconnect(clientId: string) { + this.logger.log(`Client disconnected: ${clientId}`); - private getQueryParams(rawUrl?: string): Record { + // Удаляем клиента из всех подписок + this.handleUnsubscribeAll(clientId); + + // Удаляем из активных клиентов + this.activeClients.delete(clientId); + + this.logger.debug(`Active clients: ${this.activeClients.size}, Subscriptions: ${this.realtimeSubscriptions.size}`); + } + + private clearAllSubscriptions() { + for (const [key, subscription] of this.realtimeSubscriptions) { + clearInterval(subscription.interval); + } + this.realtimeSubscriptions.clear(); + this.logger.log('All subscriptions cleared'); + } + + private getClientId(url?: string): string { + const params = this.getQueryParams(url); + return params.clientId || `client-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; + } + + private getQueryParams(url?: string): Record { try { - const url = new URL(rawUrl || '', 'http://localhost'); // безопасная база - return Object.fromEntries(url.searchParams.entries()); + if (!url) return {}; + const urlObj = new URL(url, 'http://localhost'); + return Object.fromEntries(urlObj.searchParams.entries()); } catch { return {}; } } private getSubscriptionKey(metric: string, filters: Filters): string { - const keys = Object.keys(filters).sort(); - const filterString = keys - .map((k) => `${k}=${encodeURIComponent(filters[k])}`) + const sortedFilters = Object.keys(filters) + .sort() + .map(key => `${key}=${encodeURIComponent(filters[key])}`) .join('&'); - return `${metric}${filterString ? `?${filterString}` : ''}`; + + return sortedFilters ? `${metric}?${sortedFilters}` : metric; } - private isDataEqual(a: any[], b: any[]) { - if (!a || !b || a.length !== b.length) return false; - return a.every((item, i) => { - const x = b[i]; + private isDataEqual(a: PrometheusMetric[], b: PrometheusMetric[]): boolean { + if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) { + return false; + } + + return a.every((itemA, index) => { + const itemB = b[index]; return ( - item?.value === x?.value && - item?.status === x?.status && - item?.timestamp === x?.timestamp + itemA.value === itemB.value && + itemA.timestamp === itemB.timestamp && + itemA.device === itemB.device && + itemA.source_id === itemB.source_id ); }); } - private async sendPeriodicUpdates( - metric: string, - interval: number, - cb: (data: any) => void, - filters: Filters - ) { - const initialDelay = Math.floor(Math.random() * 5000); - await new Promise((r) => setTimeout(r, initialDelay)); - - const timer = setInterval(async () => { - try { - const data = await this.prometheusService.fetchMetricsWithFilters( - metric, - filters - ); - cb(data); - } catch (e: any) { - this.logger.error( - `Error in periodic update for ${metric}:`, - e?.message - ); - } - }, interval); - - return () => clearInterval(timer); - } - private sendMessage(client: WebSocket, message: any) { if (client.readyState === WebSocket.OPEN) { - client.send(JSON.stringify(message)); + try { + client.send(JSON.stringify(message)); + } catch (error) { + this.logger.error('Error sending message to client:', error); + } } } - private broadcast(message: any) { - const raw = JSON.stringify(message); - this.wss.clients.forEach((c) => { - if (c.readyState === WebSocket.OPEN) c.send(raw); + private broadcastToClients(clientIds: string[], message: any) { + const messageStr = JSON.stringify(message); + + clientIds.forEach(clientId => { + const client = this.activeClients.get(clientId); + if (client && client.readyState === WebSocket.OPEN) { + try { + client.send(messageStr); + } catch (error) { + this.logger.error(`Error broadcasting to client ${clientId}:`, error); + } + } }); } private sendError(client: WebSocket, error: string, requestId?: string) { this.sendMessage(client, { - event: 'metrics-error', + event: 'error', data: { error, requestId }, + requestId }); } -} - - +} \ No newline at end of file diff --git a/src/prometheus/prometheus-cache.service.ts b/src/prometheus/prometheus-cache.service.ts new file mode 100644 index 0000000..804603d --- /dev/null +++ b/src/prometheus/prometheus-cache.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@nestjs/common'; +import { PrometheusMetric } from './prometheus-metric.interface'; + +interface CacheEntry { + data: T; + timestamp: number; +} + +interface MetadataCacheEntry { + type: string | null; + description: string | undefined; + timestamp: number; +} + +@Injectable() +export class PrometheusCacheService { + private metricCache = new Map>(); + private metadataCache = new Map(); + + getMetricCache(key: string): CacheEntry | undefined { + return this.metricCache.get(key); + } + + setMetricCache(key: string, data: PrometheusMetric[], ttl: number = 5000): void { + this.metricCache.set(key, { data, timestamp: Date.now() + ttl }); + } + + getMetadataCache(key: string): MetadataCacheEntry | undefined { + return this.metadataCache.get(key); + } + + setMetadataCache( + key: string, + type: string | null, + description: string | undefined, + ttl: number = 30000 + ): void { + this.metadataCache.set(key, { type, description, timestamp: Date.now() + ttl }); + } + + isCacheValid(cacheEntry: { timestamp: number }): boolean { + return Date.now() < cacheEntry.timestamp; + } + + clearCache(): void { + this.metricCache.clear(); + this.metadataCache.clear(); + } +} \ No newline at end of file diff --git a/src/prometheus/prometheus-query.service.ts b/src/prometheus/prometheus-query.service.ts new file mode 100644 index 0000000..0e91855 --- /dev/null +++ b/src/prometheus/prometheus-query.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class PrometheusQueryService { + buildFilteredQuery(metric: string, filters: Record): string { + const filterParts = Object.entries(filters) + .filter(([_, value]) => value !== undefined && value !== null && value !== "") + .map(([key, value]) => `${key}="${value}"`); + + return filterParts.length > 0 + ? `${metric}{${filterParts.join(',')}}` + : metric; + } + + calculateOptimalStep(start: number, end: number): number { + const duration = end - start; + return Math.max(Math.floor(duration / 1000), 15); + } + + generateCacheKey(metric: string, filters: Record = {}): string { + return `${metric}:${JSON.stringify(filters)}`; + } + + generateMetadataCacheKey(metric: string, type: 'type' | 'description'): string { + return `metadata-${type}-${metric}`; + } +} \ No newline at end of file diff --git a/src/prometheus/prometheus.module.ts b/src/prometheus/prometheus.module.ts index 994b741..5ee8479 100644 --- a/src/prometheus/prometheus.module.ts +++ b/src/prometheus/prometheus.module.ts @@ -1,13 +1,21 @@ import { Module } from '@nestjs/common'; import { HttpModule } from '@nestjs/axios'; +import { ConfigModule } from '@nestjs/config'; import { PrometheusService } from './prometheus.service'; import { MetricsController } from './metrics.controller'; import { MetricsGateway } from './metrics.gateway'; +import { PrometheusCacheService } from './prometheus-cache.service'; +import { PrometheusQueryService } from './prometheus-query.service'; @Module({ - imports: [HttpModule], - providers: [PrometheusService, MetricsGateway], - controllers: [MetricsController], - exports: [PrometheusService] - }) - export class PrometheusModule {} \ No newline at end of file + imports: [HttpModule, ConfigModule], + providers: [ + PrometheusCacheService, + PrometheusQueryService, + PrometheusService, + MetricsGateway + ], + controllers: [MetricsController], + exports: [PrometheusService] +}) +export class PrometheusModule { } \ No newline at end of file diff --git a/src/prometheus/prometheus.service.ts b/src/prometheus/prometheus.service.ts index 396e388..08876c6 100644 --- a/src/prometheus/prometheus.service.ts +++ b/src/prometheus/prometheus.service.ts @@ -1,47 +1,371 @@ +// import { Injectable } from '@nestjs/common'; +// import { HttpService } from '@nestjs/axios'; +// import { ConfigService } from '@nestjs/config'; +// import { lastValueFrom } from 'rxjs'; +// import { PrometheusMetric } from './prometheus-metric.interface'; +// import { MenuItem } from '../menu/menu.interface'; + +// @Injectable() +// export class PrometheusService { +// private readonly prometheusUrl: string; +// private metricCache = new Map(); +// private metadataCache = new Map(); + +// constructor( +// private readonly httpService: HttpService, +// private readonly configService: ConfigService +// ) { +// this.prometheusUrl = this.configService.get('PROMETHEUS_API', 'http://localhost:9090'); +// console.log('Prometheus API URL:', this.prometheusUrl); +// } + +// async fetchMetricType(metric: string): Promise { +// const cacheKey = `metadata-type-${metric}`; +// const cacheEntry = this.metadataCache.get(cacheKey); + +// if (cacheEntry && Date.now() - cacheEntry.timestamp < 30000) { +// return cacheEntry.type; +// } + +// try { +// const response = await lastValueFrom( +// this.httpService.get(`${this.prometheusUrl}/metadata`, { +// params: { metric }, +// }) +// ); +// const metadata = response.data.data[metric]; +// const result = metadata?.length ? metadata[0].type : null; + +// this.metadataCache.set(cacheKey, { +// type: result, +// description: cacheEntry?.description, +// timestamp: Date.now() +// }); + +// return result; +// } catch (error) { +// console.error(`Ошибка при получении типа метрики ${metric}:`, error); +// return cacheEntry?.type || null; +// } +// } + +// async fetchMetricDescription(metric: string): Promise { +// const cacheKey = `metadata-description-${metric}`; +// const cacheEntry = this.metadataCache.get(cacheKey); + +// if (cacheEntry && Date.now() - cacheEntry.timestamp < 30000) { +// return cacheEntry.description; +// } + +// try { +// const response = await lastValueFrom( +// this.httpService.get(`${this.prometheusUrl}/metadata`, { +// params: { metric }, +// }) +// ); +// const metadata = response.data.data[metric]; +// const result = metadata?.length ? metadata[0].help : undefined; + +// this.metadataCache.set(cacheKey, { +// type: cacheEntry?.type ?? null, +// description: result, +// timestamp: Date.now() +// }); + +// return result; +// } catch (error) { +// console.error(`Ошибка при получении описания метрики ${metric}:`, error); +// return cacheEntry?.description; +// } +// } + +// async fetchMetrics(metric: string): Promise { +// const cacheKey = `${metric}:{}`; +// const cacheEntry = this.metricCache.get(cacheKey); + +// if (cacheEntry && Date.now() - cacheEntry.timestamp < 5000) { +// return cacheEntry.data; +// } + +// try { +// const response = await lastValueFrom( +// this.httpService.get(`${this.prometheusUrl}/query`, { +// params: { query: metric }, +// }) +// ); + +// const metricType = await this.fetchMetricType(metric); +// const metricDescription = await this.fetchMetricDescription(metric); + +// const result = response.data.data.result.map((entry): PrometheusMetric => ({ +// __name__: entry.metric.__name__ || metric, +// device: entry.metric.device, +// instance: entry.metric.instance, +// job: entry.metric.job, +// source_id: entry.metric.source_id, +// status: entry.metric.status || '0', +// timestamp: entry.value[0] * 1000, +// value: parseFloat(entry.value[1]), +// type: metricType || 'gauge', +// description: metricDescription, +// ...entry.metric +// })); + +// this.metricCache.set(cacheKey, { data: result, timestamp: Date.now() }); +// return result; +// } catch (error) { +// console.error(`Error fetching metrics for ${metric}:`, error); +// if (cacheEntry) return cacheEntry.data; +// throw error; +// } +// } + +// async fetchMetricsWithFilters(metric: string, filters: Record): Promise { +// const cacheKey = `${metric}:${JSON.stringify(filters)}`; +// const cacheEntry = this.metricCache.get(cacheKey); + +// if (cacheEntry && Date.now() - cacheEntry.timestamp < 5000) { +// return cacheEntry.data; +// } + +// try { +// const query = this.buildFilteredQuery(metric, filters); +// const response = await lastValueFrom( +// this.httpService.get(`${this.prometheusUrl}/query`, { +// params: { query } +// }) +// ); + +// const metricType = await this.fetchMetricType(metric); +// const metricDescription = await this.fetchMetricDescription(metric); + +// const result = response.data.data.result.map((entry): PrometheusMetric => ({ +// __name__: entry.metric.__name__ || metric, +// device: entry.metric.device, +// instance: entry.metric.instance, +// job: entry.metric.job, +// source_id: entry.metric.source_id, +// status: entry.metric.status || '0', +// timestamp: entry.value[0] * 1000, +// value: parseFloat(entry.value[1]), +// type: metricType || 'gauge', +// description: metricDescription, +// ...entry.metric +// })); + +// this.metricCache.set(cacheKey, { data: result, timestamp: Date.now() }); +// return result; +// } catch (error) { +// console.error(`Error fetching metrics with filters for ${metric}:`, error); +// if (cacheEntry) return cacheEntry.data; +// throw error; +// } +// } + +// private buildFilteredQuery(metric: string, filters: Record): string { +// const filterParts = Object.entries(filters) +// .filter(([_, value]) => value !== undefined && value !== null && value !== "") +// .map(([key, value]) => { +// return `${key}="${value}"`; +// }); + +// return filterParts.length > 0 +// ? `${metric}{${filterParts.join(',')}}` +// : metric; +// } + +// async fetchMetricsRange(metric: string, start: number, end: number, step: number, filters: Record = {}): Promise { +// // Рассчитываем оптимальный шаг, если не указан +// const duration = end - start; +// const optimalStep = Math.max(Math.floor(duration / 1000), 15); // Минимум 15 секунд + +// const query = this.buildFilteredQuery(metric, { +// ...filters, +// instance: '192.168.2.34:9050' +// }); + +// try { +// const response = await lastValueFrom( +// this.httpService.get(`${this.prometheusUrl}/query_range`, { +// params: { +// query, +// start, +// end, +// step: optimalStep.toString() +// }, +// }) +// ); + +// const metricType = await this.fetchMetricType(metric); +// const metricDescription = await this.fetchMetricDescription(metric); + +// return response.data.data.result.flatMap((entry) => +// entry.values.map((value): PrometheusMetric => ({ +// __name__: entry.metric.__name__ || metric, +// device: entry.metric.device, +// instance: entry.metric.instance, +// job: entry.metric.job, +// source_id: entry.metric.source_id, +// status: entry.metric.status || '0', +// timestamp: value[0] * 1000, +// value: parseFloat(value[1]), +// type: metricType || 'gauge', +// description: metricDescription, +// ...entry.metric +// })) +// ); +// } catch (error) { +// console.error('Error in fetchMetricsRange:', { +// error: error.response?.data || error.message, +// query, +// filters +// }); +// throw error; +// } +// } + +// async getMetricsForMenuItem(menuItem: MenuItem): Promise { +// if (!menuItem.metric || !menuItem.filters) { +// throw new Error('MenuItem is not a metric item'); +// } + +// return this.fetchMetricsWithFilters(menuItem.metric, menuItem.filters); +// } + +// async fetchMetricMetadata(metric: string): Promise<{ +// name: string; +// help?: string; +// type?: string; +// }> { +// try { +// const response = await lastValueFrom( +// this.httpService.get(`${this.prometheusUrl}/metadata`, { +// params: { metric } +// }) +// ); + +// const data = response.data?.data?.[metric]?.[0]; + +// return { +// name: metric, +// help: data?.help, +// type: data?.type +// }; +// } catch (error) { +// console.error(`Error fetching metadata for ${metric}:`, error); +// return { +// name: metric +// }; +// } +// } + +// async fetchMetricSeries(metric: string): Promise[]> { +// try { +// const response = await lastValueFrom( +// this.httpService.get(`${this.prometheusUrl}/series`, { +// params: { 'match[]': metric } +// }) +// ); + +// return response.data.data || []; +// } catch (error) { +// console.error(`Error fetching series for ${metric}:`, error); +// return []; +// } +// } + +// async fetchAllMetrics(): Promise { +// try { +// const response = await lastValueFrom( +// this.httpService.get(`${this.prometheusUrl}/label/__name__/values`) +// ); +// return response.data.data; +// } catch (error) { +// console.error('Error fetching all metrics:', error); +// return []; +// } +// } + +// async fetchAllMetricsWithValues(): Promise { +// const metricNames = await this.fetchAllMetrics(); +// const zvksMetrics = metricNames.filter(metric => +// metric.startsWith('zvks') || +// metric.includes('server_li') || +// metric.includes('application_li') +// ); + +// const promises = zvksMetrics.map(async (metric) => { +// try { +// const data = await this.fetchMetrics(metric); +// return { metric, data }; +// } catch (error) { +// console.error(`Error fetching data for metric ${metric}:`, error); +// return { metric, data: [] }; +// } +// }); + +// return Promise.all(promises); +// } + +// clearCache(): void { +// this.metricCache.clear(); +// this.metadataCache.clear(); +// } +// } + + import { Injectable } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; import { lastValueFrom } from 'rxjs'; -import { PrometheusMetric } from './prometheus-metric.interface'; import { MenuItem } from '../menu/menu.interface'; +import { PrometheusMetric } from './prometheus-metric.interface'; +import { PrometheusCacheService } from './prometheus-cache.service'; +import { PrometheusQueryService } from './prometheus-query.service'; + +interface PrometheusResponse { + status: string; + data: any; +} @Injectable() export class PrometheusService { private readonly prometheusUrl: string; - private metricCache = new Map(); - private metadataCache = new Map(); constructor( private readonly httpService: HttpService, - private readonly configService: ConfigService + private readonly configService: ConfigService, + private readonly cacheService: PrometheusCacheService, + private readonly queryService: PrometheusQueryService ) { this.prometheusUrl = this.configService.get('PROMETHEUS_API', 'http://localhost:9090'); console.log('Prometheus API URL:', this.prometheusUrl); } + private async executeQuery(url: string, params: any): Promise { + const response = await lastValueFrom( + this.httpService.get(url, { params }) + ); + return response.data; + } + async fetchMetricType(metric: string): Promise { - const cacheKey = `metadata-type-${metric}`; - const cacheEntry = this.metadataCache.get(cacheKey); - - if (cacheEntry && Date.now() - cacheEntry.timestamp < 30000) { + const cacheKey = this.queryService.generateMetadataCacheKey(metric, 'type'); + const cacheEntry = this.cacheService.getMetadataCache(cacheKey); + + if (cacheEntry && this.cacheService.isCacheValid(cacheEntry)) { return cacheEntry.type; } try { - const response = await lastValueFrom( - this.httpService.get(`${this.prometheusUrl}/metadata`, { - params: { metric }, - }) - ); - const metadata = response.data.data[metric]; - const result = metadata?.length ? metadata[0].type : null; - - this.metadataCache.set(cacheKey, { - type: result, - description: cacheEntry?.description, - timestamp: Date.now() + const data = await this.executeQuery(`${this.prometheusUrl}/metadata`, { + params: { metric }, }); - + + const metadata = data.data[metric]; + const result = metadata?.length ? metadata[0].type : null; + + this.cacheService.setMetadataCache(cacheKey, result, cacheEntry?.description); return result; } catch (error) { console.error(`Ошибка при получении типа метрики ${metric}:`, error); @@ -50,28 +374,22 @@ export class PrometheusService { } async fetchMetricDescription(metric: string): Promise { - const cacheKey = `metadata-description-${metric}`; - const cacheEntry = this.metadataCache.get(cacheKey); - - if (cacheEntry && Date.now() - cacheEntry.timestamp < 30000) { + const cacheKey = this.queryService.generateMetadataCacheKey(metric, 'description'); + const cacheEntry = this.cacheService.getMetadataCache(cacheKey); + + if (cacheEntry && this.cacheService.isCacheValid(cacheEntry)) { return cacheEntry.description; } try { - const response = await lastValueFrom( - this.httpService.get(`${this.prometheusUrl}/metadata`, { - params: { metric }, - }) - ); - const metadata = response.data.data[metric]; + const data = await this.executeQuery(`${this.prometheusUrl}/metadata`, { + params: { metric }, + }); + + const metadata = data.data[metric]; const result = metadata?.length ? metadata[0].help : undefined; - - this.metadataCache.set(cacheKey, { - type: cacheEntry?.type ?? null, - description: result, - timestamp: Date.now() - }); - + + this.cacheService.setMetadataCache(cacheKey, cacheEntry?.type ?? null, result); return result; } catch (error) { console.error(`Ошибка при получении описания метрики ${metric}:`, error); @@ -79,39 +397,46 @@ export class PrometheusService { } } + private transformMetricData( + entry: any, + metric: string, + type: string | null, + description: string | undefined + ): PrometheusMetric { + return { + __name__: entry.metric.__name__ || metric, + device: entry.metric.device || '', + source_id: entry.metric.source_id || '', + value: parseFloat(entry.value[1]), + timestamp: entry.value[0] * 1000, + type: type || 'gauge', + description + }; + } + async fetchMetrics(metric: string): Promise { - const cacheKey = `${metric}:{}`; - const cacheEntry = this.metricCache.get(cacheKey); - - if (cacheEntry && Date.now() - cacheEntry.timestamp < 5000) { + const cacheKey = this.queryService.generateCacheKey(metric); + const cacheEntry = this.cacheService.getMetricCache(cacheKey); + + if (cacheEntry && this.cacheService.isCacheValid(cacheEntry)) { return cacheEntry.data; } try { - const response = await lastValueFrom( - this.httpService.get(`${this.prometheusUrl}/query`, { - params: { query: metric }, - }) + const data = await this.executeQuery(`${this.prometheusUrl}/query`, { + query: metric + }); + + const [type, description] = await Promise.all([ + this.fetchMetricType(metric), + this.fetchMetricDescription(metric) + ]); + + const result = data.data.result.map((entry: any) => + this.transformMetricData(entry, metric, type, description) ); - const metricType = await this.fetchMetricType(metric); - const metricDescription = await this.fetchMetricDescription(metric); - - const result = response.data.data.result.map((entry): PrometheusMetric => ({ - __name__: entry.metric.__name__ || metric, - device: entry.metric.device, - instance: entry.metric.instance, - job: entry.metric.job, - source_id: entry.metric.source_id, - status: entry.metric.status || '0', - timestamp: entry.value[0] * 1000, - value: parseFloat(entry.value[1]), - type: metricType || 'gauge', - description: metricDescription, - ...entry.metric - })); - - this.metricCache.set(cacheKey, { data: result, timestamp: Date.now() }); + this.cacheService.setMetricCache(cacheKey, result); return result; } catch (error) { console.error(`Error fetching metrics for ${metric}:`, error); @@ -121,39 +446,27 @@ export class PrometheusService { } async fetchMetricsWithFilters(metric: string, filters: Record): Promise { - const cacheKey = `${metric}:${JSON.stringify(filters)}`; - const cacheEntry = this.metricCache.get(cacheKey); - - if (cacheEntry && Date.now() - cacheEntry.timestamp < 5000) { + const cacheKey = this.queryService.generateCacheKey(metric, filters); + const cacheEntry = this.cacheService.getMetricCache(cacheKey); + + if (cacheEntry && this.cacheService.isCacheValid(cacheEntry)) { return cacheEntry.data; } try { - const query = this.buildFilteredQuery(metric, filters); - const response = await lastValueFrom( - this.httpService.get(`${this.prometheusUrl}/query`, { - params: { query } - }) + const query = this.queryService.buildFilteredQuery(metric, filters); + const data = await this.executeQuery(`${this.prometheusUrl}/query`, { query }); + + const [type, description] = await Promise.all([ + this.fetchMetricType(metric), + this.fetchMetricDescription(metric) + ]); + + const result = data.data.result.map((entry: any) => + this.transformMetricData(entry, metric, type, description) ); - const metricType = await this.fetchMetricType(metric); - const metricDescription = await this.fetchMetricDescription(metric); - - const result = response.data.data.result.map((entry): PrometheusMetric => ({ - __name__: entry.metric.__name__ || metric, - device: entry.metric.device, - instance: entry.metric.instance, - job: entry.metric.job, - source_id: entry.metric.source_id, - status: entry.metric.status || '0', - timestamp: entry.value[0] * 1000, - value: parseFloat(entry.value[1]), - type: metricType || 'gauge', - description: metricDescription, - ...entry.metric - })); - - this.metricCache.set(cacheKey, { data: result, timestamp: Date.now() }); + this.cacheService.setMetricCache(cacheKey, result); return result; } catch (error) { console.error(`Error fetching metrics with filters for ${metric}:`, error); @@ -162,56 +475,42 @@ export class PrometheusService { } } - private buildFilteredQuery(metric: string, filters: Record): string { - const filterParts = Object.entries(filters) - .filter(([_, value]) => value !== undefined && value !== null && value !== "") - .map(([key, value]) => { - return `${key}="${value}"`; - }); - - return filterParts.length > 0 - ? `${metric}{${filterParts.join(',')}}` - : metric; - } - - async fetchMetricsRange(metric: string, start: number, end: number, step: number, filters: Record = {}): Promise { - // Рассчитываем оптимальный шаг, если не указан - const duration = end - start; - const optimalStep = Math.max(Math.floor(duration / 1000), 15); // Минимум 15 секунд - - const query = this.buildFilteredQuery(metric, { + async fetchMetricsRange( + metric: string, + start: number, + end: number, + step: number, + filters: Record = {} + ): Promise { + const query = this.queryService.buildFilteredQuery(metric, { ...filters, instance: '192.168.2.34:9050' }); + const optimalStep = this.queryService.calculateOptimalStep(start, end); + try { - const response = await lastValueFrom( - this.httpService.get(`${this.prometheusUrl}/query_range`, { - params: { - query, - start, - end, - step: optimalStep.toString() - }, - }) - ); + const data = await this.executeQuery(`${this.prometheusUrl}/query_range`, { + query, + start, + end, + step: optimalStep.toString() + }); - const metricType = await this.fetchMetricType(metric); - const metricDescription = await this.fetchMetricDescription(metric); + const [type, description] = await Promise.all([ + this.fetchMetricType(metric), + this.fetchMetricDescription(metric) + ]); - return response.data.data.result.flatMap((entry) => - entry.values.map((value): PrometheusMetric => ({ + return data.data.result.flatMap((entry: any) => + entry.values.map((value: any) => ({ __name__: entry.metric.__name__ || metric, - device: entry.metric.device, - instance: entry.metric.instance, - job: entry.metric.job, - source_id: entry.metric.source_id, - status: entry.metric.status || '0', - timestamp: value[0] * 1000, + device: entry.metric.device || '', + source_id: entry.metric.source_id || '', value: parseFloat(value[1]), - type: metricType || 'gauge', - description: metricDescription, - ...entry.metric + timestamp: value[0] * 1000, + type: type || 'gauge', + description })) ); } catch (error) { @@ -238,36 +537,30 @@ export class PrometheusService { type?: string; }> { try { - const response = await lastValueFrom( - this.httpService.get(`${this.prometheusUrl}/metadata`, { - params: { metric } - }) - ); + const data = await this.executeQuery(`${this.prometheusUrl}/metadata`, { + params: { metric } + }); - const data = response.data?.data?.[metric]?.[0]; + const metadata = data?.data?.[metric]?.[0]; return { name: metric, - help: data?.help, - type: data?.type + help: metadata?.help, + type: metadata?.type }; } catch (error) { console.error(`Error fetching metadata for ${metric}:`, error); - return { - name: metric - }; + return { name: metric }; } } async fetchMetricSeries(metric: string): Promise[]> { try { - const response = await lastValueFrom( - this.httpService.get(`${this.prometheusUrl}/series`, { - params: { 'match[]': metric } - }) - ); + const data = await this.executeQuery(`${this.prometheusUrl}/series`, { + 'match[]': metric + }); - return response.data.data || []; + return data.data || []; } catch (error) { console.error(`Error fetching series for ${metric}:`, error); return []; @@ -276,22 +569,20 @@ export class PrometheusService { async fetchAllMetrics(): Promise { try { - const response = await lastValueFrom( - this.httpService.get(`${this.prometheusUrl}/label/__name__/values`) - ); - return response.data.data; + const data = await this.executeQuery(`${this.prometheusUrl}/label/__name__/values`, {}); + return data.data; } catch (error) { console.error('Error fetching all metrics:', error); return []; } } - async fetchAllMetricsWithValues(): Promise { + async fetchAllMetricsWithValues(): Promise> { const metricNames = await this.fetchAllMetrics(); - const zvksMetrics = metricNames.filter(metric => - metric.startsWith('zvks') || - metric.includes('server_li') || - metric.includes('application_li') + const zvksMetrics = metricNames.filter(metric => + metric.startsWith('zvks') || + metric.includes('server_li') || + metric.includes('application_li') ); const promises = zvksMetrics.map(async (metric) => { @@ -308,7 +599,6 @@ export class PrometheusService { } clearCache(): void { - this.metricCache.clear(); - this.metadataCache.clear(); + this.cacheService.clearCache(); } } \ No newline at end of file -- 2.40.1 From d8a8d6b8e5b8d5bb05500867282d1746b89e1cff Mon Sep 17 00:00:00 2001 From: SovietSpiderCat Date: Fri, 22 Aug 2025 09:58:26 +0300 Subject: [PATCH 2/3] added complex variables --- logs.txt | Bin 126014 -> 1666028 bytes src/menu/menu.service.ts | 154 ++++++++++++- src/prometheus/prometheus.service.ts | 316 --------------------------- 3 files changed, 143 insertions(+), 327 deletions(-) diff --git a/logs.txt b/logs.txt index c0969e7f9fc79999e570239463561a626b62a7cd..134fcc48aadf9b96e17f7e0ec5d13b22eae55936 100644 GIT binary patch literal 1666028 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 literal 126014 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 diff --git a/src/menu/menu.service.ts b/src/menu/menu.service.ts index 7d88842..b9ba54c 100644 --- a/src/menu/menu.service.ts +++ b/src/menu/menu.service.ts @@ -187,41 +187,171 @@ export class MenuService { private normalizeIdPart(part: string): string { return part .replace(/\$/g, '_') - .replace(/[^a-zA-Z0-9-_]/g, ''); + .replace(/,/g, '_') // Заменяем запятые + .replace(/\s+/g, '_') // Заменяем пробелы + .replace(/[^a-zA-Z0-9-_]/g, '') + .toLowerCase(); } + // private async generateModuleItems( + // device: string, + // seriesData: { metric: string; labels: Record }[], + // metadataMap: Map + // ): Promise { + // const modules = new Map(); + + // seriesData.forEach(({ labels }) => { + // if (labels.device === device && labels.source_id) { + // const sourceId = labels.source_id; + // let displayName = sourceId; + + // if (sourceId.startsWith('module$')) { + // displayName = `Module ${sourceId.split('$')[1]}`; + // } else if (sourceId.startsWith('port$')) { + // displayName = `Port ${sourceId.split('$')[1]}`; + // } + + // modules.set(sourceId, displayName); + // } + // }); + + // const modulePromises = Array.from(modules.entries()).map( + // async ([sourceId, displayName]) => ({ + // id: `module_${device}_${sourceId}`, + // title: displayName, + // items: await this.generateMetricItems(device, sourceId, seriesData, metadataMap), + // isDynamic: true + // }) + // ); + + // return Promise.all(modulePromises); + // } + + // private async generateMetricItems( + // device: string, + // module: string, + // seriesData: { metric: string; labels: Record }[], + // metadataMap: Map + // ): Promise { + // const ranges = await this.rangeService.getRanges(); + // const filtered = seriesData.filter( + // ({ labels }) => labels.device === device && labels.source_id === module + // ); + + // const uniqueMetrics = new Set(filtered.map(entry => entry.metric)); + // const safeDevice = this.normalizeIdPart(device); + // const safeModule = this.normalizeIdPart(module); + + // return Array.from(uniqueMetrics).map(metric => { + // const description = metadataMap.get(metric) || metric; + // const safeMetric = this.normalizeIdPart(metric); + // const metricRanges = ranges[description] || []; + + // return { + // id: `metric_${safeDevice}_${safeModule}_${safeMetric}`, + // title: description, + // metric, + // filters: { + // device, + // source_id: module + // }, + // ranges: metricRanges, + // isDynamic: true, + // meta: { + // originalDevice: device, + // originalModule: module + // } + // }; + // }); + // } + + //ВРЕМЕННЫЙ КОСТЫЛЬ + private async generateModuleItems( device: string, seriesData: { metric: string; labels: Record }[], metadataMap: Map ): Promise { const modules = new Map(); + const specialFolders = new Map>(); seriesData.forEach(({ labels }) => { if (labels.device === device && labels.source_id) { const sourceId = labels.source_id; - let displayName = sourceId; - if (sourceId.startsWith('module$')) { - displayName = `Module ${sourceId.split('$')[1]}`; - } else if (sourceId.startsWith('port$')) { - displayName = `Port ${sourceId.split('$')[1]}`; + // Проверяем наличие специальных тегов (complex, integration) + if (sourceId.includes(', complex') || sourceId.includes(', integration')) { + const [modulePart, folderType] = sourceId.split(', ').map(s => s.trim()); + let displayName = modulePart; + + if (modulePart.startsWith('module$')) { + displayName = `Module ${modulePart.split('$')[1]}`; + } else if (modulePart.startsWith('port$')) { + displayName = `Port ${modulePart.split('$')[1]}`; + } else if (modulePart === 'undefined') { + displayName = 'Unknown Module'; + } + + // Сохраняем в специальные папки + if (!specialFolders.has(folderType)) { + specialFolders.set(folderType, new Map()); + } + specialFolders.get(folderType)!.set(modulePart, displayName); + } + // Проверяем старые некорректные форматы (без запятой) + else if (sourceId.endsWith('complex') || sourceId.endsWith('integration')) { + // Игнорируем старые некорректные форматы + console.warn(`Ignoring legacy format: ${sourceId} for device ${device}`); + } + else { + // Обычная обработка + let displayName = sourceId; + if (sourceId.startsWith('module$')) { + displayName = `Module ${sourceId.split('$')[1]}`; + } else if (sourceId.startsWith('port$')) { + displayName = `Port ${sourceId.split('$')[1]}`; + } + modules.set(sourceId, displayName); } - - modules.set(sourceId, displayName); } }); - const modulePromises = Array.from(modules.entries()).map( + const moduleItems = Array.from(modules.entries()).map( async ([sourceId, displayName]) => ({ - id: `module_${device}_${sourceId}`, + id: `module_${device}_${this.normalizeIdPart(sourceId)}`, title: displayName, items: await this.generateMetricItems(device, sourceId, seriesData, metadataMap), isDynamic: true }) ); - return Promise.all(modulePromises); + // Создаем специальные папки + const specialFolderItems = Array.from(specialFolders.entries()).map( + async ([folderType, folderModules]) => { + const folderItems = await Promise.all( + Array.from(folderModules.entries()).map( + async ([sourceId, displayName]) => ({ + id: `module_${device}_${this.normalizeIdPart(sourceId)}_${this.normalizeIdPart(folderType)}`, + title: displayName, + items: await this.generateMetricItems(device, `${sourceId}, ${folderType}`, seriesData, metadataMap), + isDynamic: true + }) + ) + ); + + return { + id: `folder_${device}_${this.normalizeIdPart(folderType)}`, + title: folderType, + items: folderItems, + isDynamic: true + }; + } + ); + + return [ + ...(await Promise.all(moduleItems)), + ...(await Promise.all(specialFolderItems)) + ]; } private async generateMetricItems( @@ -231,6 +361,8 @@ export class MenuService { metadataMap: Map ): Promise { const ranges = await this.rangeService.getRanges(); + + // Фильтруем по device и точному совпадению source_id const filtered = seriesData.filter( ({ labels }) => labels.device === device && labels.source_id === module ); diff --git a/src/prometheus/prometheus.service.ts b/src/prometheus/prometheus.service.ts index 08876c6..3388095 100644 --- a/src/prometheus/prometheus.service.ts +++ b/src/prometheus/prometheus.service.ts @@ -1,319 +1,3 @@ -// import { Injectable } from '@nestjs/common'; -// import { HttpService } from '@nestjs/axios'; -// import { ConfigService } from '@nestjs/config'; -// import { lastValueFrom } from 'rxjs'; -// import { PrometheusMetric } from './prometheus-metric.interface'; -// import { MenuItem } from '../menu/menu.interface'; - -// @Injectable() -// export class PrometheusService { -// private readonly prometheusUrl: string; -// private metricCache = new Map(); -// private metadataCache = new Map(); - -// constructor( -// private readonly httpService: HttpService, -// private readonly configService: ConfigService -// ) { -// this.prometheusUrl = this.configService.get('PROMETHEUS_API', 'http://localhost:9090'); -// console.log('Prometheus API URL:', this.prometheusUrl); -// } - -// async fetchMetricType(metric: string): Promise { -// const cacheKey = `metadata-type-${metric}`; -// const cacheEntry = this.metadataCache.get(cacheKey); - -// if (cacheEntry && Date.now() - cacheEntry.timestamp < 30000) { -// return cacheEntry.type; -// } - -// try { -// const response = await lastValueFrom( -// this.httpService.get(`${this.prometheusUrl}/metadata`, { -// params: { metric }, -// }) -// ); -// const metadata = response.data.data[metric]; -// const result = metadata?.length ? metadata[0].type : null; - -// this.metadataCache.set(cacheKey, { -// type: result, -// description: cacheEntry?.description, -// timestamp: Date.now() -// }); - -// return result; -// } catch (error) { -// console.error(`Ошибка при получении типа метрики ${metric}:`, error); -// return cacheEntry?.type || null; -// } -// } - -// async fetchMetricDescription(metric: string): Promise { -// const cacheKey = `metadata-description-${metric}`; -// const cacheEntry = this.metadataCache.get(cacheKey); - -// if (cacheEntry && Date.now() - cacheEntry.timestamp < 30000) { -// return cacheEntry.description; -// } - -// try { -// const response = await lastValueFrom( -// this.httpService.get(`${this.prometheusUrl}/metadata`, { -// params: { metric }, -// }) -// ); -// const metadata = response.data.data[metric]; -// const result = metadata?.length ? metadata[0].help : undefined; - -// this.metadataCache.set(cacheKey, { -// type: cacheEntry?.type ?? null, -// description: result, -// timestamp: Date.now() -// }); - -// return result; -// } catch (error) { -// console.error(`Ошибка при получении описания метрики ${metric}:`, error); -// return cacheEntry?.description; -// } -// } - -// async fetchMetrics(metric: string): Promise { -// const cacheKey = `${metric}:{}`; -// const cacheEntry = this.metricCache.get(cacheKey); - -// if (cacheEntry && Date.now() - cacheEntry.timestamp < 5000) { -// return cacheEntry.data; -// } - -// try { -// const response = await lastValueFrom( -// this.httpService.get(`${this.prometheusUrl}/query`, { -// params: { query: metric }, -// }) -// ); - -// const metricType = await this.fetchMetricType(metric); -// const metricDescription = await this.fetchMetricDescription(metric); - -// const result = response.data.data.result.map((entry): PrometheusMetric => ({ -// __name__: entry.metric.__name__ || metric, -// device: entry.metric.device, -// instance: entry.metric.instance, -// job: entry.metric.job, -// source_id: entry.metric.source_id, -// status: entry.metric.status || '0', -// timestamp: entry.value[0] * 1000, -// value: parseFloat(entry.value[1]), -// type: metricType || 'gauge', -// description: metricDescription, -// ...entry.metric -// })); - -// this.metricCache.set(cacheKey, { data: result, timestamp: Date.now() }); -// return result; -// } catch (error) { -// console.error(`Error fetching metrics for ${metric}:`, error); -// if (cacheEntry) return cacheEntry.data; -// throw error; -// } -// } - -// async fetchMetricsWithFilters(metric: string, filters: Record): Promise { -// const cacheKey = `${metric}:${JSON.stringify(filters)}`; -// const cacheEntry = this.metricCache.get(cacheKey); - -// if (cacheEntry && Date.now() - cacheEntry.timestamp < 5000) { -// return cacheEntry.data; -// } - -// try { -// const query = this.buildFilteredQuery(metric, filters); -// const response = await lastValueFrom( -// this.httpService.get(`${this.prometheusUrl}/query`, { -// params: { query } -// }) -// ); - -// const metricType = await this.fetchMetricType(metric); -// const metricDescription = await this.fetchMetricDescription(metric); - -// const result = response.data.data.result.map((entry): PrometheusMetric => ({ -// __name__: entry.metric.__name__ || metric, -// device: entry.metric.device, -// instance: entry.metric.instance, -// job: entry.metric.job, -// source_id: entry.metric.source_id, -// status: entry.metric.status || '0', -// timestamp: entry.value[0] * 1000, -// value: parseFloat(entry.value[1]), -// type: metricType || 'gauge', -// description: metricDescription, -// ...entry.metric -// })); - -// this.metricCache.set(cacheKey, { data: result, timestamp: Date.now() }); -// return result; -// } catch (error) { -// console.error(`Error fetching metrics with filters for ${metric}:`, error); -// if (cacheEntry) return cacheEntry.data; -// throw error; -// } -// } - -// private buildFilteredQuery(metric: string, filters: Record): string { -// const filterParts = Object.entries(filters) -// .filter(([_, value]) => value !== undefined && value !== null && value !== "") -// .map(([key, value]) => { -// return `${key}="${value}"`; -// }); - -// return filterParts.length > 0 -// ? `${metric}{${filterParts.join(',')}}` -// : metric; -// } - -// async fetchMetricsRange(metric: string, start: number, end: number, step: number, filters: Record = {}): Promise { -// // Рассчитываем оптимальный шаг, если не указан -// const duration = end - start; -// const optimalStep = Math.max(Math.floor(duration / 1000), 15); // Минимум 15 секунд - -// const query = this.buildFilteredQuery(metric, { -// ...filters, -// instance: '192.168.2.34:9050' -// }); - -// try { -// const response = await lastValueFrom( -// this.httpService.get(`${this.prometheusUrl}/query_range`, { -// params: { -// query, -// start, -// end, -// step: optimalStep.toString() -// }, -// }) -// ); - -// const metricType = await this.fetchMetricType(metric); -// const metricDescription = await this.fetchMetricDescription(metric); - -// return response.data.data.result.flatMap((entry) => -// entry.values.map((value): PrometheusMetric => ({ -// __name__: entry.metric.__name__ || metric, -// device: entry.metric.device, -// instance: entry.metric.instance, -// job: entry.metric.job, -// source_id: entry.metric.source_id, -// status: entry.metric.status || '0', -// timestamp: value[0] * 1000, -// value: parseFloat(value[1]), -// type: metricType || 'gauge', -// description: metricDescription, -// ...entry.metric -// })) -// ); -// } catch (error) { -// console.error('Error in fetchMetricsRange:', { -// error: error.response?.data || error.message, -// query, -// filters -// }); -// throw error; -// } -// } - -// async getMetricsForMenuItem(menuItem: MenuItem): Promise { -// if (!menuItem.metric || !menuItem.filters) { -// throw new Error('MenuItem is not a metric item'); -// } - -// return this.fetchMetricsWithFilters(menuItem.metric, menuItem.filters); -// } - -// async fetchMetricMetadata(metric: string): Promise<{ -// name: string; -// help?: string; -// type?: string; -// }> { -// try { -// const response = await lastValueFrom( -// this.httpService.get(`${this.prometheusUrl}/metadata`, { -// params: { metric } -// }) -// ); - -// const data = response.data?.data?.[metric]?.[0]; - -// return { -// name: metric, -// help: data?.help, -// type: data?.type -// }; -// } catch (error) { -// console.error(`Error fetching metadata for ${metric}:`, error); -// return { -// name: metric -// }; -// } -// } - -// async fetchMetricSeries(metric: string): Promise[]> { -// try { -// const response = await lastValueFrom( -// this.httpService.get(`${this.prometheusUrl}/series`, { -// params: { 'match[]': metric } -// }) -// ); - -// return response.data.data || []; -// } catch (error) { -// console.error(`Error fetching series for ${metric}:`, error); -// return []; -// } -// } - -// async fetchAllMetrics(): Promise { -// try { -// const response = await lastValueFrom( -// this.httpService.get(`${this.prometheusUrl}/label/__name__/values`) -// ); -// return response.data.data; -// } catch (error) { -// console.error('Error fetching all metrics:', error); -// return []; -// } -// } - -// async fetchAllMetricsWithValues(): Promise { -// const metricNames = await this.fetchAllMetrics(); -// const zvksMetrics = metricNames.filter(metric => -// metric.startsWith('zvks') || -// metric.includes('server_li') || -// metric.includes('application_li') -// ); - -// const promises = zvksMetrics.map(async (metric) => { -// try { -// const data = await this.fetchMetrics(metric); -// return { metric, data }; -// } catch (error) { -// console.error(`Error fetching data for metric ${metric}:`, error); -// return { metric, data: [] }; -// } -// }); - -// return Promise.all(promises); -// } - -// clearCache(): void { -// this.metricCache.clear(); -// this.metadataCache.clear(); -// } -// } - - import { Injectable } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; -- 2.40.1 From aaa2482f048581832354f7c64819ceb56937cb5b Mon Sep 17 00:00:00 2001 From: DmitriyA Date: Thu, 28 Aug 2025 09:15:42 -0400 Subject: [PATCH 3/3] added menu editor --- src/MenuItem.interface.ts | 10 -- src/menu/menu.controller.ts | 76 ++++++++-- src/menu/menu.interface.ts | 1 + src/menu/{menu-overrides.json => menu.json} | 0 src/menu/menu.service.ts | 159 ++++++++++++++------ 5 files changed, 175 insertions(+), 71 deletions(-) delete mode 100644 src/MenuItem.interface.ts rename src/menu/{menu-overrides.json => menu.json} (100%) diff --git a/src/MenuItem.interface.ts b/src/MenuItem.interface.ts deleted file mode 100644 index 102f903..0000000 --- a/src/MenuItem.interface.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface MenuItem { - id: string; - title: string; - items?: MenuItem[]; - metric?: string; - filters?: { - device: string; - source_id: string; - }; -} \ No newline at end of file diff --git a/src/menu/menu.controller.ts b/src/menu/menu.controller.ts index 197e747..d7b0ea4 100644 --- a/src/menu/menu.controller.ts +++ b/src/menu/menu.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Post, Put, Body, Param, Headers, HttpException, HttpStatus } from '@nestjs/common'; +import { Controller, Get, Post, Put, Body, Param, Headers, HttpException, HttpStatus, Delete } from '@nestjs/common'; import { MenuService } from './menu.service'; import { MenuItem } from './menu.interface'; @@ -44,24 +44,72 @@ export class MenuController { return { hasUpdates }; } - @Post('save') - async saveMenu() { - await this.menuService.saveMenuToFile(); - return { status: 'saved' }; - } + // @Post('save') + // async saveMenu() { + // await this.menuService.saveMenuToFile(); + // return { status: 'saved' }; + // } - @Post('overrides') - async saveOverrides(@Body() data: { overrides: Partial[] }) { - await this.menuService.saveOverrides(data.overrides); - return { status: 'success' }; - } + // @Post('overrides') + // async saveOverrides(@Body() data: { overrides: Partial[] }) { + // await this.menuService.saveOverrides(data.overrides); + // return { status: 'success' }; + // } @Put(':id') async updateMenuItem( @Param('id') id: string, @Body() update: Partial ) { - const updatedItem = await this.menuService.updateMenuItem(id, update); - return updatedItem; + try { + const updatedItem = await this.menuService.updateMenuItem(id, update); + return updatedItem; + } catch (error) { + throw new HttpException( + error.message || 'Failed to update menu item', + HttpStatus.INTERNAL_SERVER_ERROR + ); + } } -} \ No newline at end of file + + @Delete('items/:id') + async deleteMenuItem(@Param('id') id: string) { + console.log(`DELETE /menu/items/${id} requested`); + try { + await this.menuService.hideMenuItem(id); + console.log(`Item ${id} hidden successfully`); + return { success: true, message: 'Item hidden successfully' }; + } catch (error) { + console.error(`Error hiding item ${id}:`, error); + throw new HttpException( + error.message || 'Failed to hide menu item', + HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } + + @Post('invalidate-cache') + async invalidateCache() { + try { + this.menuService.invalidateCache(); + return { success: true, message: 'Cache invalidated successfully' }; + } catch (error) { + throw new HttpException( + error.message || 'Failed to invalidate cache', + HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } + + @Get('debug/overrides') + async debugOverrides() { + try { + return { status: 'Debug endpoint not implemented' }; + } catch (error) { + throw new HttpException( + error.message || 'Debug failed', + HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } +} diff --git a/src/menu/menu.interface.ts b/src/menu/menu.interface.ts index 6608d56..f2b447c 100644 --- a/src/menu/menu.interface.ts +++ b/src/menu/menu.interface.ts @@ -11,4 +11,5 @@ export interface MenuItem { max: number; status: number; }>; + hidden?: boolean; } \ No newline at end of file diff --git a/src/menu/menu-overrides.json b/src/menu/menu.json similarity index 100% rename from src/menu/menu-overrides.json rename to src/menu/menu.json diff --git a/src/menu/menu.service.ts b/src/menu/menu.service.ts index b9ba54c..d07ec6c 100644 --- a/src/menu/menu.service.ts +++ b/src/menu/menu.service.ts @@ -10,29 +10,28 @@ export class MenuService { private menuCache: MenuItem | null = null; private lastModified: Date | null = null; private cacheInitialized = false; + private userOverrides: Map> = new Map(); constructor( private readonly prometheusService: PrometheusService, private readonly rangeService: RangeService ) { } - private readonly menuOverridesPath = path.join(process.cwd(), 'data', 'menu.json'); - async saveMenuToFile(): Promise { - const { menu } = await this.getFullMenuWithCache(); - await fs.mkdir(path.dirname(this.menuOverridesPath), { recursive: true }); - await fs.writeFile(this.menuOverridesPath, JSON.stringify(menu, null, 2), 'utf-8'); - } + private readonly userOverridesPath = path.join(process.cwd(), 'data', 'user_menu_overrides.json'); + async getFullMenuWithCache(ifModifiedSince?: string): Promise<{ menu: MenuItem; fresh: boolean }> { if (this.menuCache && this.lastModified && (!ifModifiedSince || new Date(ifModifiedSince) >= this.lastModified)) { return { menu: this.menuCache, fresh: false }; } + await this.loadUserOverrides(); + const dynamicItemsPromise = this.generateDynamicItems(); const baseMenu = await this.injectDynamicItems(this.getStaticStructure(), dynamicItemsPromise); - const overrides = await this.loadOverrides(); - const freshMenu = this.applyOverrides(baseMenu, overrides); + + const freshMenu = this.applyUserOverrides(baseMenu); this.menuCache = freshMenu; this.lastModified = new Date(); @@ -48,33 +47,75 @@ export class MenuService { return !this.lastModified || new Date(ifModifiedSince) < this.lastModified; } - private applyOverrides(menu: MenuItem, overrides: Partial[]): MenuItem { - const overrideMap = new Map(overrides.map(o => [o.id, o])); + async hideMenuItem(id: string): Promise { + this.userOverrides.set(id, { id, hidden: true }); + await this.saveUserOverrides(); + this.invalidateCache(); + } - const apply = (item: MenuItem): MenuItem => { - const override = overrideMap.get(item.id); - const updated = override ? { ...item, ...override } : item; + private applyUserOverrides(menu: MenuItem): MenuItem { + const apply = (item: MenuItem): MenuItem | null => { + const override = this.userOverrides.get(item.id); + + if (override?.hidden) { + return null; + } + + const updated: MenuItem = { + ...item, + ...override, + hidden: undefined + }; if (updated.items) { - updated.items = updated.items.map(apply); + const processedItems = updated.items + .map(apply) + .filter((item): item is MenuItem => item !== null); + + updated.items = processedItems.length > 0 ? processedItems : undefined; } return updated; }; - return apply(menu); + const result = apply(menu); + return result || { title: menu.title, id: menu.id, items: [] }; } - private async loadOverrides(): Promise[]> { + private async loadUserOverrides(): Promise { try { - const content = await fs.readFile(this.menuOverridesPath, 'utf-8'); + const content = await fs.readFile(this.userOverridesPath, 'utf-8'); const parsed = JSON.parse(content); - return parsed.overrides || []; + + this.userOverrides = new Map( + (parsed.overrides || []).map(o => [o.id, o]) + ); } catch (e) { - return []; + this.userOverrides = new Map(); + await this.saveUserOverrides(); // Создаем файл с пустыми данными } } + + private async saveUserOverrides(): Promise { + try { + await fs.mkdir(path.dirname(this.userOverridesPath), { recursive: true }); + const overridesArray = Array.from(this.userOverrides.values()); + await fs.writeFile( + this.userOverridesPath, + JSON.stringify({ overrides: overridesArray }, null, 2), + 'utf-8' + ); + } catch (error) { + console.error('Error saving user overrides:', error); + throw new HttpException( + 'Failed to save user preferences', + HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } + + private getStaticStructure(): MenuItem { return { title: "ЗВКС", @@ -166,9 +207,12 @@ export class MenuService { metadataMap: Map ): Promise { const moduleItems = await this.generateModuleItems(device, seriesData, metadataMap); + + const deviceName = metadataMap.get(device) ?? device; + return { id: `device_${device}`, - title: `Graviton S2082I (${device})`, + title: deviceName, items: moduleItems, isDynamic: true }; @@ -187,12 +231,13 @@ export class MenuService { private normalizeIdPart(part: string): string { return part .replace(/\$/g, '_') - .replace(/,/g, '_') // Заменяем запятые - .replace(/\s+/g, '_') // Заменяем пробелы + .replace(/,/g, '_') + .replace(/\s+/g, '_') .replace(/[^a-zA-Z0-9-_]/g, '') .toLowerCase(); } + // private async generateModuleItems( // device: string, // seriesData: { metric: string; labels: Record }[], @@ -277,9 +322,8 @@ export class MenuService { seriesData.forEach(({ labels }) => { if (labels.device === device && labels.source_id) { - const sourceId = labels.source_id; + const sourceId = this.normalizeSourceId(labels.source_id); - // Проверяем наличие специальных тегов (complex, integration) if (sourceId.includes(', complex') || sourceId.includes(', integration')) { const [modulePart, folderType] = sourceId.split(', ').map(s => s.trim()); let displayName = modulePart; @@ -292,19 +336,15 @@ export class MenuService { displayName = 'Unknown Module'; } - // Сохраняем в специальные папки if (!specialFolders.has(folderType)) { specialFolders.set(folderType, new Map()); } specialFolders.get(folderType)!.set(modulePart, displayName); } - // Проверяем старые некорректные форматы (без запятой) else if (sourceId.endsWith('complex') || sourceId.endsWith('integration')) { - // Игнорируем старые некорректные форматы console.warn(`Ignoring legacy format: ${sourceId} for device ${device}`); } else { - // Обычная обработка let displayName = sourceId; if (sourceId.startsWith('module$')) { displayName = `Module ${sourceId.split('$')[1]}`; @@ -325,7 +365,6 @@ export class MenuService { }) ); - // Создаем специальные папки const specialFolderItems = Array.from(specialFolders.entries()).map( async ([folderType, folderModules]) => { const folderItems = await Promise.all( @@ -333,7 +372,12 @@ export class MenuService { async ([sourceId, displayName]) => ({ id: `module_${device}_${this.normalizeIdPart(sourceId)}_${this.normalizeIdPart(folderType)}`, title: displayName, - items: await this.generateMetricItems(device, `${sourceId}, ${folderType}`, seriesData, metadataMap), + items: await this.generateMetricItems( + device, + `${sourceId}, ${folderType}`, + seriesData, + metadataMap + ), isDynamic: true }) ) @@ -354,6 +398,10 @@ export class MenuService { ]; } + private normalizeSourceId(raw: string): string { + return raw.split(',').map(s => s.trim()).filter(Boolean).join(', '); + } + private async generateMetricItems( device: string, module: string, @@ -362,14 +410,32 @@ export class MenuService { ): Promise { const ranges = await this.rangeService.getRanges(); - // Фильтруем по device и точному совпадению source_id - const filtered = seriesData.filter( - ({ labels }) => labels.device === device && labels.source_id === module + const normModule = this.normalizeSourceId(module); + const isPlainModule = !normModule.includes(','); + + let filtered = seriesData.filter(({ labels }) => + labels.device === device && + this.normalizeSourceId(labels.source_id || '') === normModule ); + if (isPlainModule) { + const base = normModule; + const shadowSuffixes = ['integration', 'complex']; + + filtered = filtered.filter(entry => { + return !shadowSuffixes.some(suffix => + seriesData.some(s => + s.metric === entry.metric && + s.labels.device === device && + this.normalizeSourceId(s.labels.source_id || '') === `${base}, ${suffix}` + ) + ); + }); + } + const uniqueMetrics = new Set(filtered.map(entry => entry.metric)); const safeDevice = this.normalizeIdPart(device); - const safeModule = this.normalizeIdPart(module); + const safeModule = this.normalizeIdPart(normModule); return Array.from(uniqueMetrics).map(metric => { const description = metadataMap.get(metric) || metric; @@ -382,13 +448,13 @@ export class MenuService { metric, filters: { device, - source_id: module + source_id: normModule }, ranges: metricRanges, isDynamic: true, meta: { originalDevice: device, - originalModule: module + originalModule: normModule } }; }); @@ -415,21 +481,20 @@ export class MenuService { } async updateMenuItem(id: string, update: Partial): Promise { - const { menu: fullMenu } = await this.getFullMenuWithCache(); - const item = this.findMenuItem(fullMenu, id); + const existing = this.userOverrides.get(id) || { id }; + this.userOverrides.set(id, { ...existing, ...update }); - if (!item) throw new Error('Menu item not found'); - Object.assign(item, update); + await this.saveUserOverrides(); + this.invalidateCache(); + const { menu } = await this.getFullMenuWithCache(); + const updated = this.findMenuItem(menu, id); - this.menuCache = null; - return item; - } + if (!updated) { + throw new HttpException('Updated item not found', HttpStatus.NOT_FOUND); + } - async saveOverrides(overrides: Partial[]): Promise { - await fs.writeFile(this.menuOverridesPath, JSON.stringify({ overrides }, null, 2), 'utf-8'); - - this.menuCache = null; + return updated; } invalidateCache(): void { -- 2.40.1