Compare commits
2 Commits
ff3bf02d2e
...
926ea01235
| Author | SHA1 | Date |
|---|---|---|
|
|
926ea01235 | |
|
|
8466aa1f93 |
21
.env
21
.env
|
|
@ -1,9 +1,9 @@
|
|||
#Прометеус
|
||||
# Прометеус
|
||||
#PROMETHEUS_API=http://192.168.2.34:9090/api/v1
|
||||
|
||||
#FRONTEND_URL=192.168.2.39:5173
|
||||
|
||||
#Постгресс
|
||||
# Постгресс
|
||||
#DB_HOST=192.168.2.37
|
||||
#DB_PORT=5432
|
||||
#DB_USER=trust
|
||||
|
|
@ -11,19 +11,28 @@
|
|||
#DB_NAME=trust-db
|
||||
|
||||
|
||||
#JWT
|
||||
# JWT
|
||||
#JWT_SECRET=x7F!2p9L#q1$z0*8R5vYgMnBk
|
||||
#JWT_SECRET=x7Fcdp9L#q1$z0*8R5vYgMnBk
|
||||
|
||||
#COOKIE
|
||||
# COOKIE
|
||||
# Для production
|
||||
#COOKIE_SECURE=true
|
||||
#COOKIE_SAME_SITE=strict
|
||||
|
||||
# Для development
|
||||
# COOKIE_SECURE=false
|
||||
# COOKIE_SAME_SITE=lax
|
||||
#COOKIE_SECURE=false
|
||||
#COOKIE_SAME_SITE=lax
|
||||
|
||||
# Для меню
|
||||
#RANGES_API_URL=http://192.168.2.39:9999
|
||||
#RANGES_API_ENDPOINT=/api/ranges/9999
|
||||
|
||||
# ClickHouse
|
||||
#CLICKHOUSE_HOST=http://192.168.2.37:8123
|
||||
#CLICKHOUSE_USER=vlad
|
||||
#CLICKHOUSE_PASSWORD=vlad
|
||||
#CLICKHOUSE_DB=zvks
|
||||
|
||||
# Для ai api
|
||||
#ANALYSIS_API_URL=http://192.168.2.39:5134/models/api/metrics/rest
|
||||
|
|
@ -44,7 +44,10 @@
|
|||
"@types/cookie-parser": "^1.4.8",
|
||||
"@nestjs/jwt": "^11.0.0",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/swagger": "11.1.4"
|
||||
"@nestjs/swagger": "11.1.4",
|
||||
"@clickhouse/client": "^1.11.2",
|
||||
"date-fns": "4.1.0",
|
||||
"@clickhouse/client-web": "^1.11.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import { HttpModule } from '@nestjs/axios';
|
|||
import { ConfigModule } from '@nestjs/config';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { MenuModule } from './menu/menu.module';
|
||||
import { PrometheusModule } from './prometheus.module';
|
||||
import { PrometheusModule } from './prometheus/prometheus.module';
|
||||
import { ClickHouseModule } from './clickhouse/clickhouse.module';
|
||||
import { ClickHouseController } from './clickhouse/clickhouse.controller';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -27,6 +29,8 @@ import { PrometheusModule } from './prometheus.module';
|
|||
AuthModule,
|
||||
PrometheusModule,
|
||||
MenuModule,
|
||||
ClickHouseModule,
|
||||
],
|
||||
controllers: [ClickHouseController],
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule { }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ClickHouseService } from './clickhouse.service';
|
||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@ApiTags('Clickhouse')
|
||||
@Controller('clickhouse')
|
||||
export class ClickHouseController {
|
||||
constructor(private readonly clickhouseService: ClickHouseService) { }
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Get metrics from ClickHouse' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Metrics data',
|
||||
schema: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
description: { type: 'string' },
|
||||
device: { type: 'number' },
|
||||
id: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
source: { type: 'string' },
|
||||
status: { type: 'number' },
|
||||
timestamp: { type: 'number' },
|
||||
value: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
async getClckhouse() {
|
||||
return this.clickhouseService.getClckhouse();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { Module, Global } from '@nestjs/common';
|
||||
import { createClient, ClickHouseClient } from '@clickhouse/client';
|
||||
import { ClickHouseService } from './clickhouse.service';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: 'CLICKHOUSE_CLIENT',
|
||||
useFactory: (): ClickHouseClient => {
|
||||
return createClient({
|
||||
host: process.env.CLICKHOUSE_HOST || 'http://localhost:8123',
|
||||
username: process.env.CLICKHOUSE_USER || 'default',
|
||||
password: process.env.CLICKHOUSE_PASSWORD || '',
|
||||
database: process.env.CLICKHOUSE_DB || 'default',
|
||||
});
|
||||
},
|
||||
},
|
||||
ClickHouseService,
|
||||
],
|
||||
exports: ['CLICKHOUSE_CLIENT', ClickHouseService],
|
||||
})
|
||||
export class ClickHouseModule { }
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { ClickHouseClient } from '@clickhouse/client';
|
||||
|
||||
interface ClickHouseRow {
|
||||
EventDataTime: string;
|
||||
ParameterBody: string;
|
||||
CreateDataTime: string;
|
||||
}
|
||||
|
||||
interface MetricData {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
addr?: string;
|
||||
value: number | string | null;
|
||||
description: string;
|
||||
status: number;
|
||||
device: number;
|
||||
source: string;
|
||||
}
|
||||
|
||||
interface ParameterBody {
|
||||
service_name: string;
|
||||
metrics: MetricData[];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ClickHouseService {
|
||||
constructor(
|
||||
@Inject('CLICKHOUSE_CLIENT')
|
||||
private readonly clickhouseClient: ClickHouseClient,
|
||||
) { }
|
||||
|
||||
async getClckhouse() {
|
||||
const query = `
|
||||
SELECT
|
||||
EventDataTime,
|
||||
ParameterBody,
|
||||
CreateDataTime
|
||||
FROM zvks.complex_parameters
|
||||
ORDER BY EventDataTime DESC
|
||||
LIMIT 100
|
||||
`;
|
||||
|
||||
const result = await this.clickhouseClient.query({
|
||||
query,
|
||||
format: 'JSONEachRow',
|
||||
});
|
||||
|
||||
const rows = await result.json<ClickHouseRow>();
|
||||
|
||||
// Парсинг данных
|
||||
return rows.flatMap((row: ClickHouseRow) => {
|
||||
try {
|
||||
const parameterBody: ParameterBody = JSON.parse(row.ParameterBody);
|
||||
return parameterBody.metrics.map((metric: MetricData) => ({
|
||||
id: metric.id,
|
||||
name: metric.name,
|
||||
value: metric.value !== null ? metric.value.toString() : 'null',
|
||||
description: metric.description,
|
||||
status: metric.status,
|
||||
device: metric.device,
|
||||
source: metric.source,
|
||||
timestamp: new Date(row.EventDataTime).getTime(),
|
||||
}));
|
||||
} catch (e) {
|
||||
console.error('Error parsing metric:', e);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
|
|||
import { MenuController } from './menu.controller';
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { MenuService } from './menu.service';
|
||||
import { PrometheusModule } from '../prometheus.module';
|
||||
import { PrometheusModule } from '../prometheus/prometheus.module';
|
||||
import { RangeService } from './range.service';
|
||||
import { RangeController } from './range.controller';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { PrometheusService } from '../prometheus.service';
|
||||
import { PrometheusService } from '../prometheus/prometheus.service';
|
||||
import { MenuItem } from './menu.interface';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ 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 { MenuItem } from '../menu/menu.interface';
|
||||
|
||||
@Injectable()
|
||||
export class PrometheusService {
|
||||
|
|
@ -109,22 +109,22 @@ export class PrometheusService {
|
|||
}
|
||||
|
||||
private buildFilteredQuery(metric: string, filters: Record<string, string>): string {
|
||||
const filterParts = Object.entries(filters)
|
||||
.filter(([_, value]) => value !== undefined && value !== null && value !== "")
|
||||
.map(([key, value]) => {
|
||||
// Убираем автоматическое добавление "module$" для source_id
|
||||
return `${key}="${value}"`;
|
||||
});
|
||||
const filterParts = Object.entries(filters)
|
||||
.filter(([_, value]) => value !== undefined && value !== null && value !== "")
|
||||
.map(([key, value]) => {
|
||||
// Убираем автоматическое добавление "module$" для source_id
|
||||
return `${key}="${value}"`;
|
||||
});
|
||||
|
||||
return filterParts.length > 0
|
||||
? `${metric}{${filterParts.join(',')}}`
|
||||
: metric;
|
||||
}
|
||||
return filterParts.length > 0
|
||||
? `${metric}{${filterParts.join(',')}}`
|
||||
: metric;
|
||||
}
|
||||
|
||||
async fetchMetricsRange(metric: string, start: number, end: number, step: number, filters: Record<string, string> = {}): Promise<PrometheusMetric[]> {
|
||||
const query = this.buildFilteredQuery(metric, {
|
||||
...filters,
|
||||
instance: '192.168.2.34:9050'
|
||||
instance: '192.168.2.34:9050'
|
||||
});
|
||||
try {
|
||||
const response = await lastValueFrom(
|
||||
|
|
@ -224,14 +224,14 @@ export class PrometheusService {
|
|||
}
|
||||
|
||||
async fetchAllMetricsWithValues(): Promise<any[]> {
|
||||
const metricNames = await this.fetchAllMetrics();
|
||||
const zvksMetrics = metricNames.filter(metric => metric.startsWith('zvks'));
|
||||
|
||||
const promises = zvksMetrics.map(async (metric) => {
|
||||
const data = await this.fetchMetrics(metric);
|
||||
return { metric, data };
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
const metricNames = await this.fetchAllMetrics();
|
||||
const zvksMetrics = metricNames.filter(metric => metric.startsWith('zvks'));
|
||||
|
||||
const promises = zvksMetrics.map(async (metric) => {
|
||||
const data = await this.fetchMetrics(metric);
|
||||
return { metric, data };
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue