mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-16 05:13:36 +00:00
feat: continue migrating content for ui redesign
This commit is contained in:
+1
-1
@@ -50,11 +50,11 @@ function sshHostToHost(h: SSHHostWithStatus): Host {
|
||||
notes: h.notes,
|
||||
pin: h.pin ?? false,
|
||||
macAddress: h.macAddress,
|
||||
enableSsh: h.enableSsh ?? (h.connectionType === "ssh" || !h.connectionType),
|
||||
enableTerminal: h.enableTerminal ?? true,
|
||||
enableTunnel: h.enableTunnel ?? false,
|
||||
enableFileManager: h.enableFileManager ?? false,
|
||||
enableDocker: h.enableDocker ?? false,
|
||||
enableSsh: h.connectionType === "ssh" || !h.connectionType,
|
||||
enableRdp: h.connectionType === "rdp",
|
||||
enableVnc: h.connectionType === "vnc",
|
||||
enableTelnet: h.connectionType === "telnet",
|
||||
|
||||
@@ -59,17 +59,20 @@ export function SettingRow({
|
||||
|
||||
export function FakeSwitch({
|
||||
defaultChecked = false,
|
||||
checked,
|
||||
onChange,
|
||||
}: {
|
||||
defaultChecked?: boolean;
|
||||
checked?: boolean;
|
||||
onChange?: (v: boolean) => void;
|
||||
}) {
|
||||
const [on, setOn] = useState(defaultChecked);
|
||||
const [internalOn, setInternalOn] = useState(defaultChecked);
|
||||
const on = checked !== undefined ? checked : internalOn;
|
||||
return (
|
||||
<button
|
||||
onClick={() => {
|
||||
const next = !on;
|
||||
setOn(next);
|
||||
if (checked === undefined) setInternalOn(next);
|
||||
onChange?.(next);
|
||||
}}
|
||||
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center border-2 transition-colors ${on ? "bg-accent-brand border-accent-brand" : "bg-muted border-border"}`}
|
||||
|
||||
@@ -38,7 +38,6 @@ import {
|
||||
Search,
|
||||
Grid3X3,
|
||||
List,
|
||||
ArrowUpDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
ArrowUp,
|
||||
@@ -48,8 +47,6 @@ import {
|
||||
Copy,
|
||||
Layout,
|
||||
} from "lucide-react";
|
||||
import { Card } from "@/components/card.tsx";
|
||||
import { Separator } from "@/components/separator.tsx";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -2370,7 +2367,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
visibility: isConnectionLogExpanded ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<Card className="flex flex-col shrink-0 mx-3 mt-3 rounded-none shadow-none border-border">
|
||||
<div className="flex flex-col shrink-0 mx-3 mt-3 border border-border bg-card">
|
||||
<div className="flex flex-row items-center justify-between px-3 py-2 gap-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<Button
|
||||
@@ -2640,7 +2637,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="flex-1 flex px-3 pb-3 pt-2 gap-3 min-h-0 relative"
|
||||
@@ -2664,7 +2661,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
: "hidden md:flex",
|
||||
)}
|
||||
>
|
||||
<Card className="flex-1 flex flex-col rounded-none shadow-none p-0 gap-0 overflow-hidden border-border">
|
||||
<div className="flex-1 flex flex-col overflow-hidden border border-border bg-card">
|
||||
<FileManagerSidebar
|
||||
currentHost={currentHost}
|
||||
currentPath={currentPath}
|
||||
@@ -2675,10 +2672,10 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
refreshTrigger={sidebarRefreshTrigger}
|
||||
diskInfo={diskInfo ?? undefined}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card className="flex-1 relative overflow-hidden rounded-none shadow-none p-0 gap-0 min-h-0 flex flex-col border-border">
|
||||
<div className="flex-1 relative overflow-hidden min-h-0 flex flex-col border border-border bg-card">
|
||||
<div className="flex-1 relative min-h-0 h-full">
|
||||
<FileManagerGrid
|
||||
files={filteredFiles}
|
||||
@@ -2767,7 +2764,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
onCopyPath={handleCopyPath}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@ export function FileManagerSidebar({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-full flex flex-col bg-card border-r border-border overflow-hidden">
|
||||
<div className="h-full flex flex-col bg-card overflow-hidden">
|
||||
<div className="flex-1 overflow-y-auto thin-scrollbar">
|
||||
{/* ── Recent files ──────────────────────────────────────── */}
|
||||
{renderSection(t("fileManager.recent"), recentItems, (item) =>
|
||||
|
||||
+1
-1
@@ -113,7 +113,7 @@
|
||||
--accent: oklch(0.255 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 12%);
|
||||
--border: oklch(1 0 0 / 10.8%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.87 0 0);
|
||||
|
||||
+35
-1
@@ -3364,7 +3364,41 @@
|
||||
"apiKeyActive": "Active",
|
||||
"apiKeyUsageHint": "Include your key in the",
|
||||
"apiKeyUsageHintHeader": "header.",
|
||||
"apiKeyPermissionsHint": "Keys inherit the permissions of the creating user."
|
||||
"apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.",
|
||||
"roleUser": "User",
|
||||
"authMethodDual": "Dual Auth",
|
||||
"authMethodOidc": "OIDC",
|
||||
"totpSetupFailed": "Failed to start TOTP setup",
|
||||
"totpEnter6Digits": "Enter a 6-digit code",
|
||||
"totpEnabledSuccess": "Two-factor authentication enabled",
|
||||
"totpInvalidCode": "Invalid code, please try again",
|
||||
"totpDisableInputRequired": "Enter your TOTP code or password",
|
||||
"totpDisabledSuccess": "Two-factor authentication disabled",
|
||||
"totpDisableFailed": "Failed to disable 2FA",
|
||||
"totpDisableTitle": "Disable 2FA",
|
||||
"totpDisablePlaceholder": "Enter TOTP code or password",
|
||||
"totpDisableConfirm": "Disable 2FA",
|
||||
"totpContinueVerify": "Continue to Verify",
|
||||
"totpVerifyTitle": "Verify Code",
|
||||
"totpBackupTitle": "Backup Codes",
|
||||
"totpDownloadBackup": "Download Backup Codes",
|
||||
"done": "Done",
|
||||
"secretCopied": "Secret copied to clipboard",
|
||||
"apiKeyNameRequired": "Key name is required",
|
||||
"apiKeyCreated": "API key \"{{name}}\" created",
|
||||
"apiKeyCreateFailed": "Failed to create API key",
|
||||
"apiKeyUser": "User",
|
||||
"apiKeyExpires": "Expires",
|
||||
"apiKeyRevoked": "API key \"{{name}}\" revoked",
|
||||
"apiKeyRevokeFailed": "Failed to revoke API key",
|
||||
"passwordFieldsRequired": "Current and new passwords are required",
|
||||
"passwordMismatch": "Passwords do not match",
|
||||
"passwordTooShort": "Password must be at least 6 characters",
|
||||
"passwordUpdated": "Password updated successfully",
|
||||
"passwordUpdateFailed": "Failed to update password",
|
||||
"deletePasswordRequired": "Password is required to delete your account",
|
||||
"deleteFailed": "Failed to delete account",
|
||||
"deleting": "Deleting..."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+134
-45
@@ -3,7 +3,6 @@ import { cn } from "@/lib/utils";
|
||||
import { Kbd } from "@/components/kbd";
|
||||
import {
|
||||
Command,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandGroup,
|
||||
@@ -28,6 +27,8 @@ import {
|
||||
User,
|
||||
KeyRound,
|
||||
LayoutDashboard,
|
||||
Monitor,
|
||||
Clock,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/button";
|
||||
import {
|
||||
@@ -36,21 +37,8 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/dropdown-menu";
|
||||
|
||||
type Host = {
|
||||
name: string;
|
||||
user: string;
|
||||
address: string;
|
||||
online: boolean;
|
||||
cpu: number;
|
||||
ram: number;
|
||||
lastAccess: string;
|
||||
tags?: string[];
|
||||
enableTerminal?: boolean;
|
||||
enableFileManager?: boolean;
|
||||
enableDocker?: boolean;
|
||||
enableTunnel?: boolean;
|
||||
};
|
||||
import { getRecentActivity, type RecentActivityItem } from "@/main-axios";
|
||||
import type { Host } from "@/types/ui-types";
|
||||
|
||||
interface CommandPaletteProps {
|
||||
isOpen: boolean;
|
||||
@@ -59,20 +47,26 @@ interface CommandPaletteProps {
|
||||
onOpenTab: (type: any, label?: string, pendingEvent?: string) => void;
|
||||
}
|
||||
|
||||
const ACTION_ICONS: Record<string, React.ReactNode> = {
|
||||
Terminal: <Terminal className="size-3" />,
|
||||
Files: <FolderOpen className="size-3" />,
|
||||
Docker: <Box className="size-3" />,
|
||||
Stats: <Activity className="size-3" />,
|
||||
Tunnels: <Network className="size-3" />,
|
||||
const ACTIVITY_ICONS: Record<string, React.ReactNode> = {
|
||||
terminal: <Terminal className="size-3.5" />,
|
||||
file_manager: <FolderOpen className="size-3.5" />,
|
||||
server_stats: <Activity className="size-3.5" />,
|
||||
tunnel: <Network className="size-3.5" />,
|
||||
docker: <Box className="size-3.5" />,
|
||||
telnet: <Terminal className="size-3.5" />,
|
||||
vnc: <Monitor className="size-3.5" />,
|
||||
rdp: <Monitor className="size-3.5" />,
|
||||
};
|
||||
|
||||
const ACTION_TAB_TYPE: Record<string, string> = {
|
||||
Terminal: "terminal",
|
||||
Files: "files",
|
||||
Docker: "docker",
|
||||
Stats: "stats",
|
||||
Tunnels: "tunnel",
|
||||
const ACTIVITY_TAB_TYPE: Record<string, string> = {
|
||||
terminal: "terminal",
|
||||
file_manager: "files",
|
||||
server_stats: "stats",
|
||||
tunnel: "tunnel",
|
||||
docker: "docker",
|
||||
telnet: "telnet",
|
||||
vnc: "vnc",
|
||||
rdp: "rdp",
|
||||
};
|
||||
|
||||
export function CommandPalette({
|
||||
@@ -83,11 +77,17 @@ export function CommandPalette({
|
||||
}: CommandPaletteProps) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [search, setSearch] = useState("");
|
||||
const [recentActivity, setRecentActivity] = useState<RecentActivityItem[]>(
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setTimeout(() => inputRef.current?.focus(), 50);
|
||||
setSearch("");
|
||||
getRecentActivity(5)
|
||||
.then(setRecentActivity)
|
||||
.catch(() => {});
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
@@ -104,7 +104,8 @@ export function CommandPalette({
|
||||
const filteredHosts = hosts.filter(
|
||||
(h) =>
|
||||
h.name.toLowerCase().includes(search.toLowerCase()) ||
|
||||
h.ip.toLowerCase().includes(search.toLowerCase()),
|
||||
h.ip.toLowerCase().includes(search.toLowerCase()) ||
|
||||
h.username.toLowerCase().includes(search.toLowerCase()),
|
||||
);
|
||||
|
||||
const handleAction = (action: () => void) => {
|
||||
@@ -238,18 +239,101 @@ export function CommandPalette({
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
|
||||
{recentActivity.length > 0 && (
|
||||
<>
|
||||
<CommandSeparator className="my-2" />
|
||||
<CommandGroup heading="Recent Activity" className="px-2">
|
||||
{recentActivity.map((item) => (
|
||||
<CommandItem
|
||||
key={item.id}
|
||||
onSelect={() =>
|
||||
handleAction(() =>
|
||||
onOpenTab(
|
||||
ACTIVITY_TAB_TYPE[item.type] as any,
|
||||
item.hostName,
|
||||
),
|
||||
)
|
||||
}
|
||||
className="group flex items-center gap-3 px-3 py-2 rounded-none hover:bg-accent-brand/10 cursor-pointer"
|
||||
>
|
||||
<div className="size-7 rounded-none bg-muted flex items-center justify-center group-hover:bg-accent-brand/20 transition-colors text-muted-foreground group-hover:text-accent-brand">
|
||||
{ACTIVITY_ICONS[item.type]}
|
||||
</div>
|
||||
<div className="flex flex-col flex-1 min-w-0">
|
||||
<span className="text-sm font-semibold truncate">
|
||||
{item.hostName}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground capitalize">
|
||||
{item.type.replace("_", " ")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 text-muted-foreground/50">
|
||||
<Clock className="size-3" />
|
||||
<span className="text-[10px]">
|
||||
{new Date(item.timestamp).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
<CommandSeparator className="my-2" />
|
||||
|
||||
<CommandGroup heading="Servers & Hosts" className="px-2">
|
||||
{filteredHosts.length > 0 ? (
|
||||
filteredHosts.map((host, i) => {
|
||||
const actions = [
|
||||
host.enableTerminal !== false && "Terminal",
|
||||
host.enableFileManager !== false && "Files",
|
||||
host.enableDocker && "Docker",
|
||||
host.enableTunnel && "Tunnels",
|
||||
"Stats",
|
||||
].filter(Boolean) as string[];
|
||||
host.enableSsh &&
|
||||
host.enableTerminal !== false && {
|
||||
type: "terminal",
|
||||
icon: <Terminal className="size-3" />,
|
||||
label: "Terminal",
|
||||
},
|
||||
host.enableSsh &&
|
||||
host.enableFileManager && {
|
||||
type: "files",
|
||||
icon: <FolderOpen className="size-3" />,
|
||||
label: "Files",
|
||||
},
|
||||
host.enableSsh &&
|
||||
host.enableDocker && {
|
||||
type: "docker",
|
||||
icon: <Box className="size-3" />,
|
||||
label: "Docker",
|
||||
},
|
||||
host.enableSsh &&
|
||||
host.enableTunnel && {
|
||||
type: "tunnel",
|
||||
icon: <Network className="size-3" />,
|
||||
label: "Tunnels",
|
||||
},
|
||||
host.enableSsh && {
|
||||
type: "stats",
|
||||
icon: <Activity className="size-3" />,
|
||||
label: "Stats",
|
||||
},
|
||||
host.enableRdp && {
|
||||
type: "rdp",
|
||||
icon: <Monitor className="size-3" />,
|
||||
label: "RDP",
|
||||
},
|
||||
host.enableVnc && {
|
||||
type: "vnc",
|
||||
icon: <Monitor className="size-3" />,
|
||||
label: "VNC",
|
||||
},
|
||||
host.enableTelnet && {
|
||||
type: "telnet",
|
||||
icon: <Terminal className="size-3" />,
|
||||
label: "Telnet",
|
||||
},
|
||||
].filter(Boolean) as {
|
||||
type: string;
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
}[];
|
||||
|
||||
return (
|
||||
<CommandItem
|
||||
@@ -279,28 +363,25 @@ export function CommandPalette({
|
||||
)}
|
||||
</div>
|
||||
<span className="text-xs text-muted-foreground font-mono">
|
||||
{host.ip}
|
||||
{host.username}@{host.ip}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
{actions.map((action) => (
|
||||
<Button
|
||||
key={action}
|
||||
key={action.type}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
title={action}
|
||||
title={action.label}
|
||||
className="size-7 rounded-none hover:bg-accent-brand/20 hover:text-accent-brand"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleAction(() =>
|
||||
onOpenTab(
|
||||
ACTION_TAB_TYPE[action] as any,
|
||||
host.name,
|
||||
),
|
||||
onOpenTab(action.type as any, host.name),
|
||||
);
|
||||
}}
|
||||
>
|
||||
{ACTION_ICONS[action]}
|
||||
{action.icon}
|
||||
</Button>
|
||||
))}
|
||||
<DropdownMenu>
|
||||
@@ -322,7 +403,15 @@ export function CommandPalette({
|
||||
className="rounded-none text-xs font-semibold hover:bg-accent-brand/10 hover:text-accent-brand cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleAction(() => onOpenTab("host-manager"));
|
||||
setIsOpen(false);
|
||||
onOpenTab("host-manager");
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("host-manager:edit-host", {
|
||||
detail: host.id,
|
||||
}),
|
||||
);
|
||||
}, 100);
|
||||
}}
|
||||
>
|
||||
<Edit3 className="size-3.5 mr-2" /> Edit Host
|
||||
@@ -335,7 +424,7 @@ export function CommandPalette({
|
||||
})
|
||||
) : (
|
||||
<div className="py-6 text-center text-sm text-muted-foreground">
|
||||
No hosts found matching "{search}"
|
||||
No hosts found matching “{search}”
|
||||
</div>
|
||||
)}
|
||||
</CommandGroup>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1017
-317
File diff suppressed because it is too large
Load Diff
+551
-130
File diff suppressed because it is too large
Load Diff
@@ -105,8 +105,8 @@ export function ConnectionLog({
|
||||
|
||||
const borderClass =
|
||||
position === "bottom" && !isExpanded
|
||||
? "border-t-2 border-border"
|
||||
: "border-b-2 border-border";
|
||||
? "border-t-1 border-border"
|
||||
: "border-b-1 border-border";
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -117,7 +117,7 @@ export function ConnectionLog({
|
||||
)}
|
||||
|
||||
<div
|
||||
className={`relative z-10 bg-bg-subtle pointer-events-auto ${isExpanded ? "flex flex-col h-full" : ""} ${!isExpanded ? borderClass : ""}`}
|
||||
className={`relative z-10 bg-bg-base pointer-events-auto ${isExpanded ? "flex flex-col h-full" : ""} ${!isExpanded ? borderClass : ""}`}
|
||||
>
|
||||
<div className="flex items-center justify-between px-3 py-2 shrink-0">
|
||||
<Button
|
||||
@@ -152,7 +152,7 @@ export function ConnectionLog({
|
||||
{isExpanded && (
|
||||
<div
|
||||
ref={logContainerRef}
|
||||
className="flex-1 h-0 overflow-y-auto overflow-x-hidden thin-scrollbar border-t-2 border-border bg-bg-base"
|
||||
className="flex-1 h-0 overflow-y-auto overflow-x-hidden thin-scrollbar border-t-1 border-border bg-bg-base"
|
||||
>
|
||||
<div className="px-3 py-2">
|
||||
{logs.length === 0 ? (
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
export type ConnectionStage =
|
||||
| "dns"
|
||||
| "tcp"
|
||||
| "handshake"
|
||||
| "auth"
|
||||
| "connected"
|
||||
| "connection"
|
||||
| "error"
|
||||
| "proxy"
|
||||
| "jump"
|
||||
| "docker_connecting"
|
||||
| "docker_auth"
|
||||
| "docker_session"
|
||||
| "docker_ready"
|
||||
| "stats_connecting"
|
||||
| "stats_totp"
|
||||
| "stats_polling"
|
||||
| "stats_heartbeat"
|
||||
| "tunnel_connecting"
|
||||
| "tunnel_source"
|
||||
| "tunnel_endpoint"
|
||||
| "tunnel_forwarding"
|
||||
| "tunnel_retry"
|
||||
| "tunnel_connected"
|
||||
| "sftp_connecting"
|
||||
| "sftp_auth"
|
||||
| "sftp_connected";
|
||||
|
||||
export type LogEntry = {
|
||||
id: string;
|
||||
timestamp: Date;
|
||||
type: "info" | "success" | "warning" | "error";
|
||||
stage: ConnectionStage;
|
||||
message: string;
|
||||
details?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export interface ConnectionLogResponse {
|
||||
connectionLogs?: LogEntry[];
|
||||
}
|
||||
Vendored
-136
@@ -1,136 +0,0 @@
|
||||
interface ServerConfig {
|
||||
serverUrl?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface ConnectionTestResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface DialogOptions {
|
||||
title?: string;
|
||||
defaultPath?: string;
|
||||
buttonLabel?: string;
|
||||
filters?: Array<{ name: string; extensions: string[] }>;
|
||||
properties?: string[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface DialogResult {
|
||||
canceled: boolean;
|
||||
filePath?: string;
|
||||
filePaths?: string[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ElectronAPI {
|
||||
getAppVersion: () => Promise<string>;
|
||||
getPlatform: () => Promise<string>;
|
||||
|
||||
getServerConfig: () => Promise<ServerConfig>;
|
||||
saveServerConfig: (config: ServerConfig) => Promise<{ success: boolean }>;
|
||||
testServerConnection: (serverUrl: string) => Promise<ConnectionTestResult>;
|
||||
getC2STunnelConfig: () => Promise<unknown[]>;
|
||||
saveC2STunnelConfig: (
|
||||
config: unknown[],
|
||||
) => Promise<{ success: boolean; error?: string }>;
|
||||
checkLocalPortAvailable: (
|
||||
host: string,
|
||||
port: number,
|
||||
) => Promise<{ available: boolean; error?: string }>;
|
||||
getC2STunnelPresetDefaultName: () => Promise<string>;
|
||||
startC2STunnel: (
|
||||
tunnel: unknown,
|
||||
index: number,
|
||||
) => Promise<{ success: boolean; tunnelName?: string; error?: string }>;
|
||||
testC2STunnel: (
|
||||
tunnel: unknown,
|
||||
index: number,
|
||||
) => Promise<{ success: boolean; message?: string; error?: string }>;
|
||||
stopC2STunnel: (
|
||||
tunnelName: string,
|
||||
) => Promise<{ success: boolean; error?: string }>;
|
||||
getC2STunnelStatuses: () => Promise<Record<string, unknown>>;
|
||||
onC2STunnelStatuses?: (
|
||||
callback: (statuses: Record<string, unknown>) => void,
|
||||
) => () => void;
|
||||
startC2SAutoStartTunnels: () => Promise<{
|
||||
success: boolean;
|
||||
started: number;
|
||||
errors: string[];
|
||||
}>;
|
||||
clearSessionCookies: () => Promise<void>;
|
||||
getSessionCookie: (
|
||||
name: string,
|
||||
targetUrl?: string,
|
||||
) => Promise<string | null>;
|
||||
waitForSessionCookie: (
|
||||
name: string,
|
||||
targetUrl?: string,
|
||||
previousValue?: string | null,
|
||||
timeoutMs?: number,
|
||||
) => Promise<{ success: boolean; value?: string; error?: string }>;
|
||||
|
||||
showSaveDialog: (options: DialogOptions) => Promise<DialogResult>;
|
||||
showOpenDialog: (options: DialogOptions) => Promise<DialogResult>;
|
||||
|
||||
onUpdateAvailable: (callback: () => void) => void;
|
||||
onUpdateDownloaded: (callback: () => void) => void;
|
||||
|
||||
removeAllListeners: (channel: string) => void;
|
||||
isElectron: boolean;
|
||||
isDev: boolean;
|
||||
|
||||
invoke: (channel: string, ...args: unknown[]) => Promise<unknown>;
|
||||
|
||||
createTempFile: (fileData: {
|
||||
fileName: string;
|
||||
content: string;
|
||||
encoding?: "base64" | "utf8";
|
||||
}) => Promise<{
|
||||
success: boolean;
|
||||
tempId?: string;
|
||||
path?: string;
|
||||
error?: string;
|
||||
}>;
|
||||
|
||||
createTempFolder: (folderData: {
|
||||
folderName: string;
|
||||
files: Array<{
|
||||
relativePath: string;
|
||||
content: string;
|
||||
encoding?: "base64" | "utf8";
|
||||
}>;
|
||||
}) => Promise<{
|
||||
success: boolean;
|
||||
tempId?: string;
|
||||
path?: string;
|
||||
error?: string;
|
||||
}>;
|
||||
|
||||
startDragToDesktop: (dragData: {
|
||||
tempId: string;
|
||||
fileName: string;
|
||||
}) => Promise<{
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}>;
|
||||
|
||||
cleanupTempFile: (tempId: string) => Promise<{
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI: ElectronAPI;
|
||||
IS_ELECTRON: boolean;
|
||||
electronClipboard?: {
|
||||
writeText(text: string): Promise<boolean>;
|
||||
readText(): Promise<string>;
|
||||
};
|
||||
}
|
||||
}
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
declare module "guacamole-common-js" {
|
||||
namespace Guacamole {
|
||||
class Client {
|
||||
constructor(tunnel: Tunnel);
|
||||
connect(data?: string): void;
|
||||
disconnect(): void;
|
||||
getDisplay(): Display;
|
||||
sendKeyEvent(pressed: number, keysym: number): void;
|
||||
sendMouseState(state: Mouse.State): void;
|
||||
sendSize(width: number, height: number): void;
|
||||
setClipboard(stream: OutputStream, mimetype: string): void;
|
||||
createClipboardStream(mimetype: string): OutputStream;
|
||||
onstatechange: ((state: number) => void) | null;
|
||||
onerror: ((error: Status) => void) | null;
|
||||
onclipboard: ((stream: InputStream, mimetype: string) => void) | null;
|
||||
onaudio: ((stream: InputStream, mimetype: string) => void) | null;
|
||||
}
|
||||
|
||||
class AudioPlayer {
|
||||
static getInstance(
|
||||
stream: InputStream,
|
||||
mimetype: string,
|
||||
): AudioPlayer | null;
|
||||
sync(): void;
|
||||
}
|
||||
|
||||
class Display {
|
||||
getElement(): HTMLElement;
|
||||
getWidth(): number;
|
||||
getHeight(): number;
|
||||
scale(scale: number): void;
|
||||
onresize: (() => void) | null;
|
||||
}
|
||||
|
||||
class Tunnel {
|
||||
onerror: ((status: Status) => void) | null;
|
||||
onstatechange: ((state: number) => void) | null;
|
||||
}
|
||||
|
||||
class WebSocketTunnel extends Tunnel {
|
||||
constructor(url: string);
|
||||
}
|
||||
|
||||
class Mouse {
|
||||
constructor(element: HTMLElement);
|
||||
onmousedown: ((state: Mouse.State) => void) | null;
|
||||
onmouseup: ((state: Mouse.State) => void) | null;
|
||||
onmousemove: ((state: Mouse.State) => void) | null;
|
||||
onmouseout: ((state: Mouse.State) => void) | null;
|
||||
}
|
||||
|
||||
namespace Mouse {
|
||||
class State {
|
||||
constructor(
|
||||
x: number,
|
||||
y: number,
|
||||
left?: boolean,
|
||||
middle?: boolean,
|
||||
right?: boolean,
|
||||
up?: boolean,
|
||||
down?: boolean,
|
||||
);
|
||||
constructor(state: {
|
||||
x: number;
|
||||
y: number;
|
||||
left?: boolean;
|
||||
middle?: boolean;
|
||||
right?: boolean;
|
||||
up?: boolean;
|
||||
down?: boolean;
|
||||
});
|
||||
x: number;
|
||||
y: number;
|
||||
left: boolean;
|
||||
middle: boolean;
|
||||
right: boolean;
|
||||
up: boolean;
|
||||
down: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
class Keyboard {
|
||||
constructor(element: Document | HTMLElement);
|
||||
onkeydown: ((keysym: number) => void) | null;
|
||||
onkeyup: ((keysym: number) => void) | null;
|
||||
}
|
||||
|
||||
class Status {
|
||||
code: number;
|
||||
message: string;
|
||||
isError(): boolean;
|
||||
}
|
||||
|
||||
class InputStream {
|
||||
onblob: ((data: string) => void) | null;
|
||||
onend: (() => void) | null;
|
||||
}
|
||||
|
||||
class OutputStream {
|
||||
sendBlob(data: string): void;
|
||||
sendEnd(): void;
|
||||
}
|
||||
|
||||
class StringReader {
|
||||
constructor(stream: InputStream);
|
||||
ontext: ((text: string) => void) | null;
|
||||
onend: (() => void) | null;
|
||||
}
|
||||
|
||||
class StringWriter {
|
||||
constructor(stream: OutputStream);
|
||||
sendText(text: string): void;
|
||||
sendEnd(): void;
|
||||
}
|
||||
}
|
||||
|
||||
export default Guacamole;
|
||||
}
|
||||
@@ -1,929 +0,0 @@
|
||||
import type { Client } from "ssh2";
|
||||
import type { Request } from "express";
|
||||
import type { RefObject } from "react";
|
||||
|
||||
// ============================================================================
|
||||
// HOST TYPES (SSH, RDP, VNC, Telnet)
|
||||
// ============================================================================
|
||||
|
||||
export type ConnectionType = "ssh" | "rdp" | "vnc" | "telnet";
|
||||
export type SSHAuthType = "password" | "key" | "credential" | "none" | "opkssh";
|
||||
export type GuacamoleAuthType = "password" | "credential";
|
||||
|
||||
export interface HostFeatureFlags {
|
||||
enableTerminal: boolean; // SSH, Telnet only
|
||||
enableTunnel: boolean; // SSH only
|
||||
enableFileManager: boolean; // SSH only
|
||||
enableDocker: boolean; // SSH only
|
||||
enableRemoteDesktop: boolean; // RDP, VNC only
|
||||
}
|
||||
|
||||
export interface JumpHost {
|
||||
hostId: number;
|
||||
}
|
||||
|
||||
export interface QuickAction {
|
||||
name: string;
|
||||
snippetId: number;
|
||||
}
|
||||
|
||||
export interface Host {
|
||||
id: number;
|
||||
name: string;
|
||||
ip: string;
|
||||
port: number;
|
||||
username: string;
|
||||
folder: string;
|
||||
tags: string[];
|
||||
pin: boolean;
|
||||
authType: "password" | "key" | "credential" | "none" | "opkssh";
|
||||
password?: string;
|
||||
key?: string;
|
||||
keyPassword?: string;
|
||||
keyType?: string;
|
||||
sudoPassword?: string;
|
||||
forceKeyboardInteractive?: boolean;
|
||||
|
||||
autostartPassword?: string;
|
||||
autostartKey?: string;
|
||||
autostartKeyPassword?: string;
|
||||
|
||||
credentialId?: number;
|
||||
overrideCredentialUsername?: boolean;
|
||||
userId?: string;
|
||||
enableTerminal: boolean;
|
||||
enableTunnel: boolean;
|
||||
enableFileManager: boolean;
|
||||
enableDocker: boolean;
|
||||
showTerminalInSidebar: boolean;
|
||||
showFileManagerInSidebar: boolean;
|
||||
showTunnelInSidebar: boolean;
|
||||
showDockerInSidebar: boolean;
|
||||
showServerStatsInSidebar: boolean;
|
||||
defaultPath: string;
|
||||
tunnelConnections: TunnelConnection[];
|
||||
jumpHosts?: JumpHost[];
|
||||
quickActions?: QuickAction[];
|
||||
statsConfig?: string | Record<string, unknown>;
|
||||
terminalConfig?: TerminalConfig;
|
||||
notes?: string;
|
||||
|
||||
useSocks5?: boolean;
|
||||
socks5Host?: string;
|
||||
socks5Port?: number;
|
||||
socks5Username?: string;
|
||||
socks5Password?: string;
|
||||
socks5ProxyChain?: ProxyNode[];
|
||||
|
||||
macAddress?: string;
|
||||
portKnockSequence?: Array<{
|
||||
port: number;
|
||||
protocol?: "tcp" | "udp";
|
||||
delay?: number;
|
||||
}>;
|
||||
|
||||
connectionType?: "ssh" | "rdp" | "vnc" | "telnet";
|
||||
domain?: string;
|
||||
security?: string;
|
||||
ignoreCert?: boolean;
|
||||
guacamoleConfig?: string | Record<string, unknown>;
|
||||
|
||||
enableSsh?: boolean;
|
||||
enableRdp?: boolean;
|
||||
enableVnc?: boolean;
|
||||
enableTelnet?: boolean;
|
||||
sshPort?: number;
|
||||
rdpPort?: number;
|
||||
vncPort?: number;
|
||||
telnetPort?: number;
|
||||
rdpUser?: string;
|
||||
rdpPassword?: string;
|
||||
rdpDomain?: string;
|
||||
rdpSecurity?: string;
|
||||
rdpIgnoreCert?: boolean;
|
||||
vncPassword?: string;
|
||||
vncUser?: string;
|
||||
telnetUser?: string;
|
||||
telnetPassword?: string;
|
||||
hasRdpPassword?: boolean;
|
||||
hasVncPassword?: boolean;
|
||||
hasTelnetPassword?: boolean;
|
||||
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
|
||||
hasPassword?: boolean;
|
||||
hasKey?: boolean;
|
||||
hasSudoPassword?: boolean;
|
||||
|
||||
isShared?: boolean;
|
||||
permissionLevel?: "view";
|
||||
sharedExpiresAt?: string;
|
||||
}
|
||||
|
||||
export interface JumpHostData {
|
||||
hostId: number;
|
||||
}
|
||||
|
||||
export interface QuickActionData {
|
||||
name: string;
|
||||
snippetId: number;
|
||||
}
|
||||
|
||||
export interface ProxyNode {
|
||||
host: string;
|
||||
port: number;
|
||||
type: 4 | 5 | "http";
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export interface HostData {
|
||||
name?: string;
|
||||
ip: string;
|
||||
port: number;
|
||||
username: string;
|
||||
folder?: string;
|
||||
tags?: string[];
|
||||
pin?: boolean;
|
||||
authType: "password" | "key" | "credential" | "none" | "opkssh";
|
||||
password?: string;
|
||||
key?: File | null;
|
||||
keyPassword?: string;
|
||||
keyType?: string;
|
||||
sudoPassword?: string;
|
||||
credentialId?: number | null;
|
||||
overrideCredentialUsername?: boolean;
|
||||
enableTerminal?: boolean;
|
||||
enableTunnel?: boolean;
|
||||
enableFileManager?: boolean;
|
||||
enableDocker?: boolean;
|
||||
showTerminalInSidebar?: boolean;
|
||||
showFileManagerInSidebar?: boolean;
|
||||
showTunnelInSidebar?: boolean;
|
||||
showDockerInSidebar?: boolean;
|
||||
showServerStatsInSidebar?: boolean;
|
||||
defaultPath?: string;
|
||||
forceKeyboardInteractive?: boolean;
|
||||
tunnelConnections?: TunnelConnection[];
|
||||
jumpHosts?: JumpHostData[];
|
||||
quickActions?: QuickActionData[];
|
||||
statsConfig?: string | Record<string, unknown>;
|
||||
terminalConfig?: TerminalConfig;
|
||||
notes?: string;
|
||||
|
||||
useSocks5?: boolean;
|
||||
socks5Host?: string;
|
||||
socks5Port?: number;
|
||||
socks5Username?: string;
|
||||
socks5Password?: string;
|
||||
socks5ProxyChain?: ProxyNode[];
|
||||
|
||||
macAddress?: string;
|
||||
portKnockSequence?: Array<{
|
||||
port: number;
|
||||
protocol?: "tcp" | "udp";
|
||||
delay?: number;
|
||||
}>;
|
||||
|
||||
connectionType?: "ssh" | "rdp" | "vnc" | "telnet";
|
||||
domain?: string;
|
||||
security?: string;
|
||||
ignoreCert?: boolean;
|
||||
guacamoleConfig?: Record<string, unknown> | null;
|
||||
dockerConfig?: Record<string, unknown> | null;
|
||||
|
||||
enableSsh?: boolean;
|
||||
enableRdp?: boolean;
|
||||
enableVnc?: boolean;
|
||||
enableTelnet?: boolean;
|
||||
sshPort?: number;
|
||||
rdpPort?: number;
|
||||
vncPort?: number;
|
||||
telnetPort?: number;
|
||||
rdpUser?: string;
|
||||
rdpPassword?: string;
|
||||
rdpDomain?: string;
|
||||
rdpSecurity?: string;
|
||||
rdpIgnoreCert?: boolean;
|
||||
vncPassword?: string;
|
||||
vncUser?: string;
|
||||
telnetUser?: string;
|
||||
telnetPassword?: string;
|
||||
}
|
||||
|
||||
export type SSHHost = Host;
|
||||
export type SSHHostData = HostData;
|
||||
|
||||
export interface SSHFolder {
|
||||
id: number;
|
||||
userId: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
icon?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CREDENTIAL TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface Credential {
|
||||
id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
folder?: string;
|
||||
tags: string[];
|
||||
authType: "password" | "key";
|
||||
username?: string;
|
||||
password?: string;
|
||||
key?: string;
|
||||
publicKey?: string;
|
||||
keyPassword?: string;
|
||||
keyType?: string;
|
||||
usageCount: number;
|
||||
lastUsed?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface CredentialBackend {
|
||||
id: number;
|
||||
userId: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
folder: string | null;
|
||||
tags: string;
|
||||
authType: "password" | "key";
|
||||
username: string | null;
|
||||
password: string | null;
|
||||
key: string;
|
||||
privateKey?: string;
|
||||
publicKey?: string;
|
||||
keyPassword: string | null;
|
||||
keyType?: string;
|
||||
detectedKeyType: string;
|
||||
usageCount: number;
|
||||
lastUsed: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface CredentialData {
|
||||
name: string;
|
||||
description?: string;
|
||||
folder?: string;
|
||||
tags: string[];
|
||||
authType: "password" | "key";
|
||||
username?: string;
|
||||
password?: string;
|
||||
key?: string;
|
||||
publicKey?: string;
|
||||
keyPassword?: string;
|
||||
keyType?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TUNNEL TYPES
|
||||
// ============================================================================
|
||||
|
||||
export type TunnelScope = "s2s" | "c2s";
|
||||
export type TunnelMode = "local" | "remote" | "dynamic";
|
||||
|
||||
export interface TunnelConnection {
|
||||
scope?: TunnelScope;
|
||||
mode?: TunnelMode;
|
||||
tunnelType?: "local" | "remote";
|
||||
bindHost?: string;
|
||||
sourceHostId?: number;
|
||||
sourceHostName?: string;
|
||||
sourcePort: number;
|
||||
endpointPort: number;
|
||||
endpointHost?: string;
|
||||
targetHost?: string;
|
||||
|
||||
endpointPassword?: string;
|
||||
endpointKey?: string;
|
||||
endpointKeyPassword?: string;
|
||||
endpointAuthType?: string;
|
||||
endpointKeyType?: string;
|
||||
|
||||
maxRetries: number;
|
||||
retryInterval: number;
|
||||
autoStart: boolean;
|
||||
}
|
||||
|
||||
export interface TunnelConfig {
|
||||
name: string;
|
||||
scope?: TunnelScope;
|
||||
mode?: TunnelMode;
|
||||
tunnelType?: "local" | "remote";
|
||||
bindHost?: string;
|
||||
targetHost?: string;
|
||||
|
||||
sourceHostId: number;
|
||||
tunnelIndex: number;
|
||||
|
||||
requestingUserId?: string;
|
||||
|
||||
hostName: string;
|
||||
sourceIP: string;
|
||||
sourceSSHPort: number;
|
||||
sourceUsername: string;
|
||||
sourcePassword?: string;
|
||||
sourceAuthMethod: string;
|
||||
sourceSSHKey?: string;
|
||||
sourceKeyPassword?: string;
|
||||
sourceKeyType?: string;
|
||||
sourceCredentialId?: number;
|
||||
sourceUserId?: string;
|
||||
endpointIP: string;
|
||||
endpointSSHPort: number;
|
||||
endpointUsername: string;
|
||||
endpointHost: string;
|
||||
endpointPassword?: string;
|
||||
endpointAuthMethod: string;
|
||||
endpointSSHKey?: string;
|
||||
endpointKeyPassword?: string;
|
||||
endpointKeyType?: string;
|
||||
endpointCredentialId?: number;
|
||||
endpointUserId?: string;
|
||||
sourcePort: number;
|
||||
endpointPort: number;
|
||||
maxRetries: number;
|
||||
retryInterval: number;
|
||||
autoStart: boolean;
|
||||
isPinned: boolean;
|
||||
|
||||
useSocks5?: boolean;
|
||||
socks5Host?: string;
|
||||
socks5Port?: number;
|
||||
socks5Username?: string;
|
||||
socks5Password?: string;
|
||||
socks5ProxyChain?: ProxyNode[];
|
||||
}
|
||||
|
||||
export interface C2STunnelPreset {
|
||||
id: number;
|
||||
userId: string;
|
||||
name: string;
|
||||
config: TunnelConnection[];
|
||||
platform?: string | null;
|
||||
computerName?: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface TunnelStatus {
|
||||
connected: boolean;
|
||||
status: ConnectionState;
|
||||
retryCount?: number;
|
||||
maxRetries?: number;
|
||||
nextRetryIn?: number;
|
||||
reason?: string;
|
||||
errorType?: ErrorType;
|
||||
manualDisconnect?: boolean;
|
||||
retryExhausted?: boolean;
|
||||
connectionLogs?: Array<{
|
||||
type: "info" | "success" | "warning" | "error";
|
||||
stage: string;
|
||||
message: string;
|
||||
details?: Record<string, unknown>;
|
||||
}>;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// FILE MANAGER TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface Tab {
|
||||
id: string | number;
|
||||
title: string;
|
||||
fileName: string;
|
||||
content: string;
|
||||
isSSH?: boolean;
|
||||
sshSessionId?: string;
|
||||
filePath?: string;
|
||||
loading?: boolean;
|
||||
dirty?: boolean;
|
||||
}
|
||||
|
||||
export interface FileManagerFile {
|
||||
name: string;
|
||||
path: string;
|
||||
type?: "file" | "directory";
|
||||
isSSH?: boolean;
|
||||
sshSessionId?: string;
|
||||
}
|
||||
|
||||
export interface FileManagerShortcut {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface FileItem {
|
||||
name: string;
|
||||
path: string;
|
||||
isPinned?: boolean;
|
||||
type: "file" | "directory" | "link";
|
||||
sshSessionId?: string;
|
||||
size?: number;
|
||||
modified?: string;
|
||||
permissions?: string;
|
||||
owner?: string;
|
||||
group?: string;
|
||||
linkTarget?: string;
|
||||
executable?: boolean;
|
||||
}
|
||||
|
||||
export interface ShortcutItem {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface SSHConnection {
|
||||
id: number;
|
||||
name: string;
|
||||
ip: string;
|
||||
port: number;
|
||||
username: string;
|
||||
isPinned?: boolean;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HOST INFO TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface HostInfo {
|
||||
id: number;
|
||||
name?: string;
|
||||
ip: string;
|
||||
port: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ALERT TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface TermixAlert {
|
||||
id: string;
|
||||
title: string;
|
||||
message: string;
|
||||
expiresAt: string;
|
||||
priority?: "low" | "medium" | "high" | "critical";
|
||||
type?: "info" | "warning" | "error" | "success";
|
||||
actionUrl?: string;
|
||||
actionText?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TERMINAL CONFIGURATION TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface TerminalConfig {
|
||||
cursorBlink: boolean;
|
||||
cursorStyle: "block" | "underline" | "bar";
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
letterSpacing: number;
|
||||
lineHeight: number;
|
||||
theme: string;
|
||||
|
||||
scrollback: number;
|
||||
bellStyle: "none" | "sound" | "visual" | "both";
|
||||
rightClickSelectsWord: boolean;
|
||||
fastScrollModifier: "alt" | "ctrl" | "shift";
|
||||
fastScrollSensitivity: number;
|
||||
minimumContrastRatio: number;
|
||||
|
||||
backspaceMode: "normal" | "control-h";
|
||||
agentForwarding: boolean;
|
||||
environmentVariables: Array<{ key: string; value: string }>;
|
||||
startupSnippetId: number | null;
|
||||
autoMosh: boolean;
|
||||
moshCommand: string;
|
||||
sudoPasswordAutoFill: boolean;
|
||||
keepaliveInterval?: number;
|
||||
keepaliveCountMax?: number;
|
||||
autoTmux: boolean;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TAB TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface TabContextTab {
|
||||
id: number;
|
||||
instanceId?: string;
|
||||
type:
|
||||
| "home"
|
||||
| "terminal"
|
||||
| "ssh_manager"
|
||||
| "server_stats"
|
||||
| "admin"
|
||||
| "file_manager"
|
||||
| "user_profile"
|
||||
| "docker"
|
||||
| "network_graph"
|
||||
| "rdp"
|
||||
| "vnc"
|
||||
| "telnet";
|
||||
title: string;
|
||||
hostConfig?: SSHHost;
|
||||
terminalRef?: RefObject<TerminalRefHandle | null>;
|
||||
initialTab?: string;
|
||||
_updateTimestamp?: number;
|
||||
connectionConfig?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface TerminalRefHandle {
|
||||
disconnect?: () => void;
|
||||
reconnect?: () => void;
|
||||
fit?: () => void;
|
||||
sendInput?: (data: string) => void;
|
||||
notifyResize?: () => void;
|
||||
refresh?: () => void;
|
||||
openFileManager?: () => void;
|
||||
}
|
||||
|
||||
export type SplitLayout = "2h" | "2v" | "3l" | "3r" | "3t" | "4grid";
|
||||
|
||||
export interface SplitConfiguration {
|
||||
layout: SplitLayout;
|
||||
positions: Map<number, number>;
|
||||
}
|
||||
|
||||
export interface SplitLayoutOption {
|
||||
id: SplitLayout;
|
||||
name: string;
|
||||
description: string;
|
||||
cellCount: number;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CONNECTION STATES
|
||||
// ============================================================================
|
||||
|
||||
export const CONNECTION_STATES = {
|
||||
DISCONNECTED: "disconnected",
|
||||
CONNECTING: "connecting",
|
||||
CONNECTED: "connected",
|
||||
VERIFYING: "verifying",
|
||||
FAILED: "failed",
|
||||
UNSTABLE: "unstable",
|
||||
RETRYING: "retrying",
|
||||
WAITING: "waiting",
|
||||
DISCONNECTING: "disconnecting",
|
||||
} as const;
|
||||
|
||||
export type ConnectionState =
|
||||
(typeof CONNECTION_STATES)[keyof typeof CONNECTION_STATES];
|
||||
|
||||
export type ErrorType =
|
||||
| "CONNECTION_FAILED"
|
||||
| "AUTHENTICATION_FAILED"
|
||||
| "TIMEOUT"
|
||||
| "NETWORK_ERROR"
|
||||
| "UNKNOWN";
|
||||
|
||||
// ============================================================================
|
||||
// AUTHENTICATION TYPES
|
||||
// ============================================================================
|
||||
|
||||
export type AuthType = "password" | "key" | "credential" | "none" | "opkssh";
|
||||
|
||||
export type KeyType = "rsa" | "ecdsa" | "ed25519";
|
||||
|
||||
// ============================================================================
|
||||
// API RESPONSE TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface ApiResponse<T = unknown> {
|
||||
data?: T;
|
||||
error?: string;
|
||||
message?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// COMPONENT PROP TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface CredentialsManagerProps {
|
||||
onEditCredential?: (credential: Credential) => void;
|
||||
onAddCredential?: () => void;
|
||||
}
|
||||
|
||||
export interface CredentialEditorProps {
|
||||
editingCredential?: Credential | null;
|
||||
onFormSubmit?: () => void;
|
||||
onBack?: () => void;
|
||||
}
|
||||
|
||||
export interface CredentialViewerProps {
|
||||
credential: Credential;
|
||||
onClose: () => void;
|
||||
onEdit: () => void;
|
||||
}
|
||||
|
||||
export interface CredentialSelectorProps {
|
||||
value?: number | null;
|
||||
onValueChange: (value: number | null) => void;
|
||||
}
|
||||
|
||||
export interface HostManagerProps {
|
||||
onSelectView?: (view: string) => void;
|
||||
isTopbarOpen?: boolean;
|
||||
initialTab?: string;
|
||||
hostConfig?: SSHHost;
|
||||
_updateTimestamp?: number;
|
||||
rightSidebarOpen?: boolean;
|
||||
rightSidebarWidth?: number;
|
||||
currentTabId?: number;
|
||||
updateTab?: (tabId: number, updates: Partial<Omit<Tab, "id">>) => void;
|
||||
}
|
||||
|
||||
export interface SSHManagerHostEditorProps {
|
||||
editingHost?: SSHHost | null;
|
||||
onFormSubmit?: () => void;
|
||||
}
|
||||
|
||||
export interface SSHManagerHostViewerProps {
|
||||
onEditHost?: (host: SSHHost) => void;
|
||||
onAddHost?: () => void;
|
||||
}
|
||||
|
||||
export interface HostProps {
|
||||
host: SSHHost;
|
||||
onHostConnect?: () => void;
|
||||
}
|
||||
|
||||
export interface SSHTunnelProps {
|
||||
filterHostKey?: string;
|
||||
}
|
||||
|
||||
export interface SSHTunnelViewerProps {
|
||||
hosts?: SSHHost[];
|
||||
tunnelStatuses?: Record<string, TunnelStatus>;
|
||||
tunnelActions?: Record<
|
||||
string,
|
||||
(
|
||||
action: "connect" | "disconnect" | "cancel",
|
||||
host: SSHHost,
|
||||
tunnelIndex: number,
|
||||
) => Promise<void>
|
||||
>;
|
||||
onTunnelAction?: (
|
||||
action: "connect" | "disconnect" | "cancel",
|
||||
host: SSHHost,
|
||||
tunnelIndex: number,
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface FileManagerProps {
|
||||
onSelectView?: (view: string) => void;
|
||||
embedded?: boolean;
|
||||
initialHost?: SSHHost | null;
|
||||
}
|
||||
|
||||
export interface AlertCardProps {
|
||||
alert: TermixAlert;
|
||||
onDismiss: (alertId: string) => void;
|
||||
}
|
||||
|
||||
export interface AlertManagerProps {
|
||||
alerts: TermixAlert[];
|
||||
onDismiss: (alertId: string) => void;
|
||||
loggedIn: boolean;
|
||||
}
|
||||
|
||||
export interface SSHTunnelObjectProps {
|
||||
host: SSHHost;
|
||||
tunnelIndex?: number;
|
||||
tunnelStatuses: Record<string, TunnelStatus>;
|
||||
tunnelActions: Record<string, boolean>;
|
||||
onTunnelAction: (
|
||||
action: "connect" | "disconnect" | "cancel",
|
||||
host: SSHHost,
|
||||
tunnelIndex: number,
|
||||
) => Promise<void>;
|
||||
compact?: boolean;
|
||||
bare?: boolean;
|
||||
}
|
||||
|
||||
export interface FolderStats {
|
||||
totalHosts: number;
|
||||
hostsByType: Array<{
|
||||
type: string;
|
||||
count: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SNIPPETS TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface Snippet {
|
||||
id: number;
|
||||
userId: string;
|
||||
name: string;
|
||||
content: string;
|
||||
description?: string;
|
||||
folder?: string;
|
||||
order?: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface SnippetData {
|
||||
name: string;
|
||||
content: string;
|
||||
description?: string;
|
||||
folder?: string;
|
||||
order?: number;
|
||||
}
|
||||
|
||||
export interface SnippetFolder {
|
||||
id: number;
|
||||
userId: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
icon?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BACKEND TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface HostConfig {
|
||||
host: SSHHost;
|
||||
tunnels: TunnelConfig[];
|
||||
}
|
||||
|
||||
export interface VerificationData {
|
||||
conn: Client;
|
||||
timeout: NodeJS.Timeout;
|
||||
startTime: number;
|
||||
attempts: number;
|
||||
maxAttempts: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// UTILITY TYPES
|
||||
// ============================================================================
|
||||
|
||||
export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
|
||||
export type RequiredFields<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
||||
|
||||
export type PartialExcept<T, K extends keyof T> = Partial<T> & Pick<T, K>;
|
||||
|
||||
// ============================================================================
|
||||
// EXPRESS REQUEST TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface AuthenticatedRequest extends Request {
|
||||
userId: string;
|
||||
sessionId?: string;
|
||||
user?: {
|
||||
id: string;
|
||||
username: string;
|
||||
isAdmin: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GITHUB API TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface GitHubAsset {
|
||||
id: number;
|
||||
name: string;
|
||||
size: number;
|
||||
download_count: number;
|
||||
browser_download_url: string;
|
||||
}
|
||||
|
||||
export interface GitHubRelease {
|
||||
id: number;
|
||||
tag_name: string;
|
||||
name: string;
|
||||
body: string;
|
||||
published_at: string;
|
||||
html_url: string;
|
||||
assets: GitHubAsset[];
|
||||
prerelease: boolean;
|
||||
draft: boolean;
|
||||
}
|
||||
|
||||
export interface GitHubAPIResponse<T> {
|
||||
data: T;
|
||||
cached: boolean;
|
||||
cache_age?: number;
|
||||
timestamp?: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CACHE TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface CacheEntry<T = unknown> {
|
||||
data: T;
|
||||
timestamp: number;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DATABASE EXPORT/IMPORT TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface ExportSummary {
|
||||
sshHostsImported: number;
|
||||
sshCredentialsImported: number;
|
||||
fileManagerItemsImported: number;
|
||||
dismissedAlertsImported: number;
|
||||
credentialUsageImported: number;
|
||||
settingsImported: number;
|
||||
skippedItems: number;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export interface ImportResult {
|
||||
success: boolean;
|
||||
summary: ExportSummary;
|
||||
}
|
||||
|
||||
export interface ExportRequestBody {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ImportRequestBody {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ExportPreviewBody {
|
||||
scope?: string;
|
||||
includeCredentials?: boolean;
|
||||
}
|
||||
|
||||
export interface RestoreRequestBody {
|
||||
backupPath: string;
|
||||
targetPath?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DOCKER TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface DockerContainer {
|
||||
id: string;
|
||||
name: string;
|
||||
image: string;
|
||||
status: string;
|
||||
state:
|
||||
| "created"
|
||||
| "running"
|
||||
| "paused"
|
||||
| "restarting"
|
||||
| "removing"
|
||||
| "exited"
|
||||
| "dead";
|
||||
ports: string;
|
||||
created: string;
|
||||
command?: string;
|
||||
labels?: Record<string, string>;
|
||||
networks?: string[];
|
||||
mounts?: string[];
|
||||
}
|
||||
|
||||
export interface DockerStats {
|
||||
cpu: string;
|
||||
memoryUsed: string;
|
||||
memoryLimit: string;
|
||||
memoryPercent: string;
|
||||
netInput: string;
|
||||
netOutput: string;
|
||||
blockRead: string;
|
||||
blockWrite: string;
|
||||
pids?: string;
|
||||
}
|
||||
|
||||
export interface DockerLogOptions {
|
||||
tail?: number;
|
||||
timestamps?: boolean;
|
||||
since?: string;
|
||||
until?: string;
|
||||
follow?: boolean;
|
||||
}
|
||||
|
||||
export interface DockerValidation {
|
||||
available: boolean;
|
||||
version?: string;
|
||||
error?: string;
|
||||
code?: string;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
export type WidgetType =
|
||||
| "cpu"
|
||||
| "memory"
|
||||
| "disk"
|
||||
| "network"
|
||||
| "uptime"
|
||||
| "processes"
|
||||
| "system"
|
||||
| "login_stats"
|
||||
| "ports"
|
||||
| "firewall";
|
||||
|
||||
export interface ListeningPort {
|
||||
protocol: "tcp" | "udp";
|
||||
localAddress: string;
|
||||
localPort: number;
|
||||
state?: string;
|
||||
pid?: number;
|
||||
process?: string;
|
||||
}
|
||||
|
||||
export interface PortsMetrics {
|
||||
source: "ss" | "netstat" | "none";
|
||||
ports: ListeningPort[];
|
||||
}
|
||||
|
||||
export interface FirewallRule {
|
||||
chain: string;
|
||||
target: string;
|
||||
protocol: string;
|
||||
source: string;
|
||||
destination: string;
|
||||
dport?: string;
|
||||
sport?: string;
|
||||
state?: string;
|
||||
interface?: string;
|
||||
extra?: string;
|
||||
}
|
||||
|
||||
export interface FirewallChain {
|
||||
name: string;
|
||||
policy: string;
|
||||
rules: FirewallRule[];
|
||||
}
|
||||
|
||||
export interface FirewallMetrics {
|
||||
type: "iptables" | "nftables" | "none";
|
||||
status: "active" | "inactive" | "unknown";
|
||||
chains: FirewallChain[];
|
||||
}
|
||||
|
||||
export interface StatsConfig {
|
||||
enabledWidgets: WidgetType[];
|
||||
statusCheckEnabled: boolean;
|
||||
statusCheckInterval: number;
|
||||
useGlobalStatusInterval?: boolean;
|
||||
metricsEnabled: boolean;
|
||||
metricsInterval: number;
|
||||
useGlobalMetricsInterval?: boolean;
|
||||
disableTcpPing?: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_STATS_CONFIG: StatsConfig = {
|
||||
enabledWidgets: [
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk",
|
||||
"network",
|
||||
"uptime",
|
||||
"system",
|
||||
"login_stats",
|
||||
"processes",
|
||||
"ports",
|
||||
"firewall",
|
||||
],
|
||||
statusCheckEnabled: true,
|
||||
statusCheckInterval: 30,
|
||||
useGlobalStatusInterval: true,
|
||||
metricsEnabled: true,
|
||||
metricsInterval: 30,
|
||||
useGlobalMetricsInterval: true,
|
||||
disableTcpPing: false,
|
||||
};
|
||||
@@ -1,307 +0,0 @@
|
||||
export type Host = {
|
||||
id: string;
|
||||
name: string;
|
||||
username: string;
|
||||
ip: string;
|
||||
port: number;
|
||||
folder: string;
|
||||
online: boolean;
|
||||
cpu: number;
|
||||
ram: number;
|
||||
lastAccess: string;
|
||||
tags?: string[];
|
||||
authType: "password" | "key" | "credential" | "none" | "opkssh";
|
||||
credentialId?: string;
|
||||
password?: string;
|
||||
key?: string;
|
||||
keyPassword?: string;
|
||||
keyType?: string;
|
||||
notes?: string;
|
||||
macAddress?: string;
|
||||
pin?: boolean;
|
||||
|
||||
enableTerminal: boolean;
|
||||
terminalConfig?: {
|
||||
cursorBlink: boolean;
|
||||
cursorStyle: "block" | "underline" | "bar";
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
letterSpacing: number;
|
||||
lineHeight: number;
|
||||
theme: string;
|
||||
scrollback: number;
|
||||
bellStyle: "none" | "sound" | "visual" | "both";
|
||||
rightClickSelectsWord: boolean;
|
||||
fastScrollModifier: "alt" | "ctrl" | "shift";
|
||||
fastScrollSensitivity: number;
|
||||
minimumContrastRatio: number;
|
||||
backspaceMode: "normal" | "control-h";
|
||||
agentForwarding: boolean;
|
||||
autoMosh: boolean;
|
||||
moshCommand: string;
|
||||
autoTmux: boolean;
|
||||
sudoPasswordAutoFill: boolean;
|
||||
sudoPassword?: string;
|
||||
keepaliveInterval?: number;
|
||||
keepaliveCountMax?: number;
|
||||
environmentVariables: { key: string; value: string }[];
|
||||
};
|
||||
|
||||
useSocks5?: boolean;
|
||||
socks5Host?: string;
|
||||
socks5Port?: number;
|
||||
socks5Username?: string;
|
||||
socks5Password?: string;
|
||||
socks5ProxyChain?: {
|
||||
host: string;
|
||||
port: number;
|
||||
type: 4 | 5 | "http";
|
||||
username?: string;
|
||||
password?: string;
|
||||
}[];
|
||||
jumpHosts?: { hostId: string }[];
|
||||
portKnockSequence?: {
|
||||
port: number;
|
||||
protocol: "tcp" | "udp";
|
||||
delay: number;
|
||||
}[];
|
||||
|
||||
enableTunnel: boolean;
|
||||
serverTunnels: {
|
||||
mode: "local" | "remote" | "dynamic";
|
||||
bindHost?: string;
|
||||
targetHost?: string;
|
||||
sourcePort: number;
|
||||
endpointHost: string;
|
||||
endpointPort: number;
|
||||
maxRetries: number;
|
||||
retryInterval: number;
|
||||
autoStart: boolean;
|
||||
}[];
|
||||
|
||||
enableFileManager: boolean;
|
||||
defaultPath?: string;
|
||||
|
||||
enableDocker: boolean;
|
||||
|
||||
statsConfig?: {
|
||||
statusCheckEnabled: boolean;
|
||||
statusCheckInterval: number;
|
||||
useGlobalStatusInterval: boolean;
|
||||
metricsEnabled: boolean;
|
||||
metricsInterval: number;
|
||||
useGlobalMetricsInterval: boolean;
|
||||
enabledWidgets: string[];
|
||||
};
|
||||
quickActions: { name: string; snippetId: string }[];
|
||||
|
||||
enableSsh: boolean;
|
||||
enableRdp: boolean;
|
||||
enableVnc: boolean;
|
||||
enableTelnet: boolean;
|
||||
|
||||
sshPort: number;
|
||||
rdpPort: number;
|
||||
vncPort: number;
|
||||
telnetPort: number;
|
||||
|
||||
rdpUser?: string;
|
||||
rdpPassword?: string;
|
||||
domain?: string;
|
||||
security?: string;
|
||||
ignoreCert?: boolean;
|
||||
|
||||
vncPassword?: string;
|
||||
vncUser?: string;
|
||||
|
||||
telnetUser?: string;
|
||||
telnetPassword?: string;
|
||||
|
||||
guacamoleConfig?: Record<string, any>;
|
||||
};
|
||||
|
||||
export type Credential = {
|
||||
id: string;
|
||||
name: string;
|
||||
username: string;
|
||||
type: "password" | "key";
|
||||
value?: string;
|
||||
publicKey?: string;
|
||||
passphrase?: string;
|
||||
description?: string;
|
||||
folder?: string;
|
||||
tags?: string[];
|
||||
};
|
||||
|
||||
export type HostFolder = {
|
||||
name: string;
|
||||
children: (Host | HostFolder)[];
|
||||
};
|
||||
|
||||
export type TabType =
|
||||
| "dashboard"
|
||||
| "terminal"
|
||||
| "rdp"
|
||||
| "vnc"
|
||||
| "telnet"
|
||||
| "stats"
|
||||
| "files"
|
||||
| "host-manager"
|
||||
| "user-profile"
|
||||
| "admin-settings"
|
||||
| "docker"
|
||||
| "tunnel";
|
||||
|
||||
export type TunnelStatusValue =
|
||||
| "CONNECTED"
|
||||
| "CONNECTING"
|
||||
| "DISCONNECTING"
|
||||
| "DISCONNECTED"
|
||||
| "ERROR"
|
||||
| "WAITING";
|
||||
export type TunnelMode = "local" | "remote" | "dynamic";
|
||||
|
||||
export type Tunnel = {
|
||||
id: string;
|
||||
hostId: string;
|
||||
sourcePort: number;
|
||||
endpointHost: string;
|
||||
endpointPort: number;
|
||||
status: TunnelStatusValue;
|
||||
mode: TunnelMode;
|
||||
reason?: string;
|
||||
retryCount?: number;
|
||||
maxRetries?: number;
|
||||
};
|
||||
|
||||
export type Tab = {
|
||||
id: string;
|
||||
type: TabType;
|
||||
label: string;
|
||||
host?: Host;
|
||||
};
|
||||
|
||||
export type DockerContainerStatus =
|
||||
| "running"
|
||||
| "exited"
|
||||
| "paused"
|
||||
| "created"
|
||||
| "restarting";
|
||||
|
||||
export type DockerContainer = {
|
||||
id: string;
|
||||
name: string;
|
||||
image: string;
|
||||
status: DockerContainerStatus;
|
||||
cpu: number;
|
||||
memory: string;
|
||||
ports: string[];
|
||||
created: string;
|
||||
};
|
||||
|
||||
export type DashboardCardId =
|
||||
| "stats_bar"
|
||||
| "counters_bar"
|
||||
| "quick_actions"
|
||||
| "host_status"
|
||||
| "recent_activity"
|
||||
| "network_graph";
|
||||
|
||||
export type DashboardCardConfig = {
|
||||
id: DashboardCardId;
|
||||
label: string;
|
||||
description: string;
|
||||
defaultEnabled: boolean;
|
||||
};
|
||||
|
||||
export type CardColSpan = "full" | "wide" | "half" | "narrow";
|
||||
export type CardRowSize = "short" | "medium" | "tall" | "flex";
|
||||
|
||||
export type CardLayoutConfig = {
|
||||
id: DashboardCardId;
|
||||
colSpan: CardColSpan;
|
||||
rowSize: CardRowSize;
|
||||
order: number;
|
||||
};
|
||||
|
||||
export type LayoutPresetId = "default" | "compact" | "focus" | "wide";
|
||||
|
||||
export type LayoutPreset = {
|
||||
id: LayoutPresetId;
|
||||
label: string;
|
||||
description: string;
|
||||
cards: CardLayoutConfig[];
|
||||
};
|
||||
|
||||
export type UserProfileSection =
|
||||
| "account"
|
||||
| "appearance"
|
||||
| "security"
|
||||
| "api-keys";
|
||||
export type AdminSection =
|
||||
| "general"
|
||||
| "oidc"
|
||||
| "users"
|
||||
| "sessions"
|
||||
| "roles"
|
||||
| "database"
|
||||
| "api-keys";
|
||||
export type AccentColorId = string;
|
||||
export type ThemeId =
|
||||
| "dark"
|
||||
| "light"
|
||||
| "system"
|
||||
| "dracula"
|
||||
| "catppuccin"
|
||||
| "nord"
|
||||
| "solarized"
|
||||
| "tokyo-night"
|
||||
| "one-dark"
|
||||
| "gruvbox";
|
||||
export type FontSizeId = "xs" | "sm" | "md" | "lg" | "xl";
|
||||
|
||||
export type ToolsTab = "ssh-tools" | "snippets" | "history" | "split-screen";
|
||||
export type SplitMode =
|
||||
| "none"
|
||||
| "2-way"
|
||||
| "3-way"
|
||||
| "4-way"
|
||||
| "5-way"
|
||||
| "6-way";
|
||||
|
||||
export type Snippet = {
|
||||
id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
command: string;
|
||||
folderId: number | null;
|
||||
};
|
||||
|
||||
export const FOLDER_ICONS = [
|
||||
"folder",
|
||||
"server",
|
||||
"cloud",
|
||||
"database",
|
||||
"box",
|
||||
"network",
|
||||
"copy",
|
||||
"settings",
|
||||
"cpu",
|
||||
"globe",
|
||||
] as const;
|
||||
export type FolderIconId = (typeof FOLDER_ICONS)[number];
|
||||
|
||||
export type SnippetFolder = {
|
||||
id: number;
|
||||
name: string;
|
||||
color: string;
|
||||
icon: FolderIconId;
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
export type HistoryEntry = {
|
||||
id: number;
|
||||
command: string;
|
||||
host: string;
|
||||
time: string;
|
||||
};
|
||||
Reference in New Issue
Block a user