fix: correct operational statistics and form interactions
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
export function alertHistoryRange(from?: string, to?: string, now = new Date()) {
|
||||
const dateKey = (date: Date) => new Date(date.getTime() + 8 * 3600_000).toISOString().slice(0, 10);
|
||||
const endDate = to || dateKey(now);
|
||||
const startDate = from || dateKey(new Date(now.getTime() - 6 * 86400_000));
|
||||
const parse = (value: string) => {
|
||||
const result = new Date(`${value}T00:00:00+08:00`);
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || !Number.isFinite(result.getTime()) || dateKey(result) !== value) {
|
||||
throw new BadRequestException('告警日期无效');
|
||||
}
|
||||
return result.getTime() / 1000;
|
||||
};
|
||||
const start = parse(startDate);
|
||||
const end = parse(endDate) + 86400;
|
||||
if (end <= start || end - start > 31 * 86400) throw new BadRequestException('告警日期范围须为1至31天');
|
||||
return { startDate, endDate, start, end: Math.min(end, now.getTime() / 1000) };
|
||||
}
|
||||
|
||||
export type AlertHistoryItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
severity: string;
|
||||
service: string;
|
||||
instance: string;
|
||||
startedAt: string;
|
||||
firstObservedAt: string;
|
||||
lastObservedAt: string;
|
||||
};
|
||||
|
||||
// ALERTS_FOR_STATE stores activeAt as the sample value, separating repeated trigger cycles.
|
||||
// Observation boundaries are not claimed as exact recovery times.
|
||||
export function mergeAlertHistory(
|
||||
target: Map<string, AlertHistoryItem>,
|
||||
series: Array<{ metric: Record<string, string>; values?: [number, string][] }>,
|
||||
start: number,
|
||||
end: number,
|
||||
) {
|
||||
for (const { metric, values } of series) {
|
||||
const labels = Object.entries(metric)
|
||||
.filter(([key]) => key !== '__name__')
|
||||
.sort(([a], [b]) => a.localeCompare(b));
|
||||
const fingerprint = createHash('sha256').update(JSON.stringify(labels)).digest('hex');
|
||||
for (const [time, rawActiveAt] of values ?? []) {
|
||||
const activeAt = Number(rawActiveAt);
|
||||
if (time < start || time >= end || !Number.isFinite(activeAt) || activeAt <= 0 || activeAt > time) continue;
|
||||
const id = `${fingerprint}:${activeAt}`;
|
||||
const observed = new Date(time * 1000).toISOString();
|
||||
const item = target.get(id);
|
||||
if (item) {
|
||||
if (observed < item.firstObservedAt) item.firstObservedAt = observed;
|
||||
if (observed > item.lastObservedAt) item.lastObservedAt = observed;
|
||||
} else {
|
||||
target.set(id, {
|
||||
id,
|
||||
name: metric.alertname || '未命名告警',
|
||||
severity: metric.severity || 'info',
|
||||
service: metric.service || '',
|
||||
instance: metric.instance || '',
|
||||
startedAt: new Date(activeAt * 1000).toISOString(),
|
||||
firstObservedAt: observed,
|
||||
lastObservedAt: observed,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user