diff --git a/cmd/server/servers.go b/cmd/server/servers.go index c6a0a27..cd26ef2 100644 --- a/cmd/server/servers.go +++ b/cmd/server/servers.go @@ -3,10 +3,14 @@ package main import ( "fmt" "net/http" + + "robinplatform.dev/smgr/internal/manager" ) func init() { http.HandleFunc("/api/GetServers", GetServers) + http.HandleFunc("/api/StartServer", StartServer) + http.HandleFunc("/api/CheckServerHealth", CheckServerHealth) } func GetServers(res http.ResponseWriter, req *http.Request) { @@ -16,3 +20,35 @@ func GetServers(res http.ResponseWriter, req *http.Request) { sendJson(res, serverManager.Servers) } } + +func StartServer(res http.ResponseWriter, req *http.Request) { + fmt.Printf("StartServer Running\n") + config := manager.DevServerConfig{ + Name: "hello", + Command: "ls", + } + + if err := manager.StartServer(config); err != nil { + sendError(res, 500, fmt.Errorf("failed to discover servers: %w", err)) + } else { + sendJson(res, map[string]any{ + "success": true, + }) + } +} + +func CheckServerHealth(res http.ResponseWriter, req *http.Request) { + fmt.Printf("CheckHealth Running\n") + config := manager.DevServerConfig{ + Name: "hello", + Command: "/bin/ls", + } + + if err := manager.CheckServerHealth(config); err != nil { + sendError(res, 500, fmt.Errorf("failed to discover servers: %w", err)) + } else { + sendJson(res, map[string]any{ + "success": true, + }) + } +} diff --git a/internal/manager/manager.go b/internal/manager/manager.go index f90280a..9e82fcf 100644 --- a/internal/manager/manager.go +++ b/internal/manager/manager.go @@ -13,8 +13,9 @@ import ( ) type DevServerConfig struct { + Name string `json:"-"` HealthChecks []health.HealthCheck `json:"healthChecks,omitempty"` - Command string `json:"command"` + Command string `json:"command"` } func (config *DevServerConfig) UnmarshalJSON(data []byte) error { @@ -39,8 +40,8 @@ func (config *DevServerConfig) UnmarshalJSON(data []byte) error { type ServerManagerConfig struct { FilePath string `json:"-"` - Name string `json:"name"` - DevServers map[string]DevServerConfig `json:"devServers"` + Name string `json:"name"` + DevServers []DevServerConfig `json:"devServers"` } func (config *ServerManagerConfig) load() error { @@ -54,10 +55,34 @@ func (config *ServerManagerConfig) load() error { return fmt.Errorf("failed to read server config from %s: %w", config.FilePath, err) } - if err := json.Unmarshal(buf, config); err != nil { + type ServerConfigJSON struct { + HealthChecks []health.HealthCheck `json:"healthChecks,omitempty"` + Command string `json:"command"` + } + + type ConfigJSON struct { + Name string `json:"name"` + DevServers map[string]ServerConfigJSON `json:"devServers"` + } + + var configTmp ConfigJSON + if err := json.Unmarshal(buf, &configTmp); err != nil { return fmt.Errorf("failed to unmarshal server config from %s: %w", config.FilePath, err) } + config.Name = configTmp.Name + config.DevServers = make([]DevServerConfig, 0, len(configTmp.DevServers)) + + for name, value := range configTmp.DevServers { + server := DevServerConfig{ + Name: name, + HealthChecks: value.HealthChecks, + Command: value.Command, + } + + config.DevServers = append(config.DevServers, server) + } + if config.Name == "" { config.Name = filepath.Base(filepath.Dir(config.FilePath)) } @@ -93,7 +118,7 @@ func (manager *ServerManager) DiscoverServers(projectPath string) error { if dirEntry.IsDir() { return nil } - + if filepath.Base(filename) == "robin.servers.json" { fmt.Printf("Discovered server config: %s\n", filename) diff --git a/internal/manager/start.go b/internal/manager/start.go new file mode 100644 index 0000000..44b6076 --- /dev/null +++ b/internal/manager/start.go @@ -0,0 +1,103 @@ +package manager + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" +) + +func StartServer(config DevServerConfig) error { + type Input struct { + AppId string `json:"appId"` + ProcessKey string `json:"processKey"` + Command string `json:"command"` + Args []string `json:"args"` + } + + input := Input{ + AppId: "server-manager", + ProcessKey: config.Name, + Command: "/bin/bash", + Args: []string{"-c", config.Command}, + } + + jsonValue, err := json.Marshal(input) + if err != nil { + return err + } + + resp, err := http.Post("http://localhost:9010/api/apps/rpc/StartProcess", "application/json", bytes.NewBuffer(jsonValue)) + if err != nil { + return err + } + + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + + fmt.Printf("StartServer resp body: %s\n", string(body)) + + return nil +} + +func StopServer(config DevServerConfig) error { + type Input struct { + AppId string `json:"appId"` + ProcessKey string `json:"processKey"` + } + + input := Input{ + AppId: "server-manager", + ProcessKey: config.Name, + } + + jsonValue, err := json.Marshal(input) + if err != nil { + return err + } + + resp, err := http.Post("http://localhost:9010/api/apps/rpc/StopProcess", "application/json", bytes.NewBuffer(jsonValue)) + if err != nil { + return err + } + + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + + fmt.Printf("resp body: %s\n", string(body)) + + return nil +} + +func CheckServerHealth(config DevServerConfig) error { + type Input struct { + AppId string `json:"appId"` + ProcessKey string `json:"processKey"` + } + + input := Input{ + AppId: "server-manager", + ProcessKey: config.Name, + } + + jsonValue, err := json.Marshal(input) + if err != nil { + return err + } + + resp, err := http.Post("http://localhost:9010/api/apps/rpc/CheckProcessHealth", "application/json", bytes.NewBuffer(jsonValue)) + if err != nil { + return err + } + + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + + fmt.Printf("CheckServer resp body: %s\n", string(body)) + + return nil +} diff --git a/src/ControlPanel/index.tsx b/src/ControlPanel/index.tsx index 28616e5..5ea26e1 100644 --- a/src/ControlPanel/index.tsx +++ b/src/ControlPanel/index.tsx @@ -1,16 +1,35 @@ import { useHistory } from "../hooks/useHistory"; import { useSelectedServer } from "../hooks/useSelectedServer"; +import { runAppMethod } from "@robinplatform/toolkit"; +import { useMutation } from "@tanstack/react-query"; import React from "react"; +import { z } from "zod"; export const ControlPanel: React.FC = () => { const { selectedServer } = useSelectedServer(); const history = useHistory(); + const { mutate } = useMutation({ + mutationKey: ["StartServer", selectedServer], + mutationFn: async (): Promise => { + await runAppMethod({ + methodName: "StartServer", + resultType: z.any(), + data: { + server: selectedServer, + }, + }); + + return null; + }, + }); return (

{selectedServer}

{history.pathname}

+ +
); }; diff --git a/src/ServerList.tsx b/src/ServerList.tsx index 21b7785..6f4b01f 100644 --- a/src/ServerList.tsx +++ b/src/ServerList.tsx @@ -10,6 +10,15 @@ const ServerListItem: React.FC<{ onClick: () => void; }> = ({ server, onClick }) => { const { selectedServer } = useSelectedServer(); + const { data: health } = useRpcQuery( + "CheckServerHealth", + { + name: server.name, + }, + { + refetchInterval: 3000, + }, + ); return ( ); }; diff --git a/src/app.tsx b/src/app.tsx index d3dfbe9..3880131 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -12,7 +12,7 @@ const App = () => { return (
-
+
diff --git a/src/bridge.ts b/src/bridge.ts index 416bc87..98f8a83 100644 --- a/src/bridge.ts +++ b/src/bridge.ts @@ -2,15 +2,21 @@ import { createReactRpcBridge } from "@robinplatform/toolkit/react/rpc"; import { z } from "zod"; export const ServerType = z.object({ - name: z.string(), + name: z.string(), }); export type ServerType = z.infer; const { useRpcQuery } = createReactRpcBridge({ - GetServers: { - input: z.object({}), - output: z.array(ServerType), - }, + GetServers: { + input: z.object({}), + output: z.array(ServerType), + }, + CheckServerHealth: { + input: z.object({ + name: z.string(), + }), + output: z.string(), + }, }); export { useRpcQuery };