From c07c28d20c01c394c7e60811a6b605a2041d49f2 Mon Sep 17 00:00:00 2001 From: Ilyas Rufai Date: Fri, 21 Nov 2025 00:04:22 +0100 Subject: [PATCH 1/4] update --- modules/analyze.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/analyze.js b/modules/analyze.js index df5ee02..5b8dbf9 100644 --- a/modules/analyze.js +++ b/modules/analyze.js @@ -22,7 +22,14 @@ function analyzeURL(req, res) { makeHttpRequest(urlToAnalyze, {}, (responseDetails, latencyMs, httpRequestError) => { if (httpRequestError || !responseDetails) { console.error(`Failed to request ${urlToAnalyze}: ${httpRequestError || 'Unknown error'}`); - return res.json({ isUp: false, ipAddress: null, uptime: 0, latencyMs: 0, dnsLookupMs: 0 }); + return res.json({ + isUp: false, + ipAddress: null, + uptime: 0, + latencyMs: 0, + dnsLookupMs: 0, + statusCode: null + }); } const isUp = From 3651c6e209200ddcf3d7eef093ff34328c27258e Mon Sep 17 00:00:00 2001 From: Ilyas Rufai Date: Fri, 21 Nov 2025 00:23:28 +0100 Subject: [PATCH 2/4] feat: handles protocol more better --- modules/analyze.js | 105 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 20 deletions(-) diff --git a/modules/analyze.js b/modules/analyze.js index 5b8dbf9..eb17636 100644 --- a/modules/analyze.js +++ b/modules/analyze.js @@ -5,6 +5,7 @@ const now = require('performance-now'); const { isURL } = require('validator'); const makeHttpRequest = require('./httpRequest'); const performDnsLookup = require('./dnsLookup'); +const checkSSLCertificate = require('./sslCheck'); function analyzeURL(req, res) { try { @@ -12,14 +13,46 @@ function analyzeURL(req, res) { throw new Error('URL is missing'); } - const urlToAnalyze = req.body.url; + let urlToAnalyze = req.body.url.trim(); + const originalUrl = urlToAnalyze; + const parsedUrl = url.parse(urlToAnalyze); + const hadProtocol = !!parsedUrl.protocol; + + // If no protocol, try HTTPS first + if (!hadProtocol) { + urlToAnalyze = `https://${urlToAnalyze}`; + } + // Validate the URL if (!isURL(urlToAnalyze)) { throw new Error('Invalid URL'); } const hostname = url.parse(urlToAnalyze).hostname; + const isHttps = urlToAnalyze.startsWith('https://'); + + // Try the request makeHttpRequest(urlToAnalyze, {}, (responseDetails, latencyMs, httpRequestError) => { + // If HTTPS failed and we added the protocol (no protocol originally), try HTTP as fallback + if (httpRequestError && isHttps && !hadProtocol) { + const httpUrl = urlToAnalyze.replace('https://', 'http://'); + return makeHttpRequest(httpUrl, {}, (httpResponseDetails, httpLatencyMs, httpError) => { + if (httpError || !httpResponseDetails) { + return res.json({ + isUp: false, + ipAddress: null, + uptime: 0, + latencyMs: 0, + dnsLookupMs: 0, + statusCode: null, + protocol: 'http', + sslInfo: null + }); + } + processResponse(httpResponseDetails, httpLatencyMs, hostname, 'http', res); + }); + } + if (httpRequestError || !responseDetails) { console.error(`Failed to request ${urlToAnalyze}: ${httpRequestError || 'Unknown error'}`); return res.json({ @@ -28,12 +61,17 @@ function analyzeURL(req, res) { uptime: 0, latencyMs: 0, dnsLookupMs: 0, - statusCode: null + statusCode: null, + protocol: isHttps ? 'https' : 'http', + sslInfo: null }); } + + processResponse(responseDetails, latencyMs, hostname, isHttps ? 'https' : 'http', res); + }); - const isUp = - responseDetails.statusCode >= 200 && responseDetails.statusCode < 400; + function processResponse(responseDetails, latencyMs, hostname, protocol, res) { + const isUp = responseDetails.statusCode >= 200 && responseDetails.statusCode < 400; if (!isUp) { return res.json({ isUp: false, @@ -42,31 +80,58 @@ function analyzeURL(req, res) { latencyMs, dnsLookupMs: 0, statusCode: responseDetails.statusCode, + protocol: protocol, + sslInfo: null }); } performDnsLookup(hostname, ({ ipAddress, lookupMs, error: dnsLookupError }) => { if (dnsLookupError) { - console.error(`Failed to lookup IP address for ${urlToAnalyze}: ${dnsLookupError}`); + console.error(`Failed to lookup IP address: ${dnsLookupError}`); return res.status(500).json({ error: 'Internal server error' }); } - // Calculate uptime baseline on successful check - const uptime = 100; - - console.log( - `isUp: ${isUp}, ipAddress: ${ipAddress}, uptime: ${uptime}%, latencyMs: ${latencyMs}, dnsLookupMs: ${lookupMs}` - ); - return res.json({ - isUp: true, - ipAddress, - uptime, - latencyMs, - dnsLookupMs: lookupMs, - statusCode: responseDetails.statusCode, - }); + // Check SSL certificate if HTTPS + if (protocol === 'https') { + checkSSLCertificate(hostname, 443, (sslError, sslInfo) => { + const uptime = 100; + const response = { + isUp: true, + ipAddress, + uptime, + latencyMs, + dnsLookupMs: lookupMs, + statusCode: responseDetails.statusCode, + protocol: protocol, + sslInfo: sslInfo + }; + + console.log( + `isUp: ${isUp}, ipAddress: ${ipAddress}, uptime: ${uptime}%, latencyMs: ${latencyMs}, dnsLookupMs: ${lookupMs}, protocol: ${protocol}` + ); + return res.json(response); + }); + } else { + // HTTP - no SSL info + const uptime = 100; + const response = { + isUp: true, + ipAddress, + uptime, + latencyMs, + dnsLookupMs: lookupMs, + statusCode: responseDetails.statusCode, + protocol: protocol, + sslInfo: null + }; + + console.log( + `isUp: ${isUp}, ipAddress: ${ipAddress}, uptime: ${uptime}%, latencyMs: ${latencyMs}, dnsLookupMs: ${lookupMs}, protocol: ${protocol}` + ); + return res.json(response); + } }); - }); + } } catch (error) { console.error(`Error analyzing URL: ${error.message}`); return res.status(400).json({ error: error.message }); From 015adb7f8a6d21a8c3012e92facda83feb3d0663 Mon Sep 17 00:00:00 2001 From: Ilyas Rufai Date: Fri, 21 Nov 2025 00:31:08 +0100 Subject: [PATCH 3/4] feat: ssl --- modules/analyze.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/analyze.js b/modules/analyze.js index eb17636..10242f6 100644 --- a/modules/analyze.js +++ b/modules/analyze.js @@ -107,7 +107,7 @@ function analyzeURL(req, res) { }; console.log( - `isUp: ${isUp}, ipAddress: ${ipAddress}, uptime: ${uptime}%, latencyMs: ${latencyMs}, dnsLookupMs: ${lookupMs}, protocol: ${protocol}` + `isUp: ${isUp}, ipAddress: ${ipAddress}, uptime: ${uptime}%, latencyMs: ${latencyMs}, dnsLookupMs: ${lookupMs}, protocol: ${protocol}, sslInfo: ${sslInfo ? JSON.stringify(sslInfo) : 'null'}` ); return res.json(response); }); From e96a73e580461377cdee4a4276f0b4d1f4d82199 Mon Sep 17 00:00:00 2001 From: Ilyas Rufai Date: Fri, 21 Nov 2025 00:33:01 +0100 Subject: [PATCH 4/4] feat: ssl --- modules/sslCheck.js | 60 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 modules/sslCheck.js diff --git a/modules/sslCheck.js b/modules/sslCheck.js new file mode 100644 index 0000000..8aaa470 --- /dev/null +++ b/modules/sslCheck.js @@ -0,0 +1,60 @@ +const https = require('https'); +const tls = require('tls'); + +function checkSSLCertificate(hostname, port = 443, callback) { + let certRetrieved = false; + + // Use tls.connect for more reliable certificate retrieval + const socket = tls.connect({ + host: hostname, + port: port, + rejectUnauthorized: false, // We want to check even if cert is invalid + servername: hostname, // Important for SNI + }, () => { + try { + const cert = socket.getPeerCertificate(true); + + if (cert && cert.valid_to) { + certRetrieved = true; + const expirationDate = new Date(cert.valid_to); + const now = new Date(); + const daysUntilExpiry = Math.ceil((expirationDate - now) / (1000 * 60 * 60 * 24)); + + callback(null, { + expirationDate: expirationDate.toISOString(), + daysUntilExpiry: daysUntilExpiry, + isValid: expirationDate > now, + issuer: cert.issuer ? (cert.issuer.CN || JSON.stringify(cert.issuer)) : null, + subject: cert.subject ? (cert.subject.CN || hostname) : hostname, + }); + } else { + callback(null, null); + } + } catch (err) { + callback(null, null); + } finally { + if (!socket.destroyed) { + socket.end(); + } + } + }); + + socket.on('error', (error) => { + if (!certRetrieved) { + // If we can't get the certificate, return null (might be HTTP or connection issue) + callback(null, null); + } + }); + + socket.on('timeout', () => { + if (!certRetrieved) { + socket.destroy(); + callback(null, null); + } + }); + + socket.setTimeout(5000); +} + +module.exports = checkSSLCertificate; +