package main import ( _ "embed" "fmt" "log" "net/http" "os" "path/filepath" "github.com/gorilla/websocket" ) //go:embed ui/index.html var uiHTML string var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }} func startUI(dataDir, nodeID, serverAddr string) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") fmt.Fprint(w, uiHTML) }) http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Printf("UI WS upgrade error: %v", err) return } defer conn.Close() for { var msg map[string]interface{} if err := conn.ReadJSON(&msg); err != nil { break } action, _ := msg["action"].(string) switch action { case "check": act, err := loadActivation(dataDir) if err == nil && act.Activated { conn.WriteJSON(map[string]interface{}{"action": "activated", "studentName": act.StudentName}) } else { conn.WriteJSON(map[string]interface{}{"action": "not_activated"}) } case "activate": code, _ := msg["code"].(string) // Forward to server WS go func() { forwardActivation(serverAddr, nodeID, code, conn) }() case "instances": listInstances(dataDir, conn) } } }) port := "7070" log.Printf("UI starting on http://localhost:%s", port) if err := http.ListenAndServe("127.0.0.1:"+port, nil); err != nil { log.Fatalf("UI server error: %v", err) } } func forwardActivation(serverAddr, nodeID, code string, uiConn *websocket.Conn) { conn, _, err := websocket.DefaultDialer.Dial(serverAddr, nil) if err != nil { uiConn.WriteJSON(map[string]interface{}{"action": "activation_failed", "error": err.Error()}) return } defer conn.Close() conn.WriteJSON(map[string]interface{}{"action": "register", "nodeId": nodeID}) conn.WriteJSON(map[string]interface{}{"action": "activate", "code": code, "nodeId": nodeID}) for { var msg map[string]interface{} if err := conn.ReadJSON(&msg); err != nil { break } action, _ := msg["action"].(string) if action == "activated" || action == "activation_failed" { uiConn.WriteJSON(msg) break } } } func listInstances(dataDir string, conn *websocket.Conn) { dir := filepath.Join(dataDir, "instances") entries, err := os.ReadDir(dir) if err != nil { conn.WriteJSON(map[string]interface{}{"action": "instances_list", "instances": []interface{}{}}) return } var instances []map[string]interface{} for _, e := range entries { if e.IsDir() { instances = append(instances, map[string]interface{}{"id": e.Name()}) } } conn.WriteJSON(map[string]interface{}{"action": "instances_list", "instances": instances}) }