package main import ( "encoding/json" "fmt" "log" "net/http" "time" ) // WebServer provides HTTP endpoints for monitoring type WebServer struct { config WebServerConfig loginServer *LoginServer server *http.Server } // NewWebServer creates a new web monitoring server func NewWebServer(config WebServerConfig, loginServer *LoginServer) (*WebServer, error) { ws := &WebServer{ config: config, loginServer: loginServer, } mux := http.NewServeMux() mux.HandleFunc("/status", ws.handleStatus) mux.HandleFunc("/worlds", ws.handleWorlds) mux.HandleFunc("/stats", ws.handleStats) mux.HandleFunc("/health", ws.handleHealth) ws.server = &http.Server{ Addr: fmt.Sprintf("%s:%d", config.Address, config.Port), Handler: ws.basicAuth(mux), ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, } return ws, nil } // Start begins the web server func (ws *WebServer) Start() { log.Printf("Starting web server on %s", ws.server.Addr) var err error if ws.config.CertFile != "" && ws.config.KeyFile != "" { err = ws.server.ListenAndServeTLS(ws.config.CertFile, ws.config.KeyFile) } else { err = ws.server.ListenAndServe() } if err != http.ErrServerClosed { log.Printf("Web server error: %v", err) } } // Stop shuts down the web server func (ws *WebServer) Stop() { if ws.server != nil { ws.server.Close() } } // basicAuth provides basic HTTP authentication func (ws *WebServer) basicAuth(next http.Handler) http.Handler { if ws.config.Username == "" { return next } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() if !ok || username != ws.config.Username || password != ws.config.Password { w.Header().Set("WWW-Authenticate", `Basic realm="Login Server"`) w.WriteHeader(http.StatusUnauthorized) w.Write([]byte("Unauthorized")) return } next.ServeHTTP(w, r) }) } // handleStatus returns server status information func (ws *WebServer) handleStatus(w http.ResponseWriter, r *http.Request) { stats := ws.loginServer.GetStats() status := map[string]any{ "service": "eq2emu-login-server", "version": "1.0.0", "status": "running", "timestamp": time.Now().UTC(), "statistics": stats, } ws.writeJSON(w, status) } // handleWorlds returns world server information func (ws *WebServer) handleWorlds(w http.ResponseWriter, r *http.Request) { worlds := ws.loginServer.worldList.GetActiveWorlds() worldStats := ws.loginServer.worldList.GetStats() response := map[string]any{ "world_servers": worlds, "statistics": worldStats, } ws.writeJSON(w, response) } // handleStats returns detailed server statistics func (ws *WebServer) handleStats(w http.ResponseWriter, r *http.Request) { serverStats := ws.loginServer.GetStats() worldStats := ws.loginServer.worldList.GetStats() stats := map[string]any{ "server": serverStats, "worlds": worldStats, "timestamp": time.Now().UTC(), } ws.writeJSON(w, stats) } // handleHealth returns basic health check func (ws *WebServer) handleHealth(w http.ResponseWriter, r *http.Request) { health := map[string]any{ "status": "healthy", "timestamp": time.Now().UTC(), } ws.writeJSON(w, health) } // writeJSON writes JSON response with proper headers func (ws *WebServer) writeJSON(w http.ResponseWriter, data any) { w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-cache") if err := json.NewEncoder(w).Encode(data); err != nil { log.Printf("JSON encoding error: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }