package main import ( "fmt" "log" "net/http" "time" socketio "github.com/googollee/go-socket.io" "github.com/joho/godotenv" ) func main() { err := godotenv.Load("../.env") if err != nil { //log := logger.New() //log.Info("Warning: .env file not found (ok in Docker/production)", "system", map[string]interface{}{}) fmt.Println("Warning: .env file not found") } server := socketio.NewServer(nil) server.OnConnect("/", func(s socketio.Conn) error { fmt.Println("✅ Client connected:", s.ID()) return nil }) // ROOM SUBSCRIBE server.OnEvent("/", "subscribe:logs", func(s socketio.Conn) { s.Join("logs") fmt.Println("📺", s.ID(), "joined logs") s.Emit("info", "Subscribed to logs") }) server.OnEvent("/", "unsubscribe:logs", func(s socketio.Conn) { s.Leave("logs") fmt.Println("👋", s.ID(), "left logs") s.Emit("info", "Unsubscribed from logs") }) server.OnEvent("/", "subscribe:errors", func(s socketio.Conn) { s.Join("errors") fmt.Println("📺", s.ID(), "joined errors") s.Emit("info", "Subscribed to errors") }) server.OnEvent("/", "unsubscribe:errors", func(s socketio.Conn) { s.Leave("errors") fmt.Println("👋", s.ID(), "left errors") s.Emit("info", "Unsubscribed from errors") }) server.OnDisconnect("/", func(s socketio.Conn, reason string) { fmt.Println("❌ Client disconnected:", s.ID(), reason) }) // build stuff // Subscribe to build room server.OnEvent("/", "subscribe:build", func(s socketio.Conn) { s.Join("build") fmt.Println("📺", s.ID(), "joined build room") s.Emit("info", "Subscribed to build log room") }) server.OnEvent("/", "unsubscribe:build", func(s socketio.Conn) { s.Leave("build") fmt.Println("👋", s.ID(), "left build room") s.Emit("info", "Unsubscribed from build log room") }) registerBuildChannel(server) registerUpdateChannel(server) // Broadcast logs to room go func() { for i := 0; ; i++ { time.Sleep(2 * time.Second) msg := fmt.Sprintf("Log line %d @ %s", i, time.Now().Format(time.RFC3339)) server.BroadcastToRoom("/", "logs", "logs", msg) } }() // Broadcast errors to room go func() { for i := 0; ; i++ { time.Sleep(5 * time.Second) msg := fmt.Sprintf("Error #%d @ %s", i, time.Now().Format(time.RFC3339)) server.BroadcastToRoom("/", "errors", "errors", msg) } }() go server.Serve() defer server.Close() // Enable CORS wrapper for Socket.IO route http.Handle("/socket.io/", withCORS(server)) http.Handle("/", http.FileServer(http.Dir("./static"))) fmt.Println("🚀 Socket.IO server running on :8000") log.Fatal(http.ListenAndServe(":8000", nil)) } // Reuse your proper CORS handler func withCORS(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") if origin != "" { w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Vary", "Origin") } w.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") w.Header().Set("Access-Control-Allow-Credentials", "true") if r.Method == http.MethodOptions { w.WriteHeader(http.StatusNoContent) return } h.ServeHTTP(w, r) }) }