diff --git a/server/routers/resource/getResource.ts b/server/routers/resource/getResource.ts index cd870dcbf..7a52c0a85 100644 --- a/server/routers/resource/getResource.ts +++ b/server/routers/resource/getResource.ts @@ -1,15 +1,14 @@ -import { Request, Response, NextFunction } from "express"; -import { z } from "zod"; -import { db } from "@server/db"; -import { Resource, resources, sites } from "@server/db"; -import { eq, and } from "drizzle-orm"; +import { db, resources } from "@server/db"; import response from "@server/lib/response"; -import HttpCode from "@server/types/HttpCode"; -import createHttpError from "http-errors"; -import { fromError } from "zod-validation-error"; -import logger from "@server/logger"; import stoi from "@server/lib/stoi"; +import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; +import HttpCode from "@server/types/HttpCode"; +import { and, eq } from "drizzle-orm"; +import { NextFunction, Request, Response } from "express"; +import createHttpError from "http-errors"; +import { z } from "zod"; +import { fromError } from "zod-validation-error"; const getResourceSchema = z.strictObject({ resourceId: z diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts index e4ef45f3b..18e932afa 100644 --- a/server/routers/target/listTargets.ts +++ b/server/routers/target/listTargets.ts @@ -40,6 +40,7 @@ function queryTargets(resourceId: number) { resourceId: targets.resourceId, siteId: targets.siteId, siteType: sites.type, + siteName: sites.name, hcEnabled: targetHealthCheck.hcEnabled, hcPath: targetHealthCheck.hcPath, hcScheme: targetHealthCheck.hcScheme, diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/proxy/page.tsx index 51f11a2c3..aff10dc52 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/proxy/page.tsx @@ -124,20 +124,15 @@ export default function ReverseProxyTargetsPage(props: { resourceId: resource.resourceId }) ); - const { data: sites = [], isLoading: isLoadingSites } = useQuery( - orgQueries.sites({ - orgId: params.orgId - }) - ); - if (isLoadingSites || isLoadingTargets) { + if (isLoadingTargets) { return null; } return ( @@ -160,12 +155,12 @@ export default function ReverseProxyTargetsPage(props: { } function ProxyResourceTargetsForm({ - sites, + orgId, initialTargets, resource }: { initialTargets: LocalTarget[]; - sites: ListSitesResponse["sites"]; + orgId: string; resource: GetResourceResponse; }) { const t = useTranslations(); @@ -243,17 +238,21 @@ function ProxyResourceTargetsForm({ }); }, []); + const { data: sites = [] } = useQuery( + orgQueries.sites({ + orgId + }) + ); + const updateTarget = useCallback( (targetId: number, data: Partial) => { setTargets((prevTargets) => { - const site = sites.find((site) => site.siteId === data.siteId); return prevTargets.map((target) => target.targetId === targetId ? { ...target, ...data, - updated: true, - siteType: site ? site.type : target.siteType + updated: true } : target ); @@ -453,7 +452,7 @@ function ProxyResourceTargetsForm({ return ( 0 ? sites[0].siteId : 0, + siteName: sites.length > 0 ? sites[0].name : "", path: isHttp ? null : null, pathMatchType: isHttp ? null : null, rewritePath: isHttp ? null : null, diff --git a/src/app/[orgId]/settings/resources/proxy/create/page.tsx b/src/app/[orgId]/settings/resources/proxy/create/page.tsx index 4c8cb8443..9a9eb3ba2 100644 --- a/src/app/[orgId]/settings/resources/proxy/create/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/create/page.tsx @@ -216,9 +216,7 @@ export default function Page() { const [remoteExitNodes, setRemoteExitNodes] = useState< ListRemoteExitNodesResponse["remoteExitNodes"] >([]); - const [loadingExitNodes, setLoadingExitNodes] = useState( - build === "saas" - ); + const [loadingExitNodes, setLoadingExitNodes] = useState(build === "saas"); const [createLoading, setCreateLoading] = useState(false); const [showSnippets, setShowSnippets] = useState(false); @@ -282,6 +280,7 @@ export default function Page() { method: isHttp ? "http" : null, port: 0, siteId: sites.length > 0 ? sites[0].siteId : 0, + siteName: sites.length > 0 ? sites[0].name : "", path: isHttp ? null : null, pathMatchType: isHttp ? null : null, rewritePath: isHttp ? null : null, @@ -336,8 +335,7 @@ export default function Page() { // In saas mode with no exit nodes, force HTTP const showTypeSelector = - build !== "saas" || - (!loadingExitNodes && remoteExitNodes.length > 0); + build !== "saas" || (!loadingExitNodes && remoteExitNodes.length > 0); const baseForm = useForm({ resolver: zodResolver(baseResourceFormSchema), @@ -600,7 +598,10 @@ export default function Page() { toast({ variant: "destructive", title: t("resourceErrorCreate"), - description: formatAxiosError(e, t("resourceErrorCreateMessageDescription")) + description: formatAxiosError( + e, + t("resourceErrorCreateMessageDescription") + ) }); } @@ -826,7 +827,8 @@ export default function Page() { cell: ({ row }) => ( DockerState; updateTarget: (targetId: number, data: Partial) => void; - sites: SiteWithUpdateAvailable[]; + orgId: string; proxyTarget: LocalTarget; isHttp: boolean; refreshContainersForSite: (siteId: number) => void; }; export function ResourceTargetAddressItem({ - sites, + orgId, getDockerStateForSite, updateTarget, proxyTarget, @@ -52,10 +54,34 @@ export function ResourceTargetAddressItem({ }: ResourceTargetAddressItemProps) { const t = useTranslations(); - const selectedSite = sites.find( - (site) => site.siteId === proxyTarget.siteId + const [siteSearchQuery, setSiteSearchQuery] = useState(""); + + const { data: sites = [] } = useQuery( + orgQueries.sites({ + orgId, + query: siteSearchQuery, + perPage: 10 + }) ); + const [selectedSite, setSelectedSite] = useState | null>(() => { + if ( + proxyTarget.siteName && + proxyTarget.siteType && + proxyTarget.siteId + ) { + return { + name: proxyTarget.siteName, + siteId: proxyTarget.siteId, + type: proxyTarget.siteType + }; + } + return null; + }); + const handleContainerSelectForTarget = ( hostname: string, port?: number @@ -70,28 +96,23 @@ export function ResourceTargetAddressItem({ return (
- {selectedSite && - selectedSite.type === "newt" && - (() => { - const dockerState = getDockerStateForSite( - selectedSite.siteId - ); - return ( - - refreshContainersForSite( - selectedSite.siteId - ) - } - /> - ); - })()} + {selectedSite && selectedSite.type === "newt" && ( + + refreshContainersForSite(selectedSite.siteId) + } + /> + )} @@ -113,8 +134,11 @@ export function ResourceTargetAddressItem({ - - + + setSiteSearchQuery(v)} + /> {t("siteNotFound")} @@ -122,14 +146,18 @@ export function ResourceTargetAddressItem({ + onSelect={() => { updateTarget( proxyTarget.targetId, { - siteId: site.siteId + siteId: site.siteId, + siteType: site.type, + siteName: site.name } - ) - } + ); + + setSelectedSite(site); + }} > + sites: ({ + orgId, + query, + perPage = 10_000 + }: { + orgId: string; + query?: string; + perPage?: number; + }) => queryOptions({ - queryKey: ["ORG", orgId, "SITES"] as const, + queryKey: ["ORG", orgId, "SITES", { query, perPage }] as const, queryFn: async ({ signal, meta }) => { const sp = new URLSearchParams({ - pageSize: "10000" + pageSize: perPage.toString() }); + if (query?.trim()) { + sp.set("query", query); + } + const res = await meta!.api.get< AxiosResponse >(`/org/${orgId}/sites?${sp.toString()}`, { signal });