"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}
);
}