From 74f4751bcc8b897f4a4d6027a3f0dd55f87b5670 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 11 Mar 2026 17:47:15 -0700 Subject: [PATCH] Dont show raw resource option unless remote node --- .../settings/resources/proxy/create/page.tsx | 110 ++++++++++++------ 1 file changed, 76 insertions(+), 34 deletions(-) diff --git a/src/app/[orgId]/settings/resources/proxy/create/page.tsx b/src/app/[orgId]/settings/resources/proxy/create/page.tsx index 127917555..4c8cb8443 100644 --- a/src/app/[orgId]/settings/resources/proxy/create/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/create/page.tsx @@ -54,6 +54,7 @@ import { TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip"; +import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; @@ -65,6 +66,7 @@ import { build } from "@server/build"; import { Resource } from "@server/db"; import { isTargetValid } from "@server/lib/validators"; import { ListTargetsResponse } from "@server/routers/target"; +import { ListRemoteExitNodesResponse } from "@server/routers/remoteExitNode/types"; import { ArrayElement } from "@server/types/ArrayElement"; import { useQuery } from "@tanstack/react-query"; import { @@ -81,6 +83,7 @@ import { CircleCheck, CircleX, Info, + InfoIcon, Plus, Settings, SquareArrowOutUpRight @@ -210,6 +213,13 @@ export default function Page() { orgQueries.sites({ orgId: orgId as string }) ); + const [remoteExitNodes, setRemoteExitNodes] = useState< + ListRemoteExitNodesResponse["remoteExitNodes"] + >([]); + const [loadingExitNodes, setLoadingExitNodes] = useState( + build === "saas" + ); + const [createLoading, setCreateLoading] = useState(false); const [showSnippets, setShowSnippets] = useState(false); const [niceId, setNiceId] = useState(""); @@ -224,6 +234,27 @@ export default function Page() { useState(null); const [healthCheckDialogOpen, setHealthCheckDialogOpen] = useState(false); + useEffect(() => { + if (build !== "saas") return; + + const fetchExitNodes = async () => { + try { + const res = await api.get< + AxiosResponse + >(`/org/${orgId}/remote-exit-nodes`); + if (res && res.status === 200) { + setRemoteExitNodes(res.data.data.remoteExitNodes); + } + } catch (e) { + console.error("Failed to fetch remote exit nodes:", e); + } finally { + setLoadingExitNodes(false); + } + }; + + fetchExitNodes(); + }, [orgId]); + const [isAdvancedMode, setIsAdvancedMode] = useState(() => { if (typeof window !== "undefined") { const saved = localStorage.getItem("create-advanced-mode"); @@ -289,15 +320,25 @@ export default function Page() { }, ...(!env.flags.allowRawResources ? [] - : [ - { - id: "raw" as ResourceType, - title: t("resourceRaw"), - description: build == "saas" ? t("resourceRawDescriptionCloud") : t("resourceRawDescription") - } - ]) + : build === "saas" && remoteExitNodes.length === 0 + ? [] + : [ + { + id: "raw" as ResourceType, + title: t("resourceRaw"), + description: + build == "saas" + ? t("resourceRawDescriptionCloud") + : t("resourceRawDescription") + } + ]) ]; + // In saas mode with no exit nodes, force HTTP + const showTypeSelector = + build !== "saas" || + (!loadingExitNodes && remoteExitNodes.length > 0); + const baseForm = useForm({ resolver: zodResolver(baseResourceFormSchema), defaultValues: { @@ -984,34 +1025,35 @@ export default function Page() { - {resourceTypes.length > 1 && ( - <> -
- - {t("type")} - -
+ {showTypeSelector && + resourceTypes.length > 1 && ( + <> +
+ + {t("type")} + +
- { - baseForm.setValue( - "http", - value === "http" - ); - // Update method default when switching resource type - addTargetForm.setValue( - "method", - value === "http" - ? "http" - : null - ); - }} - cols={2} - /> - - )} + { + baseForm.setValue( + "http", + value === "http" + ); + // Update method default when switching resource type + addTargetForm.setValue( + "method", + value === "http" + ? "http" + : null + ); + }} + cols={2} + /> + + )}