feat: add per-monitor Globalping toggle

Add a globalpingEnabled boolean to the Monitor model so users can
opt-in to global location checks per monitor during creation/edit.

## Changes
- Add globalpingEnabled field to server types, Mongoose model,
  Joi validation, and repository mappers
- Add globalpingEnabled to client Monitor type, Zod schemas for
  HTTP and ping monitors, and form defaults
- Add "Global monitoring" ConfigBox with Switch toggle to the
  monitor creation/edit form, visible for HTTP and ping types
- Queue helper and monitorService now check globalpingEnabled
  before running Globalping checks or fetching location data
- Add translation keys for the new form section

## Benefits
- Users can selectively enable Globalping on monitors that need it
  rather than running location checks on every HTTP/ping monitor
- Reduces unnecessary API usage and keeps the UI clean for monitors
  that only need local checks
This commit is contained in:
gorkem-bwl committed 2026-02-19 12:11:56 -05:00
1 parent 7f9d2f275e
commit 4ca24c794e
11 files changed
+47 -3

No files matched your search

+2
View File
@@ -37,6 +37,7 @@ export const useMonitorForm = ({
matchMethod: data?.matchMethod || "",
expectedValue: data?.expectedValue || "",
jsonPath: data?.jsonPath || "",
globalpingEnabled: data?.globalpingEnabled || false,
};
break;
case "ping":
@@ -44,6 +45,7 @@ export const useMonitorForm = ({
...base,
type: "ping",
url: data?.url || "",
globalpingEnabled: data?.globalpingEnabled || false,
};
break;
case "port":
+21
View File
@@ -488,6 +488,27 @@ const CreateMonitorPage = () => {
}
/>
{/* Global monitoring - only for http and ping types */}
{(watchedType === "http" || watchedType === "ping") && (
<ConfigBox
title={t("pages.createMonitor.form.globalping.title")}
subtitle={t("pages.createMonitor.form.globalping.description")}
rightContent={
<Controller
name="globalpingEnabled"
control={control}
render={({ field }) => (
<Switch
id="globalpingEnabled"
isChecked={field.value ?? false}
onChange={(e) => field.onChange(e.target.checked)}
/>
)}
/>
}
/>
)}
{/* Alert Thresholds - only for hardware type */}
{generalSettingsConfig.showSecret && (
<ConfigBox
+1
View File
@@ -59,6 +59,7 @@ export interface Monitor {
gameId?: string;
group: string | null;
locations: string[];
globalpingEnabled: boolean;
recentChecks: CheckSnapshot[];
createdAt: string;
updatedAt: string;
+2
View File
@@ -31,12 +31,14 @@ const httpSchema = baseSchema.extend({
matchMethod: z.enum(["equal", "include", "regex", ""]).optional(),
expectedValue: z.string().optional(),
jsonPath: z.string().optional(),
globalpingEnabled: z.boolean(),
});
// Ping monitor schema
const pingSchema = baseSchema.extend({
type: z.literal("ping"),
url: z.string().min(1, "Host is required"),
globalpingEnabled: z.boolean(),
});
// Port monitor schema
+9
View File
@@ -445,6 +445,15 @@
},
"title": "Check frequency"
},
"globalping": {
"title": "Global monitoring",
"description": "Run checks from multiple locations around the world using the Globalping network. Configure the API key and location count in settings.",
"option": {
"enabled": {
"label": "Enable Globalping"
}
}
},
"general": {
"option": {
"container": {
+4
View File
@@ -164,6 +164,10 @@ const MonitorSchema = new Schema<MonitorDocument>(
type: [String],
default: [],
},
globalpingEnabled: {
type: Boolean,
default: false,
},
group: {
type: String,
trim: true,
@@ -344,6 +344,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
gameId: doc.gameId ?? undefined,
group: doc.group ?? null,
locations: doc.locations ?? [],
globalpingEnabled: doc.globalpingEnabled ?? false,
recentChecks: (doc.recentChecks ?? []).map((check: any) => this.toCheckSnapshot(check)),
createdAt: toDateString(doc.createdAt),
updatedAt: toDateString(doc.updatedAt),
@@ -400,6 +401,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
gameId: doc.gameId ?? undefined,
group: doc.group ?? null,
locations: doc.locations ?? [],
globalpingEnabled: doc.globalpingEnabled ?? false,
recentChecks: (doc.recentChecks ?? []).map((check: any) => this.toCheckSnapshot(check)),
createdAt: toDateString(doc.createdAt),
updatedAt: toDateString(doc.updatedAt),
@@ -223,9 +223,9 @@ export class MonitorService implements IMonitorService {
throw new AppError({ message: `${monitor.type} monitors are not supported for uptime details`, status: 400 });
}
// Fetch location checks if this is an HTTP or ping monitor
// Fetch location checks if Globalping is enabled for this monitor
let locationChecks: Record<string, LocationCheckData> | undefined;
if (monitor.type === "http" || monitor.type === "ping") {
if (monitor.globalpingEnabled && (monitor.type === "http" || monitor.type === "ping")) {
const locData = await this.checksRepository.findLocationChecksByMonitorId(
monitor.id,
start,
@@ -128,7 +128,7 @@ class SuperSimpleQueueHelper {
}
// Step 2b. Run Globalping checks (fire-and-forget, doesn't affect local check pipeline)
if (monitor.type === "http" || monitor.type === "ping") {
if (monitor.globalpingEnabled && (monitor.type === "http" || monitor.type === "ping")) {
this.globalpingService
.runChecks(monitor)
.then((locationStatuses) => {
+1
View File
@@ -44,6 +44,7 @@ export interface Monitor {
gameId?: string;
group: string | null;
locations: string[];
globalpingEnabled: boolean;
recentChecks: CheckSnapshot[];
createdAt: string;
updatedAt: string;
+2
View File
@@ -173,6 +173,7 @@ const createMonitorBodyValidation = joi.object({
selectedDisks: joi.array().items(joi.string()).optional(),
group: joi.string().max(50).trim().allow(null, "").optional(),
locations: joi.array().items(joi.string()).max(5).optional(),
globalpingEnabled: joi.boolean().optional(),
});
const createMonitorsBodyValidation = joi.array().items(
@@ -205,6 +206,7 @@ const editMonitorBodyValidation = joi
selectedDisks: joi.array().items(joi.string()).optional(),
group: joi.string().max(50).trim().allow(null, "").optional(),
locations: joi.array().items(joi.string()).max(5).optional(),
globalpingEnabled: joi.boolean().optional(),
})
.options({ stripUnknown: true });