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
|
#PROMETHEUS_API=http://192.168.2.34:9090/api/v1
|
||||||
|
|
||||||
#FRONTEND_URL=192.168.2.39:5173
|
#FRONTEND_URL=192.168.2.39:5173
|
||||||
|
|
||||||
#Постгресс
|
# Постгресс
|
||||||
#DB_HOST=192.168.2.37
|
#DB_HOST=192.168.2.37
|
||||||
#DB_PORT=5432
|
#DB_PORT=5432
|
||||||
#DB_USER=trust
|
#DB_USER=trust
|
||||||
|
|
@ -11,19 +11,28 @@
|
||||||
#DB_NAME=trust-db
|
#DB_NAME=trust-db
|
||||||
|
|
||||||
|
|
||||||
#JWT
|
# JWT
|
||||||
#JWT_SECRET=x7F!2p9L#q1$z0*8R5vYgMnBk
|
#JWT_SECRET=x7F!2p9L#q1$z0*8R5vYgMnBk
|
||||||
#JWT_SECRET=x7Fcdp9L#q1$z0*8R5vYgMnBk
|
#JWT_SECRET=x7Fcdp9L#q1$z0*8R5vYgMnBk
|
||||||
|
|
||||||
#COOKIE
|
# COOKIE
|
||||||
# Для production
|
# Для production
|
||||||
#COOKIE_SECURE=true
|
#COOKIE_SECURE=true
|
||||||
#COOKIE_SAME_SITE=strict
|
#COOKIE_SAME_SITE=strict
|
||||||
|
|
||||||
# Для development
|
# Для development
|
||||||
# COOKIE_SECURE=false
|
#COOKIE_SECURE=false
|
||||||
# COOKIE_SAME_SITE=lax
|
#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
|
||||||
|
|
||||||
|
# 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",
|
"@types/cookie-parser": "^1.4.8",
|
||||||
"@nestjs/jwt": "^11.0.0",
|
"@nestjs/jwt": "^11.0.0",
|
||||||
"@nestjs/passport": "^11.0.5",
|
"@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": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.2.0",
|
"@eslint/eslintrc": "^3.2.0",
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import { HttpModule } from '@nestjs/axios';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { AuthModule } from './auth/auth.module';
|
import { AuthModule } from './auth/auth.module';
|
||||||
import { MenuModule } from './menu/menu.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({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -27,6 +29,8 @@ import { PrometheusModule } from './prometheus.module';
|
||||||
AuthModule,
|
AuthModule,
|
||||||
PrometheusModule,
|
PrometheusModule,
|
||||||
MenuModule,
|
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 { MenuController } from './menu.controller';
|
||||||
import { HttpModule } from '@nestjs/axios';
|
import { HttpModule } from '@nestjs/axios';
|
||||||
import { MenuService } from './menu.service';
|
import { MenuService } from './menu.service';
|
||||||
import { PrometheusModule } from '../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';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
||||||
import { PrometheusService } from '../prometheus.service';
|
import { PrometheusService } from '../prometheus/prometheus.service';
|
||||||
import { MenuItem } from './menu.interface';
|
import { MenuItem } from './menu.interface';
|
||||||
import * as fs from 'fs/promises';
|
import * as fs from 'fs/promises';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { HttpService } from '@nestjs/axios';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
import { PrometheusMetric } from './prometheus-metric.interface';
|
import { PrometheusMetric } from './prometheus-metric.interface';
|
||||||
import { MenuItem } from './menu/menu.interface';
|
import { MenuItem } from '../menu/menu.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PrometheusService {
|
export class PrometheusService {
|
||||||
|
|
@ -119,7 +119,7 @@ export class PrometheusService {
|
||||||
return filterParts.length > 0
|
return filterParts.length > 0
|
||||||
? `${metric}{${filterParts.join(',')}}`
|
? `${metric}{${filterParts.join(',')}}`
|
||||||
: metric;
|
: metric;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchMetricsRange(metric: string, start: number, end: number, step: number, filters: Record<string, string> = {}): Promise<PrometheusMetric[]> {
|
async fetchMetricsRange(metric: string, start: number, end: number, step: number, filters: Record<string, string> = {}): Promise<PrometheusMetric[]> {
|
||||||
const query = this.buildFilteredQuery(metric, {
|
const query = this.buildFilteredQuery(metric, {
|
||||||
|
|
@ -233,5 +233,5 @@ export class PrometheusService {
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue