diff --git a/daemon.go b/daemon.go index 421191f..f1d651b 100644 --- a/daemon.go +++ b/daemon.go @@ -19,6 +19,13 @@ type DaemonServer struct { procManager *ProcessManager } +type InstanceStatusResponse struct { + Name string `json:"name"` + Version string `json:"version"` + Port int `json:"port"` + Status string `json:"status"` +} + func StartDaemon(cfg *AppConfig) error { ds := &DaemonServer{ cfg: cfg, @@ -30,6 +37,7 @@ func StartDaemon(cfg *AppConfig) error { mux.HandleFunc("/instances/start", ds.handleStart) mux.HandleFunc("/instances/stop", ds.handleStop) mux.HandleFunc("/instances/command", ds.handleCommand) + mux.HandleFunc("/instances/list", ds.handleList) server := &http.Server{ Addr: cfg.Daemon.ListenAddress, @@ -72,7 +80,7 @@ func (ds *DaemonServer) handleCreate(w http.ResponseWriter, r *http.Request) { options := VsServerConfigOptions{ Version: version, ServerName: name, - Port: 42424, // Pull from custom parameters later if desired + Port: 42424, MaxClients: 10, } @@ -176,3 +184,33 @@ func (ds *DaemonServer) handleCommand(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "Command delivered successfully to %s", name) } + +func (ds *DaemonServer) handleList(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + ds.procManager.RLock() + defer ds.procManager.RUnlock() + + var responseList []InstanceStatusResponse + + for name, options := range ds.cfg.Instances { + status := "STOPPED" + if _, running := ds.procManager.ActiveInstances[name]; running { + status = "RUNNING" + } + + responseList = append(responseList, InstanceStatusResponse{ + Name: name, + Version: options.Version, + Port: options.Port, + Status: status, + }) + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(responseList) +} diff --git a/main.go b/main.go index 68f72b5..715f8df 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "path/filepath" + "text/tabwriter" ) func main() { @@ -71,6 +72,9 @@ func main() { body, _ := json.Marshal(payload) sendIPCRequest(cfg, "POST", fmt.Sprintf("/instances/command?name=%s", url.QueryEscape(name)), bytes.NewBuffer(body)) + case "list", "status": + fetchAndPrintStatus(cfg) + default: printUsage() } @@ -101,6 +105,39 @@ func sendIPCRequest(cfg *AppConfig, method string, path string, body io.Reader) fmt.Println(string(respBody)) } +func fetchAndPrintStatus(cfg *AppConfig) { + targetUrl := fmt.Sprintf("http://%s/instances/list", cfg.Daemon.ListenAddress) + resp, err := http.Get(targetUrl) + if err != nil { + log.Fatalf("IPC connection failed. Is the vs-manager daemon running? Error: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + respBody, _ := io.ReadAll(resp.Body) + log.Fatalf("Error from daemon: %s", string(respBody)) + } + + var instances []InstanceStatusResponse + if err := json.NewDecoder(resp.Body).Decode(&instances); err != nil { + log.Fatalf("Failed to decode daemon status matrix: %v", err) + } + + if len(instances) == 0 { + fmt.Println("No instances configured yet. Use 'create' to provision one.") + return + } + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) + fmt.Fprintln(w, "INSTANCE NAME\tVERSION\tPORT\tSTATUS") + fmt.Fprintln(w, "-------------\t-------\t----\t------") + + for _, inst := range instances { + fmt.Fprintf(w, "%s\t%s\t%d\t%s\n", inst.Name, inst.Version, inst.Port, inst.Status) + } + w.Flush() +} + func printUsage() { fmt.Println("Vintage Story Server Manager") fmt.Println("\nUsage:")