02650e3202
Controller: - internal/web/csrf.go (new): CsrfProtect middleware, csrfToken/csrfField helpers - auth.go: per-session CSRF token (csrfToken field, csrfTokenForSession method) - server.go: executeTemplate wrapper auto-injects CSRFField+CSRFToken - main.go: wire CsrfProtect on all routes; bump to v0.23.0 - handlers.go, storage_handlers.go, handler_restore.go: executeTemplate - All templates: CSRFField in forms, meta csrf-token, csrfHeaders() JS helper, fetch calls updated; sendBeacon→fetch+keepalive in storage_attach.html Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
627 lines
32 KiB
HTML
627 lines
32 KiB
HTML
{{define "settings"}}
|
||
{{template "layout_start" .}}
|
||
|
||
<div class="page-header">
|
||
<h2>Beállítások</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-green">✅ Aktív</span>{{else}}<span class="state-text-red">❌ 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-green">✅ Aktív</span>{{else}}<span class="state-text-red">❌ 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-green">✅ 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-green" style="margin-left:0.5em;">● Frissítés elérhető</span>
|
||
{{else}}
|
||
<span style="margin-left:0.5em; color:#888;">— 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}}
|
||
{{if .LastCheckError}}
|
||
<div class="settings-row">
|
||
<span class="settings-label">Hiba</span>
|
||
<span class="settings-value state-text-red">{{.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-green">✅ Aktív</span> <span class="mono">({{.AutoUpdateTime}})</span>{{else}}–{{end}}
|
||
</span>
|
||
</div>
|
||
{{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-green">✅ Sikeres</span> ({{.PreviousVersion}} → {{.TargetVersion}})
|
||
{{else if eq .Status "failed"}}<span class="state-text-red">❌ Sikertelen</span> — {{.Error}}
|
||
{{else if eq .Status "pending"}}<span class="state-text-yellow">⏳ 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>
|
||
|
||
<script>
|
||
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() {
|
||
if (!confirm('Biztosan frissíti a controllert?\n\nA folyamat alatt a vezérlőpult rövid időre elérhetetlenné válik.')) return;
|
||
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);
|
||
}
|
||
</script>
|
||
|
||
<!-- Section: Storage Paths -->
|
||
<div class="settings-card">
|
||
<h3>Adattárolók</h3>
|
||
<p class="settings-card-desc">Külső meghajtók kezelése alkalmazásadatok tárolásához.</p>
|
||
|
||
{{if .StorageError}}<div class="alert alert-error">{{.StorageError}}</div>{{end}}
|
||
{{if .StorageSuccess}}<div class="alert alert-info">{{.StorageSuccess}}</div>{{end}}
|
||
|
||
{{if .StoragePaths}}
|
||
<div class="storage-paths-list">
|
||
{{range .StoragePaths}}
|
||
<div class="storage-path-item{{if .Disconnected}} storage-disconnected{{else if .Decommissioned}} storage-decommissioned{{end}}">
|
||
<div class="storage-path-header">
|
||
<div class="storage-path-info">
|
||
<div class="storage-path-label-wrap" id="label-wrap-{{.Path}}">
|
||
<span class="storage-path-label" id="label-display-{{.Path}}">{{.Label}}</span>
|
||
{{if not (or .Disconnected .Decommissioned)}}<button class="btn btn-xs btn-ghost" onclick="editStorageLabel('{{.Path}}', '{{.Label}}')" title="Átnevezés">✏️</button>{{end}}
|
||
</div>
|
||
<span class="storage-path-path mono">{{.Path}}</span>
|
||
</div>
|
||
<div class="storage-path-badges">
|
||
{{if .Disconnected}}
|
||
<span class="badge badge-error">Leválasztva</span>
|
||
{{else if .Decommissioned}}
|
||
<span class="badge state-gray">Kiváltva</span>
|
||
{{else}}
|
||
{{if .IsDefault}}<span class="badge state-green">Alapértelmezett</span>{{end}}
|
||
{{if .Schedulable}}<span class="badge" style="background:rgba(0,136,204,0.15);color:var(--accent-light)">Aktív</span>{{else}}<span class="badge state-gray">Inaktív</span>{{end}}
|
||
{{if not .IsMounted}}<span class="badge badge-warn">Rendszermeghajtón</span>{{end}}
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
{{if .Disconnected}}
|
||
<div class="storage-path-details">
|
||
<div class="storage-disconnected-info">
|
||
{{if .DisconnectedAt}}<span class="form-hint">Leválasztva: {{.DisconnectedAt}}</span>{{end}}
|
||
{{if .StoppedApps}}
|
||
<span class="form-hint">Leállított alkalmazások: {{range $i, $name := .StoppedApps}}{{if $i}}, {{end}}{{$name}}{{end}}</span>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
<div class="storage-path-actions" id="storage-actions-{{.Path}}">
|
||
<button class="btn btn-xs btn-primary" onclick="storageReconnect('{{.Path}}')">Csatlakoztatás</button>
|
||
</div>
|
||
{{else if .Decommissioned}}
|
||
<div class="storage-path-details">
|
||
<div class="storage-disconnected-info">
|
||
<span class="form-hint">Adatok átköltöztetve ide: <strong>{{.MigratedToLabel}}</strong> ({{.MigratedTo}})</span>
|
||
{{if .DecommissionedAt}}<span class="form-hint">Időpont: {{.DecommissionedAt}}</span>{{end}}
|
||
</div>
|
||
</div>
|
||
<div class="storage-path-actions">
|
||
<form method="POST" action="/settings/storage/remove" style="display:inline"
|
||
onsubmit="return confirm('Biztosan eltávolítja a(z) {{.Label}} ({{.Path}}) meghajtót a rendszerből?\n\nA meghajtó adatai NEM törlődnek.')">
|
||
{{$.CSRFField}}
|
||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||
<button type="submit" class="btn btn-xs btn-outline">Eltávolítás a rendszerből</button>
|
||
</form>
|
||
</div>
|
||
{{else}}
|
||
<div class="storage-path-details">
|
||
{{if .DiskInfo}}
|
||
<div class="storage-path-disk">
|
||
<div class="system-info-header">
|
||
<span class="system-info-value">{{.DiskInfo.UsedHuman}} / {{.DiskInfo.TotalHuman}}</span>
|
||
</div>
|
||
<div class="system-bar">
|
||
<div class="system-bar-fill {{if ge .DiskInfo.UsedPercent 90.0}}system-bar-red{{else if ge .DiskInfo.UsedPercent 70.0}}system-bar-yellow{{else}}system-bar-green{{end}}"
|
||
style="width:{{printf "%.0f" .DiskInfo.UsedPercent}}%"></div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
{{if .FSInfo}}
|
||
<div class="storage-path-fsinfo mono form-hint">
|
||
{{.FSInfo.FSType}} · {{.FSInfo.Device}}{{if .FSInfo.Model}} · {{.FSInfo.Model}}{{end}}
|
||
</div>
|
||
{{end}}
|
||
{{if .StoppedApps}}
|
||
<div class="storage-stopped-apps-info" id="storage-stopped-{{.Path}}">
|
||
<span class="form-hint" style="color:var(--accent-light)">Újraindításra váró alkalmazások: {{range $i, $name := .StoppedApps}}{{if $i}}, {{end}}{{$name}}{{end}}</span>
|
||
<button class="btn btn-xs btn-primary" onclick="storageRestartApps('{{.Path}}')" style="margin-left:.5rem">Alkalmazások indítása</button>
|
||
</div>
|
||
{{end}}
|
||
<div class="storage-path-meta">
|
||
{{if .AppDetails}}
|
||
<details class="storage-app-details">
|
||
<summary class="form-hint" style="cursor:pointer">
|
||
{{.AppCount}} alkalmazás használja
|
||
</summary>
|
||
<div class="storage-app-list">
|
||
{{range .AppDetails}}
|
||
<div class="storage-app-row">
|
||
<a href="/apps/{{.Stack}}" class="storage-app-link">{{.Name}}</a>
|
||
{{if .SizeHuman}}<span class="mono form-hint">{{.SizeHuman}}</span>{{end}}
|
||
<a href="/stacks/{{.Stack}}/migrate" class="btn btn-xs btn-outline" title="Adatok áthelyezése másik tárolóra">📦 Mozgatás</a>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
</details>
|
||
{{else}}
|
||
<span class="form-hint">Nincs alkalmazás ezen a tárolón</span>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
<div class="storage-path-actions">
|
||
{{if not .IsDefault}}
|
||
<form method="POST" action="/settings/storage/default" style="display:inline">
|
||
{{$.CSRFField}}
|
||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||
<button type="submit" class="btn btn-xs btn-outline">Legyen alapértelmezett</button>
|
||
</form>
|
||
{{end}}
|
||
{{if .Schedulable}}
|
||
<form method="POST" action="/settings/storage/schedulable" style="display:inline">
|
||
{{$.CSRFField}}
|
||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||
<input type="hidden" name="schedulable" value="false">
|
||
<button type="submit" class="btn btn-xs btn-outline">Letiltás</button>
|
||
</form>
|
||
{{else}}
|
||
<form method="POST" action="/settings/storage/schedulable" style="display:inline">
|
||
{{$.CSRFField}}
|
||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||
<input type="hidden" name="schedulable" value="true">
|
||
<button type="submit" class="btn btn-xs btn-outline">Engedélyezés</button>
|
||
</form>
|
||
{{end}}
|
||
{{if .IsUSB}}
|
||
<button class="btn btn-xs btn-danger-outline" onclick="storageDisconnect('{{.Path}}', '{{.Label}}', {{.AppCount}})">Leválasztás</button>
|
||
{{end}}
|
||
{{if and (not .IsDefault) (eq .AppCount 0)}}
|
||
<form method="POST" action="/settings/storage/remove" style="display:inline"
|
||
onsubmit="return confirm('Biztosan eltávolítja a(z) {{.Path}} adattárolót?')">
|
||
{{$.CSRFField}}
|
||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||
<button type="submit" class="btn btn-xs btn-danger-outline">Eltávolítás</button>
|
||
</form>
|
||
{{end}}
|
||
{{if and (gt .AppCount 0) .HasOtherPaths}}
|
||
<a href="/settings/storage/migrate-drive?source={{.Path}}" class="btn btn-xs btn-outline">📦 Összes adat átköltöztetése</a>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
{{else}}
|
||
<div class="empty-state" style="padding:1.5rem">
|
||
Nincs regisztrált adattároló. Adjon hozzá egyet az alábbi űrlappal.
|
||
</div>
|
||
{{end}}
|
||
|
||
<div style="margin-top:1rem;display:flex;gap:.75rem;flex-wrap:wrap">
|
||
<a href="/settings/storage/init" class="btn btn-sm btn-outline">🔧 Új meghajtó inicializálása</a>
|
||
<a href="/settings/storage/attach" class="btn btn-sm btn-outline">🔗 Meglévő meghajtó csatolása</a>
|
||
</div>
|
||
|
||
<details class="storage-add-details">
|
||
<summary class="btn btn-sm btn-outline" style="margin-top:.75rem;cursor:pointer">Már csatlakoztatott tárhely hozzáadása kézzel</summary>
|
||
<form method="POST" action="/settings/storage/add" class="storage-add-form">
|
||
{{.CSRFField}}
|
||
<div class="form-group">
|
||
<label for="storage_path">Elérési út</label>
|
||
<input type="text" id="storage_path" name="storage_path" class="form-control"
|
||
placeholder="/mnt/hdd_1" required>
|
||
<span class="form-hint">Pl. /mnt/hdd_1 — a meghajtónak már csatolva kell lennie</span>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="storage_label">Megnevezés (opcionális)</label>
|
||
<input type="text" id="storage_label" name="storage_label" class="form-control"
|
||
placeholder="Külső HDD 1TB">
|
||
</div>
|
||
<label class="toggle" style="margin-bottom:1rem">
|
||
<input type="checkbox" name="storage_default" value="true">
|
||
<span class="toggle-label">Legyen alapértelmezett új telepítéseknél</span>
|
||
</label>
|
||
<button type="submit" class="btn btn-primary">Hozzáadás</button>
|
||
</form>
|
||
</details>
|
||
</div>
|
||
|
||
<!-- Section B: Password Change -->
|
||
<div class="settings-card">
|
||
<h3>Jelszó módosítás</h3>
|
||
{{if .AuthEnabled}}
|
||
{{if .PasswordError}}<div class="alert alert-error">{{.PasswordError}}</div>{{end}}
|
||
<form method="POST" action="/settings/password">
|
||
{{.CSRFField}}
|
||
<div class="form-group">
|
||
<label for="current_password">Jelenlegi jelszó</label>
|
||
<input type="password" id="current_password" name="current_password" required
|
||
placeholder="Adja meg a jelenlegi jelszavát" class="form-control">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="new_password">Új jelszó</label>
|
||
<input type="password" id="new_password" name="new_password" required minlength="8"
|
||
placeholder="Legalább 8 karakter" class="form-control">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="confirm_password">Új jelszó megerősítése</label>
|
||
<input type="password" id="confirm_password" name="confirm_password" required minlength="8"
|
||
placeholder="Jelszó mégegyszer" class="form-control">
|
||
</div>
|
||
<button type="submit" class="btn btn-primary">Jelszó módosítása</button>
|
||
</form>
|
||
{{else}}
|
||
<div class="alert alert-info">
|
||
A jelszavas védelem nincs beállítva. Kérd az üzemeltetőt a beállításhoz.
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
|
||
<!-- Section C: Notification Preferences -->
|
||
<div class="settings-card">
|
||
<h3>Értesítések</h3>
|
||
{{if .HubEnabled}}
|
||
{{if .NotificationSuccess}}<div class="alert alert-info">{{.NotificationSuccess}}</div>{{end}}
|
||
{{if .NotificationError}}<div class="alert alert-error">{{.NotificationError}}</div>{{end}}
|
||
<form method="POST" action="/settings/notifications">
|
||
{{.CSRFField}}
|
||
<div class="form-group">
|
||
<label for="notification_email">E-mail cím</label>
|
||
<input type="email" id="notification_email" name="notification_email"
|
||
value="{{with .NotificationPrefs}}{{.Email}}{{end}}"
|
||
placeholder="pelda@email.hu" class="form-control">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Hibák és figyelmeztetések:</label>
|
||
<div class="checkbox-group">
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_backup_failed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "backup_failed"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Biztonsági mentés sikertelen</span>
|
||
</label>
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_db_dump_failed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "db_dump_failed"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Adatbázis mentés sikertelen</span>
|
||
</label>
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_backup_integrity_failed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "backup_integrity_failed"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Mentés sérülés észlelve</span>
|
||
</label>
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_crossdrive_failed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "crossdrive_failed"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Másodlagos mentés sikertelen</span>
|
||
</label>
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_disk_alerts" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "disk_warning"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Lemez figyelmeztetés (90%+)</span>
|
||
</label>
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_storage_disconnected" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "storage_disconnected"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Meghajtó leválasztva</span>
|
||
</label>
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_node_down" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "node_down"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Szerver nem elérhető</span>
|
||
</label>
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_health_critical" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "health_critical"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Rendszer állapot kritikus</span>
|
||
</label>
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_expected_missed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "expected_backup_missed"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Elvárt mentés elmaradt</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Tájékoztató:</label>
|
||
<div class="checkbox-group">
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_storage_reconnected" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "storage_reconnected"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Meghajtó újra csatlakoztatva</span>
|
||
</label>
|
||
<label class="toggle">
|
||
<input type="checkbox" name="event_health_recovered" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "health_recovered"}}checked{{end}}{{end}}{{end}}>
|
||
<span class="toggle-label">Rendszer állapot helyreállt</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="cooldown_hours">Értesítési szünet</label>
|
||
<div class="form-inline">
|
||
<input type="number" id="cooldown_hours" name="cooldown_hours" min="1" max="168"
|
||
value="{{with .NotificationPrefs}}{{.CooldownHours}}{{end}}"
|
||
class="form-control form-control-narrow">
|
||
<span class="form-hint">óra (azonos probléma esetén ennyi ideig nem küld újat)</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary">Mentés</button>
|
||
<button type="submit" formaction="/settings/notifications/test" class="btn btn-outline">Teszt email küldése</button>
|
||
</div>
|
||
</form>
|
||
{{else}}
|
||
<div class="alert alert-info">
|
||
Az értesítések a központi rendszeren keresztül működnek, ami jelenleg nincs bekapcsolva.
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
|
||
<!-- Section: Recovery Info -->
|
||
{{if .RetrievalPassword}}
|
||
<div class="settings-card">
|
||
<h3>Veszhelyzeti informaciok</h3>
|
||
<p class="settings-card-desc">
|
||
Ezeket az adatokat mentse el biztos helyre. Ujratelepites eseten szukseg lesz rajuk a rendszer visszaallitasahoz.
|
||
</p>
|
||
<div class="settings-grid">
|
||
<div class="settings-row">
|
||
<span class="settings-label">Ugyfel azonosito</span>
|
||
<span class="settings-value mono">{{.CustomerID}}</span>
|
||
</div>
|
||
<div class="settings-row">
|
||
<span class="settings-label">Hub URL</span>
|
||
<span class="settings-value mono">{{.HubURL}}</span>
|
||
</div>
|
||
<div class="settings-row">
|
||
<span class="settings-label">Visszaallitasi jelszo</span>
|
||
<span class="settings-value">
|
||
<span id="retrieval-pw-hidden">••••••••••••••••
|
||
<button type="button" class="btn btn-xs btn-outline" onclick="document.getElementById('retrieval-pw-hidden').style.display='none';document.getElementById('retrieval-pw-visible').style.display='inline';">Megjelenit</button>
|
||
</span>
|
||
<span id="retrieval-pw-visible" style="display:none">
|
||
<code class="mono">{{.RetrievalPassword}}</code>
|
||
<button type="button" class="btn btn-xs btn-outline" onclick="document.getElementById('retrieval-pw-visible').style.display='none';document.getElementById('retrieval-pw-hidden').style.display='inline';">Elrejt</button>
|
||
</span>
|
||
</span>
|
||
</div>
|
||
<div class="settings-row">
|
||
<span class="settings-label">Tamogatas</span>
|
||
<span class="settings-value">
|
||
<a href="mailto:{{.SupportEmail}}" style="color: var(--accent-blue, #0088cc);">{{.SupportEmail}}</a>
|
||
|
|
||
<a href="{{.SupportURL}}" target="_blank" style="color: var(--accent-blue, #0088cc);">felhom.eu/kapcsolat</a>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
|
||
<script>
|
||
function editStorageLabel(path, currentLabel) {
|
||
var wrap = document.getElementById('label-wrap-' + path);
|
||
if (!wrap) return;
|
||
var csrfTok = (document.querySelector('meta[name="csrf-token"]') || {}).content || '';
|
||
wrap.innerHTML = '<form method="POST" action="/settings/storage/label" style="display:inline-flex;gap:.5rem;align-items:center">' +
|
||
'<input type="hidden" name="_csrf" value="' + csrfTok + '">' +
|
||
'<input type="hidden" name="storage_path" value="' + path + '">' +
|
||
'<input type="text" name="storage_label" class="form-control" value="' + currentLabel.replace(/"/g, '"') + '" style="width:200px;padding:.3rem .5rem;font-size:.9rem" maxlength="50">' +
|
||
'<button type="submit" class="btn btn-xs btn-primary">OK</button>' +
|
||
'<button type="button" class="btn btn-xs btn-outline" onclick="cancelEditLabel(\'' + path + '\', \'' + currentLabel.replace(/'/g, "\\'") + '\')">✕</button>' +
|
||
'</form>';
|
||
wrap.querySelector('input[name=storage_label]').focus();
|
||
}
|
||
function storageDisconnect(path, label, appCount) {
|
||
var msg = 'Biztos leválasztja a meghajtót: ' + label + '?';
|
||
if (appCount > 0) msg += '\n\nA rajta futó ' + appCount + ' alkalmazás le fog állni.';
|
||
msg += '\n\nA meghajtó ezután biztonságosan eltávolítható.';
|
||
if (!confirm(msg)) return;
|
||
fetch('/api/storage/disconnect', {
|
||
method: 'POST',
|
||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||
body: JSON.stringify({path: path})
|
||
}).then(function(r) { return r.json(); }).then(function(data) {
|
||
if (data.ok) {
|
||
alert('A meghajtó biztonságosan eltávolítható.');
|
||
location.reload();
|
||
} else {
|
||
alert('Hiba: ' + (data.error || 'ismeretlen'));
|
||
}
|
||
}).catch(function(e) { alert('Hiba: ' + e); });
|
||
}
|
||
function storageReconnect(path) {
|
||
var actionsDiv = document.getElementById('storage-actions-' + path);
|
||
if (actionsDiv) actionsDiv.innerHTML = '<span class="form-hint">Csatlakoztatás...</span>';
|
||
fetch('/api/storage/reconnect', {
|
||
method: 'POST',
|
||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||
body: JSON.stringify({path: path})
|
||
}).then(function(r) { return r.json(); }).then(function(data) {
|
||
if (data.ok) {
|
||
location.reload();
|
||
} else {
|
||
alert('Hiba: ' + (data.error || 'ismeretlen'));
|
||
if (actionsDiv) actionsDiv.innerHTML = '<button class="btn btn-xs btn-primary" onclick="storageReconnect(\'' + path + '\')">Csatlakoztatás</button>';
|
||
}
|
||
}).catch(function(e) {
|
||
alert('Hiba: ' + e);
|
||
if (actionsDiv) actionsDiv.innerHTML = '<button class="btn btn-xs btn-primary" onclick="storageReconnect(\'' + path + '\')">Csatlakoztatás</button>';
|
||
});
|
||
}
|
||
function storageRestartApps(path) {
|
||
fetch('/api/storage/restart-apps', {
|
||
method: 'POST',
|
||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||
body: JSON.stringify({path: path})
|
||
}).then(function(r) { return r.json(); }).then(function(data) {
|
||
if (data.ok) {
|
||
var msg = '';
|
||
if (data.started && data.started.length) msg += 'Elindítva: ' + data.started.join(', ');
|
||
if (data.failed && data.failed.length) msg += (msg ? '\n' : '') + 'Sikertelen: ' + data.failed.join(', ');
|
||
if (msg) alert(msg);
|
||
location.reload();
|
||
} else {
|
||
alert('Hiba: ' + (data.error || 'ismeretlen'));
|
||
}
|
||
}).catch(function(e) { alert('Hiba: ' + e); });
|
||
}
|
||
function cancelEditLabel(path, label) {
|
||
var wrap = document.getElementById('label-wrap-' + path);
|
||
if (!wrap) return;
|
||
// M11: Use DOM manipulation with textContent to prevent XSS if label contains HTML.
|
||
wrap.innerHTML = '';
|
||
var span = document.createElement('span');
|
||
span.className = 'storage-path-label';
|
||
span.id = 'label-display-' + path;
|
||
span.textContent = label;
|
||
var btn = document.createElement('button');
|
||
btn.className = 'btn btn-xs btn-ghost';
|
||
btn.setAttribute('title', 'Átnevezés');
|
||
btn.textContent = '✏️';
|
||
btn.addEventListener('click', function() { editStorageLabel(path, label); });
|
||
wrap.appendChild(span);
|
||
wrap.appendChild(document.createTextNode(' '));
|
||
wrap.appendChild(btn);
|
||
}
|
||
</script>
|
||
{{template "layout_end" .}}
|
||
{{end}}
|