mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-07 08:08:55 +00:00
Add login formatting
This commit is contained in:
@@ -15,8 +15,14 @@ import type {
|
|||||||
FileInfo
|
FileInfo
|
||||||
} from "@devolutions/iron-remote-desktop-rdp/dist";
|
} from "@devolutions/iron-remote-desktop-rdp/dist";
|
||||||
import { GetBrowserTargetResponse } from "@server/routers/resource";
|
import { GetBrowserTargetResponse } from "@server/routers/resource";
|
||||||
import { Card, CardContent } from "@app/components/ui/card";
|
import {
|
||||||
import LoginCardHeader from "@app/components/LoginCardHeader";
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription
|
||||||
|
} from "@app/components/ui/card";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
declare module "react" {
|
declare module "react" {
|
||||||
namespace JSX {
|
namespace JSX {
|
||||||
@@ -309,51 +315,87 @@ export default function RdpClient({
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Card className="w-full">
|
<div>
|
||||||
<LoginCardHeader subtitle="RDP" />
|
<div className="text-center mb-2">
|
||||||
<CardContent className="pt-6">
|
<span className="text-sm text-muted-foreground">
|
||||||
<p className="text-destructive text-sm">{error}</p>
|
Powered by{" "}
|
||||||
</CardContent>
|
<Link
|
||||||
</Card>
|
href="https://pangolin.net/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
Pangolin
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Card className="w-full">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>RDP</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-destructive text-sm">{error}</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{showLogin && (
|
{showLogin && (
|
||||||
<Card className="w-full">
|
<div>
|
||||||
<LoginCardHeader subtitle="Connect via RDP" />
|
<div className="text-center mb-2">
|
||||||
<CardContent className="pt-6">
|
<span className="text-sm text-muted-foreground">
|
||||||
<div className="space-y-4">
|
Powered by{" "}
|
||||||
<Field label="Domain" id="domain">
|
<Link
|
||||||
<Input
|
href="https://pangolin.net/"
|
||||||
id="domain"
|
target="_blank"
|
||||||
value={form.domain}
|
rel="noopener noreferrer"
|
||||||
onChange={(e) =>
|
className="underline"
|
||||||
update("domain", e.target.value)
|
>
|
||||||
}
|
Pangolin
|
||||||
/>
|
</Link>
|
||||||
</Field>
|
</span>
|
||||||
<Field label="Username" id="username">
|
</div>
|
||||||
<Input
|
<Card className="w-full">
|
||||||
id="username"
|
<CardHeader>
|
||||||
value={form.username}
|
<CardTitle>Sign in to Remote Desktop</CardTitle>
|
||||||
onChange={(e) =>
|
<CardDescription>
|
||||||
update("username", e.target.value)
|
Enter Windows credentials to access xxxx
|
||||||
}
|
</CardDescription>
|
||||||
/>
|
</CardHeader>
|
||||||
</Field>
|
<CardContent>
|
||||||
<Field label="Password" id="password">
|
<div className="space-y-4">
|
||||||
<Input
|
<Field label="Domain" id="domain">
|
||||||
id="password"
|
<Input
|
||||||
type="password"
|
id="domain"
|
||||||
value={form.password}
|
value={form.domain}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
update("password", e.target.value)
|
update("domain", e.target.value)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
{/*
|
<Field label="Username" id="username">
|
||||||
|
<Input
|
||||||
|
id="username"
|
||||||
|
value={form.username}
|
||||||
|
onChange={(e) =>
|
||||||
|
update("username", e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<Field label="Password" id="password">
|
||||||
|
<Input
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
value={form.password}
|
||||||
|
onChange={(e) =>
|
||||||
|
update("password", e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
{/*
|
||||||
<Field label="Pre Connection Blob (optional)" id="pcb">
|
<Field label="Pre Connection Blob (optional)" id="pcb">
|
||||||
<Input
|
<Input
|
||||||
id="pcb"
|
id="pcb"
|
||||||
@@ -362,7 +404,7 @@ export default function RdpClient({
|
|||||||
/>
|
/>
|
||||||
</Field> */}
|
</Field> */}
|
||||||
|
|
||||||
{/* <Field
|
{/* <Field
|
||||||
label="KDC Proxy URL (optional)"
|
label="KDC Proxy URL (optional)"
|
||||||
id="kdcProxyUrl"
|
id="kdcProxyUrl"
|
||||||
>
|
>
|
||||||
@@ -374,7 +416,7 @@ export default function RdpClient({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Field> */}
|
</Field> */}
|
||||||
{/* <div className="flex items-center gap-2">
|
{/* <div className="flex items-center gap-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="enable_clipboard"
|
id="enable_clipboard"
|
||||||
checked={form.enableClipboard}
|
checked={form.enableClipboard}
|
||||||
@@ -386,17 +428,20 @@ export default function RdpClient({
|
|||||||
Enable Clipboard
|
Enable Clipboard
|
||||||
</Label>
|
</Label>
|
||||||
</div> */}
|
</div> */}
|
||||||
<Button
|
<Button
|
||||||
onClick={startSession}
|
onClick={startSession}
|
||||||
disabled={!moduleReady}
|
disabled={!moduleReady}
|
||||||
loading={connecting}
|
loading={connecting}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
{moduleReady ? "Connect" : "Loading module..."}
|
{moduleReady
|
||||||
</Button>
|
? "Connect"
|
||||||
</div>
|
: "Loading module..."}
|
||||||
</CardContent>
|
</Button>
|
||||||
</Card>
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -8,8 +8,14 @@ import { Label } from "@/components/ui/label";
|
|||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import type { SignSshKeyResponse } from "@server/private/routers/ssh";
|
import type { SignSshKeyResponse } from "@server/private/routers/ssh";
|
||||||
import { GetBrowserTargetResponse } from "@server/routers/resource";
|
import { GetBrowserTargetResponse } from "@server/routers/resource";
|
||||||
import { Card, CardContent } from "@app/components/ui/card";
|
import {
|
||||||
import LoginCardHeader from "@app/components/LoginCardHeader";
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription
|
||||||
|
} from "@app/components/ui/card";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
type FormState = {
|
type FormState = {
|
||||||
username: string;
|
username: string;
|
||||||
@@ -300,126 +306,163 @@ export default function SshClient({
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Card className="w-full">
|
<div>
|
||||||
<LoginCardHeader subtitle="SSH" />
|
<div className="text-center mb-2">
|
||||||
<CardContent className="pt-6">
|
<span className="text-sm text-muted-foreground">
|
||||||
<p className="text-destructive text-sm">{error}</p>
|
Powered by{" "}
|
||||||
</CardContent>
|
<Link
|
||||||
</Card>
|
href="https://pangolin.net/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
Pangolin
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Card className="w-full">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>SSH</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-destructive text-sm">{error}</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!connected && (
|
{!connected && (
|
||||||
<Card className="w-full">
|
<div>
|
||||||
<LoginCardHeader subtitle="Connect via SSH" />
|
<div className="text-center mb-2">
|
||||||
<CardContent className="pt-6">
|
<span className="text-sm text-muted-foreground">
|
||||||
<div className="space-y-4">
|
Powered by{" "}
|
||||||
<Field label="Username" id="username">
|
<Link
|
||||||
<Input
|
href="https://pangolin.net/"
|
||||||
id="username"
|
target="_blank"
|
||||||
value={form.username}
|
rel="noopener noreferrer"
|
||||||
onChange={(e) =>
|
className="underline"
|
||||||
setForm({
|
|
||||||
...form,
|
|
||||||
username: e.target.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="root"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<Field label="Password" id="password">
|
|
||||||
<Input
|
|
||||||
id="password"
|
|
||||||
type="password"
|
|
||||||
value={form.password}
|
|
||||||
onChange={(e) =>
|
|
||||||
setForm({
|
|
||||||
...form,
|
|
||||||
password: e.target.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder={
|
|
||||||
form.privateKey
|
|
||||||
? "Optional with key auth"
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field
|
|
||||||
label="Private Key (optional)"
|
|
||||||
id="privateKey"
|
|
||||||
>
|
>
|
||||||
<Textarea
|
Pangolin
|
||||||
id="privateKey"
|
</Link>
|
||||||
value={form.privateKey}
|
</span>
|
||||||
onChange={(e) =>
|
</div>
|
||||||
setForm({
|
<Card className="w-full">
|
||||||
...form,
|
<CardHeader>
|
||||||
privateKey: e.target.value
|
<CardTitle>Sign in to SSH</CardTitle>
|
||||||
})
|
<CardDescription>
|
||||||
}
|
Enter your credentials to access xxxx
|
||||||
placeholder="Paste your private key here (PEM format)…"
|
</CardDescription>
|
||||||
rows={5}
|
</CardHeader>
|
||||||
className="font-mono text-xs"
|
<CardContent>
|
||||||
/>
|
<div className="space-y-4">
|
||||||
<div className="mt-1.5 flex items-center gap-2">
|
<Field label="Username" id="username">
|
||||||
<Button
|
<Input
|
||||||
type="button"
|
id="username"
|
||||||
variant="outline"
|
value={form.username}
|
||||||
size="sm"
|
onChange={(e) =>
|
||||||
onClick={() =>
|
setForm({
|
||||||
fileInputRef.current?.click()
|
...form,
|
||||||
|
username: e.target.value
|
||||||
|
})
|
||||||
}
|
}
|
||||||
>
|
placeholder="root"
|
||||||
Upload key file
|
/>
|
||||||
</Button>
|
</Field>
|
||||||
{form.privateKey && (
|
<Field label="Password" id="password">
|
||||||
<button
|
<Input
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
value={form.password}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({
|
||||||
|
...form,
|
||||||
|
password: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder={
|
||||||
|
form.privateKey
|
||||||
|
? "Optional with key auth"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
label="Private Key (optional)"
|
||||||
|
id="privateKey"
|
||||||
|
>
|
||||||
|
<Textarea
|
||||||
|
id="privateKey"
|
||||||
|
value={form.privateKey}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({
|
||||||
|
...form,
|
||||||
|
privateKey: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="Paste your private key here (PEM format)…"
|
||||||
|
rows={5}
|
||||||
|
className="font-mono text-xs"
|
||||||
|
/>
|
||||||
|
<div className="mt-1.5 flex items-center gap-2">
|
||||||
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
className="text-xs text-muted-foreground underline"
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setForm((prev) => ({
|
fileInputRef.current?.click()
|
||||||
...prev,
|
|
||||||
privateKey: ""
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Clear
|
Upload key file
|
||||||
</button>
|
</Button>
|
||||||
)}
|
{form.privateKey && (
|
||||||
</div>
|
<button
|
||||||
<input
|
type="button"
|
||||||
ref={fileInputRef}
|
className="text-xs text-muted-foreground underline"
|
||||||
type="file"
|
onClick={() =>
|
||||||
className="hidden"
|
setForm((prev) => ({
|
||||||
accept=".pem,.key,.pub,*"
|
...prev,
|
||||||
onChange={handleKeyFile}
|
privateKey: ""
|
||||||
/>
|
}))
|
||||||
</Field>
|
}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
type="file"
|
||||||
|
className="hidden"
|
||||||
|
accept=".pem,.key,.pub,*"
|
||||||
|
onChange={handleKeyFile}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
{connectError && (
|
{connectError && (
|
||||||
<p className="text-destructive text-sm">
|
<p className="text-destructive text-sm">
|
||||||
{connectError}
|
{connectError}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => connect()}
|
onClick={() => connect()}
|
||||||
loading={connecting}
|
loading={connecting}
|
||||||
disabled={
|
disabled={
|
||||||
!form.username ||
|
!form.username ||
|
||||||
(!form.password && !form.privateKey)
|
(!form.password && !form.privateKey)
|
||||||
}
|
}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
{connecting ? "Connecting..." : "Connect"}
|
{connecting ? "Connecting..." : "Connect"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{connected && (
|
{connected && (
|
||||||
|
|||||||
@@ -6,8 +6,14 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { toast } from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
import { GetBrowserTargetResponse } from "@server/routers/resource";
|
import { GetBrowserTargetResponse } from "@server/routers/resource";
|
||||||
import { Card, CardContent } from "@app/components/ui/card";
|
import {
|
||||||
import LoginCardHeader from "@app/components/LoginCardHeader";
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription
|
||||||
|
} from "@app/components/ui/card";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
type FormState = {
|
type FormState = {
|
||||||
password: string;
|
password: string;
|
||||||
@@ -148,39 +154,79 @@ export default function VncClient({
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Card className="w-full">
|
<div>
|
||||||
<LoginCardHeader subtitle="VNC" />
|
<div className="text-center mb-2">
|
||||||
<CardContent className="pt-6">
|
<span className="text-sm text-muted-foreground">
|
||||||
<p className="text-destructive text-sm">{error}</p>
|
Powered by{" "}
|
||||||
</CardContent>
|
<Link
|
||||||
</Card>
|
href="https://pangolin.net/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
Pangolin
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Card className="w-full">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>VNC</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-destructive text-sm">{error}</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!connected && (
|
{!connected && (
|
||||||
<Card className="w-full">
|
<div>
|
||||||
<LoginCardHeader subtitle="Connect via VNC" />
|
<div className="text-center mb-2">
|
||||||
<CardContent className="pt-6">
|
<span className="text-sm text-muted-foreground">
|
||||||
<div className="space-y-4">
|
Powered by{" "}
|
||||||
<Field label="Password (optional)" id="password">
|
<Link
|
||||||
<Input
|
href="https://pangolin.net/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
Pangolin
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Card className="w-full">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>VNC</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Enter your credentials to connect
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<Field
|
||||||
|
label="Password (optional)"
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
>
|
||||||
value={form.password}
|
<Input
|
||||||
onChange={(e) =>
|
id="password"
|
||||||
update("password", e.target.value)
|
type="password"
|
||||||
}
|
value={form.password}
|
||||||
/>
|
onChange={(e) =>
|
||||||
</Field>
|
update("password", e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Button onClick={connect} className="w-full">
|
<Button onClick={connect} className="w-full">
|
||||||
Connect
|
Connect
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user