v0.308.0 (R-554, held-back-able): recovery-info.txt points at the dashboard's Restore page and the recovery code; the first-boot setup wizard is deleted
Operator ruling 2026-10-12 (S27, 09 §3 decision 222). The operator approves the Hungarian text before release. A box whose first ingestion failed now serves one bilingual waiting page and retries every minute instead of the wizard. Own commit so the lead can hold it back. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -108,10 +108,6 @@ func (s *Server) handleDebugAPI(w http.ResponseWriter, r *http.Request) {
|
||||
case subpath == "selfupdate/dry-run" && r.Method == http.MethodPost:
|
||||
s.debugSelfUpdateDryRun(w, r)
|
||||
|
||||
// Section 7: DR / Setup
|
||||
case subpath == "dr/trigger-setup" && r.Method == http.MethodPost:
|
||||
s.debugTriggerSetupWizard(w, r)
|
||||
|
||||
// Section 8: Log viewer
|
||||
case subpath == "logs" && r.Method == http.MethodGet:
|
||||
s.debugLogBuffer(w, r)
|
||||
@@ -686,38 +682,6 @@ func (s *Server) debugSelfUpdateDryRun(w http.ResponseWriter, r *http.Request) {
|
||||
writeDebugJSON(w, http.StatusOK, true, "", result)
|
||||
}
|
||||
|
||||
// ── Section 7: DR / Setup ───────────────────────────────────────────
|
||||
|
||||
func (s *Server) debugTriggerSetupWizard(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Confirm string `json:"confirm"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeDebugJSON(w, http.StatusBadRequest, false, s.msg(r, "debug.api.invalid_request"), nil)
|
||||
return
|
||||
}
|
||||
if req.Confirm != "RESET" {
|
||||
writeDebugJSON(w, http.StatusBadRequest, false, s.msg(r, "debug.api.reset_confirm_invalid"), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Write marker file
|
||||
markerPath := filepath.Join(s.cfg.Paths.DataDir, ".needs-setup")
|
||||
if err := os.WriteFile(markerPath, []byte("debug-triggered\n"), 0644); err != nil {
|
||||
writeDebugJSON(w, http.StatusInternalServerError, false,
|
||||
s.msg(r, "debug.api.marker_write_failed", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
writeDebugJSON(w, http.StatusOK, true, s.msg(r, "debug.api.restarting_to_setup"), nil)
|
||||
|
||||
// Exit after response is sent so the container restarts into setup mode
|
||||
go func() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
||||
|
||||
// ── Section 8: Log viewer ───────────────────────────────────────────
|
||||
|
||||
func (s *Server) debugLogBuffer(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -51,6 +51,8 @@ func TestR400_DeletedControlsAreGoneFromTheTemplate(t *testing.T) {
|
||||
{"/api/debug/storage/simulate-disconnect",
|
||||
"no backing capability, and it WRITES storage state — a button that fakes a drive disconnect on a customer's machine is a foot-gun"},
|
||||
{"/api/debug/storage/simulate-reconnect", "same as simulate-disconnect"},
|
||||
{"/api/debug/dr/trigger-setup",
|
||||
"R-554 (v0.308.0): it wrote .needs-setup and restarted the box into the first-boot setup wizard, which is deleted"},
|
||||
}
|
||||
for _, d := range deleted {
|
||||
if strings.Contains(src, d.ref) {
|
||||
@@ -60,7 +62,7 @@ func TestR400_DeletedControlsAreGoneFromTheTemplate(t *testing.T) {
|
||||
|
||||
// POSITIVE CONTROL. Without it this test passes against a template it failed to read, or one that
|
||||
// was emptied — the exact shape of an assertion that proves nothing.
|
||||
for _, kept := range []string{"/api/debug/backup/integrity", "/api/debug/dr/trigger-setup"} {
|
||||
for _, kept := range []string{"/api/debug/backup/integrity", "/api/debug/hub/push"} {
|
||||
if !strings.Contains(src, kept) {
|
||||
t.Fatalf("positive control failed: %s is missing too, so the assertions above prove nothing "+
|
||||
"about what was deliberately deleted", kept)
|
||||
@@ -85,6 +87,9 @@ func TestR400_DeletedControlsLeftNoPanelOrScript(t *testing.T) {
|
||||
"btn-infra-backup",
|
||||
"btn-hub-infra",
|
||||
"section-storage",
|
||||
"btn-dr-trigger", // R-554: the setup-wizard trigger and its panel
|
||||
"triggerDR",
|
||||
"section-dr",
|
||||
} {
|
||||
if strings.Contains(src, orphan) {
|
||||
t.Errorf("%q survived the deletion — an orphaned panel or handler renders nothing forever "+
|
||||
@@ -92,7 +97,7 @@ func TestR400_DeletedControlsLeftNoPanelOrScript(t *testing.T) {
|
||||
}
|
||||
}
|
||||
// POSITIVE CONTROL: the neighbours that must stay.
|
||||
for _, kept := range []string{"btn-dr-trigger", "triggerDR", "section-dr", "btn-crossdrive"} {
|
||||
for _, kept := range []string{"section-appexport", "btn-crossdrive"} {
|
||||
if !strings.Contains(src, kept) {
|
||||
t.Fatalf("positive control failed: %q is gone too — the deletion took a neighbour with it", kept)
|
||||
}
|
||||
|
||||
@@ -163,8 +163,6 @@ func TestR516_WidenedFormalFormsAreGone(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := map[string]string{
|
||||
"debug.irja_be_reset": "Írd be: RESET",
|
||||
"debug.api.reset_confirm_invalid": "Érvénytelen megerősítés — írd be: RESET",
|
||||
"settings_security.adja_meg_a_jelenlegi_jelszavat": "Add meg a jelenlegi jelszavad",
|
||||
"storage.biztosan_eltavolitja_adattarolot": "Biztosan eltávolítod a(z) {{.Path}} adattárolót?",
|
||||
"storage_network.engedelyezze_a_felhom_gep_ip": "Engedélyezd a Felhom gép IP-címét a megosztáson. Proxmox-fürt esetén minden csomópont IP-címét.",
|
||||
|
||||
@@ -149,28 +149,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 7: DR / Setup Wizard -->
|
||||
<div class="card debug-section" id="section-dr">
|
||||
<div class="card-header debug-section-header" onclick="toggleSection('dr')">
|
||||
<h3>{{T "debug.dr_telepito_varazslo"}}</h3>
|
||||
<span class="section-toggle">▶</span>
|
||||
</div>
|
||||
<div class="card-body debug-section-body" style="display:none">
|
||||
<div class="debug-actions" style="margin-top:1rem">
|
||||
<div class="debug-dr-danger">
|
||||
<p style="color:var(--crit);font-weight:600">{{T "debug.veszhelyzet_szimulacio"}}</p>
|
||||
<p class="text-muted" style="font-size:.85rem;margin-bottom:.5rem">{{T "debug.ez_torli_az_ugyfel_azonositot"}}</p>
|
||||
<div class="debug-form-row">
|
||||
<label>{{T "debug.irja_be_reset_a_megerositeshez"}}</label>
|
||||
<input type="text" id="dr-confirm-input" class="form-control debug-confirm-input" placeholder="RESET" autocomplete="off">
|
||||
<button class="btn btn-danger btn-sm" id="btn-dr-trigger" data-label="{{T "debug.ujrainditas_setup_modban"}}" onclick="triggerDR()">{{T "debug.ujrainditas_setup_modban"}}</button>
|
||||
<span class="debug-result" id="btn-dr-trigger-result"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 9: App Export/Import -->
|
||||
<div class="card debug-section" id="section-appexport">
|
||||
<div class="card-header debug-section-header" onclick="toggleSection('appexport')">
|
||||
@@ -452,43 +430,6 @@ function loadSelfUpdateStatus() {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Section 7: DR ──
|
||||
function triggerDR() {
|
||||
var input = document.getElementById('dr-confirm-input');
|
||||
if (input.value !== 'RESET') {
|
||||
var result = document.getElementById('btn-dr-trigger-result');
|
||||
result.className = 'debug-result debug-result-error';
|
||||
result.textContent = '{{T "debug.irja_be_reset"}}';
|
||||
return;
|
||||
}
|
||||
var btn = document.getElementById('btn-dr-trigger');
|
||||
felhomConfirm(btn, '{{T "debug.figyelem_ez_ujrainditja_a_controllert"}}', function () {
|
||||
var result = document.getElementById('btn-dr-trigger-result');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '{{T "debug.folyamatban"}}';
|
||||
fetch('/api/debug/dr/trigger-setup', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type':'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({confirm: 'RESET'})
|
||||
}).then(function(r){return r.json()}).then(function(data) {
|
||||
if (data.ok) {
|
||||
result.className = 'debug-result debug-result-ok';
|
||||
result.textContent = '{{T "debug.controller_ujraindulasa"}}';
|
||||
} else {
|
||||
result.className = 'debug-result debug-result-error';
|
||||
result.textContent = data.error || '{{T "debug.hiba"}}';
|
||||
btn.disabled = false;
|
||||
btn.textContent = btn.dataset.label;
|
||||
}
|
||||
}).catch(function(e) {
|
||||
result.className = 'debug-result debug-result-error';
|
||||
result.textContent = '{{T "debug.hiba_2"}} ' + e.message;
|
||||
btn.disabled = false;
|
||||
btn.textContent = btn.dataset.label;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── Section 8: Log viewer (Vezérlő | Ügynök tabs — v0.116.0) ──
|
||||
var currentLogLevel = 'DEBUG';
|
||||
var currentLogSource = 'controller';
|
||||
|
||||
@@ -321,28 +321,6 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card debug-section" id="section-dr">
|
||||
<div class="card-header debug-section-header" onclick="toggleSection('dr')">
|
||||
<h3>DR / Telepítő varázsló</h3>
|
||||
<span class="section-toggle">▶</span>
|
||||
</div>
|
||||
<div class="card-body debug-section-body" style="display:none">
|
||||
<div class="debug-actions" style="margin-top:1rem">
|
||||
<div class="debug-dr-danger">
|
||||
<p style="color:var(--crit);font-weight:600">Vészhelyzet szimuláció</p>
|
||||
<p class="text-muted" style="font-size:.85rem;margin-bottom:.5rem">Ez törli az ügyfél azonosítót és újraindítja a controllert setup módba. A konfigurációs fájlok megmaradnak a meghajtókon.</p>
|
||||
<div class="debug-form-row">
|
||||
<label>Írd be "RESET" a megerősítéshez:</label>
|
||||
<input type="text" id="dr-confirm-input" class="form-control debug-confirm-input" placeholder="RESET" autocomplete="off">
|
||||
<button class="btn btn-danger btn-sm" id="btn-dr-trigger" data-label="Újraindítás setup módban" onclick="triggerDR()">Újraindítás setup módban</button>
|
||||
<span class="debug-result" id="btn-dr-trigger-result"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card debug-section" id="section-appexport">
|
||||
<div class="card-header debug-section-header" onclick="toggleSection('appexport')">
|
||||
<h3>Alkalmazás Export/Import</h3>
|
||||
@@ -624,43 +602,6 @@ function loadSelfUpdateStatus() {
|
||||
}
|
||||
|
||||
|
||||
function triggerDR() {
|
||||
var input = document.getElementById('dr-confirm-input');
|
||||
if (input.value !== 'RESET') {
|
||||
var result = document.getElementById('btn-dr-trigger-result');
|
||||
result.className = 'debug-result debug-result-error';
|
||||
result.textContent = 'Írd be: RESET';
|
||||
return;
|
||||
}
|
||||
var btn = document.getElementById('btn-dr-trigger');
|
||||
felhomConfirm(btn, 'FIGYELEM! Ez újraindítja a controllert setup módba. Biztosan folytatod?', function () {
|
||||
var result = document.getElementById('btn-dr-trigger-result');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Folyamatban...';
|
||||
fetch('/api/debug/dr/trigger-setup', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type':'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({confirm: 'RESET'})
|
||||
}).then(function(r){return r.json()}).then(function(data) {
|
||||
if (data.ok) {
|
||||
result.className = 'debug-result debug-result-ok';
|
||||
result.textContent = 'Controller újraindulása...';
|
||||
} else {
|
||||
result.className = 'debug-result debug-result-error';
|
||||
result.textContent = data.error || 'Hiba';
|
||||
btn.disabled = false;
|
||||
btn.textContent = btn.dataset.label;
|
||||
}
|
||||
}).catch(function(e) {
|
||||
result.className = 'debug-result debug-result-error';
|
||||
result.textContent = 'Hiba: ' + e.message;
|
||||
btn.disabled = false;
|
||||
btn.textContent = btn.dataset.label;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var currentLogLevel = 'DEBUG';
|
||||
var currentLogSource = 'controller';
|
||||
var lastLogTimestamp = '';
|
||||
|
||||
Reference in New Issue
Block a user