144 lines
3.6 KiB
Go

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)
}
}