package login
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
)
// handleWebRoot handles the root web interface page
func (s *Server) handleWebRoot(w http.ResponseWriter, r *http.Request) {
if !s.authenticateWebRequest(w, r) {
return
}
html := `
EQ2Go Login Server
API Endpoints
`
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(html))
}
// handleAPIStatus handles the status API endpoint
func (s *Server) handleAPIStatus(w http.ResponseWriter, r *http.Request) {
if !s.authenticateWebRequest(w, r) {
return
}
status := map[string]interface{}{
"server_name": s.config.ServerName,
"version": "1.0.0-dev",
"running": s.IsRunning(),
"uptime": s.formatUptime(s.GetUptime()),
"clients": s.GetClientCount(),
"worlds": s.GetWorldCount(),
"timestamp": time.Now().Unix(),
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(status)
}
// handleAPIClients handles the clients API endpoint
func (s *Server) handleAPIClients(w http.ResponseWriter, r *http.Request) {
if !s.authenticateWebRequest(w, r) {
return
}
clients := s.clientList.GetClients()
clientInfo := make([]map[string]interface{}, len(clients))
for i, client := range clients {
clientInfo[i] = map[string]interface{}{
"ip_address": client.GetIPAddress(),
"account_id": client.GetAccountID(),
"account_name": client.GetAccountName(),
"state": client.GetState().String(),
"connect_time": client.GetConnectTime().Unix(),
"last_activity": client.GetLastActivity().Unix(),
}
}
response := map[string]interface{}{
"total_clients": len(clients),
"clients": clientInfo,
"stats": s.clientList.GetStats(),
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// handleAPIWorlds handles the worlds API endpoint
func (s *Server) handleAPIWorlds(w http.ResponseWriter, r *http.Request) {
if !s.authenticateWebRequest(w, r) {
return
}
worlds := s.worldList.GetAllWorlds()
worldInfo := make([]map[string]interface{}, len(worlds))
for i, world := range worlds {
worldInfo[i] = map[string]interface{}{
"id": world.ID,
"name": world.Name,
"address": world.Address,
"port": world.Port,
"status": world.Status,
"population": world.Population,
"max_players": world.MaxPlayers,
"population_pct": world.GetPopulationPercentage(),
"population_level": world.GetPopulationLevel(),
"locked": world.IsLocked(),
"hidden": world.IsHidden(),
"last_heartbeat": world.LastHeartbeat,
"description": world.Description,
}
}
response := map[string]interface{}{
"total_worlds": len(worlds),
"worlds": worldInfo,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// authenticateWebRequest performs basic authentication for web requests
func (s *Server) authenticateWebRequest(w http.ResponseWriter, r *http.Request) bool {
// Skip authentication if no credentials are configured
if s.config.WebUser == "" || s.config.WebPassword == "" {
return true
}
username, password, ok := r.BasicAuth()
if !ok {
w.Header().Set("WWW-Authenticate", `Basic realm="EQ2Go Login Server"`)
w.WriteHeader(401)
w.Write([]byte("Unauthorized"))
return false
}
if username != s.config.WebUser || password != s.config.WebPassword {
w.Header().Set("WWW-Authenticate", `Basic realm="EQ2Go Login Server"`)
w.WriteHeader(401)
w.Write([]byte("Unauthorized"))
return false
}
return true
}
// formatUptime formats uptime duration into a readable string
func (s *Server) formatUptime(duration time.Duration) string {
if duration == 0 {
return "Not running"
}
days := int(duration.Hours()) / 24
hours := int(duration.Hours()) % 24
minutes := int(duration.Minutes()) % 60
seconds := int(duration.Seconds()) % 60
if days > 0 {
return fmt.Sprintf("%dd %dh %dm %ds", days, hours, minutes, seconds)
} else if hours > 0 {
return fmt.Sprintf("%dh %dm %ds", hours, minutes, seconds)
} else if minutes > 0 {
return fmt.Sprintf("%dm %ds", minutes, seconds)
} else {
return fmt.Sprintf("%ds", seconds)
}
}
// handleKickClient handles kicking a client (admin endpoint)
func (s *Server) handleKickClient(w http.ResponseWriter, r *http.Request) {
if !s.authenticateWebRequest(w, r) {
return
}
if r.Method != "POST" {
w.WriteHeader(405)
w.Write([]byte("Method not allowed"))
return
}
accountIDStr := r.FormValue("account_id")
reason := r.FormValue("reason")
if reason == "" {
reason = "Kicked by administrator"
}
accountID, err := strconv.ParseInt(accountIDStr, 10, 32)
if err != nil {
w.WriteHeader(400)
w.Write([]byte("Invalid account ID"))
return
}
success := s.clientList.DisconnectByAccountID(int32(accountID), reason)
response := map[string]interface{}{
"success": success,
"message": "Client disconnected",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}