wraith/frontend/src/stores/session.store.ts
Vantz Stockwell 8572e6e7ea fix: wire SSH/SFTP/terminal to real Go backend — kill all stubs
Critical path wired end-to-end:
- ConnectSSH on WraithApp resolves credentials from vault, builds auth methods
- SSH output handler emits Wails events (base64) to frontend
- useTerminal.ts forwards keystrokes to SSHService.Write, resize to SSHService.Resize
- useTerminal.ts listens for ssh:data:{sessionId} events and writes to xterm.js
- session.store.ts connect() calls real Go ConnectSSH, not mock
- useSftp.ts calls real SFTPService.List instead of hardcoded mock data
- SFTP client auto-registered on SSH connection via pkg/sftp
- DisconnectSession cleans up both SSH and SFTP

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:49:21 -04:00

114 lines
3.1 KiB
TypeScript

import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { Call } from "@wailsio/runtime";
import { useConnectionStore } from "@/stores/connection.store";
const APP = "github.com/vstockwell/wraith/internal/app.WraithApp";
export interface Session {
id: string;
connectionId: number;
name: string;
protocol: "ssh" | "rdp";
active: boolean;
}
export const useSessionStore = defineStore("session", () => {
const sessions = ref<Session[]>([]);
const activeSessionId = ref<string | null>(null);
const connecting = ref(false);
const activeSession = computed(() =>
sessions.value.find((s) => s.id === activeSessionId.value) ?? null,
);
const sessionCount = computed(() => sessions.value.length);
function activateSession(id: string): void {
activeSessionId.value = id;
}
async function closeSession(id: string): Promise<void> {
const idx = sessions.value.findIndex((s) => s.id === id);
if (idx === -1) return;
const session = sessions.value[idx];
// Disconnect the backend session
try {
await Call.ByName(`${APP}.DisconnectSession`, session.id);
} catch (err) {
console.error("Failed to disconnect session:", err);
}
sessions.value.splice(idx, 1);
if (activeSessionId.value === id) {
if (sessions.value.length === 0) {
activeSessionId.value = null;
} else {
const nextIdx = Math.min(idx, sessions.value.length - 1);
activeSessionId.value = sessions.value[nextIdx].id;
}
}
}
/**
* Connect to a server by connection ID.
* Calls the real Go backend to establish an SSH or RDP session.
*/
async function connect(connectionId: number): Promise<void> {
const connectionStore = useConnectionStore();
const conn = connectionStore.connections.find((c) => c.id === connectionId);
if (!conn) return;
// Check if there's already an active session for this connection
const existing = sessions.value.find((s) => s.connectionId === connectionId);
if (existing) {
activeSessionId.value = existing.id;
return;
}
connecting.value = true;
try {
if (conn.protocol === "ssh") {
// Call Go backend — resolves credentials, builds auth, returns sessionID
const sessionId = await Call.ByName(
`${APP}.ConnectSSH`,
connectionId,
120, // cols (will be resized by xterm.js fit addon)
40, // rows
) as string;
sessions.value.push({
id: sessionId,
connectionId,
name: conn.name,
protocol: "ssh",
active: true,
});
activeSessionId.value = sessionId;
} else if (conn.protocol === "rdp") {
// TODO: Wire RDP connect when ready
console.warn("RDP connections not yet wired");
}
} catch (err) {
console.error("Connection failed:", err);
// TODO: Show error toast in UI
} finally {
connecting.value = false;
}
}
return {
sessions,
activeSessionId,
activeSession,
sessionCount,
connecting,
activateSession,
closeSession,
connect,
};
});