b25ca60ab7
Root cause (live on Peti's box): the updater piggybacked on Git Sync creds and refused when absent, but the registry serves the public package anonymously (verified 2026-07-10). Credentials become what they were meant to be — optional, for private catalogs only. - queryRegistry: both creds empty → anonymous flow (plain GET → parse WWW-Authenticate realm/service from the header, never hardcoded → credential-free token → Bearer retry); creds present → BasicAuth path unchanged; half-configured pair → loud incomplete-credentials error - pullImage: no creds → skip docker login entirely (docker's native anonymous flow); denied anonymous access → clear 'registry denied anonymous access — a private registry requires Git Sync credentials' - settings page: 'Registry: nyilvános (hitelesítés nélkül) / hitelesített' mode line — credential-less is no longer an error state; DryRun.PullCapable counts anonymous as capable - tests: fake registry httptest token dance (zero creds, no auth on token request, correct scope), creds path unchanged (BasicAuth, no dance), both denial paths, WWW-Authenticate parser table (quoted/bare/order/comma-in-quotes/missing-realm), fake-runner pull tests (no login invoked anonymously; login/pull/logout order + stdin token with creds; partial creds refuse) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
308 lines
15 KiB
HTML
308 lines
15 KiB
HTML
{{define "settings_system"}}
|
||
{{template "layout_start" .}}
|
||
|
||
<div class="page-header">
|
||
<h2>Beállítások — Rendszer</h2>
|
||
</div>
|
||
|
||
<!-- Section A: System Configuration (read-only) -->
|
||
<div class="settings-card">
|
||
<h3>Rendszer konfiguráció</h3>
|
||
<p class="settings-card-desc">Az üzemeltető által beállított értékek. Módosításhoz kérd az üzemeltetőt.</p>
|
||
<div class="settings-grid">
|
||
<div class="settings-row">
|
||
<span class="settings-label">Ügyfél azonosító</span>
|
||
<span class="settings-value mono">{{.CustomerID}}</span>
|
||
</div>
|
||
<div class="settings-row">
|
||
<span class="settings-label">Ügyfél neve</span>
|
||
<span class="settings-value">{{.CustomerName}}</span>
|
||
</div>
|
||
<div class="settings-row">
|
||
<span class="settings-label">Domain</span>
|
||
<span class="settings-value mono">{{.CustomerDomain}}</span>
|
||
</div>
|
||
{{if .GitRepoURL}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Alkalmazás sablon forrás</span>
|
||
<span class="settings-value mono settings-value-truncate">{{.GitRepoURL}}</span>
|
||
</div>
|
||
{{end}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Sablon szinkronizálás</span>
|
||
<span class="settings-value mono">{{.GitSyncInterval}}</span>
|
||
</div>
|
||
<div class="settings-row">
|
||
<span class="settings-label">Biztonsági mentés</span>
|
||
<span class="settings-value">{{if .BackupEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Aktív</span>{{else}}<span class="state-text-neutral">Inaktív</span>{{end}}</span>
|
||
</div>
|
||
{{if .BackupEnabled}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Mentés ütemezés</span>
|
||
<span class="settings-value mono">{{.DBDumpSchedule}} / {{.ResticSchedule}}</span>
|
||
</div>
|
||
{{end}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Monitoring</span>
|
||
<span class="settings-value">{{if .MonitoringEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Aktív</span>{{else}}<span class="state-text-neutral">Inaktív</span>{{end}}</span>
|
||
</div>
|
||
{{if .MonitoringEnabled}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Healthchecks URL</span>
|
||
<span class="settings-value mono settings-value-truncate">{{if .HealthchecksBase}}{{.HealthchecksBase}}{{else}}–{{end}}</span>
|
||
</div>
|
||
{{end}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Hub jelentés</span>
|
||
<span class="settings-value">{{if .HubEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Aktív</span>{{else}}–{{end}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Section: Version & Update -->
|
||
<div class="settings-card">
|
||
<h3>Verzió és frissítés</h3>
|
||
<div class="settings-grid">
|
||
<div class="settings-row">
|
||
<span class="settings-label">Jelenlegi verzió</span>
|
||
<span class="settings-value mono">{{.Version}}</span>
|
||
</div>
|
||
{{if .SelfUpdateEnabled}}
|
||
{{if .LatestVersion}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Legújabb verzió</span>
|
||
<span class="settings-value mono">
|
||
{{.LatestVersion}}
|
||
{{if .UpdateAvailable}}
|
||
<span class="state-text-run" style="margin-left:0.5em;">● Frissítés elérhető</span>
|
||
{{else}}
|
||
<span style="margin-left:0.5em; color:var(--text-3);">— naprakész</span>
|
||
{{end}}
|
||
</span>
|
||
</div>
|
||
{{end}}
|
||
{{if .LastCheckTime}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Utolsó ellenőrzés</span>
|
||
<span class="settings-value mono">{{.LastCheckTime}}</span>
|
||
</div>
|
||
{{end}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Registry</span>
|
||
<span class="settings-value">{{if .RegistryAnonymous}}nyilvános (hitelesítés nélkül){{else}}hitelesített{{end}}</span>
|
||
</div>
|
||
{{if .LastCheckError}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Hiba</span>
|
||
<span class="settings-value state-text-crit">{{.LastCheckError}}</span>
|
||
</div>
|
||
{{end}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Automatikus frissítés</span>
|
||
<span class="settings-value">
|
||
{{if .AutoUpdateEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Aktív</span> <span class="mono">({{.AutoUpdateTime}})</span>{{else}}–{{end}}
|
||
</span>
|
||
</div>
|
||
{{if .ControllerFloor}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Minimális verzió (üzemeltető)</span>
|
||
<span class="settings-value mono">
|
||
{{.ControllerFloor}}
|
||
<span style="margin-left:0.5em; color:#888;">— a rendszer automatikusan erre a verzióra frissít, ha régebbi</span>
|
||
</span>
|
||
</div>
|
||
{{end}}
|
||
{{if .UpdateRunning}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Állapot</span>
|
||
<span class="settings-value state-text-progress" id="auto-update-status"><svg class="ico ico-sm ico-spin"><use href="#i-rotate-cw"/></svg> Frissítés folyamatban — a vezérlő hamarosan újraindul…</span>
|
||
</div>
|
||
{{end}}
|
||
{{with .LastUpdateState}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Utolsó frissítés</span>
|
||
<span class="settings-value">
|
||
{{if eq .Status "success"}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Sikeres</span> ({{.PreviousVersion}} → {{.TargetVersion}})
|
||
{{else if eq .Status "failed"}}<span class="state-text-crit"><svg class="ico ico-sm"><use href="#i-x"/></svg> Sikertelen</span> — {{.Error}}
|
||
{{else if eq .Status "pending"}}<span class="state-text-progress"><svg class="ico ico-sm ico-spin"><use href="#i-rotate-cw"/></svg> Folyamatban</span>
|
||
{{end}}
|
||
</span>
|
||
</div>
|
||
{{end}}
|
||
<div class="settings-row" style="padding-top: 0.5em;">
|
||
<span class="settings-label"></span>
|
||
<span class="settings-value">
|
||
<button class="btn btn-secondary btn-sm" id="btn-check-update" onclick="checkUpdate()">Frissítés keresése</button>
|
||
{{if .UpdateAvailable}}
|
||
<button class="btn btn-primary btn-sm" id="btn-trigger-update" onclick="triggerUpdate()" style="margin-left:0.5em;">Frissítés telepítése</button>
|
||
{{end}}
|
||
<span id="update-status-msg" style="margin-left:0.5em; display:none;"></span>
|
||
</span>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
|
||
<div id="dialog-root"></div>
|
||
<script>
|
||
// Light overlay dialog (D1) — replaces the native blocking browser dialogs (same texts).
|
||
function escText(s){var d=document.createElement('div');d.textContent=String(s==null?'':s);return d.innerHTML;}
|
||
function closeDialog(){ var r=document.getElementById('dialog-root'); if(r) r.innerHTML=''; }
|
||
function openDialog(opts){
|
||
var root=document.getElementById('dialog-root');
|
||
if(!root) return;
|
||
root.innerHTML='<div class="confirm-overlay" onclick="if(event.target===this)document.getElementById(\'dialog-cancel\').click()"><div class="confirm-box">'
|
||
+'<h3>'+escText(opts.title||'Megerősítés')+'</h3>'
|
||
+'<p style="white-space:pre-line">'+escText(opts.message||'')+'</p>'
|
||
+'<div class="form-actions"><button id="dialog-go" class="btn btn-primary">'+escText(opts.confirmLabel||'Megerősítés')+'</button>'
|
||
+'<button type="button" class="btn btn-outline" id="dialog-cancel">Mégsem</button></div>'
|
||
+'</div></div>';
|
||
document.getElementById('dialog-go').onclick=function(){ closeDialog(); if(opts.onConfirm) opts.onConfirm(); };
|
||
document.getElementById('dialog-cancel').onclick=function(){ closeDialog(); if(opts.onCancel) opts.onCancel(); };
|
||
}
|
||
|
||
function checkUpdate() {
|
||
var btn = document.getElementById('btn-check-update');
|
||
var msg = document.getElementById('update-status-msg');
|
||
btn.disabled = true;
|
||
btn.textContent = 'Ellenőrzés...';
|
||
msg.style.display = 'none';
|
||
fetch('/api/selfupdate/check', {method:'POST', headers: csrfHeaders()})
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(data) {
|
||
if (data.ok) {
|
||
location.reload();
|
||
} else {
|
||
msg.textContent = data.error || 'Hiba történt';
|
||
msg.style.display = 'inline';
|
||
btn.disabled = false;
|
||
btn.textContent = 'Frissítés keresése';
|
||
}
|
||
})
|
||
.catch(function() {
|
||
msg.textContent = 'Kapcsolódási hiba';
|
||
msg.style.display = 'inline';
|
||
btn.disabled = false;
|
||
btn.textContent = 'Frissítés keresése';
|
||
});
|
||
}
|
||
|
||
function triggerUpdate() {
|
||
openDialog({title:'Vezérlő frissítése', confirmLabel:'Frissítés',
|
||
message:'Biztosan frissíti a controllert?\n\nA folyamat alatt a vezérlőpult rövid időre elérhetetlenné válik.',
|
||
onConfirm:doTriggerUpdate});
|
||
}
|
||
function doTriggerUpdate() {
|
||
var btn = document.getElementById('btn-trigger-update');
|
||
var checkBtn = document.getElementById('btn-check-update');
|
||
var msg = document.getElementById('update-status-msg');
|
||
btn.disabled = true;
|
||
btn.textContent = 'Frissítés...';
|
||
if (checkBtn) checkBtn.disabled = true;
|
||
msg.textContent = 'Frissítés folyamatban...';
|
||
msg.style.display = 'inline';
|
||
fetch('/api/selfupdate/update', {method:'POST', headers: csrfHeaders()})
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(data) {
|
||
if (data.ok) {
|
||
msg.textContent = 'Újraindulás...';
|
||
pollUntilBack();
|
||
} else {
|
||
msg.textContent = data.error || 'Hiba történt';
|
||
btn.disabled = false;
|
||
btn.textContent = 'Frissítés telepítése';
|
||
if (checkBtn) checkBtn.disabled = false;
|
||
}
|
||
})
|
||
.catch(function() {
|
||
msg.textContent = 'Kapcsolódási hiba';
|
||
pollUntilBack();
|
||
});
|
||
}
|
||
|
||
function pollUntilBack() {
|
||
var iv = setInterval(function() {
|
||
fetch('/api/health')
|
||
.then(function(r) {
|
||
if (r.ok) {
|
||
clearInterval(iv);
|
||
location.reload();
|
||
}
|
||
})
|
||
.catch(function() {});
|
||
}, 3000);
|
||
}
|
||
|
||
// If an update is already in flight when the page loads (e.g. a floor-driven AUTO-update kicked off by
|
||
// the hub, not a button click), surface the same restart-poll panel so the page recovers itself.
|
||
{{if .UpdateRunning}}
|
||
pollUntilBack();
|
||
{{end}}
|
||
</script>
|
||
|
||
<!-- Section: Controller restart (self-serve) — always available (not gated on RetrievalPassword) -->
|
||
<div class="settings-card">
|
||
<h3>Vezérlő újraindítása</h3>
|
||
<p class="settings-card-desc">
|
||
Ha a vezérlő hibásan működik, itt biztonságosan újraindíthatja — nem kell az egész szervert újraindítani.
|
||
Az alkalmazásai futnak tovább; csak a vezérlő indul újra (néhány másodperc).
|
||
</p>
|
||
<div id="restart-status"></div>
|
||
<button type="button" class="btn btn-outline" id="btn-restart-controller" onclick="restartController()">Vezérlő újraindítása</button>
|
||
</div>
|
||
|
||
<!-- Section: Full server (guest) restart — a deliberate maintenance affordance, sibling to the controller restart -->
|
||
<div class="settings-card">
|
||
<h3>Kiszolgáló újraindítása</h3>
|
||
<p class="settings-card-desc">
|
||
Az egész kiszolgáló (szerver) újraindítása. Minden alkalmazás rövid időre leáll, és a vezérlőpult kb. 30 másodpercig nem elérhető. Akkor használja, ha a teljes rendszer újraindítására van szükség — egyébként a fenti „Vezérlő újraindítása” elegendő.
|
||
</p>
|
||
<div id="server-restart-status"></div>
|
||
<button type="button" class="btn btn-outline" id="btn-restart-server" onclick="restartServer()">Kiszolgáló újraindítása</button>
|
||
</div>
|
||
|
||
<script>
|
||
function restartController() {
|
||
openDialog({title:'Vezérlő újraindítása', confirmLabel:'Újraindítás',
|
||
message:'Biztosan újraindítja a vezérlőt? A művelet néhány másodpercig tart, és a felület rövid időre elérhetetlen lesz.',
|
||
onConfirm:doRestartController});
|
||
}
|
||
function doRestartController() {
|
||
var btn = document.getElementById('btn-restart-controller');
|
||
var status = document.getElementById('restart-status');
|
||
btn.disabled = true;
|
||
status.innerHTML = '<div class="alert alert-info">Újraindítás folyamatban… újracsatlakozás…</div>';
|
||
fetch('/api/selfrestart', { method: 'POST', headers: csrfHeaders() })
|
||
.then(function(){ pollRestart(0); })
|
||
.catch(function(){ pollRestart(0); }); // connection may drop as the process exits — poll regardless
|
||
}
|
||
function restartServer() {
|
||
openDialog({title:'Kiszolgáló újraindítása', confirmLabel:'Újraindítás',
|
||
message:'Biztosan újraindítja a kiszolgálót? Az alkalmazások és a vezérlőpult kb. 30 másodpercre elérhetetlenné válnak.',
|
||
onConfirm:doRestartServer});
|
||
}
|
||
function doRestartServer() {
|
||
var btn = document.getElementById('btn-restart-server');
|
||
var status = document.getElementById('server-restart-status');
|
||
if (btn) btn.disabled = true;
|
||
if (status) status.innerHTML = '<div class="alert alert-info">Újraindítás folyamatban… a vezérlőpult néhány másodperc múlva újratölt.</div>';
|
||
fetch('/api/server/reboot', { method: 'POST', headers: csrfHeaders() })
|
||
.then(function(){ pollRestart(0); })
|
||
.catch(function(){ pollRestart(0); }); // the guest reboot drops the connection — poll regardless
|
||
}
|
||
function pollRestart(attempt) {
|
||
if (attempt > 60) { // ~2 min cap — never leave the user on a dead page silently
|
||
document.getElementById('restart-status').innerHTML =
|
||
'<div class="alert alert-error">Az újraindítás a vártnál tovább tart. Töltse újra az oldalt kézzel.</div>';
|
||
return;
|
||
}
|
||
setTimeout(function(){
|
||
fetch('/', { method: 'GET', cache: 'no-store' })
|
||
.then(function(r){ if (r.ok) { window.location.reload(); } else { pollRestart(attempt + 1); } })
|
||
.catch(function(){ pollRestart(attempt + 1); });
|
||
}, 2000);
|
||
}
|
||
</script>
|
||
{{template "layout_end" .}}
|
||
{{end}}
|