FIx frontend old confige editor paths

This commit is contained in:
LukeGus
2025-08-17 19:23:33 -05:00
parent cf945e3665
commit 7d904c4a2c
23 changed files with 118 additions and 113 deletions

View File

@@ -2,8 +2,6 @@ import React, {useState, useEffect} from "react"
import {LeftSidebar} from "@/ui/Navigation/LeftSidebar.tsx" import {LeftSidebar} from "@/ui/Navigation/LeftSidebar.tsx"
import {Homepage} from "@/ui/Homepage/Homepage.tsx" import {Homepage} from "@/ui/Homepage/Homepage.tsx"
import {AppView} from "@/ui/Navigation/AppView.tsx" import {AppView} from "@/ui/Navigation/AppView.tsx"
// import {SSHTunnel} from "@/ui/SSH/Tunnel/SSHTunnel.tsx"
// import {ConfigEditor} from "@/ui/SSH/Config Editor/ConfigEditor.tsx"
import {HostManager} from "@/ui/apps/Host Manager/HostManager.tsx" import {HostManager} from "@/ui/apps/Host Manager/HostManager.tsx"
import {TabProvider, useTabs} from "@/ui/Navigation/Tabs/TabContext.tsx" import {TabProvider, useTabs} from "@/ui/Navigation/Tabs/TabContext.tsx"
import axios from "axios" import axios from "axios"
@@ -88,7 +86,7 @@ function AppContent() {
// Determine what to show based on current tab // Determine what to show based on current tab
const currentTabData = tabs.find(tab => tab.id === currentTab); const currentTabData = tabs.find(tab => tab.id === currentTab);
const showTerminalView = currentTabData?.type === 'terminal' || currentTabData?.type === 'server' || currentTabData?.type === 'config'; const showTerminalView = currentTabData?.type === 'terminal' || currentTabData?.type === 'server' || currentTabData?.type === 'file_manager';
const showHome = currentTabData?.type === 'home'; const showHome = currentTabData?.type === 'home';
const showSshManager = currentTabData?.type === 'ssh_manager'; const showSshManager = currentTabData?.type === 'ssh_manager';
const showAdmin = currentTabData?.type === 'admin'; const showAdmin = currentTabData?.type === 'admin';

View File

@@ -78,7 +78,7 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: TunnelConnection[]; tunnelConnections: TunnelConnection[];
createdAt: string; createdAt: string;

View File

@@ -17,7 +17,7 @@ export function AppView({ isTopbarOpen = true }: TerminalViewProps): React.React
const {tabs, currentTab, allSplitScreenTab} = useTabs() as any; const {tabs, currentTab, allSplitScreenTab} = useTabs() as any;
const { state: sidebarState } = useSidebar(); const { state: sidebarState } = useSidebar();
const terminalTabs = tabs.filter((tab: any) => tab.type === 'terminal' || tab.type === 'server' || tab.type === 'config'); const terminalTabs = tabs.filter((tab: any) => tab.type === 'terminal' || tab.type === 'server' || tab.type === 'file_manager');
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const panelRefs = useRef<Record<string, HTMLDivElement | null>>({}); const panelRefs = useRef<Record<string, HTMLDivElement | null>>({});

View File

@@ -21,7 +21,7 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: any[]; tunnelConnections: any[];
createdAt: string; createdAt: string;

View File

@@ -22,7 +22,7 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: any[]; tunnelConnections: any[];
createdAt: string; createdAt: string;

View File

@@ -67,7 +67,7 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: any[]; tunnelConnections: any[];
createdAt: string; createdAt: string;

View File

@@ -31,9 +31,9 @@ export function Tab({tabType, title, isActive, onActivate, onClose, onSplit, can
); );
} }
if (tabType === "terminal" || tabType === "server" || tabType === "config") { if (tabType === "terminal" || tabType === "server" || tabType === "file_manager") {
const isServer = tabType === 'server'; const isServer = tabType === 'server';
const isConfig = tabType === 'config'; const isFileManager = tabType === 'file_manager';
return ( return (
<ButtonGroup> <ButtonGroup>
<Button <Button
@@ -42,8 +42,8 @@ export function Tab({tabType, title, isActive, onActivate, onClose, onSplit, can
onClick={onActivate} onClick={onActivate}
disabled={disableActivate} disabled={disableActivate}
> >
{isServer ? <ServerIcon className="mr-1 h-4 w-4"/> : isConfig ? <FolderIcon className="mr-1 h-4 w-4"/> : <TerminalIcon className="mr-1 h-4 w-4"/>} {isServer ? <ServerIcon className="mr-1 h-4 w-4"/> : isFileManager ? <FolderIcon className="mr-1 h-4 w-4"/> : <TerminalIcon className="mr-1 h-4 w-4"/>}
{title || (isServer ? 'Server' : isConfig ? 'Config' : 'Terminal')} {title || (isServer ? 'Server' : isFileManager ? 'file_manager' : 'Terminal')}
</Button> </Button>
{canSplit && ( {canSplit && (
<Button <Button

View File

@@ -2,7 +2,7 @@ import React, { createContext, useContext, useState, useRef, type ReactNode } fr
export interface Tab { export interface Tab {
id: number; id: number;
type: 'home' | 'terminal' | 'ssh_manager' | 'server' | 'admin' | 'config'; type: 'home' | 'terminal' | 'ssh_manager' | 'server' | 'admin' | 'file_manager';
title: string; title: string;
hostConfig?: any; hostConfig?: any;
terminalRef?: React.RefObject<any>; terminalRef?: React.RefObject<any>;
@@ -42,7 +42,7 @@ export function TabProvider({ children }: TabProviderProps) {
const nextTabId = useRef(2); const nextTabId = useRef(2);
function computeUniqueTitle(tabType: Tab['type'], desiredTitle: string | undefined): string { function computeUniqueTitle(tabType: Tab['type'], desiredTitle: string | undefined): string {
const defaultTitle = tabType === 'server' ? 'Server' : (tabType === 'config' ? 'Config' : 'Terminal'); const defaultTitle = tabType === 'server' ? 'Server' : (tabType === 'file_manager' ? 'File Manager' : 'Terminal');
const baseTitle = (desiredTitle || defaultTitle).trim(); const baseTitle = (desiredTitle || defaultTitle).trim();
// Extract base name without trailing " (n)" // Extract base name without trailing " (n)"
const match = baseTitle.match(/^(.*) \((\d+)\)$/); const match = baseTitle.match(/^(.*) \((\d+)\)$/);
@@ -72,7 +72,7 @@ export function TabProvider({ children }: TabProviderProps) {
const addTab = (tabData: Omit<Tab, 'id'>): number => { const addTab = (tabData: Omit<Tab, 'id'>): number => {
const id = nextTabId.current++; const id = nextTabId.current++;
const needsUniqueTitle = tabData.type === 'terminal' || tabData.type === 'server' || tabData.type === 'config'; const needsUniqueTitle = tabData.type === 'terminal' || tabData.type === 'server' || tabData.type === 'file_manager';
const effectiveTitle = needsUniqueTitle ? computeUniqueTitle(tabData.type, tabData.title) : (tabData.title || ''); const effectiveTitle = needsUniqueTitle ? computeUniqueTitle(tabData.type, tabData.title) : (tabData.title || '');
const newTab: Tab = { const newTab: Tab = {
...tabData, ...tabData,

View File

@@ -53,11 +53,11 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
const isSplit = Array.isArray(allSplitScreenTab) && allSplitScreenTab.includes(tab.id); const isSplit = Array.isArray(allSplitScreenTab) && allSplitScreenTab.includes(tab.id);
const isTerminal = tab.type === 'terminal'; const isTerminal = tab.type === 'terminal';
const isServer = tab.type === 'server'; const isServer = tab.type === 'server';
const isConfig = tab.type === 'config'; const isFileManager = tab.type === 'file_manager';
const isSshManager = tab.type === 'ssh_manager'; const isSshManager = tab.type === 'ssh_manager';
const isAdmin = tab.type === 'admin'; const isAdmin = tab.type === 'admin';
// Split availability // Split availability
const isSplittable = isTerminal || isServer || isConfig; const isSplittable = isTerminal || isServer || isFileManager;
// Disable split entirely when on Home or SSH Manager // Disable split entirely when on Home or SSH Manager
const isSplitButtonDisabled = (isActive && !isSplitScreenActive) || ((allSplitScreenTab?.length || 0) >= 3 && !isSplit); const isSplitButtonDisabled = (isActive && !isSplitScreenActive) || ((allSplitScreenTab?.length || 0) >= 3 && !isSplit);
const disableSplit = !isSplittable || isSplitButtonDisabled || isActive || currentTabIsHome || currentTabIsSshManager || currentTabIsAdmin; const disableSplit = !isSplittable || isSplitButtonDisabled || isActive || currentTabIsHome || currentTabIsSshManager || currentTabIsAdmin;
@@ -70,10 +70,10 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
title={tab.title} title={tab.title}
isActive={isActive} isActive={isActive}
onActivate={() => handleTabActivate(tab.id)} onActivate={() => handleTabActivate(tab.id)}
onClose={isTerminal || isServer || isConfig || isSshManager || isAdmin ? () => handleTabClose(tab.id) : undefined} onClose={isTerminal || isServer || isFileManager || isSshManager || isAdmin ? () => handleTabClose(tab.id) : undefined}
onSplit={isSplittable ? () => handleTabSplit(tab.id) : undefined} onSplit={isSplittable ? () => handleTabSplit(tab.id) : undefined}
canSplit={isSplittable} canSplit={isSplittable}
canClose={isTerminal || isServer || isConfig || isSshManager || isAdmin} canClose={isTerminal || isServer || isFileManager || isSshManager || isAdmin}
disableActivate={disableActivate} disableActivate={disableActivate}
disableSplit={disableSplit} disableSplit={disableSplit}
disableClose={disableClose} disableClose={disableClose}

View File

@@ -7,15 +7,15 @@ import {Button} from '@/components/ui/button.tsx';
import {FIleManagerTopNavbar} from "@/ui/apps/File Manager/FIleManagerTopNavbar.tsx"; import {FIleManagerTopNavbar} from "@/ui/apps/File Manager/FIleManagerTopNavbar.tsx";
import {cn} from '@/lib/utils.ts'; import {cn} from '@/lib/utils.ts';
import { import {
getConfigEditorRecent, getFileManagerRecent,
getConfigEditorPinned, getFileManagerPinned,
getConfigEditorShortcuts, getFileManagerShortcuts,
addConfigEditorRecent, addFileManagerRecent,
removeConfigEditorRecent, removeFileManagerRecent,
addConfigEditorPinned, addFileManagerPinned,
removeConfigEditorPinned, removeFileManagerPinned,
addConfigEditorShortcut, addFileManagerShortcut,
removeConfigEditorShortcut, removeFileManagerShortcut,
readSSHFile, readSSHFile,
writeSSHFile, writeSSHFile,
getSSHStatus, getSSHStatus,
@@ -52,14 +52,18 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: any[]; tunnelConnections: any[];
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
} }
export function FileManager({onSelectView, embedded = false, initialHost = null}: { onSelectView?: (view: string) => void, embedded?: boolean, initialHost?: SSHHost | null }): React.ReactElement { export function FileManager({onSelectView, embedded = false, initialHost = null}: {
onSelectView?: (view: string) => void,
embedded?: boolean,
initialHost?: SSHHost | null
}): React.ReactElement {
const [tabs, setTabs] = useState<Tab[]>([]); const [tabs, setTabs] = useState<Tab[]>([]);
const [activeTab, setActiveTab] = useState<string | number>('home'); const [activeTab, setActiveTab] = useState<string | number>('home');
const [recent, setRecent] = useState<any[]>([]); const [recent, setRecent] = useState<any[]>([]);
@@ -118,9 +122,9 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
try { try {
const homeDataPromise = Promise.all([ const homeDataPromise = Promise.all([
getConfigEditorRecent(currentHost.id), getFileManagerRecent(currentHost.id),
getConfigEditorPinned(currentHost.id), getFileManagerPinned(currentHost.id),
getConfigEditorShortcuts(currentHost.id), getFileManagerShortcuts(currentHost.id),
]); ]);
const timeoutPromise = new Promise((_, reject) => const timeoutPromise = new Promise((_, reject) =>
@@ -197,7 +201,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
loading: false, loading: false,
error: undefined error: undefined
} : t)); } : t));
await addConfigEditorRecent({ await addFileManagerRecent({
name: file.name, name: file.name,
path: file.path, path: file.path,
isSSH: true, isSSH: true,
@@ -215,7 +219,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
const handleRemoveRecent = async (file: any) => { const handleRemoveRecent = async (file: any) => {
try { try {
await removeConfigEditorRecent({ await removeFileManagerRecent({
name: file.name, name: file.name,
path: file.path, path: file.path,
isSSH: true, isSSH: true,
@@ -229,7 +233,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
const handlePinFile = async (file: any) => { const handlePinFile = async (file: any) => {
try { try {
await addConfigEditorPinned({ await addFileManagerPinned({
name: file.name, name: file.name,
path: file.path, path: file.path,
isSSH: true, isSSH: true,
@@ -246,7 +250,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
const handleUnpinFile = async (file: any) => { const handleUnpinFile = async (file: any) => {
try { try {
await removeConfigEditorPinned({ await removeFileManagerPinned({
name: file.name, name: file.name,
path: file.path, path: file.path,
isSSH: true, isSSH: true,
@@ -286,7 +290,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
const handleAddShortcut = async (folderPath: string) => { const handleAddShortcut = async (folderPath: string) => {
try { try {
const name = folderPath.split('/').pop() || folderPath; const name = folderPath.split('/').pop() || folderPath;
await addConfigEditorShortcut({ await addFileManagerShortcut({
name, name,
path: folderPath, path: folderPath,
isSSH: true, isSSH: true,
@@ -300,7 +304,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
const handleRemoveShortcut = async (shortcut: any) => { const handleRemoveShortcut = async (shortcut: any) => {
try { try {
await removeConfigEditorShortcut({ await removeFileManagerShortcut({
name: shortcut.name, name: shortcut.name,
path: shortcut.path, path: shortcut.path,
isSSH: true, isSSH: true,
@@ -402,7 +406,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
Promise.allSettled([ Promise.allSettled([
(async () => { (async () => {
try { try {
await addConfigEditorRecent({ await addFileManagerRecent({
name: tab.fileName, name: tab.fileName,
path: tab.filePath, path: tab.filePath,
isSSH: true, isSSH: true,
@@ -445,14 +449,16 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
}; };
// Host is locked; no external host change from UI // Host is locked; no external host change from UI
const handleHostChange = (_host: SSHHost | null) => {}; const handleHostChange = (_host: SSHHost | null) => {
};
if (!currentHost) { if (!currentHost) {
return ( return (
<div style={{position: 'relative', width: '100%', height: '100%', overflow: 'hidden'}}> <div style={{position: 'relative', width: '100%', height: '100%', overflow: 'hidden'}}>
<div style={{position: 'absolute', top: 0, left: 0, width: 256, height: '100%', zIndex: 20}}> <div style={{position: 'absolute', top: 0, left: 0, width: 256, height: '100%', zIndex: 20}}>
<FileManagerLeftSidebar <FileManagerLeftSidebar
onSelectView={onSelectView || (() => {})} onSelectView={onSelectView || (() => {
})}
onOpenFile={handleOpenFile} onOpenFile={handleOpenFile}
tabs={tabs} tabs={tabs}
ref={sidebarRef} ref={sidebarRef}
@@ -483,7 +489,8 @@ export function FileManager({onSelectView, embedded = false, initialHost = null}
<div style={{position: 'relative', width: '100%', height: '100%', overflow: 'hidden'}}> <div style={{position: 'relative', width: '100%', height: '100%', overflow: 'hidden'}}>
<div style={{position: 'absolute', top: 0, left: 0, width: 256, height: '100%', zIndex: 20}}> <div style={{position: 'absolute', top: 0, left: 0, width: 256, height: '100%', zIndex: 20}}>
<FileManagerLeftSidebar <FileManagerLeftSidebar
onSelectView={onSelectView || (() => {})} onSelectView={onSelectView || (() => {
})}
onOpenFile={handleOpenFile} onOpenFile={handleOpenFile}
tabs={tabs} tabs={tabs}
ref={sidebarRef} ref={sidebarRef}

View File

@@ -5,13 +5,13 @@ import {hyperLink} from '@uiw/codemirror-extensions-hyper-link';
import {oneDark} from '@codemirror/theme-one-dark'; import {oneDark} from '@codemirror/theme-one-dark';
import {EditorView} from '@codemirror/view'; import {EditorView} from '@codemirror/view';
interface ConfigCodeEditorProps { interface FileManagerCodeEditorProps {
content: string; content: string;
fileName: string; fileName: string;
onContentChange: (value: string) => void; onContentChange: (value: string) => void;
} }
export function FileManagerFileEditor({content, fileName, onContentChange}: ConfigCodeEditorProps) { export function FileManagerFileEditor({content, fileName, onContentChange}: FileManagerCodeEditorProps) {
function getLanguageName(filename: string): string { function getLanguageName(filename: string): string {
if (!filename || typeof filename !== 'string') { if (!filename || typeof filename !== 'string') {
return 'text'; return 'text';

View File

@@ -18,7 +18,7 @@ interface ShortcutItem {
path: string; path: string;
} }
interface ConfigHomeViewProps { interface FileManagerHomeViewProps {
recent: FileItem[]; recent: FileItem[];
pinned: FileItem[]; pinned: FileItem[];
shortcuts: ShortcutItem[]; shortcuts: ShortcutItem[];
@@ -42,7 +42,7 @@ export function FileManagerHomeView({
onOpenShortcut, onOpenShortcut,
onRemoveShortcut, onRemoveShortcut,
onAddShortcut onAddShortcut
}: ConfigHomeViewProps) { }: FileManagerHomeViewProps) {
const [tab, setTab] = useState<'recent' | 'pinned' | 'shortcuts'>('recent'); const [tab, setTab] = useState<'recent' | 'pinned' | 'shortcuts'>('recent');
const [newShortcut, setNewShortcut] = useState(''); const [newShortcut, setNewShortcut] = useState('');

View File

@@ -9,9 +9,9 @@ import {
listSSHFiles, listSSHFiles,
connectSSH, connectSSH,
getSSHStatus, getSSHStatus,
getConfigEditorPinned, getFileManagerPinned,
addConfigEditorPinned, addFileManagerPinned,
removeConfigEditorPinned removeFileManagerPinned
} from '@/ui/main-axios.ts'; } from '@/ui/main-axios.ts';
interface SSHHost { interface SSHHost {
@@ -30,14 +30,14 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: any[]; tunnelConnections: any[];
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
} }
const FileManagerLeftSidebar = forwardRef(function ConfigEditorSidebar( const FileManagerLeftSidebar = forwardRef(function FileManagerSidebar(
{onSelectView, onOpenFile, tabs, host}: { {onSelectView, onOpenFile, tabs, host}: {
onSelectView?: (view: string) => void; onSelectView?: (view: string) => void;
onOpenFile: (file: any) => void; onOpenFile: (file: any) => void;
@@ -146,7 +146,7 @@ const FileManagerLeftSidebar = forwardRef(function ConfigEditorSidebar(
let pinnedFiles: any[] = []; let pinnedFiles: any[] = [];
try { try {
if (host) { if (host) {
pinnedFiles = await getConfigEditorPinned(host.id); pinnedFiles = await getFileManagerPinned(host.id);
} }
} catch (err) { } catch (err) {
} }
@@ -336,7 +336,7 @@ const FileManagerLeftSidebar = forwardRef(function ConfigEditorSidebar(
e.stopPropagation(); e.stopPropagation();
try { try {
if (item.isPinned) { if (item.isPinned) {
await removeConfigEditorPinned({ await removeFileManagerPinned({
name: item.name, name: item.name,
path: item.path, path: item.path,
hostId: host?.id, hostId: host?.id,
@@ -347,7 +347,7 @@ const FileManagerLeftSidebar = forwardRef(function ConfigEditorSidebar(
f.path === item.path ? { ...f, isPinned: false } : f f.path === item.path ? { ...f, isPinned: false } : f
)); ));
} else { } else {
await addConfigEditorPinned({ await addFileManagerPinned({
name: item.name, name: item.name,
path: item.path, path: item.path,
hostId: host?.id, hostId: host?.id,

View File

@@ -20,7 +20,7 @@ interface FileItem {
isStarred?: boolean; isStarred?: boolean;
} }
interface ConfigFileSidebarViewerProps { interface FileManagerLeftSidebarVileViewerProps {
sshConnections: SSHConnection[]; sshConnections: SSHConnection[];
onAddSSH: () => void; onAddSSH: () => void;
onConnectSSH: (conn: SSHConnection) => void; onConnectSSH: (conn: SSHConnection) => void;
@@ -60,7 +60,7 @@ export function FileManagerLeftSidebarFileViewer({
onSwitchToLocal, onSwitchToLocal,
onSwitchToSSH, onSwitchToSSH,
currentSSH, currentSSH,
}: ConfigFileSidebarViewerProps) { }: FileManagerLeftSidebarVileViewerProps) {
return ( return (
<div className="flex flex-col h-full"> <div className="flex flex-col h-full">
{/* SSH Connections */} {/* SSH Connections */}

View File

@@ -2,20 +2,20 @@ import React from 'react';
import {Button} from '@/components/ui/button.tsx'; import {Button} from '@/components/ui/button.tsx';
import {X, Home} from 'lucide-react'; import {X, Home} from 'lucide-react';
interface ConfigTab { interface FileManagerTab {
id: string | number; id: string | number;
title: string; title: string;
} }
interface ConfigTabListProps { interface FileManagerTabList {
tabs: ConfigTab[]; tabs: FileManagerTab[];
activeTab: string | number; activeTab: string | number;
setActiveTab: (tab: string | number) => void; setActiveTab: (tab: string | number) => void;
closeTab: (tab: string | number) => void; closeTab: (tab: string | number) => void;
onHomeClick: () => void; onHomeClick: () => void;
} }
export function FileManagerTabList({tabs, activeTab, setActiveTab, closeTab, onHomeClick}: ConfigTabListProps) { export function FileManagerTabList({tabs, activeTab, setActiveTab, closeTab, onHomeClick}: FileManagerTabList) {
return ( return (
<div className="inline-flex items-center h-full px-[0.5rem] overflow-x-auto"> <div className="inline-flex items-center h-full px-[0.5rem] overflow-x-auto">
<Button <Button

View File

@@ -5,7 +5,7 @@ import {Separator} from "@/components/ui/separator.tsx";
import {HostManagerHostEditor} from "@/ui/apps/Host Manager/HostManagerHostEditor.tsx"; import {HostManagerHostEditor} from "@/ui/apps/Host Manager/HostManagerHostEditor.tsx";
import {useSidebar} from "@/components/ui/sidebar.tsx"; import {useSidebar} from "@/components/ui/sidebar.tsx";
interface ConfigEditorProps { interface HostManagerProps {
onSelectView: (view: string) => void; onSelectView: (view: string) => void;
isTopbarOpen?: boolean; isTopbarOpen?: boolean;
} }
@@ -26,14 +26,14 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: any[]; tunnelConnections: any[];
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
} }
export function HostManager({onSelectView, isTopbarOpen}: ConfigEditorProps): React.ReactElement { export function HostManager({onSelectView, isTopbarOpen}: HostManagerProps): React.ReactElement {
const [activeTab, setActiveTab] = useState("host_viewer"); const [activeTab, setActiveTab] = useState("host_viewer");
const [editingHost, setEditingHost] = useState<SSHHost | null>(null); const [editingHost, setEditingHost] = useState<SSHHost | null>(null);
const {state: sidebarState} = useSidebar(); const {state: sidebarState} = useSidebar();

View File

@@ -37,7 +37,7 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: any[]; tunnelConnections: any[];
createdAt: string; createdAt: string;
@@ -120,7 +120,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
retryInterval: z.coerce.number().min(1).max(3600).default(10), retryInterval: z.coerce.number().min(1).max(3600).default(10),
autoStart: z.boolean().default(false), autoStart: z.boolean().default(false),
})).default([]), })).default([]),
enableConfigEditor: z.boolean().default(true), enableFileManager: z.boolean().default(true),
defaultPath: z.string().optional(), defaultPath: z.string().optional(),
}).superRefine((data, ctx) => { }).superRefine((data, ctx) => {
if (data.authType === 'password') { if (data.authType === 'password') {
@@ -178,7 +178,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
keyType: "auto", keyType: "auto",
enableTerminal: editingHost?.enableTerminal !== false, enableTerminal: editingHost?.enableTerminal !== false,
enableTunnel: editingHost?.enableTunnel !== false, enableTunnel: editingHost?.enableTunnel !== false,
enableConfigEditor: editingHost?.enableConfigEditor !== false, enableFileManager: editingHost?.enableFileManager !== false,
defaultPath: editingHost?.defaultPath || "/", defaultPath: editingHost?.defaultPath || "/",
tunnelConnections: editingHost?.tunnelConnections || [], tunnelConnections: editingHost?.tunnelConnections || [],
} }
@@ -205,7 +205,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
keyType: (editingHost.keyType as any) || "auto", keyType: (editingHost.keyType as any) || "auto",
enableTerminal: editingHost.enableTerminal !== false, enableTerminal: editingHost.enableTerminal !== false,
enableTunnel: editingHost.enableTunnel !== false, enableTunnel: editingHost.enableTunnel !== false,
enableConfigEditor: editingHost.enableConfigEditor !== false, enableFileManager: editingHost.enableFileManager !== false,
defaultPath: editingHost.defaultPath || "/", defaultPath: editingHost.defaultPath || "/",
tunnelConnections: editingHost.tunnelConnections || [], tunnelConnections: editingHost.tunnelConnections || [],
}); });
@@ -227,7 +227,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
keyType: "auto", keyType: "auto",
enableTerminal: true, enableTerminal: true,
enableTunnel: true, enableTunnel: true,
enableConfigEditor: true, enableFileManager: true,
defaultPath: "/", defaultPath: "/",
tunnelConnections: [], tunnelConnections: [],
}); });
@@ -989,7 +989,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
<TabsContent value="file_manager"> <TabsContent value="file_manager">
<FormField <FormField
control={form.control} control={form.control}
name="enableConfigEditor" name="enableFileManager"
render={({field}) => ( render={({field}) => (
<FormItem> <FormItem>
<FormLabel>Enable File Manager</FormLabel> <FormLabel>Enable File Manager</FormLabel>
@@ -1006,7 +1006,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
)} )}
/> />
{form.watch('enableConfigEditor') && ( {form.watch('enableFileManager') && (
<div className="mt-4"> <div className="mt-4">
<FormField <FormField
control={form.control} control={form.control}

View File

@@ -35,7 +35,7 @@ interface SSHHost {
authType: string; authType: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: any[]; tunnelConnections: any[];
createdAt: string; createdAt: string;
@@ -275,7 +275,7 @@ export function HostManagerHostViewer({onEditHost}: SSHManagerHostViewerProps) {
pin: true, pin: true,
enableTerminal: true, enableTerminal: true,
enableTunnel: false, enableTunnel: false,
enableConfigEditor: true, enableFileManager: true,
defaultPath: "/var/www" defaultPath: "/var/www"
}, },
{ {
@@ -292,7 +292,7 @@ export function HostManagerHostViewer({onEditHost}: SSHManagerHostViewerProps) {
pin: false, pin: false,
enableTerminal: true, enableTerminal: true,
enableTunnel: true, enableTunnel: true,
enableConfigEditor: false, enableFileManager: false,
tunnelConnections: [ tunnelConnections: [
{ {
sourcePort: 5432, sourcePort: 5432,
@@ -347,7 +347,7 @@ OPTIONAL FIELDS:
• pin: Pin to top (boolean) • pin: Pin to top (boolean)
• enableTerminal: Show in Terminal tab (boolean, default: true) • enableTerminal: Show in Terminal tab (boolean, default: true)
• enableTunnel: Show in Tunnel tab (boolean, default: true) • enableTunnel: Show in Tunnel tab (boolean, default: true)
• enableConfigEditor: Show in Config Editor tab (boolean, default: true) • enableFileManager: Show in File Manager tab (boolean, default: true)
• defaultPath: Default directory path (string) • defaultPath: Default directory path (string)
TUNNEL CONFIGURATION: TUNNEL CONFIGURATION:
@@ -374,7 +374,7 @@ EXAMPLE STRUCTURE:
"pin": true, "pin": true,
"enableTerminal": true, "enableTerminal": true,
"enableTunnel": false, "enableTunnel": false,
"enableConfigEditor": true, "enableFileManager": true,
"defaultPath": "/var/www" "defaultPath": "/var/www"
} }
] ]
@@ -500,8 +500,8 @@ EXAMPLE STRUCTURE:
<button class="copy-btn" onclick="navigator.clipboard.writeText('enableTunnel')">Copy</button> <button class="copy-btn" onclick="navigator.clipboard.writeText('enableTunnel')">Copy</button>
</div> </div>
<div class="field-item"> <div class="field-item">
<code>enableConfigEditor</code> - Show in Config Editor tab (boolean, default: true) <code>enableFileManager</code> - Show in File Manager tab (boolean, default: true)
<button class="copy-btn" onclick="navigator.clipboard.writeText('enableConfigEditor')">Copy</button> <button class="copy-btn" onclick="navigator.clipboard.writeText('enableFileManager')">Copy</button>
</div> </div>
<div class="field-item"> <div class="field-item">
<code>defaultPath</code> - Default directory path (string) <code>defaultPath</code> - Default directory path (string)
@@ -558,7 +558,7 @@ EXAMPLE STRUCTURE:
"pin": true, "pin": true,
"enableTerminal": true, "enableTerminal": true,
"enableTunnel": false, "enableTunnel": false,
"enableConfigEditor": true, "enableFileManager": true,
"defaultPath": "/var/www" "defaultPath": "/var/www"
} }
] ]
@@ -709,10 +709,10 @@ EXAMPLE STRUCTURE:
)} )}
</Badge> </Badge>
)} )}
{host.enableConfigEditor && ( {host.enableFileManager && (
<Badge variant="outline" className="text-xs px-1 py-0"> <Badge variant="outline" className="text-xs px-1 py-0">
<FileEdit className="h-2 w-2 mr-0.5"/> <FileEdit className="h-2 w-2 mr-0.5"/>
Config File Manager
</Badge> </Badge>
)} )}
</div> </div>

View File

@@ -145,7 +145,7 @@ export function Server({ hostConfig, title, isVisible = true, isTopbarOpen = tru
</Status> </Status>
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
{currentHostConfig?.enableConfigEditor && ( {currentHostConfig?.enableFileManager && (
<Button <Button
variant="outline" variant="outline"
className="font-semibold" className="font-semibold"
@@ -155,7 +155,7 @@ export function Server({ hostConfig, title, isVisible = true, isTopbarOpen = tru
? currentHostConfig.name.trim() ? currentHostConfig.name.trim()
: `${currentHostConfig.username}@${currentHostConfig.ip}`; : `${currentHostConfig.username}@${currentHostConfig.ip}`;
addTab({ addTab({
type: 'config', type: 'file_manager',
title: titleBase, title: titleBase,
hostConfig: currentHostConfig, hostConfig: currentHostConfig,
}); });
@@ -194,7 +194,7 @@ export function Server({ hostConfig, title, isVisible = true, isTopbarOpen = tru
{/* Memory */} {/* Memory */}
<div className="flex-1 min-w-0 px-2 py-2"> <div className="flex-1 min-w-0 px-2 py-2">
<h1 className="font-bold text-lg flex flex-row gap-2 mb-1"> <h1 className="font-bold xt-lg flex flex-row gap-2 mb-1">
<MemoryStick/> <MemoryStick/>
{(() => { {(() => {
const pct = metrics?.memory?.percent; const pct = metrics?.memory?.percent;

View File

@@ -27,7 +27,7 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: TunnelConnection[]; tunnelConnections: TunnelConnection[];
createdAt: string; createdAt: string;

View File

@@ -53,7 +53,7 @@ interface SSHHost {
authType: string; authType: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: TunnelConnection[]; tunnelConnections: TunnelConnection[];
createdAt: string; createdAt: string;

View File

@@ -22,7 +22,7 @@ interface SSHHost {
authType: string; authType: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: TunnelConnection[]; tunnelConnections: TunnelConnection[];
createdAt: string; createdAt: string;

View File

@@ -15,7 +15,7 @@ interface SSHHostData {
keyType?: string; keyType?: string;
enableTerminal?: boolean; enableTerminal?: boolean;
enableTunnel?: boolean; enableTunnel?: boolean;
enableConfigEditor?: boolean; enableFileManager?: boolean;
defaultPath?: string; defaultPath?: string;
tunnelConnections?: any[]; tunnelConnections?: any[];
} }
@@ -36,7 +36,7 @@ interface SSHHost {
keyType?: string; keyType?: string;
enableTerminal: boolean; enableTerminal: boolean;
enableTunnel: boolean; enableTunnel: boolean;
enableConfigEditor: boolean; enableFileManager: boolean;
defaultPath: string; defaultPath: string;
tunnelConnections: any[]; tunnelConnections: any[];
createdAt: string; createdAt: string;
@@ -80,7 +80,7 @@ interface TunnelStatus {
retryExhausted?: boolean; retryExhausted?: boolean;
} }
interface ConfigEditorFile { interface FileManagerFile {
name: string; name: string;
path: string; path: string;
type?: 'file' | 'directory'; type?: 'file' | 'directory';
@@ -88,7 +88,7 @@ interface ConfigEditorFile {
sshSessionId?: string; sshSessionId?: string;
} }
interface ConfigEditorShortcut { interface FileManagerShortcut {
name: string; name: string;
path: string; path: string;
} }
@@ -121,7 +121,7 @@ const tunnelApi = axios.create({
}, },
}); });
const configEditorApi = axios.create({ const fileManagerApi = axios.create({
baseURL: isLocalhost ? 'http://localhost:8084' : '', baseURL: isLocalhost ? 'http://localhost:8084' : '',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -165,7 +165,7 @@ tunnelApi.interceptors.request.use((config) => {
return config; return config;
}); });
configEditorApi.interceptors.request.use((config) => { fileManagerApi.interceptors.request.use((config) => {
const token = getCookie('jwt'); const token = getCookie('jwt');
if (token) { if (token) {
config.headers.Authorization = `Bearer ${token}`; config.headers.Authorization = `Bearer ${token}`;
@@ -199,7 +199,7 @@ export async function createSSHHost(hostData: SSHHostData): Promise<SSHHost> {
keyType: hostData.authType === 'key' ? hostData.keyType : '', keyType: hostData.authType === 'key' ? hostData.keyType : '',
enableTerminal: hostData.enableTerminal !== false, enableTerminal: hostData.enableTerminal !== false,
enableTunnel: hostData.enableTunnel !== false, enableTunnel: hostData.enableTunnel !== false,
enableConfigEditor: hostData.enableConfigEditor !== false, enableFileManager: hostData.enableFileManager !== false,
defaultPath: hostData.defaultPath || '/', defaultPath: hostData.defaultPath || '/',
tunnelConnections: hostData.tunnelConnections || [], tunnelConnections: hostData.tunnelConnections || [],
}; };
@@ -208,7 +208,7 @@ export async function createSSHHost(hostData: SSHHostData): Promise<SSHHost> {
submitData.tunnelConnections = []; submitData.tunnelConnections = [];
} }
if (!submitData.enableConfigEditor) { if (!submitData.enableFileManager) {
submitData.defaultPath = ''; submitData.defaultPath = '';
} }
@@ -253,7 +253,7 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom
keyType: hostData.authType === 'key' ? hostData.keyType : '', keyType: hostData.authType === 'key' ? hostData.keyType : '',
enableTerminal: hostData.enableTerminal !== false, enableTerminal: hostData.enableTerminal !== false,
enableTunnel: hostData.enableTunnel !== false, enableTunnel: hostData.enableTunnel !== false,
enableConfigEditor: hostData.enableConfigEditor !== false, enableFileManager: hostData.enableFileManager !== false,
defaultPath: hostData.defaultPath || '/', defaultPath: hostData.defaultPath || '/',
tunnelConnections: hostData.tunnelConnections || [], tunnelConnections: hostData.tunnelConnections || [],
}; };
@@ -261,7 +261,7 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom
if (!submitData.enableTunnel) { if (!submitData.enableTunnel) {
submitData.tunnelConnections = []; submitData.tunnelConnections = [];
} }
if (!submitData.enableConfigEditor) { if (!submitData.enableFileManager) {
submitData.defaultPath = ''; submitData.defaultPath = '';
} }
@@ -362,7 +362,7 @@ export async function cancelTunnel(tunnelName: string): Promise<any> {
} }
} }
export async function getConfigEditorRecent(hostId: number): Promise<ConfigEditorFile[]> { export async function getFileManagerRecent(hostId: number): Promise<FileManagerFile[]> {
try { try {
const response = await sshHostApi.get(`/ssh/file_manager/recent?hostId=${hostId}`); const response = await sshHostApi.get(`/ssh/file_manager/recent?hostId=${hostId}`);
return response.data || []; return response.data || [];
@@ -371,7 +371,7 @@ export async function getConfigEditorRecent(hostId: number): Promise<ConfigEdito
} }
} }
export async function addConfigEditorRecent(file: { export async function addFileManagerRecent(file: {
name: string; name: string;
path: string; path: string;
isSSH: boolean; isSSH: boolean;
@@ -386,7 +386,7 @@ export async function addConfigEditorRecent(file: {
} }
} }
export async function removeConfigEditorRecent(file: { export async function removeFileManagerRecent(file: {
name: string; name: string;
path: string; path: string;
isSSH: boolean; isSSH: boolean;
@@ -401,7 +401,7 @@ export async function removeConfigEditorRecent(file: {
} }
} }
export async function getConfigEditorPinned(hostId: number): Promise<ConfigEditorFile[]> { export async function getFileManagerPinned(hostId: number): Promise<FileManagerFile[]> {
try { try {
const response = await sshHostApi.get(`/ssh/file_manager/pinned?hostId=${hostId}`); const response = await sshHostApi.get(`/ssh/file_manager/pinned?hostId=${hostId}`);
return response.data || []; return response.data || [];
@@ -410,7 +410,7 @@ export async function getConfigEditorPinned(hostId: number): Promise<ConfigEdito
} }
} }
export async function addConfigEditorPinned(file: { export async function addFileManagerPinned(file: {
name: string; name: string;
path: string; path: string;
isSSH: boolean; isSSH: boolean;
@@ -425,7 +425,7 @@ export async function addConfigEditorPinned(file: {
} }
} }
export async function removeConfigEditorPinned(file: { export async function removeFileManagerPinned(file: {
name: string; name: string;
path: string; path: string;
isSSH: boolean; isSSH: boolean;
@@ -440,7 +440,7 @@ export async function removeConfigEditorPinned(file: {
} }
} }
export async function getConfigEditorShortcuts(hostId: number): Promise<ConfigEditorShortcut[]> { export async function getFileManagerShortcuts(hostId: number): Promise<FileManagerShortcut[]> {
try { try {
const response = await sshHostApi.get(`/ssh/file_manager/shortcuts?hostId=${hostId}`); const response = await sshHostApi.get(`/ssh/file_manager/shortcuts?hostId=${hostId}`);
return response.data || []; return response.data || [];
@@ -449,7 +449,7 @@ export async function getConfigEditorShortcuts(hostId: number): Promise<ConfigEd
} }
} }
export async function addConfigEditorShortcut(shortcut: { export async function addFileManagerShortcut(shortcut: {
name: string; name: string;
path: string; path: string;
isSSH: boolean; isSSH: boolean;
@@ -464,7 +464,7 @@ export async function addConfigEditorShortcut(shortcut: {
} }
} }
export async function removeConfigEditorShortcut(shortcut: { export async function removeFileManagerShortcut(shortcut: {
name: string; name: string;
path: string; path: string;
isSSH: boolean; isSSH: boolean;
@@ -488,7 +488,7 @@ export async function connectSSH(sessionId: string, config: {
keyPassword?: string; keyPassword?: string;
}): Promise<any> { }): Promise<any> {
try { try {
const response = await configEditorApi.post('/ssh/file_manager/ssh/connect', { const response = await fileManagerApi.post('/ssh/file_manager/ssh/connect', {
sessionId, sessionId,
...config ...config
}); });
@@ -500,7 +500,7 @@ export async function connectSSH(sessionId: string, config: {
export async function disconnectSSH(sessionId: string): Promise<any> { export async function disconnectSSH(sessionId: string): Promise<any> {
try { try {
const response = await configEditorApi.post('/ssh/file_manager/ssh/disconnect', {sessionId}); const response = await fileManagerApi.post('/ssh/file_manager/ssh/disconnect', {sessionId});
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;
@@ -509,7 +509,7 @@ export async function disconnectSSH(sessionId: string): Promise<any> {
export async function getSSHStatus(sessionId: string): Promise<{ connected: boolean }> { export async function getSSHStatus(sessionId: string): Promise<{ connected: boolean }> {
try { try {
const response = await configEditorApi.get('/ssh/file_manager/ssh/status', { const response = await fileManagerApi.get('/ssh/file_manager/ssh/status', {
params: {sessionId} params: {sessionId}
}); });
return response.data; return response.data;
@@ -520,7 +520,7 @@ export async function getSSHStatus(sessionId: string): Promise<{ connected: bool
export async function listSSHFiles(sessionId: string, path: string): Promise<any[]> { export async function listSSHFiles(sessionId: string, path: string): Promise<any[]> {
try { try {
const response = await configEditorApi.get('/ssh/file_manager/ssh/listFiles', { const response = await fileManagerApi.get('/ssh/file_manager/ssh/listFiles', {
params: {sessionId, path} params: {sessionId, path}
}); });
return response.data || []; return response.data || [];
@@ -531,7 +531,7 @@ export async function listSSHFiles(sessionId: string, path: string): Promise<any
export async function readSSHFile(sessionId: string, path: string): Promise<{ content: string; path: string }> { export async function readSSHFile(sessionId: string, path: string): Promise<{ content: string; path: string }> {
try { try {
const response = await configEditorApi.get('/ssh/file_manager/ssh/readFile', { const response = await fileManagerApi.get('/ssh/file_manager/ssh/readFile', {
params: {sessionId, path} params: {sessionId, path}
}); });
return response.data; return response.data;
@@ -542,7 +542,7 @@ export async function readSSHFile(sessionId: string, path: string): Promise<{ co
export async function writeSSHFile(sessionId: string, path: string, content: string): Promise<any> { export async function writeSSHFile(sessionId: string, path: string, content: string): Promise<any> {
try { try {
const response = await configEditorApi.post('/ssh/file_manager/ssh/writeFile', { const response = await fileManagerApi.post('/ssh/file_manager/ssh/writeFile', {
sessionId, sessionId,
path, path,
content content
@@ -558,7 +558,7 @@ export async function writeSSHFile(sessionId: string, path: string, content: str
} }
} }
export {sshHostApi, tunnelApi, configEditorApi}; export {sshHostApi, tunnelApi, fileManagerApi};
export async function getAllServerStatuses(): Promise<Record<number, ServerStatus>> { export async function getAllServerStatuses(): Promise<Record<number, ServerStatus>> {
try { try {