"use client"; import CopyToClipboard from "@app/components/CopyToClipboard"; import { InfoSection, InfoSectionContent, InfoSections, InfoSectionTitle } from "@app/components/InfoSection"; import { SettingsSection, SettingsSectionBody, SettingsSectionDescription, SettingsSectionHeader, SettingsSectionTitle } from "@app/components/Settings"; import { SidePanel, SidePanelBody, SidePanelContent, SidePanelFooter, SidePanelHeader, SidePanelTitle } from "@app/components/SidePanel"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { Button } from "@app/components/ui/button"; import { derivePublicAuthState, formatPortRestrictionDisplay, formatPublicResourceType } from "@app/lib/launcherResourceDetails"; import { getLauncherResourceAdminHref } from "@app/lib/launcherResourceAdminHref"; import { formatSiteResourceDestinationDisplay, isSafeUrlForLink } from "@app/lib/launcherResourceAccess"; import { launcherQueries } from "@app/lib/queries"; import type { LauncherResource } from "@server/routers/launcher/types"; import type { GetResourceAuthInfoResponse } from "@server/routers/resource/getResourceAuthInfo"; import type { GetResourceResponse } from "@server/routers/resource/getResource"; import type { GetSiteResourceResponse } from "@server/routers/siteResource/getSiteResource"; import { useQuery } from "@tanstack/react-query"; import { AlertCircle, CheckCircle2, Clock, ExternalLink, Loader2, ShieldCheck, ShieldOff, XCircle } from "lucide-react"; import { useTranslations } from "next-intl"; import Link from "next/link"; type LauncherResourcePanelProps = { open: boolean; onOpenChange: (open: boolean) => void; resource: LauncherResource | null; orgId: string; isAdmin: boolean; }; type LauncherResourceDetailResult = | { resourceType: "public"; data: GetResourceResponse; authInfo: GetResourceAuthInfoResponse; } | { resourceType: "site"; data: GetSiteResourceResponse }; function AccessMethodContent({ accessDisplay, accessCopyValue, accessUrl }: { accessDisplay: string; accessCopyValue: string; accessUrl?: string | null; }) { if (!accessDisplay) { return -; } const href = accessUrl ?? undefined; const canLink = Boolean(href && isSafeUrlForLink(href)); if (canLink && href) { return ( ); } return ( ); } function HealthStatusDisplay({ health }: { health: string | null | undefined; }) { const t = useTranslations(); const status = health ?? "unknown"; if (status === "healthy") { return (
{t("resourcesTableHealthy")}
); } if (status === "degraded") { return (
{t("resourcesTableDegraded")}
); } if (status === "unhealthy") { return (
{t("resourcesTableUnhealthy")}
); } return (
{t("resourcesTableUnknown")}
); } const PUBLIC_AUTH_BROWSER_MODES = ["http", "ssh", "rdp", "vnc"]; function AuthMethodStatusDisplay({ enabled }: { enabled: boolean }) { const t = useTranslations(); return (
{enabled ? ( ) : ( )} {enabled ? t("enabled") : t("disabled")}
); } function PublicResourceAuthMethods({ authInfo }: { authInfo: GetResourceAuthInfoResponse; }) { const t = useTranslations(); const authMethods = [ { key: "sso", title: t("policyAuthSsoTitle"), enabled: authInfo.sso }, { key: "password", title: t("policyAuthPasscodeTitle"), enabled: authInfo.password }, { key: "pincode", title: t("policyAuthPincodeTitle"), enabled: authInfo.pincode }, { key: "whitelist", title: t("policyAuthEmailTitle"), enabled: authInfo.whitelist }, { key: "headerAuth", title: t("policyAuthHeaderAuthTitle"), enabled: authInfo.headerAuth } ]; return ( {t("authentication")} {t("resourceLauncherAuthMethodsDescription")} {authMethods.map((method) => ( {method.title} ))} ); } function PublicResourceDetails({ launcherResource, resource, authInfo }: { launcherResource: LauncherResource; resource: GetResourceResponse; authInfo: GetResourceAuthInfoResponse; }) { const t = useTranslations(); const supportsAuth = PUBLIC_AUTH_BROWSER_MODES.includes( resource.mode || "" ); const authState = derivePublicAuthState(resource.mode, authInfo); const infoSectionCount = supportsAuth ? 4 : 3; return (
{t("resourceLauncherResourceDetails")} {t("resourceLauncherResourceDetailsDescription")} {t("type")} {formatPublicResourceType(resource)} {t("access")} {supportsAuth ? ( {t("authentication")} {authState === "protected" ? (
{t("protected")}
) : (
{t("notProtected")}
)}
) : null} {t("health")}
{supportsAuth ? ( ) : null}
); } function PrivateResourceDetails({ launcherResource, resource }: { launcherResource: LauncherResource; resource: GetSiteResourceResponse; }) { const t = useTranslations(); const modeLabel: Record = { host: t("editInternalResourceDialogModeHost"), cidr: t("editInternalResourceDialogModeCidr"), http: t("editInternalResourceDialogModeHttp"), ssh: t("editInternalResourceDialogModeSsh") }; const destination = formatSiteResourceDestinationDisplay({ mode: resource.mode, destination: resource.destination, destinationPort: resource.destinationPort, scheme: resource.scheme }); const portRestrictions = formatPortRestrictionDisplay(resource); const showAlias = resource.mode !== "cidr" && resource.mode !== "http"; const showDestination = !( resource.mode === "ssh" && resource.authDaemonMode === "native" ); const infoSectionCount = 2 + (showDestination ? 1 : 0) + (showAlias ? 1 : 0) + 1; return (
{t("resourceLauncherPrivateClientRequiredTitle")} {t("resourceLauncherPrivateClientRequired")}{" "} {t("resourceLauncherDownloadClient")} {t("resourceLauncherResourceDetails")} {t("resourceLauncherResourceDetailsDescription")} {t("type")} {modeLabel[resource.mode]} {t("access")} {showDestination ? ( {t("editInternalResourceDialogDestination")} {destination || "-"} ) : null} {showAlias ? ( {t("editInternalResourceDialogAlias")} {resource.alias?.trim() ? resource.alias : "-"} ) : null} {t("portRestrictions")} {!portRestrictions.hasNonDefaultPorts ? ( {t( "resourceLauncherNoPortRestrictions" )} ) : (
{portRestrictions.tcp.state !== "all" ? (
{t("resourceLauncherTcp")}:{" "} {portRestrictions.tcp.state === "blocked" ? t("blocked") : portRestrictions.tcp .ports}
) : null} {portRestrictions.udp.state !== "all" ? (
{t("resourceLauncherUdp")}:{" "} {portRestrictions.udp.state === "blocked" ? t("blocked") : portRestrictions.udp .ports}
) : null}
)}
); } function LauncherResourcePanelBody({ orgId, resource, open }: { orgId: string; resource: LauncherResource; open: boolean; }) { const t = useTranslations(); const { data, isPending, isError } = useQuery({ ...launcherQueries.resourceDetail(orgId, resource), enabled: open }); if (isPending) { return (
); } if (isError || !data) { return (

{t("resourceLauncherFailedToLoadDetails")}

); } const detail = data as LauncherResourceDetailResult; if (detail.resourceType === "public") { return ( ); } return ( ); } export function LauncherResourcePanel({ open, onOpenChange, resource, orgId, isAdmin }: LauncherResourcePanelProps) { const t = useTranslations(); return ( {resource?.name ?? ""} {resource ? ( ) : null} {isAdmin && resource ? ( ) : null} ); }