Files
Checkmate/server/src/repositories/monitor-stats/TimescaleMonitorStatsRepository.ts
T
2026-03-25 13:51:12 -07:00

141 lines
4.9 KiB
TypeScript

import type { Pool } from "pg";
import type { IMonitorStatsRepository } from "./IMonitorStatsRepository.js";
import type { MonitorStats } from "@/types/monitorStats.js";
import { AppError } from "@/utils/AppError.js";
interface MonitorStatsRow {
id: string;
monitor_id: string;
avg_response_time: number;
max_response_time: number;
total_checks: number;
total_up_checks: number;
total_down_checks: number;
uptime_percentage: number;
last_check_timestamp: Date | null;
last_response_time: number;
time_of_last_failure: Date | null;
created_at: Date;
updated_at: Date;
}
const COLUMNS = `id, monitor_id, avg_response_time, max_response_time, total_checks, total_up_checks,
total_down_checks, uptime_percentage, last_check_timestamp, last_response_time, time_of_last_failure,
created_at, updated_at`;
export class TimescaleMonitorStatsRepository implements IMonitorStatsRepository {
constructor(private pool: Pool) {}
create = async (data: Omit<MonitorStats, "id" | "createdAt" | "updatedAt">): Promise<MonitorStats> => {
const result = await this.pool.query<MonitorStatsRow>(
`INSERT INTO monitor_stats (monitor_id, avg_response_time, max_response_time, total_checks, total_up_checks,
total_down_checks, uptime_percentage, last_check_timestamp, last_response_time, time_of_last_failure)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING ${COLUMNS}`,
[
data.monitorId,
data.avgResponseTime,
data.maxResponseTime,
data.totalChecks,
data.totalUpChecks,
data.totalDownChecks,
data.uptimePercentage,
data.lastCheckTimestamp ? new Date(data.lastCheckTimestamp) : null,
data.lastResponseTime,
data.timeOfLastFailure ? new Date(data.timeOfLastFailure) : null,
]
);
const row = result.rows[0];
if (!row) {
throw new AppError({ message: "Failed to create monitor stats", status: 500 });
}
return this.toEntity(row);
};
findByMonitorId = async (monitorId: string): Promise<MonitorStats> => {
const result = await this.pool.query<MonitorStatsRow>(`SELECT ${COLUMNS} FROM monitor_stats WHERE monitor_id = $1`, [monitorId]);
const row = result.rows[0];
if (!row) {
throw new AppError({ message: "Monitor stats not found", status: 404 });
}
return this.toEntity(row);
};
updateByMonitorId = async (monitorId: string, data: Omit<MonitorStats, "id" | "monitorId" | "createdAt" | "updatedAt">): Promise<MonitorStats> => {
const result = await this.pool.query<MonitorStatsRow>(
`UPDATE monitor_stats SET
avg_response_time = $2,
max_response_time = $3,
total_checks = $4,
total_up_checks = $5,
total_down_checks = $6,
uptime_percentage = $7,
last_check_timestamp = $8,
last_response_time = $9,
time_of_last_failure = $10,
updated_at = NOW()
WHERE monitor_id = $1
RETURNING ${COLUMNS}`,
[
monitorId,
data.avgResponseTime,
data.maxResponseTime,
data.totalChecks,
data.totalUpChecks,
data.totalDownChecks,
data.uptimePercentage,
data.lastCheckTimestamp ? new Date(data.lastCheckTimestamp) : null,
data.lastResponseTime,
data.timeOfLastFailure ? new Date(data.timeOfLastFailure) : null,
]
);
const row = result.rows[0];
if (!row) {
throw new AppError({ message: "Monitor stats not found", status: 404 });
}
return this.toEntity(row);
};
deleteByMonitorId = async (monitorId: string): Promise<MonitorStats> => {
const result = await this.pool.query<MonitorStatsRow>(`DELETE FROM monitor_stats WHERE monitor_id = $1 RETURNING ${COLUMNS}`, [monitorId]);
const row = result.rows[0];
if (!row) {
throw new AppError({ message: "Monitor stats not found", status: 404 });
}
return this.toEntity(row);
};
deleteByMonitorIds = async (monitorIds: string[]): Promise<number> => {
if (!monitorIds.length) {
return 0;
}
const result = await this.pool.query(`DELETE FROM monitor_stats WHERE monitor_id = ANY($1)`, [monitorIds]);
return result.rowCount ?? 0;
};
deleteByMonitorIdsNotIn = async (monitorIds: string[]): Promise<number> => {
if (!monitorIds.length) {
const result = await this.pool.query(`DELETE FROM monitor_stats`);
return result.rowCount ?? 0;
}
const result = await this.pool.query(`DELETE FROM monitor_stats WHERE monitor_id != ALL($1)`, [monitorIds]);
return result.rowCount ?? 0;
};
private toEntity = (row: MonitorStatsRow): MonitorStats => ({
id: row.id,
monitorId: row.monitor_id,
avgResponseTime: row.avg_response_time,
maxResponseTime: row.max_response_time,
totalChecks: Number(row.total_checks),
totalUpChecks: Number(row.total_up_checks),
totalDownChecks: Number(row.total_down_checks),
uptimePercentage: row.uptime_percentage,
lastCheckTimestamp: row.last_check_timestamp ? row.last_check_timestamp.getTime() : 0,
lastResponseTime: row.last_response_time,
timeOfLastFailure: row.time_of_last_failure ? row.time_of_last_failure.getTime() : undefined,
createdAt: row.created_at.toISOString(),
updatedAt: row.updated_at.toISOString(),
});
}