Merge pull request #3737 from bluewave-labs/feat/head-request-fe

feat: head request front end
This commit is contained in:
Alexander Holliday authored and GitHub committed 2026-06-22 11:36:02 -07:00
commit d6f9fca907
8 files changed
+137 -31

No files matched your search

+3 -1
View File
@@ -1,6 +1,6 @@
import { useMemo } from "react";
import { monitorSchema, type MonitorFormData } from "@/Validation/monitor";
import { DefaultPageSpeedStrategy } from "@/Types/Monitor";
import { DefaultHttpMethod, DefaultPageSpeedStrategy } from "@/Types/Monitor";
import type { Monitor, MonitorType } from "@/Types/Monitor";
interface UseMonitorFormOptions {
@@ -40,6 +40,7 @@ export const getMonitorDefaults = (
...getGeoCheckDefaults(data),
type: "http",
url: data?.url || "",
method: data?.method ?? DefaultHttpMethod,
ignoreTlsErrors: data?.ignoreTlsErrors || false,
useAdvancedMatching: data?.useAdvancedMatching || false,
matchMethod: data?.matchMethod || "",
@@ -134,6 +135,7 @@ export const getMonitorDefaults = (
...getGeoCheckDefaults(data),
type: "http",
url: "",
method: DefaultHttpMethod,
ignoreTlsErrors: false,
useAdvancedMatching: false,
matchMethod: "",
+73 -23
View File
@@ -42,8 +42,11 @@ import {
type Monitor,
type MonitorType,
type GamesMap,
type HttpMethod,
supportsGeoCheck,
DefaultPageSpeedStrategy,
DefaultHttpMethod,
HttpMethods,
} from "@/Types/Monitor";
import type { Notification } from "@/Types/Notification";
import type { Tag } from "@/Types/Tag";
@@ -259,7 +262,7 @@ const CreateMonitorPage = () => {
resolver: zodResolver(schema),
defaultValues: defaults,
});
const { control, watch, handleSubmit, clearErrors, trigger, reset } = form;
const { control, watch, handleSubmit, clearErrors, trigger, reset, setValue } = form;
useEffect(() => {
reset(defaults);
@@ -271,6 +274,7 @@ const CreateMonitorPage = () => {
const showStep = (step: number) => isEditMode || currentStep === step;
const watchedType = watch("type") as MonitorType;
const watchedMethod = watch("method") as HttpMethod | undefined;
const watchedUseAdvancedMatching = watch("useAdvancedMatching") as boolean;
const watchGeoCheckEnabled = watch("geoCheckEnabled") as boolean;
@@ -1091,6 +1095,50 @@ const CreateMonitorPage = () => {
subtitle={t("pages.createMonitor.form.advanced.description")}
rightContent={
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Controller
name="method"
control={control}
render={({ field }) => (
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Select
{...field}
value={field.value ?? DefaultHttpMethod}
onChange={(e) => {
const value = e.target.value as HttpMethod;
field.onChange(value);
// HEAD has no response body, reset advanced matching fields if selected
if (value === "HEAD") {
setValue("useAdvancedMatching", false);
setValue("matchMethod", "");
setValue("expectedValue", "");
setValue("jsonPath", "");
}
}}
fieldLabel={t(
"pages.createMonitor.form.advanced.option.method.label"
)}
>
{HttpMethods.map((method) => (
<MenuItem
key={method}
value={method}
>
{method}
</MenuItem>
))}
</Select>
<Typography
component="span"
color={theme.palette.text.secondary}
sx={{ opacity: 0.8 }}
>
{t(
`pages.createMonitor.form.advanced.option.method.description.${field.value ?? DefaultHttpMethod}`
)}
</Typography>
</Stack>
)}
/>
<Controller
name="customUpCodes"
control={control}
@@ -1162,28 +1210,30 @@ const CreateMonitorPage = () => {
);
}}
/>
<Controller
name="useAdvancedMatching"
control={control}
render={({ field }) => (
<Stack
direction="row"
alignItems="center"
spacing={theme.spacing(SPACING.LG)}
>
<Switch
checked={field.value ?? false}
onChange={(e) => field.onChange(e.target.checked)}
/>
<Typography>
{t(
"pages.createMonitor.form.advanced.option.advancedMatching.label"
)}
</Typography>
</Stack>
)}
/>
{watchedUseAdvancedMatching && (
{watchedMethod !== "HEAD" && (
<Controller
name="useAdvancedMatching"
control={control}
render={({ field }) => (
<Stack
direction="row"
alignItems="center"
spacing={theme.spacing(SPACING.LG)}
>
<Switch
checked={field.value ?? false}
onChange={(e) => field.onChange(e.target.checked)}
/>
<Typography>
{t(
"pages.createMonitor.form.advanced.option.advancedMatching.label"
)}
</Typography>
</Stack>
)}
/>
)}
{watchedUseAdvancedMatching && watchedMethod !== "HEAD" && (
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Controller
name="matchMethod"
@@ -324,7 +324,9 @@ export const MonitorTable = ({
</Stack>
),
render: (row) => {
return row.type;
return row.type === "http" && row.method
? `${row.type} (${row.method})`
: row.type;
},
},
{
+5
View File
@@ -48,6 +48,10 @@ export const PageSpeedStrategies = ["desktop", "mobile"] as const;
export type PageSpeedStrategy = (typeof PageSpeedStrategies)[number];
export const DefaultPageSpeedStrategy: PageSpeedStrategy = "desktop";
export const HttpMethods = ["GET", "HEAD"] as const;
export type HttpMethod = (typeof HttpMethods)[number];
export const DefaultHttpMethod: HttpMethod = "GET";
export interface Monitor {
id: string;
userId: string;
@@ -64,6 +68,7 @@ export interface Monitor {
jsonPath?: string;
expectedValue?: string;
matchMethod?: MonitorMatchMethod;
method?: HttpMethod;
url: string;
port?: number;
isActive: boolean;
+7 -1
View File
@@ -1,7 +1,12 @@
import { z } from "zod";
import type { FieldPath } from "react-hook-form";
import { GeoContinents } from "@/Types/GeoCheck";
import { DnsRecordTypes, PageSpeedStrategies, type MonitorType } from "@/Types/Monitor";
import {
DnsRecordTypes,
HttpMethods,
PageSpeedStrategies,
type MonitorType,
} from "@/Types/Monitor";
import { ALL_HTTP_STATUS_CODES } from "@/Utils/statusCode";
// Wizard step a field is validated on. Attached inline to each field below so
@@ -62,6 +67,7 @@ const httpStatusCode = z.number().refine((code) => httpStatusCodeSet.has(code),
const httpSchema = baseSchema.extend({
type: z.literal("http"),
url: urlSchema,
method: z.enum(HttpMethods).optional().register(monitorStepRegistry, { step: 2 }),
ignoreTlsErrors: z.boolean().register(monitorStepRegistry, { step: 2 }),
useAdvancedMatching: z.boolean().register(monitorStepRegistry, { step: 2 }),
matchMethod: z
+7
View File
@@ -490,6 +490,13 @@
"advanced": {
"description": "Optional settings for advanced use cases",
"option": {
"method": {
"label": "Request method",
"description": {
"GET": "GET fetches the full response including the body. Advanced matching fully supported",
"HEAD": "HEAD fetches only response headers for reducing load on the target. Supports status code matching but not advanced matching as there is no body."
}
},
"advancedMatching": {
"label": "Use advanced matching"
},
+37 -3
View File
@@ -1554,9 +1554,9 @@
}
},
"node_modules/@emnapi/wasi-threads": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
"integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -5200,6 +5200,40 @@
"node": ">=14.0.0"
}
},
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
}
},
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz",
@@ -536,7 +536,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
teamId: toStringId(doc.teamId),
name: doc.name,
description: doc.description ?? undefined,
method: doc.method,
method: doc.method ?? "GET",
status: doc.status ?? "initializing",
statusWindow: doc.statusWindow ?? [],
statusWindowSize: doc.statusWindowSize,
@@ -603,7 +603,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
teamId: toStringId(doc.teamId),
name: doc.name,
description: doc.description ?? undefined,
method: doc.method,
method: doc.method ?? "GET",
status: doc.status ?? "initializing",
statusWindow: doc.statusWindow ?? [],
statusWindowSize: doc.statusWindowSize,