From 1da6f042197cf0f5a37f23179f14d055351bdba2 Mon Sep 17 00:00:00 2001 From: chris bell Date: Thu, 11 Jun 2026 19:18:50 -0500 Subject: [PATCH 1/3] Populated config screen with a basic editor --- vssm_web/vssm_web/src/api.ts | 39 ++++++++++++--- .../src/components/InstanceConfig.tsx | 50 +++++++++++++++++++ .../src/components/InstanceDetail.tsx | 7 +-- vssm_web/vssm_web/src/index.css | 22 ++++++++ 4 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 vssm_web/vssm_web/src/components/InstanceConfig.tsx diff --git a/vssm_web/vssm_web/src/api.ts b/vssm_web/vssm_web/src/api.ts index 24c236a..9810361 100644 --- a/vssm_web/vssm_web/src/api.ts +++ b/vssm_web/vssm_web/src/api.ts @@ -12,6 +12,9 @@ class api { } export default api; + +// --- GET CALLS --- + export async function fetchInstanceList(): Promise { const res = await fetch(`${ADDRESS}/instances/list`); if (!res.ok) { @@ -30,13 +33,24 @@ export async function fetchInstanceLogs(name: string): Promise { return res.json(); } +export async function fetchInstanceConfig(name: string): Promise { + const res = await fetch(`${ADDRESS}/instances/getConfig?name=${name}`); + if (!res.ok) { + const message = await res.text(); + throw new Error(`Could not fetch instance config: ${message.trim()}`); + } + return res.text(); +} + +// --- POST CALLS --- + export async function sendStartInstanceRequest(name: string) { const res = await fetch(`${ADDRESS}/instances/start?name=${name}`, { method: "POST" }); if (!res.ok) { const message = await res.text(); throw new Error(`Start Instance Request failed: ${message.trim()}`); } - return res.json; + return res.text(); } export async function sendStopInstanceRequest(name: string) { @@ -45,7 +59,7 @@ export async function sendStopInstanceRequest(name: string) { const message = await res.text(); throw new Error(`Stop Instance Request failed: ${message.trim()}`); } - return res.json; + return res.text(); } export async function sendCreateInstanceRequest(name: string, version: string, port: number) { @@ -54,7 +68,7 @@ export async function sendCreateInstanceRequest(name: string, version: string, p const message = await res.text(); throw new Error(`Create instance request failed: ${message.trim()}`); } - return res.json; + return res.text(); } export async function sendDeleteInstanceRequest(name: string, purge: boolean) { @@ -63,7 +77,7 @@ export async function sendDeleteInstanceRequest(name: string, purge: boolean) { const message = await res.text(); throw new Error(`Delete instance request failed: ${message.trim()}`); } - return res.json; + return res.text(); } export async function sendModifyInstanceRequest(name: string, newName: string, newPort: number) { @@ -72,7 +86,7 @@ export async function sendModifyInstanceRequest(name: string, newName: string, n const message = await res.text(); throw new Error(`Modify instance request failed: ${message.trim()}`); } - return res.json; + return res.text(); } export async function sendCommandRequest(name: string, payload: string) { @@ -85,5 +99,18 @@ export async function sendCommandRequest(name: string, payload: string) { const message = await res.text(); throw new Error(`Send Instance Command failed: ${message.trim()}`); } - return res.json; + return res.text(); +} + +export async function sendSetInstanceConfigRequest(name: string, config: string) { + const res = await fetch(`${ADDRESS}/instances/setConfig?name=${name}`, { + method: "POST", + body: config, + headers: { "Content-Type": "application/json" }, + }); + if (!res.ok) { + const message = await res.text(); + throw new Error(`Set instance config request failed: ${message.trim()}`); + } + return res.text(); } \ No newline at end of file diff --git a/vssm_web/vssm_web/src/components/InstanceConfig.tsx b/vssm_web/vssm_web/src/components/InstanceConfig.tsx new file mode 100644 index 0000000..3c328d8 --- /dev/null +++ b/vssm_web/vssm_web/src/components/InstanceConfig.tsx @@ -0,0 +1,50 @@ +import { useState, useEffect } from "react"; +import type { Instance } from "../types"; +import { fetchInstanceConfig, sendSetInstanceConfigRequest } from "../api"; + +type Props = { + instance: Instance; +}; + +export default function InstanceConfig({ instance }: Props) { + const [config, setConfig] = useState(""); + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + fetchInstanceConfig(instance.name) + .then(setConfig) + .catch(e => setError(e.message ?? String(e))) + .finally(() => setLoading(false)); + }, [instance.name]); + + async function handleSave() { + setSaving(true); + setError(null); + try { + await sendSetInstanceConfigRequest(instance.name, config); + } catch (e: any) { + setError(e.message ?? String(e)); + } finally { + setSaving(false); + } + } + + if (loading) return

Loading config...

; + + return ( +
+ {error &&
Error: {error}
} +