diff --git a/client/src/pages/uptime/UptimeDetails.tsx b/client/src/pages/uptime/UptimeDetails.tsx
index 79d6a9b70..fb88d4cef 100644
--- a/client/src/pages/uptime/UptimeDetails.tsx
+++ b/client/src/pages/uptime/UptimeDetails.tsx
@@ -69,6 +69,7 @@ const UptimeDetailsPage = () => {
}
const stats = response?.data?.stats || null;
+ console.log(stats);
const avgResponseTime = stats?.avgResponseTime || 0;
const maxResponseTime = stats?.maxResponseTime || 0;
@@ -132,6 +133,14 @@ const UptimeDetailsPage = () => {
stats?.lastResponseTime ? `${stats?.lastResponseTime} ms` : "N/A"
}
/>
+
(
@@ -70,6 +71,7 @@ const MonitorStatsSchema = new Schema(
enum: MonitorStatuses,
},
currentStreakStartedAt: { type: Number, required: false },
+ certificateExpiry: { type: Date, required: false },
},
{ timestamps: true }
);
diff --git a/server/src/services/infrastructure/NetworkService.ts b/server/src/services/infrastructure/NetworkService.ts
index ba43c5630..a74cc6c91 100644
--- a/server/src/services/infrastructure/NetworkService.ts
+++ b/server/src/services/infrastructure/NetworkService.ts
@@ -9,6 +9,8 @@ import type {
ICaptureInfo,
ILighthouseResult,
} from "@/db/models/index.js";
+import type { TLSSocket, DetailedPeerCertificate } from "node:tls";
+import type { ClientRequest } from "node:http";
import { MonitorType, MonitorStatus } from "@/db/models/monitors/Monitor.js";
import ApiError from "@/utils/ApiError.js";
import { config } from "@/config/index.js";
@@ -44,6 +46,7 @@ export interface StatusResponse {
responseTime: number;
timings?: GotTimings;
payload?: TPayload;
+ certificateExpiry?: Date | null;
}
class NetworkService implements INetworkService {
@@ -68,6 +71,7 @@ class NetworkService implements INetworkService {
private buildStatusResponse = (
monitor: IMonitor,
response: Response | null,
+ certificateExpiry: Date | null,
error: any | null
): StatusResponse => {
if (error) {
@@ -80,6 +84,7 @@ class NetworkService implements INetworkService {
message: error.message || "Network error",
responseTime: 0,
timings: { phases: {} } as GotTimings,
+ certificateExpiry,
};
if (error instanceof HTTPError) {
statusResponse.code = error?.response?.statusCode || this.NETWORK_ERROR;
@@ -99,6 +104,7 @@ class NetworkService implements INetworkService {
message: response?.statusMessage || "",
responseTime: response?.timings?.phases?.total || 0,
timings: response?.timings || ({ phases: {} } as GotTimings),
+ certificateExpiry,
};
return statusResponse;
@@ -111,11 +117,35 @@ class NetworkService implements INetworkService {
throw new Error("No URL provided");
}
+ let certificateExpiry: Date | null = null;
try {
- const response: Response = await this.client(url);
- return this.buildStatusResponse(monitor, response, null);
+ const req: any = this.client(url);
+ req.on("request", (nodeReq: ClientRequest) => {
+ nodeReq.on("socket", (socket: TLSSocket) => {
+ const capture = () => {
+ const cert = socket.getPeerCertificate(true);
+ if (cert && cert.valid_to) {
+ certificateExpiry = new Date(cert.valid_to);
+ }
+ };
+ capture();
+ socket.once("secureConnect", capture);
+ });
+ });
+ const response: Response = await req;
+ return this.buildStatusResponse(
+ monitor,
+ response,
+ certificateExpiry,
+ null
+ );
} catch (error) {
- return this.buildStatusResponse(monitor, null, error);
+ return this.buildStatusResponse(
+ monitor,
+ null,
+ certificateExpiry,
+ error
+ );
}
} catch (error) {
throw error;
@@ -142,7 +172,7 @@ class NetworkService implements INetworkService {
}
);
- statusResponse = this.buildStatusResponse(monitor, response, null);
+ statusResponse = this.buildStatusResponse(monitor, response, null, null);
if (!response?.body) {
throw new ApiError(
"No payload received from infrastructure monitor",
@@ -152,7 +182,7 @@ class NetworkService implements INetworkService {
statusResponse.payload = response?.body;
return statusResponse;
} catch (error) {
- statusResponse = this.buildStatusResponse(monitor, null, error);
+ statusResponse = this.buildStatusResponse(monitor, null, null, error);
}
return statusResponse;
};
@@ -174,10 +204,11 @@ class NetworkService implements INetworkService {
statusResponse = this.buildStatusResponse(
monitor,
response,
+ null,
null
) as StatusResponse;
} catch (error) {
- statusResponse = this.buildStatusResponse(monitor, null, error);
+ statusResponse = this.buildStatusResponse(monitor, null, null, error);
}
const pagespeedUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance&key=${apiKey}`;
diff --git a/server/src/services/infrastructure/StatusService.ts b/server/src/services/infrastructure/StatusService.ts
index 5ff3cb4e4..f42e73c7e 100644
--- a/server/src/services/infrastructure/StatusService.ts
+++ b/server/src/services/infrastructure/StatusService.ts
@@ -136,6 +136,7 @@ class StatusService implements IStatusService {
stats.maxResponseTime,
statusResponse.responseTime
);
+ stats.certificateExpiry = statusResponse.certificateExpiry || null;
return await stats.save();
};