Replace all TODO stubs in frontend stores with real Wails Call.ByName bindings. The app store now calls WraithApp.IsFirstRun/CreateVault/Unlock so vault state persists across launches. The connection store loads from ConnectionService.ListConnections/ListGroups instead of hardcoded mock data. The import dialog calls a new WraithApp.ImportMobaConf method that parses the file, creates groups and connections in SQLite, and stores host keys. ConnectionEditDialog also uses real Go CRUD calls. MainLayout loads connections on mount after vault unlock. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
114 lines
3.1 KiB
TypeScript
114 lines
3.1 KiB
TypeScript
import { defineStore } from "pinia";
|
|
import { ref, computed } from "vue";
|
|
import { Call } from "@wailsio/runtime";
|
|
|
|
/** Fully qualified Go method name prefix for ConnectionService bindings. */
|
|
const CONN = "github.com/vstockwell/wraith/internal/connections.ConnectionService";
|
|
|
|
export interface Connection {
|
|
id: number;
|
|
name: string;
|
|
hostname: string;
|
|
port: number;
|
|
protocol: "ssh" | "rdp";
|
|
groupId: number | null;
|
|
credentialId?: number | null;
|
|
color?: string;
|
|
tags?: string[];
|
|
notes?: string;
|
|
options?: string;
|
|
sortOrder?: number;
|
|
lastConnected?: string | null;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface Group {
|
|
id: number;
|
|
name: string;
|
|
parentId: number | null;
|
|
sortOrder?: number;
|
|
icon?: string;
|
|
children?: Group[];
|
|
}
|
|
|
|
/**
|
|
* Connection store.
|
|
* Manages connections, groups, and search state.
|
|
* Loads data from the Go backend via Wails bindings.
|
|
*/
|
|
export const useConnectionStore = defineStore("connection", () => {
|
|
const connections = ref<Connection[]>([]);
|
|
const groups = ref<Group[]>([]);
|
|
const searchQuery = ref("");
|
|
|
|
/** Filter connections by search query. */
|
|
const filteredConnections = computed(() => {
|
|
const q = searchQuery.value.toLowerCase().trim();
|
|
if (!q) return connections.value;
|
|
return connections.value.filter(
|
|
(c) =>
|
|
c.name.toLowerCase().includes(q) ||
|
|
c.hostname.toLowerCase().includes(q) ||
|
|
c.tags?.some((t) => t.toLowerCase().includes(q)),
|
|
);
|
|
});
|
|
|
|
/** Get connections belonging to a specific group. */
|
|
function connectionsByGroup(groupId: number): Connection[] {
|
|
const q = searchQuery.value.toLowerCase().trim();
|
|
const groupConns = connections.value.filter((c) => c.groupId === groupId);
|
|
if (!q) return groupConns;
|
|
return groupConns.filter(
|
|
(c) =>
|
|
c.name.toLowerCase().includes(q) ||
|
|
c.hostname.toLowerCase().includes(q) ||
|
|
c.tags?.some((t) => t.toLowerCase().includes(q)),
|
|
);
|
|
}
|
|
|
|
/** Check if a group has any matching connections (for search filtering). */
|
|
function groupHasResults(groupId: number): boolean {
|
|
return connectionsByGroup(groupId).length > 0;
|
|
}
|
|
|
|
/** Load connections from the Go backend. */
|
|
async function loadConnections(): Promise<void> {
|
|
try {
|
|
const conns = await Call.ByName(`${CONN}.ListConnections`) as Connection[];
|
|
connections.value = conns || [];
|
|
} catch (err) {
|
|
console.error("Failed to load connections:", err);
|
|
connections.value = [];
|
|
}
|
|
}
|
|
|
|
/** Load groups from the Go backend. */
|
|
async function loadGroups(): Promise<void> {
|
|
try {
|
|
const grps = await Call.ByName(`${CONN}.ListGroups`) as Group[];
|
|
groups.value = grps || [];
|
|
} catch (err) {
|
|
console.error("Failed to load groups:", err);
|
|
groups.value = [];
|
|
}
|
|
}
|
|
|
|
/** Load both connections and groups from the Go backend. */
|
|
async function loadAll(): Promise<void> {
|
|
await Promise.all([loadConnections(), loadGroups()]);
|
|
}
|
|
|
|
return {
|
|
connections,
|
|
groups,
|
|
searchQuery,
|
|
filteredConnections,
|
|
connectionsByGroup,
|
|
groupHasResults,
|
|
loadConnections,
|
|
loadGroups,
|
|
loadAll,
|
|
};
|
|
});
|