-
+
No ASN found. Enter a custom ASN
below.
@@ -641,7 +751,7 @@ export function PolicyAccessRulesTable({
))}
-
+
-
+
{t("noRegionFound")}
@@ -775,10 +885,19 @@ export function PolicyAccessRulesTable({
)}
))}
-
+
+ ) : row.original.match === "METHOD" ? (
+
+ updateRule(row.original.ruleId, { value })
+ }
+ />
) : (
COUNTRIES.some((country) => country.code === value),
{ message: t("rulesErrorInvalidCountryDescription") }
);
+ case "METHOD":
+ return required.refine(isValidHttpMethodList, {
+ message: t("rulesErrorInvalidMethodDescription")
+ });
case "ASN":
return required.refine(
(value) => {
diff --git a/src/components/site-selector.tsx b/src/components/site-selector.tsx
index 2a7717572..af918a4fb 100644
--- a/src/components/site-selector.tsx
+++ b/src/components/site-selector.tsx
@@ -15,7 +15,7 @@ import { CheckIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useDebounce } from "use-debounce";
-export type Selectedsite = Pick<
+export type SelectedSite = Pick<
ListSitesResponse["sites"][number],
"name" | "siteId" | "type"
> & {
@@ -24,8 +24,8 @@ export type Selectedsite = Pick<
};
type SiteOnlineStatusProps = {
- type: Selectedsite["type"];
- online: Selectedsite["online"];
+ type: SelectedSite["type"];
+ online: SelectedSite["online"];
};
/** Dot-only indicator matching `SitesTable` colors (newt/wireguard only; nothing for local or missing status). */
@@ -57,8 +57,8 @@ export function SiteOnlineStatus({ type, online }: SiteOnlineStatusProps) {
export type SitesSelectorProps = {
orgId: string;
- selectedSite?: Selectedsite | null;
- onSelectSite: (selected: Selectedsite) => void;
+ selectedSite?: SelectedSite | null;
+ onSelectSite: (selected: SelectedSite) => void;
filterTypes?: string[];
};
@@ -82,7 +82,7 @@ export function SitesSelector({
// always include the selected site in the list of sites shown
const sitesShown = useMemo(() => {
- const allSites: Array = filterTypes
+ const allSites: Array = filterTypes
? sites.filter((s) => filterTypes.includes(s.type))
: [...sites];
if (
diff --git a/src/lib/privateResourceForm.ts b/src/lib/privateResourceForm.ts
index 612cce4d8..4b8464540 100644
--- a/src/lib/privateResourceForm.ts
+++ b/src/lib/privateResourceForm.ts
@@ -421,7 +421,14 @@ export function createCreateFormSchema(t: TranslateFn) {
.min(1, t("createInternalResourceDialogNameRequired"))
.max(255, t("createInternalResourceDialogNameMaxLength")),
siteIds: z.array(z.number().int().positive()).optional(),
- mode: z.enum(["host", "cidr", "http", "ssh", "inference"]),
+ mode: z.enum([
+ "host",
+ "cidr",
+ "http",
+ "ssh",
+ "inference",
+ "gateway"
+ ]),
destination: z.string().nullish(),
alias: z.string().nullish(),
destinationPort: z
@@ -468,6 +475,7 @@ export function createCreateFormSchema(t: TranslateFn) {
if (
data.mode !== "ssh" &&
data.mode !== "inference" &&
+ data.mode !== "gateway" &&
(!trimmedDestination || trimmedDestination.length < 1)
) {
ctx.addIssue({
@@ -652,6 +660,13 @@ export function createCidrFormSchema(t: TranslateFn) {
.superRefine((data, ctx) => destinationRefine(data, ctx, t));
}
+export function createGatewayFormSchema(t: TranslateFn) {
+ return z.object({
+ siteIds: z.array(z.number().int().positive()).min(1),
+ mode: z.literal("gateway")
+ });
+}
+
export function createHttpFormSchema(t: TranslateFn) {
return z
.object({
diff --git a/src/lib/privateResourceUtils.ts b/src/lib/privateResourceUtils.ts
index 37cc63756..7d53b5d71 100644
--- a/src/lib/privateResourceUtils.ts
+++ b/src/lib/privateResourceUtils.ts
@@ -1,11 +1,11 @@
"use client";
-import type { Selectedsite } from "@app/components/site-selector";
+import type { SelectedSite } from "@app/components/site-selector";
import type { SiteResourceData } from "@app/lib/privateResourceForm";
export function buildSelectedSitesForResource(
resource: Pick
-): Selectedsite[] {
+): SelectedSite[] {
return resource.siteIds.map((siteId, idx) => ({
name: resource.siteNames[idx] ?? "",
siteId,