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() log.Printf("UI client connected from %s", r.RemoteAddr) // Register notifier to forward activation results from main WS to this UI connection notifierID := registerUINotifier(func(msg map[string]interface{}) { log.Printf("UI notifier forwarding to browser: %+v", msg) if err := conn.WriteJSON(msg); err != nil { log.Printf("UI notify error: %v", err) } else { log.Printf("UI notifier sent successfully") } }) defer unregisterUINotifier(notifierID) for { var msg map[string]interface{} if err := conn.ReadJSON(&msg); err != nil { log.Printf("UI client disconnected: %v", err) break } action, _ := msg["action"].(string) log.Printf("UI received action: %s", action) 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) log.Printf("UI handling activate with code: %s", code) if err := sendMessage(WSMessage{Action: "activate", NodeID: nodeID, Code: code}); err != nil { log.Printf("UI sendMessage failed: %v", err) conn.WriteJSON(map[string]interface{}{"action": "activation_failed", "error": err.Error()}) } else { log.Printf("UI sendMessage succeeded, waiting for server response...") } 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 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}) }