D0 Part 3: deploy + backups + monitoring in v2

- deploy.html: stacked memory bar -> meter track with two segments
  (committed solid blue, new app 35%-opacity blue), neutral threshold
  ticks; integration status badges -> tags; meta pills -> metarows;
  3-step deploy progress icons -> sprite (check/x/triangle-alert/
  spinning rotate-cw); emoji -> icons or plain text incl. the JS
  confirm string.
- backups.html: storage bars -> meters, disconnected drives render an
  empty track with a neutral 'Leválasztva' (no red), drive-state badges
  -> warn tags, checkmark/warning glyphs -> sprite icons, JS flash/info
  strings de-emojified.
- monitoring.html: bars -> meters in both template and JS-generated
  host-storage markup (usageColorClass now emits nominal/warn/crit);
  Chart.js palette per spec (cpu #2EA8F5, memory #8E7CE8, temp #E0A93E,
  load #5EC4B6 — categorical data series keep distinct hues); tooltip/
  grid/tick colors moved to v2 literals; mem-dist palette rebalanced.
- Templates repo-wide: legacy var(--*) names in inline styles and JS
  string literals renamed to v2 tokens (old names no longer resolve).
- style.css: memory-bar family rewritten for the two-segment meter,
  .ico-spin animation added (reduced-motion respected).
This commit is contained in:
2026-07-02 14:32:00 +02:00
parent 5dc277f2b1
commit f100cef842
13 changed files with 178 additions and 196 deletions
@@ -58,7 +58,7 @@
if (step.status === 'done') icon = '<span class="step-done">&#10003;</span>'; if (step.status === 'done') icon = '<span class="step-done">&#10003;</span>';
else if (step.status === 'running') icon = '<span class="spinner"></span>'; else if (step.status === 'running') icon = '<span class="spinner"></span>';
else if (step.status === 'failed') icon = '<span class="step-failed">&#10007;</span>'; else if (step.status === 'failed') icon = '<span class="step-failed">&#10007;</span>';
else icon = '<span style="color: var(--text-secondary);">&#9675;</span>'; else icon = '<span style="color: var(--text-2);">&#9675;</span>';
li.innerHTML = icon + ' ' + step.label; li.innerHTML = icon + ' ' + step.label;
if (step.error) li.innerHTML += '<br><small style="color: var(--red, #f85149);">' + step.error + '</small>'; if (step.error) li.innerHTML += '<br><small style="color: var(--red, #f85149);">' + step.error + '</small>';
list.appendChild(li); list.appendChild(li);
@@ -17,7 +17,7 @@
{{end}} {{end}}
</select> </select>
<div id="estimateBox" style="display:none;background:var(--bg-secondary);border-radius:8px;padding:1rem;margin-bottom:1.5rem"> <div id="estimateBox" style="display:none;background:var(--bg-2);border-radius:8px;padding:1rem;margin-bottom:1.5rem">
<div style="display:flex;justify-content:space-between;margin-bottom:.25rem"> <div style="display:flex;justify-content:space-between;margin-bottom:.25rem">
<span>Konfiguráció:</span> <span>Konfiguráció:</span>
<span id="estConfig">-</span> <span id="estConfig">-</span>
@@ -26,15 +26,15 @@
<span>Felhasználói adatok:</span> <span>Felhasználói adatok:</span>
<span id="estData">-</span> <span id="estData">-</span>
</div> </div>
<div style="display:flex;justify-content:space-between;font-weight:600;margin-bottom:.25rem;border-top:1px solid var(--border);padding-top:.5rem"> <div style="display:flex;justify-content:space-between;font-weight:600;margin-bottom:.25rem;border-top:1px solid var(--line);padding-top:.5rem">
<span>Összesen:</span> <span>Összesen:</span>
<span><span id="estTotal">-</span> <span id="estFree" style="color:var(--text-muted)">(szabad: -)</span></span> <span><span id="estTotal">-</span> <span id="estFree" style="color:var(--text-3)">(szabad: -)</span></span>
</div> </div>
<div style="display:flex;justify-content:space-between"> <div style="display:flex;justify-content:space-between">
<span>Becsült idő:</span> <span>Becsült idő:</span>
<span id="estTime">-</span> <span id="estTime">-</span>
</div> </div>
<div id="estWarning" style="display:none;color:var(--danger);margin-top:.5rem;font-weight:600"></div> <div id="estWarning" style="display:none;color:var(--crit);margin-top:.5rem;font-weight:600"></div>
</div> </div>
<h3>Jelszó (opcionális)</h3> <h3>Jelszó (opcionális)</h3>
@@ -47,7 +47,7 @@
<input type="checkbox" id="stopApp" checked style="margin-top:3px"> <input type="checkbox" id="stopApp" checked style="margin-top:3px">
<div> <div>
<strong>Alkalmazás leállítása mentés előtt (ajánlott)</strong> <strong>Alkalmazás leállítása mentés előtt (ajánlott)</strong>
<div style="color:var(--text-muted);font-size:.85rem">Az adatok konzisztenciája érdekében javasolt leállítani az alkalmazást mentés közben.</div> <div style="color:var(--text-3);font-size:.85rem">Az adatok konzisztenciája érdekében javasolt leállítani az alkalmazást mentés közben.</div>
</div> </div>
</label> </label>
@@ -59,14 +59,14 @@
<div id="progressCard" class="card" style="max-width:700px;display:none"> <div id="progressCard" class="card" style="max-width:700px;display:none">
<h3>Folyamat</h3> <h3>Folyamat</h3>
<div id="progressSteps"></div> <div id="progressSteps"></div>
<div id="progressError" style="display:none;color:var(--danger);margin-top:1rem;font-weight:600"></div> <div id="progressError" style="display:none;color:var(--crit);margin-top:1rem;font-weight:600"></div>
</div> </div>
<div id="doneCard" class="card" style="max-width:700px;display:none"> <div id="doneCard" class="card" style="max-width:700px;display:none">
<h3 style="color:var(--success)">Kész!</h3> <h3 style="color:var(--blue)">Kész!</h3>
<div style="margin-bottom:1rem"> <div style="margin-bottom:1rem">
<span id="doneFile" style="font-weight:600"></span> <span id="doneFile" style="font-weight:600"></span>
<span id="doneSize" style="color:var(--text-muted)"></span> <span id="doneSize" style="color:var(--text-3)"></span>
</div> </div>
<a id="doneFBLink" href="#" target="_blank" class="btn btn-outline">Megnyitás FileBrowser-ben &nearr;</a> <a id="doneFBLink" href="#" target="_blank" class="btn btn-outline">Megnyitás FileBrowser-ben &nearr;</a>
</div> </div>
@@ -190,7 +190,7 @@ function renderSteps(steps) {
if (s.status === 'running') icon = '&#10227;'; if (s.status === 'running') icon = '&#10227;';
if (s.status === 'done') icon = '&#10003;'; if (s.status === 'done') icon = '&#10003;';
if (s.status === 'failed') icon = '&#10007;'; if (s.status === 'failed') icon = '&#10007;';
var cls = s.status === 'failed' ? 'color:var(--danger)' : s.status === 'done' ? 'color:var(--success)' : s.status === 'running' ? 'color:var(--primary)' : ''; var cls = s.status === 'failed' ? 'color:var(--crit)' : s.status === 'done' ? 'color:var(--blue)' : s.status === 'running' ? 'color:var(--blue)' : '';
html += '<div style="padding:.25rem 0;' + cls + '">' + icon + ' ' + s.label; html += '<div style="padding:.25rem 0;' + cls + '">' + icon + ' ' + s.label;
if (s.error) html += ' <span style="font-size:.85rem">(' + s.error + ')</span>'; if (s.error) html += ' <span style="font-size:.85rem">(' + s.error + ')</span>';
html += '</div>'; html += '</div>';
@@ -10,8 +10,8 @@
{{if not .Bundles}} {{if not .Bundles}}
<div class="card" style="max-width:700px"> <div class="card" style="max-width:700px">
<p style="color:var(--text-muted)">Nem található .fab csomag a regisztrált tárolókon.</p> <p style="color:var(--text-3)">Nem található .fab csomag a regisztrált tárolókon.</p>
<p style="color:var(--text-muted);font-size:.85rem">Exportálj egy alkalmazást az alkalmazás oldaláról, vagy másolj egy .fab fájlt a <code>{tároló}/felhom-data/exports/</code> könyvtárba.</p> <p style="color:var(--text-3);font-size:.85rem">Exportálj egy alkalmazást az alkalmazás oldaláról, vagy másolj egy .fab fájlt a <code>{tároló}/felhom-data/exports/</code> könyvtárba.</p>
</div> </div>
{{else}} {{else}}
<div class="card" style="max-width:900px"> <div class="card" style="max-width:900px">
@@ -52,11 +52,11 @@
<input type="password" id="importPassword" placeholder="Jelszó" style="flex:1;padding:.5rem"> <input type="password" id="importPassword" placeholder="Jelszó" style="flex:1;padding:.5rem">
<button class="btn btn-primary" onclick="loadManifest()">Megnyitás</button> <button class="btn btn-primary" onclick="loadManifest()">Megnyitás</button>
</div> </div>
<div id="passwordError" style="display:none;color:var(--danger);margin-top:.5rem"></div> <div id="passwordError" style="display:none;color:var(--crit);margin-top:.5rem"></div>
</div> </div>
<div id="manifestInfo" style="display:none"> <div id="manifestInfo" style="display:none">
<div style="background:var(--bg-secondary);border-radius:8px;padding:1rem;margin-bottom:1rem"> <div style="background:var(--bg-2);border-radius:8px;padding:1rem;margin-bottom:1rem">
<div style="display:flex;justify-content:space-between;margin-bottom:.25rem"> <div style="display:flex;justify-content:space-between;margin-bottom:.25rem">
<span>Alkalmazás:</span> <span>Alkalmazás:</span>
<strong id="mfName">-</strong> <strong id="mfName">-</strong>
@@ -97,11 +97,11 @@
<div id="progressCard" class="card" style="max-width:700px;display:none"> <div id="progressCard" class="card" style="max-width:700px;display:none">
<h3>Importálás folyamata</h3> <h3>Importálás folyamata</h3>
<div id="progressSteps"></div> <div id="progressSteps"></div>
<div id="progressError" style="display:none;color:var(--danger);margin-top:1rem;font-weight:600"></div> <div id="progressError" style="display:none;color:var(--crit);margin-top:1rem;font-weight:600"></div>
</div> </div>
<div id="doneCard" class="card" style="max-width:700px;display:none"> <div id="doneCard" class="card" style="max-width:700px;display:none">
<h3 style="color:var(--success)">Importálás kész!</h3> <h3 style="color:var(--blue)">Importálás kész!</h3>
<p>Az alkalmazás sikeresen visszaállítva.</p> <p>Az alkalmazás sikeresen visszaállítva.</p>
<a id="doneLink" href="/stacks" class="btn btn-primary">Alkalmazások megtekintése</a> <a id="doneLink" href="/stacks" class="btn btn-primary">Alkalmazások megtekintése</a>
</div> </div>
@@ -267,7 +267,7 @@ function renderSteps(steps) {
if (s.status === 'running') icon = '&#10227;'; if (s.status === 'running') icon = '&#10227;';
if (s.status === 'done') icon = '&#10003;'; if (s.status === 'done') icon = '&#10003;';
if (s.status === 'failed') icon = '&#10007;'; if (s.status === 'failed') icon = '&#10007;';
var cls = s.status === 'failed' ? 'color:var(--danger)' : s.status === 'done' ? 'color:var(--success)' : s.status === 'running' ? 'color:var(--primary)' : ''; var cls = s.status === 'failed' ? 'color:var(--crit)' : s.status === 'done' ? 'color:var(--blue)' : s.status === 'running' ? 'color:var(--blue)' : '';
html += '<div style="padding:.25rem 0;' + cls + '">' + icon + ' ' + s.label; html += '<div style="padding:.25rem 0;' + cls + '">' + icon + ' ' + s.label;
if (s.error) html += ' <span style="font-size:.85rem">(' + s.error + ')</span>'; if (s.error) html += ' <span style="font-size:.85rem">(' + s.error + ')</span>';
html += '</div>'; html += '</div>';
@@ -73,7 +73,7 @@
</select> </select>
<button class="btn btn-sm btn-outline" onclick="appMigrate('{{.Stack.Name}}','{{.Meta.DisplayName}}')">Áthelyezés</button> <button class="btn btn-sm btn-outline" onclick="appMigrate('{{.Stack.Name}}','{{.Meta.DisplayName}}')">Áthelyezés</button>
</div> </div>
<div id="app-migrate-progress" style="display:none;margin-top:.75rem;padding:.75rem;border:1px solid var(--accent);border-radius:6px;background:rgba(0,136,204,0.06)"></div> <div id="app-migrate-progress" style="display:none;margin-top:.75rem;padding:.75rem;border:1px solid var(--blue);border-radius:6px;background:rgba(0,136,204,0.06)"></div>
</div> </div>
<script> <script>
function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; } function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; }
@@ -146,12 +146,12 @@ function appMigrate(app,label){
<table class="initcred-table"> <table class="initcred-table">
{{if .InitialCreds.Username}} {{if .InitialCreds.Username}}
<tr> <tr>
<td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-muted);white-space:nowrap">Felhasználónév</td> <td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap">Felhasználónév</td>
<td><code class="initcred-user" style="user-select:all">{{.InitialCreds.Username}}</code></td> <td><code class="initcred-user" style="user-select:all">{{.InitialCreds.Username}}</code></td>
</tr> </tr>
{{end}} {{end}}
<tr> <tr>
<td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-muted);white-space:nowrap;vertical-align:middle">Jelszó</td> <td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap;vertical-align:middle">Jelszó</td>
<td style="display:flex;align-items:center;gap:.5rem;flex-wrap:wrap"> <td style="display:flex;align-items:center;gap:.5rem;flex-wrap:wrap">
<code id="initcred-pw">••••••••••••</code> <code id="initcred-pw">••••••••••••</code>
<span id="initcred-pw-val" hidden>{{.InitialCreds.Password}}</span> <span id="initcred-pw-val" hidden>{{.InitialCreds.Password}}</span>
+26 -26
View File
@@ -28,32 +28,32 @@
<div class="storage-overview-grid"> <div class="storage-overview-grid">
<div class="storage-bars"> <div class="storage-bars">
{{with $.SystemInfo}} {{with $.SystemInfo}}
<div class="storage-item"> <div class="storage-item meter {{usageColor .DiskPercent}}">
<div class="storage-header"> <div class="storage-header">
<span class="storage-label">Rendszer (/)</span> <span class="storage-label">Rendszer (/)</span>
<span class="storage-value">{{fmtGB .DiskUsedGB}} / {{fmtGB .DiskTotalGB}} ({{printf "%.0f" .DiskPercent}}%)</span> <span class="storage-value">{{fmtGB .DiskUsedGB}} / {{fmtGB .DiskTotalGB}} ({{printf "%.0f" .DiskPercent}}%)</span>
</div> </div>
<div class="system-bar"> <div class="meter-track">
<div class="system-bar-fill {{usageColor .DiskPercent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .DiskPercent}}%"></div> <div class="meter-fill" style="width:{{printf "%.1f" .DiskPercent}}%"></div>
</div> </div>
</div> </div>
{{range $.StorageBars}} {{range $.StorageBars}}
{{if .Disconnected}} {{if .Disconnected}}
<div class="storage-item storage-disconnected"> <div class="storage-item meter meter-empty storage-disconnected">
<div class="storage-header"> <div class="storage-header">
<span class="storage-label">{{.Label}}</span> <span class="storage-label">{{.Label}}</span>
<span class="storage-value badge-error" style="font-size:.75rem">Leválasztva</span> <span class="storage-value"><span class="state-text-neutral">Leválasztva</span></span>
</div> </div>
<div class="system-bar"><div class="system-bar-disconnected"></div></div> <div class="meter-track"></div>
</div> </div>
{{else}} {{else}}
<div class="storage-item"> <div class="storage-item meter {{usageColor .Percent}}">
<div class="storage-header"> <div class="storage-header">
<span class="storage-label">{{.Label}}</span> <span class="storage-label">{{.Label}}</span>
<span class="storage-value">{{fmtGB .UsedGB}} / {{fmtGB .TotalGB}} ({{printf "%.0f" .Percent}}%)</span> <span class="storage-value">{{fmtGB .UsedGB}} / {{fmtGB .TotalGB}} ({{printf "%.0f" .Percent}}%)</span>
</div> </div>
<div class="system-bar"> <div class="meter-track">
<div class="system-bar-fill {{usageColor .Percent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .Percent}}%"></div> <div class="meter-fill" style="width:{{printf "%.1f" .Percent}}%"></div>
</div> </div>
</div> </div>
{{end}} {{end}}
@@ -107,7 +107,7 @@
{{if .CanTrigger}} {{if .CanTrigger}}
<div class="schedule-actions" style="margin-top:1rem"> <div class="schedule-actions" style="margin-top:1rem">
<button class="btn btn-sm btn-primary" id="wg-backup-btn" onclick="triggerGuestBackup()" {{if .Running}}disabled{{end}}>Mentés most</button> <button class="btn btn-sm btn-primary" id="wg-backup-btn" onclick="triggerGuestBackup()" {{if .Running}}disabled{{end}}>Mentés most</button>
{{if .StopMode}}<span class="form-hint" style="margin-left:.5rem"> A mentés idejére az alkalmazások rövid időre leállnak.</span> {{if .StopMode}}<span class="form-hint" style="margin-left:.5rem"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> A mentés idejére az alkalmazások rövid időre leállnak.</span>
{{else}}<span class="form-hint" style="margin-left:.5rem">Pillanatkép-mód: az alkalmazások csak néhány másodpercre állnak le.</span>{{end}} {{else}}<span class="form-hint" style="margin-left:.5rem">Pillanatkép-mód: az alkalmazások csak néhány másodpercre állnak le.</span>{{end}}
<div id="wg-backup-result" style="margin-top:.6rem"></div> <div id="wg-backup-result" style="margin-top:.6rem"></div>
</div> </div>
@@ -223,8 +223,8 @@
<div class="stat-label">Távoli mentés<br><span class="relative-time">külön hardveren (PBS)</span></div> <div class="stat-label">Távoli mentés<br><span class="relative-time">külön hardveren (PBS)</span></div>
</div> </div>
{{else}} {{else}}
<div class="stat-card" style="border-left-color: var(--gray);"> <div class="stat-card" style="border-left-color: var(--text-3);">
<div class="stat-value" style="background:var(--gray);-webkit-background-clip:text;background-clip:text;"></div> <div class="stat-value" style="background:var(--text-3);-webkit-background-clip:text;background-clip:text;"></div>
<div class="stat-label">Távoli mentés<br><span class="relative-time">nincs beállítva</span></div> <div class="stat-label">Távoli mentés<br><span class="relative-time">nincs beállítva</span></div>
</div> </div>
{{end}} {{end}}
@@ -361,7 +361,7 @@
<span class="app-backup-row-name">{{.DisplayName}}</span> <span class="app-backup-row-name">{{.DisplayName}}</span>
<div class="app-backup-row-meta"> <div class="app-backup-row-meta">
{{if .DriveDisconnected}} {{if .DriveDisconnected}}
<span class="badge badge-error" style="font-size:.7rem">Meghajtó leválasztva</span> <span class="tag tag-warn">Meghajtó leválasztva</span>
{{else if .HasHDDData}} {{else if .HasHDDData}}
{{if .StorageLabel}}<span class="meta-badge meta-badge-storage">{{.StorageLabel}}</span>{{end}} {{if .StorageLabel}}<span class="meta-badge meta-badge-storage">{{.StorageLabel}}</span>{{end}}
<span class="mono app-backup-size" style="font-size:.8rem">{{.HDDSizeHuman}}</span> <span class="mono app-backup-size" style="font-size:.8rem">{{.HDDSizeHuman}}</span>
@@ -382,13 +382,13 @@
<span class="tier-location">helyi</span> <span class="tier-location">helyi</span>
{{if .Tier1LastRun}} {{if .Tier1LastRun}}
<span class="layer-last">Utolsó: {{.Tier1LastRun}} <span class="layer-last">Utolsó: {{.Tier1LastRun}}
{{if eq .Tier1LastStatus "ok"}}<span class="text-ok"></span> {{if eq .Tier1LastStatus "ok"}}<span class="text-ok"><svg class="ico ico-sm"><use href="#i-check"/></svg></span>
{{else if eq .Tier1LastStatus "error"}}<span class="text-error"></span>{{end}} {{else if eq .Tier1LastStatus "error"}}<span class="text-error"></span>{{end}}
</span> </span>
{{end}} {{end}}
<span class="tier-contents">{{.BackupContents}}</span> <span class="tier-contents">{{.BackupContents}}</span>
{{if and .HasDB (eq .Tier1DBStatus "error")}} {{if and .HasDB (eq .Tier1DBStatus "error")}}
<span class="text-error" style="font-size:.8rem"> DB dump hiba</span> <span class="text-error" style="font-size:.8rem"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> DB dump hiba</span>
{{end}} {{end}}
</div> </div>
<!-- Tier 2: Cross-drive backup (opt-in, different device) --> <!-- Tier 2: Cross-drive backup (opt-in, different device) -->
@@ -402,7 +402,7 @@
{{else if and .Tier2Configured .Tier2DestDisconnected}} {{else if and .Tier2Configured .Tier2DestDisconnected}}
<span class="layer-method" style="opacity:.6">rsync</span> <span class="layer-method" style="opacity:.6">rsync</span>
<span class="layer-dest" style="opacity:.6">→ {{.Tier2Dest}}</span> <span class="layer-dest" style="opacity:.6">→ {{.Tier2Dest}}</span>
<span class="badge badge-warn" style="font-size:.7rem">Cél meghajtó leválasztva</span> <span class="tag tag-warn">Cél meghajtó leválasztva</span>
{{if .Tier2LastRun}} {{if .Tier2LastRun}}
<span class="layer-last" style="opacity:.6">Utolsó: {{.Tier2LastRun}}</span> <span class="layer-last" style="opacity:.6">Utolsó: {{.Tier2LastRun}}</span>
{{end}} {{end}}
@@ -413,7 +413,7 @@
{{else if and .Tier2Configured .Tier2DestInactive}} {{else if and .Tier2Configured .Tier2DestInactive}}
<span class="layer-method" style="opacity:.6">rsync</span> <span class="layer-method" style="opacity:.6">rsync</span>
<span class="layer-dest" style="opacity:.6">→ {{.Tier2Dest}}</span> <span class="layer-dest" style="opacity:.6">→ {{.Tier2Dest}}</span>
<span class="badge badge-warn" style="font-size:.7rem">Cél meghajtó inaktív</span> <span class="tag tag-warn">Cél meghajtó inaktív</span>
{{if .Tier2LastRun}} {{if .Tier2LastRun}}
<span class="layer-last" style="opacity:.6">Utolsó: {{.Tier2LastRun}}</span> <span class="layer-last" style="opacity:.6">Utolsó: {{.Tier2LastRun}}</span>
{{end}} {{end}}
@@ -439,8 +439,8 @@
<a href="/stacks/{{.StackName}}/backup" class="btn btn-xs btn-outline">Beállítás</a> <a href="/stacks/{{.StackName}}/backup" class="btn btn-xs btn-outline">Beállítás</a>
</div> </div>
{{else}} {{else}}
<span class="layer-auto-ok"> 1. mentés auto</span> <span class="layer-auto-ok"><svg class="ico ico-sm"><use href="#i-check"/></svg> 1. mentés auto</span>
<span class="layer-unconfigured"> Nincs 2. (off-drive) másolat</span> <span class="layer-unconfigured"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> Nincs 2. (off-drive) másolat</span>
{{if .Tier2LastError}} {{if .Tier2LastError}}
<span class="layer-reason" style="opacity:.85" title="A 2. mentés automatikus — külön beállítás nem kell">{{.Tier2LastError}}</span> <span class="layer-reason" style="opacity:.85" title="A 2. mentés automatikus — külön beállítás nem kell">{{.Tier2LastError}}</span>
{{end}} {{end}}
@@ -452,9 +452,9 @@
<!-- Tier 3: Remote backup (future) --> <!-- Tier 3: Remote backup (future) -->
<div class="backup-layer-row" style="opacity:.5"> <div class="backup-layer-row" style="opacity:.5">
<span class="tier-label">3. mentés</span> <span class="tier-label">3. mentés</span>
<span class="layer-badge" style="background:var(--bg-tertiary);color:var(--text-muted)">Hamarosan</span> <span class="layer-badge" style="background:var(--bg-2);color:var(--text-3)">Hamarosan</span>
<span class="tier-location">távoli (offsite)</span> <span class="tier-location">távoli (offsite)</span>
<span class="tier-contents" style="font-style:normal;color:var(--text-muted)">B2 / S3 / SFTP — hamarosan elérhető</span> <span class="tier-contents" style="font-style:normal;color:var(--text-3)">B2 / S3 / SFTP — hamarosan elérhető</span>
</div> </div>
</div> </div>
{{if .Warnings}} {{if .Warnings}}
@@ -569,7 +569,7 @@
Még nincs mentés felhasználói adattal. Még nincs mentés felhasználói adattal.
</div> </div>
<div class="restore-warning"> <div class="restore-warning">
A visszaállítás felülírja az alkalmazás jelenlegi adatait a kiválasztott mentés állapotával. <svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> A visszaállítás felülírja az alkalmazás jelenlegi adatait a kiválasztott mentés állapotával.
Az alkalmazás a folyamat során automatikusan leáll és újraindul. Az alkalmazás a folyamat során automatikusan leáll és újraindul.
</div> </div>
<div class="restore-confirm"> <div class="restore-confirm">
@@ -581,7 +581,7 @@
<div class="restore-actions"> <div class="restore-actions">
<button type="button" class="btn btn-sm btn-danger" id="restore-btn" disabled onclick="submitRestore()">Visszaállítás indítása</button> <button type="button" class="btn btn-sm btn-danger" id="restore-btn" disabled onclick="submitRestore()">Visszaállítás indítása</button>
</div> </div>
<div style="margin-top: 1rem; text-align: center; border-top: 1px solid var(--border); padding-top: 1rem;"> <div style="margin-top: 1rem; text-align: center; border-top: 1px solid var(--line); padding-top: 1rem;">
<a href="/import" class="btn btn-sm btn-outline">Importálás mentett csomagból (.fab)</a> <a href="/import" class="btn btn-sm btn-outline">Importálás mentett csomagból (.fab)</a>
</div> </div>
</div> </div>
@@ -679,7 +679,7 @@ function pollGuestBackup(out, btn) {
if (data.ok && data.data) { if (data.ok && data.data) {
var ph = data.data.phase; var ph = data.data.phase;
out.innerHTML = '<span class="form-hint">Állapot: ' + ph + '</span>'; out.innerHTML = '<span class="form-hint">Állapot: ' + ph + '</span>';
if (ph === 'done') { clearInterval(iv); out.innerHTML = '<div class="flash flash-success">A rendszermentés elkészült.</div>'; setTimeout(() => window.location.reload(), 1500); } if (ph === 'done') { clearInterval(iv); out.innerHTML = '<div class="flash flash-success">A rendszermentés elkészült.</div>'; setTimeout(() => window.location.reload(), 1500); }
else if (ph === 'failed') { clearInterval(iv); out.innerHTML = '<div class="alert alert-error">A mentés sikertelen.</div>'; btn.disabled = false; } else if (ph === 'failed') { clearInterval(iv); out.innerHTML = '<div class="alert alert-error">A mentés sikertelen.</div>'; btn.disabled = false; }
} }
}) })
@@ -746,10 +746,10 @@ function onRestoreAppChange() {
typeInfo.innerHTML = '🔄 Teljes visszaállítás: adatbázis + konfiguráció + felhasználói adatok a kiválasztott pillanatképből.'; typeInfo.innerHTML = '🔄 Teljes visszaállítás: adatbázis + konfiguráció + felhasználói adatok a kiválasztott pillanatképből.';
typeInfo.className = 'restore-info'; typeInfo.className = 'restore-info';
} else if (hasDB) { } else if (hasDB) {
typeInfo.innerHTML = ' Adatbázis és konfiguráció visszaállítása — az alkalmazásnak nincs külön felhasználói adata.'; typeInfo.innerHTML = 'Adatbázis és konfiguráció visszaállítása — az alkalmazásnak nincs külön felhasználói adata.';
typeInfo.className = 'restore-info restore-info-partial'; typeInfo.className = 'restore-info restore-info-partial';
} else { } else {
typeInfo.innerHTML = ' Csak konfiguráció visszaállítása (compose fájlok, beállítások).'; typeInfo.innerHTML = 'Csak konfiguráció visszaállítása (compose fájlok, beállítások).';
typeInfo.className = 'restore-info restore-info-partial'; typeInfo.className = 'restore-info restore-info-partial';
} }
typeInfo.style.display = 'block'; typeInfo.style.display = 'block';
+7 -7
View File
@@ -171,7 +171,7 @@
<div id="dr-status"><span class="text-muted">Betöltés...</span></div> <div id="dr-status"><span class="text-muted">Betöltés...</span></div>
<div class="debug-actions" style="margin-top:1rem"> <div class="debug-actions" style="margin-top:1rem">
<div class="debug-dr-danger"> <div class="debug-dr-danger">
<p style="color:var(--red);font-weight:600">Vészhelyzet szimuláció</p> <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> <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"> <div class="debug-form-row">
<label>Írja be "RESET" a megerősítéshez:</label> <label>Írja be "RESET" a megerősítéshez:</label>
@@ -681,8 +681,8 @@ function renderTelemetryDetail(data) {
for (var i = 0; i < apps.length; i++) { for (var i = 0; i < apps.length; i++) {
var a = apps[i]; var a = apps[i];
var errorClass = a.log_errors > 0 ? ' style="color:var(--red);font-weight:600"' : ''; var errorClass = a.log_errors > 0 ? ' style="color:var(--crit);font-weight:600"' : '';
var warnClass = a.log_warnings > 0 ? ' style="color:var(--yellow);font-weight:600"' : ''; var warnClass = a.log_warnings > 0 ? ' style="color:var(--warn);font-weight:600"' : '';
html += '<tr>'; html += '<tr>';
html += '<td><strong>' + escapeHtml(a.display_name || a.app_name) + '</strong></td>'; html += '<td><strong>' + escapeHtml(a.display_name || a.app_name) + '</strong></td>';
html += '<td class="mono" style="font-size:.8rem">' + (a.containers||[]).map(escapeHtml).join(', ') + '</td>'; html += '<td class="mono" style="font-size:.8rem">' + (a.containers||[]).map(escapeHtml).join(', ') + '</td>';
@@ -698,7 +698,7 @@ function renderTelemetryDetail(data) {
if (a.issues && a.issues.length > 0) { if (a.issues && a.issues.length > 0) {
for (var j = 0; j < a.issues.length; j++) { for (var j = 0; j < a.issues.length; j++) {
var issue = a.issues[j]; var issue = a.issues[j];
var sevColor = issue.severity === 'error' ? 'var(--red)' : 'var(--yellow)'; var sevColor = issue.severity === 'error' ? 'var(--crit)' : 'var(--warn)';
html += '<tr style="font-size:.8rem;opacity:.85"><td></td>'; html += '<tr style="font-size:.8rem;opacity:.85"><td></td>';
html += '<td colspan="6" style="padding-left:1.5rem"><span style="color:' + sevColor + '">' + escapeHtml(issue.severity.toUpperCase()) + '</span> ' + escapeHtml(issue.message) + '</td>'; html += '<td colspan="6" style="padding-left:1.5rem"><span style="color:' + sevColor + '">' + escapeHtml(issue.severity.toUpperCase()) + '</span> ' + escapeHtml(issue.message) + '</td>';
html += '<td colspan="2">×' + (issue.count||0) + '</td></tr>'; html += '<td colspan="2">×' + (issue.count||0) + '</td></tr>';
@@ -735,7 +735,7 @@ function renderAppExportStatus(d) {
html += '<dt>Verzió</dt><dd class="mono">' + (d.version||'-') + '</dd>'; html += '<dt>Verzió</dt><dd class="mono">' + (d.version||'-') + '</dd>';
html += '<dt>Csomagok</dt><dd>' + (d.bundle_count||0) + ' db</dd>'; html += '<dt>Csomagok</dt><dd>' + (d.bundle_count||0) + ' db</dd>';
html += '<dt>Temp fájlok</dt><dd>' + (d.stale_temp_count||0) + ' db'; html += '<dt>Temp fájlok</dt><dd>' + (d.stale_temp_count||0) + ' db';
if (d.stale_temp_count > 0) html += ' <span style="color:var(--warning)">⚠</span>'; if (d.stale_temp_count > 0) html += ' <span style="color:var(--warn)">⚠</span>';
html += '</dd>'; html += '</dd>';
html += '</div>'; html += '</div>';
@@ -755,7 +755,7 @@ function renderAppExportStatus(d) {
html += '<div style="margin-top:.5rem">'; html += '<div style="margin-top:.5rem">';
j.steps.forEach(function(s) { j.steps.forEach(function(s) {
var icon = s.status === 'done' ? '✓' : s.status === 'running' ? '⟳' : s.status === 'failed' ? '✗' : '○'; var icon = s.status === 'done' ? '✓' : s.status === 'running' ? '⟳' : s.status === 'failed' ? '✗' : '○';
var cls = s.status === 'failed' ? 'color:var(--danger)' : s.status === 'done' ? 'color:var(--success)' : s.status === 'running' ? 'color:var(--primary)' : 'color:var(--text-muted)'; var cls = s.status === 'failed' ? 'color:var(--crit)' : s.status === 'done' ? 'color:var(--blue)' : s.status === 'running' ? 'color:var(--blue)' : 'color:var(--text-3)';
html += '<div style="font-size:.85rem;padding:.1rem 0;' + cls + '">' + icon + ' ' + escapeHtml(s.label); html += '<div style="font-size:.85rem;padding:.1rem 0;' + cls + '">' + icon + ' ' + escapeHtml(s.label);
if (s.error) html += ' <span style="font-size:.8rem">(' + escapeHtml(s.error) + ')</span>'; if (s.error) html += ' <span style="font-size:.8rem">(' + escapeHtml(s.error) + ')</span>';
html += '</div>'; html += '</div>';
@@ -776,7 +776,7 @@ function renderAppExportStatus(d) {
// Stale temp files // Stale temp files
if (d.stale_temp_files && d.stale_temp_files.length > 0) { if (d.stale_temp_files && d.stale_temp_files.length > 0) {
html += '<h4 style="margin-top:.75rem;color:var(--warning)">Elavult temp fájlok</h4>'; html += '<h4 style="margin-top:.75rem;color:var(--warn)">Elavult temp fájlok</h4>';
html += '<ul style="font-size:.85rem;margin:0;padding-left:1.5rem">'; html += '<ul style="font-size:.85rem;margin:0;padding-left:1.5rem">';
d.stale_temp_files.forEach(function(f) { d.stale_temp_files.forEach(function(f) {
html += '<li class="mono" style="font-size:.75rem">' + escapeHtml(f) + '</li>'; html += '<li class="mono" style="font-size:.75rem">' + escapeHtml(f) + '</li>';
+33 -33
View File
@@ -18,7 +18,7 @@
<a href="https://{{.EffectiveSubdomain}}.{{.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a> <a href="https://{{.EffectiveSubdomain}}.{{.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a>
{{end}} {{end}}
{{end}} {{end}}
<a href="/apps/{{.Meta.Slug}}" class="btn btn-sm btn-outline"> Részletek</a> <a href="/apps/{{.Meta.Slug}}" class="btn btn-sm btn-outline"><svg class="ico ico-sm"><use href="#i-info"/></svg> Részletek</a>
</div> </div>
</div> </div>
@@ -30,11 +30,11 @@
<div> <div>
<h3>{{.Meta.DisplayName}}</h3> <h3>{{.Meta.DisplayName}}</h3>
{{if .Meta.Description}}<p>{{.Meta.Description}}</p>{{end}} {{if .Meta.Description}}<p>{{.Meta.Description}}</p>{{end}}
<div class="stack-meta-badges"> <div class="metarows">
{{if .Meta.Resources.MemRequest}}<span class="meta-badge">~{{.Meta.Resources.MemRequest}}</span>{{end}} {{if .Meta.Resources.MemRequest}}<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~{{.Meta.Resources.MemRequest}}</span></span>{{end}}
{{if .Meta.Resources.PiCompatible}}<span class="meta-badge meta-badge-ok">Pi kompatibilis</span>{{end}} {{if .Meta.Resources.PiCompatible}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Pi kompatibilis</span>{{end}}
{{if .Meta.Resources.NeedsHDD}}<span class="meta-badge">HDD szükséges</span>{{end}} {{if .Meta.Resources.NeedsHDD}}<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>HDD szükséges</span>{{end}}
{{if .Meta.Resources.HungarianUI}}<span class="meta-badge meta-badge-ok">Magyar felület</span>{{end}} {{if .Meta.Resources.HungarianUI}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Magyar felület</span>{{end}}
</div> </div>
<a href="/apps/{{.Meta.Slug}}" class="btn btn-sm btn-outline" style="margin-top:0.5rem"> <a href="/apps/{{.Meta.Slug}}" class="btn btn-sm btn-outline" style="margin-top:0.5rem">
Részletes leírás, képernyőképek Részletes leírás, képernyőképek
@@ -52,7 +52,7 @@
<div class="settings-grid"> <div class="settings-grid">
<div class="settings-row"> <div class="settings-row">
<span class="settings-label">Tárhely</span> <span class="settings-label">Tárhely</span>
<span class="settings-value">{{.StorageInfo.Label}} <span class="mono" style="color:var(--text-secondary)">({{.StorageInfo.Path}})</span></span> <span class="settings-value">{{.StorageInfo.Label}} <span class="mono" style="color:var(--text-2)">({{.StorageInfo.Path}})</span></span>
</div> </div>
{{if .StorageInfo.DataSizeHuman}} {{if .StorageInfo.DataSizeHuman}}
<div class="settings-row"> <div class="settings-row">
@@ -86,7 +86,7 @@
<div class="settings-grid" style="margin-bottom:.75rem"> <div class="settings-grid" style="margin-bottom:.75rem">
<div class="settings-row"> <div class="settings-row">
<span class="settings-label">Tárhely</span> <span class="settings-label">Tárhely</span>
<span class="settings-value">{{.Label}} <span class="mono" style="color:var(--text-secondary)">({{.Path}})</span></span> <span class="settings-value">{{.Label}} <span class="mono" style="color:var(--text-2)">({{.Path}})</span></span>
</div> </div>
<div class="settings-row"> <div class="settings-row">
<span class="settings-label">Méret</span> <span class="settings-label">Méret</span>
@@ -113,7 +113,7 @@
<div class="cross-drive-nightly"> <div class="cross-drive-nightly">
<span class="form-hint" style="display:block;margin-top:.25rem"> <span class="form-hint" style="display:block;margin-top:.25rem">
Az alkalmazás adatbázisa és Docker kötetei automatikusan bekerülnek az éjszakai biztonsági mentésbe. Az alkalmazás adatbázisa és Docker kötetei automatikusan bekerülnek az éjszakai biztonsági mentésbe.
<a href="/backups" style="color:var(--accent-blue)">Mentési állapot →</a> <a href="/backups" style="color:var(--blue)">Mentési állapot →</a>
</span> </span>
</div> </div>
</div> </div>
@@ -127,18 +127,18 @@
</p> </p>
{{range .Integrations}} {{range .Integrations}}
<div class="config-field" style="display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.75rem 0;border-bottom:1px solid var(--border)"> <div class="config-field" style="display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.75rem 0;border-bottom:1px solid var(--line)">
<div style="flex:1"> <div style="flex:1">
<strong>{{.Label}}</strong> <strong>{{.Label}}</strong>
<p class="config-field-help" style="margin:0">{{.Description}}</p> <p class="config-field-help" style="margin:0">{{.Description}}</p>
{{if not .TargetDeployed}} {{if not .TargetDeployed}}
<span class="badge badge-muted" style="margin-top:.25rem;display:inline-block">Nincs telepítve</span> <span class="tag tag-off" style="margin-top:.25rem"><span class="dot"></span>Nincs telepítve</span>
{{else if not .TargetRunning}} {{else if not .TargetRunning}}
<span class="badge badge-orphaned" style="margin-top:.25rem;display:inline-block">Célalkalmazás leállítva</span> <span class="tag tag-neutral" style="margin-top:.25rem"><span class="dot"></span>Célalkalmazás leállítva</span>
{{else if eq .Status "error"}} {{else if eq .Status "error"}}
<span class="badge badge-orphaned" style="margin-top:.25rem;display:inline-block">Hiba</span> <span class="tag tag-crit" style="margin-top:.25rem"><span class="dot"></span>Hiba</span>
{{else if .Enabled}} {{else if .Enabled}}
<span class="badge badge-ok" style="margin-top:.25rem;display:inline-block">Aktív</span> <span class="tag tag-run" style="margin-top:.25rem"><span class="dot"></span>Aktív</span>
{{end}} {{end}}
</div> </div>
<label class="toggle"> <label class="toggle">
@@ -433,7 +433,7 @@
<span class="memory-summary-label">Memória</span> <span class="memory-summary-label">Memória</span>
<span class="memory-summary-value">{{.AfterMB}} MB / {{.UsableMB}} MB ({{.Percent}}%)</span> <span class="memory-summary-value">{{.AfterMB}} MB / {{.UsableMB}} MB ({{.Percent}}%)</span>
</div> </div>
<div class="memory-bar-stacked"> <div class="meter-track meter-track-stacked">
<div class="memory-bar-segment memory-bar-committed" style="width:{{.UsedPercent}}%" title="Jelenlegi használat: {{.UsedMB}} MB"></div> <div class="memory-bar-segment memory-bar-committed" style="width:{{.UsedPercent}}%" title="Jelenlegi használat: {{.UsedMB}} MB"></div>
<div class="memory-bar-segment memory-bar-new" style="width:{{subtract .Percent .UsedPercent}}%" title="{{$.Meta.DisplayName}}: +{{.NewRequestMB}} MB"></div> <div class="memory-bar-segment memory-bar-new" style="width:{{subtract .Percent .UsedPercent}}%" title="{{$.Meta.DisplayName}}: +{{.NewRequestMB}} MB"></div>
</div> </div>
@@ -456,7 +456,7 @@
<form id="deploy-form" class="deploy-form"> <form id="deploy-form" class="deploy-form">
{{if .DockerBelowReserve}} {{if .DockerBelowReserve}}
<div class="alert alert-warning" style="margin-bottom:1rem"> <div class="alert alert-warning" style="margin-bottom:1rem">
Nincs elég szabad tárhely a telepítéshez. Jelenleg {{.DockerFreeHuman}} szabad, és a rendszer <svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> Nincs elég szabad tárhely a telepítéshez. Jelenleg {{.DockerFreeHuman}} szabad, és a rendszer
{{.DockerReserveHuman}} tartalékot tart fenn az alapszolgáltatások (vezérlő, proxy) védelmében. {{.DockerReserveHuman}} tartalékot tart fenn az alapszolgáltatások (vezérlő, proxy) védelmében.
A telepítés ezért átmenetileg le van tiltva — szabadítson fel helyet, vagy bővítse a tárhelyet. A telepítés ezért átmenetileg le van tiltva — szabadítson fel helyet, vagy bővítse a tárhelyet.
</div> </div>
@@ -474,7 +474,7 @@
{{range .AutoFields}} {{range .AutoFields}}
{{$val := index $autoValues .EnvVar}} {{$val := index $autoValues .EnvVar}}
<div class="form-group form-group-auto"> <div class="form-group form-group-auto">
<label>{{.Label}} {{if eq .Type "secret"}}<span class="auto-generated-badge"> Automatikusan generálva</span>{{end}}</label> <label>{{.Label}} {{if eq .Type "secret"}}<span class="auto-generated-badge"><svg class="ico ico-sm"><use href="#i-check"/></svg> Automatikusan generálva</span>{{end}}</label>
{{if $val}} {{if $val}}
{{if eq .Type "secret"}} {{if eq .Type "secret"}}
<div class="input-with-button"> <div class="input-with-button">
@@ -569,11 +569,11 @@
</option> </option>
{{end}} {{end}}
</select> </select>
<div id="storage-space-warn" class="form-hint" style="color:var(--yellow);display:none"> <div id="storage-space-warn" class="form-hint" style="color:var(--warn);display:none">
⚠️ A kiválasztott tárhely majdnem megtelt. <svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> A kiválasztott tárhely majdnem megtelt.
</div> </div>
<div class="form-hint" style="margin-top:.4rem;opacity:.8"> <div class="form-hint" style="margin-top:.4rem;opacity:.8">
A kiválasztott meghajtón az alkalmazás <strong>fájljai</strong> (média, dokumentumok) tárolódnak. <svg class="ico ico-sm"><use href="#i-info"/></svg> A kiválasztott meghajtón az alkalmazás <strong>fájljai</strong> (média, dokumentumok) tárolódnak.
Az <strong>adatbázis a gyors belső SSD-n</strong> fut — és az alkalmazással együtt készül róla biztonsági mentés. Az <strong>adatbázis a gyors belső SSD-n</strong> fut — és az alkalmazással együtt készül róla biztonsági mentés.
</div> </div>
{{else}} {{else}}
@@ -582,7 +582,7 @@
placeholder="{{.Placeholder}}" placeholder="{{.Placeholder}}"
{{if .Required}}required{{end}} {{if .Required}}required{{end}}
{{if $.AlreadyDeployed}}disabled{{end}}> {{if $.AlreadyDeployed}}disabled{{end}}>
<span class="form-hint" style="color:var(--yellow)">Nincs regisztrált adattároló — adja meg kézzel az útvonalat</span> <span class="form-hint" style="color:var(--warn)">Nincs regisztrált adattároló — adja meg kézzel az útvonalat</span>
{{end}} {{end}}
{{else}} {{else}}
<input type="text" id="field-{{.EnvVar}}" name="{{.EnvVar}}" <input type="text" id="field-{{.EnvVar}}" name="{{.EnvVar}}"
@@ -612,15 +612,15 @@
<h3>Telepítés folyamatban...</h3> <h3>Telepítés folyamatban...</h3>
<div class="deploy-steps"> <div class="deploy-steps">
<div class="deploy-step active" id="step-config"> <div class="deploy-step active" id="step-config">
<span class="step-icon">&#9203;</span> <span class="step-icon"><svg class="ico"><use href="#i-rotate-cw"/></svg></span>
<span class="step-text">Konfiguráció mentése...</span> <span class="step-text">Konfiguráció mentése...</span>
</div> </div>
<div class="deploy-step" id="step-containers"> <div class="deploy-step" id="step-containers">
<span class="step-icon">&#9203;</span> <span class="step-icon"><svg class="ico"><use href="#i-rotate-cw"/></svg></span>
<span class="step-text">Konténer(ek) indítása...</span> <span class="step-text">Konténer(ek) indítása...</span>
</div> </div>
<div class="deploy-step" id="step-health"> <div class="deploy-step" id="step-health">
<span class="step-icon">&#9203;</span> <span class="step-icon"><svg class="ico"><use href="#i-rotate-cw"/></svg></span>
<span class="step-text">Alkalmazás inicializálása...</span> <span class="step-text">Alkalmazás inicializálása...</span>
</div> </div>
</div> </div>
@@ -690,15 +690,15 @@ function buildPostDeployCard(stackName) {
if (!val) continue; if (!val) continue;
if (isPassword) { if (isPassword) {
var credId = 'postdeploy-cred-' + i; var credId = 'postdeploy-cred-' + i;
credRows += '<tr><td style="padding:.25rem .75rem .25rem 0;color:var(--text-muted);white-space:nowrap">' + f.label + '</td>' + credRows += '<tr><td style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap">' + f.label + '</td>' +
'<td style="padding:.25rem 0;display:flex;align-items:center;gap:.5rem">' + '<td style="padding:.25rem 0;display:flex;align-items:center;gap:.5rem">' +
'<code id="' + credId + '" style="font-family:var(--font-mono);letter-spacing:1px">••••••••••••</code>' + '<code id="' + credId + '" style="font-family:var(--font-data);letter-spacing:1px">••••••••••••</code>' +
'<button type="button" class="btn btn-sm btn-outline" onclick="togglePostDeployCred(this,\'' + credId + '\',\'' + val.replace(/'/g, "\\'") + '\')" style="padding:.15rem .4rem;font-size:.75rem">Megjelenítés</button>' + '<button type="button" class="btn btn-sm btn-outline" onclick="togglePostDeployCred(this,\'' + credId + '\',\'' + val.replace(/'/g, "\\'") + '\')" style="padding:.15rem .4rem;font-size:.75rem">Megjelenítés</button>' +
'<button type="button" class="btn btn-sm btn-outline" onclick="copyPostDeployCred(this,\'' + val.replace(/'/g, "\\'") + '\')" style="padding:.15rem .4rem;font-size:.75rem">Másolás</button>' + '<button type="button" class="btn btn-sm btn-outline" onclick="copyPostDeployCred(this,\'' + val.replace(/'/g, "\\'") + '\')" style="padding:.15rem .4rem;font-size:.75rem">Másolás</button>' +
'</td></tr>'; '</td></tr>';
} else { } else {
credRows += '<tr><td style="padding:.25rem .75rem .25rem 0;color:var(--text-muted);white-space:nowrap">' + f.label + '</td>' + credRows += '<tr><td style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap">' + f.label + '</td>' +
'<td style="padding:.25rem 0;font-family:var(--font-mono);user-select:all">' + val + '</td></tr>'; '<td style="padding:.25rem 0;font-family:var(--font-data);user-select:all">' + val + '</td></tr>';
} }
} }
} }
@@ -790,7 +790,7 @@ function copyPostDeployCred(btn, val) {
} }
function deleteStaleData(stackName, stalePath, btn) { function deleteStaleData(stackName, stalePath, btn) {
if (!confirm('Biztosan törölni szeretnéd a korábbi adatokat?\n\nTárhely: ' + stalePath + '\n\n⚠️ Ez a művelet visszavonhatatlan!\nElőtte győződj meg róla, hogy az alkalmazás az új tárolóról megfelelően működik.')) { if (!confirm('Biztosan törölni szeretnéd a korábbi adatokat?\n\nTárhely: ' + stalePath + '\n\nFIGYELEM: Ez a művelet visszavonhatatlan!\nElőtte győződj meg róla, hogy az alkalmazás az új tárolóról megfelelően működik.')) {
return; return;
} }
// Second confirmation // Second confirmation
@@ -913,10 +913,10 @@ document.getElementById('deploy-form').addEventListener('submit', async function
el.className = 'deploy-step ' + status; el.className = 'deploy-step ' + status;
if (text) el.querySelector('.step-text').textContent = text; if (text) el.querySelector('.step-text').textContent = text;
var icon = el.querySelector('.step-icon'); var icon = el.querySelector('.step-icon');
if (status === 'done') icon.textContent = '\u2705'; if (status === 'done') icon.innerHTML = '<svg class="ico"><use href="#i-check"/></svg>';
else if (status === 'error') icon.textContent = '\u274C'; else if (status === 'error') icon.innerHTML = '<svg class="ico"><use href="#i-x"/></svg>';
else if (status === 'warn') icon.textContent = '\u26A0\uFE0F'; else if (status === 'warn') icon.innerHTML = '<svg class="ico"><use href="#i-triangle-alert"/></svg>';
else if (status === 'active') icon.textContent = '\u23F3'; else if (status === 'active') icon.innerHTML = '<svg class="ico ico-spin"><use href="#i-rotate-cw"/></svg>';
} }
// Phase 1: Deploy request // Phase 1: Deploy request
+13 -13
View File
@@ -10,7 +10,7 @@
<meta name="csrf-token" content="{{.CSRFToken}}"> <meta name="csrf-token" content="{{.CSRFToken}}">
<script> <script>
function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};} function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};}
function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Üzenet</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-secondary);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);} function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Üzenet</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-2);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);}
</script> </script>
</head> </head>
<body> <body>
@@ -156,7 +156,7 @@
} }
modal.querySelector('.modal-card').innerHTML = modal.querySelector('.modal-card').innerHTML =
'<h3>Alkalmazás törlése: ' + name + '</h3>' + '<h3>Alkalmazás törlése: ' + name + '</h3>' +
'<p style="color:var(--text-secondary);font-size:.9rem;margin-bottom:.75rem">Ez a művelet eltávolítja a konténereket, a köteteket és a konfigurációs fájlokat.</p>' + '<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">Ez a művelet eltávolítja a konténereket, a köteteket és a konfigurációs fájlokat.</p>' +
'<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' + '<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' +
hddInfo + checkboxHTML + hddInfo + checkboxHTML +
'<div class="modal-actions">' + '<div class="modal-actions">' +
@@ -165,7 +165,7 @@
'</div>'; '</div>';
} catch (err) { } catch (err) {
modal.querySelector('.modal-card').innerHTML = modal.querySelector('.modal-card').innerHTML =
'<h3>Hiba</h3><p style="color:var(--text-secondary)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' + '<h3>Hiba</h3><p style="color:var(--text-2)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' +
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Bezárás</button></div>'; '<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Bezárás</button></div>';
} }
} }
@@ -190,15 +190,15 @@
var modal = document.getElementById('delete-modal'); var modal = document.getElementById('delete-modal');
var removedInfo = ''; var removedInfo = '';
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
removedInfo = '<p style="color:var(--text-secondary);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>'; removedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
} }
var preservedInfo = ''; var preservedInfo = '';
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
preservedInfo = '<p style="color:var(--text-secondary);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>'; preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
} }
modal.querySelector('.modal-card').innerHTML = modal.querySelector('.modal-card').innerHTML =
'<h3>Sikeresen törölve!</h3>' + '<h3>Sikeresen törölve!</h3>' +
'<p style="color:var(--text-secondary)">Az alkalmazás (' + name + ') törölve lett.</p>' + '<p style="color:var(--text-2)">Az alkalmazás (' + name + ') törölve lett.</p>' +
removedInfo + preservedInfo + removedInfo + preservedInfo +
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>'; '<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>';
} else { } else {
@@ -228,7 +228,7 @@
// Section 1: Always removed // Section 1: Always removed
sections += '<div class="modal-section">' + sections += '<div class="modal-section">' +
'<strong>Mindig törlődik:</strong>' + '<strong>Mindig törlődik:</strong>' +
'<ul style="margin:.25rem 0;padding-left:1.2rem;color:var(--text-secondary);font-size:.85rem">' + '<ul style="margin:.25rem 0;padding-left:1.2rem;color:var(--text-2);font-size:.85rem">' +
'<li>Docker kötetek (adatbázis, alkalmazás konfiguráció)</li>' + '<li>Docker kötetek (adatbázis, alkalmazás konfiguráció)</li>' +
'<li>Telepítési konfiguráció (app.yaml)</li>' + '<li>Telepítési konfiguráció (app.yaml)</li>' +
'<li>Másodlagos mentés ütemezése</li>' + '<li>Másodlagos mentés ütemezése</li>' +
@@ -266,7 +266,7 @@
} }
modal.querySelector('.modal-card').innerHTML = modal.querySelector('.modal-card').innerHTML =
'<h3>Alkalmazás eltávolítása: ' + name + '</h3>' + '<h3>Alkalmazás eltávolítása: ' + name + '</h3>' +
'<p style="color:var(--text-secondary);font-size:.9rem;margin-bottom:.75rem">Az alkalmazás visszaáll "Nincs telepítve" állapotba. A sablon megmarad, újratelepíthető.</p>' + '<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">Az alkalmazás visszaáll "Nincs telepítve" állapotba. A sablon megmarad, újratelepíthető.</p>' +
'<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' + '<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' +
sections + sections +
'<div class="modal-actions">' + '<div class="modal-actions">' +
@@ -275,7 +275,7 @@
'</div>' + hddCheckbox; '</div>' + hddCheckbox;
} catch (err) { } catch (err) {
modal.querySelector('.modal-card').innerHTML = modal.querySelector('.modal-card').innerHTML =
'<h3>Hiba</h3><p style="color:var(--text-secondary)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' + '<h3>Hiba</h3><p style="color:var(--text-2)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' +
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">Bezárás</button></div>'; '<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">Bezárás</button></div>';
} }
} }
@@ -302,18 +302,18 @@
var modal = document.getElementById('remove-modal'); var modal = document.getElementById('remove-modal');
var removedInfo = ''; var removedInfo = '';
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
removedInfo += '<p style="color:var(--text-secondary);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>'; removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
} }
if (data.data && data.data.backup_paths_removed && data.data.backup_paths_removed.length > 0) { if (data.data && data.data.backup_paths_removed && data.data.backup_paths_removed.length > 0) {
removedInfo += '<p style="color:var(--text-secondary);font-size:.85rem;margin-top:.5rem">Törölt mentések: ' + data.data.backup_paths_removed.join(', ') + '</p>'; removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt mentések: ' + data.data.backup_paths_removed.join(', ') + '</p>';
} }
var preservedInfo = ''; var preservedInfo = '';
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
preservedInfo = '<p style="color:var(--text-secondary);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>'; preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
} }
modal.querySelector('.modal-card').innerHTML = modal.querySelector('.modal-card').innerHTML =
'<h3>Sikeresen eltávolítva!</h3>' + '<h3>Sikeresen eltávolítva!</h3>' +
'<p style="color:var(--text-secondary)">Az alkalmazás (' + name + ') eltávolítva. Újratelepíthető a Telepítés gombbal.</p>' + '<p style="color:var(--text-2)">Az alkalmazás (' + name + ') eltávolítva. Újratelepíthető a Telepítés gombbal.</p>' +
removedInfo + preservedInfo + removedInfo + preservedInfo +
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>'; '<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>';
} else { } else {
@@ -10,7 +10,7 @@
<div class="monitor-card" id="host-health-card"> <div class="monitor-card" id="host-health-card">
<div class="monitor-card-header"> <div class="monitor-card-header">
<h3>Szerver állapota (gazdagép)</h3> <h3>Szerver állapota (gazdagép)</h3>
<span class="sysinfo-value" id="host-health-updated" style="font-size:.75rem;color:var(--text-muted)"></span> <span class="sysinfo-value" id="host-health-updated" style="font-size:.75rem;color:var(--text-3)"></span>
</div> </div>
<div id="host-health-unavailable" class="monitoring-banner monitoring-banner-yellow" style="display:none"> <div id="host-health-unavailable" class="monitoring-banner monitoring-banner-yellow" style="display:none">
A gazdagép metrikái jelenleg nem elérhetők. A gazdagép metrikái jelenleg nem elérhetők.
@@ -79,32 +79,32 @@
<h3>Tárhely</h3> <h3>Tárhely</h3>
<div class="storage-bars"> <div class="storage-bars">
{{with .SystemInfo}} {{with .SystemInfo}}
<div class="storage-item"> <div class="storage-item meter {{usageColor .DiskPercent}}">
<div class="storage-header"> <div class="storage-header">
<span class="storage-label">Rendszer (/)</span> <span class="storage-label">Rendszer (/)</span>
<span class="storage-value">{{fmtGB .DiskUsedGB}} / {{fmtGB .DiskTotalGB}} ({{printf "%.0f" .DiskPercent}}%)</span> <span class="storage-value">{{fmtGB .DiskUsedGB}} / {{fmtGB .DiskTotalGB}} ({{printf "%.0f" .DiskPercent}}%)</span>
</div> </div>
<div class="system-bar"> <div class="meter-track">
<div class="system-bar-fill {{usageColor .DiskPercent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .DiskPercent}}%"></div> <div class="meter-fill" style="width:{{printf "%.1f" .DiskPercent}}%"></div>
</div> </div>
</div> </div>
{{range $.StorageBars}} {{range $.StorageBars}}
{{if .Disconnected}} {{if .Disconnected}}
<div class="storage-item storage-disconnected"> <div class="storage-item meter meter-empty storage-disconnected">
<div class="storage-header"> <div class="storage-header">
<span class="storage-label">{{.Label}}</span> <span class="storage-label">{{.Label}}</span>
<span class="storage-value badge-error" style="font-size:.75rem">Leválasztva</span> <span class="storage-value"><span class="state-text-neutral">Leválasztva</span></span>
</div> </div>
<div class="system-bar"><div class="system-bar-disconnected"></div></div> <div class="meter-track"></div>
</div> </div>
{{else}} {{else}}
<div class="storage-item"> <div class="storage-item meter {{usageColor .Percent}}">
<div class="storage-header"> <div class="storage-header">
<span class="storage-label">{{.Label}}</span> <span class="storage-label">{{.Label}}</span>
<span class="storage-value">{{fmtGB .UsedGB}} / {{fmtGB .TotalGB}} ({{printf "%.0f" .Percent}}%)</span> <span class="storage-value">{{fmtGB .UsedGB}} / {{fmtGB .TotalGB}} ({{printf "%.0f" .Percent}}%)</span>
</div> </div>
<div class="system-bar"> <div class="meter-track">
<div class="system-bar-fill {{usageColor .Percent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .Percent}}%"></div> <div class="meter-fill" style="width:{{printf "%.1f" .Percent}}%"></div>
</div> </div>
{{if .Purpose}}<div class="storage-purpose" style="font-size:.72rem;opacity:.65;margin-top:.2rem">{{.Purpose}}</div>{{end}} {{if .Purpose}}<div class="storage-purpose" style="font-size:.72rem;opacity:.65;margin-top:.2rem">{{.Purpose}}</div>{{end}}
</div> </div>
@@ -259,10 +259,10 @@
// --- Chart.js dark theme defaults --- // --- Chart.js dark theme defaults ---
var colors = { var colors = {
cpu: {border: '#0088cc', bg: 'rgba(0,136,204,0.1)'}, cpu: {border: '#2EA8F5', bg: 'rgba(46,168,245,0.1)'},
memory: {border: '#238636', bg: 'rgba(35,134,54,0.1)'}, memory: {border: '#8E7CE8', bg: 'rgba(142,124,232,0.1)'},
temp: {border: '#d29922', bg: 'rgba(210,153,34,0.1)'}, temp: {border: '#E0A93E', bg: 'rgba(224,169,62,0.1)'},
load: {border: '#db6d28', bg: 'rgba(219,109,40,0.1)'} load: {border: '#5EC4B6', bg: 'rgba(94,196,182,0.1)'}
}; };
// --- Range helper: returns milliseconds for a range string --- // --- Range helper: returns milliseconds for a range string ---
@@ -294,10 +294,10 @@
plugins: { plugins: {
legend: {display: false}, legend: {display: false},
tooltip: { tooltip: {
backgroundColor: '#1c2128', backgroundColor: '#0F1B2E',
titleColor: '#e6edf3', titleColor: '#EDF2F9',
bodyColor: '#8b949e', bodyColor: '#94A6BF',
borderColor: '#30363d', borderColor: '#22344F',
borderWidth: 1, borderWidth: 1,
callbacks: { callbacks: {
title: function(items) { title: function(items) {
@@ -319,9 +319,9 @@
type: 'linear', type: 'linear',
min: now - defaultRangeMs, min: now - defaultRangeMs,
max: now, max: now,
grid: {color: 'rgba(48,54,61,0.5)'}, grid: {color: 'rgba(34,52,79,0.5)'},
ticks: { ticks: {
color: '#8b949e', color: '#94A6BF',
maxTicksLimit: 8, maxTicksLimit: 8,
callback: function(v) { callback: function(v) {
return formatTimeLabel(v); return formatTimeLabel(v);
@@ -329,10 +329,10 @@
} }
}, },
y: { y: {
grid: {color: 'rgba(48,54,61,0.5)'}, grid: {color: 'rgba(34,52,79,0.5)'},
ticks: {color: '#8b949e'}, ticks: {color: '#94A6BF'},
beginAtZero: beginAtZero !== false, beginAtZero: beginAtZero !== false,
title: {display: !!yLabel, text: yLabel || '', color: '#6e7681', font: {size: 11}} title: {display: !!yLabel, text: yLabel || '', color: '#5E7392', font: {size: 11}}
} }
} }
}; };
@@ -481,23 +481,23 @@
plugins: { plugins: {
legend: {display: false}, legend: {display: false},
tooltip: { tooltip: {
backgroundColor: '#1c2128', backgroundColor: '#0F1B2E',
titleColor: '#e6edf3', titleColor: '#EDF2F9',
bodyColor: '#8b949e', bodyColor: '#94A6BF',
borderColor: '#30363d', borderColor: '#22344F',
borderWidth: 1 borderWidth: 1
} }
}, },
scales: { scales: {
x: { x: {
grid: {color: 'rgba(48,54,61,0.5)'}, grid: {color: 'rgba(34,52,79,0.5)'},
ticks: {color: '#8b949e'}, ticks: {color: '#94A6BF'},
beginAtZero: true, beginAtZero: true,
title: {display: true, text: xLabel, color: '#6e7681', font: {size: 11}} title: {display: true, text: xLabel, color: '#5E7392', font: {size: 11}}
}, },
y: { y: {
grid: {display: false}, grid: {display: false},
ticks: {color: '#8b949e', font: {size: 12}} ticks: {color: '#94A6BF', font: {size: 12}}
} }
}, },
onClick: function(evt, elements) { onClick: function(evt, elements) {
@@ -513,12 +513,12 @@
chartContainerCPU = new Chart(document.getElementById('chart-container-cpu'), { chartContainerCPU = new Chart(document.getElementById('chart-container-cpu'), {
type: 'bar', type: 'bar',
data: {labels: [], datasets: [{data: [], backgroundColor: '#0088cc', borderRadius: 4}]}, data: {labels: [], datasets: [{data: [], backgroundColor: '#2EA8F5', borderRadius: 2}]},
options: barOpts('%') options: barOpts('%')
}); });
chartContainerMem = new Chart(document.getElementById('chart-container-mem'), { chartContainerMem = new Chart(document.getElementById('chart-container-mem'), {
type: 'bar', type: 'bar',
data: {labels: [], datasets: [{data: [], backgroundColor: '#238636', borderRadius: 4}]}, data: {labels: [], datasets: [{data: [], backgroundColor: '#8E7CE8', borderRadius: 2}]},
options: barOpts('MB') options: barOpts('MB')
}); });
} }
@@ -560,7 +560,7 @@
} }
} }
var memDistPalette = ['#238636','#0088cc','#d29922','#da3633','#8b5cf6','#ec6547','#2ea043','#1f6feb','#e3b341','#f47067']; var memDistPalette = ['#2EA8F5','#8E7CE8','#5EC4B6','#E0A93E','#D67BB5','#7FA6E8','#59B88C','#E8935E','#6FCADF','#B3A1F0'];
async function buildMemoryDistributionBar(containers) { async function buildMemoryDistributionBar(containers) {
var totalMB = {{.SystemInfo.TotalMemMB}}; var totalMB = {{.SystemInfo.TotalMemMB}};
@@ -608,12 +608,12 @@
// OS / system overhead // OS / system overhead
if (osMB > 10) { if (osMB > 10) {
var osPct = (osMB / totalMB * 100).toFixed(2); var osPct = (osMB / totalMB * 100).toFixed(2);
html += '<div class="memory-bar-segment" style="width:' + osPct + '%;background:var(--text-muted);opacity:0.5" title="Rendszer: ' + fmtMB(osMB) + '"></div>'; html += '<div class="memory-bar-segment" style="width:' + osPct + '%;background:var(--text-3);opacity:0.5" title="Rendszer: ' + fmtMB(osMB) + '"></div>';
legendHtml += '<div class="memory-legend-item"><span class="memory-legend-dot" style="background:var(--text-muted);opacity:0.5"></span>Rendszer (' + fmtMB(osMB) + ')</div>'; legendHtml += '<div class="memory-legend-item"><span class="memory-legend-dot" style="background:var(--text-3);opacity:0.5"></span>Rendszer (' + fmtMB(osMB) + ')</div>';
} }
// Free space legend only // Free space legend only
legendHtml += '<div class="memory-legend-item"><span class="memory-legend-dot" style="background:var(--bg-secondary);border:1px solid var(--border-color)"></span>Szabad (' + fmtMB(freeMB) + ')</div>'; legendHtml += '<div class="memory-legend-item"><span class="memory-legend-dot" style="background:var(--bg-2);border:1px solid var(--line)"></span>Szabad (' + fmtMB(freeMB) + ')</div>';
bar.innerHTML = html; bar.innerHTML = html;
legend.innerHTML = legendHtml; legend.innerHTML = legendHtml;
@@ -688,9 +688,9 @@
// HOST (PROXMOX BOX) HEALTH — slice 9, proxied from the host agent // HOST (PROXMOX BOX) HEALTH — slice 9, proxied from the host agent
// ============================================= // =============================================
function usageColorClass(pct) { function usageColorClass(pct) {
if (pct >= 85) return 'system-bar-red'; if (pct >= 85) return 'crit';
if (pct >= 70) return 'system-bar-yellow'; if (pct >= 70) return 'warn';
return 'system-bar-green'; return 'nominal';
} }
function fmtBytesGB(bytes) { function fmtBytesGB(bytes) {
@@ -745,21 +745,21 @@
var bars = document.getElementById('host-storage-bars'); var bars = document.getElementById('host-storage-bars');
var targets = Array.isArray(d.storage_targets) ? d.storage_targets : []; var targets = Array.isArray(d.storage_targets) ? d.storage_targets : [];
if (!targets.length) { if (!targets.length) {
bars.innerHTML = '<div class="sysinfo-value" style="color:var(--text-muted)">Nincs tároló adat.</div>'; bars.innerHTML = '<div class="sysinfo-value" style="color:var(--text-3)">Nincs tároló adat.</div>';
} else { } else {
var html = ''; var html = '';
targets.forEach(function(t) { targets.forEach(function(t) {
// Friendly label (server-supplied) with the raw PVE storage id shown muted for clarity. // Friendly label (server-supplied) with the raw PVE storage id shown muted for clarity.
var friendly = escapeHtml(t.label || t.name || ''); var friendly = escapeHtml(t.label || t.name || '');
var rawId = escapeHtml(t.name || ''); var rawId = escapeHtml(t.name || '');
var idHtml = rawId ? ' <span style="color:var(--text-muted);font-size:.72rem">(' + rawId + ')</span>' : ''; var idHtml = rawId ? ' <span style="color:var(--text-3);font-size:.72rem">(' + rawId + ')</span>' : '';
var label = friendly + idHtml; var label = friendly + idHtml;
var purposeHtml = t.purpose ? '<div class="storage-purpose" style="font-size:.72rem;opacity:.65;margin-top:.2rem">' + escapeHtml(t.purpose) + '</div>' : ''; var purposeHtml = t.purpose ? '<div class="storage-purpose" style="font-size:.72rem;opacity:.65;margin-top:.2rem">' + escapeHtml(t.purpose) + '</div>' : '';
if (t.state && t.state !== 'attached') { if (t.state && t.state !== 'attached') {
html += '<div class="storage-item storage-disconnected">' + html += '<div class="storage-item meter meter-empty storage-disconnected">' +
'<div class="storage-header"><span class="storage-label">' + label + '</span>' + '<div class="storage-header"><span class="storage-label">' + label + '</span>' +
'<span class="storage-value badge-error" style="font-size:.75rem">Nem elérhető</span></div>' + '<span class="storage-value"><span class="state-text-neutral">Nem elérhető</span></span></div>' +
'<div class="system-bar"><div class="system-bar-disconnected"></div></div>' + purposeHtml + '</div>'; '<div class="meter-track"></div>' + purposeHtml + '</div>';
return; return;
} }
var pct = (t.used_fraction != null ? t.used_fraction * 100 : 0); var pct = (t.used_fraction != null ? t.used_fraction * 100 : 0);
@@ -772,13 +772,13 @@
if (t.smart.temperature_c != null) extra.push(t.smart.temperature_c + ' °C'); if (t.smart.temperature_c != null) extra.push(t.smart.temperature_c + ' °C');
if (t.smart.percentage_used != null) extra.push('kopás: ' + t.smart.percentage_used + '%'); if (t.smart.percentage_used != null) extra.push('kopás: ' + t.smart.percentage_used + '%');
} }
var extraHtml = extra.length ? ' <span style="color:var(--text-muted);font-size:.75rem">· ' + escapeHtml(extra.join(' · ')) + '</span>' : ''; var extraHtml = extra.length ? ' <span style="color:var(--text-3);font-size:.75rem">· ' + escapeHtml(extra.join(' · ')) + '</span>' : '';
html += '<div class="storage-item">' + html += '<div class="storage-item meter ' + usageColorClass(pct) + '">' +
'<div class="storage-header">' + '<div class="storage-header">' +
'<span class="storage-label">' + label + extraHtml + '</span>' + '<span class="storage-label">' + label + extraHtml + '</span>' +
'<span class="storage-value">' + fmtBytesGB(t.used_bytes) + ' / ' + fmtBytesGB(t.total_bytes) + '<span class="storage-value">' + fmtBytesGB(t.used_bytes) + ' / ' + fmtBytesGB(t.total_bytes) +
' (' + Math.round(pct) + '%)</span></div>' + ' (' + Math.round(pct) + '%)</span></div>' +
'<div class="system-bar"><div class="system-bar-fill ' + usageColorClass(pct) + '<div class="meter-track"><div class="meter-fill' +
'" style="width:' + Math.min(100, pct).toFixed(1) + '%"></div></div>' + purposeHtml + '</div>'; '" style="width:' + Math.min(100, pct).toFixed(1) + '%"></div></div>' + purposeHtml + '</div>';
}); });
bars.innerHTML = html; bars.innerHTML = html;
@@ -242,8 +242,8 @@ pollUntilBack();
<span class="badge state-gray">Kiváltva</span> <span class="badge state-gray">Kiváltva</span>
{{else}} {{else}}
{{if .IsDefault}}<span class="badge state-green">Alapértelmezett</span>{{end}} {{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 .Schedulable}}<span class="badge" style="background:rgba(0,136,204,0.15);color:var(--blue-bright)">Aktív</span>{{else}}<span class="badge state-gray">Inaktív</span>{{end}}
{{if .IsUSB}}<span class="badge" style="background:rgba(255,165,0,0.15);color:var(--yellow)">USB</span>{{end}} {{if .IsUSB}}<span class="badge" style="background:rgba(255,165,0,0.15);color:var(--warn)">USB</span>{{end}}
{{if not .IsMounted}}<span class="badge badge-warn">Rendszermeghajtón</span>{{end}} {{if not .IsMounted}}<span class="badge badge-warn">Rendszermeghajtón</span>{{end}}
{{end}} {{end}}
</div> </div>
@@ -296,7 +296,7 @@ pollUntilBack();
{{end}} {{end}}
{{if .StoppedApps}} {{if .StoppedApps}}
<div class="storage-stopped-apps-info" id="storage-stopped-{{.Path}}"> <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> <span class="form-hint" style="color:var(--blue-bright)">Ú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> <button class="btn btn-xs btn-primary" onclick="storageRestartApps('{{.Path}}')" style="margin-left:.5rem">Alkalmazások indítása</button>
</div> </div>
{{end}} {{end}}
@@ -471,7 +471,7 @@ pollUntilBack();
} }
</script> </script>
<div id="migrate-progress" style="display:none;margin-top:1rem;padding:1rem;border:1px solid var(--accent);border-radius:6px;background:rgba(0,136,204,0.06)"> <div id="migrate-progress" style="display:none;margin-top:1rem;padding:1rem;border:1px solid var(--blue);border-radius:6px;background:rgba(0,136,204,0.06)">
<strong>Adatok áthelyezése</strong> <strong>Adatok áthelyezése</strong>
<div id="migrate-progress-body" style="margin-top:.5rem"></div> <div id="migrate-progress-body" style="margin-top:.5rem"></div>
</div> </div>
@@ -134,7 +134,7 @@ async function submitInit(ev){
// SYSTEM/BACKUP (shouldn't reach here — filtered out — but surface the opsign if it does). // SYSTEM/BACKUP (shouldn't reach here — filtered out — but surface the opsign if it does).
if(res.status===409 && res.j.data && res.j.data.refused){ if(res.status===409 && res.j.data && res.j.data.refused){
out.innerHTML='<div class="alert alert-warning"><strong>Operátori aláírás szükséges.</strong> Ez a meghajtó védett (rendszer/biztonsági mentés).' out.innerHTML='<div class="alert alert-warning"><strong>Operátori aláírás szükséges.</strong> Ez a meghajtó védett (rendszer/biztonsági mentés).'
+(res.j.data.opsign?('<pre class="mono" style="white-space:pre-wrap;background:var(--bg-primary);padding:.75rem;border-radius:6px;margin-top:.5rem">'+esc(res.j.data.opsign)+'</pre>'):'')+'</div>'; +(res.j.data.opsign?('<pre class="mono" style="white-space:pre-wrap;background:var(--bg-0);padding:.75rem;border-radius:6px;margin-top:.5rem">'+esc(res.j.data.opsign)+'</pre>'):'')+'</div>';
btn.disabled=false; return false; btn.disabled=false; return false;
} }
finishInit(res.j, out); finishInit(res.j, out);
+22 -40
View File
@@ -854,60 +854,38 @@ select.form-control option { background: var(--bg-2); color: var(--text-1); }
color: var(--text-3); color: var(--text-3);
font-family: var(--font-data); font-family: var(--font-data);
} }
.memory-bar-stacked { /* Deploy-page memory meter two segments on the meter track:
width: 100%; committed = solid blue, new app = blue at 35% opacity (v2). */
height: 10px; .meter-track-stacked {
border-radius: var(--radius); height: 3px;
display: flex; display: flex;
overflow: hidden; overflow: visible;
position: relative; position: relative;
background: linear-gradient(to right, background: var(--line-soft);
var(--blue-dim) 0%, var(--blue-dim) 70%,
var(--warn-dim) 70%, var(--warn-dim) 85%,
var(--crit-dim) 85%, var(--crit-dim) 100%
);
} }
.memory-bar-stacked::before { .meter-track-stacked::before,
.meter-track-stacked::after {
content: ''; content: '';
position: absolute; position: absolute;
left: 70%; top: -2px;
top: -1px; bottom: -2px;
bottom: -1px; width: 1px;
width: 2px; background: var(--line);
background: var(--warn);
border-radius: 0;
opacity: 0.5;
z-index: 2;
}
.memory-bar-stacked::after {
content: '';
position: absolute;
left: 85%;
top: -1px;
bottom: -1px;
width: 2px;
background: var(--crit);
border-radius: 0;
opacity: 0.5;
z-index: 2; z-index: 2;
} }
.meter-track-stacked::before { left: 70%; }
.meter-track-stacked::after { left: 85%; }
.memory-bar-segment { .memory-bar-segment {
height: 100%; height: 100%;
position: relative; position: relative;
z-index: 1; z-index: 1;
transition: width 0.3s ease; transition: width 0.3s ease;
} }
.memory-bar-segment:not([style*="width:0%"]) {
min-width: 3px;
}
.memory-bar-committed { .memory-bar-committed {
background: var(--blue); background: var(--blue);
border-radius: 0;
} }
.memory-bar-new { .memory-bar-new {
background: var(--blue-dim); background: rgba(0,131,216,.35);
border-right: 2px solid var(--blue-bright);
border-radius: 0;
} }
.memory-bar-legend { .memory-bar-legend {
display: flex; display: flex;
@@ -920,7 +898,7 @@ select.form-control option { background: var(--bg-2); color: var(--text-1); }
align-items: center; align-items: center;
gap: 0.35rem; gap: 0.35rem;
font-size: .75rem; font-size: .75rem;
color: var(--text-2); color: var(--text-3);
white-space: nowrap; white-space: nowrap;
} }
.memory-legend-dot { .memory-legend-dot {
@@ -934,8 +912,7 @@ select.form-control option { background: var(--bg-2); color: var(--text-1); }
background: var(--blue); background: var(--blue);
} }
.memory-legend-new { .memory-legend-new {
background: var(--blue-dim); background: rgba(0,131,216,.35);
border: 1px solid var(--blue-bright);
} }
.memory-dist-bar { .memory-dist-bar {
width: 100%; width: 100%;
@@ -3252,3 +3229,8 @@ span.mono, .mono { font-family: var(--font-data); }
.list .row.state-run::before, .list .row.state-run::before,
.list .row.state-progress::before { background: var(--blue); } .list .row.state-progress::before { background: var(--blue); }
.list .row.state-warn::before { background: var(--warn); } .list .row.state-warn::before { background: var(--warn); }
/* Spinning progress icon (deploy steps, async tasks) */
.ico-spin { animation: ico-spin 1s linear infinite; }
@keyframes ico-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .ico-spin { animation: none; } }
@@ -11,7 +11,7 @@
<div class="monitor-card"> <div class="monitor-card">
{{with .Tier2}} {{with .Tier2}}
<p style="color:var(--text-muted);font-size:.9rem;margin-top:0"> <p style="color:var(--text-3);font-size:.9rem;margin-top:0">
A 2. mentés egy <strong>másik fizikai meghajtóra</strong> készít másolatot az alkalmazás A 2. mentés egy <strong>másik fizikai meghajtóra</strong> készít másolatot az alkalmazás
helyreállítási csomagjáról és adatairól. Ez az egyetlen off-drive védelem a böngészhető helyreállítási csomagjáról és adatairól. Ez az egyetlen off-drive védelem a böngészhető
felhasználói fájlokhoz (a teljes rendszermentés/PBS nem éri el ezeket). felhasználói fájlokhoz (a teljes rendszermentés/PBS nem éri el ezeket).
@@ -38,7 +38,7 @@
</div> </div>
<div class="sysinfo-row"> <div class="sysinfo-row">
<span class="sysinfo-label">Megjegyzés</span> <span class="sysinfo-label">Megjegyzés</span>
<span class="sysinfo-value" style="color:var(--text-muted)">{{.NoTargetReason}}</span> <span class="sysinfo-value" style="color:var(--text-3)">{{.NoTargetReason}}</span>
</div> </div>
{{else}} {{else}}
<div class="sysinfo-row"> <div class="sysinfo-row">
@@ -47,7 +47,7 @@
</div> </div>
<div class="sysinfo-row"> <div class="sysinfo-row">
<span class="sysinfo-label">Kiválasztás módja</span> <span class="sysinfo-label">Kiválasztás módja</span>
<span class="sysinfo-value" style="color:var(--text-muted)">{{if .Preferred}}kézi választás{{else}}automatikus{{end}} — {{.EffectiveDesc}}</span> <span class="sysinfo-value" style="color:var(--text-3)">{{if .Preferred}}kézi választás{{else}}automatikus{{end}} — {{.EffectiveDesc}}</span>
</div> </div>
{{end}} {{end}}
</div> </div>
@@ -77,7 +77,7 @@
{{end}} {{end}}
</select> </select>
{{if not .Alternatives}} {{if not .Alternatives}}
<div style="font-size:.8rem;color:var(--text-muted);margin-top:.4rem"> <div style="font-size:.8rem;color:var(--text-3);margin-top:.4rem">
Nincs másik adatmeghajtó — automatikus cél a belső SSD (csak DB/konfiguráció). Egy 2. Nincs másik adatmeghajtó — automatikus cél a belső SSD (csak DB/konfiguráció). Egy 2.
adatmeghajtó hozzáadásával a teljes adat is off-drive menthető. adatmeghajtó hozzáadásával a teljes adat is off-drive menthető.
</div> </div>