2026-03-05 14:11:11 -08:00
|
|
|
import type { Monitor, MonitorStatusResponse } from "@/types/index.js";
|
2026-02-24 19:36:45 +00:00
|
|
|
import type { AxiosStatic } from "axios";
|
2026-02-23 10:19:42 +05:30
|
|
|
import { AppError } from "@/utils/AppError.js";
|
2026-03-05 14:11:11 -08:00
|
|
|
import { NETWORK_ERROR } from "@/service/infrastructure/network/utils.js";
|
2026-02-24 19:19:32 +00:00
|
|
|
import { ILogger } from "@/utils/logger.js";
|
2026-03-04 23:24:48 +00:00
|
|
|
import { IStatusProvider } from "./network/IStatusProvider.js";
|
2025-04-20 11:29:53 -07:00
|
|
|
const SERVICE_NAME = "NetworkService";
|
2025-08-15 16:04:43 -07:00
|
|
|
|
2026-01-15 23:45:40 +00:00
|
|
|
export interface INetworkService {
|
|
|
|
|
readonly serviceName: string;
|
2026-03-05 14:11:11 -08:00
|
|
|
requestStatus(monitor: Monitor): Promise<MonitorStatusResponse<unknown>>;
|
2026-03-03 19:34:28 +00:00
|
|
|
requestWebhook(
|
|
|
|
|
type: string,
|
|
|
|
|
url: string,
|
|
|
|
|
body: unknown
|
|
|
|
|
): Promise<{ type: string; status: boolean; code: number; message: string; payload?: unknown }>;
|
2026-01-15 23:45:40 +00:00
|
|
|
requestPagerDuty(args: { message: string; routingKey: string; monitorUrl: string }): Promise<boolean>;
|
|
|
|
|
requestMatrix(args: { homeserverUrl: string; accessToken: string; roomId: string; message: string }): Promise<{
|
|
|
|
|
status: boolean;
|
|
|
|
|
code: number;
|
|
|
|
|
message: string;
|
|
|
|
|
payload?: unknown;
|
|
|
|
|
}>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class NetworkService implements INetworkService {
|
2025-04-20 11:29:53 -07:00
|
|
|
static SERVICE_NAME = SERVICE_NAME;
|
|
|
|
|
|
2026-02-24 19:36:45 +00:00
|
|
|
private axios: AxiosStatic;
|
|
|
|
|
private logger: ILogger;
|
2026-01-15 23:45:40 +00:00
|
|
|
|
2026-02-24 19:19:32 +00:00
|
|
|
constructor(
|
2026-02-24 19:36:45 +00:00
|
|
|
axios: AxiosStatic,
|
2026-02-24 19:19:32 +00:00
|
|
|
logger: ILogger,
|
2026-03-05 14:11:11 -08:00
|
|
|
private providers: IStatusProvider<unknown>[]
|
2026-02-24 19:19:32 +00:00
|
|
|
) {
|
2025-04-20 11:29:53 -07:00
|
|
|
this.axios = axios;
|
|
|
|
|
this.logger = logger;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-15 23:45:40 +00:00
|
|
|
get serviceName(): string {
|
|
|
|
|
return NetworkService.SERVICE_NAME;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-15 16:04:43 -07:00
|
|
|
// Main entry point
|
2026-03-05 14:11:11 -08:00
|
|
|
async requestStatus(monitor: Monitor) {
|
|
|
|
|
const provider = this.providers.find((p) => p.supports(monitor.type));
|
|
|
|
|
if (!provider) {
|
|
|
|
|
return this.handleUnsupportedType(monitor.type);
|
2025-04-20 11:29:53 -07:00
|
|
|
}
|
2026-03-05 14:11:11 -08:00
|
|
|
return provider.handle(monitor);
|
2025-04-20 11:29:53 -07:00
|
|
|
}
|
|
|
|
|
|
2026-01-15 23:45:40 +00:00
|
|
|
private async handleUnsupportedType(type: string): Promise<MonitorStatusResponse> {
|
|
|
|
|
return {
|
|
|
|
|
monitorId: "unknown",
|
|
|
|
|
teamId: "unknown",
|
|
|
|
|
type: "unknown",
|
|
|
|
|
status: false,
|
2026-03-05 14:11:11 -08:00
|
|
|
code: NETWORK_ERROR,
|
2026-01-15 23:45:40 +00:00
|
|
|
message: `Unsupported type: ${type}`,
|
|
|
|
|
};
|
2025-04-20 11:29:53 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-15 16:04:43 -07:00
|
|
|
// Other network requests unrelated to monitoring:
|
2026-03-03 19:34:28 +00:00
|
|
|
async requestWebhook(type: string, url: string, body: unknown) {
|
2025-04-20 11:29:53 -07:00
|
|
|
try {
|
2025-06-19 12:11:43 +08:00
|
|
|
const response = await this.axios.post(url, body, {
|
2025-04-20 11:29:53 -07:00
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
type: "webhook",
|
|
|
|
|
status: true,
|
|
|
|
|
code: response.status,
|
2025-06-19 12:11:43 +08:00
|
|
|
message: `Successfully sent ${type} notification`,
|
2025-04-20 11:29:53 -07:00
|
|
|
payload: response.data,
|
|
|
|
|
};
|
2026-03-03 19:34:28 +00:00
|
|
|
} catch (err: unknown) {
|
2025-04-20 11:29:53 -07:00
|
|
|
this.logger.warn({
|
2026-03-03 19:34:28 +00:00
|
|
|
message: err instanceof Error ? err.message : String(err),
|
2026-03-05 14:11:11 -08:00
|
|
|
service: SERVICE_NAME,
|
2025-04-20 11:29:53 -07:00
|
|
|
method: "requestWebhook",
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-03 19:34:28 +00:00
|
|
|
if (err && typeof err === "object" && "response" in err) {
|
|
|
|
|
const axiosError = err as { response?: { status?: number; data?: unknown } };
|
|
|
|
|
return {
|
|
|
|
|
type: "webhook",
|
|
|
|
|
status: false,
|
2026-03-05 14:11:11 -08:00
|
|
|
code: axiosError.response?.status ?? NETWORK_ERROR,
|
2026-03-03 19:34:28 +00:00
|
|
|
message: `Failed to send ${type} notification`,
|
|
|
|
|
payload: axiosError.response?.data,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-20 11:29:53 -07:00
|
|
|
return {
|
|
|
|
|
type: "webhook",
|
|
|
|
|
status: false,
|
2026-03-05 14:11:11 -08:00
|
|
|
code: NETWORK_ERROR,
|
2025-06-19 12:11:43 +08:00
|
|
|
message: `Failed to send ${type} notification`,
|
2025-04-20 11:29:53 -07:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-15 22:33:25 +00:00
|
|
|
async requestPagerDuty({ message, routingKey, monitorUrl }: { message: string; routingKey: string; monitorUrl: string }) {
|
2025-06-10 13:44:15 +08:00
|
|
|
try {
|
|
|
|
|
const response = await this.axios.post(`https://events.pagerduty.com/v2/enqueue`, {
|
|
|
|
|
routing_key: routingKey,
|
|
|
|
|
event_action: "trigger",
|
|
|
|
|
payload: {
|
|
|
|
|
summary: message,
|
|
|
|
|
severity: "critical",
|
|
|
|
|
source: monitorUrl,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response?.data?.status !== "success") return false;
|
|
|
|
|
return true;
|
2026-03-03 19:34:28 +00:00
|
|
|
} catch (err: unknown) {
|
|
|
|
|
const originalMessage = err instanceof Error ? err.message : String(err);
|
|
|
|
|
|
|
|
|
|
throw new AppError({
|
|
|
|
|
message: originalMessage || "Error sending PagerDuty notification",
|
2026-03-05 14:11:11 -08:00
|
|
|
service: SERVICE_NAME,
|
2026-03-03 19:34:28 +00:00
|
|
|
method: "requestPagerDuty",
|
|
|
|
|
details: {
|
|
|
|
|
responseData: err && typeof err === "object" && "response" in err ? (err as { response?: { data?: unknown } }).response?.data : undefined,
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-06-10 13:44:15 +08:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-25 18:31:01 +03:00
|
|
|
|
2026-01-15 22:33:25 +00:00
|
|
|
async requestMatrix({
|
|
|
|
|
homeserverUrl,
|
|
|
|
|
accessToken,
|
|
|
|
|
roomId,
|
|
|
|
|
message,
|
|
|
|
|
}: {
|
|
|
|
|
homeserverUrl: string;
|
|
|
|
|
accessToken: string;
|
|
|
|
|
roomId: string;
|
|
|
|
|
message: string;
|
|
|
|
|
}) {
|
2025-10-25 18:31:01 +03:00
|
|
|
try {
|
|
|
|
|
const url = `${homeserverUrl}/_matrix/client/v3/rooms/${roomId}/send/m.room.message?access_token=${accessToken}`;
|
|
|
|
|
const body = {
|
|
|
|
|
msgtype: "m.text",
|
|
|
|
|
body: message,
|
|
|
|
|
format: "org.matrix.custom.html",
|
|
|
|
|
formatted_body: message,
|
|
|
|
|
};
|
|
|
|
|
const response = await this.axios.post(url, body, {
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
status: true,
|
|
|
|
|
code: response.status,
|
|
|
|
|
message: "Successfully sent Matrix notification",
|
|
|
|
|
};
|
2026-03-03 19:34:28 +00:00
|
|
|
} catch (err: unknown) {
|
|
|
|
|
if (err instanceof Error) {
|
|
|
|
|
this.logger.warn({
|
|
|
|
|
message: err.message,
|
2026-03-05 14:11:11 -08:00
|
|
|
service: SERVICE_NAME,
|
2026-03-03 19:34:28 +00:00
|
|
|
method: "requestMatrix",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (err && typeof err === "object" && "response" in err) {
|
|
|
|
|
const axiosError = err as { response?: { status?: number; data?: unknown } };
|
|
|
|
|
return {
|
|
|
|
|
status: false,
|
2026-03-05 14:11:11 -08:00
|
|
|
code: axiosError.response?.status || NETWORK_ERROR,
|
2026-03-03 19:34:28 +00:00
|
|
|
message: "Failed to send Matrix notification",
|
|
|
|
|
payload: axiosError.response?.data,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-25 18:31:01 +03:00
|
|
|
this.logger.warn({
|
2026-03-03 19:34:28 +00:00
|
|
|
message: String(err),
|
2026-03-05 14:11:11 -08:00
|
|
|
service: SERVICE_NAME,
|
2025-10-25 18:31:01 +03:00
|
|
|
method: "requestMatrix",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
status: false,
|
2026-03-05 14:11:11 -08:00
|
|
|
code: NETWORK_ERROR,
|
2025-10-25 18:31:01 +03:00
|
|
|
message: "Failed to send Matrix notification",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-20 11:29:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default NetworkService;
|