v0.308.0: controller batch 2026-10-12 — clear-address warning (R-922), secret sweep over every page (R-255), read_failed (R-266), no NVMe heat band (R-333), share globe (R-577), Hungarian dates and numbers (R-562), lost-drive option pinned (R-521)
Operator rulings 2026-10-12, 09 §3 decisions 196, 204-207, 223, 227. MinAgent 0.131.0 unchanged. R-255's sweep found one leak, fixed here: an optional-config secret_input was rendered into its settings page; it is now revealed on demand. Hungarian parity fixtures re-captured once for R-562. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -148,9 +148,9 @@ async function loadEstimate() {
|
||||
// Task 4: the class-scoped selection UI (classified apps only; legacy apps see the plain estimate).
|
||||
var lastEst = null;
|
||||
function humanBytes(b) {
|
||||
if (b >= 1073741824) return (b/1073741824).toFixed(1) + ' GB';
|
||||
if (b >= 1048576) return (b/1048576).toFixed(1) + ' MB';
|
||||
if (b >= 1024) return (b/1024).toFixed(1) + ' KB';
|
||||
if (b >= 1073741824) return fhNum(b/1073741824, 1) + ' GB';
|
||||
if (b >= 1048576) return fhNum(b/1048576, 1) + ' MB';
|
||||
if (b >= 1024) return fhNum(b/1024, 1) + ' KB';
|
||||
return b + ' B';
|
||||
}
|
||||
function renderClassSelect(est) {
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
{{range .Bundles}}
|
||||
<tr>
|
||||
<td><strong>{{if .DisplayName}}{{.DisplayName}}{{else}}{{.AppName}}{{end}}</strong></td>
|
||||
<td>{{.ExportedAt}}</td>
|
||||
<td>{{.SizeHuman}}</td>
|
||||
<td>{{fmtTimeStr .ExportedAt}}</td>
|
||||
<td>{{numText .SizeHuman}}</td>
|
||||
<td>{{if .DriveLabel}}{{.DriveLabel}}{{else}}{{.DrivePath}}{{end}}</td>
|
||||
<td>{{if .Encrypted}}🔒{{end}}</td>
|
||||
<td><button class="btn btn-sm btn-outline" onclick="showPreview('{{.Path}}', {{.Encrypted}})">{{T "app_import.reszletek"}}</button></td>
|
||||
@@ -195,7 +195,7 @@ function showManifest(m) {
|
||||
|
||||
document.getElementById('previewTitle').textContent = (m.display_name || m.app_name) + ' {{T "app_import.csomag_reszletei_2"}}';
|
||||
document.getElementById('mfName').textContent = m.display_name || m.app_name;
|
||||
document.getElementById('mfDate').textContent = m.exported_at ? new Date(m.exported_at).toLocaleString('hu-HU') : '-';
|
||||
document.getElementById('mfDate').textContent = m.exported_at ? fhDateTime(new Date(m.exported_at)) : '-';
|
||||
document.getElementById('mfVersion').textContent = m.controller_version || '-';
|
||||
|
||||
if (m.has_database && m.db_type) {
|
||||
@@ -212,9 +212,9 @@ function showManifest(m) {
|
||||
|
||||
// Format size
|
||||
var bytes = m.total_size_bytes || 0;
|
||||
var sizeStr = bytes > 1073741824 ? (bytes / 1073741824).toFixed(1) + ' GB' :
|
||||
bytes > 1048576 ? (bytes / 1048576).toFixed(1) + ' MB' :
|
||||
bytes > 1024 ? (bytes / 1024).toFixed(1) + ' KB' : bytes + ' B';
|
||||
var sizeStr = bytes > 1073741824 ? fhNum(bytes / 1073741824, 1) + ' GB' :
|
||||
bytes > 1048576 ? fhNum(bytes / 1048576, 1) + ' MB' :
|
||||
bytes > 1024 ? fhNum(bytes / 1024, 1) + ' KB' : bytes + ' B';
|
||||
document.getElementById('mfSize').textContent = sizeStr;
|
||||
|
||||
// Check if app already exists — show overwrite warning
|
||||
@@ -317,7 +317,7 @@ function fabPick() {
|
||||
}
|
||||
|
||||
function fabGB(b) {
|
||||
return (b / 1073741824).toFixed(2);
|
||||
return fhNum(b / 1073741824, 2);
|
||||
}
|
||||
|
||||
function fabProgress(done, total) {
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
<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>
|
||||
<script>
|
||||
function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; }
|
||||
function appMigFmtGB(b){ return fhNum(Number(b||0)/1e9, 1)+' GB'; }
|
||||
function appMigRender(job){
|
||||
var names={stop:'{{T "app_info.leallitas"}}',copy:'{{T "app_info.adatok_masolasa"}}',verify:'{{T "app_info.ellenorzes"}}',flip:'{{T "app_info.ujratelepites"}}',redeploy:'{{T "app_info.ujratelepites"}}',cleanup:'{{T "app_info.regi_adatok_torlese"}}'};
|
||||
var s=names[job.phase]||job.phase;
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
<span class="tag tag-warn">{{T "backups_apps.meghajto_levalasztva"}}</span>
|
||||
{{else if .HasHDDData}}
|
||||
{{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">{{numText .HDDSizeHuman}}</span>
|
||||
{{else if .HasVolumeData}}
|
||||
<span class="meta-badge">{{T "backups_apps.konfig"}}{{if .HasDB}} + DB{{end}} {{T "backups_apps.adatok"}}</span>
|
||||
{{else}}
|
||||
@@ -277,7 +277,7 @@
|
||||
<span class="{{if eq .Tier2LastStatus "error"}}text-error{{else}}text-muted{{end}}">{{.Tier2StatusBadge}}</span>
|
||||
</span>
|
||||
{{end}}
|
||||
{{if .Tier2SizeHuman}}<span class="tier-size">{{.Tier2SizeHuman}}</span>{{end}}
|
||||
{{if .Tier2SizeHuman}}<span class="tier-size">{{numText .Tier2SizeHuman}}</span>{{end}}
|
||||
{{if .Tier2LastWarning}}<span class="layer-reason" style="color:var(--warn);opacity:.9">{{.Tier2LastWarning}}</span>{{end}}
|
||||
{{/* R-403: a run that PRESERVED the copy's package instead of refreshing it must
|
||||
not render as a plain fresh copy. The status line above is about the RUN;
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
<div class="stat-label">{{T "backups_remote.utolso_tavoli_mentes"}}{{if .Offbox.LastRun}}<br><span class="relative-time">{{timeAgoStr .Offbox.LastRun}}</span>{{end}}</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" style="font-size:1.15rem">{{if and .Offbox.StatsKnown .Offbox.RepoSizeHuman}}{{.Offbox.RepoSizeHuman}}{{else}}–{{end}}</div>
|
||||
<div class="stat-value" style="font-size:1.15rem">{{if and .Offbox.StatsKnown .Offbox.RepoSizeHuman}}{{numText .Offbox.RepoSizeHuman}}{{else}}–{{end}}</div>
|
||||
<div class="stat-label">{{T "backups_remote.tarolo_meret"}} {{if .Offbox.StatsKnown}}{{T "backups_remote.pillanatkep"}}{{else}}{{T "backups_remote.a_pillanatkepek_szama_meg_ismeretlen"}}{{end}}</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
@@ -60,7 +60,7 @@
|
||||
picture of emptiness, and a picture is a claim. -->
|
||||
<!-- SLICE 4: soft-quota usage bar (shared model; quota_gb from the hub descriptor). -->
|
||||
<div id="offbox-quota-bar" style="max-width:560px;margin:.5rem 0">
|
||||
<div class="stat-label" style="margin-bottom:.25rem">{{T "backups_remote.tarhelykeret"}} {{if .Offbox.StatsKnown}}{{if .Offbox.RepoSizeHuman}}{{.Offbox.RepoSizeHuman}}{{else}}0{{end}} / {{.Offbox.QuotaGB}} GB ({{.OffboxQuotaPct}}%){{else}}{{T "backups_remote.meg_nem_tudjuk_mennyi_van"}}{{end}}</div>
|
||||
<div class="stat-label" style="margin-bottom:.25rem">{{T "backups_remote.tarhelykeret"}} {{if .Offbox.StatsKnown}}{{if .Offbox.RepoSizeHuman}}{{numText .Offbox.RepoSizeHuman}}{{else}}0{{end}} / {{.Offbox.QuotaGB}} GB ({{.OffboxQuotaPct}}%){{else}}{{T "backups_remote.meg_nem_tudjuk_mennyi_van"}}{{end}}</div>
|
||||
{{if .Offbox.StatsKnown}}<div style="background:var(--border,#334);border-radius:4px;height:8px;overflow:hidden">
|
||||
<div style="height:8px;border-radius:4px;width:{{.OffboxQuotaPct}}%;background:{{if ge .OffboxQuotaPct 100}}var(--crit,#e5484d){{else if ge .OffboxQuotaPct 80}}var(--warn,#f5a524){{else}}var(--ok,#30a46c){{end}}"></div>
|
||||
</div>{{end}}
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
<p class="form-hint" style="margin-bottom:.75rem">{{T "backups_restore.ezek_a_visszaallitott_masolatok_helyet"}}</p>
|
||||
<div class="app-row-list">
|
||||
{{range .OffsiteRestoreCopies}}
|
||||
{{template "app_list_row" dict "Slug" .Stack "Name" .Stack "Secondary" (printf "%s · %s · %s" .SizeHuman (.Created.Format "2006. 01. 02. 15:04") .Path)}}
|
||||
{{template "app_list_row" dict "Slug" .Stack "Name" .Stack "Secondary" (printf "%s · %s · %s" (numText .SizeHuman) (fmtTime .Created) .Path)}}
|
||||
<form method="POST" action="/backup/offbox/verify-copy/delete" style="display:inline">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="stack" value="{{.Stack}}">
|
||||
@@ -299,7 +299,7 @@ var huDays = ['{{T "backups_restore.vasarnap"}}', '{{T "backups_restore.hetfo"}}
|
||||
function formatSnapshot(s) {
|
||||
var t = new Date(s.time);
|
||||
var pad = function(n) { return n < 10 ? '0' + n : '' + n; };
|
||||
var label = t.getFullYear() + '-' + pad(t.getMonth()+1) + '-' + pad(t.getDate()) +
|
||||
var label = fhIsoDate(t.getFullYear() + '-' + pad(t.getMonth()+1) + '-' + pad(t.getDate())) +
|
||||
' ' + huDays[t.getDay()] + ' ' + pad(t.getHours()) + ':' + pad(t.getMinutes()) +
|
||||
' (' + s.short_id + ')';
|
||||
var tierLabel = s.tier === 2 ? '{{T "backups_restore.2_szint"}}' : '{{T "backups_restore.1_szint"}}';
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
{{if .StorageInfo.DataSizeHuman}}
|
||||
<div class="settings-row">
|
||||
<span class="settings-label">{{T "deploy.adatmeret"}}</span>
|
||||
<span class="settings-value mono">{{.StorageInfo.DataSizeHuman}}</span>
|
||||
<span class="settings-value mono">{{numText .StorageInfo.DataSizeHuman}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .StorageInfo.FreeHuman}}
|
||||
@@ -91,7 +91,7 @@
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<span class="settings-label">{{T "deploy.meret"}}</span>
|
||||
<span class="settings-value mono">{{.SizeHuman}}</span>
|
||||
<span class="settings-value mono">{{numText .SizeHuman}}</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<span class="settings-label">{{T "deploy.mappak"}}</span>
|
||||
@@ -328,13 +328,26 @@
|
||||
<label for="opt-{{.EnvVar}}">{{.Label}}</label>
|
||||
{{if .HelpText}}<p class="config-field-help">{{.HelpText}}</p>{{end}}
|
||||
{{if .HelpURL}}<p class="config-field-help"><a href="{{.HelpURL}}" target="_blank">{{T "deploy.regisztracios_utmutato"}}</a></p>{{end}}
|
||||
<input type="{{if eq .Type "secret_input"}}password{{else}}text{{end}}"
|
||||
{{if eq .Type "secret_input"}}
|
||||
{{/* R-255 (v0.308.0): a stored secret is NOT in the page; it is fetched on demand like the
|
||||
auto-generated ones (R-254). A blank field that was never revealed is not sent, so
|
||||
saving other fields keeps it. */}}
|
||||
{{$set := index $.CurrentValues .EnvVar}}
|
||||
<div class="input-with-button">
|
||||
<input type="password" id="opt-{{.EnvVar}}" name="{{.EnvVar}}" class="config-input"
|
||||
value="" placeholder="{{if $set}}••••••••••••{{else}}{{.Label}}{{end}}"
|
||||
{{if $set}}data-secret-set="1" {{end}}autocomplete="off">
|
||||
{{if $set}}<button type="button" class="btn btn-sm btn-outline" onclick="revealAutoField('{{$.Stack.Name}}','{{.EnvVar}}', this, 'opt-{{.EnvVar}}')">{{T "deploy.megjelenites"}}</button>{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<input type="text"
|
||||
id="opt-{{.EnvVar}}"
|
||||
name="{{.EnvVar}}"
|
||||
class="config-input"
|
||||
value="{{index $.CurrentValues .EnvVar}}"
|
||||
placeholder="{{.Label}}"
|
||||
autocomplete="off">
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
@@ -357,6 +370,8 @@
|
||||
|
||||
var values = {};
|
||||
inputs.forEach(function(input) {
|
||||
// R-255: a stored secret the household never revealed and left blank stays as it is.
|
||||
if (input.dataset.secretSet === '1' && input.dataset.revealed !== '1' && input.value === '') return;
|
||||
values[input.name] = input.value;
|
||||
});
|
||||
|
||||
@@ -836,15 +851,17 @@ function revealPasswordField(stackName, envVar, btn) {
|
||||
if (!j.ok) { showAlert(j.error || '{{T "deploy.a_lekeres_nem_sikerult"}}'); return; }
|
||||
el.value = j.data.value;
|
||||
el.type = 'text';
|
||||
el.dataset.revealed = '1';
|
||||
btn.dataset.shown = '1';
|
||||
}).catch(function () { btn.disabled = false; showAlert('{{T "deploy.a_lekeres_nem_sikerult"}}'); });
|
||||
}
|
||||
function revealAutoField(stackName, envVar, btn) {
|
||||
var el = document.getElementById('auto-field-' + envVar);
|
||||
function revealAutoField(stackName, envVar, btn, elId) {
|
||||
var el = document.getElementById(elId || ('auto-field-' + envVar));
|
||||
if (!el) return;
|
||||
if (btn.dataset.shown === '1') {
|
||||
el.value = '';
|
||||
el.type = 'password';
|
||||
el.dataset.revealed = '';
|
||||
btn.dataset.shown = '';
|
||||
btn.textContent = '{{T "deploy.megjelenites"}}';
|
||||
return;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
<input type="password" name="password" placeholder="{{T "launcher_share_password.jelszo"}}" autocomplete="current-password" autofocus>
|
||||
<button type="submit" class="btn btn-primary">{{T "launcher_share_password.belepes"}}</button>
|
||||
</form>
|
||||
{{/* R-577 (decision 207): the guest picks their own language; the globe posts to /lang (their cookie). */}}
|
||||
<div class="shell-lang">{{template "lang_globe" .}}</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
<p>{{T "launcher_shared.jelenleg_nincs_elerheto_alkalmazas"}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{/* R-577 (decision 207): the guest picks their own language; the globe posts to /lang (their cookie). */}}
|
||||
<div class="shell-lang">{{template "lang_globe" .}}</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
// mobile off-canvas drawer engages only when the drawer JS below can actually open it.
|
||||
document.documentElement.className=document.documentElement.className.replace('no-js','js');
|
||||
function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};}
|
||||
{{/* R-562 (decision 223): numbers and dates in the page's language. The browser twin of internal/i18n
|
||||
format.go: Hungarian 2026. 10. 10. 15:04 and a decimal comma, English 2026-10-10 15:04 and a point.
|
||||
sv-SE is used only because it prints the ISO-like order; the Hungarian form is built from it. */ -}}
|
||||
var fhEN=document.documentElement.lang==='en';
|
||||
function fhNum(v,prec){var s=Number(v).toFixed(prec);return fhEN?s:s.replace('.',',');}
|
||||
function fhIsoDate(iso){return fhEN?iso:iso.replace(/^(\d{4})-(\d{2})-(\d{2})/,'$1. $2. $3.');}
|
||||
function fhDateTime(d){return fhIsoDate(d.toLocaleString('sv-SE',{timeZone:'Europe/Budapest',year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'}));}
|
||||
function fhMonthDay(d){var s=d.toLocaleDateString('sv-SE',{timeZone:'Europe/Budapest',month:'2-digit',day:'2-digit'});return fhEN?s:s.replace(/^(\d{2})-(\d{2})$/,'$1. $2.');}
|
||||
// Inline two-step confirm (drill F-11): the trigger swaps IN PLACE to "<question> Igen/Mégse".
|
||||
// Never native confirm() — it is an OS-modal that blocks browser automation — and no overlay:
|
||||
// this is the LIGHT pattern for one-click consequential actions. Form buttons opt in with
|
||||
|
||||
@@ -363,7 +363,7 @@
|
||||
if (isNaN(ts)) return '';
|
||||
// ts should already be in ms (linear axis stores ms values)
|
||||
var d = new Date(ts);
|
||||
return d.toLocaleString('hu-HU', {timeZone: budaTZ, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit'});
|
||||
return fhDateTime(d);
|
||||
}
|
||||
|
||||
function formatTimeLabel(ts) {
|
||||
@@ -372,7 +372,7 @@
|
||||
if (isNaN(ts)) return '';
|
||||
var d = new Date(ts);
|
||||
if (currentTickFormat === 'date') {
|
||||
return d.toLocaleDateString('hu-HU', {timeZone: budaTZ, month: '2-digit', day: '2-digit'});
|
||||
return fhMonthDay(d);
|
||||
}
|
||||
return d.toLocaleTimeString('hu-HU', {timeZone: budaTZ, hour: '2-digit', minute: '2-digit'});
|
||||
}
|
||||
@@ -586,7 +586,7 @@
|
||||
var osMB = Math.max(0, usedMB - appTotal);
|
||||
var freeMB = Math.max(0, totalMB - usedMB);
|
||||
|
||||
function fmtMB(mb) { return mb >= 1024 ? (mb/1024).toFixed(1) + ' GB' : Math.round(mb) + ' MB'; }
|
||||
function fmtMB(mb) { return mb >= 1024 ? fhNum(mb/1024, 1) + ' GB' : Math.round(mb) + ' MB'; }
|
||||
|
||||
header.textContent = '{{T "monitoring.hasznalt"}} ' + fmtMB(usedMB) + ' / ' + fmtMB(totalMB) + ' (' + Math.round(usedMB/totalMB*100) + '%)';
|
||||
|
||||
@@ -696,9 +696,9 @@
|
||||
function fmtBytesGB(bytes) {
|
||||
if (!bytes || bytes <= 0) return '0 GB';
|
||||
var gb = bytes / 1073741824;
|
||||
if (gb >= 1024) return (gb / 1024).toFixed(2) + ' TB';
|
||||
if (gb >= 1024) return fhNum(gb / 1024, 2) + ' TB';
|
||||
if (gb >= 100) return Math.round(gb) + ' GB';
|
||||
return gb.toFixed(1) + ' GB';
|
||||
return fhNum(gb, 1) + ' GB';
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
@@ -720,7 +720,7 @@
|
||||
|
||||
// CPU % + load average
|
||||
document.getElementById('host-cpu').textContent =
|
||||
(h.cpu_percent != null ? h.cpu_percent.toFixed(1) : '–') + '%';
|
||||
(h.cpu_percent != null ? fhNum(h.cpu_percent, 1) : '–') + '%';
|
||||
var load = Array.isArray(h.loadavg) ? h.loadavg.join(' ') : '–';
|
||||
document.getElementById('host-load').textContent = load || '–';
|
||||
|
||||
@@ -824,7 +824,7 @@
|
||||
}
|
||||
if (d.boot_time) {
|
||||
var bt = new Date(d.boot_time);
|
||||
document.getElementById('sysinfo-boot').textContent = bt.toLocaleString('hu-HU', {timeZone: budaTZ, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit'});
|
||||
document.getElementById('sysinfo-boot').textContent = fhDateTime(bt);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('Failed to load sysinfo:', e);
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
{{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}}
|
||||
{{.CSRFField}}{{if .NotificationClearConfirm}}
|
||||
<div class="alert alert-warning" id="notification-clear-warning">
|
||||
<p>{{T "settings_notifications.clear_warning"}}</p>
|
||||
<button type="submit" name="confirm_clear" value="1" class="btn btn-danger">{{T "settings_notifications.clear_confirm"}}</button>
|
||||
</div>{{end}}
|
||||
<div class="form-group">
|
||||
<label for="notification_email">{{T "settings_notifications.e_mail_cim"}}</label>
|
||||
<input type="email" id="notification_email" name="notification_email"
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
<strong>{{T "sharing.2_mentes_masik_meghajto"}}</strong>
|
||||
{{if .SharesTier2Status}}
|
||||
{{if eq .SharesTier2Status "ok"}}
|
||||
{{T "sharing.rendben"}}{{if .SharesTier2LastRun}} — {{.SharesTier2LastRun}}{{end}}
|
||||
{{T "sharing.rendben"}}{{if .SharesTier2LastRun}} — {{fmtTimeStr .SharesTier2LastRun}}{{end}}
|
||||
{{if .SharesTier2Warning}}<span class="badge badge-warn">{{.SharesTier2Warning}}</span>{{end}}
|
||||
{{else if eq .SharesTier2Status "no_target"}}
|
||||
<span class="badge badge-warn">{{T "sharing.nincs_cel"}}</span>
|
||||
@@ -187,7 +187,7 @@
|
||||
<strong>{{T "sharing.tavoli_mentes_felho"}}</strong>
|
||||
{{if .SharesOffsiteStatus}}
|
||||
{{if eq .SharesOffsiteStatus "ok"}}
|
||||
{{T "sharing.rendben_megosztas"}}{{if .SharesOffsiteLastRun}}, {{.SharesOffsiteLastRun}}{{end}}
|
||||
{{T "sharing.rendben_megosztas"}}{{if .SharesOffsiteLastRun}}, {{fmtTimeStr .SharesOffsiteLastRun}}{{end}}
|
||||
{{else if eq .SharesOffsiteStatus "blocked"}}
|
||||
<span class="badge badge-warn">{{T "sharing.a_tarhelykeret_miatt_csak_a"}}</span>
|
||||
{{else if eq .SharesOffsiteStatus "skipped"}}
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
{{if .DiskInfo}}
|
||||
<div class="storage-path-disk">
|
||||
<div class="system-info-header">
|
||||
<span class="system-info-value">{{.DiskInfo.UsedHuman}} / {{.DiskInfo.TotalHuman}}</span>
|
||||
<span class="system-info-value">{{numText .DiskInfo.UsedHuman}} / {{numText .DiskInfo.TotalHuman}}</span>
|
||||
</div>
|
||||
<div class="meter {{if ge .DiskInfo.UsedPercent 90.0}}crit{{else if ge .DiskInfo.UsedPercent 70.0}}warn{{else}}nominal{{end}}">
|
||||
<div class="meter-track">
|
||||
@@ -102,7 +102,7 @@
|
||||
{{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}}
|
||||
{{if .SizeHuman}}<span class="mono form-hint">{{numText .SizeHuman}}</span>{{end}}
|
||||
<span class="btn btn-xs btn-outline" style="opacity:.45;cursor:not-allowed" title="{{T "storage.hamarosan"}}"><svg class="ico ico-sm"><use href="#i-upload"/></svg> {{T "storage.mozgatas"}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -181,7 +181,7 @@
|
||||
</div>
|
||||
<script>
|
||||
// Shared migration progress (used by both migrate-all here and per-app migrate on the app page).
|
||||
function migFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; }
|
||||
function migFmtGB(b){ return fhNum(Number(b||0)/1e9, 1)+' GB'; }
|
||||
function migRender(job){
|
||||
var names={stop:'{{T "storage.alkalmazasok_leallitasa"}}',copy:'{{T "storage.adatok_masolasa"}}',verify:'{{T "storage.ellenorzes"}}',flip:'{{T "storage.ujratelepites"}}',redeploy:'{{T "storage.ujratelepites"}}',cleanup:'{{T "storage.forras_torlese"}}'};
|
||||
var s=names[job.phase]||job.phase;
|
||||
@@ -247,7 +247,7 @@
|
||||
window.__registeredPaths=[{{range .StoragePaths}}{{if .Path}}"{{.Path}}",{{end}}{{end}}];
|
||||
(function(){
|
||||
function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g,function(c){return {'&':'&','<':'<','>':'>','"':'"'}[c];}); }
|
||||
function hum(b){ if(!b||b<=0) return ''; var u=['B','KB','MB','GB','TB'],i=0,v=b; while(v>=1024&&i<u.length-1){v/=1024;i++;} return (v>=10||i===0?Math.round(v):v.toFixed(1))+' '+u[i]; }
|
||||
function hum(b){ if(!b||b<=0) return ''; var u=['B','KB','MB','GB','TB'],i=0,v=b; while(v>=1024&&i<u.length-1){v/=1024;i++;} return (v>=10||i===0?Math.round(v):fhNum(v, 1))+' '+u[i]; }
|
||||
function usageColorClass(p){ if(p>=85) return 'crit'; if(p>=70) return 'warn'; return 'nominal'; }
|
||||
var LOCK_ICO='<svg class="ico ico-sm"><use href="#i-lock"/></svg>';
|
||||
function classTag(d){
|
||||
|
||||
@@ -58,7 +58,7 @@ function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g,function(c){retur
|
||||
function fmtSize(b){
|
||||
if(!b) return '';
|
||||
var g=b/1073741824;
|
||||
return g>=1 ? g.toFixed(g>=10?0:1)+' GB' : Math.round(b/1048576)+' MB';
|
||||
return g>=1 ? fhNum(g, g>=10?0:1)+' GB' : Math.round(b/1048576)+' MB';
|
||||
}
|
||||
|
||||
async function loadDisks(){
|
||||
|
||||
@@ -69,7 +69,7 @@ function dataBadge(d){
|
||||
function fmtSize(b){
|
||||
if(!b) return '';
|
||||
var g=b/1073741824;
|
||||
return g>=1 ? g.toFixed(g>=10?0:1)+' GB' : Math.round(b/1048576)+' MB';
|
||||
return g>=1 ? fhNum(g, g>=10?0:1)+' GB' : Math.round(b/1048576)+' MB';
|
||||
}
|
||||
|
||||
async function loadDisks(){
|
||||
|
||||
Reference in New Issue
Block a user