Newer
Older
<% local view, viewlibrary, page_info, session = ... %>
<% htmlviewfunctions = require("htmlviewfunctions") %>
<% html = require("acf.html") %>
<% json = require("json") %>
<% local sys = viewlibrary.dispatch_component("alpine-baselayout/health/system", nil, true) %>
<% local proc = viewlibrary.dispatch_component("alpine-baselayout/health/proc", nil, true) %>
<% local api = viewlibrary.dispatch_component("alpine-baselayout/health/api", nil, true) %>
<% local disk = viewlibrary.dispatch_component("alpine-baselayout/health/storage", nil, true) %>
<% local net = viewlibrary.dispatch_component("alpine-baselayout/health/network", nil, true) %>
<% local netstats = viewlibrary.dispatch_component("alpine-baselayout/health/networkstats", nil, true) %>
<%
-- PROTECT HOSTNAME WITH SESSION
local hostname = ""
if session.userinfo and session.userinfo.userid and viewlibrary and viewlibrary.dispatch_component then
local result = viewlibrary.dispatch_component("alpine-baselayout/hostname/read", nil, true)
if result and result.value then
hostname = result.value
end
local check_sysver = string.match(sys.value.version.value, "%d+.%d+.%d+") -- Check Local System Version
local major_sysver = string.match(check_sysver, "%d+") -- Parse Major for Upgrade
local minor_sysver = string.gsub(string.match(check_sysver, "%p%d+"), "%D", "") -- Parse Minor for Update
local patch_sysver = string.gsub(string.match(check_sysver, ".[^.]*$"), "%D", "") -- Parse Patch for Update
-- CHECK DIST RELEASE VERSION
local check_distver = (sys.value.alpinever.value) -- Get All Versions from Alpine Linux Official Website
local actual_distver = string.match(string.match(check_distver, "version\":\"%d+.%d+.%d+"), "%d+.%d+.%d+") -- Get Last Version
local major_distver = string.match(actual_distver, "%d+") -- Parse Major for Upgrade
local minor_distver = string.gsub(string.match(actual_distver, "%p%d+"), "%D", "") -- Parse Minor for Update
local patch_distver = string.gsub(string.match(actual_distver, ".[^.]*$"), "%D", "") -- Parse Patch for Fix
if major_sysver == major_distver and minor_sysver == minor_distver and patch_sysver == patch_distver then
blockcolor = "<div class='data-block data-system system-uptodate'>"
chkres = "<a id='alpine-version-link' class='version-link version-ok' href='https://www.alpinelinux.org/releases/#content' title='🟩 Up to Date' target='_blank'><span class='version-check'>Up To Date | Alpine <span class='version-number-uptodate'>" .. check_sysver .. "</span></span></a>"
blockcolor = "<div class='data-block data-system system-update'>"
chkres = "<a id='alpine-version-link' class='version-link version-update' href='https://www.alpinelinux.org/releases/#content' title='🟧 Update Needed' target='_blank'><span class='version-check'>Update Needed | Alpine <span class='version-number-update'>" .. check_sysver .. "</span></span></a>"
kernres = "<i class='fa-solid fa-exclamation icon-kernel-warn'></i>"
blockcolor = "<div class='data-block data-system system-upgrade'>"
chkres = "<a id='alpine-version-link' class='version-link version-upgrade' href='https://www.alpinelinux.org/releases/#content' title='🟥 Upgrade Needed' target='_blank'><span class='version-check'>Upgrade Required | Alpine <span class='version-number-upgrade'>" .. check_sysver .. "</span></span></a>"
kernres = "<i class='fa-solid fa-xmark icon-kernel-err'></i>"
local get_verchanges = string.match(sys.value.alpineposts.value, "[%p?%d?.%d?.%d?]+%p?".. actual_distver .."[%p?%d?.%d?.%d?]+")
-- FORMAT UPTIME
local up_time = math.floor(string.match(sys.value.uptime.value, "[%d]+"))
local up_centuries = math.floor((up_time / (3600*24) / 365) / 100)
local up_years = math.floor((up_time / (3600*24) / 365) % 100)
local up_mounths = math.floor((((up_time / (3600 * 24)) % 365) % 365) / 30)
local up_days = math.floor((((up_time / (3600 * 24)) % 365) % 365) % 30)
local up_hours = string.format("%02d", math.floor((up_time % (3600 * 24)) / 3600))
local up_minutes = string.format("%02d", math.floor(((up_time % (3600 * 24)) % 3600) / 60))
local up_seconds = string.format("%02d", math.floor(((up_time % (3600 * 24)) % 3600) % 60))
local uptime = up_centuries .. " Centuries " .. up_years .. " Years " .. up_mounths .. " Mounths " .. up_days .. " Days " .. up_hours .. "h " .. up_minutes .. "m " .. up_seconds .. "s"
if up_centuries == 1 then
uptime = up_centuries .. " Century " .. up_years .. " Year " .. up_mounths .. " Mounths " .. up_days .. " Days " .. up_hours .. "h " .. up_minutes .. "m " .. up_seconds .. "s"
elseif up_centuries == 0 then
uptime = up_years .. " Years " .. up_mounths .. " Mounths " .. up_days .. " Days " .. up_hours .. "h " .. up_minutes .. "m " .. up_seconds .. "s"
end
if up_years == 1 then
uptime = up_years .. " Year " .. up_mounths .. " Mounths " .. up_days .. " Days " .. up_hours .. "h " .. up_minutes .. "m " .. up_seconds .. "s"
elseif up_years == 0 then
uptime = up_mounths .. " Mounths " .. up_days .. " Days " .. up_hours .. "h " .. up_minutes .. "m " .. up_seconds .. "s"
end
if up_mounths == 1 then
uptime = up_mounths .. " Mounth " .. up_days .. " Days " .. up_hours .. "h " .. up_minutes .. "m " .. up_seconds .. "s"
elseif up_years == 0 and up_mounths == 0 then
uptime = up_days .. " Days " .. up_hours .. "h " .. up_minutes .. "m " .. up_seconds .. "s"
end
if up_days == 1 then
uptime = up_days .. " Day " .. up_hours .. "h " .. up_minutes .. "m " .. up_seconds .. "s"
elseif up_years == 0 and up_mounths == 0 and up_days == 0 then
uptime = up_hours .. "h " .. up_minutes .. "m " .. up_seconds .. "s"
end
function oem_parse(str)
return (string.gsub(str, "To be filled by O.E.M." or "Not Specified", "Standard PC"))
end
return (string.gsub(str, "To be filled by O.E.M." or "Not Specified", "Unknow"))
local interfaces = {}
for intf in pairs(netstats.value) do table.insert(interfaces, intf) end
table.sort(interfaces)
-- FORMAT BYTES
function bytesToSize(bytes)
kilobyte = 1000;
megabyte = kilobyte * 1000;
gigabyte = megabyte * 1000;
terabyte = gigabyte * 1000;
if((bytes >= 0) and (bytes < kilobyte)) then
return math.floor(bytes) .. " Bytes";
elseif((bytes >= kilobyte) and (bytes < megabyte)) then
return math.floor( bytes / kilobyte) .. ' KB';
elseif((bytes >= megabyte) and (bytes < gigabyte)) then
return math.floor( bytes / megabyte) .. ' MB';
elseif((bytes >= gigabyte) and (bytes < terabyte)) then
return string.format("%.2f", bytes / gigabyte) .. ' GB';
elseif(bytes >= terabyte) then
return string.format("%.2f", bytes / terabyte) .. ' TB';
else
return math.floor(bytes) .. ' B';
end
end
-- FORMAT OCTETS
function blocksToSize(octets)
kilooctet = 1000;
megaoctet = kilooctet * 1000;
gigaoctet = megaoctet * 1000;
teraoctet = gigaoctet * 1000;
if((octets >= 0) and (octets < kilooctet)) then
return math.floor(octets) .. " Octets";
elseif((octets >= kilooctet) and (octets < megaoctet)) then
return math.floor( octets / kilooctet) .. ' Ko';
elseif((octets >= megaoctet) and (octets < gigaoctet)) then
return math.floor( octets / megaoctet) .. ' Mo';
elseif((octets >= gigaoctet) and (octets < teraoctet)) then
return string.format("%.2f", octets / gigaoctet) .. ' Go';
elseif(octets >= teraoctet) then
return string.format("%.2f", octets / teraoctet) .. ' To';
-- GET DISKS & PARTITIONS
%>
<% local header_level = htmlviewfunctions.displaysectionstart(cfe({label="Dashboard"}), page_info) %>
<!-- Dashboard App Block - LINE 1 -->
<div class="dashboard-main main-block">
<h4 class="dashboard-block-title dashboard-title-system">System</h4>
<a class="version-link version-external-link" href="https://www.alpinelinux.org/posts/Alpine<%= get_verchanges %>released.html#content" title="🔗 https://www.alpinelinux.org/posts/Alpine<%= get_verchanges %>released.html#content" target="_blank">Last Release : <%= actual_distver %></a><br>
<span class="kernel-ver">Kernel @ <%= sys.value.kernel.value %> | ACF - <%= sys.value.luaver.value %></span>
<!-- Dashboard Hardware Block - 2 -->
<div class="data-block data-hardware">
<h4 class="dashboard-block-title dashboard-title-hardware">Hardware</h4>
<% if string.find((proc.value.model.value), "Intel") then
elseif string.find((proc.value.model.value), "AMD") then
<p class="dashboard-infos">
<span class="data-title">Board</span>
-- EXEMPLE TO PARSE KNOW MOBO MODELS OR YOUR OWN ONE
if string.match(sys.value.boardVendor.value, "ASUSTeK COMPUTER INC.") then
print ("<span>" .. string.gsub(sys.value.boardVendor.value, "ASUSTeK COMPUTER INC.", "ASUS"))
print (" | " .. sys.value.boardName.value .. " | ")
print (version_parse(string.gsub(sys.value.boardVersion.value, "^", "")))
elseif string.match(sys.value.boardName.value, "EMB%-H81B") then
print ("<span>" .. string.gsub(sys.value.boardVendor.value, "To be filled by O.E.M." or "Not Specified", "AAEON"))
print (" | " .. sys.value.boardName.value .. " | ")
print (string.gsub(sys.value.boardVersion.value, "To be filled by O.E.M." or "Not Specified" or "Unknow", "Rev: 2.00") .. "</span>")
-- AAEON EMB-Q87A
elseif string.match(sys.value.boardName.value, "EMB%-Q87A") then
print ("<span>" .. string.gsub(sys.value.boardVendor.value, "To be filled by O.E.M." or "Not Specified" or "Unknow", "AAEON"))
print (" | " .. sys.value.boardName.value .. " | ")
print (string.gsub(sys.value.boardVersion.value, "To be filled by O.E.M." or "Not Specified" or "Unknow", "Rev: 2.00") .. "</span>")
print ("<span>" .. oem_parse(sys.value.boardVendor.value))
print (" | " .. oem_parse(sys.value.boardName.value) .. " | ")
print (version_parse(string.gsub(sys.value.boardVersion.value, "^", "Rev : ")))
<p class="dashboard-infos dash-info-cpu" style="cursor: pointer;" onclick="window.open('/cgi-bin/acf/alpine-baselayout/health/proc', '_blank')" >
<span class="data-title">CPU</span><%= string.sub((proc.value.model.value), 14) %>
<p class="dashboard-infos dash-info-memory" style="cursor: pointer;" onclick="window.open('/cgi-bin/acf/alpine-baselayout/health/proc', '_blank')" >
<%= string.gsub(bytesToSize(tonumber(sys.value.memory.totalData)), ".%d+%w+", "") %> Total |
<%= bytesToSize(tonumber(sys.value.memory.freeData)) %> Free |
<%= bytesToSize(tonumber(sys.value.memory.usedData)) %> Used
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
</p>
</div>
<!-- Dashboard Monitoring Block - 3 -->
<div class="data-block data-monitoring">
<h4 class="dashboard-block-title dashboard-title-system-uptime">Monitoring</h4>
<span class="icon-trinity"></span>
<p class="dashboard-infos">
<span class="data-title">Uptime</span>
<span id="uptime" class="uptime">
<%= uptime %><br>
<script type="application/javascript">
// IMPORT UPTIME FOR JS LIVE TIMER
if(window.location.href.indexOf("welcome/read") > -1){
let increment = <%= up_time or "unknow"%>;
let delay = () =>
{
increment += 1;
// CONVERT JS UPTIME
var js_uptime = parseInt(increment);
var js_centuries = Math.floor((js_uptime / (3600*24) / 365) / 100);
var js_years = Math.floor((js_uptime / (3600*24) / 365) % 100);
var js_mounths = Math.floor((((js_uptime / (3600 * 24)) % 365) % 365) / 30);
var js_days = Math.floor((((js_uptime / (3600 * 24)) % 365) % 365) % 30)
var js_hours = Math.floor(js_uptime % (3600*24) / 3600);
var js_minutes = Math.floor(js_uptime % 3600 / 60);
var js_seconds = Math.floor(js_uptime % 60);
// FORMAT JS UPTIME UP TO CENTURIES
var centuries_display = js_centuries > 0 ? js_centuries + (js_centuries <= 1 ? " Century " : " Centuries ") : "";
var years_display = js_years > 0 ? js_years + (js_years <= 1 ? " Year " : " Years ") : "";
var mounths_display = js_mounths > 0 ? js_mounths + (js_mounths <= 1 ? " Mounth " : " Mounths ") : "";
var days_display = js_days > 0 ? js_days + (js_days <= 1 ? " Day " : " Days ") : "";
var hours_display = js_hours < 10 ? "0" + js_hours + "h " : js_hours + "h ";
var minutes_display = js_minutes < 10 ? "0" + js_minutes + "m " : js_minutes + "m ";
var secondes_display = js_seconds < 10 ? "0" + js_seconds + "s" : js_seconds + "s";
// RETURN JS FORMATED TIME
return centuries_display + years_display + mounths_display + days_display + hours_display + minutes_display + secondes_display;
};
// JS FORMATED TIME LIVE COUNT
setInterval(() => document.getElementById("uptime").innerHTML = delay(), 1000);
};
</script>
</span>
</p>
<p class="dashboard-infos">
<span class="data-title">System Temp</span>
<a href='javascript:void(0);' id='toggle-degree' title='Celsius to Fahrenheit' onclick='toggleDegree()'>
<span id="cpuTemp" class="dash-monitoring-temp">
<%
if ((tonumber(api.value.cpuTemp.value)) ~= nil) and ((tonumber(api.value.cpuTemp.value)) < 50000) then
print (math.ceil(tonumber(api.value.boardTemp.value / 1000)) .. " °C | " .. "<span class='normal'>" .. math.floor(tonumber(api.value.cpuTemp.value / 1000)) .. " °C</span>")
elseif ((tonumber(api.value.cpuTemp.value)) ~= nil) and ((tonumber(api.value.cpuTemp.value)) >= 50000) then
print (math.ceil(tonumber(api.value.boardTemp.value / 1000)) .. " °C | " .. "<span class='medium'>" .. math.floor(tonumber(api.value.cpuTemp.value / 1000)) .. " °C</span>")
elseif((tonumber(api.value.cpuTemp.value)) ~= nil) and ((tonumber(api.value.cpuTemp.value)) >= 75000) then
print (math.ceil(tonumber(api.value.boardTemp.value / 1000)) .. " °C | " .. "<span class='hot'>" .. math.floor(tonumber(api.value.cpuTemp.value / 1000)) .. " °C</span>")
<script type="application/javascript" defer>
// CONVERT TEMP TO FAHRENHEIT
if (((<%= tonumber(api.value.cpuTemp.value) %>) < 50000) && (window.localStorage.getItem('toggle-degree') === 'fahrenheit')) {
document.getElementById("cpuTemp").innerHTML = ((Math.ceil(((<%= tonumber(api.value.boardTemp.value) %>) / 1000) * 9 / 5) + 32) + " °F | <span class='normal'>" + (Math.floor(((<%= tonumber(api.value.cpuTemp.value) %>) / 1000) * 9 / 5) + 32)) + " °F</span>";
} else if (((<%= tonumber(api.value.cpuTemp.value) %>) >= 50000) && (window.localStorage.getItem('toggle-degree') === 'fahrenheit')) {
document.getElementById("cpuTemp").innerHTML = ((Math.ceil(((<%= tonumber(api.value.boardTemp.value) %>) / 1000) * 9 / 5) + 32) + " °F | <span class='medium'>" + (Math.floor(((<%= tonumber(api.value.cpuTemp.value) %>) / 1000) * 9 / 5) + 32)) + " °F</span>";
} else if (((<%= tonumber(api.value.cpuTemp.value) %>) >= 75000) && (window.localStorage.getItem('toggle-degree') === 'fahrenheit')) {
document.getElementById("cpuTemp").innerHTML = ((Math.ceil(((<%= tonumber(api.value.boardTemp.value) %>) / 1000) * 9 / 5) + 32) + " °F | <span class='hot'>" + (Math.floor(((<%= tonumber(api.value.cpuTemp.value) %>) / 1000) * 9 / 5) + 32)) + " °F</span>";
}
</script>
</p>
<p class="dashboard-infos">
<span class="data-title">IP</span>
<span class="value-title value-net-local"><%= net.value.wanIP.value %> via <%= netstats.value.br0.ipaddr %></span>
<!-- Dashboard App Block - LINE 1 -->
</div>
<!-- Dashboard App Block - LINE 2 -->
<div class="dashboard-main main-block">
<!-- Dashboard CPU Block - 1 -->
<div class="data-block data-cpu">
<h4 class="dashboard-block-title dashboard-title-cpu-stats">CPU Temp</h4>
<!-- Dashboard Main Block - NETWORK CHART.JS -->
<canvas id="chartCpuTemp" class="data-chart block-chart"></canvas>
</div>
<!-- Dashboard Memory Block - 2 -->
<div class="data-block data-memory">
<h4 class="dashboard-block-title dashboard-title-memory-stats">Memory Usage</h4>
<!-- Dashboard Main Block - NETWORK CHART.JS -->
<canvas id="chartMemUsed" class="data-chart block-chart"></canvas>
</div>
<!-- Dashboard App Block - LINE 2 -->
</div>
<!-- Dashboard App Block - LINE 3 -->
<div class="dashboard-main main-block">
<!-- Dashboard Main Block - SYSTEM - BLOCK 1 -->
<div class="disk-list">
<%
local fdisk_output = sys.value.drivelist.value
-- Function to parse fdisk output and detect disks
function detect_disks(fdisk_output)
local disks = {}
for disk_name in fdisk_output:gmatch("Disk /dev/(sd%a):") do
table.insert(disks, "/dev/" .. disk_name)
end
return disks
end
-- Function to parse fdisk output
function parse_fdisk_output(output)
local disk_info = {}
-- Initialize variables to store disk geometry and partitions
local geometry = {}
local partitions = {}
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
-- Flags to track when to start capturing geometry and partitions
local capture_geometry = false
local capture_partitions = false
-- Split the output into lines
for line in output:gmatch("[^\r\n]+") do
-- Start capturing geometry when 'Disk /dev/' is encountered
if line:match("^Disk%s/dev/(sd%a):") then
capture_geometry = true
end
-- Start capturing partitions when 'Device' is encountered
if line:match("^Device") then
capture_partitions = true
end
-- Capture disk geometry
if capture_geometry then
local disk_size = line:match("Disk /dev/sd%a: (%S+ %a+)")
if disk_size then
geometry.disk_size = ("<span class='disk-size'>" .. disk_size .. "</span>")
end
local sector_size = line:match("Sector size %(logical/physical%): (%d+)/(%d+)")
if sector_size then
geometry.logical_sector_size = tonumber(sector_size)
end
local total_sectors = line:match("total (%d+) sectors")
if total_sectors then
geometry.total_sectors = tonumber(total_sectors)
end
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
local disk_model = line:match("Disk model: (.+)")
if disk_model then
geometry.disk_model = disk_model
end
local heads_cylinders_sectors = line:match("(%d+) heads, (%d+) sectors/track, (%d+) cylinders")
if heads_cylinders_sectors then
geometry.heads = tonumber(heads_cylinders_sectors:match("(%d+)"))
geometry.sectors_per_track = tonumber(heads_cylinders_sectors:match(", (%d+)"))
geometry.cylinders = tonumber(heads_cylinders_sectors:match(", (%d+)$"))
end
end
-- Capture partition information
if capture_partitions then
-- Extracting information from each line
local partition_device, start_sector, end_sector, sectors, size, partition_type, name = line:match("^%s*([^%s]+)%s+(%d+)%s+(%d+)%s+(%d+)%s+([%d%.]+%s*[A-Za-z]+)%s+(%w+)%s+(.*)$")
if partition_device then
local partition = {
device = partition_device,
start_sector = tonumber(start_sector),
end_sector = tonumber(end_sector),
size = size,
type = partition_type,
name = name
}
table.insert(partitions, partition)
else
-- Print unmatched lines for debugging
print()
end
end
end
-- Store geometry and partitions in disk_info table
disk_info.geometry = geometry
disk_info.partitions = partitions
return disk_info
end
-- Detect disks
local disks = detect_disks(fdisk_output)
-- Iterate over each disk
for _, disk in ipairs(disks) do
print("<div class='disk-block disk-" .. disk:gsub("/dev/", "") .. "-block'>")
print("Parsing disk:", disk)
print("--------------------")
-- Execute fdisk command for the disk and store the output in a variable
local fdisk_output_disk = io.popen("fdisk -l " .. disk):read("*a")
-- Parse fdisk output for the disk
local disk_info = parse_fdisk_output(fdisk_output_disk)
-- Print disk geometry
--print("Disk Geometry:")
for key, value in pairs(disk_info.geometry) do
print(key .. ":", value)
--print("disk Size":", disk_info.geometry.disk_size)
end
print()
-- Print parsed partitions for the disk
print("Partitions:")
for _, partition in ipairs(disk_info.partitions) do
print("Partition Device:", partition.device)
--print("Start Sector:", partition.start_sector)
--print("End Sector:", partition.end_sector)
print("Size:", partition.size)
--print("Type:", partition.type)
print("Name:", partition.name)
print()
end
print("--------------------")
print()
print(disk_info.geometry.disk_size)
print("</div>")
end
%>
</div>
<!-- Dashboard Main Block - DISK - BLOCK 2 -->
</div>
<% htmlviewfunctions.displaysectionend(header_level) %>