v0.151.0 — the Megosztás page stops reloading, and says how to connect
S-1: /sharing/status coerced idle->running on the PHASE channel, so the first poll of every steady-state page load reported a terminal job that never ran and the client's repaint-reload fired ~1.2s apart, forever. The coercion's real duty (liveness must never be contradicted) belongs to the 'running' LEVEL field beside it, and is now pinned by its own regression test. S-4 core: a terminal 'running' is served exactly once, so a REAL bring-up cannot re-arm the reload on the page it just caused. failed/needs_password/in-flight are never consumed. Unified async-job feedback stays the ROADMAP item. S-2/S-5: new connect card with the Windows form, the Mac form and the direct smb://<IP>, read from the SAMBA container's netns (the controller is on a docker bridge and would answer 172.x). Derived per render, cached nowhere - the address is a DHCP lease. Underivable => the line is omitted. sharing.html's <script> block is byte-identical to v0.150.0. Red-proofed three ways. 23/23 packages green.
This commit is contained in:
@@ -122,7 +122,17 @@ func (s *Server) sharingPageData() map[string]interface{} {
|
||||
data["SMBEnabled"] = smb.Enabled
|
||||
data["SMBServerName"] = smb.EffectiveServerName()
|
||||
data["SMBUserSet"] = smb.UserSet
|
||||
data["SMBRunning"] = s.stackMgr.SambaRunning()
|
||||
data["SMBRunning"] = s.stackMgr != nil && s.stackMgr.SambaRunning()
|
||||
|
||||
// „Csatlakozás a megosztáshoz" (v0.151.0, S-2): the page has always shown the configured NAME and
|
||||
// never an address, so a customer whose network does not resolve the name had nothing to fall
|
||||
// back on and guessed — which is how this diagnosis started, with the Proxmox HOST's IP typed
|
||||
// into Finder (DIAG-sharing-2026-07-20.md). Derived FRESH on every render and cached nowhere:
|
||||
// the guest holds this address by DHCP, so a stored copy is a copy that eventually misdirects
|
||||
// people (S-5). "" simply omits the line — an address-less page beats a wrong address.
|
||||
if smb.Enabled {
|
||||
data["SMBDirectAddress"] = s.sambaLANAddress()
|
||||
}
|
||||
|
||||
type shareRow struct {
|
||||
Name string
|
||||
@@ -177,6 +187,18 @@ func (s *Server) sharingPageData() map[string]interface{} {
|
||||
return data
|
||||
}
|
||||
|
||||
// sambaLANAddress resolves the connect-address seam. Never cached at this level either — the seam
|
||||
// exists so tests can supply an address without docker, not so anyone can memoize one.
|
||||
func (s *Server) sambaLANAddress() string {
|
||||
if s.sambaAddrFn != nil {
|
||||
return s.sambaAddrFn()
|
||||
}
|
||||
if s.stackMgr == nil {
|
||||
return ""
|
||||
}
|
||||
return s.stackMgr.SambaLANAddress()
|
||||
}
|
||||
|
||||
func (s *Server) sharingPageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := s.sharingPageData()
|
||||
if f := strings.TrimSpace(r.URL.Query().Get("flash")); f != "" {
|
||||
@@ -231,20 +253,28 @@ func (s *Server) sharingEnableHandler(w http.ResponseWriter, r *http.Request) {
|
||||
sharingRedirect(w, r, "Beállítás mentve. A megosztási szolgáltatás előkészítése folyamatban…")
|
||||
}
|
||||
|
||||
// sharingStatusHandler is the 4b poll target (GET /sharing/status). Reports the ensure job's phase
|
||||
// plus the live container state, so a page loaded AFTER the job finished (or after a restart, when
|
||||
// the in-memory job is gone) still shows the truth.
|
||||
// sharingStatusHandler is the 4b poll target (GET /sharing/status). It carries TWO independent
|
||||
// channels in one envelope, and keeping them apart is the whole point of this handler:
|
||||
//
|
||||
// phase — the ensure JOB. The client treats a terminal `running` as an EDGE ("the bring-up I was
|
||||
// watching just succeeded") and reloads once to repaint the server-rendered badge.
|
||||
// running — the service LEVEL, straight from the liveness probe. True whenever the container is up,
|
||||
// with or without a job, and it is what makes a page loaded after the job finished (or
|
||||
// after a controller restart, when the in-memory job is gone) still show the truth.
|
||||
//
|
||||
// v0.147.0 coerced `idle` → `running` here so that liveness could never be contradicted by a missing
|
||||
// job. That duty belongs to — and was already discharged by — the `running` field beside it; on the
|
||||
// phase channel the same value reads as a fresh terminal edge, so the client reloaded on the FIRST
|
||||
// poll of every steady-state page load and the page looped at ~1.2s forever
|
||||
// (felhom.eu/documentation/audits/DIAG-sharing-2026-07-20.md, S-1). The coercion is gone: no job,
|
||||
// no edge. See consumeIfRunning for the other half — a REAL bring-up must be reported exactly once.
|
||||
func (s *Server) sharingStatusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
phase := sambaPhaseIdle
|
||||
errMsg := ""
|
||||
if job := s.sambaEnsure.snapshot(); job != nil {
|
||||
if job := s.sambaEnsure.consumeIfRunning(); job != nil {
|
||||
phase, errMsg = job.Phase, job.Error
|
||||
}
|
||||
running := s.stackMgr != nil && s.stackMgr.SambaRunning()
|
||||
// A stale `idle`/`running` job must never contradict reality: liveness wins on a fresh page.
|
||||
if phase == sambaPhaseIdle && running {
|
||||
phase = sambaPhaseRunning
|
||||
}
|
||||
writeDiskJSON(w, http.StatusOK, true, "", map[string]any{
|
||||
"phase": phase, "error": errMsg, "running": running,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user