Add Backups card

This commit is contained in:
Josh Holtrop 2026-04-20 22:39:45 -04:00
parent ca45a90930
commit 4b74bc01a7
2 changed files with 60 additions and 3 deletions

View File

@ -213,6 +213,18 @@ def read_server_info
info info
end end
def read_backups
paths = [
["rsync", "/backup/last-backup"],
["gmail", "/backup/gmail/new"]
]
entries = paths.map do |label, path|
mtime = (File.mtime(path).to_i rescue nil)
{ "label" => label, "path" => path, "mtime" => mtime }
end
{ "type" => "backup", "entries" => entries }
end
def read_mail def read_mail
users = [] users = []
@ -308,6 +320,7 @@ def handle_info(conn)
info << read_server_info info << read_server_info
info << read_mail info << read_mail
info << read_backups
if (ups = read_ups) if (ups = read_ups)
info << ups info << ups

View File

@ -7,6 +7,14 @@ require "json"
require "securerandom" require "securerandom"
require "socket" require "socket"
def human_age(seconds)
return "unknown" if seconds.nil?
return "just now" if seconds < 60
return "#{seconds / 60} min ago" if seconds < 3600
return "#{seconds / 3600} h ago" if seconds < 86400
"#{seconds / 86400} d ago"
end
def human_capacity(bytes) def human_capacity(bytes)
units = ["B", "KB", "MB", "GB", "TB", "PB"] units = ["B", "KB", "MB", "GB", "TB", "PB"]
i = 0 i = 0
@ -371,8 +379,10 @@ if cgi.params.key?("content")
upses = info.select { |e| e["type"] == "ups" } upses = info.select { |e| e["type"] == "ups" }
mail = info.find { |e| e["type"] == "mail" } mail = info.find { |e| e["type"] == "mail" }
mail_visible = mail && (mail["users"] || []).any? mail_visible = mail && (mail["users"] || []).any?
backup = info.find { |e| e["type"] == "backup" }
backup_visible = backup && (backup["entries"] || []).any?
if drives.any? || upses.any? || mail_visible if drives.any? || upses.any? || mail_visible || backup_visible
html << %(<div style="display:grid;grid-template-columns:2fr 1fr;gap:1rem;align-items:start;margin-top:1rem;">) html << %(<div style="display:grid;grid-template-columns:2fr 1fr;gap:1rem;align-items:start;margin-top:1rem;">)
end end
@ -414,7 +424,7 @@ if cgi.params.key?("content")
html << %(</div>) html << %(</div>)
end end
if upses.any? || mail_visible if upses.any? || mail_visible || backup_visible
html << %(<div>) html << %(<div>)
upses.each do |ups| upses.each do |ups|
@ -497,10 +507,44 @@ if cgi.params.key?("content")
html << %(</div>) html << %(</div>)
end end
if backup_visible
now = Time.now.to_i
classified = backup["entries"].map do |e|
mtime = e["mtime"]
cls = if mtime.nil?
"bad"
else
age = now - mtime
age < 2 * 86400 ? "ok" : age < 7 * 86400 ? "warn" : "bad"
end
[e, cls]
end
backup_card_cls = if classified.any? { |_, c| c == "bad" } then "bad"
elsif classified.any? { |_, c| c == "warn" } then "warn"
else "ok"
end
html << %(<div class="section-label">Backups</div>)
html << %(<div class="card #{backup_card_cls}"><div class="card-header"><span class="card-title">Backups</span></div><div class="subitems">)
classified.each do |e, cls|
age_label = e["mtime"] ? human_age(now - e["mtime"]) : "missing"
html << <<~HTML
<div class="subitem">
<div class="subitem-left"><div class="dot #{cls}"></div><span class="subitem-name">#{CGI.escapeHTML(e["label"].to_s)}</span></div>
<span class="subitem-value #{cls}">#{CGI.escapeHTML(age_label)}</span>
</div>
HTML
end
html << %(</div></div>)
end
html << %(</div>) html << %(</div>)
end end
if drives.any? || upses.any? || mail_visible if drives.any? || upses.any? || mail_visible || backup_visible
html << %(</div>) html << %(</div>)
end end