2026-01-15 23:45:40 +00:00
import { HTTPError , RequestError } from "got" ;
import type { Got , Response } from "got" ;
import type { Monitor , MonitorStatusResponse } from "@/types/index.js" ;
2026-01-15 22:33:25 +00:00
2025-10-28 22:41:05 -07:00
import CacheableLookup from "cacheable-lookup" ;
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
type MonitorStatusResponseOverrides < T > = Partial < Omit < MonitorStatusResponse < T >, "monitorId" | "teamId" | "type" >>;
interface BuildStatusResponseArgs < T > {
monitor : Monitor ;
response? : Response < T > | null ;
error? : Error | RequestError | HTTPError | null ;
payload? : T | null ;
jsonPath? : string ;
matchMethod? : MonitorStatusResponse [ "matchMethod" ];
expectedValue? : string ;
extracted? : unknown ;
overrides? : MonitorStatusResponseOverrides < T >;
}
export interface INetworkService {
readonly serviceName : string ;
requestStatus ( monitor : Monitor ) : Promise < MonitorStatusResponse >;
requestWebhook ( type : string , url : string , body : any ) : Promise < { type : string ; status : boolean ; code : number ; message : string ; payload? : unknown } > ;
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-01-15 22:33:25 +00:00
private TYPE_PING : string ;
private TYPE_HTTP : string ;
private TYPE_PAGESPEED : string ;
private TYPE_HARDWARE : string ;
private TYPE_DOCKER : string ;
private TYPE_PORT : string ;
private TYPE_GAME : string ;
private SERVICE_NAME : string ;
private NETWORK_ERROR : number ;
private PING_ERROR : number ;
private axios : any ;
private got : Got ;
private https : any ;
private jmespath : any ;
private GameDig : any ;
private ping : any ;
private logger : any ;
private Docker : any ;
private net : any ;
private settingsService : any ;
2026-01-15 23:45:40 +00:00
private buildStatusResponse = < T >({
monitor ,
response ,
error ,
payload ,
jsonPath ,
matchMethod ,
expectedValue ,
extracted ,
overrides ,
} : BuildStatusResponseArgs < T >) : MonitorStatusResponse < T > => {
if ( error ) {
const statusResponse : MonitorStatusResponse < T > = {
monitorId : monitor.id ,
teamId : monitor.teamId ,
type : monitor . type ,
status : false ,
code : this.NETWORK_ERROR ,
message : error.message ?? "Network error" ,
responseTime : 0 ,
timings : undefined ,
jsonPath ,
matchMethod ,
expectedValue ,
extracted ,
payload ,
};
if ( error instanceof HTTPError || error instanceof RequestError ) {
statusResponse . code = error ? . response ? . statusCode ?? this . NETWORK_ERROR ;
statusResponse . message = error . message ;
statusResponse . responseTime = error . timings ? . phases ? . total ?? 0 ;
statusResponse . timings = error . timings ;
}
return { ... statusResponse , ...( overrides ?? {}) };
}
return {
monitorId : monitor.id ,
teamId : monitor.teamId ,
type : monitor . type ,
status : response?.ok ?? false ,
code : response?.statusCode ?? this . NETWORK_ERROR ,
message : response?.statusMessage ?? "" ,
responseTime : response?.timings?.phases?.total ?? 0 ,
timings : response?.timings ,
payload : payload ?? response ? . body ,
jsonPath ,
matchMethod ,
expectedValue ,
extracted ,
...( overrides ?? {}),
};
};
2026-01-15 22:33:25 +00:00
constructor ({
axios ,
got ,
https ,
jmespath ,
GameDig ,
ping ,
logger ,
http ,
Docker ,
net ,
settingsService ,
} : {
axios : any ;
got : Got ;
https : any ;
jmespath : any ;
GameDig : any ;
ping : any ;
logger : any ;
http : any ;
Docker : any ;
net : any ;
settingsService : any ;
}) {
2025-04-20 11:29:53 -07:00
this . TYPE_PING = "ping" ;
this . TYPE_HTTP = "http" ;
this . TYPE_PAGESPEED = "pagespeed" ;
this . TYPE_HARDWARE = "hardware" ;
this . TYPE_DOCKER = "docker" ;
this . TYPE_PORT = "port" ;
2025-08-02 02:04:06 +03:00
this . TYPE_GAME = "game" ;
2025-04-20 11:29:53 -07:00
this . SERVICE_NAME = SERVICE_NAME ;
this . NETWORK_ERROR = 5000 ;
this . PING_ERROR = 5001 ;
this . axios = axios ;
2025-08-15 16:04:43 -07:00
this . https = https ;
this . jmespath = jmespath ;
this . GameDig = GameDig ;
2025-04-20 11:29:53 -07:00
this . ping = ping ;
this . logger = logger ;
this . Docker = Docker ;
this . net = net ;
this . settingsService = settingsService ;
2025-10-28 22:41:05 -07:00
const cacheable = new CacheableLookup ();
this . got = got . extend ({
dnsCache : cacheable ,
timeout : {
request : 30000 ,
},
retry : { limit : 1 },
});
2025-04-20 11:29:53 -07:00
}
2026-01-15 23:45:40 +00:00
get serviceName () : string {
return NetworkService . SERVICE_NAME ;
}
2025-08-15 16:04:43 -07:00
// Helper functions
2026-01-15 23:45:40 +00:00
private async timeRequest < T >( operation : () => Promise < T >) : Promise < { response : T | null ; responseTime : number ; error : unknown } > {
2025-08-15 16:04:43 -07:00
const start = process . hrtime . bigint ();
2025-04-20 11:29:53 -07:00
try {
const response = await operation ();
2025-10-09 09:33:14 -07:00
const elapsedMs = Math . round ( Number ( process . hrtime . bigint () - start ) / 1 _000_000 );
2026-01-15 23:45:40 +00:00
return { response , responseTime : elapsedMs , error : null };
2025-04-20 11:29:53 -07:00
} catch ( error ) {
2025-10-09 09:33:14 -07:00
const elapsedMs = Math . round ( Number ( process . hrtime . bigint () - start ) / 1 _000_000 );
return { response : null , responseTime : elapsedMs , error };
2025-08-15 16:04:43 -07:00
}
}
// Main entry point
2026-01-15 23:45:40 +00:00
async requestStatus ( monitor : Monitor ) : Promise < MonitorStatusResponse > {
2025-08-15 16:04:43 -07:00
const type = monitor ? . type || "unknown" ;
switch ( type ) {
case this . TYPE_PING :
return await this . requestPing ( monitor );
case this . TYPE_HTTP :
return await this . requestHttp ( monitor );
case this . TYPE_PAGESPEED :
return await this . requestPageSpeed ( monitor );
case this . TYPE_HARDWARE :
return await this . requestHardware ( monitor );
case this . TYPE_DOCKER :
return await this . requestDocker ( monitor );
case this . TYPE_PORT :
return await this . requestPort ( monitor );
case this . TYPE_GAME :
return await this . requestGame ( monitor );
default :
2026-01-15 23:45:40 +00:00
return this . handleUnsupportedType ( type );
2025-04-20 11:29:53 -07:00
}
}
2026-01-15 23:45:40 +00:00
private async requestPing ( monitor : Monitor ) : Promise < MonitorStatusResponse > {
2025-04-20 11:29:53 -07:00
try {
2025-08-15 16:04:43 -07:00
if ( ! monitor ? . url ) {
throw new Error ( "Monitor URL is required" );
}
2025-10-02 12:46:51 +05:30
const rawUrl = monitor . url ;
const sanitizedHost = rawUrl
. replace ( /^https?:\/\// , "" )
. replace ( /\/.*$/ , "" )
. replace ( /:.*/ , "" );
const { response , error } = await this . timeRequest (() => this . ping . promise . probe ( sanitizedHost ));
2025-04-20 11:29:53 -07:00
2025-08-15 16:04:43 -07:00
if ( ! response ) {
2025-09-08 06:42:36 -07:00
if ( error ) {
throw error ;
}
2025-08-15 16:04:43 -07:00
throw new Error ( "Ping failed - no result returned" );
}
2026-01-15 23:45:40 +00:00
const pingResponse = this . buildStatusResponse ({
monitor ,
2025-04-20 11:29:53 -07:00
payload : response ,
2026-01-15 23:45:40 +00:00
overrides : {
status : ( response as { alive? : boolean }) ? . alive ?? false ,
code : 200 ,
message : "Success" ,
responseTime : ( response as { time? : number }) ? . time ?? 0 ,
payload : response ,
},
});
2025-08-15 16:04:43 -07:00
2025-04-20 11:29:53 -07:00
if ( error ) {
pingResponse . status = false ;
2026-01-15 23:45:40 +00:00
pingResponse . code = this . PING_ERROR ;
2025-08-15 16:04:43 -07:00
pingResponse . message = "Ping failed" ;
2025-04-20 11:29:53 -07:00
return pingResponse ;
}
return pingResponse ;
2026-01-15 22:33:25 +00:00
} catch ( err : any ) {
2025-08-15 16:04:43 -07:00
err . service = this . SERVICE_NAME ;
err . method = "requestPing" ;
throw err ;
2025-04-20 11:29:53 -07:00
}
}
2026-01-15 23:45:40 +00:00
private async requestHttp ( monitor : Monitor ) : Promise < MonitorStatusResponse > {
2026-01-30 20:21:31 +00:00
const { url , secret , id , teamId , type , ignoreTlsErrors , useAdvancedMatching , jsonPath , matchMethod , expectedValue } = monitor ;
2026-01-15 23:45:40 +00:00
const httpResponse = this . buildStatusResponse ({
monitor ,
overrides : {
status : false ,
code : this.NETWORK_ERROR ,
message : "Request not executed" ,
},
});
2025-04-20 11:29:53 -07:00
2025-08-15 16:04:43 -07:00
try {
if ( ! url ) {
throw new Error ( "Monitor URL is required" );
}
2026-01-15 22:33:25 +00:00
const config : Record < string , any > = {
2025-08-15 16:04:43 -07:00
headers : secret ? { Authorization : `Bearer ${ secret } ` } : undefined ,
};
2025-04-20 11:29:53 -07:00
2025-08-15 16:04:43 -07:00
if ( ignoreTlsErrors ) {
config . agent = {
https : new this . https . Agent ({
rejectUnauthorized : false ,
}),
};
2025-05-06 10:08:19 -07:00
}
2025-08-15 16:04:43 -07:00
const response = await this . got ( url , config );
2025-04-20 11:29:53 -07:00
2026-01-15 22:33:25 +00:00
let payload : any ;
2025-08-15 16:04:43 -07:00
const contentType = response . headers [ "content-type" ];
2025-04-20 11:29:53 -07:00
2025-08-15 16:04:43 -07:00
if ( contentType && contentType . includes ( "application/json" )) {
try {
payload = JSON . parse ( response . body );
} catch {
payload = response . body ;
}
} else {
payload = response . body ;
2025-04-20 11:29:53 -07:00
}
2026-01-15 23:45:40 +00:00
Object . assign ( httpResponse , {
code : response.statusCode ,
status : response.ok ,
message : response.statusMessage ?? "" ,
responseTime : response.timings.phases.total || 0 ,
payload ,
timings : response.timings ,
});
2025-04-20 11:29:53 -07:00
2026-01-30 20:21:31 +00:00
if ( ! useAdvancedMatching ) {
2025-04-20 11:29:53 -07:00
return httpResponse ;
}
2025-09-17 23:23:16 -03:00
if ( expectedValue && ! jsonPath ) {
2025-08-26 14:54:24 -07:00
let ok = false ;
if ( matchMethod === "equal" ) ok = payload === expectedValue ;
2025-09-17 23:23:16 -03:00
if ( matchMethod === "include" && typeof payload === "string" ) ok = payload . includes ( expectedValue );
if ( matchMethod === "regex" && typeof payload === "string" ) ok = new RegExp ( expectedValue ). test ( payload );
2025-08-26 14:54:24 -07:00
if ( ok === true ) {
return httpResponse ;
} else {
httpResponse . code = 500 ;
httpResponse . status = false ;
2026-01-20 23:06:23 +00:00
httpResponse . message = "Expected value did not match" ;
2025-08-26 14:54:24 -07:00
return httpResponse ;
}
}
2025-04-20 11:29:53 -07:00
if ( jsonPath ) {
const contentType = response . headers [ "content-type" ];
const isJson = contentType ? . includes ( "application/json" );
if ( ! isJson ) {
httpResponse . status = false ;
2026-01-20 23:06:23 +00:00
httpResponse . message = "Response is not JSON" ;
2025-04-20 11:29:53 -07:00
return httpResponse ;
}
try {
2025-09-17 23:23:16 -03:00
const extracted = this . jmespath . search ( payload , jsonPath );
if ( expectedValue ) {
let ok = false ;
if ( matchMethod === "equal" ) ok = extracted === expectedValue ;
if ( matchMethod === "include" && typeof extracted === "string" ) ok = extracted . includes ( expectedValue );
if ( matchMethod === "regex" && typeof extracted === "string" ) ok = new RegExp ( expectedValue ). test ( extracted );
if ( ok ) {
httpResponse . extracted = extracted ;
return httpResponse ;
} else {
httpResponse . status = false ;
httpResponse . code = 500 ;
2026-01-20 23:06:23 +00:00
httpResponse . message = "Expected value did not match" ;
2025-09-17 23:23:16 -03:00
httpResponse . extracted = extracted ;
return httpResponse ;
}
} else {
const isFalsey = extracted === false || extracted === "false" || extracted === undefined || extracted === null ;
if ( ! isFalsey ) {
httpResponse . extracted = extracted ;
return httpResponse ;
} else {
httpResponse . status = false ;
httpResponse . code = 500 ;
2026-01-20 23:06:23 +00:00
httpResponse . message = "Expected value did not match" ;
2025-09-17 23:23:16 -03:00
httpResponse . extracted = extracted ;
return httpResponse ;
}
}
2025-08-15 16:04:43 -07:00
} catch {
2025-04-20 11:29:53 -07:00
httpResponse . status = false ;
2026-01-20 23:06:23 +00:00
httpResponse . message = "Error evaluating JSON path" ;
2025-04-20 11:29:53 -07:00
return httpResponse ;
}
}
2025-08-15 16:04:43 -07:00
return httpResponse ;
2026-01-15 22:33:25 +00:00
} catch ( err : any ) {
2025-08-15 16:04:43 -07:00
if ( err . name === "HTTPError" || err . name === "RequestError" ) {
httpResponse . code = err ? . response ? . statusCode || this . NETWORK_ERROR ;
2025-04-20 11:29:53 -07:00
httpResponse . status = false ;
2025-08-15 16:04:43 -07:00
httpResponse . message = err ? . response ? . statusCode || err . message ;
httpResponse . responseTime = err ? . timings ? . phases ? . total || 0 ;
httpResponse . payload = null ;
httpResponse . timings = err ? . timings || {};
2025-04-20 11:29:53 -07:00
return httpResponse ;
}
2025-08-15 16:04:43 -07:00
err . service = this . SERVICE_NAME ;
err . method = "requestHttp" ;
throw err ;
2025-04-20 11:29:53 -07:00
}
}
2026-01-15 23:45:40 +00:00
private async requestPageSpeed ( monitor : Monitor ) : Promise < MonitorStatusResponse > {
2025-04-20 11:29:53 -07:00
try {
2025-06-06 14:05:59 +08:00
const url = monitor . url ;
2025-08-15 16:04:43 -07:00
if ( ! url ) {
throw new Error ( "Monitor URL is required" );
}
let pageSpeedUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url= ${ url } &category=seo&category=accessibility&category=best-practices&category=performance` ;
2025-05-12 10:56:58 -07:00
const dbSettings = await this . settingsService . getDBSettings ();
if ( dbSettings ? . pagespeedApiKey ) {
2025-08-15 16:04:43 -07:00
pageSpeedUrl += `&key= ${ dbSettings . pagespeedApiKey } ` ;
2025-05-12 10:56:58 -07:00
} else {
2025-05-19 11:23:13 -07:00
this . logger . warn ({
2025-08-15 16:04:43 -07:00
message : "PageSpeed API key not found, job not executed" ,
2025-05-12 10:56:58 -07:00
service : this.SERVICE_NAME ,
method : "requestPagespeed" ,
details : { url },
});
2025-04-20 11:29:53 -07:00
}
2025-08-15 16:04:43 -07:00
return await this . requestHttp ({
... monitor ,
url : pageSpeedUrl ,
});
2026-01-15 22:33:25 +00:00
} catch ( err : any ) {
2025-08-15 16:04:43 -07:00
err . service = this . SERVICE_NAME ;
err . method = "requestPageSpeed" ;
throw err ;
2025-04-20 11:29:53 -07:00
}
}
2026-01-15 23:45:40 +00:00
private async requestHardware ( monitor : Monitor ) : Promise < MonitorStatusResponse > {
2025-04-20 11:29:53 -07:00
try {
2025-06-06 14:05:59 +08:00
return await this . requestHttp ( monitor );
2026-01-15 22:33:25 +00:00
} catch ( err : any ) {
2025-08-15 16:04:43 -07:00
err . service = this . SERVICE_NAME ;
err . method = "requestHardware" ;
throw err ;
2025-04-20 11:29:53 -07:00
}
}
2026-01-15 23:45:40 +00:00
private async requestDocker ( monitor : Monitor ) : Promise < MonitorStatusResponse > {
2025-04-20 11:29:53 -07:00
try {
2025-08-15 16:04:43 -07:00
if ( ! monitor . url ) {
throw new Error ( "Monitor URL is required" );
}
2025-04-20 11:29:53 -07:00
const docker = new this . Docker ({
socketPath : "/var/run/docker.sock" ,
handleError : true , // Enable error handling
});
2026-01-15 23:45:40 +00:00
const dockerResponse = this . buildStatusResponse ({
monitor ,
overrides : {
status : false ,
code : this.NETWORK_ERROR ,
message : "No response" ,
},
});
2025-08-29 09:22:02 -07:00
2025-04-20 11:29:53 -07:00
const containers = await docker . listContainers ({ all : true });
2025-08-29 00:27:01 -04:00
2025-08-29 00:53:40 -04:00
// Normalize input: strip leading slashes and convert to lowercase for comparison
const normalizedInput = monitor . url . replace ( /^\/+/ , "" ). toLowerCase ();
// Priority-based matching to avoid ambiguity:
// 1. Exact full ID match (64-char)
2026-01-15 22:33:25 +00:00
let exactIdMatch = containers . find (( c : any ) => c . Id . toLowerCase () === normalizedInput );
2025-08-29 00:53:40 -04:00
// 2. Exact container name match (case-insensitive)
2026-01-15 22:33:25 +00:00
let exactNameMatch = containers . find (( c : any ) =>
c . Names . some (( name : string ) => {
2025-08-29 00:53:40 -04:00
const cleanName = name . replace ( /^\/+/ , "" ). toLowerCase ();
return cleanName === normalizedInput ;
})
);
// 3. Partial ID match (fallback for backwards compatibility)
2026-01-15 22:33:25 +00:00
let partialIdMatch = containers . find (( c : any ) => c . Id . toLowerCase (). startsWith ( normalizedInput ));
2025-08-29 00:53:40 -04:00
// Select container based on priority
let targetContainer = exactIdMatch || exactNameMatch || partialIdMatch ;
2025-08-29 09:22:02 -07:00
// Return negative response if no container
if ( ! targetContainer ) {
this . logger . warn ({
message : `No container found for " ${ monitor . url } ".` ,
service : this.SERVICE_NAME ,
method : "requestDocker" ,
details : { url : monitor.url },
});
dockerResponse . code = 404 ;
dockerResponse . status = false ;
2026-01-20 23:06:23 +00:00
dockerResponse . message = "Docker container not found" ;
2025-08-29 09:22:02 -07:00
return dockerResponse ;
2025-08-29 00:53:40 -04:00
}
2025-08-29 00:27:01 -04:00
2025-08-29 09:22:02 -07:00
// Return negative response if ambiguous matches exist
const matchTypes = [];
if ( exactIdMatch ) matchTypes . push ( "exact ID" );
if ( exactNameMatch ) matchTypes . push ( "exact name" );
if ( partialIdMatch && ! exactIdMatch ) matchTypes . push ( "partial ID" );
if ( matchTypes . length > 1 ) {
this . logger . warn ({
message : `Ambiguous container match for " ${ monitor . url } ". Matched by: ${ matchTypes . join ( ", " ) } . Using ${ exactIdMatch ? "exact ID" : exactNameMatch ? "exact name" : "partial ID" } match.` ,
service : this.SERVICE_NAME ,
method : "requestDocker" ,
details : { url : monitor.url },
});
dockerResponse . status = false ;
dockerResponse . message = `Ambiguous container match for " ${ monitor . url } ". Matched by: ${ matchTypes . join ( ", " ) } . Using ${ exactIdMatch ? "exact ID" : exactNameMatch ? "exact name" : "partial ID" } match.` ;
return dockerResponse ;
2025-04-20 11:29:53 -07:00
}
2025-08-29 00:27:01 -04:00
const container = docker . getContainer ( targetContainer . Id );
2026-01-15 22:33:25 +00:00
const { response , responseTime , error } : { response? : any ; responseTime? : number ; error? : any } = await this . timeRequest (() =>
container . inspect ()
);
2025-04-20 11:29:53 -07:00
2025-10-09 09:33:14 -07:00
dockerResponse . responseTime = responseTime ;
2025-08-29 09:22:02 -07:00
dockerResponse . status = response ? . State ? . Status === "running" ? true : false ;
dockerResponse . code = 200 ;
dockerResponse . message = "Docker container status fetched successfully" ;
2025-04-20 11:29:53 -07:00
if ( error ) {
dockerResponse . status = false ;
dockerResponse . code = error . statusCode || this . NETWORK_ERROR ;
2025-07-21 10:34:16 -07:00
dockerResponse . message = error . reason || "Failed to fetch Docker container information" ;
2025-04-20 11:29:53 -07:00
return dockerResponse ;
}
2025-08-15 16:04:43 -07:00
2025-04-20 11:29:53 -07:00
return dockerResponse ;
2026-01-15 22:33:25 +00:00
} catch ( err : any ) {
2025-08-15 16:04:43 -07:00
err . service = this . SERVICE_NAME ;
err . method = "requestDocker" ;
throw err ;
2025-04-20 11:29:53 -07:00
}
}
2026-01-15 23:45:40 +00:00
private async requestPort ( monitor : Monitor ) : Promise < MonitorStatusResponse > {
2025-04-20 11:29:53 -07:00
try {
2025-06-06 14:05:59 +08:00
const { url , port } = monitor ;
2025-10-09 09:33:14 -07:00
const { response , responseTime , error } = await this . timeRequest ( async () => {
2025-04-20 11:29:53 -07:00
return new Promise (( resolve , reject ) => {
const socket = this . net . createConnection (
{
host : url ,
port ,
},
() => {
socket . end ();
socket . destroy ();
resolve ({ success : true });
}
);
socket . setTimeout ( 5000 );
socket . on ( "timeout" , () => {
socket . destroy ();
reject ( new Error ( "Connection timeout" ));
});
2026-01-15 22:33:25 +00:00
socket . on ( "error" , ( err : any ) => {
2025-04-20 11:29:53 -07:00
socket . destroy ();
reject ( err );
});
});
});
2026-01-15 23:45:40 +00:00
const portResponse = this . buildStatusResponse ({
monitor ,
overrides : {
code : 200 ,
status : ( response as { success? : boolean }) ? . success ?? false ,
2026-01-20 23:06:23 +00:00
message : "Port check successful" ,
2026-01-15 23:45:40 +00:00
responseTime ,
},
});
2025-04-20 11:29:53 -07:00
if ( error ) {
portResponse . code = this . NETWORK_ERROR ;
2025-08-15 16:04:43 -07:00
portResponse . status = false ;
2026-01-20 23:06:23 +00:00
portResponse . message = "Port check failed" ;
2025-04-20 11:29:53 -07:00
return portResponse ;
}
return portResponse ;
2026-01-15 22:33:25 +00:00
} catch ( error : any ) {
2025-04-20 11:29:53 -07:00
error . service = this . SERVICE_NAME ;
error . method = "requestTCP" ;
throw error ;
}
}
2026-01-15 23:45:40 +00:00
private async requestGame ( monitor : Monitor ) : Promise < MonitorStatusResponse > {
2025-08-15 16:04:43 -07:00
try {
const { url , port , gameId } = monitor ;
2026-01-15 23:45:40 +00:00
const gameResponse = this . buildStatusResponse ({
monitor ,
overrides : {
code : 200 ,
status : true ,
message : "Success" ,
},
});
2025-08-15 16:04:43 -07:00
const state = await this . GameDig . query ({
type : gameId ,
host : url ,
port : port ,
2026-01-15 22:33:25 +00:00
}). catch (( error : any ) => {
2025-08-15 16:04:43 -07:00
this . logger . warn ({
message : error.message ,
service : this.SERVICE_NAME ,
method : "requestGame" ,
details : { url , port , gameId },
});
});
if ( ! state ) {
gameResponse . code = this . NETWORK_ERROR ;
gameResponse . status = false ;
gameResponse . message = "No response" ;
return gameResponse ;
}
gameResponse . responseTime = state . ping ;
gameResponse . payload = state ;
return gameResponse ;
2026-01-15 22:33:25 +00:00
} catch ( error : any ) {
2025-08-15 16:04:43 -07:00
error . service = this . SERVICE_NAME ;
error . method = "requestPing" ;
throw error ;
}
}
2026-01-15 23:45:40 +00:00
private async handleUnsupportedType ( type : string ) : Promise < MonitorStatusResponse > {
return {
monitorId : "unknown" ,
teamId : "unknown" ,
type : "unknown" ,
status : false ,
code : this.NETWORK_ERROR ,
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-01-15 22:33:25 +00:00
async requestWebhook ( type : string , url : string , body : any ) {
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-01-15 22:33:25 +00:00
} catch ( error : any ) {
2025-04-20 11:29:53 -07:00
this . logger . warn ({
message : error.message ,
service : this.SERVICE_NAME ,
method : "requestWebhook" ,
});
return {
type : "webhook" ,
status : false ,
code : error.response?.status || this . NETWORK_ERROR ,
2025-06-19 12:11:43 +08:00
message : `Failed to send ${ type } notification` ,
2025-04-20 11:29:53 -07:00
payload : error.response?.data ,
};
}
}
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-01-15 22:33:25 +00:00
} catch ( error : any ) {
2025-06-10 13:44:15 +08:00
error . details = error . response ? . data ;
error . service = this . SERVICE_NAME ;
error . method = "requestPagerDuty" ;
throw error ;
}
}
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-01-15 22:33:25 +00:00
} catch ( error : any ) {
2025-10-25 18:31:01 +03:00
this . logger . warn ({
message : error.message ,
service : this.SERVICE_NAME ,
method : "requestMatrix" ,
});
return {
status : false ,
code : error.response?.status || this . NETWORK_ERROR ,
message : "Failed to send Matrix notification" ,
payload : error.response?.data ,
};
}
}
2025-04-20 11:29:53 -07:00
}
export default NetworkService ;