feat(secubox-core): Add LED heartbeat for MochaBin and Vortex services dashboard
- Add LED heartbeat to secubox-core daemon using MochaBin's RGB LEDs (led1) - Green flash: system healthy - Double red flash: warning state - Long red flash: error state - Blue flash: boot/startup - LED pulses once per watchdog cycle (default 60s) - New UCI options: led_heartbeat (default 1), watchdog_interval (default 60) - Add "Node Services" section to Vortex DNS LuCI dashboard showing published sites - Bump secubox-core version to 0.10.0-r12 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e3784537c3
commit
5205b3b2bd
@ -50,12 +50,19 @@ var callMeshSync = rpc.declare({
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callGetPublished = rpc.declare({
|
||||
object: 'luci.vortex-dns',
|
||||
method: 'get_published',
|
||||
expect: { services: [] }
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
callStatus(),
|
||||
callGetSlaves(),
|
||||
callGetPeers()
|
||||
callGetPeers(),
|
||||
callGetPublished()
|
||||
]);
|
||||
},
|
||||
|
||||
@ -63,6 +70,7 @@ return view.extend({
|
||||
var status = data[0] || {};
|
||||
var slaves = data[1] || [];
|
||||
var peers = data[2] || [];
|
||||
var published = data[3] || [];
|
||||
|
||||
var view = E('div', { 'class': 'cbi-map' }, [
|
||||
E('h2', {}, 'Vortex DNS'),
|
||||
@ -103,6 +111,9 @@ return view.extend({
|
||||
// Mesh Peers Section
|
||||
this.renderPeersSection(status.mesh, peers),
|
||||
|
||||
// Published Services Section
|
||||
this.renderServicesSection(published, status.master),
|
||||
|
||||
// Actions Section
|
||||
this.renderActionsSection(status)
|
||||
]);
|
||||
@ -225,6 +236,58 @@ return view.extend({
|
||||
]);
|
||||
},
|
||||
|
||||
renderServicesSection: function(services, master) {
|
||||
var wildcard = master ? master.wildcard_domain : null;
|
||||
var nodePrefix = wildcard ? wildcard.split('.')[0] : null;
|
||||
|
||||
// Deduplicate services by name
|
||||
var seen = {};
|
||||
var uniqueServices = [];
|
||||
(services || []).forEach(function(s) {
|
||||
if (!seen[s.name]) {
|
||||
seen[s.name] = true;
|
||||
uniqueServices.push(s);
|
||||
}
|
||||
});
|
||||
|
||||
return E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', {}, 'Node Services'),
|
||||
E('div', { 'class': 'cbi-section-descr' },
|
||||
wildcard ? 'Services published on *.' + wildcard : 'Published services on this node'),
|
||||
|
||||
uniqueServices.length > 0 ? E('table', { 'class': 'table' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, 'Service'),
|
||||
E('th', { 'class': 'th' }, 'Domain'),
|
||||
E('th', { 'class': 'th' }, 'Node URL'),
|
||||
E('th', { 'class': 'th' }, 'Actions')
|
||||
])
|
||||
].concat(uniqueServices.map(function(s) {
|
||||
var nodeUrl = nodePrefix ? 'https://' + s.name + '.' + wildcard : null;
|
||||
return E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td' }, E('strong', {}, s.name)),
|
||||
E('td', { 'class': 'td' }, E('a', {
|
||||
'href': 'https://' + s.domain,
|
||||
'target': '_blank',
|
||||
'style': 'color: #00d4aa;'
|
||||
}, s.domain)),
|
||||
E('td', { 'class': 'td' }, nodeUrl ? E('a', {
|
||||
'href': nodeUrl,
|
||||
'target': '_blank',
|
||||
'style': 'color: #888; font-size: 0.9em;'
|
||||
}, s.name + '.' + wildcard) : '-'),
|
||||
E('td', { 'class': 'td' }, E('a', {
|
||||
'href': 'https://' + s.domain,
|
||||
'target': '_blank',
|
||||
'class': 'btn cbi-button-action',
|
||||
'style': 'padding: 2px 8px; font-size: 0.85em;'
|
||||
}, 'Open'))
|
||||
]);
|
||||
}))) : E('p', { 'style': 'color: #888; margin-top: 8px;' },
|
||||
'No services published. Use "metablogizerctl emancipate" to publish sites.')
|
||||
]);
|
||||
},
|
||||
|
||||
renderActionsSection: function(status) {
|
||||
var self = this;
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=secubox-core
|
||||
PKG_VERSION:=0.10.0
|
||||
PKG_RELEASE:=11
|
||||
PKG_RELEASE:=12
|
||||
PKG_ARCH:=all
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=SecuBox Team
|
||||
|
||||
@ -5,6 +5,8 @@ config core 'main'
|
||||
option appstore_url 'https://repo.secubox.org/catalog'
|
||||
option appstore_fallback_local '1'
|
||||
option health_check_interval '300'
|
||||
option watchdog_interval '60'
|
||||
option led_heartbeat '1'
|
||||
option ai_enabled '0'
|
||||
option ai_mode 'copilot'
|
||||
option device_id ''
|
||||
|
||||
@ -10,16 +10,82 @@
|
||||
. /lib/functions.sh
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
SECUBOX_VERSION="0.8.1"
|
||||
SECUBOX_VERSION="0.8.2"
|
||||
LOG_FILE="/var/log/secubox/core.log"
|
||||
PID_FILE="/var/run/secubox/core.pid"
|
||||
STATE_DIR="/var/run/secubox"
|
||||
WATCHDOG_STATE="/var/run/secubox/watchdog.json"
|
||||
|
||||
# LED paths for MochaBin (RGB LEDs: led1, led2, led3)
|
||||
LED_GREEN="/sys/class/leds/green:led1"
|
||||
LED_RED="/sys/class/leds/red:led1"
|
||||
LED_BLUE="/sys/class/leds/blue:led1"
|
||||
LED_ENABLED=0
|
||||
|
||||
# Services to monitor (init.d name:check_method:restart_delay)
|
||||
# check_method: pid, docker, lxc, port:PORT
|
||||
MONITORED_SERVICES=""
|
||||
|
||||
# LED helper functions
|
||||
led_init() {
|
||||
# Check if LEDs are available (MochaBin or compatible)
|
||||
if [ -d "$LED_GREEN" ] && [ -d "$LED_RED" ]; then
|
||||
LED_ENABLED=1
|
||||
# Set trigger to none for manual control
|
||||
echo none > "$LED_GREEN/trigger" 2>/dev/null
|
||||
echo none > "$LED_RED/trigger" 2>/dev/null
|
||||
echo none > "$LED_BLUE/trigger" 2>/dev/null
|
||||
log debug "LED heartbeat enabled (MochaBin detected)"
|
||||
else
|
||||
log debug "LED heartbeat disabled (no compatible LEDs)"
|
||||
fi
|
||||
}
|
||||
|
||||
led_set() {
|
||||
local led="$1"
|
||||
local state="$2" # 0 = off, 1 = on
|
||||
[ "$LED_ENABLED" = "1" ] || return 0
|
||||
[ -f "${led}/brightness" ] && echo "$state" > "${led}/brightness" 2>/dev/null
|
||||
}
|
||||
|
||||
led_pulse() {
|
||||
local led="$1"
|
||||
[ "$LED_ENABLED" = "1" ] || return 0
|
||||
led_set "$led" 255
|
||||
# Brief flash - actual timing handled by background subshell
|
||||
( sleep 1 && led_set "$led" 0 ) &
|
||||
}
|
||||
|
||||
# Heartbeat function - shows system status via LED
|
||||
# Green pulse = healthy, Red pulse = warning/error
|
||||
led_heartbeat() {
|
||||
[ "$LED_ENABLED" = "1" ] || return 0
|
||||
local status="$1" # healthy, warning, error
|
||||
|
||||
case "$status" in
|
||||
healthy)
|
||||
# Single green flash
|
||||
led_set "$LED_GREEN" 255
|
||||
( sleep 1 && echo 0 > "${LED_GREEN}/brightness" 2>/dev/null ) &
|
||||
;;
|
||||
warning)
|
||||
# Double red flash
|
||||
led_set "$LED_RED" 255
|
||||
( sleep 1 && echo 0 > "${LED_RED}/brightness" && sleep 1 && echo 255 > "${LED_RED}/brightness" && sleep 1 && echo 0 > "${LED_RED}/brightness" ) &
|
||||
;;
|
||||
error)
|
||||
# Long red flash
|
||||
led_set "$LED_RED" 255
|
||||
( sleep 2 && echo 0 > "${LED_RED}/brightness" 2>/dev/null ) &
|
||||
;;
|
||||
boot)
|
||||
# Blue pulse during startup
|
||||
led_set "$LED_BLUE" 255
|
||||
( sleep 2 && echo 0 > "${LED_BLUE}/brightness" 2>/dev/null ) &
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Auto-discover SecuBox services from ctl scripts
|
||||
discover_secubox_services() {
|
||||
local services=""
|
||||
@ -429,16 +495,24 @@ daemon_mode() {
|
||||
# Write PID
|
||||
echo $$ > "$PID_FILE"
|
||||
|
||||
# Initialize LED heartbeat
|
||||
led_init
|
||||
led_heartbeat boot
|
||||
|
||||
# Get health check interval
|
||||
local health_interval=$(uci -q get secubox.main.health_check_interval || echo "300")
|
||||
|
||||
# Get watchdog interval (faster than health check)
|
||||
local watchdog_interval=$(uci -q get secubox.main.watchdog_interval || echo "60")
|
||||
|
||||
# LED heartbeat setting (enabled by default)
|
||||
local led_heartbeat_enabled=$(uci -q get secubox.main.led_heartbeat || echo "1")
|
||||
|
||||
# Main daemon loop
|
||||
local health_counter=0
|
||||
local health_cycles=$((health_interval / watchdog_interval))
|
||||
[ "$health_cycles" -lt 1 ] && health_cycles=1
|
||||
local last_health_status="healthy"
|
||||
|
||||
while true; do
|
||||
# Run watchdog every cycle
|
||||
@ -447,10 +521,18 @@ daemon_mode() {
|
||||
# Run health check every N cycles
|
||||
health_counter=$((health_counter + 1))
|
||||
if [ "$health_counter" -ge "$health_cycles" ]; then
|
||||
run_health_check > /tmp/secubox/health-status.json
|
||||
local health_output=$(run_health_check)
|
||||
echo "$health_output" > /tmp/secubox/health-status.json
|
||||
# Extract status for LED
|
||||
last_health_status=$(echo "$health_output" | jsonfilter -e '@.status' 2>/dev/null || echo "healthy")
|
||||
health_counter=0
|
||||
fi
|
||||
|
||||
# LED heartbeat pulse (once per watchdog cycle)
|
||||
if [ "$led_heartbeat_enabled" = "1" ]; then
|
||||
led_heartbeat "$last_health_status"
|
||||
fi
|
||||
|
||||
# Sleep until next check
|
||||
sleep "$watchdog_interval"
|
||||
done
|
||||
|
||||
Loading…
Reference in New Issue
Block a user