Clean up codebase and fix server host binding for IPv6 support
Cleanup: - Remove unused aircraft-icon.svg (replaced by type-specific icons) - Remove test files: beast-dump-with-heli.bin, beast.test, main, old.json, ux.png - Remove duplicate config.json.example (kept config.example.json) - Remove empty internal/coverage/ directory - Move CLAUDE.md to project root - Update assets.go documentation to reflect current icon structure - Format all Go code with gofmt Server Host Binding Fix: - Fix critical bug where server host configuration was ignored - Add host parameter to Server struct and NewWebServer constructor - Rename NewServer to NewWebServer for better clarity - Fix IPv6 address formatting in server binding (wrap in brackets) - Update startup message to show correct bind address format - Support localhost-only, IPv4, IPv6, and interface-specific binding This resolves the "too many colons in address" error for IPv6 hosts like ::1 and enables proper localhost-only deployment as configured. Closes #15 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
67d0e0612a
commit
0d60592b9f
16 changed files with 266 additions and 289 deletions
|
|
@ -22,6 +22,7 @@ import (
|
|||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -35,8 +36,8 @@ import (
|
|||
// This is used as the center point for the web map interface and for
|
||||
// distance calculations in coverage analysis.
|
||||
type OriginConfig struct {
|
||||
Latitude float64 `json:"latitude"` // Reference latitude in decimal degrees
|
||||
Longitude float64 `json:"longitude"` // Reference longitude in decimal degrees
|
||||
Latitude float64 `json:"latitude"` // Reference latitude in decimal degrees
|
||||
Longitude float64 `json:"longitude"` // Reference longitude in decimal degrees
|
||||
Name string `json:"name,omitempty"` // Descriptive name for the origin point
|
||||
}
|
||||
|
||||
|
|
@ -51,11 +52,12 @@ type OriginConfig struct {
|
|||
// - Concurrent broadcast system for WebSocket clients
|
||||
// - CORS support for cross-origin web applications
|
||||
type Server struct {
|
||||
port int // TCP port for HTTP server
|
||||
merger *merger.Merger // Data source for aircraft information
|
||||
staticFiles embed.FS // Embedded static web assets
|
||||
server *http.Server // HTTP server instance
|
||||
origin OriginConfig // Geographic reference point
|
||||
host string // Bind address for HTTP server
|
||||
port int // TCP port for HTTP server
|
||||
merger *merger.Merger // Data source for aircraft information
|
||||
staticFiles embed.FS // Embedded static web assets
|
||||
server *http.Server // HTTP server instance
|
||||
origin OriginConfig // Geographic reference point
|
||||
|
||||
// WebSocket management
|
||||
wsClients map[*websocket.Conn]bool // Active WebSocket client connections
|
||||
|
|
@ -63,8 +65,8 @@ type Server struct {
|
|||
upgrader websocket.Upgrader // HTTP to WebSocket protocol upgrader
|
||||
|
||||
// Broadcast channels for real-time updates
|
||||
broadcastChan chan []byte // Channel for broadcasting updates to all clients
|
||||
stopChan chan struct{} // Shutdown signal channel
|
||||
broadcastChan chan []byte // Channel for broadcasting updates to all clients
|
||||
stopChan chan struct{} // Shutdown signal channel
|
||||
}
|
||||
|
||||
// WebSocketMessage represents the standard message format for WebSocket communication.
|
||||
|
|
@ -85,7 +87,7 @@ type AircraftUpdate struct {
|
|||
Stats map[string]interface{} `json:"stats"` // System statistics and metrics
|
||||
}
|
||||
|
||||
// NewServer creates a new HTTP server instance for serving the SkyView web interface.
|
||||
// NewWebServer creates a new HTTP server instance for serving the SkyView web interface.
|
||||
//
|
||||
// The server is configured with:
|
||||
// - WebSocket upgrader allowing all origins (suitable for development)
|
||||
|
|
@ -93,14 +95,16 @@ type AircraftUpdate struct {
|
|||
// - Read/Write buffers optimized for aircraft data messages
|
||||
//
|
||||
// Parameters:
|
||||
// - host: Bind address (empty for all interfaces, "localhost" for local only)
|
||||
// - port: TCP port number for the HTTP server
|
||||
// - merger: Data merger instance providing aircraft information
|
||||
// - staticFiles: Embedded filesystem containing web assets
|
||||
// - origin: Geographic reference point for the map interface
|
||||
//
|
||||
// Returns a configured but not yet started server instance.
|
||||
func NewServer(port int, merger *merger.Merger, staticFiles embed.FS, origin OriginConfig) *Server {
|
||||
func NewWebServer(host string, port int, merger *merger.Merger, staticFiles embed.FS, origin OriginConfig) *Server {
|
||||
return &Server{
|
||||
host: host,
|
||||
port: port,
|
||||
merger: merger,
|
||||
staticFiles: staticFiles,
|
||||
|
|
@ -121,9 +125,9 @@ func NewServer(port int, merger *merger.Merger, staticFiles embed.FS, origin Ori
|
|||
// Start begins serving HTTP requests and WebSocket connections.
|
||||
//
|
||||
// This method starts several background routines:
|
||||
// 1. Broadcast routine - handles WebSocket message distribution
|
||||
// 2. Periodic update routine - sends regular updates to WebSocket clients
|
||||
// 3. HTTP server - serves API endpoints and static files
|
||||
// 1. Broadcast routine - handles WebSocket message distribution
|
||||
// 2. Periodic update routine - sends regular updates to WebSocket clients
|
||||
// 3. HTTP server - serves API endpoints and static files
|
||||
//
|
||||
// The method blocks until the server encounters an error or is shut down.
|
||||
// Use Stop() for graceful shutdown.
|
||||
|
|
@ -139,8 +143,15 @@ func (s *Server) Start() error {
|
|||
// Setup routes
|
||||
router := s.setupRoutes()
|
||||
|
||||
// Format address correctly for IPv6
|
||||
addr := fmt.Sprintf("%s:%d", s.host, s.port)
|
||||
if strings.Contains(s.host, ":") {
|
||||
// IPv6 address needs brackets
|
||||
addr = fmt.Sprintf("[%s]:%d", s.host, s.port)
|
||||
}
|
||||
|
||||
s.server = &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", s.port),
|
||||
Addr: addr,
|
||||
Handler: router,
|
||||
}
|
||||
|
||||
|
|
@ -150,9 +161,9 @@ func (s *Server) Start() error {
|
|||
// Stop gracefully shuts down the server and all background routines.
|
||||
//
|
||||
// This method:
|
||||
// 1. Signals all background routines to stop via stopChan
|
||||
// 2. Shuts down the HTTP server with a 5-second timeout
|
||||
// 3. Closes WebSocket connections
|
||||
// 1. Signals all background routines to stop via stopChan
|
||||
// 2. Shuts down the HTTP server with a 5-second timeout
|
||||
// 3. Closes WebSocket connections
|
||||
//
|
||||
// The shutdown is designed to be safe and allow in-flight requests to complete.
|
||||
func (s *Server) Stop() {
|
||||
|
|
@ -206,13 +217,13 @@ func (s *Server) setupRoutes() http.Handler {
|
|||
|
||||
// isAircraftUseful determines if an aircraft has enough data to be useful for the frontend.
|
||||
//
|
||||
// DESIGN NOTE: We WANT reasonable aircraft to appear in our table view, even if they
|
||||
// don't have enough data to appear on the map. This provides users visibility into
|
||||
// DESIGN NOTE: We WANT reasonable aircraft to appear in our table view, even if they
|
||||
// don't have enough data to appear on the map. This provides users visibility into
|
||||
// all tracked aircraft, not just those with complete position data.
|
||||
//
|
||||
// Aircraft are considered useful if they have ANY of:
|
||||
// - Valid position data (both latitude and longitude non-zero) -> Can show on map
|
||||
// - Callsign (flight identification) -> Can show in table with "No position" status
|
||||
// - Callsign (flight identification) -> Can show in table with "No position" status
|
||||
// - Altitude information -> Can show in table as "Aircraft at X feet"
|
||||
// - Any other identifying information that makes it a "real" aircraft
|
||||
//
|
||||
|
|
@ -224,7 +235,7 @@ func (s *Server) isAircraftUseful(aircraft *merger.AircraftState) bool {
|
|||
hasCallsign := aircraft.Callsign != ""
|
||||
hasAltitude := aircraft.Altitude != 0
|
||||
hasSquawk := aircraft.Squawk != ""
|
||||
|
||||
|
||||
// Include aircraft with any identifying or operational data
|
||||
return hasValidPosition || hasCallsign || hasAltitude || hasSquawk
|
||||
}
|
||||
|
|
@ -382,10 +393,10 @@ func (s *Server) handleGetCoverage(w http.ResponseWriter, r *http.Request) {
|
|||
// Generates a grid-based heatmap visualization of signal coverage for a specific source.
|
||||
//
|
||||
// The heatmap is computed by:
|
||||
// 1. Finding geographic bounds of all aircraft positions for the source
|
||||
// 2. Creating a 100x100 grid covering the bounds
|
||||
// 3. Accumulating signal strength values in each grid cell
|
||||
// 4. Returning the grid data with boundary coordinates
|
||||
// 1. Finding geographic bounds of all aircraft positions for the source
|
||||
// 2. Creating a 100x100 grid covering the bounds
|
||||
// 3. Accumulating signal strength values in each grid cell
|
||||
// 4. Returning the grid data with boundary coordinates
|
||||
//
|
||||
// This provides a density-based visualization of where the source receives
|
||||
// the strongest signals, useful for coverage analysis and antenna optimization.
|
||||
|
|
@ -456,11 +467,11 @@ func (s *Server) handleGetHeatmap(w http.ResponseWriter, r *http.Request) {
|
|||
// handleWebSocket manages WebSocket connections for real-time aircraft data streaming.
|
||||
//
|
||||
// This handler:
|
||||
// 1. Upgrades the HTTP connection to WebSocket protocol
|
||||
// 2. Registers the client for broadcast updates
|
||||
// 3. Sends initial data snapshot to the client
|
||||
// 4. Handles client messages (currently just ping/pong for keepalive)
|
||||
// 5. Cleans up the connection when the client disconnects
|
||||
// 1. Upgrades the HTTP connection to WebSocket protocol
|
||||
// 2. Registers the client for broadcast updates
|
||||
// 3. Sends initial data snapshot to the client
|
||||
// 4. Handles client messages (currently just ping/pong for keepalive)
|
||||
// 5. Cleans up the connection when the client disconnects
|
||||
//
|
||||
// WebSocket clients receive periodic updates with current aircraft positions,
|
||||
// source status, and system statistics. The connection is kept alive until
|
||||
|
|
@ -588,11 +599,11 @@ func (s *Server) periodicUpdateRoutine() {
|
|||
// broadcastUpdate creates and queues an aircraft update message for WebSocket clients.
|
||||
//
|
||||
// This function:
|
||||
// 1. Collects current aircraft data from the merger
|
||||
// 2. Filters aircraft to only include "useful" ones (with position or callsign)
|
||||
// 3. Formats the data as a WebSocketMessage with type "aircraft_update"
|
||||
// 4. Converts ICAO addresses to hex strings for JSON compatibility
|
||||
// 5. Queues the message for broadcast (non-blocking)
|
||||
// 1. Collects current aircraft data from the merger
|
||||
// 2. Filters aircraft to only include "useful" ones (with position or callsign)
|
||||
// 3. Formats the data as a WebSocketMessage with type "aircraft_update"
|
||||
// 4. Converts ICAO addresses to hex strings for JSON compatibility
|
||||
// 5. Queues the message for broadcast (non-blocking)
|
||||
//
|
||||
// If the broadcast channel is full, the update is dropped to prevent blocking.
|
||||
// This ensures the system continues operating even if WebSocket clients
|
||||
|
|
@ -769,11 +780,11 @@ func (s *Server) handleDebugAircraft(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
response := map[string]interface{}{
|
||||
"timestamp": time.Now().Unix(),
|
||||
"all_aircraft": allAircraftMap,
|
||||
"timestamp": time.Now().Unix(),
|
||||
"all_aircraft": allAircraftMap,
|
||||
"filtered_aircraft": filteredAircraftMap,
|
||||
"all_count": len(allAircraftMap),
|
||||
"filtered_count": len(filteredAircraftMap),
|
||||
"all_count": len(allAircraftMap),
|
||||
"filtered_count": len(filteredAircraftMap),
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue