mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-16 05:13:36 +00:00
Finalized ssh config editor
This commit is contained in:
+228
-54
@@ -83,28 +83,30 @@ interface TunnelStatus {
|
||||
|
||||
// Determine the base URL based on environment
|
||||
const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
|
||||
const baseURL = isLocalhost ? 'http://localhost:8081' : window.location.origin;
|
||||
|
||||
// Create separate axios instances for different services
|
||||
const sshHostApi = axios.create({
|
||||
baseURL: isLocalhost ? 'http://localhost:8081' : window.location.origin,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const tunnelApi = axios.create({
|
||||
baseURL: isLocalhost ? 'http://localhost:8083' : window.location.origin,
|
||||
// Create axios instance with base configuration for database operations (port 8081)
|
||||
const api = axios.create({
|
||||
baseURL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Create config editor API instance for file operations (port 8084)
|
||||
const configEditorApi = axios.create({
|
||||
baseURL: isLocalhost ? 'http://localhost:8084' : window.location.origin,
|
||||
baseURL: isLocalhost ? 'http://localhost:8084' : `${window.location.origin}/ssh`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
// Create tunnel API instance
|
||||
const tunnelApi = axios.create({
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
function getCookie(name: string): string | undefined {
|
||||
const value = `; ${document.cookie}`;
|
||||
@@ -112,8 +114,16 @@ function getCookie(name: string): string | undefined {
|
||||
if (parts.length === 2) return parts.pop()?.split(';').shift();
|
||||
}
|
||||
|
||||
// Add request interceptor to include JWT token for SSH Host API
|
||||
sshHostApi.interceptors.request.use((config) => {
|
||||
// Add request interceptor to include JWT token for all API instances
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = getCookie('jwt');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
configEditorApi.interceptors.request.use((config) => {
|
||||
const token = getCookie('jwt');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
@@ -121,7 +131,6 @@ sshHostApi.interceptors.request.use((config) => {
|
||||
return config;
|
||||
});
|
||||
|
||||
// Add request interceptor to include JWT token for Tunnel API
|
||||
tunnelApi.interceptors.request.use((config) => {
|
||||
const token = getCookie('jwt');
|
||||
if (token) {
|
||||
@@ -130,12 +139,10 @@ tunnelApi.interceptors.request.use((config) => {
|
||||
return config;
|
||||
});
|
||||
|
||||
// Host-related functions (use port 8081 for localhost)
|
||||
|
||||
// Get all SSH hosts
|
||||
// Get all SSH hosts - FIXED: Changed from /ssh/host to /ssh/db/host
|
||||
export async function getSSHHosts(): Promise<SSHHost[]> {
|
||||
try {
|
||||
const response = await sshHostApi.get('/ssh/db/host');
|
||||
const response = await api.get('/ssh/db/host');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching SSH hosts:', error);
|
||||
@@ -153,44 +160,37 @@ export async function createSSHHost(hostData: SSHHostData): Promise<SSHHost> {
|
||||
port: parseInt(hostData.port.toString()) || 22,
|
||||
username: hostData.username,
|
||||
folder: hostData.folder || '',
|
||||
tags: hostData.tags || [], // Array of strings
|
||||
tags: hostData.tags || [],
|
||||
pin: hostData.pin || false,
|
||||
authMethod: hostData.authType, // Backend expects 'authMethod'
|
||||
authMethod: hostData.authType,
|
||||
password: hostData.authType === 'password' ? hostData.password : '',
|
||||
key: hostData.authType === 'key' ? hostData.key : null,
|
||||
keyPassword: hostData.authType === 'key' ? hostData.keyPassword : '',
|
||||
keyType: hostData.authType === 'key' ? hostData.keyType : '',
|
||||
enableTerminal: hostData.enableTerminal !== false, // Default to true
|
||||
enableTunnel: hostData.enableTunnel !== false, // Default to true
|
||||
enableConfigEditor: hostData.enableConfigEditor !== false, // Default to true
|
||||
enableTerminal: hostData.enableTerminal !== false,
|
||||
enableTunnel: hostData.enableTunnel !== false,
|
||||
enableConfigEditor: hostData.enableConfigEditor !== false,
|
||||
defaultPath: hostData.defaultPath || '/',
|
||||
tunnelConnections: hostData.tunnelConnections || [], // Array of tunnel objects
|
||||
tunnelConnections: hostData.tunnelConnections || [],
|
||||
};
|
||||
|
||||
// If tunnel is disabled, clear tunnel data
|
||||
if (!submitData.enableTunnel) {
|
||||
submitData.tunnelConnections = [];
|
||||
}
|
||||
|
||||
// If config editor is disabled, clear config data
|
||||
if (!submitData.enableConfigEditor) {
|
||||
submitData.defaultPath = '';
|
||||
}
|
||||
|
||||
// Handle file upload for SSH key
|
||||
if (hostData.authType === 'key' && hostData.key instanceof File) {
|
||||
const formData = new FormData();
|
||||
|
||||
// Add the file
|
||||
formData.append('key', hostData.key);
|
||||
|
||||
// Add all other data as JSON string
|
||||
const dataWithoutFile = { ...submitData };
|
||||
delete dataWithoutFile.key;
|
||||
formData.append('data', JSON.stringify(dataWithoutFile));
|
||||
|
||||
// Submit with FormData
|
||||
const response = await sshHostApi.post('/ssh/db/host', formData, {
|
||||
const response = await api.post('/ssh/db/host', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
@@ -198,8 +198,7 @@ export async function createSSHHost(hostData: SSHHostData): Promise<SSHHost> {
|
||||
|
||||
return response.data;
|
||||
} else {
|
||||
// Submit with JSON
|
||||
const response = await sshHostApi.post('/ssh/db/host', submitData);
|
||||
const response = await api.post('/ssh/db/host', submitData);
|
||||
return response.data;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -231,7 +230,6 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom
|
||||
tunnelConnections: hostData.tunnelConnections || [],
|
||||
};
|
||||
|
||||
// Handle disabled features
|
||||
if (!submitData.enableTunnel) {
|
||||
submitData.tunnelConnections = [];
|
||||
}
|
||||
@@ -239,7 +237,6 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom
|
||||
submitData.defaultPath = '';
|
||||
}
|
||||
|
||||
// Handle file upload for SSH key
|
||||
if (hostData.authType === 'key' && hostData.key instanceof File) {
|
||||
const formData = new FormData();
|
||||
formData.append('key', hostData.key);
|
||||
@@ -248,7 +245,7 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom
|
||||
delete dataWithoutFile.key;
|
||||
formData.append('data', JSON.stringify(dataWithoutFile));
|
||||
|
||||
const response = await sshHostApi.put(`/ssh/db/host/${hostId}`, formData, {
|
||||
const response = await api.put(`/ssh/db/host/${hostId}`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
@@ -256,7 +253,7 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom
|
||||
|
||||
return response.data;
|
||||
} else {
|
||||
const response = await sshHostApi.put(`/ssh/db/host/${hostId}`, submitData);
|
||||
const response = await api.put(`/ssh/db/host/${hostId}`, submitData);
|
||||
return response.data;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -268,7 +265,7 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom
|
||||
// Delete SSH host
|
||||
export async function deleteSSHHost(hostId: number): Promise<any> {
|
||||
try {
|
||||
const response = await sshHostApi.delete(`/ssh/db/host/${hostId}`);
|
||||
const response = await api.delete(`/ssh/db/host/${hostId}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error deleting SSH host:', error);
|
||||
@@ -279,7 +276,7 @@ export async function deleteSSHHost(hostId: number): Promise<any> {
|
||||
// Get SSH host by ID
|
||||
export async function getSSHHostById(hostId: number): Promise<SSHHost> {
|
||||
try {
|
||||
const response = await sshHostApi.get(`/ssh/db/host/${hostId}`);
|
||||
const response = await api.get(`/ssh/db/host/${hostId}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching SSH host:', error);
|
||||
@@ -287,12 +284,11 @@ export async function getSSHHostById(hostId: number): Promise<SSHHost> {
|
||||
}
|
||||
}
|
||||
|
||||
// Tunnel-related functions (use port 8083 for localhost)
|
||||
|
||||
// Get all tunnel statuses (per-tunnel)
|
||||
// Tunnel-related functions
|
||||
export async function getTunnelStatuses(): Promise<Record<string, TunnelStatus>> {
|
||||
try {
|
||||
const response = await tunnelApi.get('/ssh/tunnel/status');
|
||||
const tunnelUrl = isLocalhost ? 'http://localhost:8083/status' : `${baseURL}/ssh_tunnel/status`;
|
||||
const response = await tunnelApi.get(tunnelUrl);
|
||||
return response.data || {};
|
||||
} catch (error) {
|
||||
console.error('Error fetching tunnel statuses:', error);
|
||||
@@ -300,16 +296,15 @@ export async function getTunnelStatuses(): Promise<Record<string, TunnelStatus>>
|
||||
}
|
||||
}
|
||||
|
||||
// Get status for a specific tunnel by tunnel name
|
||||
export async function getTunnelStatusByName(tunnelName: string): Promise<TunnelStatus | undefined> {
|
||||
const statuses = await getTunnelStatuses();
|
||||
return statuses[tunnelName];
|
||||
}
|
||||
|
||||
// Connect tunnel (per-tunnel)
|
||||
export async function connectTunnel(tunnelConfig: TunnelConfig): Promise<any> {
|
||||
try {
|
||||
const response = await tunnelApi.post('/ssh/tunnel/connect', tunnelConfig);
|
||||
const tunnelUrl = isLocalhost ? 'http://localhost:8083/connect' : `${baseURL}/ssh_tunnel/connect`;
|
||||
const response = await tunnelApi.post(tunnelUrl, tunnelConfig);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error connecting tunnel:', error);
|
||||
@@ -317,10 +312,10 @@ export async function connectTunnel(tunnelConfig: TunnelConfig): Promise<any> {
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect tunnel (per-tunnel)
|
||||
export async function disconnectTunnel(tunnelName: string): Promise<any> {
|
||||
try {
|
||||
const response = await tunnelApi.post('/ssh/tunnel/disconnect', { tunnelName });
|
||||
const tunnelUrl = isLocalhost ? 'http://localhost:8083/disconnect' : `${baseURL}/ssh_tunnel/disconnect`;
|
||||
const response = await tunnelApi.post(tunnelUrl, { tunnelName });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error disconnecting tunnel:', error);
|
||||
@@ -330,7 +325,8 @@ export async function disconnectTunnel(tunnelName: string): Promise<any> {
|
||||
|
||||
export async function cancelTunnel(tunnelName: string): Promise<any> {
|
||||
try {
|
||||
const response = await tunnelApi.post('/ssh/tunnel/cancel', { tunnelName });
|
||||
const tunnelUrl = isLocalhost ? 'http://localhost:8083/cancel' : `${baseURL}/ssh_tunnel/cancel`;
|
||||
const response = await tunnelApi.post(tunnelUrl, { tunnelName });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error canceling tunnel:', error);
|
||||
@@ -338,6 +334,184 @@ export async function cancelTunnel(tunnelName: string): Promise<any> {
|
||||
}
|
||||
}
|
||||
|
||||
// Config-related functions (use port 8084 for localhost)
|
||||
export { api, configEditorApi };
|
||||
|
||||
export { sshHostApi, tunnelApi, configEditorApi };
|
||||
// Config Editor API functions
|
||||
interface ConfigEditorFile {
|
||||
name: string;
|
||||
path: string;
|
||||
type?: 'file' | 'directory';
|
||||
isSSH?: boolean;
|
||||
sshSessionId?: string;
|
||||
}
|
||||
|
||||
interface ConfigEditorShortcut {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
// Config Editor database functions (use port 8081 for database operations)
|
||||
export async function getConfigEditorRecent(hostId: number): Promise<ConfigEditorFile[]> {
|
||||
try {
|
||||
const response = await api.get(`/ssh/config_editor/recent?hostId=${hostId}`);
|
||||
return response.data || [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching recent files:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function addConfigEditorRecent(file: { name: string; path: string; isSSH: boolean; sshSessionId?: string; hostId: number }): Promise<any> {
|
||||
try {
|
||||
const response = await api.post('/ssh/config_editor/recent', file);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error adding recent file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeConfigEditorRecent(file: { name: string; path: string; isSSH: boolean; sshSessionId?: string; hostId: number }): Promise<any> {
|
||||
try {
|
||||
const response = await api.delete('/ssh/config_editor/recent', { data: file });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error removing recent file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getConfigEditorPinned(hostId: number): Promise<ConfigEditorFile[]> {
|
||||
try {
|
||||
const response = await api.get(`/ssh/config_editor/pinned?hostId=${hostId}`);
|
||||
return response.data || [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching pinned files:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function addConfigEditorPinned(file: { name: string; path: string; isSSH: boolean; sshSessionId?: string; hostId: number }): Promise<any> {
|
||||
try {
|
||||
const response = await api.post('/ssh/config_editor/pinned', file);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error adding pinned file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeConfigEditorPinned(file: { name: string; path: string; isSSH: boolean; sshSessionId?: string; hostId: number }): Promise<any> {
|
||||
try {
|
||||
const response = await api.delete('/ssh/config_editor/pinned', { data: file });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error removing pinned file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getConfigEditorShortcuts(hostId: number): Promise<ConfigEditorShortcut[]> {
|
||||
try {
|
||||
const response = await api.get(`/ssh/config_editor/shortcuts?hostId=${hostId}`);
|
||||
return response.data || [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching shortcuts:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function addConfigEditorShortcut(shortcut: { name: string; path: string; isSSH: boolean; sshSessionId?: string; hostId: number }): Promise<any> {
|
||||
try {
|
||||
const response = await api.post('/ssh/config_editor/shortcuts', shortcut);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error adding shortcut:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeConfigEditorShortcut(shortcut: { name: string; path: string; isSSH: boolean; sshSessionId?: string; hostId: number }): Promise<any> {
|
||||
try {
|
||||
const response = await api.delete('/ssh/config_editor/shortcuts', { data: shortcut });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error removing shortcut:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// SSH file operations - FIXED: Using configEditorApi for port 8084
|
||||
export async function connectSSH(sessionId: string, config: {
|
||||
ip: string;
|
||||
port: number;
|
||||
username: string;
|
||||
password?: string;
|
||||
sshKey?: string;
|
||||
keyPassword?: string;
|
||||
}): Promise<any> {
|
||||
try {
|
||||
const response = await configEditorApi.post('/ssh/config_editor/ssh/connect', {
|
||||
sessionId,
|
||||
...config
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error connecting SSH:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function disconnectSSH(sessionId: string): Promise<any> {
|
||||
try {
|
||||
const response = await configEditorApi.post('/ssh/config_editor/ssh/disconnect', { sessionId });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error disconnecting SSH:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSSHStatus(sessionId: string): Promise<{ connected: boolean }> {
|
||||
try {
|
||||
const response = await configEditorApi.get('/ssh/config_editor/ssh/status', {
|
||||
params: { sessionId }
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error getting SSH status:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function listSSHFiles(sessionId: string, path: string): Promise<any[]> {
|
||||
try {
|
||||
const response = await configEditorApi.get('/ssh/config_editor/ssh/listFiles', {
|
||||
params: { sessionId, path }
|
||||
});
|
||||
return response.data || [];
|
||||
} catch (error) {
|
||||
console.error('Error listing SSH files:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function readSSHFile(sessionId: string, path: string): Promise<{ content: string; path: string }> {
|
||||
try {
|
||||
const response = await configEditorApi.get('/ssh/config_editor/ssh/readFile', {
|
||||
params: { sessionId, path }
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error reading SSH file:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function writeSSHFile(sessionId: string, path: string, content: string): Promise<any> {
|
||||
try {
|
||||
const response = await configEditorApi.post('/ssh/config_editor/ssh/writeFile', {
|
||||
sessionId,
|
||||
path,
|
||||
content
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error writing SSH file:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user