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.
This commit is contained in:
gorkem-bwl committed 2026-02-20 01:31:31 -05:00
1 parent b7c9e2710a
commit d25ce9e498
11 files changed
+87 -44

No files matched your search

@@ -113,10 +113,16 @@ const SettingsGlobalping = ({
control={<Radio sx={{ p: 0, mr: theme.spacing(4) }} />}
label={
<Box>
<Typography fontWeight={500} lineHeight={1.5}>
<Typography
fontWeight={500}
lineHeight={1.5}
>
{t(`pages.settings.globalpingSettings.${tier.labelKey}`)}
</Typography>
<Typography variant="body2" color="text.secondary">
<Typography
variant="body2"
color="text.secondary"
>
{t(`pages.settings.globalpingSettings.${tier.descKey}`)}
</Typography>
</Box>
@@ -145,18 +151,30 @@ const SettingsGlobalping = ({
<Typography sx={{ mb: theme.spacing(4) }}>
{t("pages.settings.globalpingSettings.rateLimitsDescription")}
</Typography>
<Box component="ul" sx={{ m: 0, pl: theme.spacing(8) }}>
<Typography component="li" sx={{ mb: theme.spacing(2) }}>
<Box
component="ul"
sx={{ m: 0, pl: theme.spacing(8) }}
>
<Typography
component="li"
sx={{ mb: theme.spacing(2) }}
>
{t("pages.settings.globalpingSettings.rateLimitNoAuth")}
</Typography>
<Typography component="li" sx={{ mb: theme.spacing(2) }}>
<Typography
component="li"
sx={{ mb: theme.spacing(2) }}
>
{t("pages.settings.globalpingSettings.rateLimitAuth")}
</Typography>
<Typography component="li">
{t("pages.settings.globalpingSettings.rateLimitSponsor")}
</Typography>
</Box>
<Typography color="text.secondary" sx={{ mt: theme.spacing(4), fontStyle: "italic" }}>
<Typography
color="text.secondary"
sx={{ mt: theme.spacing(4), fontStyle: "italic" }}
>
{t("pages.settings.globalpingSettings.rateLimitsNote")}
</Typography>
</Box>
+5 -1
View File
@@ -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;
}
+3 -1
View File
@@ -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<string, string>;
@@ -177,7 +177,10 @@ export const MonitorTable = ({
{row?.name}
{row.globalpingEnabled && (
<Tooltip title={t("pages.uptime.table.headers.globalpingEnabled")}>
<Globe size={14} aria-label={t("pages.uptime.table.headers.globalpingEnabled")} />
<Globe
size={14}
aria-label={t("pages.uptime.table.headers.globalpingEnabled")}
/>
</Tooltip>
)}
</Stack>
+16
View File
@@ -13,6 +13,7 @@ import type {
CheckNetworkInterfaceInfo,
GotTimings,
ILighthouseAudit,
LocationResult,
} from "@/types/check.js";
type CheckMetadataDocument = Omit<CheckMetadata, "monitorId" | "teamId"> & {
@@ -176,6 +177,17 @@ const auditsSchema = new Schema<CheckAudits>(
{ _id: false }
);
const locationResultSchema = new Schema<LocationResult>(
{
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<CheckMetadataDocument>(
{
monitorId: {
@@ -285,6 +297,10 @@ const CheckSchema = new Schema<CheckDocument>(
type: auditsSchema,
default: undefined,
},
locationResults: {
type: [locationResultSchema],
default: undefined,
},
},
{
timestamps: true,
@@ -35,12 +35,7 @@ export interface IChecksRepository {
options?: { type?: MonitorType }
): Promise<UptimeChecksResult | HardwareChecksResult | PageSpeedChecksResult>;
findSummaryByTeamId(teamId: string, dateRange: string): Promise<ChecksSummary>;
findLocationChecksByMonitorId(
monitorId: string,
startDate: Date,
endDate: Date,
dateString: string
): Promise<Record<string, UptimeChecksResult>>;
findLocationChecksByMonitorId(monitorId: string, startDate: Date, endDate: Date, dateString: string): Promise<Record<string, UptimeChecksResult>>;
// update
//delete
deleteByMonitorId(monitorId: string): Promise<number>;
@@ -422,12 +422,7 @@ class MongoChecksRepository implements IChecksRepository {
return result.deletedCount ?? 0;
};
findLocationChecksByMonitorId = async (
monitorId: string,
startDate: Date,
endDate: Date,
dateString: string
): Promise<Record<string, any>> => {
findLocationChecksByMonitorId = async (monitorId: string, startDate: Date, endDate: Date, dateString: string): Promise<Record<string, any>> => {
const monitorObjectId = new mongoose.Types.ObjectId(monitorId);
const matchStage = {
"metadata.monitorId": monitorObjectId,
@@ -469,8 +464,7 @@ class MongoChecksRepository implements IChecksRepository {
const locationChecks: Record<string, any> = {};
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,
@@ -226,12 +226,7 @@ export class MonitorService implements IMonitorService {
// Fetch location checks if Globalping is enabled for this monitor
let locationChecks: Record<string, LocationCheckData> | 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;
}
@@ -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({
+9
View File
@@ -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;
+8
View File
@@ -37,6 +37,14 @@ export interface MonitorStatusResponse<T = any> {
location?: string;
}
export interface GlobalpingCheckResult {
location: string;
status: boolean;
responseTime: number;
statusCode: number;
message: string;
}
export interface PingStatusPayload {
host: string;
numeric_host?: string;