working with better api

This commit is contained in:
EthanShoeDev
2025-09-14 21:17:01 -04:00
parent ba1b37a258
commit 84a950f2dc
6 changed files with 493 additions and 152 deletions

View File

@@ -12,7 +12,7 @@ import {
connectionDetailsSchema,
secretsManager,
} from '../lib/secrets-manager';
import { sshConnectionManager } from '../lib/ssh-connection-manager';
// import { sshConnectionManager } from '../lib/ssh-connection-manager';
const defaultValues: ConnectionDetails = {
host: 'test.rebex.net',
@@ -52,20 +52,23 @@ const useSshConnMutation = () => {
details: connectionDetails,
priority: 0,
});
await sshConnection.startShell({
const shellInterface = await sshConnection.startShell({
pty: 'Xterm',
onStatusChange: (status) => {
console.log('SSH shell status', status);
},
});
const sshConn = sshConnectionManager.addSession({
client: sshConnection,
});
console.log('Connected to SSH server', sshConn.sessionId);
const channelId = shellInterface.channelId as number;
const connectionId =
sshConnection.connectionId ??
`${sshConnection.connectionDetails.username}@${sshConnection.connectionDetails.host}:${sshConnection.connectionDetails.port}|${Math.floor(sshConnection.createdAtMs)}`;
console.log('Connected to SSH server', connectionId, channelId);
router.push({
pathname: '/shell',
params: {
sessionId: sshConn.sessionId,
connectionId,
channelId: String(channelId),
},
});
} catch (error) {

View File

@@ -1,6 +1,7 @@
/**
* This is the page that is shown after an ssh connection
*/
import { RnRussh } from '@fressh/react-native-uniffi-russh';
import { useLocalSearchParams } from 'expo-router';
import { useEffect, useRef, useState } from 'react';
import {
@@ -12,46 +13,47 @@ import {
TextInput,
View,
} from 'react-native';
import { sshConnectionManager } from '../lib/ssh-connection-manager';
export default function Shell() {
// https://docs.expo.dev/router/reference/url-parameters/
const { sessionId } = useLocalSearchParams<{ sessionId: string }>();
const sshConn = sshConnectionManager.getSession({ sessionId }); // this throws if the session is not found
const { connectionId, channelId } = useLocalSearchParams<{
connectionId: string;
channelId: string;
}>();
const channelIdNum = Number(channelId);
const connection = RnRussh.getSshConnection(connectionId);
const shell = RnRussh.getSshShell(connectionId, channelIdNum);
const [shellData, setShellData] = useState('');
// Subscribe to data frames on the connection
useEffect(() => {
// Decode ArrayBuffer bytes from the SSH channel into text
if (!connection) return;
const decoder = new TextDecoder('utf-8');
const channelListenerId = sshConn.client.addChannelListener((data) => {
try {
const bytes = new Uint8Array(data);
const chunk = decoder.decode(bytes);
console.log('Received data (on Shell):', chunk.length, 'chars');
setShellData((prev) => prev + chunk);
} catch (e) {
console.warn('Failed to decode shell data', e);
}
});
const channelListenerId = connection.addChannelListener(
(data: ArrayBuffer) => {
try {
const bytes = new Uint8Array(data);
const chunk = decoder.decode(bytes);
console.log('Received data (on Shell):', chunk.length, 'chars');
setShellData((prev) => prev + chunk);
} catch (e) {
console.warn('Failed to decode shell data', e);
}
},
);
return () => {
sshConn.client.removeChannelListener(channelListenerId);
connection.removeChannelListener(channelListenerId);
};
}, [setShellData, sshConn.client]);
}, [connection]);
// Cleanup when leaving screen
useEffect(() => {
return () => {
console.log('Clean up shell screen (immediate disconnect)');
void sshConnectionManager
.removeAndDisconnectSession({ sessionId })
.then(() => {
console.log('Disconnected from SSH server');
})
.catch((e: unknown) => {
console.error('Error disconnecting from SSH server', e);
});
if (connection) void connection.disconnect().catch(() => {});
};
}, [sessionId]);
}, [connection, shell]);
const scrollViewRef = useRef<ScrollView | null>(null);
@@ -77,7 +79,7 @@ export default function Shell() {
<CommandInput
executeCommand={async (command) => {
console.log('Executing command:', command);
await sshConn.client.sendData(
await shell?.sendData(
Uint8Array.from(new TextEncoder().encode(command + '\n')).buffer,
);
}}

View File

@@ -1,41 +0,0 @@
import { type SshConnection } from '@fressh/react-native-uniffi-russh';
import * as Crypto from 'expo-crypto';
export type SSHConn = {
client: SshConnection;
sessionId: string;
createdAt: Date;
};
const sshConnections = new Map<string, SSHConn>();
function addSession(params: { client: SshConnection }) {
const sessionId = Crypto.randomUUID();
const createdAt = new Date();
const sshConn: SSHConn = {
client: params.client,
sessionId,
createdAt,
};
sshConnections.set(sessionId, sshConn);
return sshConn;
}
function getSession(params: { sessionId: string }) {
const sshConn = sshConnections.get(params.sessionId);
if (!sshConn) throw new Error('Session not found');
return sshConn;
}
async function removeAndDisconnectSession(params: { sessionId: string }) {
const sshConn = getSession(params);
await sshConn.client.disconnect();
sshConnections.delete(params.sessionId);
}
export const sshConnectionManager = {
addSession,
getSession,
removeAndDisconnectSession,
};