2024-06-17 10:47:36 -07:00
const axios = require ( "axios" );
const ping = require ( "ping" );
const logger = require ( "../utils/logger" );
class NetworkService {
constructor ( db ) {
this . db = db ;
this . TYPE_PING = "ping" ;
this . TYPE_HTTP = "http" ;
2024-07-25 14:59:31 -07:00
this . TYPE_PAGESPEED = "pagespeed" ;
2024-06-17 10:47:36 -07:00
this . SERVICE_NAME = "NetworkService" ;
2024-06-18 11:42:25 -07:00
this . NETWORK_ERROR = 5000 ;
2024-06-17 10:47:36 -07:00
}
2024-06-17 13:52:20 -07:00
/**
* Measures the response time of an asynchronous operation.
* @param {Function} operation - An asynchronous operation to measure.
* @returns {Promise<{responseTime: number, response: any}>} An object containing the response time in milliseconds and the response from the operation.
* @throws {Error} The error object from the operation, contains response time.
*/
2024-06-17 11:33:55 -07:00
async measureResponseTime ( operation ) {
2024-06-17 11:05:22 -07:00
const startTime = Date . now ();
2024-06-17 10:47:36 -07:00
try {
2024-06-17 11:33:55 -07:00
const response = await operation ();
const endTime = Date . now ();
return { responseTime : endTime - startTime , response };
} catch ( error ) {
2024-06-17 13:52:20 -07:00
const endTime = Date . now ();
error . responseTime = endTime - startTime ;
2024-06-17 11:33:55 -07:00
throw error ;
}
}
2024-06-17 13:52:20 -07:00
/**
* Handles the ping operation for a given job, measures its response time, and logs the result.
* @param {Object} job - The job object containing data for the ping operation.
* @returns {Promise<{boolean}} The result of logging and storing the check
*/
2024-06-17 11:33:55 -07:00
async handlePing ( job ) {
const operation = async () => {
2024-06-17 10:47:36 -07:00
const response = await ping . promise . probe ( job . data . url );
2024-06-17 11:33:55 -07:00
return response ;
};
try {
2024-07-25 14:59:31 -07:00
const { responseTime , response } =
await this . measureResponseTime ( operation );
2024-06-17 10:47:36 -07:00
const isAlive = response . alive ;
2024-06-18 11:42:25 -07:00
2024-07-25 14:59:31 -07:00
const checkData = {
2024-06-18 11:42:25 -07:00
monitorId : job . data . _id ,
status : isAlive ,
responseTime ,
2024-07-25 14:59:31 -07:00
};
return await this . logAndStoreCheck ( checkData , this . db . createCheck );
2024-06-17 10:47:36 -07:00
} catch ( error ) {
2024-07-25 14:59:31 -07:00
const checkData = {
2024-06-18 11:42:25 -07:00
monitorId : job . data . _id ,
status : false ,
responseTime : error . responseTime ,
2024-07-25 14:59:31 -07:00
};
return await this . logAndStoreCheck ( checkData , this . db . createCheck );
2024-06-17 10:47:36 -07:00
}
}
2024-06-17 13:52:20 -07:00
/**
* Handles the http operation for a given job, measures its response time, and logs the result.
* @param {Object} job - The job object containing data for the ping operation.
* @returns {Promise<{boolean}} The result of logging and storing the check
*/
2024-06-17 10:47:36 -07:00
async handleHttp ( job ) {
2024-06-18 11:42:25 -07:00
// Define operation for timing
2024-06-17 11:33:55 -07:00
const operation = async () => {
2024-06-17 10:47:36 -07:00
const response = await axios . get ( job . data . url );
2024-06-17 11:33:55 -07:00
return response ;
};
2024-06-18 11:42:25 -07:00
// attempt connection
2024-06-17 11:33:55 -07:00
try {
2024-07-25 14:59:31 -07:00
const { responseTime , response } =
await this . measureResponseTime ( operation );
2024-06-18 11:42:25 -07:00
// check if response is in the 200 range, if so, service is up
2024-06-17 10:47:36 -07:00
const isAlive = response . status >= 200 && response . status < 300 ;
2024-06-18 11:42:25 -07:00
//Create a check with relevant data
2024-07-25 14:59:31 -07:00
const checkData = {
2024-06-18 11:42:25 -07:00
monitorId : job . data . _id ,
status : isAlive ,
responseTime ,
statusCode : response . status ,
2024-07-25 14:59:31 -07:00
};
return await this . logAndStoreCheck ( checkData , this . db . createCheck );
2024-06-17 10:47:36 -07:00
} catch ( error ) {
2024-07-25 14:59:31 -07:00
const checkData = {
2024-06-18 11:42:25 -07:00
monitorId : job . data . _id ,
status : false ,
responseTime : error . responseTime ,
2024-07-25 14:59:31 -07:00
};
2024-06-18 11:42:25 -07:00
// The server returned a response
if ( error . response ) {
2024-07-25 14:59:31 -07:00
checkData . statusCode = error . response . status ;
2024-06-18 11:42:25 -07:00
} else {
2024-07-25 14:59:31 -07:00
checkData . statusCode = this . NETWORK_ERROR ;
2024-06-18 11:42:25 -07:00
}
2024-07-25 14:59:31 -07:00
return await this . logAndStoreCheck ( checkData , this . db . createCheck );
2024-06-17 10:47:36 -07:00
}
}
2024-06-18 11:42:25 -07:00
2024-07-25 23:10:30 -07:00
/**
* Handles PageSpeed job types by fetching and processing PageSpeed insights.
*
* This method sends a request to the Google PageSpeed Insights API to get performance metrics
* for the specified URL, then logs and stores the check results.
*
* @param {Object} job - The job object containing data related to the PageSpeed check.
* @param {string} job.data.url - The URL to be analyzed by the PageSpeed Insights API.
* @param {string} job.data._id - The unique identifier for the monitor associated with the check.
*
* @returns {Promise<void>} A promise that resolves when the check results have been logged and stored.
*
* @throws {Error} Throws an error if there is an issue with fetching or processing the PageSpeed insights.
*/
async handlePagespeed ( job ) {
try {
const url = job . data . url ;
const response = await axios . get (
`https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url= ${ url } &category=seo&category=accessibility&category=best-practices&category=performance`
);
const pageSpeedResults = response . data ;
const categories = pageSpeedResults . lighthouseResult ? . categories ;
2024-08-02 12:32:08 -07:00
const audits = pageSpeedResults . lighthouseResult ? . audits ;
const {
"cumulative-layout-shift" : cls ,
"speed-index" : si ,
"first-contentful-paint" : fcp ,
"largest-contentful-paint" : lcp ,
"total-blocking-time" : tbt ,
} = audits ;
// Weights
// First Contentful Paint 10%
// Speed Index 10%
// Largest Contentful Paint 25%
// Total Blocking Time 30%
// Cumulative Layout Shift 25%
2024-07-25 23:10:30 -07:00
const checkData = {
monitorId : job . data . _id ,
status : true ,
accessibility : ( categories . accessibility ? . score || 0 ) * 100 ,
bestPractices : ( categories [ "best-practices" ] ? . score || 0 ) * 100 ,
seo : ( categories . seo ? . score || 0 ) * 100 ,
performance : ( categories . performance ? . score || 0 ) * 100 ,
2024-08-02 12:32:08 -07:00
audits : {
cls ,
si ,
fcp ,
lcp ,
tbt ,
},
2024-07-25 23:10:30 -07:00
};
2024-08-02 12:32:08 -07:00
2024-07-25 23:10:30 -07:00
this . logAndStoreCheck ( checkData , this . db . createPageSpeedCheck );
} catch ( error ) {
const checkData = {
monitorId : job . data . _id ,
status : false ,
accessibility : 0 ,
bestPractices : 0 ,
seo : 0 ,
performance : 0 ,
};
this . logAndStoreCheck ( checkData , this . db . createPageSpeedCheck );
}
2024-07-25 19:11:24 -07:00
}
2024-07-25 14:59:31 -07:00
2024-06-17 13:52:20 -07:00
/**
* Retrieves the status of a given job based on its type.
* For unsupported job types, it logs an error and returns false.
*
* @param {Object} job - The job object containing data necessary for processing.
* @returns {Promise<boolean>} The status of the job if it is supported and processed successfully, otherwise false.
*/
2024-06-17 10:47:36 -07:00
async getStatus ( job ) {
2024-06-17 11:05:22 -07:00
switch ( job . data . type ) {
case this . TYPE_PING :
return await this . handlePing ( job );
case this . TYPE_HTTP :
return await this . handleHttp ( job );
2024-07-25 14:59:31 -07:00
case this . TYPE_PAGESPEED :
return await this . handlePagespeed ( job );
2024-06-17 11:05:22 -07:00
default :
2024-06-17 13:52:20 -07:00
logger . error ( `Unsupported type: ${ job . data . type } ` , {
2024-06-17 11:05:22 -07:00
service : this . SERVICE_NAME ,
2024-06-17 13:52:20 -07:00
jobId : job . id ,
2024-06-17 11:05:22 -07:00
});
return false ;
2024-06-17 10:47:36 -07:00
}
}
2024-06-17 13:52:20 -07:00
/**
* Logs and stores the result of a check for a specific job.
*
2024-07-25 14:59:31 -07:00
* @param {Object} data - Data to be written
* @param {function} writeToDB - DB write method
*
2024-06-17 13:52:20 -07:00
* @returns {Promise<boolean>} The status of the inserted check if successful, otherwise false.
*/
2024-07-25 14:59:31 -07:00
async logAndStoreCheck ( data , writeToDB ) {
2024-06-17 13:52:20 -07:00
try {
2024-07-25 14:59:31 -07:00
const insertedCheck = await writeToDB ( data );
2024-06-17 13:52:20 -07:00
return insertedCheck . status ;
} catch ( error ) {
2024-07-26 18:56:39 -07:00
console . log ( error );
2024-07-25 14:59:31 -07:00
logger . error ( `Error wrtiting check for ${ data . monitorId } ` , {
2024-06-17 13:52:20 -07:00
service : this . SERVICE_NAME ,
2024-07-25 14:59:31 -07:00
monitorId : data . monitorId ,
2024-06-17 13:52:20 -07:00
error : error ,
});
2024-06-17 10:47:36 -07:00
}
}
}
module . exports = NetworkService ;