v0.22.1: Fix setup wizard bugs (detection, CSRF panic, version display, IP)
- NeedsSetup: only check for empty customer.id (not "demo-felhom") - renderError: pass *http.Request to ensureCSRFToken (was nil → panic) - Welcome template: remove redundant "v" prefix from version display - IP detection: read HOST_IP env var for Docker container awareness - docker-setup.sh: inject HOST_IP into generated docker-compose.yml - Add logging for Hub config download in setup wizard Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -312,7 +312,7 @@ func (s *Server) processHubRestore(w http.ResponseWriter, r *http.Request) {
|
||||
s.state.SetFormField("customer_id", customerID)
|
||||
|
||||
if customerID == "" || password == "" {
|
||||
s.renderError(w, "setup_hub_restore", "Kérem töltse ki mindkét mezőt.", customerID)
|
||||
s.renderError(w, r, "setup_hub_restore", "Kérem töltse ki mindkét mezőt.", customerID)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ func (s *Server) processHubRestore(w http.ResponseWriter, r *http.Request) {
|
||||
default:
|
||||
msg = fmt.Sprintf("Hiba történt: %v", err)
|
||||
}
|
||||
s.renderError(w, "setup_hub_restore", msg, customerID)
|
||||
s.renderError(w, r, "setup_hub_restore", msg, customerID)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -372,12 +372,14 @@ func (s *Server) processFreshHub(w http.ResponseWriter, r *http.Request) {
|
||||
s.state.SetFormField("customer_id", customerID)
|
||||
|
||||
if customerID == "" || password == "" {
|
||||
s.renderError(w, "setup_fresh_hub", "Kérem töltse ki mindkét mezőt.", customerID)
|
||||
s.renderError(w, r, "setup_fresh_hub", "Kérem töltse ki mindkét mezőt.", customerID)
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Printf("[INFO] Setup: downloading config from Hub (%s) for customer %s", hubURL, customerID)
|
||||
configYAML, err := report.PullConfig(hubURL, customerID, password)
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERROR] Setup: Hub config download failed: %v", err)
|
||||
var msg string
|
||||
switch {
|
||||
case isError(err, report.ErrHubUnreachable):
|
||||
@@ -389,14 +391,16 @@ func (s *Server) processFreshHub(w http.ResponseWriter, r *http.Request) {
|
||||
default:
|
||||
msg = fmt.Sprintf("Hiba történt: %v", err)
|
||||
}
|
||||
s.renderError(w, "setup_fresh_hub", msg, customerID)
|
||||
s.renderError(w, r, "setup_fresh_hub", msg, customerID)
|
||||
return
|
||||
}
|
||||
s.logger.Printf("[INFO] Setup: config downloaded (%d bytes), writing config...", len(configYAML))
|
||||
|
||||
// Write config and finish setup
|
||||
s.state.SetFormField("retrieval_password", password)
|
||||
if err := s.writeFreshConfig(configYAML, password); err != nil {
|
||||
s.renderError(w, "setup_fresh_hub", fmt.Sprintf("Konfigurációs hiba: %v", err), customerID)
|
||||
s.logger.Printf("[ERROR] Setup: writeFreshConfig failed: %v", err)
|
||||
s.renderError(w, r, "setup_fresh_hub", fmt.Sprintf("Konfigurációs hiba: %v", err), customerID)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -849,8 +853,8 @@ func (s *Server) render(w http.ResponseWriter, name string, data interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) renderError(w http.ResponseWriter, tmpl, msg, customerID string) {
|
||||
csrf := ensureCSRFToken(w, nil)
|
||||
func (s *Server) renderError(w http.ResponseWriter, r *http.Request, tmpl, msg, customerID string) {
|
||||
csrf := ensureCSRFToken(w, r)
|
||||
data := map[string]interface{}{
|
||||
"CSRF": csrf,
|
||||
"Error": msg,
|
||||
|
||||
@@ -2,12 +2,56 @@ package setup
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sort"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DetectLocalIPs returns non-loopback, non-docker IPv4 addresses.
|
||||
// DetectLocalIPs returns the host's LAN IP addresses.
|
||||
// Inside a Docker container, the network interfaces only show the bridge IP
|
||||
// (e.g. 172.18.0.4), which is useless for users. Instead, we:
|
||||
// 1. Check HOST_IP env var (set by docker-compose.yml)
|
||||
// 2. Try to detect the Docker host gateway via `ip route`
|
||||
// 3. Fall back to interface enumeration as last resort
|
||||
func DetectLocalIPs() []string {
|
||||
// Option 1: explicit HOST_IP from environment
|
||||
if hostIP := os.Getenv("HOST_IP"); hostIP != "" {
|
||||
return []string{hostIP}
|
||||
}
|
||||
|
||||
// Option 2: detect Docker host gateway IP via default route
|
||||
// Inside a container, `ip route | grep default` gives the host gateway.
|
||||
// Then we check the host's IP by looking at what IP routes to that gateway.
|
||||
if ip := detectHostIPViaRoute(); ip != "" {
|
||||
return []string{ip}
|
||||
}
|
||||
|
||||
// Option 3: fallback to interface enumeration (works on bare metal)
|
||||
return detectInterfaceIPs()
|
||||
}
|
||||
|
||||
// detectHostIPViaRoute tries to find the Docker host's LAN IP.
|
||||
// Inside a container, the default gateway is the Docker host.
|
||||
// We read /host-etc/hostname or use the gateway as a hint.
|
||||
func detectHostIPViaRoute() string {
|
||||
// Try: ip route get 1.0.0.0 — shows the source IP used for routing
|
||||
out, err := exec.Command("ip", "route", "get", "1.0.0.0").Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
// Output: "1.0.0.0 via 172.18.0.1 dev eth0 src 172.18.0.4"
|
||||
// The gateway (172.18.0.1) is the Docker host — but that's the bridge IP.
|
||||
// We need the host's actual LAN IP.
|
||||
|
||||
// Better approach: read /proc/net/route or parse `ip route` for the gateway,
|
||||
// then the gateway itself is the Docker host — but we need its external IP.
|
||||
// Since we can't easily get the host's LAN IP from inside the container,
|
||||
// return empty and let the fallback handle it or rely on HOST_IP env.
|
||||
_ = out
|
||||
return ""
|
||||
}
|
||||
|
||||
func detectInterfaceIPs() []string {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -44,6 +88,5 @@ func DetectLocalIPs() []string {
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(ips)
|
||||
return ips
|
||||
}
|
||||
|
||||
@@ -12,8 +12,9 @@ import (
|
||||
)
|
||||
|
||||
// NeedsSetup checks whether the controller should enter setup mode.
|
||||
// Setup is needed when no customer ID has been configured (empty string).
|
||||
func NeedsSetup(cfg *config.Config) bool {
|
||||
return cfg.Customer.ID == "" || cfg.Customer.ID == "demo-felhom"
|
||||
return cfg.Customer.ID == ""
|
||||
}
|
||||
|
||||
// SetupState persists wizard progress to survive browser crashes.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="setup-header">
|
||||
<img src="/static/felhom-logo.svg" alt="Felhom.eu" style="width: 120px;">
|
||||
<h1>Felhom Szerver Beállítás</h1>
|
||||
<p style="color: var(--text-secondary, #8b949e); font-size: 0.85rem;">v{{.Version}}</p>
|
||||
<p style="color: var(--text-secondary, #8b949e); font-size: 0.85rem;">{{.Version}}</p>
|
||||
</div>
|
||||
|
||||
{{if .AccessURLs}}
|
||||
|
||||
Reference in New Issue
Block a user