secubox-openwrt/package/secubox/luci-app-mqtt-bridge/root/usr/sbin/mqtt-bridge
CyberMind-FR 31a87c5d7a feat(structure): reorganize luci-app packages into package/secubox/ + appstore migration
Major structural reorganization and feature additions:

## Folder Reorganization
- Move 17 luci-app-* packages to package/secubox/ (except luci-app-secubox core hub)
- Update all tooling to support new structure:
  - secubox-tools/quick-deploy.sh: search both locations
  - secubox-tools/validate-modules.sh: validate both directories
  - secubox-tools/fix-permissions.sh: fix permissions in both locations
  - .github/workflows/test-validate.yml: build from both paths
- Update README.md links to new package/secubox/ paths

## AppStore Migration (Complete)
- Add catalog entries for all remaining luci-app packages:
  - network-tweaks.json: Network optimization tools
  - secubox-bonus.json: Documentation & demos hub
- Total: 24 apps in AppStore catalog (22 existing + 2 new)
- New category: 'documentation' for docs/demos/tutorials

## VHost Manager v2.0 Enhancements
- Add profile activation system for Internal Services and Redirects
- Implement createVHost() API wrapper for template-based deployment
- Fix Virtual Hosts view rendering with proper LuCI patterns
- Fix RPCD backend shell script errors (remove invalid local declarations)
- Extend backend validation for nginx return directives (redirect support)
- Add section_id parameter for named VHost profiles
- Add Remove button to Redirects page for feature parity
- Update README to v2.0 with comprehensive feature documentation

## Network Tweaks Dashboard
- Close button added to component details modal

Files changed: 340+ (336 renames with preserved git history)
Packages affected: 19 luci-app, 2 secubox-app, 1 theme, 4 tools

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 14:59:38 +01:00

276 lines
6.4 KiB
Bash
Executable File

#!/bin/sh
#
# SecuBox MQTT Bridge daemon
# Handles USB adapter discovery, stats tracking, and automation hooks.
. /lib/functions.sh
UCI_NAMESPACE="mqtt-bridge"
LOGTAG="mqtt-bridge"
SCAN_INTERVAL=10
COMMIT_NEEDED=0
START_TIME="$(date +%s)"
PREV_PAYLOAD_COUNT=0
PREV_TIMESTAMP="$START_TIME"
LAST_EVENT=""
log_msg() {
logger -t "$LOGTAG" "$*"
}
format_duration() {
local seconds="$1"
local h=$((seconds / 3600))
local m=$(( (seconds % 3600) / 60 ))
local s=$((seconds % 60))
printf '%02dh %02dm %02ds' "$h" "$m" "$s"
}
load_interval() {
config_load "$UCI_NAMESPACE"
config_get interval monitor interval
[ -n "$interval" ] && SCAN_INTERVAL="$interval"
}
find_usb_device() {
local vendor="$1"
local product="$2"
local dev
for dev in /sys/bus/usb/devices/*; do
[ -f "$dev/idVendor" ] || continue
[ -f "$dev/idProduct" ] || continue
local idVendor idProduct
idVendor="$(cat "$dev/idVendor" 2>/dev/null)"
idProduct="$(cat "$dev/idProduct" 2>/dev/null)"
[ "$idVendor" = "$vendor" ] || continue
[ "$idProduct" = "$product" ] || continue
echo "$dev"
return 0
done
return 1
}
find_usb_tty() {
local base="$1"
local path node
for path in "$base" "$base"/* "$base"/*/*; do
[ -d "$path/tty" ] || continue
for node in "$path"/tty/*; do
[ -e "$node" ] || continue
local tty
tty="$(basename "$node")"
[ -e "/dev/$tty" ] && { echo "/dev/$tty"; return 0; }
done
done
return 1
}
set_option_if_changed() {
local section="$1"
local key="$2"
local value="$3"
local current
current="$(uci -q get ${UCI_NAMESPACE}.adapter.${section}.${key} 2>/dev/null)"
[ "$current" = "$value" ] && return
uci set ${UCI_NAMESPACE}.adapter.${section}.${key}="$value"
COMMIT_NEEDED=1
}
clear_option_if_needed() {
local section="$1"
local key="$2"
local current
current="$(uci -q get ${UCI_NAMESPACE}.adapter.${section}.${key} 2>/dev/null)"
[ -z "$current" ] && return
uci delete ${UCI_NAMESPACE}.adapter.${section}.${key}
COMMIT_NEEDED=1
}
publish_alert() {
local message="$1"
local topic="$2"
[ -n "$message" ] || return
log_msg "ALERT: $message"
if [ -n "$topic" ]; then
printf '%s %s\n' "$(date -Iseconds)" "$message" >> /tmp/mqtt-bridge-alerts.log
fi
}
RULE_MATCH_ADAPTER=""
RULE_MATCH_STATE=""
apply_rule() {
local section="$1"
local target when action message topic
config_get target "$section" adapter
config_get when "$section" when
config_get action "$section" action
config_get message "$section" message
config_get topic "$section" topic
[ "$RULE_MATCH_ADAPTER" = "$target" ] || return
[ "$RULE_MATCH_STATE" = "$when" ] || return
case "$action" in
alert)
publish_alert "$message" "$topic"
;;
rescan)
log_msg "Rule $section triggered rescan for $target"
run_detection_once
;;
esac
}
run_rules() {
RULE_MATCH_ADAPTER="$1"
RULE_MATCH_STATE="$2"
config_foreach apply_rule rule
}
update_adapter_section() {
local section="$1"
local enabled vendor product title preset
config_get enabled "$section" enabled "1"
config_get vendor "$section" vendor
config_get product "$section" product
config_get preset "$section" preset
config_get title "$section" title
if [ "$enabled" != "1" ]; then
set_option_if_changed "$section" detected "0"
set_option_if_changed "$section" health "disabled"
return
fi
if [ -z "$vendor" ] || [ -z "$product" ]; then
set_option_if_changed "$section" detected "0"
set_option_if_changed "$section" health "unknown"
return
fi
local dev_path
dev_path="$(find_usb_device "$vendor" "$product")" || dev_path=""
local prev_detected
prev_detected="$(uci -q get ${UCI_NAMESPACE}.adapter.${section}.detected 2>/dev/null)"
if [ -n "$dev_path" ]; then
local bus devnum port ts
bus="$(cat "$dev_path/busnum" 2>/dev/null)"
devnum="$(cat "$dev_path/devnum" 2>/dev/null)"
port="$(find_usb_tty "$dev_path")"
ts="$(date -Iseconds)"
set_option_if_changed "$section" detected "1"
set_option_if_changed "$section" health "online"
[ -n "$bus" ] && set_option_if_changed "$section" bus "$bus"
[ -n "$devnum" ] && set_option_if_changed "$section" device "$devnum"
if [ -n "$port" ]; then
set_option_if_changed "$section" port "$port"
else
clear_option_if_needed "$section" port
fi
set_option_if_changed "$section" last_seen "$ts"
if [ "$prev_detected" != "1" ]; then
LAST_EVENT="$ts"
log_msg "Adapter $section ($title) detected on bus $bus dev $devnum $port"
run_rules "$section" "online"
fi
else
set_option_if_changed "$section" detected "0"
set_option_if_changed "$section" health "missing"
clear_option_if_needed "$section" port
clear_option_if_needed "$section" bus
clear_option_if_needed "$section" device
if [ "$prev_detected" = "1" ]; then
LAST_EVENT="$(date -Iseconds)"
log_msg "Adapter $section ($title) disconnected"
run_rules "$section" "missing"
fi
fi
}
count_enabled_clients() {
local count=0
config_foreach _count_client adapter "$1"
echo "$count"
}
_count_client() {
local section="$1"
local enabled detected
config_get enabled "$section" enabled 1
config_get detected "$section" detected 0
if [ "$enabled" = "1" ] && [ "$detected" = "1" ]; then
count=$((count + 1))
fi
}
payload_count() {
uci -q show mqtt-bridge.payloads 2>/dev/null | grep -c '=payload'
}
update_stats() {
local now payloads clients delta count elapsed
now="$(date +%s)"
config_load "$UCI_NAMESPACE"
count=0
config_foreach _count_client adapter
clients="$count"
payloads="$(payload_count)"
elapsed=$((now - PREV_TIMESTAMP))
if [ "$elapsed" -gt 0 ]; then
delta=$((payloads - PREV_PAYLOAD_COUNT))
if [ "$delta" -lt 0 ]; then
delta=0
fi
local mps=$((delta / elapsed))
uci set ${UCI_NAMESPACE}.stats.mps="$mps"
fi
PREV_PAYLOAD_COUNT="$payloads"
PREV_TIMESTAMP="$now"
uci set ${UCI_NAMESPACE}.stats.clients="$clients"
uci set ${UCI_NAMESPACE}.stats.retained="${payloads:-0}"
uci set ${UCI_NAMESPACE}.stats.uptime="$(format_duration $((now - START_TIME)))"
[ -n "$LAST_EVENT" ] && uci set ${UCI_NAMESPACE}.stats.last_event="$LAST_EVENT"
uci commit ${UCI_NAMESPACE}
}
run_detection_once() {
COMMIT_NEEDED=0
config_load "$UCI_NAMESPACE"
config_foreach update_adapter_section adapter
if [ "$COMMIT_NEEDED" -eq 1 ]; then
uci commit "$UCI_NAMESPACE"
fi
update_stats
}
daemon_loop() {
while true; do
load_interval
run_detection_once
sleep "$SCAN_INTERVAL"
done
}
case "$1" in
--rescan)
run_detection_once
;;
--oneshot)
run_detection_once
;;
*)
daemon_loop
;;
esac