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:
Ole-Morten Duesund 2025-08-24 18:36:14 +02:00
commit 0d60592b9f
16 changed files with 266 additions and 289 deletions

View file

@ -111,7 +111,7 @@ func (a *AircraftState) MarshalJSON() ([]byte, error) {
SelectedAltitude int `json:"SelectedAltitude"`
SelectedHeading float64 `json:"SelectedHeading"`
BaroSetting float64 `json:"BaroSetting"`
// From AircraftState
Sources map[string]*SourceData `json:"sources"`
LastUpdate time.Time `json:"last_update"`
@ -155,7 +155,7 @@ func (a *AircraftState) MarshalJSON() ([]byte, error) {
SelectedAltitude: a.Aircraft.SelectedAltitude,
SelectedHeading: a.Aircraft.SelectedHeading,
BaroSetting: a.Aircraft.BaroSetting,
// Copy all fields from AircraftState
Sources: a.Sources,
LastUpdate: a.LastUpdate,
@ -238,7 +238,7 @@ type Merger struct {
sources map[string]*Source // Source ID -> source information
icaoDB *icao.Database // ICAO country lookup database
mu sync.RWMutex // Protects all maps and slices
historyLimit int // Maximum history points to retain
historyLimit int // Maximum history points to retain
staleTimeout time.Duration // Time before aircraft considered stale (15 seconds)
updateMetrics map[uint32]*updateMetric // ICAO24 -> update rate calculation data
}
@ -291,13 +291,13 @@ func (m *Merger) AddSource(source *Source) {
// UpdateAircraft merges new aircraft data from a source using intelligent fusion strategies.
//
// This is the core method of the merger, handling:
// 1. Aircraft state creation for new aircraft
// 2. Source data tracking and statistics
// 3. Multi-source data fusion with conflict resolution
// 4. Historical data updates with retention limits
// 5. Distance and bearing calculations
// 6. Update rate metrics
// 7. Source status maintenance
// 1. Aircraft state creation for new aircraft
// 2. Source data tracking and statistics
// 3. Multi-source data fusion with conflict resolution
// 4. Historical data updates with retention limits
// 5. Distance and bearing calculations
// 6. Update rate metrics
// 7. Source status maintenance
//
// Data fusion strategies:
// - Position: Use source with strongest signal
@ -326,7 +326,7 @@ func (m *Merger) UpdateAircraft(sourceID string, aircraft *modes.Aircraft, signa
AltitudeHistory: make([]AltitudePoint, 0),
SpeedHistory: make([]SpeedPoint, 0),
}
// Lookup country information for new aircraft
icaoHex := fmt.Sprintf("%06X", aircraft.ICAO24)
if countryInfo, err := m.icaoDB.LookupCountry(icaoHex); err == nil {
@ -339,7 +339,7 @@ func (m *Merger) UpdateAircraft(sourceID string, aircraft *modes.Aircraft, signa
state.CountryCode = "XX"
state.Flag = "🏳️"
}
m.aircraft[aircraft.ICAO24] = state
m.updateMetrics[aircraft.ICAO24] = &updateMetric{
updates: make([]time.Time, 0),
@ -585,10 +585,10 @@ func (m *Merger) updateHistories(state *AircraftState, aircraft *modes.Aircraft,
// updateUpdateRate calculates and maintains the message update rate for an aircraft.
//
// The calculation:
// 1. Records the timestamp of each update
// 2. Maintains a sliding 30-second window of updates
// 3. Calculates updates per second over this window
// 4. Updates the aircraft's UpdateRate field
// 1. Records the timestamp of each update
// 2. Maintains a sliding 30-second window of updates
// 3. Calculates updates per second over this window
// 4. Updates the aircraft's UpdateRate field
//
// This provides real-time feedback on data quality and can help identify
// aircraft that are updating frequently (close, good signal) vs infrequently
@ -644,10 +644,10 @@ func (m *Merger) getBestSignalSource(state *AircraftState) string {
// GetAircraft returns a snapshot of all current aircraft states.
//
// This method:
// 1. Filters out stale aircraft (older than staleTimeout)
// 2. Calculates current age for each aircraft
// 3. Determines closest receiver distance and bearing
// 4. Returns copies to prevent external modification
// 1. Filters out stale aircraft (older than staleTimeout)
// 2. Calculates current age for each aircraft
// 3. Determines closest receiver distance and bearing
// 4. Returns copies to prevent external modification
//
// The returned map uses ICAO24 addresses as keys and can be safely
// used by multiple goroutines without affecting the internal state.
@ -813,7 +813,7 @@ func calculateDistanceBearing(lat1, lon1, lat2, lon2 float64) (float64, float64)
func (m *Merger) Close() error {
m.mu.Lock()
defer m.mu.Unlock()
if m.icaoDB != nil {
return m.icaoDB.Close()
}