"use client"; import { AiClientConfigSection } from "@app/components/ai-client-config/AiClientConfigSection"; import CopyToClipboard from "@app/components/CopyToClipboard"; import { InfoSection, InfoSectionContent, InfoSections, InfoSectionTitle } from "@app/components/InfoSection"; import { PrivateResourceInfoSections } from "@app/components/PrivateResourceInfoBox"; import { LauncherInferenceApiKeysSection } from "@app/components/resource-launcher/LauncherInferenceApiKeysSection"; import { LauncherInferenceModelsSection } from "@app/components/resource-launcher/LauncherInferenceModelsSection"; 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 { useMyVirtualApiKeySecret } from "@app/hooks/useMyVirtualApiKeySecret"; import { derivePublicAuthState, formatPublicResourceType } from "@app/lib/launcherResourceDetails"; import { getLauncherResourceAdminHref } from "@app/lib/launcherResourceAdminHref"; import { 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_METHODS_MODES = ["http", "ssh", "rdp", "vnc"]; const PUBLIC_AUTH_BADGE_MODES = [...PUBLIC_AUTH_METHODS_MODES, "inference"]; 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({ orgId, launcherResource, resource, authInfo }: { orgId: string; launcherResource: LauncherResource; resource: GetResourceResponse; authInfo: GetResourceAuthInfoResponse; }) { const t = useTranslations(); const mode = resource.mode || ""; const isInference = mode === "inference"; const showAuthBadge = PUBLIC_AUTH_BADGE_MODES.includes(mode); const showAuthMethods = PUBLIC_AUTH_METHODS_MODES.includes(mode); const showHealth = !isInference; const authState = derivePublicAuthState(resource.mode, authInfo); const infoSectionCount = 2 + (showAuthBadge ? 1 : 0) + (showHealth ? 1 : 0); const { data: aiKeysData } = useQuery({ ...launcherQueries.myVirtualApiKeys(orgId, resource.resourceGuid), enabled: isInference }); const { getCopyText: getAiKeyCopyText } = useMyVirtualApiKeySecret( orgId, aiKeysData?.userKey.virtualApiKeyId ?? "" ); return (
{t("resourceLauncherResourceDetails")} {t("resourceLauncherResourceDetailsDescription")} {t("type")} {formatPublicResourceType(resource)} {t("access")} {showAuthBadge ? ( {t("authentication")} {authState === "protected" ? (
{t("protected")}
) : (
{t("notProtected")}
)}
) : null} {showHealth ? ( {t("health")} ) : null}
{showAuthMethods ? ( ) : null} {isInference ? ( <> {aiKeysData ? ( ) : null} ) : null}
); } function PrivateResourceDetails({ orgId, launcherResource, resource }: { orgId: string; launcherResource: LauncherResource; resource: GetSiteResourceResponse; }) { const t = useTranslations(); const isInference = resource.mode === "inference"; return (
{t("resourceLauncherPrivateClientRequiredTitle")} {t("resourceLauncherPrivateClientRequired")}{" "} {t("resourceLauncherDownloadClient")} {t("resourceLauncherResourceDetails")} {t("resourceLauncherResourceDetailsDescription")} {isInference ? ( <> ) : 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} ); }