use resource name in ssh/rdp/vnc page meta

This commit is contained in:
miloschwartz
2026-06-04 16:45:22 -07:00
parent 9d3f96cf83
commit 889f78ddb8
7 changed files with 92 additions and 94 deletions

View File

@@ -58,6 +58,7 @@ export async function getBrowserTarget(
authToken: browserGatewayTarget.authToken,
resourceId: resources.resourceId,
niceId: resources.niceId,
name: resources.name,
orgId: resources.orgId,
pamMode: resources.pamMode,
authDaemonMode: resources.authDaemonMode
@@ -93,7 +94,8 @@ export async function getBrowserTarget(
authDaemonMode: browserTarget.authDaemonMode,
orgId: browserTarget.orgId,
resourceId: browserTarget.resourceId,
niceId: browserTarget.niceId
niceId: browserTarget.niceId,
name: browserTarget.name
},
success: true,
error: false,

View File

@@ -5,6 +5,7 @@ export type GetBrowserTargetResponse = {
orgId: string;
resourceId: number;
niceId: string;
name: string;
pamMode: "passthrough" | "push" | null;
authDaemonMode: "site" | "remote" | "native" | null;
};

View File

@@ -1,34 +1,17 @@
import { headers } from "next/headers";
import { priv } from "@app/lib/api";
import { AxiosResponse } from "axios";
import { GetBrowserTargetResponse } from "@server/routers/browserGatewayTarget";
import { generateBrowserGatewayMetadata } from "@app/lib/browserGatewayMetadata";
import { getBrowserTargetForRequest } from "@app/lib/getBrowserTargetForRequest";
import RdpClient from "./RdpClient";
import AuthFooter from "@app/components/AuthFooter";
export const dynamic = "force-dynamic";
export const metadata = {
title: "RDP"
};
export async function generateMetadata() {
return generateBrowserGatewayMetadata("RDP");
}
export default async function RdpPage() {
const headersList = await headers();
const host = headersList.get("host") || "";
const hostname = host.split(":")[0];
let target: GetBrowserTargetResponse | null = null;
const error: string | null = null;
try {
const res = await priv.get<AxiosResponse<GetBrowserTargetResponse>>(
`/resource/browser-target?fullDomain=${encodeURIComponent(hostname)}`
);
target = res.data.data;
console.log("Fetched browser target:", target);
} catch (error) {
console.error("Error fetching browser target:", error);
error = "No resource found for this domain";
}
const { target } = await getBrowserTargetForRequest();
const error = target ? null : "No resource found for this domain";
return (
<div className="h-full flex flex-col">

View File

@@ -1,5 +1,7 @@
import { headers } from "next/headers";
import { priv } from "@app/lib/api";
import { generateBrowserGatewayMetadata } from "@app/lib/browserGatewayMetadata";
import { getBrowserTargetForRequest } from "@app/lib/getBrowserTargetForRequest";
import { AxiosResponse } from "axios";
import { GetBrowserTargetResponse } from "@server/routers/browserGatewayTarget";
import SshClient from "./SshClient";
@@ -99,14 +101,12 @@ function generateEphemeralKeyPair(): {
export const dynamic = "force-dynamic";
export const metadata = {
title: "SSH"
};
export async function generateMetadata() {
return generateBrowserGatewayMetadata("SSH");
}
export default async function SshPage() {
const headersList = await headers();
const host = headersList.get("host") || "";
const hostname = host.split(":")[0];
const cookieHeader = headersList.get("cookie") || "";
let target: GetBrowserTargetResponse | null = null;
@@ -114,49 +114,44 @@ export default async function SshPage() {
let privateKey: string | null = null;
let error: string | null = null;
try {
const res = await priv.get<AxiosResponse<GetBrowserTargetResponse>>(
`/resource/browser-target?fullDomain=${encodeURIComponent(hostname)}`
);
target = res.data.data;
const { target: browserTarget } = await getBrowserTargetForRequest();
target = browserTarget;
if (target.pamMode === "push") {
try {
const { privateKeyPem, publicKeyOpenSSH } =
generateEphemeralKeyPair();
privateKey = privateKeyPem;
const res = await priv.post<AxiosResponse<SignSshKeyResponse>>(
`/org/${target.orgId}/ssh/sign-key`,
{
publicKey: publicKeyOpenSSH,
resourceId: target.resourceId,
type: "public"
},
{
headers: {
Cookie: cookieHeader
}
}
);
signedKeyData = res.data.data;
const messageIds =
signedKeyData.messageIds.length > 0
? signedKeyData.messageIds
: signedKeyData.messageId
? [signedKeyData.messageId]
: [];
await waitForRoundTripCompletion(messageIds, cookieHeader);
} catch (err) {
console.error("Error signing SSH key:", err);
error =
"Failed to sign SSH key for PAM push authentication. Did you sign in as a user?";
}
}
} catch (err) {
console.error("Error fetching browser target:", err);
if (!target) {
error = "No resource found for this domain";
} else if (target.pamMode === "push") {
try {
const { privateKeyPem, publicKeyOpenSSH } =
generateEphemeralKeyPair();
privateKey = privateKeyPem;
const res = await priv.post<AxiosResponse<SignSshKeyResponse>>(
`/org/${target.orgId}/ssh/sign-key`,
{
publicKey: publicKeyOpenSSH,
resourceId: target.resourceId,
type: "public"
},
{
headers: {
Cookie: cookieHeader
}
}
);
signedKeyData = res.data.data;
const messageIds =
signedKeyData.messageIds.length > 0
? signedKeyData.messageIds
: signedKeyData.messageId
? [signedKeyData.messageId]
: [];
await waitForRoundTripCompletion(messageIds, cookieHeader);
} catch (err) {
console.error("Error signing SSH key:", err);
error =
"Failed to sign SSH key for PAM push authentication. Did you sign in as a user?";
}
}
return (

View File

@@ -1,33 +1,17 @@
import { headers } from "next/headers";
import { priv } from "@app/lib/api";
import { AxiosResponse } from "axios";
import { GetBrowserTargetResponse } from "@server/routers/browserGatewayTarget";
import { generateBrowserGatewayMetadata } from "@app/lib/browserGatewayMetadata";
import { getBrowserTargetForRequest } from "@app/lib/getBrowserTargetForRequest";
import VncClient from "./VncClient";
import AuthFooter from "@app/components/AuthFooter";
export const dynamic = "force-dynamic";
export const metadata = {
title: "VNC"
};
export async function generateMetadata() {
return generateBrowserGatewayMetadata("VNC");
}
export default async function VncPage() {
const headersList = await headers();
const host = headersList.get("host") || "";
const hostname = host.split(":")[0];
let target: GetBrowserTargetResponse | null = null;
const error: string | null = null;
try {
const res = await priv.get<AxiosResponse<GetBrowserTargetResponse>>(
`/resource/browser-target?fullDomain=${encodeURIComponent(hostname)}`
);
target = res.data.data;
} catch (error) {
console.error("Error fetching browser target:", error);
error = "No resource found for this domain";
}
const { target } = await getBrowserTargetForRequest();
const error = target ? null : "No resource found for this domain";
return (
<div className="h-full flex flex-col">

View File

@@ -0,0 +1,13 @@
import { getBrowserTargetForRequest } from "@app/lib/getBrowserTargetForRequest";
import type { Metadata } from "next";
export async function generateBrowserGatewayMetadata(
protocol: "SSH" | "RDP" | "VNC"
): Promise<Metadata> {
const { target } = await getBrowserTargetForRequest();
return {
title: target?.name
? `${protocol} - ${target.name}`
: `${protocol} - Pangolin`
};
}

View File

@@ -0,0 +1,20 @@
import { priv } from "@app/lib/api";
import { GetBrowserTargetResponse } from "@server/routers/browserGatewayTarget";
import { AxiosResponse } from "axios";
import { headers } from "next/headers";
import { cache } from "react";
export const getBrowserTargetForRequest = cache(async () => {
const headersList = await headers();
const host = headersList.get("host") || "";
const hostname = host.split(":")[0];
try {
const res = await priv.get<AxiosResponse<GetBrowserTargetResponse>>(
`/resource/browser-target?fullDomain=${encodeURIComponent(hostname)}`
);
return { target: res.data.data };
} catch {
return { target: null };
}
});