Files
felhom-controller/controller/internal/web/templates/settings_system.html
T
admin ff68b0b433
gates / gates (push) Successful in 20s
v0.248.0: i18n slice 1 release A — apps and settings in English, Hungarian byte-identical (R-556)
Ten pages converted against fixtures captured from unconverted templates (6be55a0); marker
coverage test; English page mask narrowed; English retrieval-promise stems; common keys;
title keys and infraMeta from the bundle; old wording tests read the Hungarian expansion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-17 17:36:08 +02:00

422 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "settings_system"}}
{{template "layout_start" .}}
<div class="page-header">
<h2>{{T "settings_system.beallitasok_rendszer"}}</h2>
</div>
<!-- Section A: System Configuration (read-only) -->
<div class="settings-card">
<h3>{{T "settings_system.rendszer_konfiguracio"}}</h3>
<p class="settings-card-desc">{{T "settings_system.az_uzemelteto_altal_beallitott_ertekek"}}</p>
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">{{T "common.ugyfel_azonosito"}}</span>
<span class="settings-value mono">{{.CustomerID}}</span>
</div>
<div class="settings-row">
<span class="settings-label">{{T "settings_system.ugyfel_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">{{T "settings_system.alkalmazas_sablon_forras"}}</span>
<span class="settings-value mono settings-value-truncate">{{.GitRepoURL}}</span>
</div>
{{end}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.sablon_szinkronizalas"}}</span>
<span class="settings-value mono">{{.GitSyncInterval}}</span>
</div>
<div class="settings-row">
<span class="settings-label">{{T "settings_system.biztonsagi_mentes"}}</span>
<span class="settings-value">{{if .BackupEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "settings_system.aktiv"}}</span>{{else}}<span class="state-text-neutral">{{T "settings_system.inaktiv"}}</span>{{end}}</span>
</div>
{{if .BackupEnabled}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.mentes_utemezes"}}</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> {{T "settings_system.aktiv"}}</span>{{else}}<span class="state-text-neutral">{{T "settings_system.inaktiv"}}</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">{{T "settings_system.hub_jelentes"}}</span>
<span class="settings-value">{{if .HubEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "settings_system.aktiv"}}</span>{{else}}–{{end}}</span>
</div>
</div>
</div>
<!-- Section: Version & Update -->
<div class="settings-card">
<h3>{{T "settings_system.verzio_es_frissites"}}</h3>
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">{{T "settings_system.jelenlegi_verzio"}}</span>
<span class="settings-value mono">{{.Version}}</span>
</div>
{{if .SelfUpdateEnabled}}
{{if .LatestVersion}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.legujabb_verzio"}}</span>
<span class="settings-value mono">
{{.LatestVersion}}
{{if .UpdateAvailable}}
<span class="state-text-run" style="margin-left:0.5em;">{{T "settings_system.frissites_elerheto"}}</span>
{{else}}
<span style="margin-left:0.5em; color:var(--text-3);">{{T "settings_system.naprakesz"}}</span>
{{end}}
</span>
</div>
{{end}}
{{if .LastCheckTime}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.utolso_ellenorzes"}}</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}}{{T "settings_system.nyilvanos_hitelesites_nelkul"}}{{else}}{{T "settings_system.hitelesitett"}}{{end}}</span>
</div>
{{if .LastCheckError}}
<div class="settings-row">
<span class="settings-label">{{T "common.hiba"}}</span>
<span class="settings-value state-text-crit">{{.LastCheckError}}</span>
</div>
{{end}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.automatikus_frissites"}}</span>
<span class="settings-value">
{{if .AutoUpdateEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "settings_system.aktiv"}}</span> <span class="mono">({{.AutoUpdateTime}})</span>{{else}}–{{end}}
</span>
</div>
{{if .ControllerFloor}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.minimalis_verzio_uzemelteto"}}</span>
<span class="settings-value mono">
{{.ControllerFloor}}
<span style="margin-left:0.5em; color:#888;">{{T "settings_system.a_rendszer_automatikusan_erre_a"}}</span>
</span>
</div>
{{end}}
{{if .UpdateRunning}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.allapot"}}</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> {{T "settings_system.frissites_folyamatban_a_vezerlo_hamarosa"}}</span>
</div>
{{end}}
{{with .LastUpdateState}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.utolso_frissites"}}</span>
<span class="settings-value">
{{if eq .Status "success"}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "settings_system.sikeres"}}</span> ({{.PreviousVersion}} → {{.TargetVersion}})
{{else if eq .Status "failed"}}<span class="state-text-crit"><svg class="ico ico-sm"><use href="#i-x"/></svg> {{T "settings_system.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> {{T "settings_system.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()">{{T "settings_system.frissites_keresese"}}</button>
{{if .UpdateAvailable}}
<button class="btn btn-primary btn-sm" id="btn-trigger-update" onclick="triggerUpdate()" style="margin-left:0.5em;">{{T "settings_system.frissites_telepitese"}}</button>
{{end}}
<span id="update-status-msg" style="margin-left:0.5em; display:none;"></span>
</span>
</div>
{{end}}
</div>
</div>
<!-- Section: Network (R-66) — the box's own address. Every value is live-computed per render and
stored NOWHERE (S-5: a DHCP lease persisted anywhere eventually misdirects people); a missing
value renders „—" because an address-less row beats a wrong address. -->
<div class="settings-card">
<h3>{{T "settings_system.halozat"}}</h3>
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">{{T "settings_system.helyi_cim_lan"}}</span>
<span class="settings-value mono">{{if .NetLANAddress}}{{.NetLANAddress}}{{else}}— <span style="color:var(--text-3);">{{T "settings_system.nem_allapithato_meg"}}</span>{{end}}</span>
</div>
{{if .NetSMBName}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.halozati_nev"}}</span>
<span class="settings-value mono">\\{{.NetSMBName}}</span>
</div>
{{end}}
<div class="settings-row">
<span class="settings-label">{{T "settings_system.atjaro"}}</span>
<span class="settings-value mono">{{if .NetGateway}}{{.NetGateway}}{{else}}— <span style="color:var(--text-3);">{{T "settings_system.nem_allapithato_meg"}}</span>{{end}}</span>
</div>
</div>
<p class="settings-card-desc" style="margin-top:0.5em;">{{T "settings_system.ezek_az_ertekek_a_helyi"}}</p>
</div>
<div class="settings-card">
<h3>{{T "settings_system.szerver_memoria_ram"}}</h3>
{{if .MemorySupported}}
{{if .MemoryReachable}}
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">{{T "settings_system.jelenlegi_memoria"}}</span>
<span class="settings-value mono">{{.MemoryAllocatedMB}} MB</span>
</div>
<div class="settings-row">
<span class="settings-label">{{T "settings_system.felhasznalt"}}</span>
<span class="settings-value mono">{{.MemoryUsageMB}} MB</span>
</div>
<div class="settings-row">
<span class="settings-label">{{T "settings_system.megengedett_tartomany"}}</span>
<span class="settings-value mono">{{.MemoryMinMB}} MB – {{.MemoryMaxMB}} MB</span>
</div>
<div class="settings-row" style="padding-top: 0.5em;">
<span class="settings-label">{{T "settings_system.uj_meret_mb"}}</span>
<span class="settings-value">
<input type="number" id="mem-input" class="input mono" style="width:8em;"
value="{{.MemoryAllocatedMB}}" min="{{.MemoryMinMB}}" max="{{.MemoryMaxMB}}" step="256"
data-current="{{.MemoryAllocatedMB}}">
<button class="btn btn-primary btn-sm" id="btn-mem-resize" onclick="resizeMemory()" style="margin-left:0.5em;">{{T "settings_system.atmeretezes"}}</button>
<span id="mem-status-msg" style="margin-left:0.5em; display:none;"></span>
</span>
</div>
</div>
{{else}}
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">{{T "settings_system.jelenlegi_memoria"}}</span>
<span class="settings-value mono">{{if .MemoryProcMB}}{{.MemoryProcMB}} MB{{else}}n/a{{end}}</span>
</div>
</div>
<p class="settings-card-desc">{{T "settings_system.a_kiszolgalo_ugynok_jelenleg_nem"}}</p>
{{end}}
{{else}}
<p class="settings-card-desc">{{T "settings_system.a_memoria_atmeretezesehez_a_kiszolgalo"}}</p>
{{end}}
</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||'{{T "settings_system.megerosites"}}')+'</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||'{{T "settings_system.megerosites"}}')+'</button>'
+'<button type="button" class="btn btn-outline" id="dialog-cancel">{{T "common.megsem"}}</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 = '{{T "settings_system.ellenorzes"}}';
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 || '{{T "common.hiba_tortent"}}';
msg.style.display = 'inline';
btn.disabled = false;
btn.textContent = '{{T "settings_system.frissites_keresese"}}';
}
})
.catch(function() {
msg.textContent = '{{T "settings_system.kapcsolodasi_hiba"}}';
msg.style.display = 'inline';
btn.disabled = false;
btn.textContent = '{{T "settings_system.frissites_keresese"}}';
});
}
function triggerUpdate() {
openDialog({title:'{{T "settings_system.vezerlo_frissitese"}}', confirmLabel:'{{T "settings_system.frissites"}}',
message:'{{T "settings_system.biztosan_frissiti_a_controllert_n"}}',
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 = '{{T "settings_system.frissites_2"}}';
if (checkBtn) checkBtn.disabled = true;
msg.textContent = '{{T "settings_system.frissites_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 = '{{T "settings_system.ujraindulas"}}';
pollUntilBack();
} else {
msg.textContent = data.error || '{{T "common.hiba_tortent"}}';
btn.disabled = false;
btn.textContent = '{{T "settings_system.frissites_telepitese"}}';
if (checkBtn) checkBtn.disabled = false;
}
})
.catch(function() {
msg.textContent = '{{T "settings_system.kapcsolodasi_hiba"}}';
pollUntilBack();
});
}
function resizeMemory() {
var input = document.getElementById('mem-input');
var target = parseInt(input.value, 10);
var current = parseInt(input.getAttribute('data-current'), 10);
if (isNaN(target) || target <= 0) {
showMemMsg('{{T "settings_system.ervenytelen_memoria_ertek"}}', true);
return;
}
if (target < current) {
openDialog({title:'{{T "settings_system.memoria_csokkentese"}}', confirmLabel:'{{T "settings_system.csokkentes"}}',
message:'{{T "settings_system.a_memoria_csokkentese_hatassal_lehet"}}',
onConfirm:function(){ doResizeMemory(target); }});
return;
}
doResizeMemory(target);
}
function doResizeMemory(target) {
var btn = document.getElementById('btn-mem-resize');
btn.disabled = true;
btn.textContent = '{{T "settings_system.atmeretezes_2"}}';
fetch('/api/system/memory/resize', {
method:'POST', headers: Object.assign({'Content-Type':'application/json'}, csrfHeaders()),
body: JSON.stringify({memory_mb: target})
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.ok) {
showMemMsg(data.error || data.message || '{{T "settings_system.kesz"}}', false);
setTimeout(function(){ location.reload(); }, 1200);
} else {
showMemMsg(data.error || '{{T "settings_system.a_muvelet_nem_sikerult"}}', true);
btn.disabled = false;
btn.textContent = '{{T "settings_system.atmeretezes"}}';
}
})
.catch(function() {
showMemMsg('{{T "settings_system.kapcsolodasi_hiba"}}', true);
btn.disabled = false;
btn.textContent = '{{T "settings_system.atmeretezes"}}';
});
}
function showMemMsg(text, isErr) {
var msg = document.getElementById('mem-status-msg');
if (!msg) return;
msg.textContent = text;
msg.style.color = isErr ? 'var(--danger, #c0392b)' : 'var(--text-2)';
msg.style.display = 'inline';
}
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>{{T "settings_system.vezerlo_ujrainditasa"}}</h3>
<p class="settings-card-desc">
{{T "settings_system.ha_a_vezerlo_hibasan_mukodik"}}
</p>
<div id="restart-status"></div>
<button type="button" class="btn btn-outline" id="btn-restart-controller" onclick="restartController()">{{T "settings_system.vezerlo_ujrainditasa"}}</button>
</div>
<!-- Section: Full server (guest) restart — a deliberate maintenance affordance, sibling to the controller restart -->
<div class="settings-card">
<h3>{{T "settings_system.kiszolgalo_ujrainditasa"}}</h3>
<p class="settings-card-desc">
{{T "settings_system.az_egesz_kiszolgalo_szerver_ujrainditasa"}}
</p>
<div id="server-restart-status"></div>
<button type="button" class="btn btn-outline" id="btn-restart-server" onclick="restartServer()">{{T "settings_system.kiszolgalo_ujrainditasa"}}</button>
</div>
<script>
function restartController() {
openDialog({title:'{{T "settings_system.vezerlo_ujrainditasa"}}', confirmLabel:'{{T "common.ujrainditas"}}',
message:'{{T "settings_system.biztosan_ujrainditja_a_vezerlot_a"}}',
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">{{T "settings_system.ujrainditas_folyamatban_ujracsatlakozas"}}</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:'{{T "settings_system.kiszolgalo_ujrainditasa"}}', confirmLabel:'{{T "common.ujrainditas"}}',
message:'{{T "settings_system.biztosan_ujrainditja_a_kiszolgalot_az"}}',
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">{{T "settings_system.ujrainditas_folyamatban_a_vezerlopult_ne"}}</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">{{T "settings_system.az_ujrainditas_a_vartnal_tovabb"}}</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}}