From d25ce9e498ab5c2086c285a86fda83bc621adf1c Mon Sep 17 00:00:00 2001 From: gorkem-bwl Date: Fri, 20 Feb 2026 00:55:28 -0500 Subject: [PATCH] feat: add LocationResult type and locationResults to Check model ## Changes - Add LocationResult interface to server check types - Add locationResults optional field to Check interface - Add GlobalpingCheckResult interface to network types - Add locationResultSchema subdocument and locationResults field to Mongoose Check schema - Fix formatting in pre-existing files (SettingsGlobalping, Settings index, UptimeMonitorsTable, monitorService) ## Benefits Non-destructive addition of the data model needed to store per-location Globalping results inside a single Check document. No existing behaviour is changed. --- .../src/Pages/Settings/SettingsGlobalping.jsx | 30 +++++++++++++++---- client/src/Pages/Settings/index.jsx | 6 +++- client/src/Pages/Uptime/Details/index.tsx | 4 ++- .../Components/UptimeMonitorsTable.tsx | 5 +++- server/src/db/models/Check.ts | 16 ++++++++++ .../repositories/checks/IChecksRepository.ts | 7 +---- .../checks/MongoChecksRepistory.ts | 10 ++----- server/src/service/business/monitorService.ts | 7 +---- .../infrastructure/globalpingService.ts | 29 +++++++++--------- server/src/types/check.ts | 9 ++++++ server/src/types/network.ts | 8 +++++ 11 files changed, 87 insertions(+), 44 deletions(-) diff --git a/client/src/Pages/Settings/SettingsGlobalping.jsx b/client/src/Pages/Settings/SettingsGlobalping.jsx index c97f14631..d8476eb90 100644 --- a/client/src/Pages/Settings/SettingsGlobalping.jsx +++ b/client/src/Pages/Settings/SettingsGlobalping.jsx @@ -113,10 +113,16 @@ const SettingsGlobalping = ({ control={} label={ - + {t(`pages.settings.globalpingSettings.${tier.labelKey}`)} - + {t(`pages.settings.globalpingSettings.${tier.descKey}`)} @@ -145,18 +151,30 @@ const SettingsGlobalping = ({ {t("pages.settings.globalpingSettings.rateLimitsDescription")} - - + + {t("pages.settings.globalpingSettings.rateLimitNoAuth")} - + {t("pages.settings.globalpingSettings.rateLimitAuth")} {t("pages.settings.globalpingSettings.rateLimitSponsor")} - + {t("pages.settings.globalpingSettings.rateLimitsNote")} diff --git a/client/src/Pages/Settings/index.jsx b/client/src/Pages/Settings/index.jsx index f8f1d109b..efdb0269d 100644 --- a/client/src/Pages/Settings/index.jsx +++ b/client/src/Pages/Settings/index.jsx @@ -120,7 +120,11 @@ const Settings = () => { if (isGlobalpingKeySet && !globalpingKeyHasBeenReset && !toSubmit.globalpingApiKey) { delete toSubmit.globalpingApiKey; } - if (isEmailPasswordSet && !emailPasswordHasBeenReset && !toSubmit.systemEmailPassword) { + if ( + isEmailPasswordSet && + !emailPasswordHasBeenReset && + !toSubmit.systemEmailPassword + ) { delete toSubmit.systemEmailPassword; } diff --git a/client/src/Pages/Uptime/Details/index.tsx b/client/src/Pages/Uptime/Details/index.tsx index 2fa25c6a6..fdcc4a1d8 100644 --- a/client/src/Pages/Uptime/Details/index.tsx +++ b/client/src/Pages/Uptime/Details/index.tsx @@ -113,7 +113,9 @@ const UptimeDetailsPage = () => { const checks = checksData?.checks ?? []; const checksCount = checksData?.checksCount ?? 0; - const globalpingLocationsUrl = monitor?.globalpingEnabled ? "/monitors/globalping/locations" : null; + const globalpingLocationsUrl = monitor?.globalpingEnabled + ? "/monitors/globalping/locations" + : null; const { data: globalpingData } = useGet<{ locations: { id: string; label: string }[]; labels: Record; diff --git a/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx b/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx index 320675100..89489b81b 100644 --- a/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx +++ b/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx @@ -177,7 +177,10 @@ export const MonitorTable = ({ {row?.name} {row.globalpingEnabled && ( - + )} diff --git a/server/src/db/models/Check.ts b/server/src/db/models/Check.ts index 750930a76..3545960f2 100644 --- a/server/src/db/models/Check.ts +++ b/server/src/db/models/Check.ts @@ -13,6 +13,7 @@ import type { CheckNetworkInterfaceInfo, GotTimings, ILighthouseAudit, + LocationResult, } from "@/types/check.js"; type CheckMetadataDocument = Omit & { @@ -176,6 +177,17 @@ const auditsSchema = new Schema( { _id: false } ); +const locationResultSchema = new Schema( + { + location: { type: String, required: true }, + status: { type: Boolean, required: true }, + responseTime: { type: Number, required: true }, + statusCode: { type: Number, required: true }, + message: { type: String, default: "" }, + }, + { _id: false } +); + const metadataSchema = new Schema( { monitorId: { @@ -285,6 +297,10 @@ const CheckSchema = new Schema( type: auditsSchema, default: undefined, }, + locationResults: { + type: [locationResultSchema], + default: undefined, + }, }, { timestamps: true, diff --git a/server/src/repositories/checks/IChecksRepository.ts b/server/src/repositories/checks/IChecksRepository.ts index 7d86c4466..60e7aab06 100644 --- a/server/src/repositories/checks/IChecksRepository.ts +++ b/server/src/repositories/checks/IChecksRepository.ts @@ -35,12 +35,7 @@ export interface IChecksRepository { options?: { type?: MonitorType } ): Promise; findSummaryByTeamId(teamId: string, dateRange: string): Promise; - findLocationChecksByMonitorId( - monitorId: string, - startDate: Date, - endDate: Date, - dateString: string - ): Promise>; + findLocationChecksByMonitorId(monitorId: string, startDate: Date, endDate: Date, dateString: string): Promise>; // update //delete deleteByMonitorId(monitorId: string): Promise; diff --git a/server/src/repositories/checks/MongoChecksRepistory.ts b/server/src/repositories/checks/MongoChecksRepistory.ts index 3a2b99f87..b1e5d4059 100644 --- a/server/src/repositories/checks/MongoChecksRepistory.ts +++ b/server/src/repositories/checks/MongoChecksRepistory.ts @@ -422,12 +422,7 @@ class MongoChecksRepository implements IChecksRepository { return result.deletedCount ?? 0; }; - findLocationChecksByMonitorId = async ( - monitorId: string, - startDate: Date, - endDate: Date, - dateString: string - ): Promise> => { + findLocationChecksByMonitorId = async (monitorId: string, startDate: Date, endDate: Date, dateString: string): Promise> => { const monitorObjectId = new mongoose.Types.ObjectId(monitorId); const matchStage = { "metadata.monitorId": monitorObjectId, @@ -469,8 +464,7 @@ class MongoChecksRepository implements IChecksRepository { const locationChecks: Record = {}; for (const loc of results) { - const uptimePercentage = - loc.totalAllChecks > 0 ? loc.totalUpChecks / loc.totalAllChecks : 0; + const uptimePercentage = loc.totalAllChecks > 0 ? loc.totalUpChecks / loc.totalAllChecks : 0; locationChecks[loc._id] = { groupedChecks: loc.groupedChecks, uptimePercentage, diff --git a/server/src/service/business/monitorService.ts b/server/src/service/business/monitorService.ts index 4db410303..7143226ea 100644 --- a/server/src/service/business/monitorService.ts +++ b/server/src/service/business/monitorService.ts @@ -226,12 +226,7 @@ export class MonitorService implements IMonitorService { // Fetch location checks if Globalping is enabled for this monitor let locationChecks: Record | undefined; if (monitor.globalpingEnabled && (monitor.type === "http" || monitor.type === "ping")) { - const locData = await this.checksRepository.findLocationChecksByMonitorId( - monitor.id, - start, - end, - this.getDateFormat(rangeKey) - ); + const locData = await this.checksRepository.findLocationChecksByMonitorId(monitor.id, start, end, this.getDateFormat(rangeKey)); if (Object.keys(locData).length > 0) { locationChecks = locData; } diff --git a/server/src/service/infrastructure/globalpingService.ts b/server/src/service/infrastructure/globalpingService.ts index 0d46d7335..935177e61 100644 --- a/server/src/service/infrastructure/globalpingService.ts +++ b/server/src/service/infrastructure/globalpingService.ts @@ -179,27 +179,26 @@ class GlobalpingService { return []; } - const locations = locationIds - .map((loc) => LOCATION_MAP[loc]) - .filter(Boolean); + const locations = locationIds.map((loc) => LOCATION_MAP[loc]).filter(Boolean); if (locations.length === 0) { return []; } try { - const createResult = measurementType === "http" - ? await client.createMeasurement({ - type: "http" as const, - target, - locations: locations as any, - measurementOptions: { request: { method: "HEAD" }, protocol: "HTTPS" }, - }) - : await client.createMeasurement({ - type: "ping" as const, - target, - locations: locations as any, - }); + const createResult = + measurementType === "http" + ? await client.createMeasurement({ + type: "http" as const, + target, + locations: locations as any, + measurementOptions: { request: { method: "HEAD" }, protocol: "HTTPS" }, + }) + : await client.createMeasurement({ + type: "ping" as const, + target, + locations: locations as any, + }); if (!createResult.ok) { this.logger.warn({ diff --git a/server/src/types/check.ts b/server/src/types/check.ts index 683ac94a9..e85b53323 100644 --- a/server/src/types/check.ts +++ b/server/src/types/check.ts @@ -9,6 +9,14 @@ export interface CheckMetadata { location?: string; } +export interface LocationResult { + location: string; + status: boolean; + responseTime: number; + statusCode: number; + message: string; +} + export interface CheckCpuInfo { physical_core?: number; logical_core?: number; @@ -114,6 +122,7 @@ export interface Check { seo?: number; performance?: number; audits?: CheckAudits; + locationResults?: LocationResult[]; __v: number; createdAt: string; updatedAt: string; diff --git a/server/src/types/network.ts b/server/src/types/network.ts index e6c67f059..3191350af 100644 --- a/server/src/types/network.ts +++ b/server/src/types/network.ts @@ -37,6 +37,14 @@ export interface MonitorStatusResponse { location?: string; } +export interface GlobalpingCheckResult { + location: string; + status: boolean; + responseTime: number; + statusCode: number; + message: string; +} + export interface PingStatusPayload { host: string; numeric_host?: string;