mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-12 14:01:18 +02:00
show ssh commands
This commit is contained in:
@@ -4279,6 +4279,8 @@
|
|||||||
"resourceLauncherSitesDescription": "The resource is accessible via the following sites.",
|
"resourceLauncherSitesDescription": "The resource is accessible via the following sites.",
|
||||||
"resourceLauncherViewSiteAsAdmin": "View Site as Admin",
|
"resourceLauncherViewSiteAsAdmin": "View Site as Admin",
|
||||||
"resourceLauncherFilterBySite": "Filter by Site",
|
"resourceLauncherFilterBySite": "Filter by Site",
|
||||||
|
"resourceLauncherSshCommand": "SSH Command",
|
||||||
|
"resourceLauncherSshCommandDescription": "Use the Pangolin CLI to open an SSH session to this resource.",
|
||||||
"resourceLauncherAuthMethodsDescription": "Authentication methods enabled for this resource.",
|
"resourceLauncherAuthMethodsDescription": "Authentication methods enabled for this resource.",
|
||||||
"resourceLauncherPrivateClientRequired": "Connect with a client on your device to access this resource privately.",
|
"resourceLauncherPrivateClientRequired": "Connect with a client on your device to access this resource privately.",
|
||||||
"resourceLauncherPrivateClientRequiredTitle": "Client Connection Required",
|
"resourceLauncherPrivateClientRequiredTitle": "Client Connection Required",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { AiClientConfigSection } from "@app/components/ai-client-config/AiClientConfigSection";
|
import { AiClientConfigSection } from "@app/components/ai-client-config/AiClientConfigSection";
|
||||||
|
import { AiConfigBlocks } from "@app/components/ai-client-config/AiConfigBlocks";
|
||||||
|
import CopyTextBox from "@app/components/CopyTextBox";
|
||||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||||
import {
|
import {
|
||||||
InfoSection,
|
InfoSection,
|
||||||
@@ -43,6 +45,7 @@ import {
|
|||||||
TableRow
|
TableRow
|
||||||
} from "@app/components/ui/table";
|
} from "@app/components/ui/table";
|
||||||
import { useMyVirtualApiKeySecret } from "@app/hooks/useMyVirtualApiKeySecret";
|
import { useMyVirtualApiKeySecret } from "@app/hooks/useMyVirtualApiKeySecret";
|
||||||
|
import type { AiConfigBlock } from "@app/lib/aiClientConfig";
|
||||||
import { cn } from "@app/lib/cn";
|
import { cn } from "@app/lib/cn";
|
||||||
import {
|
import {
|
||||||
derivePublicAuthState,
|
derivePublicAuthState,
|
||||||
@@ -259,10 +262,11 @@ function LauncherResourceSitesSection({
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<Table>
|
<div className="min-w-0 overflow-x-auto overflow-y-hidden">
|
||||||
|
<Table sticky>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>
|
<TableHead className="whitespace-nowrap">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="h-8 px-3"
|
className="h-8 px-3"
|
||||||
@@ -272,7 +276,7 @@ function LauncherResourceSitesSection({
|
|||||||
<ChevronsUpDown className="ml-2 size-4" />
|
<ChevronsUpDown className="ml-2 size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead>
|
<TableHead className="whitespace-nowrap">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="h-8 px-3"
|
className="h-8 px-3"
|
||||||
@@ -282,19 +286,35 @@ function LauncherResourceSitesSection({
|
|||||||
<ChevronsUpDown className="ml-2 size-4" />
|
<ChevronsUpDown className="ml-2 size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="w-12">
|
<TableHead
|
||||||
<span className="sr-only">{t("actions")}</span>
|
className={cn(
|
||||||
|
"whitespace-nowrap text-right",
|
||||||
|
"sticky right-0 z-10 w-auto min-w-fit bg-card",
|
||||||
|
"[mask-image:linear-gradient(to_right,transparent_0%,black_20px)]"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="sr-only">
|
||||||
|
{t("actions")}
|
||||||
|
</span>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{sortedSites.map((site) => (
|
{sortedSites.map((site) => (
|
||||||
<TableRow key={site.siteId}>
|
<TableRow key={site.siteId}>
|
||||||
<TableCell>{site.name}</TableCell>
|
<TableCell className="whitespace-nowrap">
|
||||||
<TableCell>
|
{site.name}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="whitespace-nowrap">
|
||||||
<SiteStatusCell site={site} />
|
<SiteStatusCell site={site} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell
|
||||||
|
className={cn(
|
||||||
|
"whitespace-nowrap text-right",
|
||||||
|
"sticky right-0 z-10 w-auto min-w-fit bg-card",
|
||||||
|
"[mask-image:linear-gradient(to_right,transparent_0%,black_20px)]"
|
||||||
|
)}
|
||||||
|
>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@@ -335,6 +355,67 @@ function LauncherResourceSitesSection({
|
|||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
|
</div>
|
||||||
|
</SettingsSectionBody>
|
||||||
|
</SettingsSection>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LauncherPrivateSshCommandSection({
|
||||||
|
alias,
|
||||||
|
niceId,
|
||||||
|
pamMode
|
||||||
|
}: {
|
||||||
|
alias?: string | null;
|
||||||
|
niceId: string;
|
||||||
|
pamMode?: string | null;
|
||||||
|
}) {
|
||||||
|
const t = useTranslations();
|
||||||
|
const trimmedAlias = alias?.trim() || null;
|
||||||
|
const hasDistinctAlias = Boolean(trimmedAlias && trimmedAlias !== niceId);
|
||||||
|
const useUserPrefix = pamMode !== "push";
|
||||||
|
|
||||||
|
function formatSshTarget(target: string) {
|
||||||
|
return useUserPrefix ? `user@${target}` : target;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blocks: AiConfigBlock[] = hasDistinctAlias
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id: "alias",
|
||||||
|
label: t("editInternalResourceDialogAlias"),
|
||||||
|
displayText: `pangolin ssh ${formatSshTarget(trimmedAlias!)}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "niceId",
|
||||||
|
label: t("identifier"),
|
||||||
|
displayText: `pangolin ssh ${formatSshTarget(niceId)}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
id: "niceId",
|
||||||
|
label: t("identifier"),
|
||||||
|
displayText: `pangolin ssh ${formatSshTarget(niceId)}`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsSection>
|
||||||
|
<SettingsSectionHeader>
|
||||||
|
<SettingsSectionTitle>
|
||||||
|
{t("resourceLauncherSshCommand")}
|
||||||
|
</SettingsSectionTitle>
|
||||||
|
<SettingsSectionDescription>
|
||||||
|
{t("resourceLauncherSshCommandDescription")}
|
||||||
|
</SettingsSectionDescription>
|
||||||
|
</SettingsSectionHeader>
|
||||||
|
<SettingsSectionBody>
|
||||||
|
{hasDistinctAlias ? (
|
||||||
|
<AiConfigBlocks blocks={blocks} relation="options" />
|
||||||
|
) : (
|
||||||
|
<CopyTextBox text={blocks[0].displayText} />
|
||||||
|
)}
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
);
|
);
|
||||||
@@ -572,6 +653,7 @@ function PrivateResourceDetails({
|
|||||||
}) {
|
}) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const isInference = resource.mode === "inference";
|
const isInference = resource.mode === "inference";
|
||||||
|
const isSsh = resource.mode === "ssh";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -624,6 +706,13 @@ function PrivateResourceDetails({
|
|||||||
isAdmin={isAdmin}
|
isAdmin={isAdmin}
|
||||||
onFilterBySite={onFilterBySite}
|
onFilterBySite={onFilterBySite}
|
||||||
/>
|
/>
|
||||||
|
{isSsh ? (
|
||||||
|
<LauncherPrivateSshCommandSection
|
||||||
|
alias={resource.alias}
|
||||||
|
niceId={resource.niceId}
|
||||||
|
pamMode={resource.pamMode}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
{isInference ? (
|
{isInference ? (
|
||||||
<>
|
<>
|
||||||
<LauncherInferenceModelsSection
|
<LauncherInferenceModelsSection
|
||||||
|
|||||||
Reference in New Issue
Block a user